From 9d8cdbe428cd309a250a02dc7650b81282c26e29 Mon Sep 17 00:00:00 2001 From: Antonov548 Date: Wed, 28 Jun 2023 13:23:14 +0200 Subject: [PATCH 1/2] update vendor cigraph --- src/vendor/cigraph/.all-contributorsrc | 135 - src/vendor/cigraph/.azure/build-win.yml | 14 +- src/vendor/cigraph/.azure/build.yml | 17 +- .../cigraph/.devcontainer/devcontainer.json | 40 - src/vendor/cigraph/.pre-commit-config.yaml | 1 + src/vendor/cigraph/.travis.yml | 63 +- src/vendor/cigraph/.zenodo.json | 22 +- src/vendor/cigraph/ACKNOWLEDGEMENTS.md | 17 +- src/vendor/cigraph/AUTHORS | 1 - src/vendor/cigraph/CHANGELOG.md | 541 +--- src/vendor/cigraph/CMakeLists.txt | 69 +- src/vendor/cigraph/CONTRIBUTING.md | 4 +- src/vendor/cigraph/CONTRIBUTORS.md | 19 - src/vendor/cigraph/ONEWS | 4 +- src/vendor/cigraph/README.md | 8 +- src/vendor/cigraph/appveyor.yml | 4 +- src/vendor/cigraph/azure-pipelines.yml | 47 +- src/vendor/cigraph/codecov.yml | 1 - .../cigraph/etc/cmake/CTestCustom.cmake.in | 9 + .../cigraph/etc/cmake/CheckTLSSupport.cmake | 4 +- .../cigraph/etc/cmake/FindCXSparse.cmake | 75 + .../etc/cmake/PreventInSourceBuilds.cmake | 12 +- .../cigraph/etc/cmake/attribute_support.cmake | 27 - src/vendor/cigraph/etc/cmake/compilers.cmake | 42 +- .../etc/cmake/cpack_install_script.cmake | 21 +- .../cigraph/etc/cmake/dependencies.cmake | 10 +- src/vendor/cigraph/etc/cmake/features.cmake | 14 - .../etc/cmake/ieee754_endianness.cmake | 54 - .../etc/cmake/ieee754_endianness_check.c | 24 - .../cigraph/etc/cmake/pkgconfig_helpers.cmake | 6 +- .../cigraph/etc/cmake/run_legacy_test.cmake | 19 +- .../cigraph/etc/cmake/safe_math_support.cmake | 18 - src/vendor/cigraph/etc/cmake/summary.cmake | 9 - .../cigraph/etc/cmake/test_helpers.cmake | 12 +- src/vendor/cigraph/etc/cmake/tls.cmake | 16 +- .../cigraph/etc/cmake/uint128_support.cmake | 43 - src/vendor/cigraph/igraph.pc.in | 1 - src/vendor/cigraph/include/igraph.h | 5 +- src/vendor/cigraph/include/igraph_adjlist.h | 111 +- src/vendor/cigraph/include/igraph_arpack.h | 33 +- src/vendor/cigraph/include/igraph_array.h | 6 +- src/vendor/cigraph/include/igraph_array_pmt.h | 19 +- .../cigraph/include/igraph_attributes.h | 214 +- src/vendor/cigraph/include/igraph_bipartite.h | 47 +- src/vendor/cigraph/include/igraph_blas.h | 10 +- .../cigraph/include/igraph_centrality.h | 90 +- src/vendor/cigraph/include/igraph_cliques.h | 78 +- .../cigraph/include/igraph_cocitation.h | 23 +- .../cigraph/include/igraph_cohesive_blocks.h | 11 +- src/vendor/cigraph/include/igraph_coloring.h | 16 +- src/vendor/cigraph/include/igraph_community.h | 150 +- src/vendor/cigraph/include/igraph_complex.h | 16 +- .../cigraph/include/igraph_components.h | 43 +- src/vendor/cigraph/include/igraph_config.h.in | 55 - src/vendor/cigraph/include/igraph_constants.h | 48 +- .../cigraph/include/igraph_constructors.h | 78 +- .../cigraph/include/igraph_conversion.h | 44 +- src/vendor/cigraph/include/igraph_cycles.h | 30 - src/vendor/cigraph/include/igraph_datatype.h | 53 +- src/vendor/cigraph/include/igraph_dqueue.h | 16 +- .../cigraph/include/igraph_dqueue_pmt.h | 27 +- src/vendor/cigraph/include/igraph_eigen.h | 18 +- src/vendor/cigraph/include/igraph_embedding.h | 8 +- src/vendor/cigraph/include/igraph_epidemics.h | 5 +- src/vendor/cigraph/include/igraph_error.h | 386 +-- src/vendor/cigraph/include/igraph_eulerian.h | 7 +- src/vendor/cigraph/include/igraph_flow.h | 76 +- src/vendor/cigraph/include/igraph_foreign.h | 72 +- src/vendor/cigraph/include/igraph_games.h | 110 +- .../cigraph/include/igraph_graph_list.h | 60 - .../cigraph/include/igraph_graphicality.h | 24 +- src/vendor/cigraph/include/igraph_graphlets.h | 21 +- src/vendor/cigraph/include/igraph_heap.h | 6 +- src/vendor/cigraph/include/igraph_heap_pmt.h | 17 +- src/vendor/cigraph/include/igraph_hrg.h | 66 +- src/vendor/cigraph/include/igraph_interface.h | 68 +- src/vendor/cigraph/include/igraph_interrupt.h | 4 +- src/vendor/cigraph/include/igraph_iterators.h | 223 +- src/vendor/cigraph/include/igraph_lapack.h | 15 +- src/vendor/cigraph/include/igraph_layout.h | 132 +- src/vendor/cigraph/include/igraph_lsap.h | 3 +- src/vendor/cigraph/include/igraph_matching.h | 18 +- src/vendor/cigraph/include/igraph_matrix.h | 23 +- .../cigraph/include/igraph_matrix_list.h | 59 - .../cigraph/include/igraph_matrix_pmt.h | 221 +- src/vendor/cigraph/include/igraph_memory.h | 11 +- .../include/igraph_microscopic_update.h | 17 +- src/vendor/cigraph/include/igraph_mixing.h | 19 +- src/vendor/cigraph/include/igraph_motifs.h | 50 +- .../cigraph/include/igraph_neighborhood.h | 10 +- src/vendor/cigraph/include/igraph_nongraph.h | 71 +- src/vendor/cigraph/include/igraph_operators.h | 73 +- src/vendor/cigraph/include/igraph_paths.h | 351 +-- src/vendor/cigraph/include/igraph_pmt.h | 110 +- src/vendor/cigraph/include/igraph_pmt_off.h | 30 +- src/vendor/cigraph/include/igraph_progress.h | 7 +- src/vendor/cigraph/include/igraph_psumtree.h | 15 +- src/vendor/cigraph/include/igraph_random.h | 142 +- src/vendor/cigraph/include/igraph_scan.h | 29 +- src/vendor/cigraph/include/igraph_scg.h | 143 + .../cigraph/include/igraph_separators.h | 20 +- src/vendor/cigraph/include/igraph_sparsemat.h | 220 +- src/vendor/cigraph/include/igraph_spmatrix.h | 109 + src/vendor/cigraph/include/igraph_stack.h | 22 +- src/vendor/cigraph/include/igraph_stack_pmt.h | 12 +- src/vendor/cigraph/include/igraph_statusbar.h | 11 +- .../cigraph/include/igraph_structural.h | 126 +- src/vendor/cigraph/include/igraph_strvector.h | 88 +- src/vendor/cigraph/include/igraph_topology.h | 170 +- .../cigraph/include/igraph_transitivity.h | 26 +- .../cigraph/include/igraph_typed_list_pmt.h | 119 - src/vendor/cigraph/include/igraph_types.h | 114 +- src/vendor/cigraph/include/igraph_vector.h | 79 +- .../cigraph/include/igraph_vector_list.h | 73 - .../cigraph/include/igraph_vector_pmt.h | 220 +- .../cigraph/include/igraph_vector_ptr.h | 53 +- .../cigraph/include/igraph_version.h.in | 8 +- src/vendor/cigraph/include/igraph_visitor.h | 55 +- src/vendor/cigraph/interfaces/CMakeLists.txt | 2 +- src/vendor/cigraph/interfaces/functions.yaml | 1468 ++++------ src/vendor/cigraph/interfaces/types.yaml | 299 +- src/vendor/cigraph/src/CMakeLists.txt | 85 +- .../cigraph/src/centrality/betweenness.c | 1729 +++++------ .../src/centrality/centrality_internal.h | 37 - .../cigraph/src/centrality/centrality_other.c | 1548 +++++++++- .../cigraph/src/centrality/centralization.c | 75 +- src/vendor/cigraph/src/centrality/closeness.c | 267 +- src/vendor/cigraph/src/centrality/coreness.c | 68 +- .../cigraph/src/centrality/eigenvector.c | 549 ---- .../cigraph/src/centrality/hub_authority.c | 495 ---- src/vendor/cigraph/src/centrality/pagerank.c | 708 ----- src/vendor/cigraph/src/centrality/prpack.cpp | 134 +- .../centrality/prpack/prpack_base_graph.cpp | 6 +- .../src/centrality/prpack/prpack_base_graph.h | 4 - .../centrality/prpack/prpack_igraph_graph.cpp | 91 +- .../centrality/prpack/prpack_igraph_graph.h | 23 +- .../src/centrality/prpack/prpack_result.h | 3 +- .../src/centrality/prpack/prpack_solver.cpp | 12 +- .../src/centrality/prpack/prpack_solver.h | 2 - .../src/centrality/prpack/prpack_utils.cpp | 3 +- .../cigraph/src/centrality/prpack_internal.h | 6 +- src/vendor/cigraph/src/centrality/truss.cpp | 286 -- .../src/cliques/cliquer/CMakeLists.txt | 3 +- .../cigraph/src/cliques/cliquer/cliquer.c | 430 ++- .../cigraph/src/cliques/cliquer/cliquer.h | 35 +- src/vendor/cigraph/src/cliques/cliquer/set.h | 8 +- .../cigraph/src/cliques/cliquer_internal.h | 40 +- .../cigraph/src/cliques/cliquer_wrapper.c | 265 +- src/vendor/cigraph/src/cliques/cliques.c | 701 +++-- src/vendor/cigraph/src/cliques/glet.c | 481 ++-- .../cigraph/src/cliques/maximal_cliques.c | 286 +- .../src/cliques/maximal_cliques_template.h | 175 +- .../cigraph/src/community/community_misc.c | 373 ++- .../cigraph/src/community/edge_betweenness.c | 373 ++- .../cigraph/src/community/fast_modularity.c | 364 +-- src/vendor/cigraph/src/community/fluid.c | 130 +- .../cigraph/src/community/infomap/infomap.cc | 232 +- .../community/infomap/infomap_FlowGraph.cc | 299 +- .../src/community/infomap/infomap_FlowGraph.h | 47 +- .../src/community/infomap/infomap_Greedy.cc | 401 +-- .../src/community/infomap/infomap_Greedy.h | 38 +- .../src/community/infomap/infomap_Node.cc | 71 + .../src/community/infomap/infomap_Node.h | 29 +- .../cigraph/src/community/label_propagation.c | 206 +- .../src/community/leading_eigenvector.c | 326 ++- src/vendor/cigraph/src/community/leiden.c | 489 ++-- src/vendor/cigraph/src/community/louvain.c | 400 +-- src/vendor/cigraph/src/community/modularity.c | 67 +- .../src/community/optimal_modularity.c | 59 +- .../src/community/spinglass/NetRoutines.cpp | 22 +- .../src/community/spinglass/NetRoutines.h | 2 +- .../src/community/spinglass/clustertool.cpp | 149 +- .../src/community/spinglass/pottsmodel_2.cpp | 183 +- .../src/community/spinglass/pottsmodel_2.h | 28 +- .../src/community/walktrap/walktrap.cpp | 149 +- .../walktrap/walktrap_communities.cpp | 229 +- .../community/walktrap/walktrap_communities.h | 28 +- .../src/community/walktrap/walktrap_graph.cpp | 78 +- .../src/community/walktrap/walktrap_graph.h | 5 +- .../src/community/walktrap/walktrap_heap.cpp | 103 +- .../src/community/walktrap/walktrap_heap.h | 30 +- src/vendor/cigraph/src/config.h.in | 36 +- .../src/connectivity/cohesive_blocks.c | 382 +-- .../cigraph/src/connectivity/components.c | 919 +++--- .../cigraph/src/connectivity/separators.c | 497 ++-- .../cigraph/src/constructors/adjacency.c | 1250 ++------ .../cigraph/src/constructors/atlas-edges.h | 22 +- src/vendor/cigraph/src/constructors/atlas.c | 16 +- .../src/constructors/basic_constructors.c | 75 +- .../cigraph/src/constructors/circulant.c | 111 - .../cigraph/src/constructors/de_bruijn.c | 46 +- src/vendor/cigraph/src/constructors/famous.c | 85 +- src/vendor/cigraph/src/constructors/full.c | 273 +- .../src/constructors/generalized_petersen.c | 95 - src/vendor/cigraph/src/constructors/kautz.c | 100 +- .../cigraph/src/constructors/lattices.c | 603 ---- src/vendor/cigraph/src/constructors/lcf.c | 62 +- .../cigraph/src/constructors/linegraph.c | 102 +- src/vendor/cigraph/src/constructors/prufer.c | 36 +- src/vendor/cigraph/src/constructors/regular.c | 607 +--- src/vendor/cigraph/src/constructors/trees.c | 163 -- src/vendor/cigraph/src/core/array.c | 4 +- src/vendor/cigraph/src/core/array.pmt | 50 +- src/vendor/cigraph/src/core/buckets.c | 94 +- src/vendor/cigraph/src/core/buckets.h | 47 +- src/vendor/cigraph/src/core/cutheap.c | 55 +- src/vendor/cigraph/src/core/cutheap.h | 22 +- src/vendor/cigraph/src/core/dqueue.c | 10 +- src/vendor/cigraph/src/core/dqueue.pmt | 194 +- src/vendor/cigraph/src/core/error.c | 157 +- src/vendor/cigraph/src/core/estack.c | 30 +- src/vendor/cigraph/src/core/estack.h | 21 +- src/vendor/cigraph/src/core/exceptions.h | 25 +- .../cigraph/src/core/fixed_vectorlist.c | 56 +- .../cigraph/src/core/fixed_vectorlist.h | 13 +- src/vendor/cigraph/src/core/genheap.c | 302 -- src/vendor/cigraph/src/core/genheap.h | 73 - src/vendor/cigraph/src/core/grid.c | 167 +- src/vendor/cigraph/src/core/grid.h | 36 +- src/vendor/cigraph/src/core/heap.c | 4 +- src/vendor/cigraph/src/core/heap.pmt | 137 +- src/vendor/cigraph/src/core/indheap.c | 399 +-- src/vendor/cigraph/src/core/indheap.h | 99 +- src/vendor/cigraph/src/core/interruption.c | 10 +- src/vendor/cigraph/src/core/interruption.h | 14 +- src/vendor/cigraph/src/core/marked_queue.c | 76 +- src/vendor/cigraph/src/core/marked_queue.h | 47 +- src/vendor/cigraph/src/core/math.h | 95 +- src/vendor/cigraph/src/core/matrix.c | 290 +- src/vendor/cigraph/src/core/matrix.pmt | 795 ++--- src/vendor/cigraph/src/core/matrix_list.c | 54 - src/vendor/cigraph/src/core/memory.c | 95 +- src/vendor/cigraph/src/core/printing.c | 219 +- src/vendor/cigraph/src/core/progress.c | 7 +- src/vendor/cigraph/src/core/psumtree.c | 46 +- src/vendor/cigraph/src/core/set.c | 111 +- src/vendor/cigraph/src/core/set.h | 13 +- src/vendor/cigraph/src/core/sparsemat.c | 1189 +++----- src/vendor/cigraph/src/core/spmatrix.c | 1066 +++++++ src/vendor/cigraph/src/core/stack.c | 39 + src/vendor/cigraph/src/core/stack.pmt | 155 +- src/vendor/cigraph/src/core/statusbar.c | 21 +- src/vendor/cigraph/src/core/strvector.c | 745 ++--- src/vendor/cigraph/src/core/trie.c | 296 +- src/vendor/cigraph/src/core/trie.h | 30 +- src/vendor/cigraph/src/core/typed_list.pmt | 1099 ------- src/vendor/cigraph/src/core/vector.c | 526 ++-- src/vendor/cigraph/src/core/vector.pmt | 1201 +++----- src/vendor/cigraph/src/core/vector_list.c | 171 -- src/vendor/cigraph/src/core/vector_ptr.c | 313 +- src/vendor/cigraph/src/f2c.h | 3 +- src/vendor/cigraph/src/flow/flow.c | 1172 ++++---- src/vendor/cigraph/src/flow/flow_conversion.c | 93 - src/vendor/cigraph/src/flow/flow_internal.h | 19 +- src/vendor/cigraph/src/flow/st-cuts.c | 1198 ++++---- src/vendor/cigraph/src/games/barabasi.c | 481 ++-- .../cigraph/src/games/callaway_traits.c | 55 +- src/vendor/cigraph/src/games/citations.c | 174 +- src/vendor/cigraph/src/games/correlated.c | 99 +- .../cigraph/src/games/degree_sequence.c | 312 +- .../degree_sequence_vl/degree_sequence_vl.h | 34 - .../degree_sequence_vl/gengraph_box_list.cpp | 110 + .../degree_sequence_vl/gengraph_box_list.h | 83 + .../degree_sequence_vl/gengraph_definitions.h | 36 +- .../gengraph_degree_sequence.cpp | 321 ++- .../gengraph_degree_sequence.h | 44 +- .../gengraph_graph_molloy_hash.cpp | 689 ++++- .../gengraph_graph_molloy_hash.h | 110 +- .../gengraph_graph_molloy_optimized.cpp | 1317 ++++++++- .../gengraph_graph_molloy_optimized.h | 145 +- .../games/degree_sequence_vl/gengraph_hash.h | 61 +- .../degree_sequence_vl/gengraph_header.h | 2 +- .../gengraph_mr-connected.cpp | 82 +- .../degree_sequence_vl/gengraph_powerlaw.cpp | 272 ++ .../degree_sequence_vl/gengraph_powerlaw.h | 86 + .../games/degree_sequence_vl/gengraph_qsort.h | 363 ++- .../degree_sequence_vl/gengraph_random.cpp | 2 + .../degree_sequence_vl/gengraph_random.h | 2 +- .../gengraph_vertex_cover.h | 72 + src/vendor/cigraph/src/games/dotproduct.c | 79 +- src/vendor/cigraph/src/games/erdos_renyi.c | 169 +- src/vendor/cigraph/src/games/establishment.c | 48 +- src/vendor/cigraph/src/games/forestfire.c | 113 +- src/vendor/cigraph/src/games/grg.c | 31 +- src/vendor/cigraph/src/games/growing_random.c | 46 +- src/vendor/cigraph/src/games/islands.c | 129 +- src/vendor/cigraph/src/games/k_regular.c | 32 +- src/vendor/cigraph/src/games/preference.c | 324 ++- src/vendor/cigraph/src/games/recent_degree.c | 150 +- src/vendor/cigraph/src/games/sbm.c | 352 ++- src/vendor/cigraph/src/games/static_fitness.c | 47 +- src/vendor/cigraph/src/games/tree.c | 23 +- src/vendor/cigraph/src/games/watts_strogatz.c | 32 +- src/vendor/cigraph/src/graph/adjlist.c | 743 ++--- src/vendor/cigraph/src/graph/attributes.c | 205 +- src/vendor/cigraph/src/graph/attributes.h | 66 +- src/vendor/cigraph/src/graph/basic_query.c | 19 +- src/vendor/cigraph/src/graph/caching.c | 185 -- src/vendor/cigraph/src/graph/caching.h | 52 - src/vendor/cigraph/src/graph/cattributes.c | 2505 ++++++++-------- src/vendor/cigraph/src/graph/graph_list.c | 61 - src/vendor/cigraph/src/graph/iterators.c | 1202 ++++---- .../src/graph/{internal.h => neighbors.h} | 29 +- src/vendor/cigraph/src/graph/type_common.c | 207 -- .../cigraph/src/graph/type_indexededgelist.c | 1688 ++++++----- src/vendor/cigraph/src/graph/visitors.c | 452 ++- src/vendor/cigraph/src/hrg/dendro.h | 8 +- src/vendor/cigraph/src/hrg/graph.h | 3 +- src/vendor/cigraph/src/hrg/hrg.cc | 591 ++-- src/vendor/cigraph/src/hrg/hrg_types.cc | 39 +- .../cigraph/src/internal/glpk_support.c | 17 +- .../cigraph/src/internal/glpk_support.h | 17 +- src/vendor/cigraph/src/internal/hacks.c | 29 +- src/vendor/cigraph/src/internal/hacks.h | 31 +- src/vendor/cigraph/src/internal/lsap.c | 115 +- src/vendor/cigraph/src/internal/pstdint.h | 817 ++++++ src/vendor/cigraph/src/internal/qsort.c | 7 +- src/vendor/cigraph/src/internal/utils.c | 95 - src/vendor/cigraph/src/internal/utils.h | 32 - src/vendor/cigraph/src/internal/zeroin.c | 39 +- src/vendor/cigraph/src/io/dimacs.c | 247 +- src/vendor/cigraph/src/io/dl-header.h | 17 +- src/vendor/cigraph/src/io/dl-lexer.l | 34 +- src/vendor/cigraph/src/io/dl-parser.y | 187 +- src/vendor/cigraph/src/io/dl.c | 58 +- src/vendor/cigraph/src/io/dot.c | 113 +- src/vendor/cigraph/src/io/edgelist.c | 79 +- src/vendor/cigraph/src/io/gml-header.h | 12 +- src/vendor/cigraph/src/io/gml-lexer.l | 39 +- src/vendor/cigraph/src/io/gml-parser.y | 277 +- src/vendor/cigraph/src/io/gml-tree.c | 159 +- src/vendor/cigraph/src/io/gml-tree.h | 63 +- src/vendor/cigraph/src/io/gml.c | 1283 +++------ src/vendor/cigraph/src/io/graphdb.c | 82 +- src/vendor/cigraph/src/io/graphml.c | 1195 +++----- src/vendor/cigraph/src/io/leda.c | 158 +- src/vendor/cigraph/src/io/lgl-header.h | 7 +- src/vendor/cigraph/src/io/lgl-lexer.l | 2 +- src/vendor/cigraph/src/io/lgl-parser.y | 68 +- src/vendor/cigraph/src/io/lgl.c | 165 +- src/vendor/cigraph/src/io/ncol-header.h | 5 +- src/vendor/cigraph/src/io/ncol-lexer.l | 2 +- src/vendor/cigraph/src/io/ncol-parser.y | 67 +- src/vendor/cigraph/src/io/ncol.c | 142 +- src/vendor/cigraph/src/io/pajek-header.h | 28 +- src/vendor/cigraph/src/io/pajek-lexer.l | 105 +- src/vendor/cigraph/src/io/pajek-parser.y | 524 ++-- src/vendor/cigraph/src/io/pajek.c | 365 ++- src/vendor/cigraph/src/io/parse_utils.c | 387 --- src/vendor/cigraph/src/io/parse_utils.h | 41 - src/vendor/cigraph/src/isomorphism/bliss.cc | 194 +- .../cigraph/src/isomorphism/bliss/graph.hh | 14 +- .../cigraph/src/isomorphism/isoclasses.c | 129 +- .../src/isomorphism/isomorphism_misc.c | 28 +- src/vendor/cigraph/src/isomorphism/lad.c | 495 ++-- src/vendor/cigraph/src/isomorphism/queries.c | 86 +- src/vendor/cigraph/src/isomorphism/vf2.c | 911 +++--- src/vendor/cigraph/src/layout/circular.c | 104 +- .../cigraph/src/layout/davidson_harel.c | 248 +- .../cigraph/src/layout/drl/DensityGrid.cpp | 29 +- .../cigraph/src/layout/drl/DensityGrid_3d.cpp | 29 +- src/vendor/cigraph/src/layout/drl/drl_Node.h | 8 +- .../cigraph/src/layout/drl/drl_Node_3d.h | 8 +- .../cigraph/src/layout/drl/drl_graph.cpp | 67 +- src/vendor/cigraph/src/layout/drl/drl_graph.h | 30 +- .../cigraph/src/layout/drl/drl_graph_3d.cpp | 65 +- .../cigraph/src/layout/drl/drl_graph_3d.h | 30 +- .../cigraph/src/layout/drl/drl_layout.cpp | 18 +- .../cigraph/src/layout/drl/drl_layout_3d.cpp | 12 +- .../cigraph/src/layout/fruchterman_reingold.c | 145 +- src/vendor/cigraph/src/layout/gem.c | 124 +- src/vendor/cigraph/src/layout/graphopt.c | 88 +- src/vendor/cigraph/src/layout/kamada_kawai.c | 65 +- src/vendor/cigraph/src/layout/large_graph.c | 157 +- .../cigraph/src/layout/layout_bipartite.c | 16 +- src/vendor/cigraph/src/layout/layout_grid.c | 22 +- .../cigraph/src/layout/layout_internal.h | 32 +- src/vendor/cigraph/src/layout/layout_random.c | 38 +- src/vendor/cigraph/src/layout/mds.c | 103 +- src/vendor/cigraph/src/layout/merge_dla.c | 106 +- src/vendor/cigraph/src/layout/merge_grid.c | 51 +- src/vendor/cigraph/src/layout/merge_grid.h | 21 +- .../cigraph/src/layout/reingold_tilford.c | 397 ++- src/vendor/cigraph/src/layout/sugiyama.c | 556 ++-- src/vendor/cigraph/src/layout/umap.c | 1260 -------- src/vendor/cigraph/src/linalg/arpack.c | 195 +- .../cigraph/src/linalg/arpack_internal.h | 5 - src/vendor/cigraph/src/linalg/blas.c | 123 +- src/vendor/cigraph/src/linalg/blas_internal.h | 5 - src/vendor/cigraph/src/linalg/eigen.c | 344 ++- src/vendor/cigraph/src/linalg/lapack.c | 312 +- .../cigraph/src/linalg/lapack_internal.h | 5 - src/vendor/cigraph/src/math/bfgs.c | 221 ++ src/vendor/cigraph/src/math/complex.c | 13 +- src/vendor/cigraph/src/math/safe_intop.c | 156 - src/vendor/cigraph/src/math/safe_intop.h | 136 - src/vendor/cigraph/src/math/utils.c | 275 +- src/vendor/cigraph/src/misc/bipartite.c | 564 ++-- src/vendor/cigraph/src/misc/chordality.c | 157 +- src/vendor/cigraph/src/misc/cocitation.c | 276 +- src/vendor/cigraph/src/misc/coloring.c | 221 +- src/vendor/cigraph/src/misc/conversion.c | 750 +++-- .../{order_cycle.h => conversion_internal.h} | 23 +- src/vendor/cigraph/src/misc/cycle_bases.c | 540 ---- .../cigraph/src/misc/degree_sequence.cpp | 193 +- src/vendor/cigraph/src/misc/embedding.c | 264 +- .../cigraph/src/misc/feedback_arc_set.c | 343 ++- .../cigraph/src/misc/feedback_arc_set.h | 15 +- src/vendor/cigraph/src/misc/graphicality.c | 379 ++- src/vendor/cigraph/src/misc/matching.c | 396 ++- .../cigraph/src/misc/microscopic_update.c | 128 +- src/vendor/cigraph/src/misc/mixing.c | 339 +-- src/vendor/cigraph/src/misc/motifs.c | 577 ++-- src/vendor/cigraph/src/misc/order_cycle.cpp | 100 - src/vendor/cigraph/src/misc/other.c | 340 ++- src/vendor/cigraph/src/misc/power_law_fit.c | 328 --- src/vendor/cigraph/src/misc/scan.c | 390 ++- src/vendor/cigraph/src/misc/sir.c | 41 +- src/vendor/cigraph/src/misc/spanning_trees.c | 311 +- src/vendor/cigraph/src/operators/add_edge.c | 13 +- .../cigraph/src/operators/complementer.c | 48 +- src/vendor/cigraph/src/operators/compose.c | 61 +- .../src/operators/connect_neighborhood.c | 85 +- src/vendor/cigraph/src/operators/contract.c | 121 +- src/vendor/cigraph/src/operators/difference.c | 70 +- .../cigraph/src/operators/disjoint_union.c | 86 +- .../cigraph/src/operators/intersection.c | 205 +- .../cigraph/src/operators/misc_internal.c | 129 +- .../cigraph/src/operators/misc_internal.h | 13 +- src/vendor/cigraph/src/operators/permute.c | 53 +- src/vendor/cigraph/src/operators/reverse.c | 25 +- src/vendor/cigraph/src/operators/rewire.c | 57 +- .../cigraph/src/operators/rewire_edges.c | 125 +- .../cigraph/src/operators/rewire_internal.h | 9 +- src/vendor/cigraph/src/operators/simplify.c | 55 +- src/vendor/cigraph/src/operators/subgraph.c | 373 +-- src/vendor/cigraph/src/operators/subgraph.h | 15 +- src/vendor/cigraph/src/operators/union.c | 173 +- .../cigraph/src/paths/all_shortest_paths.c | 266 +- src/vendor/cigraph/src/paths/astar.c | 275 -- src/vendor/cigraph/src/paths/bellman_ford.c | 384 ++- src/vendor/cigraph/src/paths/dijkstra.c | 831 +++--- src/vendor/cigraph/src/paths/distances.c | 839 +----- src/vendor/cigraph/src/paths/eulerian.c | 335 ++- src/vendor/cigraph/src/paths/floyd_warshall.c | 363 --- src/vendor/cigraph/src/paths/histogram.c | 48 +- src/vendor/cigraph/src/paths/johnson.c | 127 +- src/vendor/cigraph/src/paths/random_walk.c | 378 +-- src/vendor/cigraph/src/paths/shortest_paths.c | 868 ++---- src/vendor/cigraph/src/paths/simple_paths.c | 82 +- src/vendor/cigraph/src/paths/sparsifier.c | 460 --- src/vendor/cigraph/src/paths/unweighted.c | 429 ++- src/vendor/cigraph/src/paths/voronoi.c | 425 --- src/vendor/cigraph/src/paths/widest_paths.c | 727 ----- .../cigraph/src/properties/basic_properties.c | 74 +- .../cigraph/src/properties/constraint.c | 154 +- .../src/properties/convergence_degree.c | 56 +- src/vendor/cigraph/src/properties/dag.c | 202 +- src/vendor/cigraph/src/properties/degrees.c | 220 +- src/vendor/cigraph/src/properties/ecc.c | 411 --- src/vendor/cigraph/src/properties/girth.c | 99 +- src/vendor/cigraph/src/properties/loops.c | 30 +- .../cigraph/src/properties/multiplicity.c | 337 +-- .../cigraph/src/properties/neighborhood.c | 241 +- src/vendor/cigraph/src/properties/perfect.c | 191 -- .../src/properties/properties_internal.h | 11 +- src/vendor/cigraph/src/properties/spectral.c | 673 ++--- src/vendor/cigraph/src/properties/trees.c | 512 +--- src/vendor/cigraph/src/properties/triangles.c | 397 +-- .../src/properties/triangles_template.h | 40 +- .../src/properties/triangles_template1.h | 28 +- src/vendor/cigraph/src/random/random.c | 1659 +++++------ .../cigraph/src/random/random_internal.h | 38 - src/vendor/cigraph/src/random/rng_glibc2.c | 144 - src/vendor/cigraph/src/random/rng_mt19937.c | 179 -- src/vendor/cigraph/src/random/rng_pcg32.c | 124 - src/vendor/cigraph/src/random/rng_pcg64.c | 138 - src/vendor/cigraph/src/scg/scg.c | 2303 +++++++++++++++ .../cigraph/src/scg/scg_approximate_methods.c | 173 ++ src/vendor/cigraph/src/scg/scg_exact_scg.c | 69 + src/vendor/cigraph/src/scg/scg_headers.h | 128 + src/vendor/cigraph/src/scg/scg_kmeans.c | 102 + .../cigraph/src/scg/scg_optimal_method.c | 241 ++ src/vendor/cigraph/src/scg/scg_utils.c | 94 + src/vendor/cigraph/src/version.c | 40 +- src/vendor/cigraph/vendor/CMakeLists.txt | 1 - src/vendor/cigraph/vendor/cs/CMakeLists.txt | 4 +- .../cigraph/vendor/cs/SuiteSparse_config.h | 221 ++ src/vendor/cigraph/vendor/cs/cs.h | 631 +++- src/vendor/cigraph/vendor/f2c/CMakeLists.txt | 22 +- src/vendor/cigraph/vendor/f2c/exit_.c | 2 +- src/vendor/cigraph/vendor/f2c/main.c | 148 + src/vendor/cigraph/vendor/f2c/s_paus.c | 6 - src/vendor/cigraph/vendor/f2c/s_stop.c | 3 - src/vendor/cigraph/vendor/f2c/sig_die.c | 5 +- src/vendor/cigraph/vendor/f2c/uninit.c | 11 +- src/vendor/cigraph/vendor/glpk/CMakeLists.txt | 2 +- .../cigraph/vendor/lapack/CMakeLists.txt | 4 +- .../cigraph/vendor/mini-gmp/CMakeLists.txt | 2 +- src/vendor/cigraph/vendor/pcg/CMakeLists.txt | 21 - src/vendor/cigraph/vendor/pcg/LICENSE.txt | 19 - .../cigraph/vendor/pcg/pcg-advance-128.c | 61 - .../cigraph/vendor/pcg/pcg-advance-64.c | 59 - .../cigraph/vendor/pcg/pcg-output-128.c | 63 - src/vendor/cigraph/vendor/pcg/pcg-output-32.c | 63 - src/vendor/cigraph/vendor/pcg/pcg-output-64.c | 73 - src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c | 378 --- src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c | 255 -- src/vendor/cigraph/vendor/pcg/pcg_variants.h | 2557 ----------------- .../cigraph/vendor/plfit/arithmetic_ansi.h | 2 +- src/vendor/cigraph/vendor/plfit/gss.c | 34 +- src/vendor/cigraph/vendor/plfit/hzeta.c | 19 +- src/vendor/cigraph/vendor/plfit/lbfgs.c | 6 +- src/vendor/cigraph/vendor/plfit/lbfgs.h | 10 +- src/vendor/cigraph/vendor/plfit/mt.c | 10 +- src/vendor/cigraph/vendor/plfit/plfit.c | 77 +- src/vendor/cigraph/vendor/plfit/plfit_error.c | 5 +- src/vendor/cigraph/vendor/plfit/plfit_error.h | 5 +- .../cigraph/vendor/plfit/plfit_version.h | 4 +- src/vendor/cigraph/vendor/plfit/sampling.c | 8 +- 520 files changed, 42054 insertions(+), 57049 deletions(-) delete mode 100644 src/vendor/cigraph/.devcontainer/devcontainer.json create mode 100644 src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in create mode 100644 src/vendor/cigraph/etc/cmake/FindCXSparse.cmake delete mode 100644 src/vendor/cigraph/etc/cmake/attribute_support.cmake delete mode 100644 src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake delete mode 100644 src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c delete mode 100644 src/vendor/cigraph/etc/cmake/safe_math_support.cmake delete mode 100644 src/vendor/cigraph/etc/cmake/uint128_support.cmake delete mode 100644 src/vendor/cigraph/include/igraph_config.h.in delete mode 100644 src/vendor/cigraph/include/igraph_cycles.h delete mode 100644 src/vendor/cigraph/include/igraph_graph_list.h delete mode 100644 src/vendor/cigraph/include/igraph_matrix_list.h create mode 100644 src/vendor/cigraph/include/igraph_scg.h create mode 100644 src/vendor/cigraph/include/igraph_spmatrix.h delete mode 100644 src/vendor/cigraph/include/igraph_typed_list_pmt.h delete mode 100644 src/vendor/cigraph/include/igraph_vector_list.h delete mode 100644 src/vendor/cigraph/src/centrality/centrality_internal.h delete mode 100644 src/vendor/cigraph/src/centrality/eigenvector.c delete mode 100644 src/vendor/cigraph/src/centrality/hub_authority.c delete mode 100644 src/vendor/cigraph/src/centrality/pagerank.c delete mode 100644 src/vendor/cigraph/src/centrality/truss.cpp create mode 100644 src/vendor/cigraph/src/community/infomap/infomap_Node.cc delete mode 100644 src/vendor/cigraph/src/constructors/circulant.c delete mode 100644 src/vendor/cigraph/src/constructors/generalized_petersen.c delete mode 100644 src/vendor/cigraph/src/constructors/lattices.c delete mode 100644 src/vendor/cigraph/src/constructors/trees.c delete mode 100644 src/vendor/cigraph/src/core/genheap.c delete mode 100644 src/vendor/cigraph/src/core/genheap.h delete mode 100644 src/vendor/cigraph/src/core/matrix_list.c create mode 100644 src/vendor/cigraph/src/core/spmatrix.c delete mode 100644 src/vendor/cigraph/src/core/typed_list.pmt delete mode 100644 src/vendor/cigraph/src/core/vector_list.c delete mode 100644 src/vendor/cigraph/src/flow/flow_conversion.c delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h delete mode 100644 src/vendor/cigraph/src/graph/caching.c delete mode 100644 src/vendor/cigraph/src/graph/caching.h delete mode 100644 src/vendor/cigraph/src/graph/graph_list.c rename src/vendor/cigraph/src/graph/{internal.h => neighbors.h} (55%) delete mode 100644 src/vendor/cigraph/src/graph/type_common.c create mode 100644 src/vendor/cigraph/src/internal/pstdint.h delete mode 100644 src/vendor/cigraph/src/internal/utils.c delete mode 100644 src/vendor/cigraph/src/internal/utils.h delete mode 100644 src/vendor/cigraph/src/io/parse_utils.c delete mode 100644 src/vendor/cigraph/src/io/parse_utils.h delete mode 100644 src/vendor/cigraph/src/layout/umap.c create mode 100644 src/vendor/cigraph/src/math/bfgs.c delete mode 100644 src/vendor/cigraph/src/math/safe_intop.c delete mode 100644 src/vendor/cigraph/src/math/safe_intop.h rename src/vendor/cigraph/src/misc/{order_cycle.h => conversion_internal.h} (62%) delete mode 100644 src/vendor/cigraph/src/misc/cycle_bases.c delete mode 100644 src/vendor/cigraph/src/misc/order_cycle.cpp delete mode 100644 src/vendor/cigraph/src/misc/power_law_fit.c delete mode 100644 src/vendor/cigraph/src/paths/astar.c delete mode 100644 src/vendor/cigraph/src/paths/floyd_warshall.c delete mode 100644 src/vendor/cigraph/src/paths/sparsifier.c delete mode 100644 src/vendor/cigraph/src/paths/voronoi.c delete mode 100644 src/vendor/cigraph/src/paths/widest_paths.c delete mode 100644 src/vendor/cigraph/src/properties/ecc.c delete mode 100644 src/vendor/cigraph/src/properties/perfect.c delete mode 100644 src/vendor/cigraph/src/random/random_internal.h delete mode 100644 src/vendor/cigraph/src/random/rng_glibc2.c delete mode 100644 src/vendor/cigraph/src/random/rng_mt19937.c delete mode 100644 src/vendor/cigraph/src/random/rng_pcg32.c delete mode 100644 src/vendor/cigraph/src/random/rng_pcg64.c create mode 100644 src/vendor/cigraph/src/scg/scg.c create mode 100644 src/vendor/cigraph/src/scg/scg_approximate_methods.c create mode 100644 src/vendor/cigraph/src/scg/scg_exact_scg.c create mode 100644 src/vendor/cigraph/src/scg/scg_headers.h create mode 100644 src/vendor/cigraph/src/scg/scg_kmeans.c create mode 100644 src/vendor/cigraph/src/scg/scg_optimal_method.c create mode 100644 src/vendor/cigraph/src/scg/scg_utils.c create mode 100644 src/vendor/cigraph/vendor/cs/SuiteSparse_config.h create mode 100644 src/vendor/cigraph/vendor/f2c/main.c delete mode 100644 src/vendor/cigraph/vendor/pcg/CMakeLists.txt delete mode 100644 src/vendor/cigraph/vendor/pcg/LICENSE.txt delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-advance-128.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-advance-64.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-128.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-32.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-64.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c delete mode 100644 src/vendor/cigraph/vendor/pcg/pcg_variants.h diff --git a/src/vendor/cigraph/.all-contributorsrc b/src/vendor/cigraph/.all-contributorsrc index 9c26f453ded..ce094a9d7a6 100644 --- a/src/vendor/cigraph/.all-contributorsrc +++ b/src/vendor/cigraph/.all-contributorsrc @@ -378,141 +378,6 @@ "contributions": [ "code" ] - }, - { - "login": "Katterrina", - "name": "Kateřina Č.", - "avatar_url": "https://avatars.githubusercontent.com/u/31630249?v=4", - "profile": "https://github.com/Katterrina", - "contributions": [ - "code" - ] - }, - { - "login": "valdaarhun", - "name": "valdaarhun", - "avatar_url": "https://avatars.githubusercontent.com/u/39989901?v=4", - "profile": "https://github.com/valdaarhun", - "contributions": [ - "code" - ] - }, - { - "login": "YuliYudith", - "name": "YuliYudith", - "avatar_url": "https://avatars.githubusercontent.com/u/54366258?v=4", - "profile": "https://github.com/YuliYudith", - "contributions": [ - "code" - ] - }, - { - "login": "alexsyou", - "name": "alexsyou", - "avatar_url": "https://avatars.githubusercontent.com/u/54590871?v=4", - "profile": "https://github.com/alexsyou", - "contributions": [ - "code" - ] - }, - { - "login": "rohitt28", - "name": "Rohit Tawde", - "avatar_url": "https://avatars.githubusercontent.com/u/67415747?v=4", - "profile": "https://github.com/rohitt28", - "contributions": [ - "code" - ] - }, - { - "login": "alexperrone", - "name": "alexperrone", - "avatar_url": "https://avatars.githubusercontent.com/u/4990236?v=4", - "profile": "https://github.com/alexperrone", - "contributions": [ - "code" - ] - }, - { - "login": "borsgeorgica", - "name": "Georgica Bors", - "avatar_url": "https://avatars.githubusercontent.com/u/15649138?v=4", - "profile": "https://github.com/borsgeorgica", - "contributions": [ - "code" - ] - }, - { - "login": "meetpatel0963", - "name": "MEET PATEL", - "avatar_url": "https://avatars.githubusercontent.com/u/63169740?v=4", - "profile": "https://www.linkedin.com/in/meet-patel-b1329a16b/", - "contributions": [ - "code" - ] - }, - { - "login": "kwofach", - "name": "kwofach", - "avatar_url": "https://avatars.githubusercontent.com/u/97578264?v=4", - "profile": "https://github.com/kwofach", - "contributions": [ - "code" - ] - }, - { - "login": "Gomango999", - "name": "Kevin Zhu", - "avatar_url": "https://avatars.githubusercontent.com/u/37771462?v=4", - "profile": "https://github.com/Gomango999", - "contributions": [ - "code" - ] - }, - { - "login": "pradkrish", - "name": "Pradeep Krishnamurthy", - "avatar_url": "https://avatars.githubusercontent.com/u/47261443?v=4", - "profile": "https://github.com/pradkrish", - "contributions": [ - "code" - ] - }, - { - "login": "flange-ipb", - "name": "flange-ipb", - "avatar_url": "https://avatars.githubusercontent.com/u/34936695?v=4", - "profile": "https://github.com/flange-ipb", - "contributions": [ - "code" - ] - }, - { - "login": "JJ", - "name": "Juan Julián Merelo Guervós", - "avatar_url": "https://avatars.githubusercontent.com/u/500?v=4", - "profile": "http://goo.gl/IlWG8U", - "contributions": [ - "code" - ] - }, - { - "login": "rfulekjames", - "name": "Radoslav Fulek", - "avatar_url": "https://avatars.githubusercontent.com/u/54232342?v=4", - "profile": "https://github.com/rfulekjames", - "contributions": [ - "code" - ] - }, - { - "login": "professorcode1", - "name": "professorcode1", - "avatar_url": "https://avatars.githubusercontent.com/u/42749164?v=4", - "profile": "https://github.com/professorcode1", - "contributions": [ - "code" - ] } ], "contributorsPerLine": 7 diff --git a/src/vendor/cigraph/.azure/build-win.yml b/src/vendor/cigraph/.azure/build-win.yml index 6ddc3a2b63d..dfc938cbbc1 100644 --- a/src/vendor/cigraph/.azure/build-win.yml +++ b/src/vendor/cigraph/.azure/build-win.yml @@ -8,6 +8,9 @@ parameters: - name: int_arpack type: boolean default: true + - name: int_cxsparse + type: boolean + default: true - name: int_gmp type: boolean default: true @@ -25,7 +28,7 @@ parameters: default: true - name: build_type type: string - default: 'Release' + default: 'RelWithDebInfo' - name: extra_cmake_args type: string default: '' @@ -65,9 +68,7 @@ steps: - task: Cache@2 condition: eq('${{ parameters.use_ccache }}', true) inputs: - key: 'ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" | "$(Build.SourceVersion)"' - restoreKeys: | - ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" + key: 'ccache | "$(Agent.OS)"' path: $(CCACHE_DIR) displayName: Ccache @@ -93,6 +94,7 @@ steps: -DIGRAPH_USE_INTERNAL_LAPACK=${{ parameters.int_lapack }} ^ -DIGRAPH_USE_INTERNAL_ARPACK=${{ parameters.int_arpack }} ^ -DIGRAPH_USE_INTERNAL_GLPK=${{ parameters.int_glpk }} ^ + -DIGRAPH_USE_INTERNAL_CXSPARSE=${{ parameters.int_cxsparse }} ^ -DIGRAPH_USE_INTERNAL_GMP=${{ parameters.int_gmp }} ^ -DIGRAPH_VERIFY_FINALLY_STACK=${{ parameters.verify_finally }} ^ -DBUILD_SHARED_LIBS=${{ parameters.build_shared }} ^ @@ -102,7 +104,6 @@ steps: -DIGRAPH_PRINT_ARITH_HEADER=${{ parameters.print_arith_header }} ^ -DVCPKG_TARGET_TRIPLET=${{ parameters.vcpkg_target_triplet }} ^ -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%/scripts/buildsystems/vcpkg.cmake ^ - -DFLEX_KEEP_LINE_NUMBERS=ON ^ ${{ parameters.extra_cmake_args }} cmake --build . --target build_tests @@ -110,6 +111,3 @@ steps: - script: cd build && ctest -j 4 --output-on-failure ${{ parameters.extra_ctest_args }} --timeout 60 displayName: Test - - - script: ccache -s - displayName: Ccache statistics diff --git a/src/vendor/cigraph/.azure/build.yml b/src/vendor/cigraph/.azure/build.yml index 494df29fc90..d072cb6850b 100644 --- a/src/vendor/cigraph/.azure/build.yml +++ b/src/vendor/cigraph/.azure/build.yml @@ -8,6 +8,9 @@ parameters: - name: int_arpack type: boolean default: true + - name: int_cxsparse + type: boolean + default: true - name: int_gmp type: boolean default: true @@ -25,7 +28,7 @@ parameters: default: true - name: build_type type: string - default: 'Release' + default: 'RelWithDebInfo' - name: extra_cmake_args type: string default: '' @@ -38,9 +41,6 @@ parameters: - name: print_arith_header type: boolean default: true - - name: force_colored_output - type: boolean - default: true steps: - task: CMake@1 @@ -52,6 +52,7 @@ steps: -DIGRAPH_USE_INTERNAL_LAPACK=${{ parameters.int_lapack }} -DIGRAPH_USE_INTERNAL_ARPACK=${{ parameters.int_arpack }} -DIGRAPH_USE_INTERNAL_GLPK=${{ parameters.int_glpk }} + -DIGRAPH_USE_INTERNAL_CXSPARSE=${{ parameters.int_cxsparse }} -DIGRAPH_USE_INTERNAL_GMP=${{ parameters.int_gmp }} -DIGRAPH_VERIFY_FINALLY_STACK=${{ parameters.verify_finally }} -DBUILD_SHARED_LIBS=${{ parameters.build_shared }} @@ -59,16 +60,13 @@ steps: -DUSE_CCACHE=${{ parameters.use_ccache }} -DCMAKE_BUILD_TYPE=${{ parameters.build_type }} -DIGRAPH_PRINT_ARITH_HEADER=${{ parameters.print_arith_header }} - -DFORCE_COLORED_OUTPUT=${{ parameters.force_colored_output }} -DFLEX_KEEP_LINE_NUMBERS=ON ${{ parameters.extra_cmake_args }} - task: Cache@2 condition: eq('${{ parameters.use_ccache }}', true) inputs: - key: 'ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" | "$(Build.SourceVersion)"' - restoreKeys: | - ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" + key: 'ccache | "$(Agent.OS)"' path: $(CCACHE_DIR) displayName: Ccache @@ -80,6 +78,3 @@ steps: # TODO: use -j `nproc` on Linux , -j `sysctl -n hw.ncpu` on Darwin - script: cd build && ctest -j 4 --output-on-failure ${{ parameters.extra_ctest_args }} --timeout 60 displayName: Test - - - script: ccache -s - displayName: Ccache statistics diff --git a/src/vendor/cigraph/.devcontainer/devcontainer.json b/src/vendor/cigraph/.devcontainer/devcontainer.json deleted file mode 100644 index fc3081d84db..00000000000 --- a/src/vendor/cigraph/.devcontainer/devcontainer.json +++ /dev/null @@ -1,40 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu -{ - "name": "igraph dev environment", - - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/base:jammy", - - // Features to add to the dev container. More info: https://containers.dev/features. - "features": { - "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": { - "packages": "build-essential,clang,cmake,cmake-curses-gui,ninja-build,ccache,colordiff,astyle,bison,flex,libxml2-dev,libarpack2-dev,libglpk-dev,libgmp-dev,xmlto,texinfo,source-highlight,libxml2-utils,xsltproc,fop" - } - }, - - // Environment variables containing settings for various tools - "containerEnv": { - "CMAKE_GENERATOR": "Ninja", - "ASAN_OPTIONS": "color=always" - }, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "uname -a", - - // Configure tool-specific properties. - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools-extension-pack", // C/C++ and CMake support - "daohong-emilio.yash" // flex/bison support - ] - } - } - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} diff --git a/src/vendor/cigraph/.pre-commit-config.yaml b/src/vendor/cigraph/.pre-commit-config.yaml index ca953ac7637..038e7e4e76b 100644 --- a/src/vendor/cigraph/.pre-commit-config.yaml +++ b/src/vendor/cigraph/.pre-commit-config.yaml @@ -14,3 +14,4 @@ repos: exclude: "\\.out$" - id: check-merge-conflict - id: fix-byte-order-marker + diff --git a/src/vendor/cigraph/.travis.yml b/src/vendor/cigraph/.travis.yml index 295272b1d35..9805c4a5a65 100644 --- a/src/vendor/cigraph/.travis.yml +++ b/src/vendor/cigraph/.travis.yml @@ -1,8 +1,8 @@ language: c cache: ccache +# dist: xenial os: linux -dist: focal # Ignore branches with names starting with certain keywords: branches: @@ -21,27 +21,27 @@ git: addons: apt: - update: true packages: - - ninja-build - - flex - - bison -# - docbook2x -# - xmlto -# - texinfo -# - source-highlight -# - libxml2-utils -# - xsltproc -# - fop - - libgmp-dev - - libglpk-dev - - libarpack2-dev -# - libblas-dev -# - liblapack-dev - - libopenblas-dev - - libxml2-dev - - git - - colordiff + - ninja-build + - flex + - bison +# - docbook2x +# - xmlto +# - texinfo +# - source-highlight +# - libxml2-utils +# - xsltproc +# - fop + - libgmp-dev + - libglpk-dev + - libarpack2-dev +# - libblas-dev +# - liblapack-dev + - libopenblas-dev + - libsuitesparse-dev + - libxml2-dev + - git + - colordiff snaps: - name: cmake confinement: classic @@ -50,7 +50,7 @@ addons: # if this phase fails, the build stops immediately before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_CXSPARSE=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address\;Undefined # building and testing is in script # use && to ensure that ctest is not run if the build failed @@ -67,28 +67,31 @@ jobs: - name: "Linux arm64" os: linux - arch: arm64 + arch: arm64-graviton2 # faster than arm64 - name: "Linux arm64 external" os: linux - arch: arm64 + arch: arm64-graviton2 # faster than arm64 before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=OFF -DIGRAPH_USE_INTERNAL_LAPACK=OFF -DIGRAPH_USE_INTERNAL_ARPACK=OFF -DIGRAPH_USE_INTERNAL_GLPK=OFF -DIGRAPH_USE_INTERNAL_GMP=OFF -DIGRAPH_VERIFY_FINALLY_STACK=OFF -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=OFF -DIGRAPH_USE_INTERNAL_LAPACK=OFF -DIGRAPH_USE_INTERNAL_ARPACK=OFF -DIGRAPH_USE_INTERNAL_GLPK=OFF -DIGRAPH_USE_INTERNAL_CXSPARSE=OFF -DIGRAPH_USE_INTERNAL_GMP=OFF -DIGRAPH_VERIFY_FINALLY_STACK=ON -DBLA_VENDOR=OpenBLAS -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address\;Undefined - name: "Linux ppc64" os: linux + dist: focal # Snap fails with ppc64 on earlier Ubuntu. arch: ppc64le - name: "Linux s390x" os: linux + dist: focal # Some packages are missing in earlier Ubuntu on this platform. arch: s390x # Do not enable ASan, as it leads to linking errors. + # This is possibly a conflict with LTO. before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_CXSPARSE=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_ENABLE_LTO=AUTO -DIGRAPH_PRINT_ARITH_HEADER=ON -#notifications: -# email: -# on_success: change -# on_failure: always +notifications: + email: + on_success: change + on_failure: always diff --git a/src/vendor/cigraph/.zenodo.json b/src/vendor/cigraph/.zenodo.json index 231fa545526..d3545885a24 100644 --- a/src/vendor/cigraph/.zenodo.json +++ b/src/vendor/cigraph/.zenodo.json @@ -7,27 +7,7 @@ ], "creators": [ { - "name": "Csárdi, Gábor", - "orcid": "0000-0001-7098-9676" - }, - { - "name": "Nepusz, Tamás", - "orcid": "0000-0002-1451-338X" - }, - { - "name": "Horvát, Szabolcs", - "orcid": "0000-0002-3100-523X" - }, - { - "name": "Traag, Vincent", - "orcid": "0000-0003-3170-3879" - }, - { - "name": "Zanini, Fabio", - "orcid": "0000-0001-7097-8539" - }, - { - "name": "Noom, Daniel" + "name": "The igraph Core Team" } ] } diff --git a/src/vendor/cigraph/ACKNOWLEDGEMENTS.md b/src/vendor/cigraph/ACKNOWLEDGEMENTS.md index f32713268c9..579c46c6667 100644 --- a/src/vendor/cigraph/ACKNOWLEDGEMENTS.md +++ b/src/vendor/cigraph/ACKNOWLEDGEMENTS.md @@ -87,6 +87,13 @@ Copyright (C) 2006-2008 Aaron Clauset. License: [GNU GPLv2][gpl2] or later +#### SCGlib (Spectral Coarse Graining) + +Copyright (C) 2008 David Morton de Lachapelle + +License: [GNU GPLv2][gpl2] or later + + #### Spinglass community detection Copyright (C) 2004 by Joerg Reichardt. @@ -178,12 +185,12 @@ use or performance of this software. License: [GNU LGPLv2.1][lgpl2] or later -#### [GLPK (GNU Linear Programming Kit) Version 5.0](https://www.gnu.org/software/glpk/) - -Copyright (C) 2000-2020 Free Software Foundation, Inc. +#### [GLPK (GNU Linear Programming Kit) Version 4.45](https://www.gnu.org/software/glpk/) -Written by Andrew Makhorin, Department for Applied Informatics, -Moscow Aviation Institute, Moscow, Russia. E-mail: . +Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, +2009, 2010 Andrew Makhorin, Department for Applied Informatics, +Moscow Aviation Institute, Moscow, Russia. All rights reserved. +E-mail: . License: [GNU GPLv3][gpl3] or later diff --git a/src/vendor/cigraph/AUTHORS b/src/vendor/cigraph/AUTHORS index 446955dd5d9..5abbf975b79 100644 --- a/src/vendor/cigraph/AUTHORS +++ b/src/vendor/cigraph/AUTHORS @@ -3,4 +3,3 @@ Tamas Nepusz Szabolcs Horvat Vincent Traag Fabio Zanini -Daniel Noom diff --git a/src/vendor/cigraph/CHANGELOG.md b/src/vendor/cigraph/CHANGELOG.md index 1fad8213fd6..4c9f37dc51a 100644 --- a/src/vendor/cigraph/CHANGELOG.md +++ b/src/vendor/cigraph/CHANGELOG.md @@ -1,536 +1,5 @@ # igraph C library changelog -## [master] - -### Changes - - - `igraph_community_walktrap()` no longer requires `modularity` and `merges` to be non-NULL when `membership` is non-NULL. - -### Fixed - - - `igraph_hub_and_authority_scores()`, `igraph_hub_score()` and `igraph_authority_score()` considered self-loops only once on the diagonal of the adjacency matrix of undirected graphs, thus the result was not identical to that obtained by `igraph_eigenvector_centrality()` on loopy undirected graphs. This is now corrected. - - `igraph_community_infomap()` now checks edge and vertex weights for validity. - - `igraph_minimum_spanning_tree()` and `igraph_minimum_spanning_tree_prim()` now check that edge weights are not NaN. - - Fixed an initialization error in the string attribute combiner of the C attribute handler. - - Fixed an issue with the weighted clique number calculation when all the weights were the same. - - HRG functions now require a graph with at least 3 vertices; previous versions crashed with smaller graphs. - - `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()`, i.e. the ARPACK interface in igraph, are now interruptible. As a result, several other functions that rely on ARPACK (eigenvector centrality, hub and authority scores, etc.) also became interruptible. - - `igraph_get_shortest_paths_dijkstra()`, `igraph_get_all_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now validate the `from` vertex. - -### Deprecated - -- `igraph_automorphisms()` is now deprecated; its new name is `igraph_count_automorphisms()`. The old name is kept available until at least igraph 0.11. -- `igraph_hub_score()` and `igraph_authority_score()` are now deprecated. Use `igraph_hub_and_authority_scores()` instead. -- `igraph_get_incidence()` is now deprecated; its new name is `igraph_get_biadjacency()` to reflect that the returned matrix is an _adjacency_ matrix between pairs of vertices and not an _incidence_ matrix between vertices and edges. The new name is kept available until at least igraph 0.11. We plan to re-use the name in later versions to provide a proper incidence matrix where the rows are vertices and the columns are edges. - -### Other - - - Improved performance for `igraph_vertex_connectivity()`. - - Documentation improvements. - -## [0.10.4] - 2023-01-26 - -### Added - - - `igraph_get_shortest_path_astar()` finds a shortest path with the A* algorithm. - - `igraph_vertex_coloring_greedy()` now supports the DSatur heuristics (#2284, thanks to @professorcode1). - -### Changed - - - The `test` build target now only _runs_ the unit tests, but it does not _build_ them. In order to both build and run tests, use the `check` target, which continues to behave as before (PR #2291). - - The experimental function `igraph_distances_floyd_warshall()` now has `from` and `to` parameters for choosing source and target vertices. - - The experimental function `igraph_distances_floyd_warshall()` now has an additional `method` parameter to select a specific algorithm. A faster "Tree" variant of the Floyd-Warshall algorithm is now available (#2267, thanks to @rfulekjames). - -### Fixed - - - The Bellman-Ford shortest path finder is now interruptible. - - The Floyd-Warshall shortest path finder is now interruptible. - - Running CTest no longer builds the tests automatically, as this interfered with VSCode, which would invoke the `ctest` executable after configuring a project in order to determine test executables. Use the `build_tests` target to build the tests first, or use the `check` target to both _build_ and _run_ all unit tests (PR #2291). - -### Other - - - Improved the performance and memory usage of `igraph_widest_path_widths_floyd_warshall()`. - - Documentation improvements. - -## [0.10.3] - 2022-12-30 - -### Added - - - `igraph_matrix_init_array()` to initialize an igraph matrix by copying an existing C array in column-major or row-major order. - - `igraph_layout_umap_compute_weights()` computes weights for the UMAP layout algorithm from distances. This used to be part of `igraph_layout_umap()`, but it is now in a separate function to allow the user to experiment with different weighting schemes. - - `igraph_triangular_lattice()` to generate triangular lattices of various kinds (#2235, thanks to @rfulekjames). - - `igraph_hexagonal_lattice()` to generate hexagonal lattices of various kinds (#2262, thanks to @rfulekjames). - - `igraph_tree_from_parent_vector()` to create a tree or a forest from a parent vector (i.e. a vector that encodes the parent vertex of each vertex). - - `igraph_induced_subgraph_edges()` produces the IDs of edges contained within a subgraph induced by the given vertices. - -### Changed - - - The signature of the experimental `igraph_layout_umap()` function changed; the last argument is now a Boolean that specifies whether distances should already be treated as weights, and the sampling probability argument was removed. - -### Fixed - - - `igraph_transitivity_barrat()`, `igraph_community_fluid_communities()`, `igraph_sir()`, `igraph_trussness()` and graphlet functions did not correctly detect when a directed input graph had effective multi-edges due to ignoring edge directions. Such graphs are now rejected by these functions. - - Fixed a bug in `igraph_2dgrid_move()` that sometimes crashed the Large Graph Layout function when a grid cell became empty. - - `igraph_pagerank()` and `igraph_personalized_pagerank()` would fail to converge when the ARPACK implementation was used and a vertex had more than one outgoing edge but all these edges had zero weights. - - `igraph_pagerank()` and `igraph_personalized_pagerank()` no longer allow negative weights. Previously, edges with negative weights were silently ignored when using the PRPACK implementation. The ARPACK implementation would issue a warning saying that they are ignored, but in fact it computed an incorrect result. - - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` no longer trigger the "Finally stack too large" fatal error when called on certain large graphs. This was a regression in igraph 0.10. - - `igraph_community_label_propagation()` no longer rounds weights to integers. This was a regression in igraph 0.10. - - `igraph_read_graph_graphdb()` does more thorough checks on the input file. - - `igraph_calloc()` did not zero-initialize the allocated memory. This is now corrected. Note that the macro `IGRAPH_CALLOC()` was _not_ affected. - - Fixed new warnings issued by the Xcode 14.1 toolchain. - -### Deprecated - -- `igraph_subgraph_edges()` is now deprecated to avoid confusion with `igraph_induced_subgraph_edges()`; its new name is `igraph_subgraph_from_edges()`. The old name is kept available until at least igraph 0.11. - -### Other - - - Significantly improved performance for `igraph_matrix_transpose()`. - - Documentation improvements. - -## [0.10.2] - 2022-10-14 - -### Added - - - `igraph_distances_cutoff()` and `igraph_distances_dijkstra_cutoff()` calculate shortest paths with an upper limit on the path length (experimental functions). - - `igraph_distances_floyd_warshall()` for computing all-pairs shortest path lengths in dense graphs (experimental function). - - `igraph_ecc()` computes the edge clustering coefficient of some edges (experimental function). - - `igraph_voronoi()` computes a Voronoi partitioning of vertices (experimental function). - - `igraph_count_multiple_1()` determines the multiplicity of a single edge in the graph. - - `igraph_dqueue_get()` accesses an element in a queue by index. - - `igraph_degree_1()` efficiently retrieves the degee of a single vertex. - - `igraph_lazy_adjlist_has()` and `igraph_lazy_inclist_has()` to check if adjacent vertices / incident edges have already been computed and stored for a given vertex in a lazy adjlist / inclist. - -### Changed - - - `igraph_edge()` now verifies that the input edge ID is valid. - - `igraph_community_leading_eigenvector()`, `igraph_adjacency_spectral_embedding()`, `igraph_laplacian_spectral_embedding()`, `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()` now generate a random starting vector using igraph's own RNG if needed instead of relying on LAPACK or ARPACK to do so. This makes sure that the results obtained from these functions remain the same if igraph's RNG is seeded with the same value. - - `igraph_community_leading_eigenvector()` does not stop the splitting process any more when there are multiple equally likely splits (indicated by the multiplicity of the leading eigenvector being larger than 1). The algorithm picks an arbitrary split instead and proceeds normally. - -### Fixed - - - Fixed a bug in `igraph_get_k_shortest_paths()` that sometimes yielded incorrect results on undirected graphs when the `mode` argument was set to `IGRAPH_OUT` or `IGRAPH_IN`. - - `igraph_trussness()` is now interruptible. - - `igraph_spanner()` is now interruptible. - - `igraph_layout_umap()` and `igraph_layout_umap3d()` are now interruptible. - - In some rare cases, roundoff errors would cause `igraph_distance_johnson()` to fail on graphs with negative weights. - - `igraph_eulerian_cycle()` and `igraph_eulerian_path()` now returns a more specific error code (`IGRAPH_ENOSOL`) when the graph contains no Eulerian cycle or path. - - `igraph_heap_init_array()` did not copy the array data correctly for non-real specializations. - - `igraph_layout_umap_3d()` now actually uses three dimensions. - - `igraph_layout_umap()` and `igraph_layout_umap_3d()` are now interruptible. - - `igraph_vit_create()` and `igraph_eit_create()` no longer fails when trying to create an iterator for the null graph or edgeless graph from an empty range-based vertex or edge selector. - - `igraph_write_graph_leda()` did not correctly print attribute names in some warning messages. - - Addressed new warnings introduced by Clang 15. - - In the generated pkg-config file, libxml2 is now placed in the `Requires.private` section instead of the `Libs.private` one. - -### Removed - - - Removed unused and undocumented `igraph_bfgs()` function. - - Removed the undocumented function `igraph_complex_mod()`. Use `igraph_complex_abs()` instead, as it has identical functionality. - -### Deprecated - - - The `IGRAPH_EDRL` error code was deprecated; the DrL algorithm now returns `IGRAPH_FAILURE` when it used to return `IGRAPH_EDRL` (not likely to happen in practice). - - The undocumented function `igraph_dqueue_e()` is now deprecated and replaced by `igraph_dqueue_get()`. - - `igraph_finite()`, `igraph_is_nan()`, `igraph_is_inf()`, `igraph_is_posinf()` and `igraph_is_neginf()` are now deprecated. They were relics from a time when no standard alternatives existed. Use the C99 standard `isfinite()`, `isnan()` and `isinf()` instead. - -### Other - - - Documentation improvements. - -## [0.10.1] - 2022-09-08 - -### Fixed - - - Corrected a regression (compared to igraph 0.9) in weighted clique search functions. - - `igraph_girth()` no longer fails when the graph has no cycles and the `girth` parameter is set to `NULL`. - - `igraph_write_graph_gml()` did not respect entity encoding options when writing the `Creator` line. - - Fixed potential memory leak on out-of-memory condition in `igraph_asymmetric_preference_game()`, `igraph_vs_copy()` and `igraph_es_copy()`. - - Fixed an assertion failure in `igraph_barabasi_game()` and `igraph_barabasi_aging_game()` when passing in negative degree exponents. - - Fixed a compilation failure with some old Clang versions. - -### Changed - - - `igraph_write_graph_leda()` can now write boolean attributes. - -### Other - - - Support for ARM64 on Windows. - - Documentation improvements. - -## [0.10.0] - 2022-09-05 - -### Release notes - -This release focuses on infrastructural improvements, stability, and making the igraph interface more consistent, more predictable and easier to use. It contains many API-breaking changes and function renamings, in preparation for a future 1.0 release, at which point the API will become stable. Changes in this direction are likely to continue through a 0.11 release. It is recommended that you migrate your code from 0.9 to 0.10 soon, to make the eventual transition to 1.0 easier. - -Some of the highlights are: - - - A consistent use of `igraph_integer_t` for all indices and most integer quantities, both in the API and internally. This type is 64-bit by default on all 64-bit systems, bringing support for very large graphs with more than 2 billion vertices. Previously, vertex and edge indices were often represented as `igraph_real_t`. The move to an `igraph_integer_t` also implies a change from `igraph_vector_t` to `igraph_vector_int_t` in many functions. - - The random number generation framework has been overhauled. Sampling from the full range of `igraph_integer_t` is now possible. Similarly, the sampling of random reals has been improved to utilize almost the full range of the mantissa of an `igraph_real_t`. - - There is a new fully memory-managed container type for lists of vectors (`igraph_vector_list_t`), replacing most previous uses of the non-managed `igraph_vector_ptr_t`. Functions that previously used `igraph_vector_ptr_t` to return results and relied on the user to manage memory appropriately are now using `igraph_vector_list_t`, `igraph_graph_list_t` or similar and manage memory on their own. - - Some simple graph properties, such as whether a graph contains self-loops or multi-edges, or whether it is connected, are now cached in the graph data structure. Querying these properties for a second time will take constant computational time. The `igraph_invalidate_cache()` function is provided for debugging purposes. It will invaidate all cache entries. - - File format readers are much more robust and more tolerant of invalid input. - - igraph is much more resilient to overflow errors. - - Many improvements to robustness and reliability, made possible by internal refactorings. - -### Breaking changes - - - igraph now requires CMake 3.18 or later. - - In order to facilitate the usage of graphs with more than 2 billion vertices and edges, we have made the size of the `igraph_integer_t` data type to be 32 bits on 32-bit platforms and 64 bits on 64-bit platforms by default. You also have the option to compile a 32-bit igraph variant on a 64-bit platform by changing the `IGRAPH_INTEGER_SIZE` build variable in CMake to 32. - - `igraph_bool_t` is now a C99 `bool` and not an `int`. Similarly, `igraph_vector_bool_t` now consumes `sizeof(bool)` bytes per entry only, not `sizeof(int)`. The standard constants `true` and `false` may be used for Boolean values for readability. - - The random number generator interface, `igraph_rng_type_t`, has been overhauled. Check the declaration of the type for details. - - The default random number generator has been changed from Mersenne Twister to PCG32. - - Functions related to spectral coarse graining (i.e. all functions starting with `igraph_scg_...`) were separated into a project of its own. If you wish to keep on using these functions, please refer to the repository hosting the spectral coarse graining code at https://github.com/igraph/igraph-scg . The spectral coarse graining code was updated to support igraph 0.10. - - Since `igraph_integer_t` aims to be the largest integer size that is feasible on a particular platform, there is no need for generic data types based on `long int` any more. The `long` variants of generic data types (e.g., `igraph_vector_long_t`) are therefore removed; you should use the corresponding `int` variant instead, whose elements are of type `igraph_integer_t`. - - Generic data types based on `float` were removed as they were not used anywhere in the library. - - Several igraph functions that used to take a `long int` or return a `long int` now takes or returns an `igraph_integer_t` instead to make the APIs more consistent. Similarly, igraph functions that used `igraph_vector_t` for arguments that take or return _integral_ vectors (e.g., vertex or edge indices) now take `igraph_vector_int_t` instead. Graph-related functions where the API was changed due to this reason are listed below, one by one. - - Similarly, igraph functions that used to accept the `long` variant of a generic igraph data type (e.g., `igraph_vector_long_t`) now take the `int` variant of the same data type. - - The type `igraph_stack_ptr_t` and its associated functions were removed. Use `igraph_vector_ptr_t` and associated functions instead. - - Error handlers should no longer perform a `longjmp()`. Doing so will introduce memory leaks, as resource cleanup is now done in multiple stages, through multiple calls to the error handler. Thus, the error handler should either abort execution immediately (as the default handler does), or report the error, call `IGRAPH_FINALLY_FREE()`, and return normally. - - Most callback functions now return an error code. In previous versions they returned a boolean value indicating whether to terminate the search. A request to stop the search is now indicated with the special return code `IGRAPH_STOP`. - - `igraph_add_edges()` now uses an `igraph_vector_int_t` for its `edges` parameter. - - `igraph_adjacency()` no longer accepts a negative number of edges in its adjacency matrix. When negative entries are found, an error is generated. - - `igraph_adjacency()` gained an additional `loops` argument that lets you specify whether the diagonal entries should be ignored or should be interpreted as raw edge counts or _twice_ the number of edges (which is common in linear algebra contexts). - - `igraph_all_minimal_st_separators()` now returns the separators in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. - - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` now return the cuts in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. - - `igraph_arpack_unpack_complex()` now uses `igraph_integer_t` for its `nev` argument instead of `long int`. - - `igraph_articulation_points()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. - - `igraph_assortativity_nominal()` now accepts vertex types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_asymmetric_preferennce_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. - - `igraph_atlas()` now uses `igraph_integer_t` for its `number` argument. - - `igraph_automorphism_group()` now returns the generators in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. - - `igraph_barabasi_game()`, `igraph_barabasi_aging_game()`, `igraph_recent_degree_game()` and `igraph_recent_degree_aging_game()` now use an `igraph_vector_int_t` for the out-degree sequence of the nodes being generated instead of an `igraph_vector_t`. - - `igraph_bfs()` now takes an `igraph_vector_int_t` for its `roots`, `restricted`, `order`, `father`, `pred`, `succ` and `dist` arguments instead of an `igraph_vector_t`. - - `igraph_bfs_simple()` now takes `igraph_vector_int_t` for its `vids`, `layers` and `parents` arguments instead of an `igraph_vector_t`. - - `igraph_bfs_simple()` now returns -1 in `parents` for the root node of the traversal, and -2 for unreachable vertices. This is now consistent with other functions that return a parent vector. - - `igraph_biconnected_components()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. Also, the container used for the edges and vertices of the components is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. - - `igraph_bipartite_projection()` now uses `igraph_vector_int_t` to return `multiplicity1` and `multiplicity2`, not `igraph_vector_t`. - - `igraph_bridges()` now uses an `igraph_vector_int_t` to return the list of bridges, not an `igraph_vector_t`. - - `igraph_callaway_traits_game()` returns the node types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_canonical_permutation()` now uses an `igraph_vector_int_t` for its labeling parameter. - - `igraph_cattribute_list()` now uses `igraph_vector_int_t` to return `gtypes`, `vtypes` and `etypes`. - - `igraph_cited_type_game()` now uses an `igraph_vector_int_t` for its types parameter. - - `igraph_citing_cited_type_game()` now uses an `igraph_vector_int_t` for its - types parameter. - - `igraph_clique_handler_t` now uses an `igraph_vector_int_t` for its `clique` parameter, and must return an `igraph_error_t`. Use `IGRAPH_STOP` as the return code to terminate the search prematurely. The vector that the handler receives is owned by the clique search routine. If you want to hold on to the vector for a longer period of time, you need to make a copy of it in the handler. Cliques passed to the callback are marked as `const` as a reminder to this change. - - The `res` parameter of `igraph_cliques()` is now an `igraph_vector_int_list_t`. - - Callbacks used by `igraph_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. - - `igraph_closeness()` and `igraph_closeness_cutoff()` now use an `igraph_vector_int_t` to return `reachable_count`, not an `igraph_vector_t`. - - `igraph_cohesive_blocks()` now uses an `igraph_vector_int_t` to return the mapping from block indices to parent block indices, and the `cohesion`; also, it uses an `igraph_vector_int_list_t` to return the blocks themselves instead of a pointer vector of `igraph_vector_t`. - - The `igraph_community_eb_get_merges()` bridges parameter now starts the indices into the edge removal vector at 0, not 1. - - The `igraph_community_eb_get_merges()` now reports an error when not all edges in the graph are removed, instead of a nonsensical result. - - `igraph_community_edge_betweenness()` now uses an `igraph_vector_int_t` to return the edge IDs in the order of their removal as well as the list of edge IDs whose removal broke a single component into two. - - `igraph_community_fluid_communities()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. - - `igraph_community_infomap()` now uses `igraph_integer_t` for its `nb_trials` argument. - - `igraph_community_label_propagation()` now uses an `igraph_vector_int_t` for its `initial` parameter. It also takes a `mode` argument that specifies how labels should be propagated along edges (forward, backward or ignoring edge directions). - - `igraph_community_label_propagation()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. - - `igraph_community_leiden()` has an additional parameter to indicate the number of iterations to perform (PR #2177). - - `igraph_community_walktrap()`, `igraph_community_edge_betweenness()`, `igraph_community_eb_get_merges()`, `igraph_community_fastgreedy()`, `igraph_community_to_membership()`, `igraph_le_community_to_membership()`, `igraph_community_leading_eigenvector()` now use an `igraph_vector_int_t` for their `merges` parameter. - - `igraph_community_walktrap()` now uses `igraph_integer_t` for its `steps` argument. - - `igraph_coreness()` now uses an `igraph_vector_int_t` to return the coreness - values. - - `igraph_convex_hull()` now uses an `igraph_vector_int_t` to return the indices of the input vertices that were chosen to be in the convex hull. - - `igraph_correlated_game()` and `igraph_correlated_pair_game()` now take an `igraph_vector_int_t` as the permutation vector, not an `igraph_vector_t`. - - `igraph_create()` now uses an `igraph_vector_int_t` for its `edges` parameter. - - `igraph_create_bipartite()` now uses an `igraph_vector_int_t` for its `edges` parameter. - - `igraph_compose()` now returns the edge maps in an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_count_multiple()` now returns the multiplicities in an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_decompose()` now uses an `igraph_integer_t` for its `maxcompno` and `minelements` arguments instead of a `long int`. - - `igraph_degree()` now uses an `igraph_vector_int_t` to return the degrees. If you need the degrees in a vector containing floating-point numbers instead (e.g., because you want to pass them on to some other function that takes an `igraph_vector_t`), use `igraph_strength()` instead with a null weight vector. - - `igraph_degree_sequence_game()` now takes degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. - - `igraph_degseq_t`, used by `igraph_degree_sequence_game()`, uses new names for its constants. The old names are deprecated, but retained for compatibility. See `igraph_constants.h` to see which new name corresponds to which old one. - - `igraph_delete_vertices_idx()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. - - `igraph_deterministic_optimal_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. - - `igraph_dfs()` now takes an `igraph_vector_int_t` for its `order`, `order_out`, `father` and `dist` arguments instead of an `igraph_vector_t`. Furthermore, these vectors will contain -2 for vertices that have not been visited; in earlier versions, they used to contain NaN instead. Note that -1 is still used in the `father` vector to indicate the root of a DFS tree. - - `igraph_diameter()` and `igraph_diameter_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the diameter. - - `igraph_dominator_tree()` now takes an `igraph_vector_int_t` for its `dom` and `leftout` arguments instead of an `igraph_vector_t`. - - `igraph_dyad_census()` now uses `igraph_real_t` instead of `igraph_integer_t` for its output arguments, and it no longer returns -1 when overflow occurs. - - `igraph_edges()` now takes an `igraph_vector_int_t` for its `edges` argument instead of an `igraph_vector_t`. - - `igraph_es_multipairs()` was removed; you can use the newly added `igraph_es_all_between()` instead. - - `igraph_establishment_game()` now takes an `igraph_vector_int_t` for its `node_type_vec` argument instead of an `igraph_vector_t`. - - `igraph_eulerian_path()` and `igraph_eulerian_cycle()` now use `igraph_vector_int_t` to return the list of edge and vertex IDs participating in an Eulerian path or cycle instead of an `igraph_vector_t`. - - `igraph_feedback_arc_set()` now uses an `igraph_vector_int_t` to return the IDs of the edges in the feedback arc set instead of an `igraph_vector_t`. - - `igraph_get_adjacency()` no longer has the `eids` argument, which would produce an adjacency matrix where non-zero values were 1-based (not 0-based) edge IDs. If you need a matrix with edge IDs, create it manually. - - `igraph_get_adjacency_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. - - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now has a `loops` argument that lets the user specify how loop edges should be handled. - - `igraph_get_edgelist()` now uses an `igraph_vector_int_t` for its `res` parameter. - - `igraph_get_eids()` now uses `igraph_vector_int_t` to return lists of edge IDs and to receive lists of vertex IDs. - - The `path` argument of `igraph_get_eids()` was removed. You can replicate the old behaviour by constructing the list of vertex IDs explicitly from the path by duplicating each vertex in the path except the first and last ones. A helper function called `igraph_expand_path_to_pairs()` is provided to ease the transition. - - `igraph_get_eids_multi()` was removed as its design was fundamentally broken; there was no way to retrieve the IDs of all edges between a specific pair of vertices without knowing in advance how many such edges there are in the graph. Use `igraph_get_all_eids_between()` instead. - - `igraph_get_incidence()` now returns the vertex IDs corresponding to the rows and columns of the incidence matrix as `igraph_vector_int_t`. - - `igraph_get_shortest_path()`, `igraph_get_shortest_path_bellman_ford()` and `igraph_get_shortest_path_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the shortest path. - - `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now use an `igraph_vector_int_t` to return the predecessors and inbound edges instead of an `igraph_vector_long_t`. - - The functions `igraph_get_all_shortest_paths()`, `igraph_get_all_shortest_paths_dijkstra()`, `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now return paths in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. - - The vector of parents in `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now use -1 to represent the starting vertex, and -2 for unreachable vertices. - - The `maps` parameters in `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. - - `igraph_get_stochastic()` now has an additional `weights` argument for edge weights. - - `igraph_get_stochastic_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. It also received an additional `weights` argument for edge weights. - - `igraph_girth()` now uses an `igraph_vector_int_t` for its `circle` parameter. - - `igraph_girth()` now uses `igraph_real_t` as the return value so we can return infinity for graphs with no cycles (instead of zero). - - The `cliques` parameters of type `igraph_vector_ptr_t` in `igraph_graphlets()`, `igraph_graphlets_candidate_basis()` and `igraph_graphlets_project()` were changed to an `igraph_vector_int_list_t`. - - `igraph_hrg_init()` and `igraph_hrg_resize()` now takes an `igraph_integer_t` as their size arguments instead of an `int`. - - `igraph_hrg_consensus()` now returns the parent vector in an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_hrg_create()` now takes a vector of probabilities corresponding to the internal nodes of the dendogram. It used to also take probabilities for the leaf nodes and then ignore them. - - `igraph_hrg_predict()` now uses an `igraph_vector_int_t` for its `edges` parameter. - - `igraph_hrg_sample()` now always samples a single graph only. Use `igraph_hrg_sample_many()` if you need more than one sample, and call `igraph_hrg_fit()` beforehand if you do not have a HRG model but only a single input graph. - - `igraph_hrg_size()` now returns an `igraph_integer_t` instead of an `int`. - - `igraph_incidence()` does not accept negative incidence counts any more. - - `igraph_incident()` now uses an `igraph_vector_int_t` for its `eids` parameter. - - The `res` parameter in `igraph_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. - - `igraph_induced_subgraph_map()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. - - `igraph_intersection()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. - - The `edgemaps` parameter of `igraph_intersection_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. - - `igraph_is_chordal()` now uses an `igraph_vector_int_t` for its `alpha`, `alpham1` and `fill_in` parameters. - - `igraph_is_graphical()` and `igraph_is_bigraphical()` now take degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. - - `igraph_is_matching()`, `igraph_is_maximal_matching()` and `igraph_maximum_bipartite_matching` now use an `igraph_vector_int_t` to return the matching instead of an `igraph_vector_long_t`. - - `igraph_is_mutual()` has an additional parameter which controls whether directed self-loops are considered mutual. - - The `vids` parameter for `igraph_isoclass_subgraph()` is now an `igraph_vector_int_t` instead of `igraph_vector_t`. - - `igraph_isomorphic_vf2()`, `igraph_get_isomorphisms_vf2_callback()` (which used to be called `igraph_isomorphic_function_vf2()`) and `igraph_isohandler_t` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. - - The `cliques` parameter of type `igraph_vector_ptr_t` in `igraph_largest_cliques()` was changed to an `igraph_vector_int_list_t`. - - The `res` parameters of type `igraph_vector_ptr_t` in `igraph_largest_independent_vertex_sets()` and `igraph_largest_weighted_cliques()` were changed to an `igraph_vector_int_list_t`. - - The dimension vector parameter for `igraph_square_lattice()` (used to be `igraph_lattice()`) is now an `igraph_vector_int_t` instead of `igraph_vector_t`. - - The maxiter parameter of `igraph_layout_bipartite()` is now an `igraph_integer_t` instead of `long int`. - - The fixed parameter of `igraph_layout_drl()` and `igraph_layout_drl_3d()` was removed as it has never been implemented properly. - - The width parameter of `igraph_layout_grid()` is now an `igraph_integer_t` instead of `long int`. - - The width and height parameters of `igraph_layout_grid_3d()` are now `igraph_integer_t` instead of `long int`. - - The dimension parameter of `igraph_layout_mds()` is now an `igraph_integer_t` instead of `long int`. - - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. - - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford_circular()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. - - The order parameter of `igraph_layout_star()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - The maxiter parameter of `igraph_layout_sugiyama()` is now an `igraph_integer_t` instead of `long int`. Also, the function now uses an `igraph_vector_int_t` for its `extd_to_orig_eids` parameter. - - The shifts parameter of `igraph_lcf_vector()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. - - `igraph_matrix_minmax()`, `igraph_matrix_which_minmax()`, `igraph_matrix_which_min()` and `igraph_matrix_which_max()` no longer return an error code. The return type is now `void`. These functions never fail. - - `igraph_maxflow()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. - - The `igraph_maxflow_stats_t` struct now contains `igraph_integer_t` values instead of `int` ones. - - The `res` parameters in `igraph_maximal_cliques()` and `igraph_maximal_cliques_subset()` are now of type `igraph_vector_int_list_t`. - - Callbacks used by `igraph_maximal_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. - - The `res` parameter in `igraph_maximal_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. - - `igraph_maximum_cardinality_search()` now uses an `igraph_vector_int_t` for its `alpha` and `alpham1` arguments. - - `igraph_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. - - `igraph_moran_process()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. - - Motif callbacks of type `igraph_motifs_handler_t` now take an `igraph_vector_int_t` with the vertex IDs instead of an `igraph_vector_t`, and use `igraph_integer_t` for the isoclass parameter. - - Motif functions now use `igraph_integer_t` instead of `int` for their `size` parameter. - - `igraph_neighborhood_size()` now uses an `igraph_vector_int_t` for its `res` parameter. - - The `res` parameter of `igraph_neighborhood()` is now an `igraph_vector_int_list_t`. - - `igraph_neighbors()` now uses an `igraph_vector_int_t` for its `neis` parameter. - - `igraph_permute_vertices()` now takes an `igraph_vector_int_t` as the permutation vector. - - `igraph_power_law_fit()` does not calculate the p-value automatically any more because the previous estimation method did not match the results from the original paper of Clauset, Shalizi and Newman (2009) and the implementation of the method outlined in the paper runs slower than the previous naive estimate. A separate function named `igraph_plfit_result_calculate_p_value()` is now provided for calculating the p-value. The automatic selection of the `x_min` cutoff also uses a different method than earlier versions. As a consequence, results might be slightly different if you used tests where the `x_min` cutoff was selected automatically. The new behaviour is now consistent with the defaults of the underlying `plfit` library. - - `igraph_preference_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. - - `igraph_random_walk()` now uses an `igraph_vector_int_t` for its results. Also, the function now takes both vertices and edges as parameters. It can return IDs of vertices and/or edges on the walk. The function now takes weights as a parameter to support weighted graphs. - - `igraph_random_edge_walk()` now uses an `igraph_vector_int_t` for its `edgewalk` parameter. - - `igraph_read_graph_dimacs_flow()` now uses an `igraph_vector_int_t` for its label parameter. - - `igraph_read_graph_graphml()` now uses `igraph_integer_t` for its `index` argument. - - `igraph_read_graph_pajek()` now creates a Boolean `type` attribute for bipartite graphs. Previously it created a numeric attribute. - - `igraph_realize_degree_sequence()` now uses an `igraph_vector_int_t` for its `outdeg` and `indeg` parameters. - - `igraph_reindex_membership()` now uses an `igraph_vector_int_t` for its `new_to_old` parameter. - - `igraph_rng_seed()` now requires an `igraph_uint_t` as its seed arguments. RNG implementations are free to use only the lower bits of the seed if they do not support 64-bit seeds. - - `igraph_rngtype_rand` (i.e. the RNG that is based on BSD `rand()`) was removed due to poor statistical properties that sometimes resulted in weird artifacts like all-even "random" numbers when igraph's usage patterns happened to line up with the shortcomings of the `rand()` generator in a certain way. - - `igraph_roulette_wheel_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. - - `igraph_similarity_dice_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. - - `igraph_similarity_jaccard_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. - - `igraph_simple_interconnected_islands_game()` does not generate multi-edges between islands any more. - - `igraph_sort_vertex_ids_by_degree()` and `igraph_topological_sorting()` now use an `igraph_vector_int_t` to return the vertex IDs instead of an `igraph_vector_t`. - - `igraph_spanning_tree()`, `igraph_minimum_spanning_tree()` and `igraph_random_spanning_tree()` now all use an `igraph_vector_int_t` to return the vector of edge IDs in the spanning tree instead of an `igraph_vector_t`. - - `igraph_sparsemat_cholsol()`, `igraph_sparsemat_lusol()`, `igraph_sparsemat_symbqr()` and `igraph_sparsemat_symblu()` now take an `igraph_integer_t` as their `order` parameter. - - `igraph_sparsemat_count_nonzero()` and `igraph_sparsemat_count_nonzerotol()` now return an `igraph_integer_t`. - - `igraph_sparsemat_is_symmetric()` now returns an error code and the result itself is provided in an output argument. - - The `values` argument of `igraph_sparsemat_transpose()` was removed; now the function always copies the values over to the transposed matrix. - - `igraph_spmatrix_t` and related functions were removed as they mostly duplicated functionality that was already present in `igraph_sparsemat_t`. Functions that used `igraph_spmatrix_t` in the library now use `igraph_sparsemat_t`. - - `igraph_stochastic_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. - - `igraph_st_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. - - `igraph_st_vertex_connectivity()` now ignores edges between source and target for `IGRAPH_VCONN_NEI_IGNORE` - - `igraph_strvector_get()` now returns strings in the return value, not in an output argument. - - `igraph_subcomponent()` now uses an `igraph_integer_t` for the seed vertex instead of an `igraph_real_t`. It also uses an `igraph_vector_int_t` to return the list of vertices in the same component as the seed vertex instead of an `igraph_vector_t`. - - `igraph_subisomorphic_vf2()`, `igraph_get_subisomorphisms_vf2_callback()` (which used to be called `igraph_subisomorphic_function_vf2()`) and `igraph_isomorphic_bliss()` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. - - The `maps` parameters in `igraph_subisomorphic_lad()`, `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. - - `igraph_subisomorphic_lad()` now uses an `igraph_vector_int_t` for its `map` parameter. Also, its `domains` parameter is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. - - `igraph_unfold_tree()` now uses an `igraph_vector_int_t` for its `vertex_index` and `roots` parameters. - - `igraph_union()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. - - The `edgemaps` parameter of `igraph_union_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. - - `igraph_vector_init_copy()` was refactored to take _another_ vector that the newly initialized vector should copy. The old array-based initialization function is now called `igraph_vector_init_array()`. - - `igraph_vector_ptr_init_copy()` was renamed to `igraph_vector_ptr_init_array()` for sake of consistency. - - `igraph_vs_vector()`, `igraph_vss_vector()` and `igraph_vs_vector_copy()` now all take an `igraph_vector_int_t` as the vector of vertex IDs, not an `igraph_vector_t`. Similarly, `igraph_vs_as_vector()` now returns the vector of matched vertex IDs in an `igraph_vector_int_t`, not an `igraph_vector_t`. - - The `res` parameter of `igraph_weighted_cliques()` is now an `igraph_vector_int_list_t`. - - `igraph_write_graph_dimacs_flow()` now uses `igraph_integer_t` for the source and target vertex index instead of a `long int`. - - `igraph_vector_*()`, `igraph_matrix_*()`, `igraph_stack_*()`, `igraph_array_*()` and several other generic igraph data types now use `igraph_integer_t` for indexing, _not_ `long int`. Please refer to the headers for the exact details; the list of affected functions is too large to include here. - - `igraph_vector_minmax()` and `igraph_vector_which_minmax()` no longer return an error code. The return type is now `void`. These functions never fail. - - `igraph_vector_order()` was removed; use `igraph_vector_int_pair_order()` instead. (The original function worked for vectors containing integers only). - - `igraph_vector_resize_min()` and `igraph_matrix_resize_min()` no longer return an error code (return type is now `void`). The vector or matrix is always left in a consistent state by these functions, with all data intact, even if releasing unused storage is not successful. - - `igraph_vector_qsort_ind()` and its variants now take an `igraph_order_t` enum instead of a boolean to denote whether the order should be ascending or descending. - - `igraph_weighted_adjacency()` now returns the weights in a separate vector instead of storing it in a vertex attribute. The reason is twofold: first, the previous solution worked only with the C attribute handler (not the ones from the higher-level interfaces), and second, it wasn't consistent with other igraph functions that use weights provided as separate arguments. - - The `loops` argument of `igraph_weighted_adjacency()` was converted to an `igraph_loops_t` for sake of consistency with `igraph_adjacency()` and `igraph_get_adjacency()`. - - `igraph_write_graph_gml()` takes an additional bitfield parameter controlling some aspects of writing the GML file. - - The `add_edges()` function in the attribute handler now takes an `igraph_vector_int_t` for its `edges` parameter instead of an `igraph_vector_t`. The `add_vertices()` function now takes an `igraph_integer_t` for the vertex count instead of a `long int`. The `combine_vertices()` and `combine_edges()` functions now take an `igraph_vector_ptr_t` containing vectors of type `igraph_vector_int_t` in their `merges` parameters. The `get_info()` function now uses `igraph_vector_int_t` to return the types of the graph, vertex and edge attribute types. The `permute_vertices()` and `permute_edges()` functions in the attribute handler tables now take an `igraph_vector_int_t` instead of an `igraph_vector_t` for the index vectors. These are relevant only to maintainers of higher level interfaces to igraph; they should update their attribute handlers accordingly. - - igraph functions that interface with external libraries such as BLAS or LAPACK may now fail if the underlying BLAS or LAPACK implementation cannot handle the size of input vectors or matrices (BLAS and LAPACK are usually limited to vectors whose size fits in an `int`). `igraph_blas_dgemv()` and `igraph_blas_dgemv_array()` thus now return an `igraph_error_t`, which may be set to `IGRAPH_EOVERFLOW` if the input vectors or matrices are too large. - - Functions that used an `igraph_vector_t` to represent cluster size and cluster membership now use an `igraph_vector_int_t` instead. These are: - - `igraph_connected_components()` (used to be `igraph_clusters()` in 0.9 and before) - - `igraph_community_eb_get_merges()` - - `igraph_community_edge_betweenness()` - - `igraph_community_fastgreedy()` - - `igraph_community_fluid_communities()` - - `igraph_community_infomap()` - - `igraph_community_label_propagation()` - - `igraph_community_leading_eigenvector()` - - `igraph_community_leiden()` - - `igraph_community_multilevel()` - - `igraph_community_optimal_modularity()` - - `igraph_community_spinglass()` - - `igraph_community_spinglass_single()` - - `igraph_community_to_membership()` - - `igraph_community_walktrap()` - - `igraph_compare_communities()` - - `igraph_le_community_to_membership()` - - `igraph_modularity()` - - `igraph_reindex_membership()` - - `igraph_split_join_distance()` - - `igraph_community_multilevel()` additionally uses a `igraph_matrix_int_t` instead of `igraph_matrix_t()` for its memberships parameter. - - `IGRAPH_TOTAL` was removed from the `igraph_neimode_t` enum; use the equivalent `IGRAPH_ALL` instead. - -### Added - - - A new integer type, `igraph_uint_t` has been added. This is the unsigned pair of `igraph_integer_t` and they are always consistent in size. - - A new container type, `igraph_vector_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of vectors. The type contains `igraph_vector_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). There is also a variant named `igraph_vector_int_list_t` for vectors of `igraph_vector_int_t` objects. - - A new container type, `igraph_matrix_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of matrices. The type contains `igraph_matrix_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). - - A new container type, `igraph_graph_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of graphs. The type contains `igraph_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). - - The vector container type, `igraph_vector_t`, has been extended with a new variant whose functions all start with `igraph_vector_fortran_int_...`. This vector container can be used for interfacing with Fortran code as it guarantees that the integers in the vector are compatible with Fortran integers. Note that `igraph_vector_int_t` is not suitable any more, as the elements of `igraph_vector_int_t` are of type `igraph_integer_t`, whose size may differ on 32-bit and 64-bit platforms, depending on how igraph was compiled. - - `igraph_adjlist_init_from_inclist()` to create an adjacency list from an already existing incidence list by resolving edge IDs to their corresponding endpoints. This function is useful for algorithms when both an adjacency and an incidence list is needed and they should be in the same order. - - `igraph_almost_equals()` and `igraph_cmp_epsilon()` to compare floating point numbers with a relative tolerance. - - `igraph_betweenness_subset()` and `igraph_edge_betweenness_subset()` calculates betweenness and edge betweenness scores using shortest paths between a subset of vertices only (#1711, thanks to @guyroznb) - - `igraph_blas_dgemm()` to multiply two matrices. - - `igraph_calloc()` and `igraph_realloc()` are now publicly exposed; these functions provide variants of `calloc()` and `realloc()` that can safely be deallocated within igraph functions. - - `igraph_circulant()` to create circulant graphs (#1856, thanks to @Gomango999). - - `igraph_complex_almost_equals()` to compare complex numbers with a relative tolerance. - - `igraph_eccentricity_dijkstra()` finds the longest weighted path length among all shortest paths between a set of vertices. - - `igraph_enter_safelocale()` and `igraph_exit_safelocale()` for temporarily setting the locale to C. Foreign format readers and writers require a locale which uses a decimal point instead of decimal comma. - - `igraph_es_all_between()` to create an edge selector that selects all edges between a pair of vertices. - - `igraph_full_multipartite()` generates full multipartite graphs (a generalization of bipartite graphs to multiple groups). - - `igraph_fundamental_cycles()` computes a fundamental cycle basis (experimental). - - `igraph_generalized_petersen()` to create generalized Petersen graphs (#1844, thanks to @alexsyou). - - `igraph_get_all_eids_between()` returns the IDs of all edges between a pair of vertices. - - `igraph_get_k_shortest_paths()` finds the k shortest paths between a source and a target vertex. - - `igraph_get_laplacian()` and `igraph_get_laplacian_sparse()` return the Laplacian matrix of the graph as a dense or sparse matrix, with various kinds of normalizations. They replace the now-deprecated `igraph_laplacian()` function. This makes the API consistent with `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()`. - - `igraph_get_widest_path()`, `igraph_get_widest_paths()`, `igraph_widest_path_widths_dijkstra()` and `igraph_widest_path_widths_floyd_warshall()` to find widest paths (#1893, thanks to @Gomango999). - - `igraph_graph_center()` finds the central vertices of the graph. The central vertices are the ones having a minimum eccentricity (PR #2084, thanks to @pradkrish). - - `igraph_graph_count()` returns the number of unlabelled graphs on a given number of vertices. It is meant to find the maximum isoclass value. - - `igraph_has_mutual()` checks if a directed graph has any mutual edges. - - `igraph_heap_clear()` and `igraph_heap_min_clear()` remove all elements from an `igraph_heap_t` or an `igraph_heap_min_t`, respectively. - - `igraph_invalidate_cache()` invalidates all cached graph properties, forcing their recomputation next time they are requested. This function should not be needed in everyday usage, but may be useful in debugging and benchmarking. - - `igraph_is_forest()` to check whether a graph is a forest (#1888, thanks to @rohitt28). - - `igraph_is_acyclic()` to check whether a graph is acyclic (#1945, thanks to @borsgeorgica). - - `igraph_is_perfect()` to check whether a graph is a perfect graph (#1730, thanks to @guyroznb). - - `igraph_hub_and_authority_scores()` calculates the hub and authority scores of a graph as a matching pair. - - `igraph_layout_umap()` and `igraph_layout_umap_3d()` to lay out a graph in 2D or 3D space using the UMAP dimensionality reduction algorithm. - - `igraph_local_scan_subset_ecount()` counts the number of edges in induced sugraphs from a subset of vertices. - - `igraph_matrix_view_from_vector()` allows interpreting the data stored in a vector as a matrix of the specified size. - - `igraph_minimum_cycle_basis()` computes an unweighted minimum cycle basis (experimental). - - `igraph_pseudo_diameter()` and `igraph_pseudo_diameter_dijkstra()` to determine a lower bound for the diameter of a graph (unweighted or weighted). - - `igraph_regular_tree()` creates a regular tree where all internal vertices have the same total degree. - - `igraph_rngtype_pcg32` and `igraph_rngtype_pcg64` implement 32-bit and 64-bit variants of the PCG random number generator. - - `igraph_rng_get_pois()` generates random variates from the Poisson distribution. - - `igraph_roots_for_tree_layout()` computes a set of roots suitable for a nice tree layout. - - `igraph_spanner()` calculates a spanner of a graph with a given stretch factor (#1752, thanks to @guyroznb) - - `igraph_sparse_adjacency()` and `igraph_sparse_weighted_adjacency()` constructs graphs from (weighted) sparse matrices. - - `igraph_sparsemat_get()` to retrieve a single element of a sparse matrix. - - `igraph_sparsemat_normalize_rows()` and `igraph_sparsemat_normalize_cols()` to normalize sparse matrices row-wise or column-wise. - - `igraph_stack_capacity()` to query the capacity of a stack. - - `igraph_strvector_capacity()` returns the maximum number of strings that can be stored in a string vector without reallocating the memory block holding the pointers to the individual strings. - - `igraph_strvector_merge()` moves all strings from one string vectors to the end of another without re-allocating them. - - `igraph_strvector_push_back_len()` adds a new string to the end of a string vector and allows the user to specify the length of the string being added. - - `igraph_strvector_reserve()` reserves space for a given number of string pointers in a string vector. - - `igraph_symmetric_tree()` to create a tree with the specified number of branches at each level (#1859, thanks to @YuliYudith and @DoruntinaM). - - `igraph_trussness()` calculates the trussness of each edge in the graph (#1034, thanks to @alexperrone) - - `igraph_turan()` generates Turán graphs (#2088, thanks to @pradkrish) - - `igraph_vector_all_almost_e()`, `igraph_vector_complex_all_almost_e()`, `igraph_matrix_all_almost_e()`, `igraph_matrix_complex_all_almost_e()` for elementwise comparisons of floating point vector and matrices with a relative tolerance. - - `igraph_vector_complex_zapsmall()` and `igraph_matrix_complex_zapsmall()` for replacing small components of complex vector or matrix elements with exact zeros. - - `igraph_vector_lex_cmp_untyped()` and `igraph_vector_colex_cmp_untyped()` for lexicographic and colexicographic comparison of vectors, similarly to `igraph_vector_lex_cmp()` and `igraph_vector_colex_cmp()`. The difference between the two variants is that the untyped versions declare the vectors as `const void*`, making the functions suitable as comparators for `qsort()`. - - `igraph_vector_permute()` functions to permute a vector based on an index vector. - - `igraph_vector_ptr_sort_ind()` to obtain an index vector that would sort a vector of pointers based on some comparison function. - - `igraph_vector_range()` to fill an existing vector with a range of increasing numbers. - - `igraph_vector_remove_fast()` functions to remove an item from a vector by swapping it with the last element and then popping it off. It allows one to remove an item from a vector in constant time if the order of items does not matter. - - `igraph_vertex_path_from_edge_path()` converts a sequence of edge IDs representing a path to an equivalent sequence of vertex IDs that represent the vertices the path travelled through. - - `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` creates vertex and edge sequences from C-style intervals (closed from the left, open from the right). - - `igraph_wheel()` to create a wheel graph (#1938, thanks to @kwofach). - -### Removed - - - `igraph_adjlist_remove_duplicate()`, `igraph_betweenness_estimate()`, `igraph_closeness_estimate()`, `igraph_edge_betweenness_estimate()`, `igraph_inclist_remove_duplicate()`, `igraph_is_degree_sequence()` and `igraph_is_graphical_degree_sequence()` were deprecated earlier in 0.9.0 and are now removed in this release. - - `igraph_dnorm()`, `igraph_strvector_move_interval()`, `igraph_strvector_permdelete()` and `igraph_strvector_remove_negidx()` were removed. These are not breaking changes as the functions were never documented, they were only exposed from one of the headers. - - `igraph_eigen_laplacian()`, `igraph_es_fromto()` and `igraph_maximum_matching()` were removed. These are not breaking changes either as the functions were never implemented, they returned an error code unconditionally. - -### Changed - - - `igraph_degree_sequence_game()` now supports an additional method, `IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE`, an edge-switching MCMC sampler. - - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now count loop edges _twice_ in undirected graphs when using `IGRAPH_GET_ADJACENCY_BOTH`. This is to ensure consistency with `IGRAPH_GET_ADJACENCY_UPPER` and `IGRAPH_GET_ADJACENCY_LOWER` such that the sum of the upper and the lower triangle matrix is equal to the full adjacency matrix even in the presence of loop edges. - - `igraph_matrix_print()` and `igraph_matrix_fprint()` functions now align columns when priting. - - `igraph_read_graph_gml()` now supports graph attributes (in addition to vertex and edge attributes). - - `igraph_read_graph_gml()` now uses NaN as the default numerical attribute values instead of 0. - - The Pajek parser in `igraph_read_graph_pajek()` is now less strict and accepts more files. - - `igraph_ring()` no longer simplifies its result when generating a one- or two-vertex graph. The one-cycle has a self-loop and the undirected two-cycle has parallel edges. - - `igraph_vector_view()` now allows `data` to be `NULL` in the special case when `length == 0`. - - `igraph_version()` no longer returns an error code. - - `igraph_write_graph_gml()` uses the `creator` parameter in a different way: the supplied string is now written into the Creator line as-is instead of being appended to a default value. - - `igraph_write_graph_gml()` skips writing NaN values. These two changes ensure consistent round-tripping. - - `igraph_write_graph_gml()` and `igraph_read_graph_gml()` now have limited support for entity encoding. - - `igraph_write_graph_ncol()` now preserves the edge ordering of the graph when writing an NCOL file. - - igraph functions that take an ARPACK options object now also accept `NULL` in place of an options object, and they will fall back to using a default object provided by `igraph_arpack_options_get_default()`. - - Foreign format readers now present more informative error messages. - - The default tolerance of the zapsmall functions is now `eps^(2/3)` instead of `eps^(1/2)` where eps is the machine epsilon of `igraph_real_t`. - - It is now possible to override the uniform integer and the Poisson samplers in the random number generator interface. - -### Fixed - - - When an error occurs during parsing DL, GML, NCOL, LGL or Pajek files, line numbers are now reported correctly. - - The GraphML parser does not print to stderr any more in case of encoding errors and other error conditions originating from the underlying `libxml2` library. - - The GraphML parser would omit some edges and vertices when reading files with custom attribute types, such as those produced by yEd. This is now corrected. - - The GML parser no longer mixes up Inf and NaN and -Inf now works. - - The GML parser now supports nodes with no id field. - - The GML parser now performs more stringent checks on the input file, such as verifying that `id`, `source`, `target` and `directed` fields are not duplicated. - - The core data structures (vector, etc.) have overflow checks now. - - Deterministic graph generators, as well as most random ones, have overflow checks now. - - Graphs no longer lose all their attributes after calling `igraph_contract_vertices()`. - - `igraph_hrg_init()` does not throw an assertion error anymore for zero vertices. - - `igraph_matrix_complex_create()` and `igraph_matrix_complex_create_polar()` now set their sizes correctly. - - `igraph_random_walk()` took one fewer steps than specified. - - `igraph_sparsemat_getelements_sorted()` did not sort the elements for triplet matrices correctly; this is fixed now. - - `igraph_write_graph_gml()` no longer produces corrupt output when some string attribute values contain `"` characters. - -### Deprecated - - - `igraph_clusters()` has been renamed to `igraph_connected_components()`; the old name is deprecated and will be removed in 0.11. - - `igraph_complex_eq_tol()` is now deprecated in favour of `igraph_complex_almost_equals()`. - - `igraph_get_sparsemat()` is deprecated in favour of `igraph_get_adjacency_sparse()`, and will be removed in 0.11. Note that `igraph_get_adjacency_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_sparsemat()` which takes an uninitialized one. - - `igraph_get_stochastic_sparsemat()` is deprecated in favour of `igraph_get_stochastic_sparse()`, and will be removed in 0.11. Note that `igraph_get_stochastic_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_stochastic_sparsemat()`, which takes an uninitialized one. - - `igraph_isomorphic_34()` has been deprecated in favour of `igraph_isomorphic()`. Note that `igraph_isomorphic()` calls an optimized version for directed graphs of size 3 and 4, and undirected graphs with 3-6 vertices, so there is no need for a separate function. - - `igraph_laplacian()` is now deprecated; use `igraph_get_laplacian()` or `igraph_get_laplacian_sparse()` depending on whether you need a dense or a sparse matrix. - - `igraph_lattice()` has been renamed to `igraph_square_lattice()` to indicate that this function generates square lattices only. The old name is deprecated and will either be removed in 0.11 or will be changed to become a generic lattice generator that also supports other types of lattices. - - `igraph_local_scan_neighborhood_ecount()` is now deprecated in favour of `igraph_local_scan_subset_ecount()`. - - `igraph_matrix_all_e_tol()` is now deprecated in favour of `igraph_matrix_all_almost_e()`. - - `igraph_matrix_copy()` is now deprecated; use `igraph_matrix_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target matrix. The old name will be removed in 0.11. - - `igraph_matrix_e()` and `igraph_matrix_e_ptr()` have been renamed to `igraph_matrix_get()` and `igraph_matrix_get_ptr()`. The old names are deprecated and will be removed in 0.11. -- `igraph_random_edge_walk()` has been deprecated by `igraph_random_walk()` to support edges and/or vertices for the random walk in a single function. It will be removed in 0.11. - - `igraph_read_graph_dimacs()` has been renamed to `igraph_read_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS reader in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. - - `igraph_shortest_paths()` and related functions were renamed to `igraph_distances()`; the old name was unfortunate because these functions calculated _path lengths_ only and not the paths themselves. The old names are deprecated and will be removed in 0.11. - - `igraph_sparsemat_copy()`, `igraph_sparsemat_diag()` and `igraph_sparsemat_eye()` have been renamed to `igraph_sparsemat_init_copy()`, `igraph_sparsemat_init_diag()` and `igraph_sparsemat_init_eye()` to indicate that they _initialize_ a new sparse matrix. The old names are deprecated and will be removed in 0.11. - - `igraph_strvector_add()` has been renamed to `igraph_strvector_push_back()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. - - `igraph_strvector_copy()` has been renamed to `igraph_strvector_init_copy()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. - - `igraph_strvector_get()` now returns a `const char*` and not a `char*` to indicate that you are not supposed to modify the string in the vector directly. If you do want to modify it and you are aware of the implications (i.e. the new string must not be longer than the original one), you can cast away the constness of the return value before modifying it. - - `igraph_strvector_set2()` has been renamed to `igraph_strvector_set_len()`; the old name is deprecated and will be removed in 0.11. - - `igraph_tree()` has been renamed to `igraph_kary_tree()`; the old name is deprecated and will be removed in 0.11. - - `igraph_vector_e()` and `igraph_vector_e_ptr()` have been renamed to `igraph_vector_get()` and `igraph_vector_get_ptr()`. The old names are deprecated and will be removed in 0.11. - - `igraph_vector_e_tol()` is now deprecated in favour of `igraph_vector_all_almost_e()`. - - `igraph_vector_copy()` is now deprecated; use `igraph_vector_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target vector. The old name will be removed in 0.11. - - `igraph_vector_init_seq()` is now deprecated in favour of `igraph_vector_init_range()`, which uses C-style intervals (closed from the left and open from the right). - - `igraph_vs_seq()`, `igraph_vss_seq()`, `igraph_es_seq()` and `igraph_ess_seq()` are now deprecated in favour of `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` because these use C-style intervals (closed from the left, open from the right). - - `igraph_write_graph_dimacs()` has been renamed to `igraph_write_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS writer in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. - - `igraph_zeroin()` is deprecated and will be removed in 0.11, with no replacement. The function is not graph-related and was never part of the public API. - - The macros `igraph_Calloc`, `igraph_Realloc` and `igraph_Free` have been deprecated in favour of `IGRAPH_CALLOC`, `IGRAPH_REALLOC` and `IGRAPH_FREE` to simplify the API. The deprecated variants will be removed in 0.11. - -### Other - - - Documentation improvements. - - Support for Intel's LLVM-based compiler. - ## [0.9.10] - 2022-09-02 ### Added @@ -596,7 +65,6 @@ Some of the highlights are: ### Other - - Greatly improved error reporting from foregin format parsers. - Documentation improvements. ## [0.9.8] - 2022-04-08 @@ -647,6 +115,8 @@ Some of the highlights are: ## [0.9.6] - 2022-01-05 +### Changed + - Isomorphism class functions (`igraph_isoclass()`, `igraph_isoclass_subgraph()`, `igraph_isoclass_create`) and motif finder functions (`igraph_motifs_randesu()`, `igraph_motifs_randesu_estimate()`, `igraph_motifs_randesu_callback()`) now @@ -1162,12 +632,7 @@ Some of the highlights are: - Provide proper support for Windows, using `__declspec(dllexport)` and `__declspec(dllimport)` for `DLL`s and static usage by using `#define IGRAPH_STATIC 1`. - Provided integer versions of `dqueue` and `stack` data types. -[master]: https://github.com/igraph/igraph/compare/0.10.4..master -[0.10.4]: https://github.com/igraph/igraph/compare/0.10.3..0.10.4 -[0.10.3]: https://github.com/igraph/igraph/compare/0.10.2..0.10.3 -[0.10.2]: https://github.com/igraph/igraph/compare/0.10.1..0.10.2 -[0.10.1]: https://github.com/igraph/igraph/compare/0.10.0..0.10.1 -[0.10.0]: https://github.com/igraph/igraph/compare/0.9.10..0.10.0 +[Unreleased]: https://github.com/igraph/igraph/compare/0.9.10..HEAD [0.9.10]: https://github.com/igraph/igraph/compare/0.9.9...0.9.10 [0.9.9]: https://github.com/igraph/igraph/compare/0.9.8...0.9.9 [0.9.8]: https://github.com/igraph/igraph/compare/0.9.7...0.9.8 diff --git a/src/vendor/cigraph/CMakeLists.txt b/src/vendor/cigraph/CMakeLists.txt index faef69dcdd1..26a03a1c29e 100644 --- a/src/vendor/cigraph/CMakeLists.txt +++ b/src/vendor/cigraph/CMakeLists.txt @@ -1,10 +1,7 @@ -# Minimum CMake that we require is 3.18. -# Some of the recent features we use: -# * --ignore-eol when comparing unit test results with expected outcomes (3.14) -# * CROSSCOMPILING_EMULATOR can be a semicolon-separated list to pass arguments (3.15) -# * SKIP_REGULAR_EXPRESSION to handle skipped tests properly (3.16) -# * CheckLinkerFlag for HAVE_NEW_DTAGS test (3.18) -cmake_minimum_required(VERSION 3.18...3.25) +# Minimum CMake that we require is 3.16 because we use --ignore-eol when +# comparing unit test results with expected outcomes (added in 3.14) and we +# also use SKIP_REGULAR_EXPRESSION to handle skipped tests properly +cmake_minimum_required(VERSION 3.16) # Add etc/cmake to CMake's search path so we can put our private stuff there list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/etc/cmake) @@ -41,9 +38,6 @@ project( # Include some compiler-related helpers and set global compiler options include(compilers) -# Detect is certain attributes are supported by the compiler -include(attribute_support) - # Set default symbol visibility to hidden set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) @@ -76,41 +70,26 @@ find_dependencies() # Run compile-time checks, generate config.h and igraph_threading.h include(CheckSymbolExists) -include(CheckIncludeFiles) -include(CMakePushCheckState) # First we check for some functions and symbols -cmake_push_check_state() +set(CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) if(NEED_LINKING_AGAINST_LIBM) list(APPEND CMAKE_REQUIRED_LIBRARIES m) endif() +check_symbol_exists(expm1 math.h HAVE_EXPM1) +check_symbol_exists(fmin math.h HAVE_FMIN) +check_symbol_exists(finite math.h HAVE_FINITE) +check_symbol_exists(isfinite math.h HAVE_ISFINITE) +check_symbol_exists(log2 math.h HAVE_LOG2) +check_symbol_exists(log1p math.h HAVE_LOG1P) +check_symbol_exists(rint math.h HAVE_RINT) +check_symbol_exists(rintf math.h HAVE_RINTF) +check_symbol_exists(round math.h HAVE_ROUND) +check_symbol_exists(stpcpy string.h HAVE_STPCPY) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) -check_symbol_exists(strncasecmp strings.h HAVE_STRNCASECMP) -check_symbol_exists(_stricmp string.h HAVE__STRICMP) -check_symbol_exists(_strnicmp string.h HAVE__STRNICMP) check_symbol_exists(strdup string.h HAVE_STRDUP) -check_symbol_exists(strndup string.h HAVE_STRNDUP) -check_include_files(xlocale.h HAVE_XLOCALE) -if(HAVE_XLOCALE) - # On BSD, uselocale() is in xlocale.h instead of locale.h. - # Some systems provide xlocale.h, but uselocale() is still in locale.h, - # thus we try both. - check_symbol_exists(uselocale "xlocale.h;locale.h" HAVE_USELOCALE) -else() - check_symbol_exists(uselocale locale.h HAVE_USELOCALE) -endif() -check_symbol_exists(_configthreadlocale locale.h HAVE__CONFIGTHREADLOCALE) -cmake_pop_check_state() - -# Check for 128-bit integer multiplication support, floating-point endianness -# and support for built-in overflow detection. -include(ieee754_endianness) -include(uint128_support) -include(safe_math_support) - -if(NOT HAVE_USELOCALE AND NOT HAVE__CONFIGTHREADLOCALE) - message(WARNING "igraph cannot set per-thread locale on this platform. igraph_enter_safelocale() and igraph_exit_safelocale() will not be safe to use in multithreaded programs.") -endif() +check_symbol_exists(_stricmp string.h HAVE__STRICMP) +set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_SAVE}) # Check for code coverage support option(IGRAPH_ENABLE_CODE_COVERAGE "Enable code coverage calculation" OFF) @@ -122,9 +101,9 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND IGRAPH_ENABLE_CODE_COVERAGE) EXECUTABLE "${CMAKE_COMMAND}" "--build" "${PROJECT_BINARY_DIR}" "--target" "check" # Generated files are excluded; apparently the CodeCoverage script has some # problems with them. Yes, the exclusion is correct, it refers to a nonexistent - # directory that somehow gets into the coverage results. /Applications and - # /Library/Developer are for macOS -- they exclude files from the macOS SDK. - EXCLUDE "io/*.l" "src/io/parsers/*" "io/parsers/*" "/Applications/Xcode*" "/Library/Developer/*" "examples/*" "interfaces/*" "tests/*" "vendor/pcg/*" + # directory that somehow gets into the coverage results. /Applications is for + # macOS -- it excludes files from the macOS SDK. + EXCLUDE "io/*.l" "io/parsers/*" "/Applications/Xcode*" "examples/*" "tests/*" ) endif() @@ -133,10 +112,6 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h ) -configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_config.h.in - ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_config.h -) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_threading.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_threading.h @@ -146,6 +121,10 @@ configure_file( # included as a sub-project in another CMake project if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) + configure_file( + ${PROJECT_SOURCE_DIR}/etc/cmake/CTestCustom.cmake.in + ${PROJECT_BINARY_DIR}/CTestCustom.cmake + ) endif() # Traverse subdirectories. vendor/ should come first because code in diff --git a/src/vendor/cigraph/CONTRIBUTING.md b/src/vendor/cigraph/CONTRIBUTING.md index 1e5ed69a161..f3c61e5374e 100644 --- a/src/vendor/cigraph/CONTRIBUTING.md +++ b/src/vendor/cigraph/CONTRIBUTING.md @@ -16,7 +16,7 @@ experienced with C, you can contribute in a number of ways: the issue stating that this is still a problem in version X. - Some [issues point out problems with the documentation](https://github.com/igraph/igraph/labels/documentation); perhaps you could help correct these? - - Some [issues require clarifying a mathematical problem, or some literature research](https://github.com/igraph/igraph/labels/theory), + - Some [issues require clarifying a mathematical problem, or some literature research](https://github.com/igraph/igraph/labels/theory), before any programming can begin. Can you contribute through your theoretical expertise? - Looking to contribute code? Take a look at some [good first issues](https://github.com/igraph/igraph/labels/good%20first%20issue). @@ -157,7 +157,7 @@ Follow the following steps if you would like to make a new pull request: ```bash git pull [--rebase] upstream ``` - + Rebasing is preferable over merging as you do not need to deal with merge conflicts; however, if you already have many commits, merging the upstream development branch may be faster. diff --git a/src/vendor/cigraph/CONTRIBUTORS.md b/src/vendor/cigraph/CONTRIBUTORS.md index 2e86e405fe5..abefae6a0a0 100644 --- a/src/vendor/cigraph/CONTRIBUTORS.md +++ b/src/vendor/cigraph/CONTRIBUTORS.md @@ -58,25 +58,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Aman Verma

💻
guy rozenberg

💻
Artem V L

💻 -
Kateřina Č.

💻 - - -
valdaarhun

💻 -
YuliYudith

💻 -
alexsyou

💻 -
Rohit Tawde

💻 -
alexperrone

💻 -
Georgica Bors

💻 -
MEET PATEL

💻 - - -
kwofach

💻 -
Kevin Zhu

💻 -
Pradeep Krishnamurthy

💻 -
flange-ipb

💻 -
Juan Julián Merelo Guervós

💻 -
Radoslav Fulek

💻 -
professorcode1

💻 diff --git a/src/vendor/cigraph/ONEWS b/src/vendor/cigraph/ONEWS index 4a262caf0c7..c6bdcc42f74 100644 --- a/src/vendor/cigraph/ONEWS +++ b/src/vendor/cigraph/ONEWS @@ -796,7 +796,7 @@ New in the C layer - Some stochastic test results are ignored (for spinglass community detection, some Erdos-Renyi generator tests) - Weighted shortest paths, Dijkstra's algorithm. -- The unweighted shortest path routine returns 'Inf' for unreachable +- The unweigthed shortest path routine returns 'Inf' for unreachable vertices. - New function, igraph_adjlist can create igraph graphs from adjacency lists. @@ -1326,7 +1326,7 @@ Changes in the R interface - graph.edgelist to create a graph from an edge list, can also handle edge lists with symbolic names - get.edgelist has now a 'names' argument and can return symbolic - vertex names instead of vertex IDs, by default id uses the 'name' + vertex names instead of vertex ids, by default id uses the 'name' vertex attribute is returned - printing graphs on screen also prints symbolic symbolic names (the 'name' attribute if present) diff --git a/src/vendor/cigraph/README.md b/src/vendor/cigraph/README.md index ba5b88cec3e..f9fa3f13076 100644 --- a/src/vendor/cigraph/README.md +++ b/src/vendor/cigraph/README.md @@ -6,10 +6,12 @@ The igraph library ------------------ -igraph is a C library for complex network analysis and graph theory, with -emphasis on efficiency, portability and ease of use. +igraph is a C library for creating, manipulating and analysing graphs. +It is intended to be as powerful (i.e. fast) as possible to enable +working with large graphs. -See https://igraph.org for installation instructions and documentation. +See https://igraph.org for installation instructions +and documentation. igraph can also be used from: diff --git a/src/vendor/cigraph/appveyor.yml b/src/vendor/cigraph/appveyor.yml index d7d867a66d6..28cc611c7af 100644 --- a/src/vendor/cigraph/appveyor.yml +++ b/src/vendor/cigraph/appveyor.yml @@ -51,7 +51,7 @@ for: -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DIGRAPH_GRAPHML_SUPPORT=1 - -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 + -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 - matrix: @@ -67,7 +67,7 @@ for: -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBUILD_SHARED_LIBS=1 -DIGRAPH_GRAPHML_SUPPORT=1 - -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 + -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 configuration: Release diff --git a/src/vendor/cigraph/azure-pipelines.yml b/src/vendor/cigraph/azure-pipelines.yml index 15bed1aaf61..07c12de37da 100644 --- a/src/vendor/cigraph/azure-pipelines.yml +++ b/src/vendor/cigraph/azure-pipelines.yml @@ -4,31 +4,10 @@ pool: variables: CMAKE_GENERATOR: Ninja CCACHE_DIR: $(Pipeline.Workspace)/ccache - CCACHE_MAXSIZE: 256M - ASAN_OPTIONS: detect_stack_use_after_return=1:color=always - UBSAN_OTIONS: print_stacktrace=1:color=always OMP_NUM_THREADS: 1 jobs: - # In this test we install and generate locales so that igraph_enter/exit_safelocale() can be tested - job: linux_static_vendored - steps: - - script: | - sudo apt-get update - sudo apt-get install ninja-build ccache language-pack-de -y - displayName: Install dependencies - - - script: | - sudo locale-gen de_DE - sudo update-locale - displayName: Generate locales - - - template: .azure/build.yml - parameters: - build_type: Debug - extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero"' - - - job: linux_static_vendored_32 steps: - script: sudo apt-get install ninja-build ccache -y displayName: Install dependencies @@ -36,7 +15,7 @@ jobs: - template: .azure/build.yml parameters: build_type: Debug - extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DIGRAPH_INTEGER_SIZE=32' + extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og" -DCMAKE_CXX_FLAGS="-Og"' - job: linux_static_external steps: @@ -48,6 +27,7 @@ jobs: int_blas: false int_lapack: false int_arpack: false + int_cxsparse: false int_gmp: false int_glpk: false extra_cmake_args: '-DBLA_VENDOR=OpenBLAS' @@ -71,27 +51,12 @@ jobs: int_blas: false int_lapack: false int_arpack: false + int_cxsparse: false int_gmp: false int_glpk: false extra_cmake_args: '-DBLA_VENDOR=OpenBLAS' build_shared: true - - job: linux_clang_15 - pool: - vmImage: 'ubuntu-22.04' - steps: - - script: | - sudo apt-get install ninja-build ccache -y - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 15 - displayName: Install dependencies - - - template: .azure/build.yml - parameters: - build_type: Debug - extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_C_COMPILER=clang-15 -DCMAKE_CXX_COMPILER=clang++-15' - - job: linux_x87 steps: - script: sudo apt-get install ninja-build ccache -y @@ -105,12 +70,11 @@ jobs: steps: # https://github.com/alpinelinux/alpine-chroot-install - bash: | - set -e - wget https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.14.0/alpine-chroot-install && echo 'ccbf65f85cdc351851f8ad025bb3e65bae4d5b06 alpine-chroot-install' | sha1sum -c || exit 1 + wget https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.13.2/alpine-chroot-install && echo '60c7e0b5d82e21d1a549fc9a46ba3b36688c09dc alpine-chroot-install' | sha1sum -c || exit 1 alpine() { /alpine/enter-chroot -u "$USER" "$@"; } sudo sh alpine-chroot-install -p 'build-base linux-headers git cmake ninja bison flex gmp-dev' mkdir build && cd build - alpine cmake .. -GNinja -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_ENABLE_TLS=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 + alpine cmake .. -GNinja -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_ENABLE_TLS=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 alpine cmake --build . --target build_tests alpine ctest -j `nproc` --output-on-failure @@ -119,6 +83,7 @@ jobs: vmImage: macos-latest steps: - script: | + brew update brew install ninja ccache displayName: Install dependencies diff --git a/src/vendor/cigraph/codecov.yml b/src/vendor/cigraph/codecov.yml index f4a2e6f637d..0f21612efba 100644 --- a/src/vendor/cigraph/codecov.yml +++ b/src/vendor/cigraph/codecov.yml @@ -28,4 +28,3 @@ comment: ignore: - "tests" - "examples" - - "vendor/pcg" diff --git a/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in b/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in new file mode 100644 index 00000000000..004e01545fc --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in @@ -0,0 +1,9 @@ +# Ask CTest to run the build_tests target before running the tests. This can be +# turned off with an environment variable, which is useful in cases when you +# are working on unit tests for a long time and you are manually rebuilding the +# ones affected by your modifications, but you _don't_ want to wait for CTest +# to rebuild everything +if(NOT DEFINED ENV{IGRAPH_CTEST_MANUAL_BUILD}) + message(STATUS "Auto rebuild tests") + set(CTEST_CUSTOM_PRE_TEST "@CMAKE_COMMAND@ --build @PROJECT_BINARY_DIR@ --target build_tests") +endif() diff --git a/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake b/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake index 5a72791091e..1a97d3c9bdc 100644 --- a/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake +++ b/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake @@ -1,9 +1,7 @@ include(CheckCSourceCompiles) -include(CMakePushCheckState) macro(check_tls_support VAR) if(NOT DEFINED "${VAR}") - cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) check_c_source_compiles(" @@ -31,6 +29,6 @@ macro(check_tls_support VAR) set(${VAR} "" CACHE INTERNAL "Thread-local storage keyword in compiler") endif() endif() - cmake_pop_check_state() + set(CMAKE_REQUIRED_QUIET 0) endif() endmacro() diff --git a/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake b/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake new file mode 100644 index 00000000000..13ee090c748 --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake @@ -0,0 +1,75 @@ +# Retrieved from https://github.com/microsoft/vcpkg/blob/3b433e5081f35a32331492d98a8b0c1c2477048e/ports/suitesparse/FindCXSparse.cmake +# +# Distributed under the OSI-approved BSD 3-Clause License. +# +#.rst: +# FindCXSparse +# -------- +# +# Find the CXSparse library +# +# Result Variables +# ^^^^^^^^^^^^^^^^ +# +# The following variables will be defined: +# +# ``CXSparse_FOUND`` +# True if CXSparse found on the local system +# +# ``CXSPARSE_FOUND`` +# True if CXSparse found on the local system +# +# ``CXSparse_INCLUDE_DIRS`` +# Location of CXSparse header files +# +# ``CXSPARSE_INCLUDE_DIRS`` +# Location of CXSparse header files +# +# ``CXSparse_LIBRARIES`` +# List of the CXSparse libraries found +# +# ``CXSPARSE_LIBRARIES`` +# List of the CXSparse libraries found +# +# + +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +include(${CMAKE_ROOT}/Modules/SelectLibraryConfigurations.cmake) + +find_path(CXSPARSE_INCLUDE_DIR NAMES cs.h PATH_SUFFIXES suitesparse) + +find_library(CXSPARSE_LIBRARY_RELEASE NAMES cxsparse libcxsparse) +find_library(CXSPARSE_LIBRARY_DEBUG NAMES cxsparsed libcxsparsed) +select_library_configurations(CXSPARSE) + +if(CXSPARSE_INCLUDE_DIR) + set(CXSPARSE_VERSION_FILE ${CXSPARSE_INCLUDE_DIR}/cs.h) + file(READ ${CXSPARSE_INCLUDE_DIR}/cs.h CXSPARSE_VERSION_FILE_CONTENTS) + + string(REGEX MATCH "#define CS_VER [0-9]+" + CXSPARSE_MAIN_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define CS_VER ([0-9]+)" "\\1" + CXSPARSE_MAIN_VERSION "${CXSPARSE_MAIN_VERSION}") + + string(REGEX MATCH "#define CS_SUBVER [0-9]+" + CXSPARSE_SUB_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define CS_SUBVER ([0-9]+)" "\\1" + CXSPARSE_SUB_VERSION "${CXSPARSE_SUB_VERSION}") + + string(REGEX MATCH "#define CS_SUBSUB [0-9]+" + CXSPARSE_SUBSUB_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") + string(REGEX REPLACE "#define CS_SUBSUB ([0-9]+)" "\\1" + CXSPARSE_SUBSUB_VERSION "${CXSPARSE_SUBSUB_VERSION}") + + set(CXSPARSE_VERSION "${CXSPARSE_MAIN_VERSION}.${CXSPARSE_SUB_VERSION}.${CXSPARSE_SUBSUB_VERSION}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CXSparse + REQUIRED_VARS CXSPARSE_INCLUDE_DIR CXSPARSE_LIBRARIES + VERSION_VAR CXSPARSE_VERSION) + +set(CXSPARSE_FOUND ${CXSparse_FOUND}) +set(CXSPARSE_INCLUDE_DIRS ${CXSPARSE_INCLUDE_DIR}) +set(CXSparse_INCLUDE_DIRS ${CXSPARSE_INCLUDE_DIR}) +set(CXSparse_LIBRARIES ${CXSPARSE_LIBRARIES}) diff --git a/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake b/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake index c157d7579cd..503f15ea273 100644 --- a/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake +++ b/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake @@ -14,16 +14,16 @@ function(AssureOutOfSourceBuilds) message("##########################################################################") message("# igraph should not be configured & built in the igraph source directory") message("# You must run cmake in a build directory.") - message("#") - message("# Example:") + message("#") + message("# Example:") message("# mkdir build; cd build; cmake ..; make") message("#") message("# NOTE: Given that you already tried to make an in-source build") message("# CMake have already created several files & directories") - message("# in your source tree. If you are using git, run 'git clean -dfx'") - message("# to start from scratch. If you don't have git, remove") - message("# CMakeCache.txt and the CMakeFiles/ folder from the top of") - message("# the source tree.") + message("# in your source tree. If you are using git, run 'git clean -dfx'") + message("# to start from scratch. If you don't have git, remove") + message("# CMakeCache.txt and the CMakeFiles/ folder from the top of") + message("# the source tree.") message("#") message("##########################################################################") message("") diff --git a/src/vendor/cigraph/etc/cmake/attribute_support.cmake b/src/vendor/cigraph/etc/cmake/attribute_support.cmake deleted file mode 100644 index 4b1d392f683..00000000000 --- a/src/vendor/cigraph/etc/cmake/attribute_support.cmake +++ /dev/null @@ -1,27 +0,0 @@ - -# Detect if certain attributes are supported by the compiler -# The result will be used to set macros in include/igraph_config.h - -# GCC-style enum value deprecation - -include(CheckCSourceCompiles) -include(CMakePushCheckState) - -# Only check with Clang and GCC as we assume that the -Werror option is supported -# For other compilers, assume that the attribute is unsupported. -if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") - cmake_push_check_state() - # Require compiling with no warning: - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") - check_c_source_compiles( - "enum { A __attribute__ ((deprecated)) = 0 }; int main(void) { return 0; }" - COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR - ) - cmake_pop_check_state() -else() - set(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR FALSE) -endif() - -if(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR) - set(IGRAPH_DEPRECATED_ENUMVAL "__attribute__ ((deprecated))") -endif() diff --git a/src/vendor/cigraph/etc/cmake/compilers.cmake b/src/vendor/cigraph/etc/cmake/compilers.cmake index 1c2f9df8da2..336ee380d6b 100644 --- a/src/vendor/cigraph/etc/cmake/compilers.cmake +++ b/src/vendor/cigraph/etc/cmake/compilers.cmake @@ -5,12 +5,9 @@ if(MSVC) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # necessary to compile for UWP endif() -if(NOT MSVC) - # Even though we will later use 'no-unknown-warning-option', we perform the test for - # 'unknown-warning-option', without the 'no-' prefix. This is necessary because GCC - # will accept any warning option starting with 'no-', and will not error, yet it still - # prints a message about the unrecognized option. - check_c_compiler_flag("-Wunknown-warning-option" COMPILER_SUPPORTS_UNKNOWN_WARNING_OPTION_FLAG) +if (NOT MSVC) + check_c_compiler_flag("-Wno-varargs" COMPILER_SUPPORTS_NO_VARARGS_FLAG) + check_c_compiler_flag("-Wno-unknown-warning-option" COMPILER_SUPPORTS_NO_UNKNOWN_WARNING_OPTION_FLAG) endif() set( @@ -18,17 +15,6 @@ set( "Treat warnings as errors with GCC-like compilers" ) -option(FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." FALSE) -if(FORCE_COLORED_OUTPUT) - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - add_compile_options(-fdiagnostics-color=always) - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_compile_options(-fcolor-diagnostics) - elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - add_compile_options(-fcolor-diagnostics) - endif() -endif() - macro(use_all_warnings TARGET_NAME) if(MSVC) target_compile_options(${TARGET_NAME} PRIVATE @@ -40,33 +26,23 @@ macro(use_all_warnings TARGET_NAME) /wd4800 # forcing value to 'true' or 'false' (performance warning) /wd4204 # nonstandard extension used: non-constant aggregate initializer /wd4701 # potentially uninitialized local variable - /wd4054 # 'type cast': from function pointer '...' to data pointer 'void *' - /wd4055 # from data pointer 'void *' to function pointer '...' - /wd4221 # nonstandard extension used: '...': cannot be initialized using address of automatic variable '...' ) else() - # Notes: - # GCC does not complain when encountering an unsupported "no"-prefixed wanring option such as -Wno-foo. - # Clang does complain, but these complaints can be silenced with -Wno-unknown-warning-option. - # Therefore it is generally safe to use -Wno-... options that are only supported by recent GCC/Clang. target_compile_options(${TARGET_NAME} PRIVATE # GCC-style compilers: - $<$: + $<$: $<$:-Werror> -Wall -Wextra -pedantic - -Wstrict-prototypes - -Wno-unused-function -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare -Wno-constant-logical-operand + -Wno-unused-function -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare > - $<$:-Wno-unknown-warning-option> + $<$:-Wno-varargs> + $<$:-Wno-unknown-warning-option> # Intel compiler: $<$: # disable #279: controlling expression is constant; affecting assert(condition && "message") + # disable #188: enumerated type mixed with another type; affecting IGRAPH_CHECK # disable #592: variable "var" is used before its value is set; affecting IGRAPH_UNUSED - -wd279 -wd592 -diag-disable=remark - > - # Intel LLVM: - $<$: - -fp-model=precise # The default 'fast' mode is not compatible with igraph's extensive use of NaN/Inf + -wd279 -wd188 -wd592 -diag-disable=remark > ) endif() diff --git a/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake b/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake index 308240be891..fa620c2d65e 100644 --- a/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake +++ b/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake @@ -59,23 +59,18 @@ if(CPACK_SOURCE_INSTALLED_DIRECTORIES) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src" ) file( - INSTALL - "${CPACK_PACKAGE_DIRECTORY}/src/io/parsers" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src/io" - ) + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/src/io/parsers" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src/io" + ) file( INSTALL "${SOURCE_DIR}/tools/removeexamples.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" ) file( - INSTALL - "${SOURCE_DIR}/tools/strip_licenses_from_examples.py" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" - ) - file( - INSTALL - "${CPACK_PACKAGE_DIRECTORY}/doc/html" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/doc" - ) + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/doc/html" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/doc" + ) endif() diff --git a/src/vendor/cigraph/etc/cmake/dependencies.cmake b/src/vendor/cigraph/etc/cmake/dependencies.cmake index b803df2c0a0..1a7048586c0 100644 --- a/src/vendor/cigraph/etc/cmake/dependencies.cmake +++ b/src/vendor/cigraph/etc/cmake/dependencies.cmake @@ -1,7 +1,6 @@ include(helpers) include(CheckSymbolExists) -include(CMakePushCheckState) # The threading library is not needed for igraph itself, but might be needed # for tests @@ -9,7 +8,7 @@ include(FindThreads) macro(find_dependencies) # Declare the list of dependencies that _may_ be vendored - set(VENDORABLE_DEPENDENCIES BLAS GLPK LAPACK ARPACK GMP PLFIT) + set(VENDORABLE_DEPENDENCIES BLAS CXSparse GLPK LAPACK ARPACK GMP PLFIT) # Declare optional dependencies associated with IGRAPH_..._SUPPORT flags # Note that GLPK is both vendorable and optional @@ -19,6 +18,7 @@ macro(find_dependencies) tristate(IGRAPH_USE_INTERNAL_GMP "Compile igraph with internal Mini-GMP" AUTO) tristate(IGRAPH_USE_INTERNAL_ARPACK "Compile igraph with internal ARPACK" AUTO) tristate(IGRAPH_USE_INTERNAL_BLAS "Compile igraph with internal BLAS" AUTO) + tristate(IGRAPH_USE_INTERNAL_CXSPARSE "Compile igraph with internal CXSparse" AUTO) tristate(IGRAPH_USE_INTERNAL_GLPK "Compile igraph with internal GLPK" AUTO) tristate(IGRAPH_USE_INTERNAL_LAPACK "Compile igraph with internal LAPACK" AUTO) tristate(IGRAPH_USE_INTERNAL_PLFIT "Compile igraph with internal plfit" AUTO) @@ -136,11 +136,12 @@ macro(find_dependencies) # Check whether we need to link to the math library if(NOT DEFINED CACHE{NEED_LINKING_AGAINST_LIBM}) - cmake_push_check_state() + set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) set(CMAKE_REQUIRED_QUIET ON) check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) if(NOT SINH_FUNCTION_EXISTS) unset(SINH_FUNCTION_EXISTS CACHE) + set(CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES m) check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) if(SINH_FUNCTION_EXISTS) @@ -148,9 +149,10 @@ macro(find_dependencies) else() message(FATAL_ERROR "Failed to figure out how to link to the math library on this platform") endif() + set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_SAVE}) endif() unset(SINH_FUNCTION_EXISTS CACHE) - cmake_pop_check_state() + set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) endif() if(NEED_LINKING_AGAINST_LIBM) diff --git a/src/vendor/cigraph/etc/cmake/features.cmake b/src/vendor/cigraph/etc/cmake/features.cmake index acfa4a1c539..f5c495c402f 100644 --- a/src/vendor/cigraph/etc/cmake/features.cmake +++ b/src/vendor/cigraph/etc/cmake/features.cmake @@ -6,17 +6,3 @@ include(lto) option(IGRAPH_GLPK_SUPPORT "Compile igraph with GLPK support" ON) tristate(IGRAPH_GRAPHML_SUPPORT "Compile igraph with GraphML support" AUTO) tristate(IGRAPH_OPENMP_SUPPORT "Use OpenMP for parallelization" AUTO) - -set(IGRAPH_INTEGER_SIZE AUTO CACHE STRING "Set size of igraph integers") -set_property(CACHE IGRAPH_INTEGER_SIZE PROPERTY STRINGS AUTO 32 64) - -if(IGRAPH_INTEGER_SIZE STREQUAL AUTO) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(IGRAPH_INTEGER_SIZE 64) - else() - set(IGRAPH_INTEGER_SIZE 32) - endif() -endif() - -option(FLEX_KEEP_LINE_NUMBERS "Keep references to the original line numbers in generated Flex/Bison parser files" OFF) -mark_as_advanced(FLEX_KEEP_LINE_NUMBERS) diff --git a/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake b/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake deleted file mode 100644 index fc66751ea67..00000000000 --- a/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake +++ /dev/null @@ -1,54 +0,0 @@ -include(CheckCSourceRuns) - -cmake_push_check_state(RESET) - -# Check whether IEEE754 doubles are laid out in little-endian order. We do this -# only when not cross-compiling; during cross-compilation, the host architecture -# might have different endianness conventions than the target, and we are running -# the test on the host here -if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) - # If we are cross-compiling and we have no emulator, let's just assume that - # IEEE754 doubles use the same endianness as uint64_t - set(IEEE754_DOUBLE_ENDIANNESS_MATCHES YES) - message(WARNING "\ -igraph is being cross-compiled, therefore we cannot validate whether the \ -endianness of IEEE754 doubles is the same as the endianness of uint64_t. \ -Most likely it is, unless you are compiling for some esoteric platform, \ -in which case you need make sure that this is the case on your own.\ -") -else() - if(NOT DEFINED CACHE{IEEE754_DOUBLE_ENDIANNESS_MATCHES}) - try_run( - IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE - IEEE754_DOUBLE_ENDIANNESS_TEST_COMPILES - ${CMAKE_BINARY_DIR} - ${PROJECT_SOURCE_DIR}/etc/cmake/ieee754_endianness_check.c - RUN_OUTPUT_VARIABLE IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT - ) - # Strip trailing newline, which is necessary on some platforms (such as node.js) - # to complete printing the output. - string(STRIP "${IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT}" IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT) - if(IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE EQUAL 0) - if(IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT STREQUAL "OK") - set(TEST_RESULT YES) - else() - set(TEST_RESULT NO) - endif() - else() - message(FATAL_ERROR "IEEE754 double endianness test terminated abnormally.") - endif() - - set( - IEEE754_DOUBLE_ENDIANNESS_MATCHES ${TEST_RESULT} CACHE BOOL - "Specifies whether the endianness of IEEE754 doubles is the same as the endianness of uint64_t." - FORCE - ) - mark_as_advanced(IEEE754_DOUBLE_ENDIANNESS_MATCHES) - endif() -endif() - -cmake_pop_check_state() - -if(NOT IEEE754_DOUBLE_ENDIANNESS_MATCHES) - message(FATAL_ERROR "igraph only supports platforms where IEEE754 doubles have the same endianness as uint64_t.") -endif() diff --git a/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c b/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c deleted file mode 100644 index 6296825f620..00000000000 --- a/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c +++ /dev/null @@ -1,24 +0,0 @@ -/* Checks whether the endianness of IEEE754 doubles matches the endianness of - * uint64_t on the target system. This is needed to ensure that the trick we - * employ in igraph_rng_get_unif01() works. */ - -#include -#include - -union { - uint64_t as_uint64_t; - double as_double; -} value; - -int main(void) { - value.as_uint64_t = 4841376218035192321ULL; - if (value.as_double == 4510218239279617.0) { - /* endianness of uint64_t and double match */ - printf("OK\n"); - } - /* We always return 0, even for a negative result, this is because we - * need to tell on the CMake side whether a compiler misconfiguration - * aborted our program, which can then be detected from a nonzero exit - * code. */ - return 0; -} diff --git a/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake b/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake index d0d9ecb0ab4..e1562d86a68 100644 --- a/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake +++ b/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake @@ -26,7 +26,6 @@ if(MATH_LIBRARY) else() set(PKGCONFIG_LIBS_PRIVATE "") endif() -set(PKGCONFIG_REQUIRES_PRIVATE "") if(NOT MSVC) check_cxx_symbol_exists(_LIBCPP_VERSION "vector" USING_LIBCXX) @@ -39,7 +38,7 @@ if(NOT MSVC) endif() if(IGRAPH_GRAPHML_SUPPORT) - set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} libxml-2.0") + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lxml2 -lz") endif() if(NOT IGRAPH_USE_INTERNAL_GMP) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lgmp") @@ -47,6 +46,9 @@ endif() if(NOT IGRAPH_USE_INTERNAL_BLAS) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lblas") endif() +if(NOT IGRAPH_USE_INTERNAL_CXSPARSE) + set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lcxsparse") +endif() if(IGRAPH_GLPK_SUPPORT AND NOT IGRAPH_USE_INTERNAL_GLPK) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lglpk") endif() diff --git a/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake b/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake index ef09642f0ec..f3cb216f647 100644 --- a/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake +++ b/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake @@ -13,10 +13,23 @@ # - IGRAPH_VERSION: version string of igraph that should be replaced in # expected outputs +function(print_file FILENAME) + # Replacement of "cmake -E cat" for older CMake versions. cat was added in + # CMake 3.18 + file(TO_NATIVE_PATH "${FILENAME}" FILENAME_NATIVE) + if(UNIX OR APPLE) + # Most likely Linux or macOS + execute_process(COMMAND "/bin/sh" "-c" "cat ${FILENAME_NATIVE}") + elseif(WIN32) + # Most likely Windows + execute_process(COMMAND "cmd" "/c" "type" "${FILENAME_NATIVE}") + endif() +endfunction() + get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) execute_process( - COMMAND ${CROSSCOMPILING_EMULATOR} ${TEST_EXECUTABLE} + COMMAND ${TEST_EXECUTABLE} WORKING_DIRECTORY ${WORK_DIR} RESULT_VARIABLE ERROR_CODE OUTPUT_VARIABLE OBSERVED_OUTPUT @@ -27,7 +40,7 @@ if(ERROR_CODE EQUAL 77) elseif(ERROR_CODE) set(MESSAGE "Test exited abnormally with error: ${ERROR_CODE}") file(WRITE ${OBSERVED_OUTPUT_FILE} "${MESSAGE}\n=========================================\n${OBSERVED_OUTPUT}") - execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${OBSERVED_OUTPUT_FILE}") + print_file("${OBSERVED_OUTPUT_FILE}") file(REMOVE ${DIFF_FILE}) message(FATAL_ERROR "Exiting test.") else() @@ -59,7 +72,7 @@ else() if(EXISTS ${DIFF_FILE}) message(STATUS "See diff below:") message(STATUS "-------------------------------------------------------") - execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${DIFF_FILE}") + print_file("${DIFF_FILE}") message(STATUS "-------------------------------------------------------") else() message(STATUS "Diff omitted; no diff tool was installed.") diff --git a/src/vendor/cigraph/etc/cmake/safe_math_support.cmake b/src/vendor/cigraph/etc/cmake/safe_math_support.cmake deleted file mode 100644 index a9e0ef49abb..00000000000 --- a/src/vendor/cigraph/etc/cmake/safe_math_support.cmake +++ /dev/null @@ -1,18 +0,0 @@ -include(CheckCXXSourceCompiles) - -# Check whether the compiler supports the __builtin_add_overflow() and __builtin_mul_overflow() -# builtins. These are present in recent GCC-compatible compilers. -cmake_push_check_state(RESET) - -check_cxx_source_compiles(" - int main(void) { - long long a=1, b=2, c; - __builtin_add_overflow(a, b, &c); - __builtin_mul_overflow(a, b, &c); - return 0; - } - " - HAVE_BUILTIN_OVERFLOW -) - -cmake_pop_check_state() diff --git a/src/vendor/cigraph/etc/cmake/summary.cmake b/src/vendor/cigraph/etc/cmake/summary.cmake index 6d7f44f4979..1cf132d7326 100644 --- a/src/vendor/cigraph/etc/cmake/summary.cmake +++ b/src/vendor/cigraph/etc/cmake/summary.cmake @@ -34,15 +34,6 @@ if(BUILD_SHARED_LIBS) else() message(STATUS "Library type: static") endif() -if(${IGRAPH_INTEGER_SIZE} STREQUAL "AUTO") - print_str("igraph_integer_t size" "auto") -elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 64) - print_str("igraph_integer_t size" "64 bits") -elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 32) - print_str("igraph_integer_t size" "32 bits") -else() - print_str("igraph_integer_t size" "INVALID") -endif() if(USE_CCACHE) if(CCACHE_PROGRAM) message(STATUS "Compiler cache: ccache") diff --git a/src/vendor/cigraph/etc/cmake/test_helpers.cmake b/src/vendor/cigraph/etc/cmake/test_helpers.cmake index 4d72877ba30..a324cd10201 100644 --- a/src/vendor/cigraph/etc/cmake/test_helpers.cmake +++ b/src/vendor/cigraph/etc/cmake/test_helpers.cmake @@ -13,9 +13,6 @@ function(add_legacy_test FOLDER NAME NAMESPACE) use_all_warnings(${TARGET_NAME}) add_dependencies(build_tests ${TARGET_NAME}) target_link_libraries(${TARGET_NAME} PRIVATE igraph) - if (NAMESPACE STREQUAL "test") - target_link_libraries(${TARGET_NAME} PRIVATE test_utilities) - endif() if (NOT BUILD_SHARED_LIBS) # Add a compiler definition required to compile igraph in static mode @@ -28,9 +25,12 @@ function(add_legacy_test FOLDER NAME NAMESPACE) ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) - # Some tests include cs.h from CXSparse + # Some tests include cs.h from CXSparse. The following ensures that the + # correct version is included, depending on whether CXSparse is vendored target_include_directories( - ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/vendor/cs + ${TARGET_NAME} PRIVATE + $<$:$> + $<$:${CXSPARSE_INCLUDE_DIRS}> ) if (MSVC) @@ -44,7 +44,6 @@ function(add_legacy_test FOLDER NAME NAMESPACE) get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) if(EXISTS ${EXPECTED_OUTPUT_FILE}) - get_property(CROSSCOMPILING_EMULATOR TARGET ${TARGET_NAME} PROPERTY CROSSCOMPILING_EMULATOR) add_test( NAME ${TEST_NAME} COMMAND ${CMAKE_COMMAND} @@ -55,7 +54,6 @@ function(add_legacy_test FOLDER NAME NAMESPACE) -DDIFF_TOOL=${DIFF_TOOL} -DFC_TOOL=${FC_TOOL} -DIGRAPH_VERSION=${PACKAGE_VERSION} - "-DCROSSCOMPILING_EMULATOR=${CROSSCOMPILING_EMULATOR}" -P ${CMAKE_SOURCE_DIR}/etc/cmake/run_legacy_test.cmake ) set_property(TEST ${TEST_NAME} PROPERTY SKIP_REGULAR_EXPRESSION "Test skipped") diff --git a/src/vendor/cigraph/etc/cmake/tls.cmake b/src/vendor/cigraph/etc/cmake/tls.cmake index f0f4834b5b9..372eadb1b64 100644 --- a/src/vendor/cigraph/etc/cmake/tls.cmake +++ b/src/vendor/cigraph/etc/cmake/tls.cmake @@ -1,17 +1,9 @@ -tristate(IGRAPH_ENABLE_TLS "Enable thread-local storage for igraph global variables" AUTO) - -include(CheckTLSSupport) -check_tls_support(TLS_KEYWORD) - -if(IGRAPH_ENABLE_TLS STREQUAL "AUTO") - if(TLS_KEYWORD) - set(IGRAPH_ENABLE_TLS ON) - else() - set(IGRAPH_ENABLE_TLS OFF) - endif() -endif() +option(IGRAPH_ENABLE_TLS "Enable thread-local storage for igraph global variables" OFF) if(IGRAPH_ENABLE_TLS) + include(CheckTLSSupport) + check_tls_support(TLS_KEYWORD) + if(NOT TLS_KEYWORD) message(FATAL_ERROR "Thread-local storage not supported on this compiler") endif() diff --git a/src/vendor/cigraph/etc/cmake/uint128_support.cmake b/src/vendor/cigraph/etc/cmake/uint128_support.cmake deleted file mode 100644 index 968c50abe33..00000000000 --- a/src/vendor/cigraph/etc/cmake/uint128_support.cmake +++ /dev/null @@ -1,43 +0,0 @@ -include(CheckCXXSourceCompiles) -include(CheckTypeSize) - -cmake_push_check_state(RESET) - -# Check whether the compiler supports the _umul128() intrinsic -check_cxx_source_compiles(" - #include - - int main(void) { - unsigned long long a = 0, b = 0; - unsigned long long c; - volatile unsigned long long d; - d = _umul128(a, b, &c); - return 0; - } - " - HAVE__UMUL128 -) - -# Check whether the compiler supports the __umulh() intrinsic -check_cxx_source_compiles(" - #include - - int main(void) { - unsigned long long a = 0, b = 0; - volatile unsigned long long c; - c = __umulh(a, b); - return 0; - } - " - HAVE___UMULH -) - -# Check whether the compiler has __uint128_t -check_type_size("__uint128_t" UINT128 LANGUAGE CXX) -if(UINT128 EQUAL 16) - set(HAVE___UINT128_T ON) -else() - set(HAVE___UINT128_T OFF) -endif() - -cmake_pop_check_state() diff --git a/src/vendor/cigraph/igraph.pc.in b/src/vendor/cigraph/igraph.pc.in index f869537d633..4c036fbdc48 100644 --- a/src/vendor/cigraph/igraph.pc.in +++ b/src/vendor/cigraph/igraph.pc.in @@ -9,5 +9,4 @@ Version: @PROJECT_VERSION@ URL: @PROJECT_HOMEPAGE_URL@ Libs: -L${libdir} -ligraph Libs.private: @PKGCONFIG_LIBS_PRIVATE@ -Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@ Cflags: -I${includedir}/igraph diff --git a/src/vendor/cigraph/include/igraph.h b/src/vendor/cigraph/include/igraph.h index d77e6261e27..5b8c14736a1 100644 --- a/src/vendor/cigraph/include/igraph.h +++ b/src/vendor/cigraph/include/igraph.h @@ -45,14 +45,13 @@ #include "igraph_heap.h" #include "igraph_psumtree.h" #include "igraph_strvector.h" -#include "igraph_vector_list.h" #include "igraph_vector_ptr.h" +#include "igraph_spmatrix.h" #include "igraph_sparsemat.h" #include "igraph_qsort.h" #include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_graph_list.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_constructors.h" @@ -89,6 +88,7 @@ #include "igraph_hrg.h" #include "igraph_threading.h" #include "igraph_interrupt.h" +#include "igraph_scg.h" #include "igraph_matching.h" #include "igraph_embedding.h" #include "igraph_scan.h" @@ -98,6 +98,5 @@ #include "igraph_coloring.h" #include "igraph_eulerian.h" #include "igraph_graphicality.h" -#include "igraph_cycles.h" #endif diff --git a/src/vendor/cigraph/include/igraph_adjlist.h b/src/vendor/cigraph/include/igraph_adjlist.h index a7aa69e22b7..ee56bdb8016 100644 --- a/src/vendor/cigraph/include/igraph_adjlist.h +++ b/src/vendor/cigraph/include/igraph_adjlist.h @@ -26,7 +26,6 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -37,30 +36,25 @@ typedef struct igraph_adjlist_t { igraph_vector_int_t *adjs; } igraph_adjlist_t; -typedef struct igraph_inclist_t { - igraph_integer_t length; - igraph_vector_int_t *incs; -} igraph_inclist_t; - -IGRAPH_EXPORT igraph_error_t igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); +IGRAPH_EXPORT int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); IGRAPH_EXPORT igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); -IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, +IGRAPH_EXPORT int igraph_adjlist_init_complementer(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_from_inclist( - const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il); IGRAPH_EXPORT void igraph_adjlist_destroy(igraph_adjlist_t *al); IGRAPH_EXPORT void igraph_adjlist_clear(igraph_adjlist_t *al); IGRAPH_EXPORT void igraph_adjlist_sort(igraph_adjlist_t *al); -IGRAPH_EXPORT igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al); -IGRAPH_EXPORT igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al); -IGRAPH_EXPORT igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); +IGRAPH_EXPORT int igraph_adjlist_simplify(igraph_adjlist_t *al); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_adjlist_remove_duplicate(const igraph_t *graph, + igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_print(const igraph_adjlist_t *al); +IGRAPH_EXPORT int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); IGRAPH_EXPORT igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t 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_EXPORT 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); /** * \define igraph_adjlist_get @@ -74,28 +68,35 @@ IGRAPH_EXPORT igraph_error_t igraph_adjlist_replace_edge(igraph_adjlist_t* al, i * * Time complexity: O(1). */ -#define igraph_adjlist_get(al,no) (&(al)->adjs[(igraph_integer_t)(no)]) +#define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)]) -IGRAPH_EXPORT igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, +IGRAPH_EXPORT int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, igraph_neimode_t mode, igraph_bool_t duplicate); -IGRAPH_EXPORT igraph_error_t igraph_inclist_init(const igraph_t *graph, +typedef struct igraph_inclist_t { + igraph_integer_t length; + igraph_vector_int_t *incs; +} igraph_inclist_t; + +IGRAPH_EXPORT int igraph_inclist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops); -IGRAPH_EXPORT igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); +IGRAPH_EXPORT int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); IGRAPH_EXPORT igraph_integer_t igraph_inclist_size(const igraph_inclist_t *al); IGRAPH_EXPORT void igraph_inclist_destroy(igraph_inclist_t *il); IGRAPH_EXPORT void igraph_inclist_clear(igraph_inclist_t *il); -IGRAPH_EXPORT igraph_error_t igraph_inclist_print(const igraph_inclist_t *il); -IGRAPH_EXPORT igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_inclist_remove_duplicate(const igraph_t *graph, + igraph_inclist_t *il); +IGRAPH_EXPORT int igraph_inclist_print(const igraph_inclist_t *il); +IGRAPH_EXPORT int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); /** * \define igraph_inclist_get * \brief Query a vector in an incidence list. * * Returns a pointer to an igraph_vector_int_t object from an - * incidence list containing edge IDs. The vector can be modified, + * incidence list containing edge ids. The vector can be modified, * resized, etc. as desired. * \param il Pointer to the incidence list. * \param no The vertex for which the incident edges are returned. @@ -103,7 +104,7 @@ IGRAPH_EXPORT igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *il, F * * Time complexity: O(1). */ -#define igraph_inclist_get(il,no) (&(il)->incs[(igraph_integer_t)(no)]) +#define igraph_inclist_get(il,no) (&(il)->incs[(long int)(no)]) typedef struct igraph_lazy_adjlist_t { const igraph_t *graph; @@ -112,9 +113,10 @@ typedef struct igraph_lazy_adjlist_t { igraph_neimode_t mode; igraph_loops_t loops; igraph_multiple_t multiple; + igraph_vector_t dummy; } igraph_lazy_adjlist_t; -IGRAPH_EXPORT igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, +IGRAPH_EXPORT int igraph_lazy_adjlist_init(const igraph_t *graph, igraph_lazy_adjlist_t *al, igraph_neimode_t mode, igraph_loops_t loops, @@ -123,19 +125,6 @@ IGRAPH_EXPORT void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al); IGRAPH_EXPORT void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al); IGRAPH_EXPORT igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al); -/** - * \define igraph_lazy_adjlist_has - * \brief Are adjacenct vertices already stored in a lazy adjacency list? - * - * \param al The lazy adjacency list. - * \param no The vertex ID to query. - * \return True if the adjacent vertices of this vertex are already computed - * and stored, false otherwise. - * - * Time complexity: O(1). - */ -#define igraph_lazy_adjlist_has(al,no) ((igraph_bool_t) (al)->adjs[(igraph_integer_t)(no)]) - /** * \define igraph_lazy_adjlist_get * \brief Query neighbor vertices. @@ -144,23 +133,17 @@ IGRAPH_EXPORT igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlis * result is stored in the adjacency list and no further query * operations are needed when the neighbors of the same vertex are * queried again. - * * \param al The lazy adjacency list. * \param no The vertex ID to query. - * \return Pointer to a vector, or \c NULL upon error. Errors can only - * occur the first time this function is called for a given vertex. - * It is safe to modify this vector, + * \return Pointer to a vector. It is allowed to modify it and * modification does not affect the original graph. * - * \sa \ref igraph_lazy_adjlist_has() to check if this function has - * already been called for a vertex. - * * Time complexity: O(d), the number of neighbor vertices for the * first time, O(1) for subsequent calls. */ #define igraph_lazy_adjlist_get(al,no) \ - (igraph_lazy_adjlist_has(al,no) ? ((al)->adjs[(igraph_integer_t)(no)]) \ - : (igraph_i_lazy_adjlist_get_real(al, no))) + ((al)->adjs[(long int)(no)] != 0 ? ((al)->adjs[(long int)(no)]) : \ + (igraph_i_lazy_adjlist_get_real(al, no))) IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, igraph_integer_t no); typedef struct igraph_lazy_inclist_t { @@ -168,10 +151,11 @@ typedef struct igraph_lazy_inclist_t { igraph_integer_t length; igraph_vector_int_t **incs; igraph_neimode_t mode; + igraph_vector_t dummy; igraph_loops_t loops; } igraph_lazy_inclist_t; -IGRAPH_EXPORT igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, +IGRAPH_EXPORT int igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops); @@ -179,19 +163,6 @@ IGRAPH_EXPORT void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il); IGRAPH_EXPORT void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il); IGRAPH_EXPORT igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il); -/** - * \define igraph_lazy_inclist_has - * \brief Are incident edges already stored in a lazy inclist? - * - * \param il The lazy incidence list. - * \param no The vertex ID to query. - * \return True if the incident edges of this vertex are already computed - * and stored, false otherwise. - * - * Time complexity: O(1). - */ -#define igraph_lazy_inclist_has(il,no) ((igraph_bool_t) (il)->incs[(igraph_integer_t)(no)]) - /** * \define igraph_lazy_inclist_get * \brief Query incident edges. @@ -200,24 +171,18 @@ IGRAPH_EXPORT igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclis * result is stored in the incidence list and no further query * operations are needed when the incident edges of the same vertex are * queried again. - * - * \param il The lazy incidence list object. - * \param no The vertex ID to query. - * \return Pointer to a vector, or \c NULL upon error. Errors can only - * occur the first time this function is called for a given vertex. - * It is safe to modify this vector, + * \param al The lazy incidence list object. + * \param no The vertex id to query. + * \return Pointer to a vector. It is allowed to modify it and * modification does not affect the original graph. * - * \sa \ref igraph_lazy_inclist_has() to check if this function has - * already been called for a vertex. - * * Time complexity: O(d), the number of incident edges for the first * time, O(1) for subsequent calls with the same \p no argument. */ -#define igraph_lazy_inclist_get(il,no) \ - (igraph_lazy_inclist_has(il,no) ? ((il)->incs[(igraph_integer_t)(no)]) \ - : (igraph_i_lazy_inclist_get_real(il,no))) -IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, igraph_integer_t no); +#define igraph_lazy_inclist_get(al,no) \ + ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \ + (igraph_i_lazy_inclist_get_real(al, no))) +IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *al, igraph_integer_t no); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_arpack.h b/src/vendor/cigraph/include/igraph_arpack.h index 8999819acfe..c28af921dbf 100644 --- a/src/vendor/cigraph/include/igraph_arpack.h +++ b/src/vendor/cigraph/include/igraph_arpack.h @@ -25,10 +25,9 @@ #define IGRAPH_ARPACK_H #include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" +#include "igraph_matrix.h" __BEGIN_DECLS @@ -97,15 +96,14 @@ __BEGIN_DECLS /** * \struct igraph_arpack_options_t - * \brief Options for ARPACK. + * \brief Options for ARPACK * - * This data structure contains the options of the ARPACK eigenvalue + * This data structure contains the options of thee ARPACK eigenvalue * solver routines. It must be initialized by calling \ref * igraph_arpack_options_init() on it. Then it can be used for * multiple ARPACK calls, as the ARPACK solvers do not modify it. * * Input options: - * * \member bmat Character. Whether to solve a standard ('I') ot a * generalized problem ('B'). * \member n Dimension of the eigenproblem. @@ -188,7 +186,6 @@ __BEGIN_DECLS * \ref igraph_arpack_rssolve() of \ref igraph_arpack_rnsolve() call. * * Output options: - * * \member info Error flag of ARPACK. Possible values: * \clist \cli 0 * Normal exit. @@ -259,7 +256,7 @@ typedef struct igraph_arpack_options_t { /** * \struct igraph_arpack_storage_t - * \brief Storage for ARPACK. + * \brief Storage for ARPACK * * Public members, do not modify them directly, these are considered * to be read-only. @@ -287,21 +284,19 @@ typedef struct igraph_arpack_storage_t { igraph_real_t *resid; igraph_real_t *ax; int *select; - /* The following two are only used for non-symmetric problems: */ - igraph_real_t *di; + igraph_real_t *di; /* These two only for non-symmetric problems */ igraph_real_t *workev; } igraph_arpack_storage_t; IGRAPH_EXPORT void igraph_arpack_options_init(igraph_arpack_options_t *o); -IGRAPH_EXPORT igraph_arpack_options_t* igraph_arpack_options_get_default(void); -IGRAPH_EXPORT igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, - igraph_integer_t maxncv, igraph_integer_t maxldv, igraph_bool_t symm); +IGRAPH_EXPORT int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, + long int maxncv, long int maxldv, igraph_bool_t symm); IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); /** * \typedef igraph_arpack_function_t - * \brief Type of the ARPACK callback function. + * Type of the ARPACK callback function * * \param to Pointer to an \c igraph_real_t, the result of the * matrix-vector product is expected to be stored here. @@ -312,25 +307,25 @@ IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); * \param extra Extra argument to the matrix-vector calculation * function. This is coming from the \ref igraph_arpack_rssolve() * or \ref igraph_arpack_rnsolve() function. - * \return Error code. If not \c IGRAPH_SUCCESS, then the ARPACK solver considers + * \return Error code, if not zero, then the ARPACK solver considers * this as an error, stops and calls the igraph error handler. */ -typedef igraph_error_t igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, +typedef int igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, int n, void *extra); -IGRAPH_EXPORT igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, - igraph_integer_t nev); +IGRAPH_EXPORT int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + long int nev); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_array.h b/src/vendor/cigraph/include/igraph_array.h index 1f098d0a3c3..627433ad65e 100644 --- a/src/vendor/cigraph/include/igraph_array.h +++ b/src/vendor/cigraph/include/igraph_array.h @@ -25,8 +25,6 @@ #define IGRAPH_ARRAY_H #include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_vector.h" __BEGIN_DECLS @@ -40,11 +38,11 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_INT +#define BASE_LONG #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" -#undef BASE_INT +#undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" diff --git a/src/vendor/cigraph/include/igraph_array_pmt.h b/src/vendor/cigraph/include/igraph_array_pmt.h index cd15fd7a7f5..18c73ae8d0b 100644 --- a/src/vendor/cigraph/include/igraph_array_pmt.h +++ b/src/vendor/cigraph/include/igraph_array_pmt.h @@ -23,7 +23,7 @@ typedef struct TYPE(igraph_array3) { TYPE(igraph_vector) data; - igraph_integer_t n1, n2, n3, n1n2; + long int n1, n2, n3, n1n2; } TYPE(igraph_array3); #ifndef IGRAPH_ARRAY3_INIT_FINALLY @@ -36,19 +36,16 @@ typedef struct TYPE(igraph_array3) { #define ARRAY3(m,i,j,k) ((m).data.stor_begin[(m).n1n2*(k)+(m).n1*(j)+(i)]) #endif -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, init)( - TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, - igraph_integer_t n3); +IGRAPH_EXPORT int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3); IGRAPH_EXPORT void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_array3, n)( - const TYPE(igraph_array3) *a, igraph_integer_t idx); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, resize)( - TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, - igraph_integer_t n3); +IGRAPH_EXPORT long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); +IGRAPH_EXPORT long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx); +IGRAPH_EXPORT int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2, + long int n3); IGRAPH_EXPORT void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a); IGRAPH_EXPORT BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a); IGRAPH_EXPORT void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by); IGRAPH_EXPORT void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, +IGRAPH_EXPORT int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, const TYPE(igraph_array3) *from); diff --git a/src/vendor/cigraph/include/igraph_attributes.h b/src/vendor/cigraph/include/igraph_attributes.h index c4d973fd4d8..37c87394711 100644 --- a/src/vendor/cigraph/include/igraph_attributes.h +++ b/src/vendor/cigraph/include/igraph_attributes.h @@ -24,14 +24,11 @@ #ifndef IGRAPH_ATTRIBUTES_H #define IGRAPH_ATTRIBUTES_H -#include "igraph_config.h" #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_strvector.h" -#include "igraph_vector_list.h" #include "igraph_vector_ptr.h" #include "igraph_iterators.h" @@ -44,11 +41,10 @@ __BEGIN_DECLS /** * \section about_attributes * - * Attributes are numbers, boolean values or strings associated with - * the vertices or edges of a graph, or with the graph itself. E.g. you may - * label vertices with symbolic names or attach numeric weights to the edges - * of a graph. In addition to these three basic types, a custom object - * type is supported as well. + * Attributes are numbers or strings (or basically any kind + * of data) associated with the vertices or edges of a graph, or + * with the graph itself. Eg. you may label vertices with symbolic names + * or attach numeric weights to the edges of a graph. * * igraph attributes are designed to be flexible and extensible. * In igraph attributes are implemented via an interface abstraction: @@ -56,7 +52,7 @@ __BEGIN_DECLS * for storing vertex, edge and graph attributes. This means that * different attribute implementations can be used together with * igraph. This is reasonable: if igraph is used from Python attributes can be - * of any Python type, from R all R types are allowed. There is also an + * of any Python type, from GNU R all R types are allowed. There is an * experimental attribute implementation to be used when programming * in C, but by default it is currently turned off. * @@ -75,13 +71,10 @@ __BEGIN_DECLS * notify the attribute handling code about the structural changes in * a graph. See the documentation of this type for details. * - * By default there is no attribute interface attached to \a igraph. - * To attach one, call \ref igraph_set_attribute_table with your new - * table. This is normally done on program startup, and is kept untouched - * for the program's lifetime. It must be done before any graph object - * is created, as graphs created with a given attribute handler - * cannot be manipulated while a different attribute handler is - * active. + * By default there is no attribute interface attached to \a igraph, + * to attach one, call \ref igraph_set_attribute_table with your new + * table. + * */ /** @@ -108,29 +101,27 @@ __BEGIN_DECLS * * Note that this is only the * type communicated by the attribute interface towards igraph - * functions. E.g. in the R attribute handler, it is safe to say + * functions. Eg. in the GNU R attribute handler, it is safe to say * that all complex R object attributes are strings, as long as this * interface is able to serialize them into strings. See also \ref * igraph_attribute_table_t. - * \enumval IGRAPH_ATTRIBUTE_UNSPECIFIED Currently used internally - * as a "null value" or "placeholder value" in some algorithms. - * Attribute records with this type must not be passed to igraph - * functions. + * \enumval IGRAPH_ATTRIBUTE_DEFAULT Currently not used for anything. * \enumval IGRAPH_ATTRIBUTE_NUMERIC Numeric attribute. * \enumval IGRAPH_ATTRIBUTE_BOOLEAN Logical values, true or false. * \enumval IGRAPH_ATTRIBUTE_STRING Attribute that can be converted to * a string. - * \enumval IGRAPH_ATTRIBUTE_OBJECT Custom attribute type, to be - * used for special data types by client applications. The R and - * Python interfaces use this for attributes that hold R or Python - * objects. Usually ignored by igraph functions. + * \enumval IGRAPH_ATTRIBUTE_R_OBJECT An R object. This is usually + * ignored by the igraph functions. + * \enumval IGRAPH_ATTRIBUTE_PY_OBJECT A Python object. Usually + * ignored by the igraph functions. + * */ -typedef enum { IGRAPH_ATTRIBUTE_UNSPECIFIED = 0, - IGRAPH_ATTRIBUTE_DEFAULT IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_ATTRIBUTE_UNSPECIFIED, +typedef enum { IGRAPH_ATTRIBUTE_DEFAULT = 0, IGRAPH_ATTRIBUTE_NUMERIC = 1, - IGRAPH_ATTRIBUTE_BOOLEAN = 2, - IGRAPH_ATTRIBUTE_STRING = 3, - IGRAPH_ATTRIBUTE_OBJECT = 127 + IGRAPH_ATTRIBUTE_BOOLEAN = 5, + IGRAPH_ATTRIBUTE_STRING = 2, + IGRAPH_ATTRIBUTE_R_OBJECT = 3, + IGRAPH_ATTRIBUTE_PY_OBJECT = 4 } igraph_attribute_type_t; typedef struct igraph_attribute_record_t { @@ -193,27 +184,26 @@ typedef struct igraph_attribute_combination_t { #define IGRAPH_NO_MORE_ATTRIBUTES ((const char*)0) -IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb); -IGRAPH_EXPORT igraph_error_t igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); +IGRAPH_EXPORT int igraph_attribute_combination_init(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); IGRAPH_EXPORT void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); -IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb, +IGRAPH_EXPORT int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t type, igraph_function_pointer_t func); -IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, +IGRAPH_EXPORT int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name); -IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, +IGRAPH_EXPORT 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); /** * \struct igraph_attribute_table_t - * \brief Table of functions to perform operations on attributes. + * \brief Table of functions to perform operations on attributes * * This type collects the functions defining an attribute handler. * It has the following members: - * * \member init This function is called whenever a new graph object is * created, right after it is created but before any vertices or * edges are added. It is supposed to set the \c attr member of the \c @@ -287,58 +277,58 @@ IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_query(const igraph_att */ typedef struct igraph_attribute_table_t { - igraph_error_t (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); - void (*destroy)(igraph_t *graph); - igraph_error_t (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, - igraph_bool_t va, igraph_bool_t ea); - igraph_error_t (*add_vertices)(igraph_t *graph, igraph_integer_t nv, igraph_vector_ptr_t *attr); - igraph_error_t (*permute_vertices)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_int_t *idx); - igraph_error_t (*combine_vertices)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_int_list_t *merges, - const igraph_attribute_combination_t *comb); - igraph_error_t (*add_edges)(igraph_t *graph, const igraph_vector_int_t *edges, - igraph_vector_ptr_t *attr); - igraph_error_t (*permute_edges)(const igraph_t *graph, - igraph_t *newgraph, const igraph_vector_int_t *idx); - igraph_error_t (*combine_edges)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_int_list_t *merges, - const igraph_attribute_combination_t *comb); - igraph_error_t (*get_info)(const igraph_t *graph, - igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, - igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, - igraph_strvector_t *enames, igraph_vector_int_t *etypes); + int (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); + void (*destroy)(igraph_t *graph); + int (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, + igraph_bool_t va, igraph_bool_t ea); + int (*add_vertices)(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr); + int (*permute_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx); + int (*combine_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); + int (*add_edges)(igraph_t *graph, const igraph_vector_t *edges, + igraph_vector_ptr_t *attr); + int (*permute_edges)(const igraph_t *graph, + igraph_t *newgraph, const igraph_vector_t *idx); + int (*combine_edges)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_ptr_t *merges, + const igraph_attribute_combination_t *comb); + int (*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 (*has_attr)(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); - igraph_error_t (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, - igraph_attribute_elemtype_t elemtype, const char *name); - igraph_error_t (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, - igraph_vector_t *value); - igraph_error_t (*get_string_graph_attr)(const igraph_t *graph, const char *name, - igraph_strvector_t *value); - igraph_error_t (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, - igraph_vector_bool_t *value); - igraph_error_t (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_vector_t *value); - igraph_error_t (*get_string_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_strvector_t *value); - igraph_error_t (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_vector_bool_t *value); - igraph_error_t (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_vector_t *value); - igraph_error_t (*get_string_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_strvector_t *value); - igraph_error_t (*get_bool_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_vector_bool_t *value); + int (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, const char *name); + int (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, + igraph_vector_t *value); + int (*get_string_graph_attr)(const igraph_t *graph, const char *name, + igraph_strvector_t *value); + int (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, + igraph_vector_bool_t *value); + int (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_t *value); + int (*get_string_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_strvector_t *value); + int (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); + int (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_vector_t *value); + int (*get_string_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_strvector_t *value); + int (*get_bool_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_vector_bool_t *value); } igraph_attribute_table_t; IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table); @@ -366,57 +356,57 @@ IGRAPH_EXPORT igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const c IGRAPH_EXPORT const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, igraph_integer_t eid); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VANV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EANV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VASV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_strvector_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EASV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_strvector_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VABV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_bool_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EABV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_bool_t *result); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_list(const igraph_t *graph, - igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, - igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, - igraph_strvector_t *enames, igraph_vector_int_t *etypes); +IGRAPH_EXPORT 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); IGRAPH_EXPORT igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, igraph_real_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, igraph_bool_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, const char *value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_real_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_bool_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, igraph_integer_t vid, const char *value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_real_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_bool_t value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, igraph_integer_t eid, const char *value); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); -IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); IGRAPH_EXPORT void igraph_cattribute_remove_g(igraph_t *graph, const char *name); diff --git a/src/vendor/cigraph/include/igraph_bipartite.h b/src/vendor/cigraph/include/igraph_bipartite.h index 0bed57a4069..13771e7a5c6 100644 --- a/src/vendor/cigraph/include/igraph_bipartite.h +++ b/src/vendor/cigraph/include/igraph_bipartite.h @@ -25,12 +25,11 @@ #define IGRAPH_BIPARTITE_H #include "igraph_decls.h" -#include "igraph_datatype.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" +#include "igraph_datatype.h" __BEGIN_DECLS @@ -38,73 +37,61 @@ __BEGIN_DECLS /* Bipartite networks */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_full_bipartite(igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, - const igraph_vector_int_t *edges, +IGRAPH_EXPORT int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, + const igraph_vector_t *edges, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_bipartite_projection(const igraph_t *graph, +IGRAPH_EXPORT int igraph_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj1, igraph_t *proj2, - igraph_vector_int_t *multiplicity1, - igraph_vector_int_t *multiplicity2, + igraph_vector_t *multiplicity1, + igraph_vector_t *multiplicity2, igraph_integer_t probe1); -IGRAPH_EXPORT igraph_error_t igraph_biadjacency(igraph_t *graph, igraph_vector_bool_t *types, - const igraph_matrix_t *input, igraph_bool_t directed, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_get_biadjacency(const igraph_t *graph, +IGRAPH_EXPORT int igraph_get_incidence(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, - igraph_vector_int_t *row_ids, - igraph_vector_int_t *col_ids); + igraph_vector_t *row_ids, + igraph_vector_t *col_ids); -IGRAPH_EXPORT igraph_error_t igraph_is_bipartite(const igraph_t *graph, +IGRAPH_EXPORT int igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *types); -IGRAPH_EXPORT igraph_error_t igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT 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); -/* Deprecated functions: */ - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_incidence( - const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, - igraph_vector_int_t *row_ids, igraph_vector_int_t *col_ids -); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_blas.h b/src/vendor/cigraph/include/igraph_blas.h index ae7daa47d24..4daf5df89d9 100644 --- a/src/vendor/cigraph/include/igraph_blas.h +++ b/src/vendor/cigraph/include/igraph_blas.h @@ -25,7 +25,6 @@ #define IGRAPH_BLAS_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" @@ -52,19 +51,16 @@ __BEGIN_DECLS * */ -IGRAPH_EXPORT igraph_error_t igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, - igraph_bool_t transpose_b, igraph_real_t alpha, const igraph_matrix_t* a, - const igraph_matrix_t* b, igraph_real_t beta, igraph_matrix_t* c); -IGRAPH_EXPORT igraph_error_t igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, +IGRAPH_EXPORT 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); IGRAPH_EXPORT igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, +IGRAPH_EXPORT int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t *res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_centrality.h b/src/vendor/cigraph/include/igraph_centrality.h index 1e515a696dd..c6f5abbc8f0 100644 --- a/src/vendor/cigraph/include/igraph_centrality.h +++ b/src/vendor/cigraph/include/igraph_centrality.h @@ -26,7 +26,6 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" @@ -38,51 +37,43 @@ __BEGIN_DECLS /* Centrality */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_int_t *reachable_count, igraph_bool_t *all_reachable, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_int_t *reachable_count, igraph_bool_t *all_reachable, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_harmonic_centrality(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, +IGRAPH_EXPORT int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, - const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weigths); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, - const igraph_vs_t vids, igraph_bool_t directed, - const igraph_vs_t sources, const igraph_vs_t targets, - const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_bool_t directed, - const igraph_vs_t sources, const igraph_vs_t targets, - const igraph_vector_t *weights); /** * \typedef igraph_pagerank_algo_t - * \brief PageRank algorithm implementation. + * \brief PageRank algorithm implementation * * Algorithms to calculate PageRank. * \enumval IGRAPH_PAGERANK_ALGO_ARPACK Use the ARPACK library, this @@ -97,18 +88,18 @@ typedef enum { IGRAPH_PAGERANK_ALGO_PRPACK = 2 } igraph_pagerank_algo_t; -IGRAPH_EXPORT igraph_error_t igraph_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_personalized_pagerank(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_personalized_pagerank_vs(const igraph_t *graph, +IGRAPH_EXPORT 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, @@ -116,62 +107,65 @@ IGRAPH_EXPORT igraph_error_t igraph_personalized_pagerank_vs(const igraph_t *gra igraph_vs_t reset_vids, const igraph_vector_t *weights, igraph_arpack_options_t *options); -IGRAPH_EXPORT igraph_error_t igraph_eigenvector_centrality(const igraph_t *graph, igraph_vector_t *vector, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, igraph_vector_t *hub_vector, - igraph_vector_t *authority_vector, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, +IGRAPH_EXPORT int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *ins, igraph_vector_t *outs); IGRAPH_EXPORT igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized); -IGRAPH_EXPORT igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_centralization_degree_tmax(const igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, +IGRAPH_EXPORT int igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res); -IGRAPH_EXPORT igraph_error_t igraph_centralization_closeness(const igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, +IGRAPH_EXPORT int igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res); -IGRAPH_EXPORT igraph_error_t igraph_centralization_eigenvector_centrality( +IGRAPH_EXPORT int igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, @@ -181,23 +175,29 @@ IGRAPH_EXPORT igraph_error_t igraph_centralization_eigenvector_centrality( igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); -IGRAPH_EXPORT igraph_error_t igraph_centralization_eigenvector_centrality_tmax( +IGRAPH_EXPORT 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); + /* Deprecated functions: */ -IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t 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); +IGRAPH_EXPORT IGRAPH_DEPRECATED 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_EXPORT IGRAPH_DEPRECATED 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_EXPORT IGRAPH_DEPRECATED 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); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_cliques.h b/src/vendor/cigraph/include/igraph_cliques.h index 141dd261f23..179b3f788bc 100644 --- a/src/vendor/cigraph/include/igraph_cliques.h +++ b/src/vendor/cigraph/include/igraph_cliques.h @@ -25,10 +25,9 @@ #define IGRAPH_CLIQUES_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -36,51 +35,51 @@ __BEGIN_DECLS /* Cliques, maximal independent vertex sets */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques( - const igraph_t *graph, igraph_vector_int_list_t *res, - igraph_integer_t min_size, igraph_integer_t max_size -); -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, +IGRAPH_EXPORT int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, + igraph_integer_t min_size, igraph_integer_t max_size); +IGRAPH_EXPORT int igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, +IGRAPH_EXPORT int igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_subset( - const igraph_t *graph, const igraph_vector_int_t *subset, - igraph_vector_int_list_t *res, igraph_integer_t *no, - FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size -); -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_hist(const igraph_t *graph, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_maximal_cliques_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, +IGRAPH_EXPORT int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, +IGRAPH_EXPORT int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_largest_cliques(const igraph_t *graph, - igraph_vector_int_list_t *cliques); -IGRAPH_EXPORT igraph_error_t igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); -IGRAPH_EXPORT igraph_error_t igraph_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, +IGRAPH_EXPORT int igraph_largest_cliques(const igraph_t *graph, + igraph_vector_ptr_t *cliques); +IGRAPH_EXPORT int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); -IGRAPH_EXPORT igraph_error_t igraph_weighted_clique_number(const igraph_t *graph, +IGRAPH_EXPORT int igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res); +IGRAPH_EXPORT int igraph_weighted_clique_number(const igraph_t *graph, const igraph_vector_t *vertex_weights, igraph_real_t *res); -IGRAPH_EXPORT igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res, +IGRAPH_EXPORT int igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res); -IGRAPH_EXPORT igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res); -IGRAPH_EXPORT igraph_error_t igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT int igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res); +IGRAPH_EXPORT int igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res); +IGRAPH_EXPORT int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); /** * \typedef igraph_clique_handler_t @@ -91,22 +90,21 @@ IGRAPH_EXPORT igraph_error_t igraph_independence_number(const igraph_t *graph, i * See the details at the documentation of \ref * igraph_cliques_callback(). * - * \param clique The current clique. The clique is owned by the clique search - * routine. You do not need to destroy or free it if you do not want to store - * it; however, if you want to hold on to it for a longer period of time, you - * need to make a copy of it on your own and store the copy itself. + * \param clique The current clique. Destroying and freeing + * this vector is left to the user. + * Use \ref igraph_vector_destroy() and \ref igraph_free() + * to do this. * \param arg This extra argument was passed to \ref * igraph_cliques_callback() when it was called. - * \return Error code; \c IGRAPH_SUCCESS to continue the search or - * \c IGRAPH_STOP to stop the search without signaling an error. + * \return Boolean, whether to continue with the clique search. */ -typedef igraph_error_t igraph_clique_handler_t(const igraph_vector_int_t *clique, void *arg); +typedef igraph_bool_t igraph_clique_handler_t(igraph_vector_t *clique, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_cliques_callback(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_callback(const igraph_t *graph, +IGRAPH_EXPORT 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); diff --git a/src/vendor/cigraph/include/igraph_cocitation.h b/src/vendor/cigraph/include/igraph_cocitation.h index 4dfd406ab94..32699b10804 100644 --- a/src/vendor/cigraph/include/igraph_cocitation.h +++ b/src/vendor/cigraph/include/igraph_cocitation.h @@ -25,7 +25,6 @@ #define IGRAPH_COCITATION_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" @@ -37,28 +36,28 @@ __BEGIN_DECLS /* Cocitation and other similarity measures */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, - const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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_EXPORT 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_EXPORT igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, - const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_similarity_inverse_log_weighted(const igraph_t *graph, +IGRAPH_EXPORT int igraph_similarity_inverse_log_weighted(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode); diff --git a/src/vendor/cigraph/include/igraph_cohesive_blocks.h b/src/vendor/cigraph/include/igraph_cohesive_blocks.h index a52cf68f733..b26a5f3d581 100644 --- a/src/vendor/cigraph/include/igraph_cohesive_blocks.h +++ b/src/vendor/cigraph/include/igraph_cohesive_blocks.h @@ -26,16 +26,15 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_vector.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, - igraph_vector_int_list_t *blocks, - igraph_vector_int_t *cohesion, - igraph_vector_int_t *parent, +IGRAPH_EXPORT 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); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_coloring.h b/src/vendor/cigraph/include/igraph_coloring.h index ff7f690f081..02876ebd94b 100644 --- a/src/vendor/cigraph/include/igraph_coloring.h +++ b/src/vendor/cigraph/include/igraph_coloring.h @@ -23,7 +23,6 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" __BEGIN_DECLS @@ -33,21 +32,14 @@ __BEGIN_DECLS * * Ordering heuristics for \ref igraph_vertex_coloring_greedy(). * - * \enumval IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS - * Choose the vertex with largest number of already colored neighbors. - * \enumval IGRAPH_COLORING_GREEDY_DSATUR - * Choose the vertex with largest number of unique colors in its neighborhood, i.e. its - * "saturation degree". When multiple vertices have the same saturation degree, choose - * the one with the most not yet colored neighbors. This heuristic is known as "DSatur", - * and was introduced in Daniel Brélaz: New methods to color the vertices of a graph, - * Commun. ACM 22, 4 (1979), 251–256. https://doi.org/10.1145/359094.359101 + * \enumval IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS Choose vertex with largest number of already colored neighbors. + * */ typedef enum { - IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0, - IGRAPH_COLORING_GREEDY_DSATUR = 1 + IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0 } igraph_coloring_greedy_t; -IGRAPH_EXPORT igraph_error_t igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic); +IGRAPH_EXPORT int igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_community.h b/src/vendor/cigraph/include/igraph_community.h index d81b8bd555e..aaaa2461ebd 100644 --- a/src/vendor/cigraph/include/igraph_community.h +++ b/src/vendor/cigraph/include/igraph_community.h @@ -25,24 +25,20 @@ #define IGRAPH_COMMUNITY_H #include "igraph_decls.h" - -#include "igraph_arpack.h" #include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_vector_list.h" +#include "igraph_arpack.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS /* -------------------------------------------------- */ -/* K-Cores and K-Truss */ +/* K-Cores */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_coreness( - const igraph_t *graph, igraph_vector_int_t *cores, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_trussness( - const igraph_t* graph, igraph_vector_int_t* trussness); +IGRAPH_EXPORT int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, + igraph_neimode_t mode); /* -------------------------------------------------- */ /* Community Structure */ @@ -52,17 +48,17 @@ IGRAPH_EXPORT igraph_error_t igraph_trussness( /* TODO: edge.type.matrix */ /* TODO: */ -IGRAPH_EXPORT igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, - igraph_vector_int_t *membership, + igraph_vector_t *membership, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_community_spinglass(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_spinglass(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -70,13 +66,17 @@ IGRAPH_EXPORT igraph_error_t igraph_community_spinglass(const igraph_t *graph, igraph_real_t coolfact, igraph_spincomm_update_t update_rule, igraph_real_t gamma, - igraph_spinglass_implementation_t implementation, - igraph_real_t gamma_minus); - -IGRAPH_EXPORT igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, + /* 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 lambda); + +IGRAPH_EXPORT int igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, - igraph_vector_int_t *community, + igraph_vector_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -85,69 +85,69 @@ IGRAPH_EXPORT igraph_error_t igraph_community_spinglass_single(const igraph_t *g igraph_spincomm_update_t update_rule, igraph_real_t gamma); -IGRAPH_EXPORT igraph_error_t igraph_community_walktrap(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, - igraph_integer_t steps, - igraph_matrix_int_t *merges, + int steps, + igraph_matrix_t *merges, igraph_vector_t *modularity, - igraph_vector_int_t *membership); + igraph_vector_t *membership); -IGRAPH_EXPORT igraph_error_t igraph_community_infomap(const igraph_t * graph, +IGRAPH_EXPORT int igraph_community_infomap(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights, - igraph_integer_t nb_trials, - igraph_vector_int_t *membership, + int nb_trials, + igraph_vector_t *membership, igraph_real_t *codelength); -IGRAPH_EXPORT igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, - igraph_vector_int_t *result, +IGRAPH_EXPORT int igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_t *result, igraph_vector_t *edge_betweenness, - igraph_matrix_int_t *merges, - igraph_vector_int_t *bridges, + igraph_matrix_t *merges, + igraph_vector_t *bridges, igraph_vector_t *modularity, - igraph_vector_int_t *membership, + igraph_vector_t *membership, igraph_bool_t directed, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_eb_get_merges(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_int_t *edges, + const igraph_vector_t *edges, const igraph_vector_t *weights, - igraph_matrix_int_t *merges, - igraph_vector_int_t *bridges, + igraph_matrix_t *merges, + igraph_vector_t *bridges, igraph_vector_t *modularity, - igraph_vector_int_t *membership); + igraph_vector_t *membership); -IGRAPH_EXPORT igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_int_t *merges, + igraph_matrix_t *merges, igraph_vector_t *modularity, - igraph_vector_int_t *membership); + igraph_vector_t *membership); -IGRAPH_EXPORT igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, +IGRAPH_EXPORT int igraph_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t nodes, igraph_integer_t steps, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize); -IGRAPH_EXPORT igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, + igraph_vector_t *membership, + igraph_vector_t *csize); +IGRAPH_EXPORT int igraph_le_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t steps, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize); + igraph_vector_t *membership, + igraph_vector_t *csize); -IGRAPH_EXPORT igraph_error_t igraph_modularity(const igraph_t *graph, - const igraph_vector_int_t *membership, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_modularity_matrix(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, - igraph_vector_int_t *new_to_old, +IGRAPH_EXPORT int igraph_reindex_membership(igraph_vector_t *membership, + igraph_vector_t *new_to_old, igraph_integer_t *nb_clusters); typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, @@ -168,7 +168,7 @@ typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, * \param membership The actual membership vector, before recording * the potential change implied by the newly found eigenvalue. * \param comm The id of the community that the algorithm tried to - * split in the last iteration. The community IDs are indexed from + * split in the last iteration. The community ids are indexed from * zero here! * \param eigenvalue The eigenvalue the algorithm has just found. * \param eigenvector The eigenvector corresponding to the eigenvalue @@ -184,67 +184,67 @@ typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, * igraph_arpack_function_t, \ref igraph_arpack_rssolve(). */ -typedef igraph_error_t igraph_community_leading_eigenvector_callback_t( - const igraph_vector_int_t *membership, - igraph_integer_t comm, +typedef int igraph_community_leading_eigenvector_callback_t( + 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); -IGRAPH_EXPORT igraph_error_t igraph_community_leading_eigenvector(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_leading_eigenvector(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_int_t *merges, - igraph_vector_int_t *membership, + 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_list_t *eigenvectors, + igraph_vector_ptr_t *eigenvectors, igraph_vector_t *history, igraph_community_leading_eigenvector_callback_t *callback, void *callback_extra); -IGRAPH_EXPORT igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_fluid_communities(const igraph_t *graph, igraph_integer_t no_of_communities, - igraph_vector_int_t *membership); + igraph_vector_t *membership, + igraph_real_t *modularity); -IGRAPH_EXPORT igraph_error_t igraph_community_label_propagation(const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_neimode_t mode, +IGRAPH_EXPORT int igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_t *membership, const igraph_vector_t *weights, - const igraph_vector_int_t *initial, - const igraph_vector_bool_t *fixed); + const igraph_vector_t *initial, + const igraph_vector_bool_t *fixed, + igraph_real_t *modularity); -IGRAPH_EXPORT igraph_error_t igraph_community_multilevel(const igraph_t *graph, +IGRAPH_EXPORT int igraph_community_multilevel(const igraph_t *graph, const igraph_vector_t *weights, const igraph_real_t resolution, - igraph_vector_int_t *membership, - igraph_matrix_int_t *memberships, + igraph_vector_t *membership, + igraph_matrix_t *memberships, igraph_vector_t *modularity); -IGRAPH_EXPORT igraph_error_t igraph_community_leiden(const igraph_t *graph, +IGRAPH_EXPORT 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, - const igraph_integer_t n_iterations, - igraph_vector_int_t *membership, + igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality); /* -------------------------------------------------- */ /* Community Structure Comparison */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, - const igraph_vector_int_t *comm2, +IGRAPH_EXPORT int igraph_compare_communities(const igraph_vector_t *comm1, + const igraph_vector_t *comm2, igraph_real_t* result, igraph_community_comparison_t method); -IGRAPH_EXPORT igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, - const igraph_vector_int_t *comm2, +IGRAPH_EXPORT int igraph_split_join_distance(const igraph_vector_t *comm1, + const igraph_vector_t *comm2, igraph_integer_t* distance12, igraph_integer_t* distance21); diff --git a/src/vendor/cigraph/include/igraph_complex.h b/src/vendor/cigraph/include/igraph_complex.h index d17b854bd70..2ff0b45aa88 100644 --- a/src/vendor/cigraph/include/igraph_complex.h +++ b/src/vendor/cigraph/include/igraph_complex.h @@ -40,13 +40,9 @@ typedef struct igraph_complex_t { IGRAPH_EXPORT igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); IGRAPH_EXPORT igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); -IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, - igraph_complex_t z2, - igraph_real_t tol); - -IGRAPH_EXPORT igraph_bool_t igraph_complex_almost_equals(igraph_complex_t z1, - igraph_complex_t z2, - igraph_real_t eps); +IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t tol); IGRAPH_EXPORT igraph_real_t igraph_complex_mod(igraph_complex_t z); IGRAPH_EXPORT igraph_real_t igraph_complex_arg(igraph_complex_t z); @@ -103,12 +99,6 @@ IGRAPH_EXPORT igraph_complex_t igraph_complex_sec(igraph_complex_t z); IGRAPH_EXPORT igraph_complex_t igraph_complex_csc(igraph_complex_t z); IGRAPH_EXPORT igraph_complex_t igraph_complex_cot(igraph_complex_t z); -IGRAPH_EXPORT int igraph_complex_printf(igraph_complex_t val); -IGRAPH_EXPORT int igraph_complex_fprintf(FILE *file, igraph_complex_t val); -IGRAPH_EXPORT int igraph_complex_printf_aligned(int width, igraph_complex_t val); -IGRAPH_EXPORT int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val); -IGRAPH_EXPORT int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_components.h b/src/vendor/cigraph/include/igraph_components.h index b3d05e44d20..b9c47972860 100644 --- a/src/vendor/cigraph/include/igraph_components.h +++ b/src/vendor/cigraph/include/igraph_components.h @@ -25,15 +25,11 @@ #define IGRAPH_COMPONENTS_H #include "igraph_decls.h" - #include "igraph_constants.h" -#include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_graph_list.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_list.h" -#include "igraph_vector_ptr.h" /* because of igraph_decompose_destroy() */ +#include "igraph_vector_ptr.h" +#include "igraph_datatype.h" __BEGIN_DECLS @@ -41,31 +37,24 @@ __BEGIN_DECLS /* Components */ /* -------------------------------------------------- */ -/* Deprecated alias to igraph_connected_components; will be removed in 0.11 */ -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no, +IGRAPH_EXPORT int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no, igraph_connectedness_t mode); -IGRAPH_EXPORT igraph_error_t igraph_connected_components(const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no, - igraph_connectedness_t mode); -IGRAPH_EXPORT igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, +IGRAPH_EXPORT int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, igraph_connectedness_t mode); -IGRAPH_EXPORT igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, +IGRAPH_EXPORT void igraph_decompose_destroy(igraph_vector_ptr_t *complist); +IGRAPH_EXPORT int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, igraph_connectedness_t mode, - igraph_integer_t maxcompno, igraph_integer_t minelements); -IGRAPH_EXPORT igraph_error_t igraph_articulation_points(const igraph_t *graph, - igraph_vector_int_t *res); -IGRAPH_EXPORT igraph_error_t igraph_biconnected_components(const igraph_t *graph, + long int maxcompno, long int minelements); +IGRAPH_EXPORT int igraph_articulation_points(const igraph_t *graph, + igraph_vector_t *res); +IGRAPH_EXPORT int igraph_biconnected_components(const igraph_t *graph, igraph_integer_t *no, - igraph_vector_int_list_t *tree_edges, - igraph_vector_int_list_t *component_edges, - igraph_vector_int_list_t *components, - igraph_vector_int_t *articulation_points); -IGRAPH_EXPORT igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges); - -/* Deprecated in igraph 0.10 when we switched to igraph_graph_list_t. Will be - * removed in 0.11 */ -IGRAPH_EXPORT IGRAPH_DEPRECATED void igraph_decompose_destroy(igraph_vector_ptr_t *complist); + igraph_vector_ptr_t *tree_edges, + igraph_vector_ptr_t *component_edges, + igraph_vector_ptr_t *components, + igraph_vector_t *articulation_points); +IGRAPH_EXPORT int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_config.h.in b/src/vendor/cigraph/include/igraph_config.h.in deleted file mode 100644 index 99e6feeecf9..00000000000 --- a/src/vendor/cigraph/include/igraph_config.h.in +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2011-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_CONFIG_H -#define IGRAPH_CONFIG_H - -#include "igraph_decls.h" - -__BEGIN_DECLS - -/** - * \define IGRAPH_INTEGER_SIZE - * - * Specifies the size of igraph's integer data type; must be one of 32 (for - * 32-bit integers) or 64 (for 64-bit integers). - */ -#define IGRAPH_INTEGER_SIZE @IGRAPH_INTEGER_SIZE@ - -#define IGRAPH_DEPRECATED_ENUMVAL @IGRAPH_DEPRECATED_ENUMVAL@ - -/** - * \define IGRAPH_BOOL_TYPE - * - * Specifies the C type to be used for igraph_bool_t. This is added here _only_ - * to support the R interface, where we want to be able to create views into - * R boolean vectors and treat them as an igraph_vector_bool_t, which requires - * us to align igraph_bool_t with R's boolean type. - * - * Any other use-case of overriding igraph's bool type is completely - * unsupported. - */ -#define IGRAPH_BOOL_TYPE bool - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_constants.h b/src/vendor/cigraph/include/igraph_constants.h index 2452c8c5a5d..3fcb305aba2 100644 --- a/src/vendor/cigraph/include/igraph_constants.h +++ b/src/vendor/cigraph/include/igraph_constants.h @@ -24,8 +24,9 @@ #ifndef IGRAPH_CONSTANTS_H #define IGRAPH_CONSTANTS_H -#include "igraph_config.h" #include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_datatype.h" __BEGIN_DECLS @@ -47,9 +48,9 @@ typedef enum { IGRAPH_ASCENDING = 0, IGRAPH_DESCENDING = 1 } igraph_order_t; typedef enum { IGRAPH_MINIMUM = 0, IGRAPH_MAXIMUM = 1 } igraph_optimal_t; -/* Do not renumber the following values! Some internal code treats them as bitmasks - * and assumes that IGRAPH_ALL == IGRAPH_IN | IGRAPH_OUT and IGRAPH_IN & IGRAPH_OUT == 0. */ -typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 3 } igraph_neimode_t; +typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 3, + IGRAPH_TOTAL = 3 + } igraph_neimode_t; /* Reverse IGRAPH_OUT to IGRAPH_IN and vice versa. Leave other values alone. */ #define IGRAPH_REVERSE_MODE(mode) \ @@ -62,10 +63,9 @@ typedef enum { IGRAPH_RECIPROCITY_DEFAULT = 0, } igraph_reciprocity_t; typedef enum { IGRAPH_ADJ_DIRECTED = 0, - IGRAPH_ADJ_UNDIRECTED, + IGRAPH_ADJ_UNDIRECTED = 1, IGRAPH_ADJ_MAX = 1, IGRAPH_ADJ_UPPER, IGRAPH_ADJ_LOWER, IGRAPH_ADJ_MIN, - IGRAPH_ADJ_PLUS, - IGRAPH_ADJ_MAX, + IGRAPH_ADJ_PLUS } igraph_adjacency_t; typedef enum { IGRAPH_STAR_OUT = 0, IGRAPH_STAR_IN, @@ -73,11 +73,6 @@ typedef enum { IGRAPH_STAR_OUT = 0, IGRAPH_STAR_IN, IGRAPH_STAR_MUTUAL } igraph_star_mode_t; -typedef enum { IGRAPH_WHEEL_OUT = 0, IGRAPH_WHEEL_IN, - IGRAPH_WHEEL_UNDIRECTED, - IGRAPH_WHEEL_MUTUAL - } igraph_wheel_mode_t; - typedef enum { IGRAPH_TREE_OUT = 0, IGRAPH_TREE_IN, IGRAPH_TREE_UNDIRECTED } igraph_tree_mode_t; @@ -91,16 +86,10 @@ typedef enum { IGRAPH_GET_ADJACENCY_UPPER = 0, IGRAPH_GET_ADJACENCY_BOTH } igraph_get_adjacency_t; -typedef enum { IGRAPH_DEGSEQ_CONFIGURATION = 0, /* Configuration model, allowing non-simple graphs */ - IGRAPH_DEGSEQ_VL, /* Viger-Latapy, generates simple connected graphs */ - IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, /* Fast heuristic, generates simple graphs */ - IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE, /* Configuration model, generates simple graphs */ - IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE, /* Edge-switching MCMC, generates simple graphs */ - - /* Deprecated, kept for backwards compatibility: */ - IGRAPH_DEGSEQ_SIMPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION, - IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, - IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE +typedef enum { IGRAPH_DEGSEQ_SIMPLE = 0, + IGRAPH_DEGSEQ_VL, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM } igraph_degseq_t; typedef enum { IGRAPH_REALIZE_DEGSEQ_SMALLEST = 0, @@ -192,6 +181,13 @@ typedef enum { IGRAPH_IMITATE_AUGMENTED = 0, IGRAPH_IMITATE_CONTRACTED } igraph_imitate_algorithm_t; +typedef igraph_real_t igraph_scalar_function_t(const igraph_vector_t *var, + const igraph_vector_t *par, + void* extra); +typedef void igraph_vector_function_t(const igraph_vector_t *var, + const igraph_vector_t *par, + igraph_vector_t* res, void* extra); + typedef enum { IGRAPH_LAYOUT_GRID = 0, IGRAPH_LAYOUT_NOGRID, IGRAPH_LAYOUT_AUTOGRID @@ -201,14 +197,6 @@ typedef enum { IGRAPH_RANDOM_WALK_STUCK_ERROR = 0, IGRAPH_RANDOM_WALK_STUCK_RETURN } igraph_random_walk_stuck_t; -typedef enum { IGRAPH_VORONOI_FIRST = 0, - IGRAPH_VORONOI_LAST, - IGRAPH_VORONOI_RANDOM - } igraph_voronoi_tiebreaker_t; - -typedef enum { IGRAPH_ROW_MAJOR = 0, - IGRAPH_COLUMN_MAJOR = 1 - } igraph_matrix_storage_t; __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_constructors.h b/src/vendor/cigraph/include/igraph_constructors.h index bd64dcf2157..d7b1987349e 100644 --- a/src/vendor/cigraph/include/igraph_constructors.h +++ b/src/vendor/cigraph/include/igraph_constructors.h @@ -26,12 +26,10 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" #include "igraph_graphicality.h" -#include "igraph_sparsemat.h" __BEGIN_DECLS @@ -39,65 +37,43 @@ __BEGIN_DECLS /* Constructors, deterministic */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, igraph_integer_t n, +IGRAPH_EXPORT int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - int first, ...); -IGRAPH_EXPORT igraph_error_t igraph_adjacency( - igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, - igraph_loops_t loops); -IGRAPH_EXPORT igraph_error_t igraph_weighted_adjacency( - igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, - igraph_vector_t *weights, igraph_loops_t loops); -IGRAPH_EXPORT igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops); -IGRAPH_EXPORT igraph_error_t igraph_sparse_weighted_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_vector_t *weights, igraph_loops_t loops); -IGRAPH_EXPORT igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, +IGRAPH_EXPORT int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + ...); +IGRAPH_EXPORT int igraph_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode); +IGRAPH_EXPORT int igraph_weighted_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode, const char* attr, + igraph_bool_t loops); +IGRAPH_EXPORT int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, igraph_integer_t center); -IGRAPH_EXPORT igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, - igraph_integer_t center); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_square_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, - igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *circular); -IGRAPH_EXPORT igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +IGRAPH_EXPORT int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, +IGRAPH_EXPORT int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, igraph_tree_mode_t type); -IGRAPH_EXPORT igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, - igraph_tree_mode_t type); -IGRAPH_EXPORT igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, - igraph_tree_mode_t type); -IGRAPH_EXPORT igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, - igraph_tree_mode_t type); -IGRAPH_EXPORT igraph_error_t igraph_tree_from_parent_vector(igraph_t *graph, const igraph_vector_int_t *parents, - igraph_tree_mode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); -IGRAPH_EXPORT igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_full_multipartite(igraph_t *graph, igraph_vector_int_t *types, const igraph_vector_int_t *n, - igraph_bool_t directed, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_turan(igraph_t *graph, igraph_vector_int_t *types, igraph_integer_t n, igraph_integer_t r); -IGRAPH_EXPORT igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); +IGRAPH_EXPORT int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT int igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number); -IGRAPH_EXPORT igraph_error_t igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, - const igraph_matrix_int_t *W, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); +IGRAPH_EXPORT int igraph_atlas(igraph_t *graph, int number); +IGRAPH_EXPORT int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, + const igraph_matrix_t *W, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); -IGRAPH_EXPORT igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *l, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k); -IGRAPH_EXPORT igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t igraph_famous(igraph_t *graph, const char *name); -IGRAPH_EXPORT igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, - const igraph_vector_int_t *shifts, +IGRAPH_EXPORT int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT int igraph_famous(igraph_t *graph, const char *name); +IGRAPH_EXPORT int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_t *shifts, igraph_integer_t repeats); -IGRAPH_EXPORT igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); -IGRAPH_EXPORT igraph_error_t igraph_realize_degree_sequence(igraph_t *graph, - const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, +IGRAPH_EXPORT int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_triangular_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); -IGRAPH_EXPORT igraph_error_t igraph_hexagonal_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_conversion.h b/src/vendor/cigraph/include/igraph_conversion.h index d47d7f677d9..d191dc28809 100644 --- a/src/vendor/cigraph/include/igraph_conversion.h +++ b/src/vendor/cigraph/include/igraph_conversion.h @@ -26,9 +26,9 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_spmatrix.h" #include "igraph_matrix.h" #include "igraph_sparsemat.h" #include "igraph_attributes.h" @@ -39,41 +39,27 @@ __BEGIN_DECLS /* Conversion */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_get_adjacency( - const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, - const igraph_vector_t *weights, igraph_loops_t loops -); -IGRAPH_EXPORT igraph_error_t igraph_get_adjacency_sparse( - const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, - const igraph_vector_t *weights, igraph_loops_t loops -); - -IGRAPH_EXPORT igraph_error_t igraph_get_stochastic( - const igraph_t *graph, igraph_matrix_t *matrix, igraph_bool_t column_wise, - const igraph_vector_t *weights -); - -IGRAPH_EXPORT igraph_error_t igraph_get_stochastic_sparse( - const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise, - const igraph_vector_t *weights -); +IGRAPH_EXPORT int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, + igraph_get_adjacency_t type, igraph_bool_t eids); +IGRAPH_EXPORT int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, + igraph_get_adjacency_t type); -/* Deprecated, will be removed in 0.11. Use igraph_get_adjacency_sparse() instead, paying attention to differences. */ -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); +IGRAPH_EXPORT int igraph_get_stochastic(const igraph_t *graph, + igraph_matrix_t *matrix, + igraph_bool_t column_wise); -/* Deprecated, will be removed in 0.11. Use igraph_get_stochastic_sparse() instead, paying attention to differences. */ -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, - igraph_sparsemat_t *res, +IGRAPH_EXPORT int igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *sparsemat, igraph_bool_t column_wise); -IGRAPH_EXPORT igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol); +IGRAPH_EXPORT int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol); -IGRAPH_EXPORT igraph_error_t igraph_to_directed(igraph_t *graph, +IGRAPH_EXPORT int igraph_to_directed(igraph_t *graph, igraph_to_directed_t flags); -IGRAPH_EXPORT igraph_error_t igraph_to_undirected(igraph_t *graph, +IGRAPH_EXPORT int igraph_to_undirected(igraph_t *graph, igraph_to_undirected_t mode, const igraph_attribute_combination_t *edge_comb); -IGRAPH_EXPORT igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); +IGRAPH_EXPORT int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_cycles.h b/src/vendor/cigraph/include/igraph_cycles.h deleted file mode 100644 index dd5870003b3..00000000000 --- a/src/vendor/cigraph/include/igraph_cycles.h +++ /dev/null @@ -1,30 +0,0 @@ - -#ifndef IGRAPH_CYCLES_H -#define IGRAPH_CYCLES_H - -#include "igraph_datatype.h" -#include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_types.h" -#include "igraph_vector_list.h" - -__BEGIN_DECLS - -IGRAPH_EXPORT igraph_error_t igraph_fundamental_cycles( - const igraph_t *graph, - igraph_vector_int_list_t *result, - igraph_integer_t start_vid, - igraph_integer_t bfs_cutoff, - const igraph_vector_t *weights); - -IGRAPH_EXPORT igraph_error_t igraph_minimum_cycle_basis( - const igraph_t *graph, - igraph_vector_int_list_t *result, - igraph_integer_t bfs_cutoff, - igraph_bool_t complete, - igraph_bool_t use_cycle_order, - const igraph_vector_t *weights); - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_datatype.h b/src/vendor/cigraph/include/igraph_datatype.h index c0ee080e864..5e2219255e0 100644 --- a/src/vendor/cigraph/include/igraph_datatype.h +++ b/src/vendor/cigraph/include/igraph_datatype.h @@ -30,44 +30,6 @@ __BEGIN_DECLS -struct igraph_i_property_cache_t; -typedef struct igraph_i_property_cache_t igraph_i_property_cache_t; - -typedef enum { - /* Stores whether the graph has at least one self-loop. */ - IGRAPH_PROP_HAS_LOOP = 0, - - /* Stores whether the graph has at least one multi-edge, taking into account - * edge directions in directed graphs. In other words, this property should - * be false for a directed graph with edges (a, b) and (b, a), and true - * for a directed graph with edges (a, b) and (a, b) again. */ - IGRAPH_PROP_HAS_MULTI, - - /* Stores whether the graph has at least one reciprocal edge pair. Ignored - * in undirected graphs. This property should be true for a directed graph - * with edges (a, b) and (b, a), and false for a directed graph with - * edges (a, b) and (a, b) again. Self-loops (a, a) are not considered - * reciprocal. */ - IGRAPH_PROP_HAS_MUTUAL, - - /* Stores whether the graph is weakly connected. */ - IGRAPH_PROP_IS_WEAKLY_CONNECTED, - - /* Stores whether the graph is strongly connected. Ignored in undirected graphs. */ - IGRAPH_PROP_IS_STRONGLY_CONNECTED, - - /* Stores whether the graph is a directed acyclic graph. Not used for - * undirected graphs. */ - IGRAPH_PROP_IS_DAG, - - /* Stores whether the graph is a forest, i.e. an undirected or directed - * graph that is cycle-free even if we ignore edge directions. */ - IGRAPH_PROP_IS_FOREST, - - /* Dummy value used to count enum values */ - IGRAPH_PROP_I_SIZE -} igraph_cached_property_t; - /** * \ingroup internal * \struct igraph_t @@ -110,18 +72,15 @@ typedef enum { typedef struct igraph_s { igraph_integer_t n; igraph_bool_t directed; - igraph_vector_int_t from; - igraph_vector_int_t to; - igraph_vector_int_t oi; - igraph_vector_int_t ii; - igraph_vector_int_t os; - igraph_vector_int_t is; + igraph_vector_t from; + igraph_vector_t to; + igraph_vector_t oi; + igraph_vector_t ii; + igraph_vector_t os; + igraph_vector_t is; void *attr; - igraph_i_property_cache_t *cache; } igraph_t; -IGRAPH_EXPORT void igraph_invalidate_cache(const igraph_t* graph); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_dqueue.h b/src/vendor/cigraph/include/igraph_dqueue.h index 53c8b0e4ba7..5aab71f3e71 100644 --- a/src/vendor/cigraph/include/igraph_dqueue.h +++ b/src/vendor/cigraph/include/igraph_dqueue.h @@ -25,7 +25,6 @@ #define IGRAPH_DQUEUE_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -40,6 +39,12 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL +#define BASE_LONG +#include "igraph_pmt.h" +#include "igraph_dqueue_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_LONG + #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" @@ -59,12 +64,9 @@ __BEGIN_DECLS #undef BASE_INT #define IGRAPH_DQUEUE_NULL { 0,0,0,0 } -#define IGRAPH_DQUEUE_INIT_FINALLY(q, capacity) \ - do { IGRAPH_CHECK(igraph_dqueue_init(q, capacity)); \ - IGRAPH_FINALLY(igraph_dqueue_destroy, q); } while (0) -#define IGRAPH_DQUEUE_INT_INIT_FINALLY(q, capacity) \ - do { IGRAPH_CHECK(igraph_dqueue_int_init(q, capacity)); \ - IGRAPH_FINALLY(igraph_dqueue_int_destroy, q); } while (0) +#define IGRAPH_DQUEUE_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_dqueue_init(v, size)); \ + IGRAPH_FINALLY(igraph_dqueue_destroy, v); } while (0) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_dqueue_pmt.h b/src/vendor/cigraph/include/igraph_dqueue_pmt.h index e0320195075..d478bdce31f 100644 --- a/src/vendor/cigraph/include/igraph_dqueue_pmt.h +++ b/src/vendor/cigraph/include/igraph_dqueue_pmt.h @@ -33,18 +33,17 @@ typedef struct TYPE(igraph_dqueue) { BASE *stor_end; } TYPE(igraph_dqueue); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity); -IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, empty)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT void FUNCTION(igraph_dqueue, clear)(TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, full)(TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, clear) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q); IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, head)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, back)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); -IGRAPH_DEPRECATED IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx); diff --git a/src/vendor/cigraph/include/igraph_eigen.h b/src/vendor/cigraph/include/igraph_eigen.h index 122eeb8a3a8..67ba26d4ad1 100644 --- a/src/vendor/cigraph/include/igraph_eigen.h +++ b/src/vendor/cigraph/include/igraph_eigen.h @@ -26,7 +26,6 @@ #include "igraph_decls.h" #include "igraph_arpack.h" -#include "igraph_error.h" #include "igraph_lapack.h" #include "igraph_sparsemat.h" @@ -64,7 +63,7 @@ typedef struct igraph_eigen_which_t { igraph_lapack_dgeevx_balance_t balance; } igraph_eigen_which_t; -IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, +IGRAPH_EXPORT int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -75,7 +74,7 @@ IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t igraph_vector_t *values, igraph_matrix_t *vectors); -IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, +IGRAPH_EXPORT int igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -86,7 +85,7 @@ IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors); -IGRAPH_EXPORT igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, +IGRAPH_EXPORT int igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, @@ -96,6 +95,17 @@ IGRAPH_EXPORT igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors); +IGRAPH_EXPORT 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); + + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_embedding.h b/src/vendor/cigraph/include/igraph_embedding.h index 2462b010c88..3eb97c67d76 100644 --- a/src/vendor/cigraph/include/igraph_embedding.h +++ b/src/vendor/cigraph/include/igraph_embedding.h @@ -26,13 +26,13 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_arpack.h" #include "igraph_eigen.h" +#include "igraph_constants.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, +IGRAPH_EXPORT int igraph_adjacency_spectral_embedding(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -50,7 +50,7 @@ typedef enum { IGRAPH_EMBEDDING_OAP } igraph_laplacian_spectral_embedding_type_t; -IGRAPH_EXPORT igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, +IGRAPH_EXPORT int igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -61,7 +61,7 @@ IGRAPH_EXPORT igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t igraph_vector_t *D, igraph_arpack_options_t *options); -IGRAPH_EXPORT igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); +IGRAPH_EXPORT int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_epidemics.h b/src/vendor/cigraph/include/igraph_epidemics.h index a13f8cbcd0f..52c1122c387 100644 --- a/src/vendor/cigraph/include/igraph_epidemics.h +++ b/src/vendor/cigraph/include/igraph_epidemics.h @@ -26,7 +26,6 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" @@ -56,10 +55,10 @@ typedef struct igraph_sir_t { igraph_vector_int_t no_s, no_i, no_r; } igraph_sir_t; -IGRAPH_EXPORT igraph_error_t igraph_sir_init(igraph_sir_t *sir); +IGRAPH_EXPORT int igraph_sir_init(igraph_sir_t *sir); IGRAPH_EXPORT void igraph_sir_destroy(igraph_sir_t *sir); -IGRAPH_EXPORT igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, +IGRAPH_EXPORT 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); diff --git a/src/vendor/cigraph/include/igraph_error.h b/src/vendor/cigraph/include/igraph_error.h index f1bea33ac66..c9d6e54a462 100644 --- a/src/vendor/cigraph/include/igraph_error.h +++ b/src/vendor/cigraph/include/igraph_error.h @@ -25,7 +25,6 @@ #define IGRAPH_ERROR_H #include "igraph_decls.h" -#include "igraph_config.h" #include @@ -36,32 +35,20 @@ __BEGIN_DECLS * prefix renamed to IGRAPH_), as I couldn't find a better way to do * them. */ -/* With some compilers, we use function attributes to help diagnostics - * and optimizations. These are not part of the public API, do not use - * them outside of igraph itself. - * - * IGRAPH_FUNCATTR_NORETURN indicates to the compiler that a function does not return. +/* IGRAPH_NORETURN indicates to the compiler that a function does not return. * There are standard facilities for this, namely _Noreturn in C11 and [[noreturn]] in C++11. * However, since igraph is currently compiled with older standards, and since * the standard 'noreturn' specification would need to be diferent between C and C++, * we do not use these facilities. - * - * IGRAPH_FUNCATTR_PRINTFLIKE(string, first) marks a function as having a printf-like syntax, - * allowing the compiler to check that the format specifiers match argument types. - * 'string' is the index of the string-argument and 'first' is the index of the - * first argument to check against format specifiers. */ #if defined(__GNUC__) /* Compilers that support the GNU C syntax. Use __noreturn__ instead of 'noreturn' as the latter is a macro in C11. */ -#define IGRAPH_FUNCATTR_NORETURN __attribute__((__noreturn__)) -#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) __attribute__((format(printf, string, first))) +#define IGRAPH_NORETURN __attribute__((__noreturn__)) #elif defined(_MSC_VER) /* Compilers that support the MSVC syntax. */ -#define IGRAPH_FUNCATTR_NORETURN __declspec(noreturn) -#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) +#define IGRAPH_NORETURN __declspec(noreturn) #else -#define IGRAPH_FUNCATTR_NORETURN -#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) +#define IGRAPH_NORETURN #endif /** @@ -133,8 +120,7 @@ __BEGIN_DECLS * * The contents of the rest of this chapter might be useful only * for those who want to create an interface to \a igraph from another - * language, or use igraph from a GUI application. Most readers can - * safely skip to the next chapter. + * language. Most readers can safely skip to the next chapter. * * * @@ -142,27 +128,17 @@ __BEGIN_DECLS * function of type \ref igraph_error_handler_t and calling * \ref igraph_set_error_handler(). This feature is useful for interface * writers, as \a igraph will have the chance to - * signal errors the appropriate way. For example, the R interface uses - * R's native printing facilities to communicate errors, while the Python - * interface converts them into Python exceptions. - * - * - * - * The two main tasks of the error handler are to report the error - * (i.e. print the error message) and ensure proper resource cleanup. - * This is ensured by calling \ref IGRAPH_FINALLY_FREE(), which deallocates - * some of the temporary memory to avoid memory leaks. Note that this may - * invalidate the error message buffer \p reason passed to the error handler. - * Do not access it after having called \ref IGRAPH_FINALLY_FREE(). + * signal errors the appropriate way, e.g. the R interface defines an + * error handler which calls the error() + * function, as required by R, while the Python interface has an error + * handler which raises an exception according to the Python way. * - * * - * As of \a igraph 0.10, temporary memory is dellocated in stages, through - * multiple calls to the error handler (and indirectly to \ref IGRAPH_FINALLY_FREE()). - * Therefore, error handlers that do not abort the program - * immediately are expected to return. The error handler should not perform - * a longjmp, as this may lead to some of the memory not - * getting freed. + * If you want to write an error handler, your error handler should + * call \ref IGRAPH_FINALLY_FREE() to deallocate all temporary memory to + * prevent memory leaks. Note that this may invalidate the error message + * buffer \p reason passed to the error handler. Do not access it after + * having called \ref IGRAPH_FINALLY_FREE(). * */ @@ -224,6 +200,78 @@ __BEGIN_DECLS * */ +/** + * \section error_handling_threads Error handling and threads + * + * + * It is likely that the \a igraph error handling + * method is \em not thread-safe, mainly because of + * the static global stack which is used to store the address of the + * temporarily allocated objects. This issue might be addressed in a + * later version of \a igraph. + * + */ + +/** + * \typedef igraph_error_handler_t + * \brief The type of error handler functions. + * + * This is the type of the error handler functions. + * \param reason Textual description of the error. + * \param file The source file in which the error is noticed. + * \param line The number of the line in the source file which triggered + * the error + * \param igraph_errno The \a igraph error code. + */ + +typedef void igraph_error_handler_t (const char *reason, const char *file, + int line, int igraph_errno); + +/** + * \var igraph_error_handler_abort + * \brief Abort program in case of error. + * + * The default error handler, prints an error message and aborts the + * program. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_abort; + +/** + * \var igraph_error_handler_ignore + * \brief Ignore errors. + * + * This error handler frees the temporarily allocated memory and returns + * with the error code. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_ignore; + +/** + * \var igraph_error_handler_printignore + * \brief Print and ignore errors. + * + * Frees temporarily allocated memory, prints an error message to the + * standard error and returns with the error code. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; + +/** + * \function igraph_set_error_handler + * \brief Sets a new error handler. + * + * Installs a new error handler. If called with 0, it installs the + * default error handler (which is currently + * \ref igraph_error_handler_abort). + * \param new_handler The error handler function to install. + * \return The old error handler function. This should be saved and + * restored if \p new_handler is not needed any + * more. + */ + +IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler); + /** * \typedef igraph_error_type_t * \brief Error code type. @@ -242,9 +290,9 @@ __BEGIN_DECLS * number was specified as the number of vertices. * \enumval IGRAPH_EXISTS A graph/vertex/edge attribute is already * installed with the given name. - * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex IDs. A vertex ID + * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex ids. A vertex id * is either negative or bigger than the number of vertices minus one. - * \enumval IGRAPH_EINVVID Invalid vertex ID, negative or too big. + * \enumval IGRAPH_EINVVID Invalid vertex id, negative or too big. * \enumval IGRAPH_NONSQUARE A non-square matrix was received while a * square matrix was expected. * \enumval IGRAPH_EINVMODE Invalid mode parameter. @@ -292,15 +340,12 @@ __BEGIN_DECLS * \enumval IGRAPH_EATTRCOMBINE Unimplemented attribute combination * method for the given attribute type. * \enumval IGRAPH_ELAPACK A LAPACK call resulted in an error. - * \enumval IGRAPH_EDRL Internal error in the DrL layout generator; not used - * any more (replaced by IGRAPH_EINTERNAL). + * \enumval IGRAPH_EDRL Internal error in the DrL layout generator. * \enumval IGRAPH_EOVERFLOW Integer or double overflow. * \enumval IGRAPH_EGLP Internal GLPK error. * \enumval IGRAPH_CPUTIME CPU time exceeded. * \enumval IGRAPH_EUNDERFLOW Integer or double underflow. * \enumval IGRAPH_ERWSTUCK Random walk got stuck. - * \enumval IGRAPH_ERANGE Maximum vertex or edge count exceeded. - * \enumval IGRAPH_ENOSOL Input problem has no solution. */ typedef enum { @@ -357,107 +402,21 @@ typedef enum { IGRAPH_EATTRIBUTES = 51, IGRAPH_EATTRCOMBINE = 52, IGRAPH_ELAPACK = 53, - IGRAPH_EDRL IGRAPH_DEPRECATED_ENUMVAL = 54, + IGRAPH_EDRL = 54, IGRAPH_EOVERFLOW = 55, IGRAPH_EGLP = 56, IGRAPH_CPUTIME = 57, IGRAPH_EUNDERFLOW = 58, IGRAPH_ERWSTUCK = 59, - IGRAPH_STOP = 60, - IGRAPH_ERANGE = 61, - IGRAPH_ENOSOL = 62 + IGRAPH_STOP = 60 /* undocumented, used internally */ } igraph_error_type_t; /* Each enum value above must have a corresponding error string in - * igraph_i_error_strings[] in core/error.c + * igraph_i_error_strings[] in igraph_error.c * * Information on undocumented codes: * - IGRAPH_STOP signals a request to stop in functions like igraph_i_maximal_cliques_bk() */ -/** - * \section error_handling_threads Error handling and threads - * - * - * It is likely that the \a igraph error handling - * method is \em not thread-safe, mainly because of - * the static global stack which is used to store the address of the - * temporarily allocated objects. This issue might be addressed in a - * later version of \a igraph. - * - */ - -/** - * \typedef igraph_error_t - * \brief Return type for functions returning an error code. - * - * This type is used as the return type of igraph functions that return an - * error code. It is a type alias because \type igraph_error_t used to be - * an \c int, and was used slightly differenly than \type igraph_error_type_t. - */ -typedef igraph_error_type_t igraph_error_t; - -/** - * \typedef igraph_error_handler_t - * \brief The type of error handler functions. - * - * This is the type of the error handler functions. - * - * \param reason Textual description of the error. - * \param file The source file in which the error is noticed. - * \param line The number of the line in the source file which triggered - * the error - * \param igraph_errno The \a igraph error code. - */ - -typedef void igraph_error_handler_t (const char *reason, const char *file, - int line, igraph_error_t igraph_errno); - -/** - * \var igraph_error_handler_abort - * \brief Abort program in case of error. - * - * The default error handler, prints an error message and aborts the - * program. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_abort; - -/** - * \var igraph_error_handler_ignore - * \brief Ignore errors. - * - * This error handler frees the temporarily allocated memory and returns - * with the error code. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_ignore; - -/** - * \var igraph_error_handler_printignore - * \brief Print and ignore errors. - * - * Frees temporarily allocated memory, prints an error message to the - * standard error and returns with the error code. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; - -/** - * \function igraph_set_error_handler - * \brief Sets a new error handler. - * - * Installs a new error handler. If called with \c NULL, it installs the - * default error handler (which is currently \ref igraph_error_handler_abort). - * - * \param new_handler The error handler function to install. - * \return The old error handler function. This should be saved and - * restored if \p new_handler is not needed any - * more. - */ - -IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler); - - /* We use IGRAPH_FILE_BASENAME instead of __FILE__ to ensure that full * paths don't leak into the library code. IGRAPH_FILE_BASENAME is set up * by the build system when compiling the individual files. However, when @@ -470,7 +429,7 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand /** * \define IGRAPH_ERROR - * \brief Triggers an error. + * \brief Trigger an error. * * \a igraph functions usually use this macro when they notice an error. * It calls @@ -479,7 +438,6 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand * code. If for some (suspicious) reason you want to call the error * handler without returning from the current function, call * \ref igraph_error() directly. - * * \param reason Textual description of the error. This should be * something more descriptive than the text associated with the error * code. E.g. if the error code is \c IGRAPH_EINVAL, @@ -502,7 +460,7 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand /** * \function igraph_error - * \brief Reports an error. + * \brief Triggers an error. * * \a igraph functions usually call this function (most often via the * \ref IGRAPH_ERROR macro) if they notice an error. @@ -519,8 +477,8 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand * \sa igraph_errorf(). */ -IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, int line, - igraph_error_t igraph_errno); +IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, + int igraph_errno); /** * \define IGRAPH_ERRORF @@ -533,7 +491,6 @@ IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, * error code. If for some (suspicious) reason you want to call the * error handler without returning from the current function, call * \ref igraph_errorf() directly. - * * \param reason Textual description of the error, a template string * with the same syntax as the standard printf C library function. * This should be something more descriptive than the text associated @@ -555,7 +512,7 @@ IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, /** * \function igraph_errorf - * \brief Reports an error, printf-like version. + * \brief Triggers an error, printf-like version. * * \param reason Textual description of the error, interpreted as * a \c printf format string. @@ -568,12 +525,11 @@ IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, * \sa igraph_error(). */ -IGRAPH_FUNCATTR_PRINTFLIKE(1,5) -IGRAPH_EXPORT igraph_error_t igraph_errorf(const char *reason, const char *file, int line, - igraph_error_t igraph_errno, ...); +IGRAPH_EXPORT int igraph_errorf(const char *reason, const char *file, int line, + int igraph_errno, ...); -IGRAPH_EXPORT igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, - igraph_error_t igraph_errno, va_list ap); +IGRAPH_EXPORT int igraph_errorvf(const char *reason, const char *file, int line, + int igraph_errno, va_list ap); /** * \function igraph_strerror @@ -586,7 +542,7 @@ IGRAPH_EXPORT igraph_error_t igraph_errorvf(const char *reason, const char *file * \return pointer to the textual description of the error code. */ -IGRAPH_EXPORT const char* igraph_strerror(const igraph_error_t igraph_errno); +IGRAPH_EXPORT const char* igraph_strerror(const int igraph_errno); #define IGRAPH_ERROR_SELECT_2(a,b) ((a) != IGRAPH_SUCCESS ? (a) : ((b) != IGRAPH_SUCCESS ? (b) : IGRAPH_SUCCESS)) #define IGRAPH_ERROR_SELECT_3(a,b,c) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_2(b,c)) @@ -599,7 +555,7 @@ IGRAPH_EXPORT const char* igraph_strerror(const igraph_error_t igraph_errno); * information. We don't use the exception handling code though. */ struct igraph_i_protectedPtr { - int level; + int all; void *ptr; void (*func)(void*); }; @@ -613,18 +569,8 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); * \brief Signals clean deallocation of objects. * * Removes the specified number of objects from the stack of - * temporarily allocated objects. It is typically called - * immediately after manually destroying the objects: - * - * - * igraph_vector_t vector; - * igraph_vector_init(&vector, 10); - * IGRAPH_FINALLY(igraph_vector_destroy, &vector); - * // use vector - * igraph_vector_destroy(&vector); - * IGRAPH_FINALLY_CLEAN(1); - * - * + * temporarily allocated objects. Most often this is called just + * before returning from a function. * \param num The number of objects to remove from the bookkeeping * stack. */ @@ -633,13 +579,11 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); /** * \function IGRAPH_FINALLY_FREE - * \brief Deallocates objects registered at the current level. + * \brief Deallocates all registered objects. * - * Calls the destroy function for all objects in the current level - * of the stack of temporarily allocated objects, i.e. up to the - * nearest mark set by IGRAPH_FINALLY_ENTER(). - * This function must only be called from an error handler. - * It is \em not appropriate to use it + * Calls the destroy function for all objects in the stack of + * temporarily allocated objects. This is usually called only from an + * error handler. It is \em not appropriate to use it * instead of destroying each unneeded object of a function, as it * destroys the temporary objects of the caller function (and so on) * as well. @@ -647,9 +591,6 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); IGRAPH_EXPORT void IGRAPH_FINALLY_FREE(void); -IGRAPH_EXPORT void IGRAPH_FINALLY_ENTER(void); -IGRAPH_EXPORT void IGRAPH_FINALLY_EXIT(void); - /** * \function IGRAPH_FINALLY_STACK_SIZE * \brief The number of registered objects. @@ -679,16 +620,14 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); /** * \define IGRAPH_FINALLY * \brief Registers an object for deallocation. + * \param func The address of the function which is normally called to + * destroy the object. + * \param ptr Pointer to the object itself. * * This macro places the address of an object, together with the * address of its destructor in a stack. This stack is used if an * error happens to deallocate temporarily allocated objects to - * prevent memory leaks. After manual deallocation, objects are removed - * from the stack using \ref IGRAPH_FINALLY_CLEAN(). - * - * \param func The function which is normally called to - * destroy the object. - * \param ptr Pointer to the object itself. + * prevent memory leaks. */ #define IGRAPH_FINALLY(func, ptr) \ @@ -716,12 +655,12 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); #define IGRAPH_CHECK(a) \ do { \ int enter_stack_size = IGRAPH_FINALLY_STACK_SIZE(); \ - igraph_error_t igraph_i_ret=(a); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ + int igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ IGRAPH_ERROR("", igraph_i_ret); \ } \ if (IGRAPH_UNLIKELY(enter_stack_size != IGRAPH_FINALLY_STACK_SIZE())) { \ - IGRAPH_FATAL("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN."); \ + IGRAPH_ERROR("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN", IGRAPH_FAILURE); \ } \ } while (0) #else @@ -729,8 +668,7 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * \define IGRAPH_CHECK * \brief Checks the return value of a function call. * - * \param expr An expression, usually a function call. It is guaranteed to - * be evaluated only once. + * \param a An expression, usually a function call. * * Executes the expression and checks its value. If this is not * \c IGRAPH_SUCCESS, it calls \ref IGRAPH_ERROR with @@ -748,67 +686,14 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * by using IGRAPH_CHECK on every \a igraph * call which can return an error code. */ -#define IGRAPH_CHECK(expr) \ - do { \ - igraph_error_t igraph_i_ret = (expr); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ +#define IGRAPH_CHECK(a) do { \ + int igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ IGRAPH_ERROR("", igraph_i_ret); \ - } \ - } while (0) + } } while (0) #endif -/** - * \define IGRAPH_CHECK_CALLBACK - * \brief Checks the return value of a callback. - * - * Identical to \ref IGRAPH_CHECK, but treats \c IGRAPH_STOP as a normal - * (non-erroneous) return code. This macro is used in some igraph functions - * that allow the user to hook into a long-running calculation with a callback - * function. When the user-defined callback function returns \c IGRAPH_SUCCESS, - * the calculation will proceed normally. Returning \c IGRAPH_STOP from the - * callback will terminate the calculation without reporting an error. Returning - * any other value from the callback is treated as an error code, and igraph - * will trigger the necessary cleanup functions before exiting the function. - * - * - * Note that \c IGRAPH_CHECK_CALLBACK does not handle \c IGRAPH_STOP by any - * means except returning it in the variable pointed to by \c code. It is the - * responsibility of the caller to handle \c IGRAPH_STOP accordingly. - * - * \param expr An expression, usually a call to a user-defined callback function. - * It is guaranteed to be evaluated only once. - * \param code Pointer to an optional variable of type \c igraph_error_t; the - * value of this variable will be set to the error code if it is not a null - * pointer. - */ -#define IGRAPH_CHECK_CALLBACK(expr, code) \ - do { \ - igraph_error_t igraph_i_ret = (expr); \ - if (code) { \ - *(code) = igraph_i_ret; \ - } \ - if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS && igraph_i_ret != IGRAPH_STOP)) { \ - IGRAPH_ERROR("", igraph_i_ret); \ - } \ - } while (0) -/** - * \define IGRAPH_CHECK_OOM - * \brief Checks for out-of-memory conditions after a memory allocation. - * - * This function should be called on pointers after memory allocations. The - * function checks whether the returned pointer is NULL, and if so, sets an - * error message with the \c IGRAPH_ENOMEM error code. - * - * \param ptr The pointer to check. - * \param message The error message to use when the pointer is \c NULL. - */ -#define IGRAPH_CHECK_OOM(ptr, message) \ - do { \ - if (IGRAPH_UNLIKELY(!ptr)) { \ - IGRAPH_ERROR(message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ - } \ - } while (0) /** * \section about_igraph_warnings Warning messages @@ -845,7 +730,7 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * argument is not used. */ -typedef void igraph_warning_handler_t (const char *reason, const char *file, int line); +typedef igraph_error_handler_t igraph_warning_handler_t; /** * \function igraph_set_warning_handler @@ -865,11 +750,10 @@ IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; /** * \function igraph_warning - * \brief Reports a warning. + * \brief Triggers a warning. * * Call this function if you want to trigger a warning from within * a function that uses \a igraph. - * * \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 @@ -879,7 +763,8 @@ IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; * \return The supplied error code. */ -IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line); +IGRAPH_EXPORT int igraph_warning(const char *reason, const char *file, int line, + int igraph_errno); /** * \define IGRAPH_WARNINGF @@ -898,19 +783,18 @@ IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line #define IGRAPH_WARNINGF(reason, ...) \ do { \ igraph_warningf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ - __VA_ARGS__); \ + -1, __VA_ARGS__); \ } while (0) /** * \function igraph_warningf - * \brief Reports a warning, printf-like version. + * \brief Triggers a warning, printf-like version. * * This function is similar to \ref igraph_warning(), but * uses a printf-like syntax. It substitutes the additional arguments * into the \p reason template string and calls \ref igraph_warning(). - * * \param reason Textual description of the warning, a template string * with the same syntax as the standard printf C library function. * \param file The source file in which the warning was noticed. @@ -920,10 +804,11 @@ IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line * but this is currently not used in igraph. * \param ... The additional arguments to be substituted into the * template string. + * \return The supplied error code. */ -IGRAPH_FUNCATTR_PRINTFLIKE(1,4) -IGRAPH_EXPORT void igraph_warningf(const char *reason, const char *file, int line, ...); +IGRAPH_EXPORT int igraph_warningf(const char *reason, const char *file, int line, + int igraph_errno, ...); /** * \define IGRAPH_WARNING @@ -936,7 +821,7 @@ IGRAPH_EXPORT void igraph_warningf(const char *reason, const char *file, int lin #define IGRAPH_WARNING(reason) \ do { \ - igraph_warning(reason, IGRAPH_FILE_BASENAME, __LINE__); \ + igraph_warning(reason, IGRAPH_FILE_BASENAME, __LINE__, -1); \ } while (0) @@ -1020,7 +905,7 @@ IGRAPH_EXPORT igraph_fatal_handler_t igraph_fatal_handler_abort; * \param line The number of line in the source file which triggered the error. */ -IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatal(const char *reason, const char *file, int line); +IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatal(const char *reason, const char *file, int line); /** * \function igraph_fatalf @@ -1036,8 +921,7 @@ IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatal(const char *reason, con * \param ... The additional arguments to be substituted into the template string. */ -IGRAPH_FUNCATTR_PRINTFLIKE(1,4) -IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatalf(const char *reason, const char *file, int line, ...); +IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char *file, int line, ...); /** * \define IGRAPH_FATALF @@ -1094,7 +978,7 @@ IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatalf(const char *reason, co * This macro is meant for internal use by \a igraph. * * - * Since a typical fatal error handler does a longjmp(), avoid using this + * Since a typial fatal error handler does a longjmp(), avoid using this * macro in C++ code. With most compilers, destructor will not be called when * longjmp() leaves the current scope. * @@ -1103,7 +987,7 @@ IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatalf(const char *reason, co #define IGRAPH_ASSERT(condition) \ do { \ - if (IGRAPH_UNLIKELY(!(condition))) { \ + if (!(condition)) { \ igraph_fatal("Assertion failed: " #condition, IGRAPH_FILE_BASENAME, __LINE__); \ } \ } while (0) diff --git a/src/vendor/cigraph/include/igraph_eulerian.h b/src/vendor/cigraph/include/igraph_eulerian.h index bb9a089a7b4..79f669fd81f 100644 --- a/src/vendor/cigraph/include/igraph_eulerian.h +++ b/src/vendor/cigraph/include/igraph_eulerian.h @@ -26,13 +26,12 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); -IGRAPH_EXPORT igraph_error_t igraph_eulerian_path(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); -IGRAPH_EXPORT igraph_error_t igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); +IGRAPH_EXPORT int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); +IGRAPH_EXPORT int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); +IGRAPH_EXPORT int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_flow.h b/src/vendor/cigraph/include/igraph_flow.h index 354f29f7e7b..37ee4179b64 100644 --- a/src/vendor/cigraph/include/igraph_flow.h +++ b/src/vendor/cigraph/include/igraph_flow.h @@ -26,10 +26,9 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -39,7 +38,8 @@ __BEGIN_DECLS /** * \typedef igraph_maxflow_stats_t - * \brief Data structure holding statistics from the push-relabel maximum flow solver. + * A simple data type to return some statistics from the + * push-relabel maximum flow solver. * * \param nopush The number of push operations performed. * \param norelabel The number of relabel operarions performed. @@ -54,101 +54,101 @@ __BEGIN_DECLS */ typedef struct { - igraph_integer_t nopush, norelabel, nogap, nogapnodes, nobfs; + int nopush, norelabel, nogap, nogapnodes, nobfs; } igraph_maxflow_stats_t; -IGRAPH_EXPORT igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, - igraph_vector_t *flow, igraph_vector_int_t *cut, - igraph_vector_int_t *partition, igraph_vector_int_t *partition2, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_t *cut, igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_mincut(const igraph_t *graph, +IGRAPH_EXPORT int igraph_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, - igraph_vector_int_t *cut, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_vector_t *cut, const igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_st_vertex_connectivity(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); /* s-t cut listing related stuff */ -IGRAPH_EXPORT igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, +IGRAPH_EXPORT int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_residual_graph(const igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, +IGRAPH_EXPORT int igraph_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow); -IGRAPH_EXPORT igraph_error_t igraph_dominator_tree(const igraph_t *graph, +IGRAPH_EXPORT int igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, - igraph_vector_int_t *dom, + igraph_vector_t *dom, igraph_t *domtree, - igraph_vector_int_t *leftout, + igraph_vector_t *leftout, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_all_st_cuts(const igraph_t *graph, - igraph_vector_int_list_t *cuts, - igraph_vector_int_list_t *partition1s, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_list_t *cuts, - igraph_vector_int_list_t *partition1s, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph, +IGRAPH_EXPORT int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, igraph_vector_t *flows, const igraph_vector_t *capacity); diff --git a/src/vendor/cigraph/include/igraph_foreign.h b/src/vendor/cigraph/include/igraph_foreign.h index a0625cba62b..e4a94422277 100644 --- a/src/vendor/cigraph/include/igraph_foreign.h +++ b/src/vendor/cigraph/include/igraph_foreign.h @@ -27,7 +27,6 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_strvector.h" @@ -39,75 +38,48 @@ __BEGIN_DECLS /* Read and write foreign formats */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_ncol(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, - igraph_integer_t index); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT int igraph_read_graph_pajek(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, + int index); +IGRAPH_EXPORT int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, igraph_strvector_t *problem, - igraph_vector_int_t *label, + igraph_vector_t *label, igraph_integer_t *source, igraph_integer_t *target, igraph_vector_t *capacity, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_dimacs_flow(igraph_t *graph, FILE *instream, - igraph_strvector_t *problem, - igraph_vector_int_t *label, - igraph_integer_t *source, - igraph_integer_t *target, - igraph_vector_t *capacity, - igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream); -IGRAPH_EXPORT igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT int igraph_read_graph_gml(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed); -typedef unsigned int igraph_write_gml_sw_t; - -enum { - IGRAPH_WRITE_GML_DEFAULT_SW = 0x0, /* default settings */ - IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW = 0x1 /* only encode " characters, nothing else */ -}; - -IGRAPH_EXPORT igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, const char *names, const char *weights); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, const char *names, const char *weights, igraph_bool_t isolates); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, igraph_bool_t prefixattr); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, - igraph_integer_t source, igraph_integer_t target, +IGRAPH_EXPORT int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + long int source, long int target, const igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, - igraph_integer_t source, igraph_integer_t target, - const igraph_vector_t *capacity); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, - igraph_write_gml_sw_t options, - const igraph_vector_t *id, const char *creator); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + const igraph_vector_t *id, const char *creator); +IGRAPH_EXPORT int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, const char* vertex_attr_name, const char* edge_attr_name); -/* -------------------------------------------------- */ -/* Convenience functions for temporary locale setting */ -/* -------------------------------------------------- */ - -typedef struct igraph_safelocale_s *igraph_safelocale_t; - -IGRAPH_EXPORT igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc); -IGRAPH_EXPORT void igraph_exit_safelocale(igraph_safelocale_t *loc); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_games.h b/src/vendor/cigraph/include/igraph_games.h index 1214ad2abd2..df5f6a91edc 100644 --- a/src/vendor/cigraph/include/igraph_games.h +++ b/src/vendor/cigraph/include/igraph_games.h @@ -26,13 +26,11 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_matrix.h" -#include "igraph_matrix_list.h" #include "igraph_types.h" +#include "igraph_matrix.h" #include "igraph_vector.h" -#include "igraph_vector_list.h" +#include "igraph_datatype.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -40,31 +38,31 @@ __BEGIN_DECLS /* Constructors, games (=stochastic) */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, - const igraph_vector_int_t *outseq, + 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); -IGRAPH_EXPORT igraph_error_t igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, - const igraph_vector_int_t *in_deg, +IGRAPH_EXPORT int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, + const igraph_vector_t *in_deg, igraph_degseq_t method); -IGRAPH_EXPORT igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation); -IGRAPH_EXPORT igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, +IGRAPH_EXPORT int igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -74,18 +72,18 @@ IGRAPH_EXPORT igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, igraph_real_t deg_coef, igraph_real_t age_coef, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t window, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t zero_appeal, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, +IGRAPH_EXPORT int igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -93,131 +91,131 @@ IGRAPH_EXPORT igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t window, igraph_real_t zero_appeal, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT 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_int_t *node_type_vec); -IGRAPH_EXPORT igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, + igraph_vector_t *node_type_vec); +IGRAPH_EXPORT 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_int_t *node_type_vec); -IGRAPH_EXPORT igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, + igraph_vector_t *node_type_vec); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT 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_int_t *node_type_vec, + igraph_vector_t *node_type_vec, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT 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_int_t *node_type_out_vec, - igraph_vector_int_t *node_type_in_vec, + igraph_vector_t *node_type_out_vec, + igraph_vector_t *node_type_in_vec, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, +IGRAPH_EXPORT int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_bool_t multiple); -IGRAPH_EXPORT igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, +IGRAPH_EXPORT int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_lastcit_game(igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_int_t *types, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_int_t *types, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT 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 ambs, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_simple_interconnected_islands_game( +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_static_power_law_game(igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_k_regular_game(igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *mlist, - const igraph_vector_list_t *rholist, - const igraph_matrix_list_t *Clist, + const igraph_vector_ptr_t *rholist, + const igraph_vector_ptr_t *Clist, igraph_real_t p); -IGRAPH_EXPORT igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, +IGRAPH_EXPORT int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, igraph_real_t corr, igraph_real_t p, - const igraph_vector_int_t *permutation); + const igraph_vector_t *permutation); -IGRAPH_EXPORT igraph_error_t igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2, +IGRAPH_EXPORT 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_int_t *permutation); + const igraph_vector_t *permutation); -IGRAPH_EXPORT igraph_error_t igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +IGRAPH_EXPORT int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method); -IGRAPH_EXPORT igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, +IGRAPH_EXPORT int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, +IGRAPH_EXPORT int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, igraph_matrix_t *res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_graph_list.h b/src/vendor/cigraph/include/igraph_graph_list.h deleted file mode 100644 index 7f8232621a0..00000000000 --- a/src/vendor/cigraph/include/igraph_graph_list.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -*- 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_GRAPH_LIST_H -#define IGRAPH_GRAPH_LIST_H - -#include "igraph_datatype.h" -#include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* List of graphs */ -/* -------------------------------------------------- */ - -#define GRAPH_LIST -#define BASE_GRAPH -#define EXTRA_TYPE_FIELDS igraph_bool_t directed; -#include "igraph_pmt.h" -#include "igraph_typed_list_pmt.h" -#include "igraph_pmt_off.h" -#undef EXTRA_TYPE_FIELDS -#undef BASE_GRAPH -#undef GRAPH_LIST - -void igraph_graph_list_set_directed(igraph_graph_list_t* list, igraph_bool_t directed); - -/* -------------------------------------------------- */ -/* Helper macros */ -/* -------------------------------------------------- */ - -#define IGRAPH_GRAPH_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_graph_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_graph_list_destroy, v); } while (0) - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_graphicality.h b/src/vendor/cigraph/include/igraph_graphicality.h index f639217b5b1..02feabfbeaa 100644 --- a/src/vendor/cigraph/include/igraph_graphicality.h +++ b/src/vendor/cigraph/include/igraph_graphicality.h @@ -22,12 +22,11 @@ #define IGRAPH_GRAPHICALITY_H #include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_vector.h" +#include "igraph_datatype.h" __BEGIN_DECLS -typedef unsigned int igraph_edge_type_sw_t; +typedef unsigned char igraph_edge_type_sw_t; /* * bit 0: self-loops alowed? @@ -40,16 +39,27 @@ enum { IGRAPH_MULTI_SW = 0x06 /* 110 */ }; -IGRAPH_EXPORT igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, - const igraph_vector_int_t *in_degrees, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, - const igraph_vector_int_t *degrees2, +IGRAPH_EXPORT 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); + +/* Legacy functions (deprecated): */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + igraph_bool_t *res); + +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, + const igraph_vector_t *in_degrees, + igraph_bool_t *res); + __END_DECLS #endif // IGRAPH_GRAPHICALITY_H diff --git a/src/vendor/cigraph/include/igraph_graphlets.h b/src/vendor/cigraph/include/igraph_graphlets.h index b80165a6966..6b5285362c7 100644 --- a/src/vendor/cigraph/include/igraph_graphlets.h +++ b/src/vendor/cigraph/include/igraph_graphlets.h @@ -25,28 +25,27 @@ #define IGRAPH_GRAPHLETS_H #include "igraph_decls.h" - #include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" +#include "igraph_interface.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, +IGRAPH_EXPORT int igraph_graphlets_candidate_basis(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *cliques, + igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds); -IGRAPH_EXPORT igraph_error_t igraph_graphlets_project(const igraph_t *graph, +IGRAPH_EXPORT int igraph_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, - const igraph_vector_int_list_t *cliques, + const igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, - igraph_integer_t niter); + int niter); -IGRAPH_EXPORT igraph_error_t igraph_graphlets(const igraph_t *graph, +IGRAPH_EXPORT int igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *cliques, - igraph_vector_t *Mu, igraph_integer_t niter); + igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, int niter); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_heap.h b/src/vendor/cigraph/include/igraph_heap.h index 3eb81c2f972..37292260a3b 100644 --- a/src/vendor/cigraph/include/igraph_heap.h +++ b/src/vendor/cigraph/include/igraph_heap.h @@ -25,8 +25,6 @@ #define IGRAPH_HEAP_H #include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_types.h" __BEGIN_DECLS @@ -52,7 +50,7 @@ __BEGIN_DECLS #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL -#define BASE_INT +#define BASE_LONG #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "igraph_heap_pmt.h" @@ -63,7 +61,7 @@ __BEGIN_DECLS #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN -#undef BASE_INT +#undef BASE_LONG #define BASE_CHAR #define HEAP_TYPE_MAX diff --git a/src/vendor/cigraph/include/igraph_heap_pmt.h b/src/vendor/cigraph/include/igraph_heap_pmt.h index f4b12bb6344..437a1996521 100644 --- a/src/vendor/cigraph/include/igraph_heap_pmt.h +++ b/src/vendor/cigraph/include/igraph_heap_pmt.h @@ -25,16 +25,15 @@ typedef struct TYPE(igraph_heap) { BASE* stor_begin; BASE* stor_end; BASE* end; - igraph_bool_t destroy; + int destroy; } TYPE(igraph_heap); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, const BASE *data, igraph_integer_t len); +IGRAPH_EXPORT int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, BASE* data, long int len); IGRAPH_EXPORT void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_heap, empty)(const TYPE(igraph_heap)* h); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); -IGRAPH_EXPORT BASE FUNCTION(igraph_heap, top)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); +IGRAPH_EXPORT BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h); IGRAPH_EXPORT BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity); +IGRAPH_EXPORT long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size); diff --git a/src/vendor/cigraph/include/igraph_hrg.h b/src/vendor/cigraph/include/igraph_hrg.h index 40b699c4c8d..e81efb24696 100644 --- a/src/vendor/cigraph/include/igraph_hrg.h +++ b/src/vendor/cigraph/include/igraph_hrg.h @@ -25,17 +25,15 @@ #define IGRAPH_HRG_H #include "igraph_decls.h" - -#include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_graph_list.h" #include "igraph_vector.h" +#include "igraph_vector_ptr.h" +#include "igraph_datatype.h" __BEGIN_DECLS /** * \struct igraph_hrg_t - * \brief Data structure to store a hierarchical random graph. + * Data structure to store a hierarchical random graph * * A hierarchical random graph (HRG) can be given as a binary tree, * where the internal vertices are labeled with real numbers. @@ -47,7 +45,6 @@ __BEGIN_DECLS * * * It has the following members: - * * \member left Vector that contains the left children of the internal * tree vertices. The first vertex is always the root vertex, so * the first element of the vector is the left child of the root @@ -68,55 +65,48 @@ __BEGIN_DECLS */ typedef struct igraph_hrg_t { - igraph_vector_int_t left; - igraph_vector_int_t right; - igraph_vector_t prob; - igraph_vector_int_t vertices; - igraph_vector_int_t edges; + igraph_vector_t left, right, prob, edges, vertices; } igraph_hrg_t; -IGRAPH_EXPORT igraph_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n); +IGRAPH_EXPORT int igraph_hrg_init(igraph_hrg_t *hrg, int n); IGRAPH_EXPORT void igraph_hrg_destroy(igraph_hrg_t *hrg); -IGRAPH_EXPORT igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg); -IGRAPH_EXPORT igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize); - -IGRAPH_EXPORT igraph_error_t igraph_hrg_fit( - const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t steps -); +IGRAPH_EXPORT int igraph_hrg_size(const igraph_hrg_t *hrg); +IGRAPH_EXPORT int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize); -IGRAPH_EXPORT igraph_error_t igraph_hrg_sample( - const igraph_hrg_t *hrg, igraph_t *sample -); +IGRAPH_EXPORT int igraph_hrg_fit(const igraph_t *graph, + igraph_hrg_t *hrg, + igraph_bool_t start, + int steps); -IGRAPH_EXPORT igraph_error_t igraph_hrg_sample_many( - const igraph_hrg_t *hrg, igraph_graph_list_t *samples, - igraph_integer_t num_samples -); +IGRAPH_EXPORT int igraph_hrg_sample(const igraph_t *graph, + igraph_t *sample, + igraph_vector_ptr_t *samples, + igraph_integer_t no_samples, + igraph_hrg_t *hrg, + igraph_bool_t start); -IGRAPH_EXPORT igraph_error_t igraph_hrg_game( - igraph_t *graph, const igraph_hrg_t *hrg -); +IGRAPH_EXPORT int igraph_hrg_game(igraph_t *graph, + const igraph_hrg_t *hrg); -IGRAPH_EXPORT igraph_error_t igraph_hrg_dendrogram(igraph_t *graph, +IGRAPH_EXPORT int igraph_hrg_dendrogram(igraph_t *graph, const igraph_hrg_t *hrg); -IGRAPH_EXPORT igraph_error_t igraph_hrg_consensus(const igraph_t *graph, - igraph_vector_int_t *parents, +IGRAPH_EXPORT int igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t num_samples); + int num_samples); -IGRAPH_EXPORT igraph_error_t igraph_hrg_predict(const igraph_t *graph, - igraph_vector_int_t *edges, +IGRAPH_EXPORT int igraph_hrg_predict(const igraph_t *graph, + igraph_vector_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t num_samples, - igraph_integer_t num_bins); + int num_samples, + int num_bins); -IGRAPH_EXPORT igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, +IGRAPH_EXPORT int igraph_hrg_create(igraph_hrg_t *hrg, const igraph_t *graph, const igraph_vector_t *prob); diff --git a/src/vendor/cigraph/include/igraph_interface.h b/src/vendor/cigraph/include/igraph_interface.h index ccab1b67f5f..d8ec5689020 100644 --- a/src/vendor/cigraph/include/igraph_interface.h +++ b/src/vendor/cigraph/include/igraph_interface.h @@ -27,7 +27,6 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS @@ -36,58 +35,45 @@ __BEGIN_DECLS /* Interface */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); +IGRAPH_EXPORT int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); IGRAPH_EXPORT void igraph_destroy(igraph_t *graph); -IGRAPH_EXPORT igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from); -IGRAPH_EXPORT igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, +IGRAPH_EXPORT int igraph_copy(igraph_t *to, const igraph_t *from); +IGRAPH_EXPORT int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr); -IGRAPH_EXPORT igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, +IGRAPH_EXPORT int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); -IGRAPH_EXPORT igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges); -IGRAPH_EXPORT igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); -IGRAPH_EXPORT igraph_error_t igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, - igraph_vector_int_t *idx, - igraph_vector_int_t *invidx); +IGRAPH_EXPORT int igraph_delete_edges(igraph_t *graph, igraph_es_t edges); +IGRAPH_EXPORT int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); +IGRAPH_EXPORT int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, + igraph_vector_t *idx, + igraph_vector_t *invidx); IGRAPH_EXPORT igraph_integer_t igraph_vcount(const igraph_t *graph); IGRAPH_EXPORT igraph_integer_t igraph_ecount(const igraph_t *graph); -IGRAPH_EXPORT igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t vid, +IGRAPH_EXPORT int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid, igraph_neimode_t mode); IGRAPH_EXPORT igraph_bool_t igraph_is_directed(const igraph_t *graph); -IGRAPH_EXPORT igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, - igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, +IGRAPH_EXPORT int igraph_degree(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_edge(const igraph_t *graph, igraph_integer_t eid, +IGRAPH_EXPORT int igraph_edge(const igraph_t *graph, igraph_integer_t eid, igraph_integer_t *from, igraph_integer_t *to); -IGRAPH_EXPORT igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, - igraph_vector_int_t *edges); -IGRAPH_EXPORT igraph_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, +IGRAPH_EXPORT int igraph_edges(const igraph_t *graph, igraph_es_t eids, + igraph_vector_t *edges); +IGRAPH_EXPORT int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed, igraph_bool_t error); -IGRAPH_EXPORT igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, - const igraph_vector_int_t *pairs, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_get_all_eids_between(const igraph_t *graph, igraph_vector_int_t *eids, - igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t vid, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); - -IGRAPH_EXPORT igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop); -IGRAPH_EXPORT igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop); -IGRAPH_EXPORT void igraph_i_property_cache_set_bool(const igraph_t *cache, igraph_cached_property_t prop, igraph_bool_t value); -IGRAPH_EXPORT void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop); -IGRAPH_EXPORT void igraph_i_property_cache_invalidate_all(const igraph_t *graph); - -#define IGRAPH_RETURN_IF_CACHED_BOOL(graphptr, prop, resptr) \ - do { \ - if (igraph_i_property_cache_has((graphptr), (prop))) { \ - *(resptr) = igraph_i_property_cache_get_bool((graphptr), (prop)); \ - return IGRAPH_SUCCESS; \ - } \ - } while (0) +IGRAPH_EXPORT int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); /** * \define IGRAPH_FROM @@ -100,7 +86,7 @@ IGRAPH_EXPORT void igraph_i_property_cache_invalidate_all(const igraph_t *graph) * \return The source vertex of the edge. * \sa \ref igraph_edge() if error checking is desired. */ -#define IGRAPH_FROM(graph,eid) ((igraph_integer_t)(VECTOR((graph)->from)[(igraph_integer_t)(eid)])) +#define IGRAPH_FROM(graph,eid) ((igraph_integer_t)(VECTOR((graph)->from)[(long int)(eid)])) /** * \define IGRAPH_TO @@ -113,7 +99,7 @@ IGRAPH_EXPORT void igraph_i_property_cache_invalidate_all(const igraph_t *graph) * \return The target vertex of the edge. * \sa \ref igraph_edge() if error checking is desired. */ -#define IGRAPH_TO(graph,eid) ((igraph_integer_t)(VECTOR((graph)->to) [(igraph_integer_t)(eid)])) +#define IGRAPH_TO(graph,eid) ((igraph_integer_t)(VECTOR((graph)->to) [(long int)(eid)])) /** * \define IGRAPH_OTHER diff --git a/src/vendor/cigraph/include/igraph_interrupt.h b/src/vendor/cigraph/include/igraph_interrupt.h index 6d8fb85ca8a..1c32b69ba56 100644 --- a/src/vendor/cigraph/include/igraph_interrupt.h +++ b/src/vendor/cigraph/include/igraph_interrupt.h @@ -106,7 +106,7 @@ __BEGIN_DECLS * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ -typedef igraph_error_t igraph_interruption_handler_t (void* data); +typedef int igraph_interruption_handler_t (void* data); /** * \function igraph_allow_interruption @@ -119,7 +119,7 @@ typedef igraph_error_t igraph_interruption_handler_t (void* data); * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ -IGRAPH_EXPORT igraph_error_t igraph_allow_interruption(void* data); +IGRAPH_EXPORT int igraph_allow_interruption(void* data); IGRAPH_EXPORT igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); diff --git a/src/vendor/cigraph/include/igraph_iterators.h b/src/vendor/cigraph/include/igraph_iterators.h index ec7589e010b..86d604f7b89 100644 --- a/src/vendor/cigraph/include/igraph_iterators.h +++ b/src/vendor/cigraph/include/igraph_iterators.h @@ -24,12 +24,8 @@ #ifndef IGRAPH_ITERATORS_H #define IGRAPH_ITERATORS_H -#include "igraph_datatype.h" #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" -#include "igraph_types.h" -#include "igraph_vector.h" __BEGIN_DECLS @@ -37,91 +33,84 @@ __BEGIN_DECLS /* Vertex selectors */ /* -------------------------------------------------- */ -typedef enum { - IGRAPH_VS_ALL, - IGRAPH_VS_ADJ, - IGRAPH_VS_NONE, - IGRAPH_VS_1, - IGRAPH_VS_VECTORPTR, - IGRAPH_VS_VECTOR, - IGRAPH_VS_RANGE, - IGRAPH_VS_NONADJ, -} igraph_vs_type_t; +#define IGRAPH_VS_ALL 0 +#define IGRAPH_VS_ADJ 1 +#define IGRAPH_VS_NONE 2 +#define IGRAPH_VS_1 3 +#define IGRAPH_VS_VECTORPTR 4 +#define IGRAPH_VS_VECTOR 5 +#define IGRAPH_VS_SEQ 6 +#define IGRAPH_VS_NONADJ 7 typedef struct igraph_vs_t { - igraph_vs_type_t type; + int type; union { igraph_integer_t vid; /* single vertex */ - const igraph_vector_int_t *vecptr; /* vector of vertices */ + const igraph_vector_t *vecptr; /* vector of vertices */ struct { igraph_integer_t vid; igraph_neimode_t mode; - } adj; /* adjacent vertices */ + } adj; /* adjacent vertices */ struct { - igraph_integer_t start; /* first index (inclusive) */ - igraph_integer_t end; /* last index (exclusive) */ - } range; /* range of vertices */ + igraph_integer_t from; /* first index */ + igraph_integer_t to; /* last index */ + } seq; /* sequence of vertices from:to */ } data; } igraph_vs_t; -IGRAPH_EXPORT igraph_error_t igraph_vs_all(igraph_vs_t *vs); +IGRAPH_EXPORT int igraph_vs_all(igraph_vs_t *vs); IGRAPH_EXPORT igraph_vs_t igraph_vss_all(void); -IGRAPH_EXPORT igraph_error_t igraph_vs_adj(igraph_vs_t *vs, +IGRAPH_EXPORT int igraph_vs_adj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, +IGRAPH_EXPORT int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_vs_none(igraph_vs_t *vs); +IGRAPH_EXPORT int igraph_vs_none(igraph_vs_t *vs); IGRAPH_EXPORT igraph_vs_t igraph_vss_none(void); -IGRAPH_EXPORT igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); +IGRAPH_EXPORT int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); IGRAPH_EXPORT igraph_vs_t igraph_vss_1(igraph_integer_t vid); -IGRAPH_EXPORT igraph_error_t igraph_vs_vector(igraph_vs_t *vs, - const igraph_vector_int_t *v); -IGRAPH_EXPORT igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_vs_t igraph_vss_vector(const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...); +IGRAPH_EXPORT int igraph_vs_vector_small(igraph_vs_t *vs, ...); -IGRAPH_EXPORT igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, - const igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_t *v); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); - -IGRAPH_EXPORT igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end); -IGRAPH_EXPORT igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); IGRAPH_EXPORT void igraph_vs_destroy(igraph_vs_t *vs); IGRAPH_EXPORT igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); -IGRAPH_EXPORT igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); +IGRAPH_EXPORT int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); -IGRAPH_EXPORT igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, - igraph_vector_int_t *v); -IGRAPH_EXPORT igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, +IGRAPH_EXPORT int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result); -IGRAPH_EXPORT igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs); +IGRAPH_EXPORT int igraph_vs_type(const igraph_vs_t *vs); /* -------------------------------------------------- */ /* Vertex iterators */ /* -------------------------------------------------- */ -typedef enum { - IGRAPH_VIT_RANGE, - IGRAPH_VIT_VECTOR, - IGRAPH_VIT_VECTORPTR, -} igraph_vit_type_t; +#define IGRAPH_VIT_SEQ 0 +#define IGRAPH_VIT_VECTOR 1 +#define IGRAPH_VIT_VECTORPTR 2 typedef struct igraph_vit_t { - igraph_vit_type_t type; - igraph_integer_t pos; - igraph_integer_t start; /* first index */ - igraph_integer_t end; /* one past last index */ - const igraph_vector_int_t *vec; + int type; + long int pos; + long int start; /* first index */ + long int end; /* one past last index */ + const igraph_vector_t *vec; } igraph_vit_t; /** @@ -147,7 +136,7 @@ typedef struct igraph_vit_t { * igraph_vs_adj(&vs, 0, IGRAPH_ALL); * igraph_vit_create(&graph, vs, &vit); * while (!IGRAPH_VIT_END(vit)) { - * printf(" %" IGRAPH_PRId, IGRAPH_VIT_GET(vit)); + * printf(" %li", (long int) IGRAPH_VIT_GET(vit)); * IGRAPH_VIT_NEXT(vit); * } * printf("\n"); @@ -207,136 +196,124 @@ typedef struct igraph_vit_t { * \define IGRAPH_VIT_GET * \brief Query the current position. * - * Gives the vertex ID of the current vertex pointed to by the + * Gives the vertex id of the current vertex pointed to by the * iterator. * \param vit The vertex iterator. - * \return The vertex ID of the current vertex. + * \return The vertex id of the current vertex. * * Time complexity: O(1). */ #define IGRAPH_VIT_GET(vit) \ - ((igraph_integer_t)(((vit).type == IGRAPH_VIT_RANGE) ? (vit).pos : \ + ((igraph_integer_t)(((vit).type == IGRAPH_VIT_SEQ) ? (vit).pos : \ VECTOR(*(vit).vec)[(vit).pos])) -IGRAPH_EXPORT igraph_error_t igraph_vit_create(const igraph_t *graph, +IGRAPH_EXPORT int igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit); IGRAPH_EXPORT void igraph_vit_destroy(const igraph_vit_t *vit); -IGRAPH_EXPORT igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); /* -------------------------------------------------- */ /* Edge Selectors */ /* -------------------------------------------------- */ -typedef enum { - IGRAPH_ES_ALL, - IGRAPH_ES_ALLFROM, - IGRAPH_ES_ALLTO, - IGRAPH_ES_INCIDENT, - IGRAPH_ES_NONE, - IGRAPH_ES_1, - IGRAPH_ES_VECTORPTR, - IGRAPH_ES_VECTOR, - IGRAPH_ES_RANGE, - IGRAPH_ES_PAIRS, - IGRAPH_ES_PATH, - IGRAPH_ES_UNUSED_WAS_MULTIPAIRS, /* placeholder for deprecated IGRAPH_ES_MULTIPAIRS from igraph 0.10 */ - IGRAPH_ES_ALL_BETWEEN, -} igraph_es_type_t; +#define IGRAPH_ES_ALL 0 +#define IGRAPH_ES_ALLFROM 1 +#define IGRAPH_ES_ALLTO 2 +#define IGRAPH_ES_INCIDENT 3 +#define IGRAPH_ES_NONE 4 +#define IGRAPH_ES_1 5 +#define IGRAPH_ES_VECTORPTR 6 +#define IGRAPH_ES_VECTOR 7 +#define IGRAPH_ES_SEQ 8 +#define IGRAPH_ES_PAIRS 9 +#define IGRAPH_ES_PATH 10 +#define IGRAPH_ES_MULTIPAIRS 11 typedef struct igraph_es_t { - igraph_es_type_t type; + int type; union { igraph_integer_t vid; igraph_integer_t eid; - const igraph_vector_int_t *vecptr; + const igraph_vector_t *vecptr; struct { igraph_integer_t vid; igraph_neimode_t mode; } incident; struct { - igraph_integer_t start; /* first index (inclusive) */ - igraph_integer_t end; /* last index (exclusive) */ - } range; + igraph_integer_t from; /* first index */ + igraph_integer_t to; /* last index */ + } seq; struct { - const igraph_vector_int_t *ptr; + const igraph_vector_t *ptr; igraph_bool_t mode; } path; - struct { - igraph_integer_t from; - igraph_integer_t to; - igraph_bool_t directed; - } between; } data; } igraph_es_t; -IGRAPH_EXPORT igraph_error_t igraph_es_all(igraph_es_t *es, +IGRAPH_EXPORT int igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order); IGRAPH_EXPORT igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); -IGRAPH_EXPORT igraph_error_t igraph_es_incident(igraph_es_t *es, +IGRAPH_EXPORT int igraph_es_incident(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_es_none(igraph_es_t *es); +IGRAPH_EXPORT int igraph_es_none(igraph_es_t *es); IGRAPH_EXPORT igraph_es_t igraph_ess_none(void); -IGRAPH_EXPORT igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid); +IGRAPH_EXPORT int igraph_es_1(igraph_es_t *es, igraph_integer_t eid); IGRAPH_EXPORT igraph_es_t igraph_ess_1(igraph_integer_t eid); -IGRAPH_EXPORT igraph_error_t igraph_es_vector(igraph_es_t *es, - const igraph_vector_int_t *v); -IGRAPH_EXPORT igraph_es_t igraph_ess_vector(const igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_es_vector(igraph_es_t *es, + const igraph_vector_t *v); +IGRAPH_EXPORT igraph_es_t igraph_ess_vector(const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT igraph_es_t igraph_ess_range(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_es_fromto(igraph_es_t *es, + igraph_vs_t from, igraph_vs_t to); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT igraph_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v); -IGRAPH_EXPORT igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, +IGRAPH_EXPORT int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); +IGRAPH_EXPORT int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...); -IGRAPH_EXPORT igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, - igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); +IGRAPH_EXPORT int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_es_all_between( - igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, - igraph_bool_t directed -); +IGRAPH_EXPORT int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, + igraph_bool_t directed); +IGRAPH_EXPORT int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...); IGRAPH_EXPORT void igraph_es_destroy(igraph_es_t *es); IGRAPH_EXPORT igraph_bool_t igraph_es_is_all(const igraph_es_t *es); -IGRAPH_EXPORT igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); +IGRAPH_EXPORT int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); -IGRAPH_EXPORT igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, - igraph_vector_int_t *v); -IGRAPH_EXPORT igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, +IGRAPH_EXPORT int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_t *v); +IGRAPH_EXPORT int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); -IGRAPH_EXPORT igraph_es_type_t igraph_es_type(const igraph_es_t *es); +IGRAPH_EXPORT int igraph_es_type(const igraph_es_t *es); /* -------------------------------------------------- */ /* Edge Iterators */ /* -------------------------------------------------- */ -typedef enum { - IGRAPH_EIT_RANGE, - IGRAPH_EIT_VECTOR, - IGRAPH_EIT_VECTORPTR, -} igraph_eit_type_t; +#define IGRAPH_EIT_SEQ 0 +#define IGRAPH_EIT_VECTOR 1 +#define IGRAPH_EIT_VECTORPTR 2 typedef struct igraph_eit_t { - igraph_eit_type_t type; - igraph_integer_t pos; - igraph_integer_t start; /* first index */ - igraph_integer_t end; /* one past last index */ - const igraph_vector_int_t *vec; + int type; + long int pos; + long int start; /* first index */ + long int end; /* one past last index */ + const igraph_vector_t *vec; } igraph_eit_t; /** @@ -400,21 +377,21 @@ typedef struct igraph_eit_t { * \define IGRAPH_EIT_GET * \brief Query an edge iterator. * - * Gives the edge ID of the current edge pointed to by an iterator. + * Gives the edge id of the current edge pointed to by an iterator. * \param eit The edge iterator. * \return The id of the current edge. * * Time complexity: O(1). */ #define IGRAPH_EIT_GET(eit) \ - (igraph_integer_t)((((eit).type == IGRAPH_EIT_RANGE) ? (eit).pos : \ + (igraph_integer_t)((((eit).type == IGRAPH_EIT_SEQ) ? (eit).pos : \ VECTOR(*(eit).vec)[(eit).pos])) -IGRAPH_EXPORT igraph_error_t igraph_eit_create(const igraph_t *graph, +IGRAPH_EXPORT int igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); IGRAPH_EXPORT void igraph_eit_destroy(const igraph_eit_t *eit); -IGRAPH_EXPORT igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v); +IGRAPH_EXPORT int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_lapack.h b/src/vendor/cigraph/include/igraph_lapack.h index 76b30f37dd0..79b3c026d5c 100644 --- a/src/vendor/cigraph/include/igraph_lapack.h +++ b/src/vendor/cigraph/include/igraph_lapack.h @@ -25,7 +25,6 @@ #define IGRAPH_LAPACK_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_matrix.h" @@ -57,11 +56,11 @@ __BEGIN_DECLS * */ -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +IGRAPH_EXPORT int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, int *info); -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, +IGRAPH_EXPORT int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, const igraph_vector_int_t *ipiv, igraph_matrix_t *b); -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +IGRAPH_EXPORT int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b, int *info); typedef enum { IGRAPH_LAPACK_DSYEV_ALL, @@ -69,7 +68,7 @@ typedef enum { IGRAPH_LAPACK_DSYEV_ALL, IGRAPH_LAPACK_DSYEV_SELECT } igraph_lapack_dsyev_which_t; -IGRAPH_EXPORT igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, +IGRAPH_EXPORT 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, @@ -78,7 +77,7 @@ IGRAPH_EXPORT igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, /* TODO: should we use complex vectors/matrices? */ -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, +IGRAPH_EXPORT int igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, @@ -91,7 +90,7 @@ typedef enum { IGRAPH_LAPACK_DGEEVX_BALANCE_NONE = 0, } igraph_lapack_dgeevx_balance_t; -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, +IGRAPH_EXPORT int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, @@ -103,7 +102,7 @@ IGRAPH_EXPORT igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t igraph_vector_t *rcondv, int *info); -IGRAPH_EXPORT igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, +IGRAPH_EXPORT int igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result); diff --git a/src/vendor/cigraph/include/igraph_layout.h b/src/vendor/cigraph/include/igraph_layout.h index 1730f1a710d..16c7b3c2bd1 100644 --- a/src/vendor/cigraph/include/igraph_layout.h +++ b/src/vendor/cigraph/include/igraph_layout.h @@ -25,16 +25,14 @@ #define IGRAPH_LAYOUT_H #include "igraph_decls.h" - #include "igraph_constants.h" -#include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_iterators.h" -#include "igraph_matrix_list.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_matrix.h" +#include "igraph_datatype.h" +#include "igraph_arpack.h" +#include "igraph_iterators.h" __BEGIN_DECLS @@ -54,59 +52,59 @@ __BEGIN_DECLS /* Layouts */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t order); -IGRAPH_EXPORT igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t center, const igraph_vector_int_t *order); -IGRAPH_EXPORT igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width); -IGRAPH_EXPORT igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, +IGRAPH_EXPORT int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_t *order); +IGRAPH_EXPORT int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width); +IGRAPH_EXPORT 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 *weights, + 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_EXPORT igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t maxiter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t root); -IGRAPH_EXPORT igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - const igraph_vector_int_t *roots, - const igraph_vector_int_t *rootlevel); -IGRAPH_EXPORT igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel); +IGRAPH_EXPORT int igraph_layout_reingold_tilford_circular(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - const igraph_vector_int_t *roots, - const igraph_vector_int_t *rootlevel); -IGRAPH_EXPORT igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, - igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, - const igraph_vector_int_t* layers, igraph_real_t hgap, - igraph_real_t vgap, igraph_integer_t maxiter, const igraph_vector_t *weights); - -IGRAPH_EXPORT igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t width, igraph_integer_t height); -IGRAPH_EXPORT igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel); +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + long int width, long int height); +IGRAPH_EXPORT 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 *weights, + const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -114,7 +112,7 @@ IGRAPH_EXPORT igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_ const igraph_vector_t *minz, const igraph_vector_t *maxz); -IGRAPH_EXPORT igraph_error_t igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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, @@ -122,7 +120,7 @@ IGRAPH_EXPORT igraph_error_t igraph_layout_kamada_kawai_3d(const igraph_t *graph const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz); -IGRAPH_EXPORT igraph_error_t igraph_layout_graphopt(const igraph_t *graph, +IGRAPH_EXPORT 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, @@ -130,34 +128,13 @@ IGRAPH_EXPORT igraph_error_t igraph_layout_graphopt(const igraph_t *graph, igraph_real_t max_sa_movement, igraph_bool_t use_seed); -IGRAPH_EXPORT igraph_error_t igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, - const igraph_matrix_t *dist, igraph_integer_t dim); +IGRAPH_EXPORT int igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, long int dim); -IGRAPH_EXPORT igraph_error_t igraph_layout_bipartite(const igraph_t *graph, +IGRAPH_EXPORT 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, igraph_integer_t maxiter); - -IGRAPH_EXPORT igraph_error_t igraph_layout_umap(const igraph_t *graph, - igraph_matrix_t *res, - igraph_bool_t use_seed, - const igraph_vector_t *distances, - igraph_real_t min_dist, - igraph_integer_t epochs, - igraph_bool_t distances_are_weights); - -IGRAPH_EXPORT igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, - igraph_matrix_t *res, - igraph_bool_t use_seed, - const igraph_vector_t *distances, - igraph_real_t min_dist, - igraph_integer_t epochs, - igraph_bool_t distances_are_weights); - -IGRAPH_EXPORT igraph_error_t igraph_layout_umap_compute_weights(const igraph_t *graph, - const igraph_vector_t *distances, - igraph_vector_t *weights); - + igraph_real_t vgap, long int maxiter); /** * \struct igraph_layout_drl_options_t @@ -245,28 +222,30 @@ typedef enum { IGRAPH_LAYOUT_DRL_DEFAULT = 0, IGRAPH_LAYOUT_DRL_FINAL } igraph_layout_drl_default_t; -IGRAPH_EXPORT igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, +IGRAPH_EXPORT int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, igraph_layout_drl_default_t templ); -IGRAPH_EXPORT igraph_error_t igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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_t *weights, + const igraph_vector_bool_t *fixed); -IGRAPH_EXPORT igraph_error_t igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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_t *weights, + const igraph_vector_bool_t *fixed); -IGRAPH_EXPORT igraph_error_t igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, - const igraph_matrix_list_t *coords, +IGRAPH_EXPORT int igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, + const igraph_vector_ptr_t *coords, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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, @@ -274,25 +253,6 @@ IGRAPH_EXPORT igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_real_t weight_edge_crossings, igraph_real_t weight_node_edge_dist); -/** - * \typedef igraph_root_choice_t - * \brief Root choice heuristic for tree visualizations. - * - * Used with \ref igraph_roots_for_tree_layout(). - */ - -typedef enum { - IGRAPH_ROOT_CHOICE_DEGREE, - IGRAPH_ROOT_CHOICE_ECCENTRICITY -} igraph_root_choice_t; - -IGRAPH_EXPORT igraph_error_t igraph_roots_for_tree_layout( - const igraph_t *graph, - igraph_neimode_t mode, - igraph_vector_int_t *roots, - igraph_root_choice_t use_eccentricity); - - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_lsap.h b/src/vendor/cigraph/include/igraph_lsap.h index 61898d40ca5..eeffedec006 100644 --- a/src/vendor/cigraph/include/igraph_lsap.h +++ b/src/vendor/cigraph/include/igraph_lsap.h @@ -3,14 +3,13 @@ #define IGRAPH_LSAP_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_matrix.h" #include "igraph_vector.h" #include "igraph_types.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_solve_lsap(const igraph_matrix_t *c, igraph_integer_t n, +IGRAPH_EXPORT int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n, igraph_vector_int_t *p); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_matching.h b/src/vendor/cigraph/include/igraph_matching.h index 44b9082f488..c9a154058a8 100644 --- a/src/vendor/cigraph/include/igraph_matching.h +++ b/src/vendor/cigraph/include/igraph_matching.h @@ -24,8 +24,8 @@ #define IGRAPH_MATCHING_H #include "igraph_decls.h" +#include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -35,18 +35,22 @@ __BEGIN_DECLS /* Matchings in graphs */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_is_matching(const igraph_t* graph, - const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, +IGRAPH_EXPORT int igraph_is_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, igraph_bool_t* result); -IGRAPH_EXPORT igraph_error_t igraph_is_maximal_matching(const igraph_t* graph, - const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_maximum_bipartite_matching(const igraph_t* graph, +IGRAPH_EXPORT 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_int_t* matching, + igraph_real_t* matching_weight, igraph_vector_long_t* matching, const igraph_vector_t* weights, igraph_real_t eps); +IGRAPH_EXPORT 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); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_matrix.h b/src/vendor/cigraph/include/igraph_matrix.h index 6a77444b492..a8b8b5e0244 100644 --- a/src/vendor/cigraph/include/igraph_matrix.h +++ b/src/vendor/cigraph/include/igraph_matrix.h @@ -25,7 +25,6 @@ #define IGRAPH_MATRIX_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -46,6 +45,12 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_INT +#define BASE_LONG +#include "igraph_pmt.h" +#include "igraph_matrix_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_LONG + #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" @@ -68,9 +73,6 @@ __BEGIN_DECLS #define IGRAPH_MATRIX_INIT_FINALLY(m, nr, nc) \ do { IGRAPH_CHECK(igraph_matrix_init(m, nr, nc)); \ IGRAPH_FINALLY(igraph_matrix_destroy, m); } while (0) -#define IGRAPH_MATRIX_INT_INIT_FINALLY(m, nr, nc) \ - do { IGRAPH_CHECK(igraph_matrix_int_init(m, nr, nc)); \ - IGRAPH_FINALLY(igraph_matrix_int_destroy, m); } while (0) /** * \ingroup matrix @@ -87,16 +89,11 @@ __BEGIN_DECLS */ #define MATRIX(m,i,j) ((m).data.stor_begin[(m).nrow*(j)+(i)]) -IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, - const igraph_matrix_t *rhs, - igraph_real_t tol); - -IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, - const igraph_matrix_t *rhs, - igraph_real_t eps); +IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol); +IGRAPH_EXPORT int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_matrix_list.h b/src/vendor/cigraph/include/igraph_matrix_list.h deleted file mode 100644 index 1e44cf8d405..00000000000 --- a/src/vendor/cigraph/include/igraph_matrix_list.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -*- 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_MATRIX_LIST_H -#define IGRAPH_MATRIX_LIST_H - -#include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_matrix.h" -#include "igraph_types.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Flexible list of matrices */ -/* -------------------------------------------------- */ - -/* Indicate to igraph_typed_list_pmt.h that we are going to work with _matrices_ - * of the base type, not the base type directly */ -#define MATRIX_LIST - -#define BASE_IGRAPH_REAL -#include "igraph_pmt.h" -#include "igraph_typed_list_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_IGRAPH_REAL - -#undef MATRIX_LIST - -/* -------------------------------------------------- */ -/* Helper macros */ -/* -------------------------------------------------- */ - -#define IGRAPH_MATRIX_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_matrix_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_matrix_list_destroy, v); } while (0) - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_matrix_pmt.h b/src/vendor/cigraph/include/igraph_matrix_pmt.h index 472170ea47e..81a735303e8 100644 --- a/src/vendor/cigraph/include/igraph_matrix_pmt.h +++ b/src/vendor/cigraph/include/igraph_matrix_pmt.h @@ -23,40 +23,31 @@ typedef struct TYPE(igraph_matrix) { TYPE(igraph_vector) data; - igraph_integer_t nrow, ncol; + long int nrow, ncol; } TYPE(igraph_matrix); /*---------------*/ /* Allocation */ /*---------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init)( - TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_array)( - TYPE(igraph_matrix)* m, const BASE* data, igraph_integer_t nrow, igraph_integer_t ncol, igraph_matrix_storage_t storage); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_copy)( - TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m, + long int nrow, long int ncol); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, + const TYPE(igraph_matrix) *from); IGRAPH_EXPORT void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_matrix, copy)( - TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); /*--------------------*/ /* Accessing elements */ /*--------------------*/ /* MATRIX */ -IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_matrix, e)( - const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); -IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_matrix, e_ptr)( - const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); -IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, get)( - const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); -IGRAPH_EXPORT BASE* FUNCTION(igraph_matrix, get_ptr)( - const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)( - TYPE(igraph_matrix)* m, igraph_integer_t row, igraph_integer_t col, BASE value); +IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + long int row, long int col); +IGRAPH_EXPORT BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, + long int row, long int col); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, + BASE value); /*------------------------------*/ /* Initializing matrix elements */ @@ -69,70 +60,70 @@ IGRAPH_EXPORT void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) /* Matrix views */ /*-----------------------*/ -IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)( - const TYPE(igraph_matrix) *m, const BASE *data, - igraph_integer_t nrow, igraph_integer_t ncol); -IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( - const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, - igraph_integer_t ncol -); +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m, + const BASE *data, + long int nrow, + long int ncol); /*------------------*/ /* Copying matrices */ /*------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); /*--------------------------*/ /* Copying rows and columns */ /*--------------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_row)( - const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_col)( - const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_row)( - TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_col)( - TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows)( - const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *rows); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_cols)( - const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *cols); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)( - const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, - const igraph_vector_int_t *rows, const igraph_vector_int_t *cols); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, long int index); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *rows); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, + TYPE(igraph_matrix) *res, + const igraph_vector_t *cols); +IGRAPH_EXPORT 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); /*-----------------------------*/ /* Exchanging rows and columns */ /*-----------------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rows)( - TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_cols)( - TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rowcol)( - TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rowcol)(TYPE(igraph_matrix) *m, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); /*-----------------------------*/ /* Matrix operations */ /*-----------------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); IGRAPH_EXPORT void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by); IGRAPH_EXPORT void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus); @@ -144,15 +135,15 @@ IGRAPH_EXPORT void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, #ifndef NOTORDERED IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_min)( - const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_max)( - const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, minmax)( - const TYPE(igraph_matrix) *m, BASE *min, BASE *max); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_minmax)( - const TYPE(igraph_matrix) *m, igraph_integer_t *imin, igraph_integer_t *jmin, - igraph_integer_t *imax, igraph_integer_t *jmax); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m, + long int *i, long int *j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m, + long int *i, long int *j); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, + BASE *min, BASE *max); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, + long int *imin, long int *jmin, + long int *imax, long int *jmax); #endif /*------------------------------*/ @@ -178,15 +169,15 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_ma IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, +IGRAPH_EXPORT int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); @@ -199,65 +190,55 @@ IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(ig /* Searching for elements */ /*------------------------*/ -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, contains)( - const TYPE(igraph_matrix) *m, BASE e); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, search)( - const TYPE(igraph_matrix) *m, igraph_integer_t from, BASE what, - igraph_integer_t *pos, igraph_integer_t *row, igraph_integer_t *col); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, + BASE e); +IGRAPH_EXPORT 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); /*------------------------*/ /* Resizing operations */ /*------------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, resize)( - TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, resize_min)( - TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_cols)( - TYPE(igraph_matrix) *m, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_rows)( - TYPE(igraph_matrix) *m, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_col)( - TYPE(igraph_matrix) *m, igraph_integer_t col); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_row)( - TYPE(igraph_matrix) *m, igraph_integer_t row); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, + long int nrow, long int ncol); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row); /*------------------------*/ /* Print as text */ /*------------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, + const char *format); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, + FILE *file); -#ifdef OUT_FORMAT -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, const char *format); -#endif /* OUT_FORMAT */ +#ifdef BASE_COMPLEX -/*-----------------------------------------*/ -/* Operations specific to complex matrices */ -/*-----------------------------------------*/ +IGRAPH_EXPORT int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, + igraph_matrix_t *real); +IGRAPH_EXPORT int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, + igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, + igraph_matrix_t *real, + igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_create(igraph_matrix_complex_t *v, + const igraph_matrix_t *real, + const igraph_matrix_t *imag); +IGRAPH_EXPORT int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, + const igraph_matrix_t *r, + const igraph_matrix_t *theta); -#ifdef BASE_COMPLEX +#endif -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *v, - igraph_matrix_t *real); -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, - igraph_matrix_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, - igraph_matrix_t *real, - igraph_matrix_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *v, - const igraph_matrix_t *real, - const igraph_matrix_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, - const igraph_matrix_t *r, - const igraph_matrix_t *theta); - -IGRAPH_EXPORT igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, - igraph_matrix_complex_t *rhs, - igraph_real_t eps); - -#endif /* BASE_COMPLEX */ - -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( - TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, + long int *index, long int nremove); +IGRAPH_EXPORT int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m, + const igraph_vector_t *neg, + long int nremove); diff --git a/src/vendor/cigraph/include/igraph_memory.h b/src/vendor/cigraph/include/igraph_memory.h index 643501c1174..98305722d05 100644 --- a/src/vendor/cigraph/include/igraph_memory.h +++ b/src/vendor/cigraph/include/igraph_memory.h @@ -29,21 +29,16 @@ __BEGIN_DECLS -#define IGRAPH_CALLOC(n,t) (t*) calloc( (n) > 0 ? (size_t)((n)*sizeof(t)) : (size_t)1, 1 ) -#define IGRAPH_MALLOC(n) malloc( (n) > 0 ? (size_t)((n)) : (size_t)1 ) +#define IGRAPH_CALLOC(n,t) (t*) calloc( (n) > 0 ? (size_t)(n) : (size_t)1, sizeof(t) ) #define IGRAPH_REALLOC(p,n,t) (t*) realloc((void*)(p), (n) > 0 ? (size_t)((n)*sizeof(t)) : (size_t)1) #define IGRAPH_FREE(p) (free( (void *)(p) ), (p) = NULL) -/* These are deprecated and scheduled for removal in 0.11 */ #define igraph_Calloc IGRAPH_CALLOC #define igraph_Realloc IGRAPH_REALLOC #define igraph_Free IGRAPH_FREE -/* Deprecated section ends here */ -IGRAPH_EXPORT void *igraph_calloc(size_t count, size_t size); -IGRAPH_EXPORT void *igraph_malloc(size_t size); -IGRAPH_EXPORT void *igraph_realloc(void* ptr, size_t size); -IGRAPH_EXPORT void igraph_free(void *ptr); +IGRAPH_EXPORT void igraph_free(void *p); +IGRAPH_EXPORT void *igraph_malloc(size_t n); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_microscopic_update.h b/src/vendor/cigraph/include/igraph_microscopic_update.h index bff0e03a1eb..7432b71810d 100644 --- a/src/vendor/cigraph/include/igraph_microscopic_update.h +++ b/src/vendor/cigraph/include/igraph_microscopic_update.h @@ -25,35 +25,34 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_iterators.h" #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, +IGRAPH_EXPORT int igraph_deterministic_optimal_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_optimal_t optimality, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_moran_process(const igraph_t *graph, +IGRAPH_EXPORT int igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, +IGRAPH_EXPORT int igraph_roulette_wheel_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_bool_t islocal, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, +IGRAPH_EXPORT int igraph_stochastic_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_imitate_algorithm_t algo, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_mixing.h b/src/vendor/cigraph/include/igraph_mixing.h index 4bf2af82c4e..5bc62eccd43 100644 --- a/src/vendor/cigraph/include/igraph_mixing.h +++ b/src/vendor/cigraph/include/igraph_mixing.h @@ -27,25 +27,22 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, - const igraph_vector_int_t *types, +IGRAPH_EXPORT int igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_t *types, igraph_real_t *res, - igraph_bool_t directed, - igraph_bool_t normalized); + igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_assortativity(const igraph_t *graph, - const igraph_vector_t *values, - const igraph_vector_t *values_in, +IGRAPH_EXPORT 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, - igraph_bool_t normalized); + igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_assortativity_degree(const igraph_t *graph, +IGRAPH_EXPORT int igraph_assortativity_degree(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed); diff --git a/src/vendor/cigraph/include/igraph_motifs.h b/src/vendor/cigraph/include/igraph_motifs.h index 6394b86266a..7768ee17d8d 100644 --- a/src/vendor/cigraph/include/igraph_motifs.h +++ b/src/vendor/cigraph/include/igraph_motifs.h @@ -27,7 +27,6 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS @@ -38,62 +37,59 @@ __BEGIN_DECLS /** * \typedef igraph_motifs_handler_t - * \brief Callback type for \c igraph_motifs_randesu_callback. + * Callback type for \c igraph_motifs_randesu_callback * * \ref igraph_motifs_randesu_callback() calls a specified callback * function whenever a new motif is found during a motif search. This * callback function must be of type \c igraph_motifs_handler_t. It has * the following arguments: - * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vids The IDs of the vertices in the motif that has just been * found. This vector is owned by the motif search algorithm, so do not * modify or destroy it; make a copy of it if you need it later. * \param isoclass The isomorphism class of the motif that has just been - * found. Use \ref igraph_graph_count() to find the maximum possible - * isoclass for graphs of a given size. See \ref igraph_isoclass and - * \ref igraph_isoclass_subgraph for more information. + * found. Use \ref igraph_isoclass or \ref igraph_isoclass_subgraph to find + * out which isomorphism class belongs to a given motif. * \param extra The extra argument that was passed to \ref * igraph_motifs_randesu_callback(). - * \return \c IGRAPH_SUCCESS to continue the motif search, - * \c IGRAPH_STOP to stop the motif search and return to the caller - * normally. Any other return value is interpreted as an igraph error code, - * which will terminate the search and return the same error code to the - * caller. + * \return A logical value, if TRUE (=non-zero), that is interpreted + * as a request to stop the motif search and return to the caller. * * \sa \ref igraph_motifs_randesu_callback() */ -typedef igraph_error_t igraph_motifs_handler_t(const igraph_t *graph, - igraph_vector_int_t *vids, - igraph_integer_t isoclass, +typedef igraph_bool_t igraph_motifs_handler_t(const igraph_t *graph, + igraph_vector_t *vids, + int isoclass, void* extra); -IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, - igraph_integer_t size, const igraph_vector_t *cut_prob); +IGRAPH_EXPORT int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + int size, const igraph_vector_t *cut_prob); -IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, +IGRAPH_EXPORT int igraph_motifs_randesu_callback(const igraph_t *graph, int size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra); -IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, - igraph_integer_t size, const igraph_vector_t *cut_prob, +IGRAPH_EXPORT 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_int_t *sample); -IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, - igraph_integer_t size, const igraph_vector_t *cut_prob); + const igraph_vector_t *sample); +IGRAPH_EXPORT int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + int size, const igraph_vector_t *cut_prob); -IGRAPH_EXPORT igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, - igraph_real_t *asym, igraph_real_t *null); -IGRAPH_EXPORT igraph_error_t igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); +IGRAPH_EXPORT int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, + igraph_integer_t *asym, igraph_integer_t *null); +IGRAPH_EXPORT int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); +IGRAPH_EXPORT int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, + igraph_real_t *res4); -IGRAPH_EXPORT igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, +IGRAPH_EXPORT int igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT igraph_error_t igraph_list_triangles(const igraph_t *graph, +IGRAPH_EXPORT int igraph_list_triangles(const igraph_t *graph, igraph_vector_int_t *res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_neighborhood.h b/src/vendor/cigraph/include/igraph_neighborhood.h index d96d22c9cf9..88a1c4ff190 100644 --- a/src/vendor/cigraph/include/igraph_neighborhood.h +++ b/src/vendor/cigraph/include/igraph_neighborhood.h @@ -26,20 +26,18 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_graph_list.h" #include "igraph_iterators.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_t *res, +IGRAPH_EXPORT 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); diff --git a/src/vendor/cigraph/include/igraph_nongraph.h b/src/vendor/cigraph/include/igraph_nongraph.h index 43b6ec3c507..47ebedb4a8f 100644 --- a/src/vendor/cigraph/include/igraph_nongraph.h +++ b/src/vendor/cigraph/include/igraph_nongraph.h @@ -25,35 +25,20 @@ #define IGRAPH_NONGRAPH_H #include "igraph_decls.h" -#include "igraph_error.h" +#include "igraph_constants.h" #include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" __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 - -typedef igraph_real_t igraph_scalar_function_t(const igraph_vector_t *var, - const igraph_vector_t *par, - void* extra); -typedef void igraph_vector_function_t(const igraph_vector_t *var, - const igraph_vector_t *par, - igraph_vector_t* res, void* extra); - /* -------------------------------------------------- */ /* Other, not graph related */ /* -------------------------------------------------- */ /** * \struct igraph_plfit_result_t - * \brief Result of fitting a power-law distribution to a vector. + * \brief Result of fitting a power-law distribution to a vector * * This data structure contains the result of \ref igraph_power_law_fit(), * which tries to fit a power-law distribution to a vector of numbers. The @@ -71,44 +56,36 @@ typedef void igraph_vector_function_t(const igraph_vector_t *var, * \member D The test statistic of a Kolmogorov-Smirnov test that compares * the fitted distribution with the input vector. Smaller scores * denote better fit. - * \member p The p-value of the Kolmogorov-Smirnov test; \c NaN if it has - * not been calculated yet. Small p-values (less than 0.05) - * indicate that the test rejected the hypothesis that the - * original data could have been drawn from the fitted power-law - * distribution. - * \member data The vector containing the original input data. May not be valid - * any more if the caller already destroyed the vector. + * \member p The p-value of the Kolmogorov-Smirnov test. Small p-values + * (less than 0.05) indicate that the test rejected the hypothesis + * that the original data could have been drawn from the fitted + * power-law distribution. */ typedef struct igraph_plfit_result_t { igraph_bool_t continuous; - igraph_real_t alpha; - igraph_real_t xmin; - igraph_real_t L; - igraph_real_t D; - const igraph_vector_t* data; + double alpha; + double xmin; + double L; + double D; + double p; } igraph_plfit_result_t; -IGRAPH_EXPORT igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, +IGRAPH_EXPORT int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth); -IGRAPH_EXPORT igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, +IGRAPH_EXPORT int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, igraph_integer_t length); -IGRAPH_EXPORT igraph_error_t igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_int_t *resverts, +IGRAPH_EXPORT int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, igraph_matrix_t *rescoords); -IGRAPH_EXPORT igraph_bool_t igraph_almost_equals(double a, double b, double eps); -IGRAPH_EXPORT int igraph_cmp_epsilon(double a, double b, double eps); - -IGRAPH_EXPORT igraph_error_t igraph_power_law_fit( - const igraph_vector_t* vector, igraph_plfit_result_t* result, - igraph_real_t xmin, igraph_bool_t force_continuous -); -IGRAPH_EXPORT igraph_error_t igraph_plfit_result_calculate_p_value( - const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision -); - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_zeroin( - igraph_real_t *ax, igraph_real_t *bx, igraph_real_t (*f)(igraph_real_t x, void *info), - void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res -); +IGRAPH_EXPORT int igraph_zeroin(igraph_real_t *ax, igraph_real_t *bx, + igraph_real_t (*f)(igraph_real_t x, void *info), + void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT int igraph_power_law_fit(const igraph_vector_t* vector, igraph_plfit_result_t* result, + igraph_real_t xmin, igraph_bool_t force_continuous); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_operators.h b/src/vendor/cigraph/include/igraph_operators.h index 698dc9504c2..7992bdfe38a 100644 --- a/src/vendor/cigraph/include/igraph_operators.h +++ b/src/vendor/cigraph/include/igraph_operators.h @@ -25,13 +25,10 @@ #define IGRAPH_OPERATORS_H #include "igraph_decls.h" - #include "igraph_attributes.h" #include "igraph_constants.h" -#include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_vector_list.h" +#include "igraph_datatype.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -40,56 +37,52 @@ __BEGIN_DECLS /* Graph operators */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT igraph_error_t igraph_disjoint_union(igraph_t *res, +IGRAPH_EXPORT int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int igraph_disjoint_union(igraph_t *res, const igraph_t *left, const igraph_t *right); -IGRAPH_EXPORT igraph_error_t igraph_disjoint_union_many(igraph_t *res, +IGRAPH_EXPORT int igraph_disjoint_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs); -IGRAPH_EXPORT igraph_error_t igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); -IGRAPH_EXPORT igraph_error_t igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_int_list_t *edgemaps); -IGRAPH_EXPORT igraph_error_t igraph_intersection(igraph_t *res, +IGRAPH_EXPORT int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps); +IGRAPH_EXPORT int igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, - igraph_vector_int_t *edge_map2); -IGRAPH_EXPORT igraph_error_t igraph_intersection_many(igraph_t *res, + igraph_vector_t *edge_map1, + igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_intersection_many(igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_int_list_t *edgemaps); -IGRAPH_EXPORT igraph_error_t igraph_difference(igraph_t *res, + igraph_vector_ptr_t *edgemaps); +IGRAPH_EXPORT int igraph_difference(igraph_t *res, const igraph_t *orig, const igraph_t *sub); -IGRAPH_EXPORT igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, +IGRAPH_EXPORT int igraph_complementer(igraph_t *res, const igraph_t *graph, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); -IGRAPH_EXPORT igraph_error_t igraph_contract_vertices(igraph_t *graph, - const igraph_vector_int_t *mapping, - const igraph_attribute_combination_t *vertex_comb); -IGRAPH_EXPORT igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, - const igraph_vector_int_t *permutation); -IGRAPH_EXPORT igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, +IGRAPH_EXPORT int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); +IGRAPH_EXPORT int igraph_contract_vertices(igraph_t *graph, + const igraph_vector_t *mapping, + const igraph_attribute_combination_t + *vertex_comb); +IGRAPH_EXPORT int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_t *permutation); +IGRAPH_EXPORT int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode); -IGRAPH_EXPORT igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, +IGRAPH_EXPORT int igraph_rewire(igraph_t *graph, + igraph_integer_t n, igraph_rewiring_t mode); +IGRAPH_EXPORT int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_bool_t loops, const igraph_attribute_combination_t *edge_comb); -IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +IGRAPH_EXPORT int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl, - igraph_vector_int_t *map, - igraph_vector_int_t *invmap); -IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, + igraph_vector_t *map, + igraph_vector_t *invmap); +IGRAPH_EXPORT int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl); -IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph_edges( - const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges); -IGRAPH_EXPORT igraph_error_t igraph_subgraph_from_edges(const igraph_t *graph, igraph_t *res, +IGRAPH_EXPORT int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, const igraph_es_t eids, igraph_bool_t delete_vertices); -IGRAPH_EXPORT igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids); +IGRAPH_EXPORT int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_subgraph_edges( - const igraph_t *graph, igraph_t *res, const igraph_es_t eids, - igraph_bool_t delete_vertices -); __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_paths.h b/src/vendor/cigraph/include/igraph_paths.h index 40256b4c243..067449d151b 100644 --- a/src/vendor/cigraph/include/igraph_paths.h +++ b/src/vendor/cigraph/include/igraph_paths.h @@ -22,335 +22,154 @@ #ifndef IGRAPH_PATHS_H #define IGRAPH_PATHS_H -#include "igraph_constants.h" -#include "igraph_datatype.h" #include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_iterators.h" -#include "igraph_matrix.h" +#include "igraph_constants.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" +#include "igraph_matrix.h" +#include "igraph_iterators.h" __BEGIN_DECLS -/** - * \typedef igraph_astar_heuristic_func_t - * \brief Distance estimator for A* algorithm. - * - * \ref igraph_get_shortest_path_astar() uses a heuristic based on a distance - * estimate to the target vertex to guide its search, and determine - * which vertex to try next. The heurstic function is expected to compute - * an estimate of the distance between \p from and \p to. In order for - * \ref igraph_get_shortest_path_astar() to find an exact shortest path, - * the distance must not be overestimated, i.e. the heuristic function - * must be \em admissible. - * - * \param result The result of the heuristic, i.e. the estimated distance. - * A lower value will mean this vertex will be a better candidate for - * exploration. - * \param from The vertex ID of the candidate vertex will be passed here. - * \param to The vertex ID of the endpoint of the path, i.e. the \c to parameter - * given to \ref igraph_get_shortest_path_astar(), will be passed here. - * \param extra The \c extra argument that was passed to - * \ref igraph_get_shortest_path_astar(). - * \return Error code. Must return \c IGRAPH_SUCCESS if there were no errors. - * This can be used to break off the algorithm if something unexpected happens, - * like a failed memory allocation (\c IGRAPH_ENOMEM). - * - * \sa \ref igraph_get_shortest_path_astar() - */ -typedef igraph_error_t igraph_astar_heuristic_func_t( - igraph_real_t *result, - igraph_integer_t from, igraph_integer_t to, - void *extra); - -typedef enum { - IGRAPH_FLOYD_WARSHALL_AUTOMATIC = 0, - IGRAPH_FLOYD_WARSHALL_ORIGINAL = 1, - IGRAPH_FLOYD_WARSHALL_TREE = 2 -} igraph_floyd_warshall_algorithm_t; - -IGRAPH_EXPORT igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_diameter(const igraph_t *graph, igraph_real_t *res, igraph_integer_t *from, igraph_integer_t *to, - igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_path, + igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t unconn); -IGRAPH_EXPORT igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT int igraph_diameter_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, - igraph_real_t *res, - igraph_integer_t *from, - igraph_integer_t *to, - igraph_vector_int_t *vertex_path, - igraph_vector_int_t *edge_path, + igraph_real_t *pres, + igraph_integer_t *pfrom, + igraph_integer_t *pto, + igraph_vector_t *path, igraph_bool_t directed, igraph_bool_t unconn); -IGRAPH_EXPORT igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t from, const igraph_vs_t to, - igraph_neimode_t mode, igraph_real_t cutoff); -IGRAPH_EXPORT igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t from, const igraph_vs_t to, - igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_distances_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); -IGRAPH_EXPORT igraph_error_t igraph_distances_dijkstra_cutoff(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, - igraph_real_t cutoff); -IGRAPH_EXPORT igraph_error_t igraph_distances_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); -IGRAPH_EXPORT igraph_error_t igraph_distances_johnson(const igraph_t *graph, - igraph_matrix_t *res, - const igraph_vs_t from, - const igraph_vs_t to, - const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_distances_floyd_warshall(const igraph_t *graph, - igraph_matrix_t *res, - igraph_vs_t from, - igraph_vs_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_floyd_warshall_algorithm_t method); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_shortest_paths_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT 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); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); - -IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_integer_t from, const igraph_vs_t to, - igraph_neimode_t mode, - igraph_vector_int_t *parents, - igraph_vector_int_t *inbound_edges); -IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_integer_t from, - igraph_vs_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_vector_int_t *parents, - igraph_vector_int_t *inbound_edges); -IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_integer_t from, - igraph_vs_t to, +IGRAPH_EXPORT 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, - igraph_vector_int_t *parents, - igraph_vector_int_t *inbound_edges); - -IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t from, - igraph_integer_t to, - igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, + igraph_neimode_t mode); +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t from, - igraph_integer_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_astar_heuristic_func_t *heuristic, - void *extra); - -IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_vector_int_t *nrgeo, - igraph_integer_t from, const igraph_vs_t to, - igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_vector_int_t *nrgeo, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_average_path_length(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_average_path_length_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *unconnected, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *weights, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_eccentricity(const igraph_t *graph, +IGRAPH_EXPORT int igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_t *res, - igraph_vs_t vids, - igraph_neimode_t mode); - -IGRAPH_EXPORT igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, +IGRAPH_EXPORT int igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_graph_center(const igraph_t *graph, - igraph_vector_int_t *res, - igraph_neimode_t mode); - -IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, - igraph_real_t *diameter, - igraph_integer_t vid_start, - igraph_integer_t *from, - igraph_integer_t *to, - igraph_bool_t directed, - igraph_bool_t unconn); -IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_real_t *diameter, - igraph_integer_t vid_start, - igraph_integer_t *from, - igraph_integer_t *to, - igraph_bool_t directed, - igraph_bool_t unconn); - -IGRAPH_EXPORT igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_random_walk(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t start, - igraph_neimode_t mode, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_get_k_shortest_paths(const igraph_t *graph, +IGRAPH_EXPORT int igraph_random_edge_walk(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *vertex_paths, - igraph_vector_int_list_t *edge_paths, - igraph_integer_t k, - igraph_integer_t from, - igraph_integer_t to, - igraph_neimode_t mode); - -IGRAPH_EXPORT igraph_error_t igraph_spanner(const igraph_t *graph, - igraph_vector_int_t *spanner, - igraph_real_t stretch, - const igraph_vector_t *weights); - -IGRAPH_EXPORT igraph_error_t igraph_get_widest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_integer_t from, - igraph_vs_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_vector_int_t *parents, - igraph_vector_int_t *inbound_edges); -IGRAPH_EXPORT igraph_error_t igraph_get_widest_path(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t from, - igraph_integer_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_widest_path_widths_floyd_warshall(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); -IGRAPH_EXPORT igraph_error_t igraph_widest_path_widths_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); -IGRAPH_EXPORT igraph_error_t igraph_voronoi(const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_vector_t *distances, - const igraph_vector_int_t *generators, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_voronoi_tiebreaker_t tiebreaker); - -IGRAPH_EXPORT igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t *path); - -IGRAPH_EXPORT igraph_error_t igraph_vertex_path_from_edge_path( - const igraph_t *graph, igraph_integer_t start, - const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, - igraph_neimode_t mode); - -/* Deprecated functions: */ - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_random_edge_walk(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_int_t *edgewalk, - igraph_integer_t start, - igraph_neimode_t mode, - igraph_integer_t steps, - igraph_random_walk_stuck_t stuck); + igraph_vector_t *edgewalk, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_pmt.h b/src/vendor/cigraph/include/igraph_pmt.h index fe8c350d3eb..ab810d6bcf8 100644 --- a/src/vendor/cigraph/include/igraph_pmt.h +++ b/src/vendor/cigraph/include/igraph_pmt.h @@ -27,27 +27,35 @@ #define CONCAT3(a,b,c) CONCAT3x(a,b,c) #define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d #define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d) -#define CONCAT5x(a,b,c,d,e) a ## _ ## b ## _ ## c ## _ ## d ## _ ## e -#define CONCAT5(a,b,c,d,e) CONCAT5x(a,b,c,d,e) #if defined(BASE_IGRAPH_REAL) #define BASE igraph_real_t - #define BASE_VECTOR igraph_vector_t - #define BASE_MATRIX igraph_matrix_t #define SHORT - #define OUT_FORMAT "%g" + #define OUT_FORMAT "%G" #define PRINTFUNC(val) igraph_real_printf(val) - #define SNPRINTFUNC(str, size, val) igraph_real_snprintf(str, size, val) - #define FPRINTFUNC_ALIGNED(file, width, val) igraph_real_fprintf_aligned(file, width, val) #define FPRINTFUNC(file, val) igraph_real_fprintf(file, val) #define ZERO 0.0 #define ONE 1.0 #define MULTIPLICITY 1 +#elif defined(BASE_FLOAT) + #define BASE float + #define SHORT float + #define OUT_FORMAT "%f" + #define ZERO 0.0F + #define ONE 1.0F + #define MULTIPLICITY 1 + +#elif defined(BASE_LONG) + #define BASE long + #define SHORT long + #define OUT_FORMAT "%ld" + #define ZERO 0L + #define ONE 1L + #define MULTIPLICITY 1 + #elif defined(BASE_CHAR) #define BASE char - #define BASE_VECTOR igraph_vector_char_t - #define BASE_MATRIX igraph_matrix_char_t #define SHORT char #define OUT_FORMAT "%d" #define ZERO 0 @@ -56,8 +64,6 @@ #elif defined(BASE_BOOL) #define BASE igraph_bool_t - #define BASE_VECTOR igraph_vector_bool_t - #define BASE_MATRIX igraph_matrix_bool_t #define SHORT bool #define OUT_FORMAT "%d" #define ZERO 0 @@ -69,22 +75,12 @@ #elif defined(BASE_INT) #define BASE igraph_integer_t - #define BASE_VECTOR igraph_vector_int_t - #define BASE_MATRIX igraph_matrix_int_t #define SHORT int #define OUT_FORMAT "%" IGRAPH_PRId #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 -#elif defined(BASE_FORTRAN_INT) - #define BASE int - #define SHORT fortran_int - #define OUT_FORMAT "%d" - #define ZERO 0 - #define ONE 1 - #define MULTIPLICITY 1 - #elif defined(BASE_PTR) #define BASE void* #define SHORT ptr @@ -94,15 +90,9 @@ #elif defined(BASE_COMPLEX) #undef complex #define BASE igraph_complex_t - #define BASE_VECTOR igraph_vector_complex_t - #define BASE_MATRIX igraph_matrix_complex_t #define SHORT complex - #define PRINTFUNC(val) igraph_complex_printf(val) - #define SNPRINTFUNC(str, size, val) igraph_complex_snprintf(str, size, val) - #define FPRINTFUNC_ALIGNED(file, width, val) igraph_complex_fprintf_aligned(file, width, val) - #define FPRINTFUNC(file, val) igraph_complex_fprintf(file, val) - #define ZERO {{0.0, 0.0}} - #define ONE {{1.0, 0.0}} + #define ZERO igraph_complex(0,0) + #define ONE {{1.0,0.0}} #define MULTIPLICITY 2 #define NOTORDERED 1 #define NOABS 1 @@ -113,62 +103,21 @@ #define EQ(a,b) IGRAPH_COMPLEX_EQ((a),(b)) #define SQ(a) IGRAPH_REAL(igraph_complex_mul((a),(a))) -#elif defined(BASE_GRAPH) - #define BASE igraph_t - #else #error unknown BASE_ directive #endif -#if defined(VECTOR_LIST) - #if defined(BASE_IGRAPH_REAL) - #define FUNCTION(c) CONCAT2x(igraph_vector_list,c) - #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_list,c) - #define TYPE igraph_vector_list_t - #elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define FUNCTION(c) CONCAT2x(igraph_vector_bool_list,c) - #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_bool_list,c) - #define TYPE igraph_vector_bool_list_t - #else - #define FUNCTION(c) CONCAT4(igraph_vector,SHORT,list,c) - #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_vector,SHORT,list,c) - #define TYPE CONCAT3(igraph_vector,SHORT,list_t) - #endif -#elif defined(MATRIX_LIST) - #if defined(BASE_IGRAPH_REAL) - #define FUNCTION(c) CONCAT2x(igraph_matrix_list,c) - #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_list,c) - #define TYPE igraph_matrix_list_t - #elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define FUNCTION(c) CONCAT2x(igraph_matrix_bool_list,c) - #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_bool_list,c) - #define TYPE igraph_matrix_bool_list_t - #else - #define FUNCTION(c) CONCAT4(igraph_matrix,SHORT,list,c) - #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_matrix,SHORT,list,c) - #define TYPE CONCAT3(igraph_matrix,SHORT,list_t) - #endif -#elif defined(GRAPH_LIST) - #define FUNCTION(c) CONCAT2x(igraph_graph_list,c) - #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_graph_list,c) - #define TYPE igraph_graph_list_t +#if defined(BASE_IGRAPH_REAL) + #define FUNCTION(dir,name) CONCAT2(dir,name) + #define TYPE(dir) CONCAT2(dir,t) +#elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(a,c) CONCAT3x(a,bool,c) + #define TYPE(dir) CONCAT3x(dir,bool,t) #else - #if defined(BASE_IGRAPH_REAL) - #define FUNCTION(a,c) CONCAT2(a,c) - #define TYPE(a) CONCAT2(a,t) - #elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define FUNCTION(a,c) CONCAT3x(a,bool,c) - #define TYPE(a) CONCAT3x(a,bool,t) - #else - #define FUNCTION(a,c) CONCAT3(a,SHORT,c) - #define TYPE(a) CONCAT3(a,SHORT,t) - #endif + #define FUNCTION(a,c) CONCAT3(a,SHORT,c) + #define TYPE(dir) CONCAT3(dir,SHORT,t) #endif #if defined(HEAP_TYPE_MIN) @@ -177,7 +126,6 @@ #define HEAPLESS > #define HEAPLESSEQ >= #undef FUNCTION - #undef INTERNAL_FUNCTION #undef TYPE #if defined(BASE_IGRAPH_REAL) #define FUNCTION(dir,name) CONCAT3(dir,min,name) diff --git a/src/vendor/cigraph/include/igraph_pmt_off.h b/src/vendor/cigraph/include/igraph_pmt_off.h index 03ef43c7fd3..95a957e7fe1 100644 --- a/src/vendor/cigraph/include/igraph_pmt_off.h +++ b/src/vendor/cigraph/include/igraph_pmt_off.h @@ -37,14 +37,6 @@ #undef BASE_EPSILON #endif -#ifdef BASE_MATRIX - #undef BASE_MATRIX -#endif - -#ifdef BASE_VECTOR - #undef BASE_VECTOR -#endif - #ifdef CONCAT2 #undef CONCAT2 #endif @@ -69,14 +61,6 @@ #undef CONCAT4x #endif -#ifdef CONCAT5 - #undef CONCAT5 -#endif - -#ifdef CONCAT5x - #undef CONCAT5x -#endif - #ifdef FP #undef FP #endif @@ -89,10 +73,6 @@ #undef IN_FORMAT #endif -#ifdef INTERNAL_FUNCTION - #undef INTERNAL_FUNCTION -#endif - #ifdef MULTIPLICITY #undef MULTIPLICITY #endif @@ -169,16 +149,8 @@ #undef PRINTFUNC #endif -#ifdef SNPRINTFUNC - #undef SNPRINTFUNC -#endif - -#ifdef FPRINTFUNC_ALIGNED - #undef FPRINTFUNC_ALIGNED -#endif - #ifdef FPRINTFUNC - #undef FPRINTFUNC + #undef PRINTFUNC #endif #ifdef UNSIGNED diff --git a/src/vendor/cigraph/include/igraph_progress.h b/src/vendor/cigraph/include/igraph_progress.h index f85139a412d..f7a0f25e1d3 100644 --- a/src/vendor/cigraph/include/igraph_progress.h +++ b/src/vendor/cigraph/include/igraph_progress.h @@ -25,7 +25,6 @@ #define IGRAPH_PROGRESS_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -143,16 +142,16 @@ __BEGIN_DECLS * error code \c IGRAPH_INTERRUPTED in this case. */ -typedef igraph_error_t igraph_progress_handler_t(const char *message, igraph_real_t percent, +typedef int igraph_progress_handler_t(const char *message, igraph_real_t percent, void *data); IGRAPH_EXPORT extern igraph_progress_handler_t igraph_progress_handler_stderr; IGRAPH_EXPORT igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler); -IGRAPH_EXPORT igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void *data); +IGRAPH_EXPORT int igraph_progress(const char *message, igraph_real_t percent, void *data); -IGRAPH_EXPORT igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void *data, +IGRAPH_EXPORT int igraph_progressf(const char *message, igraph_real_t percent, void *data, ...); /** diff --git a/src/vendor/cigraph/include/igraph_psumtree.h b/src/vendor/cigraph/include/igraph_psumtree.h index 658d155a5c9..c27d5764679 100644 --- a/src/vendor/cigraph/include/igraph_psumtree.h +++ b/src/vendor/cigraph/include/igraph_psumtree.h @@ -25,25 +25,24 @@ #define IGRAPH_PSUMTREE_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS typedef struct { igraph_vector_t v; - igraph_integer_t size; - igraph_integer_t offset; + long int size; + long int offset; } igraph_psumtree_t; -IGRAPH_EXPORT igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size); +IGRAPH_EXPORT int igraph_psumtree_init(igraph_psumtree_t *t, long int size); IGRAPH_EXPORT void igraph_psumtree_reset(igraph_psumtree_t *t); IGRAPH_EXPORT void igraph_psumtree_destroy(igraph_psumtree_t *t); -IGRAPH_EXPORT igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t idx); -IGRAPH_EXPORT igraph_integer_t igraph_psumtree_size(const igraph_psumtree_t *t); -IGRAPH_EXPORT igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, +IGRAPH_EXPORT igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx); +IGRAPH_EXPORT long int igraph_psumtree_size(const igraph_psumtree_t *t); +IGRAPH_EXPORT int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, igraph_real_t elem); -IGRAPH_EXPORT igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, +IGRAPH_EXPORT int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, igraph_real_t new_value); IGRAPH_EXPORT igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); diff --git a/src/vendor/cigraph/include/igraph_random.h b/src/vendor/cigraph/include/igraph_random.h index 71b4e2557d5..cbdb5c32368 100644 --- a/src/vendor/cigraph/include/igraph_random.h +++ b/src/vendor/cigraph/include/igraph_random.h @@ -25,131 +25,73 @@ #define IGRAPH_RANDOM_H #include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include +__BEGIN_DECLS + #include #include -__BEGIN_DECLS +#include "igraph_types.h" +#include "igraph_vector.h" + +/* The new RNG interface is (somewhat) modelled based on the GSL */ -/* The new RNG interface is (somewhat) modelled on the GSL */ - -/* When implementing your own RNG in igraph, the following methods must be - * supplied in the corresponding igraph_rng_type_t structure: - * - * - init() - * - destroy() - * - seed() - * - get() - * - * Optionally, you can provide specialized routines for several distributions - * in the following functions: - * - * - get_int() - * - get_real() - * - get_norm() - * - get_geom() - * - get_binom() - * - get_exp() - * - get_gamma() - * - get_pois() - * - * The best is probably to define get() leave the others as NULL; igraph will use - * default implementations for these. - * - * Note that if all that you would do in a get_real() implementation is to - * generate random bits with get() and divide by the maximum, don't do that; - * The default implementation takes care of calling get() a sufficient number of - * times to utilize most of the precision of the igraph_real_t type, and generate - * accurate variates. Inaccuracies in the output of get_real() can get magnified - * when using the default generators for non-uniform distributions. - * When implementing get_real(), the sampling range must be half-open, i.e. [0, 1). - * If unsure, leave get_real() unimplemented and igraph will provide an implementation - * in terms of get(). - * - * When implementing get_int(), you do not need to check whether lo < hi; - * the caller is responsible for ensuring that this is the case. You can always - * assume that hi > lo. Note that both endpoints are _inclusive_, and you must - * make sure that your generation scheme works for both 32-bit and 64-bit - * versions of igraph_integer_t as igraph can be compiled for both cases. If - * you are unsure, leave get_int() unimplemented and igraph will provide its - * own implementation based on get(). - */ typedef struct igraph_rng_type_t { const char *name; - uint8_t bits; - - /* Initialization and destruction */ - igraph_error_t (*init)(void **state); + unsigned long int min; /* 'min' must always be set to 0 */ + unsigned long int max; + int (*init)(void **state); void (*destroy)(void *state); - - /* Seeding */ - igraph_error_t (*seed)(void *state, igraph_uint_t seed); - - /* Fundamental generator: return as many random bits as the RNG supports in - * a single round */ - igraph_uint_t (*get)(void *state); - - /* Optional generators; defaults are provided by igraph that rely solely - * on get() */ - igraph_integer_t (*get_int)(void *state, igraph_integer_t l, igraph_integer_t h); + int (*seed)(void *state, unsigned long int seed); + unsigned long int (*get)(void *state); igraph_real_t (*get_real)(void *state); igraph_real_t (*get_norm)(void *state); igraph_real_t (*get_geom)(void *state, igraph_real_t p); - igraph_real_t (*get_binom)(void *state, igraph_integer_t n, igraph_real_t p); + igraph_real_t (*get_binom)(void *state, long int n, igraph_real_t p); igraph_real_t (*get_exp)(void *state, igraph_real_t rate); igraph_real_t (*get_gamma)(void *state, igraph_real_t shape, igraph_real_t scale); - igraph_real_t (*get_pois)(void *state, igraph_real_t mu); } igraph_rng_type_t; typedef struct igraph_rng_t { const igraph_rng_type_t *type; void *state; - igraph_bool_t is_seeded; + int def; } igraph_rng_t; /* --------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); +IGRAPH_EXPORT int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); IGRAPH_EXPORT void igraph_rng_destroy(igraph_rng_t *rng); -IGRAPH_EXPORT igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed); -IGRAPH_EXPORT igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng); -IGRAPH_EXPORT igraph_uint_t igraph_rng_max(const igraph_rng_t *rng); -IGRAPH_EXPORT const char *igraph_rng_name(const igraph_rng_t *rng); - -IGRAPH_EXPORT igraph_integer_t igraph_rng_get_integer( - igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t h -); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_normal( - igraph_rng_t *rng, igraph_real_t m, igraph_real_t s -); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif( - igraph_rng_t *rng, igraph_real_t l, igraph_real_t h -); +IGRAPH_EXPORT int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed); +IGRAPH_EXPORT unsigned long int igraph_rng_max(igraph_rng_t *rng); +IGRAPH_EXPORT IGRAPH_DEPRECATED unsigned long int igraph_rng_min(igraph_rng_t *rng); +IGRAPH_EXPORT const char *igraph_rng_name(igraph_rng_t *rng); + +IGRAPH_EXPORT long int igraph_rng_get_integer(igraph_rng_t *rng, + long int l, long int h); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, + igraph_real_t m, igraph_real_t s); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, + igraph_real_t l, igraph_real_t h); IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng); IGRAPH_EXPORT igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_binom( - igraph_rng_t *rng, igraph_integer_t n, igraph_real_t p -); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, + igraph_real_t p); IGRAPH_EXPORT igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma( - igraph_rng_t *rng, igraph_real_t shape, igraph_real_t scale -); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate); -IGRAPH_EXPORT igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, +IGRAPH_EXPORT unsigned long int igraph_rng_get_int31(igraph_rng_t *rng); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape, + igraph_real_t scale); +IGRAPH_EXPORT int igraph_rng_get_dirichlet(igraph_rng_t *rng, const igraph_vector_t *alpha, igraph_vector_t *result); /* --------------------------------- */ IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_glibc2; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_rand; IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_mt19937; -IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg32; -IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg64; IGRAPH_EXPORT igraph_rng_t *igraph_rng_default(void); IGRAPH_EXPORT void igraph_rng_set_default(igraph_rng_t *rng); @@ -163,15 +105,20 @@ void PutRNGstate(void); #define RNG_BEGIN() GetRNGstate() #define RNG_END() PutRNGstate() +double Rf_dnorm4(double x, double mu, double sigma, int give_log); +#define igraph_dnorm Rf_dnorm4 + #else #define RNG_BEGIN() \ - do { if (!igraph_rng_default()->is_seeded) { \ + if (igraph_rng_default()->def == 1) { \ igraph_rng_seed(igraph_rng_default(), time(0)); \ - igraph_rng_default()->is_seeded = 1; \ - } } while (0) -#define RNG_END() \ - do { /* nothing */ } while (0) + igraph_rng_default()->def=2; \ + } +#define RNG_END() /* do nothing */ + +IGRAPH_EXPORT double igraph_dnorm(double x, double mu, double sigma, int give_log); + #endif #define RNG_INTEGER(l,h) (igraph_rng_get_integer(igraph_rng_default(),(l),(h))) @@ -180,10 +127,7 @@ void PutRNGstate(void); #define RNG_UNIF01() (igraph_rng_get_unif01(igraph_rng_default())) #define RNG_GEOM(p) (igraph_rng_get_geom(igraph_rng_default(),(p))) #define RNG_BINOM(n,p) (igraph_rng_get_binom(igraph_rng_default(),(n),(p))) -#define RNG_EXP(rate) (igraph_rng_get_exp(igraph_rng_default(),(rate))) -#define RNG_POIS(rate) (igraph_rng_get_pois(igraph_rng_default(),(rate))) -#define RNG_GAMMA(shape, scale) \ - (igraph_rng_get_gamma(igraph_rng_default(), (shape), (scale))) +#define RNG_INT31() (igraph_rng_get_int31(igraph_rng_default())) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_scan.h b/src/vendor/cigraph/include/igraph_scan.h index bfc47c46626..0b1480f186e 100644 --- a/src/vendor/cigraph/include/igraph_scan.h +++ b/src/vendor/cigraph/include/igraph_scan.h @@ -26,47 +26,44 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_arpack.h" #include "igraph_constants.h" -#include "igraph_error.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, +IGRAPH_EXPORT int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, igraph_vector_t *res, - const igraph_vector_t *weights_them, + const igraph_vector_t *weigths_them, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, +IGRAPH_EXPORT int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, - igraph_integer_t k, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, +IGRAPH_EXPORT int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, - const igraph_vector_int_list_t *neighborhoods); -IGRAPH_EXPORT igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vector_t *weights, - const igraph_vector_int_list_t *neighborhoods); + const igraph_vector_ptr_t *neighborhoods); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_scg.h b/src/vendor/cigraph/include/igraph_scg.h new file mode 100644 index 00000000000..7b466a42496 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_scg.h @@ -0,0 +1,143 @@ +/* -*- 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 + +*/ + +#ifndef IGRAPH_SCG_H +#define IGRAPH_SCG_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_matrix.h" +#include "igraph_sparsemat.h" + +__BEGIN_DECLS + +typedef enum { IGRAPH_SCG_SYMMETRIC = 1, IGRAPH_SCG_LAPLACIAN = 2, + IGRAPH_SCG_STOCHASTIC = 3 + } igraph_scg_matrix_t; + +typedef enum { IGRAPH_SCG_OPTIMUM = 1, IGRAPH_SCG_INTERV_KM = 2, + IGRAPH_SCG_INTERV = 3, IGRAPH_SCG_EXACT = 4 + } +igraph_scg_algorithm_t; + +typedef enum { IGRAPH_SCG_NORM_ROW = 1, IGRAPH_SCG_NORM_COL = 2 } +igraph_scg_norm_t; + +typedef enum { IGRAPH_SCG_DIRECTION_DEFAULT = 1, + IGRAPH_SCG_DIRECTION_LEFT = 2, + IGRAPH_SCG_DIRECTION_RIGHT = 3 + } igraph_scg_direction_t; + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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); + +IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_separators.h b/src/vendor/cigraph/include/igraph_separators.h index 7008d9261b1..aed1d5a6f9a 100644 --- a/src/vendor/cigraph/include/igraph_separators.h +++ b/src/vendor/cigraph/include/igraph_separators.h @@ -25,28 +25,28 @@ #define IGRAPH_SEPARATORS_H #include "igraph_decls.h" - +#include "igraph_constants.h" +#include "igraph_types.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_iterators.h" -#include "igraph_types.h" -#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_is_separator(const igraph_t *graph, +IGRAPH_EXPORT int igraph_is_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_all_minimal_st_separators(const igraph_t *graph, - igraph_vector_int_list_t *separators); +IGRAPH_EXPORT int igraph_all_minimal_st_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators); -IGRAPH_EXPORT igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, +IGRAPH_EXPORT int igraph_is_minimal_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_minimum_size_separators(const igraph_t *graph, - igraph_vector_int_list_t *separators); +IGRAPH_EXPORT int igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_sparsemat.h b/src/vendor/cigraph/include/igraph_sparsemat.h index 50ba93682cf..6070ca834dd 100644 --- a/src/vendor/cigraph/include/igraph_sparsemat.h +++ b/src/vendor/cigraph/include/igraph_sparsemat.h @@ -25,7 +25,6 @@ #define IGRAPH_SPARSEMAT_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_datatype.h" @@ -35,25 +34,20 @@ __BEGIN_DECLS -/* - * These types are private to igraph, and customized to use igraph_integer_t. - * Do not attempt to access them using a separate copy of the CXSparse library. - * Use the public igraph_sparsemat_... types instead. - */ -struct cs_igraph_sparse; -struct cs_igraph_symbolic; -struct cs_igraph_numeric; +struct cs_di_sparse; +struct cs_di_symbolic; +struct cs_di_numeric; typedef struct { - struct cs_igraph_sparse *cs; + struct cs_di_sparse *cs; } igraph_sparsemat_t; typedef struct { - struct cs_igraph_symbolic *symbolic; + struct cs_di_symbolic *symbolic; } igraph_sparsemat_symbolic_t; typedef struct { - struct cs_igraph_numeric *numeric; + struct cs_di_numeric *numeric; } igraph_sparsemat_numeric_t; typedef enum { IGRAPH_SPARSEMAT_TRIPLET, @@ -61,154 +55,149 @@ typedef enum { IGRAPH_SPARSEMAT_TRIPLET, } igraph_sparsemat_type_t; typedef struct { - const igraph_sparsemat_t *mat; - igraph_integer_t pos; - igraph_integer_t col; + igraph_sparsemat_t *mat; + int pos; + int col; } igraph_sparsemat_iterator_t; -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init( - igraph_sparsemat_t *A, igraph_integer_t rows, igraph_integer_t cols, - igraph_integer_t nzmax -); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_copy( - igraph_sparsemat_t *to, const igraph_sparsemat_t *from); +IGRAPH_EXPORT int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax); +IGRAPH_EXPORT int igraph_sparsemat_copy(igraph_sparsemat_t *to, + const igraph_sparsemat_t *from); IGRAPH_EXPORT void igraph_sparsemat_destroy(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax); - -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_eye(igraph_sparsemat_t *A, - igraph_integer_t n, igraph_integer_t nzmax, - igraph_real_t value, igraph_bool_t compress); +IGRAPH_EXPORT int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_diag(igraph_sparsemat_t *A, - igraph_integer_t nzmax, const igraph_vector_t *values, - igraph_bool_t compress); - -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_ncol(const igraph_sparsemat_t *B); +IGRAPH_EXPORT long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A); +IGRAPH_EXPORT long int igraph_sparsemat_ncol(const igraph_sparsemat_t *B); IGRAPH_EXPORT igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_permute(const igraph_sparsemat_t *A, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_real_t igraph_sparsemat_get( - const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col -); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, - igraph_integer_t row, igraph_integer_t col, igraph_real_t elem); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, + igraph_real_t elem); +IGRAPH_EXPORT int igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_transpose( - const igraph_sparsemat_t *A, igraph_sparsemat_t *res -); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_fkeep(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res, int values); +IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_dupl(igraph_sparsemat_t *A); +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); +IGRAPH_EXPORT int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_usolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - igraph_integer_t order); + int order); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - igraph_integer_t order, + int order, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_print(const igraph_sparsemat_t *A, FILE *outstream); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, + igraph_real_t value, + igraph_bool_t compress); + +IGRAPH_EXPORT int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, + const igraph_vector_t *values, + igraph_bool_t compress); + +IGRAPH_EXPORT int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, +IGRAPH_EXPORT int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); + +IGRAPH_EXPORT int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, const igraph_matrix_t *mat, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_as_matrix(igraph_matrix_t *res, +IGRAPH_EXPORT int igraph_sparsemat_as_matrix(igraph_matrix_t *res, const igraph_sparsemat_t *spmat); typedef enum { IGRAPH_SPARSEMAT_SOLVE_LU, IGRAPH_SPARSEMAT_SOLVE_QR } igraph_sparsemat_solve_t; -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_lu(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din, double tol); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_qr(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); @@ -217,95 +206,82 @@ IGRAPH_EXPORT void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t * IGRAPH_EXPORT igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_minmax(igraph_sparsemat_t *A, igraph_real_t *min, igraph_real_t *max); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, +IGRAPH_EXPORT long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); +IGRAPH_EXPORT long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_colmins(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); +IGRAPH_EXPORT int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t n); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, - igraph_integer_t nrow, igraph_integer_t ncol, igraph_integer_t nzmax); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n); +IGRAPH_EXPORT int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n); +IGRAPH_EXPORT int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, + long int ncol, int nzmax); +IGRAPH_EXPORT int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, const igraph_vector_t *fact); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, const igraph_vector_t *fact); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, const igraph_matrix_t *B, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, +IGRAPH_EXPORT int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, const igraph_sparsemat_t *B, igraph_matrix_t *res); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, - igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz); +IGRAPH_EXPORT int igraph_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz); +IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, + int *p, int *i, double *x, int nz); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, +IGRAPH_EXPORT int igraph_sparsemat_sort(const igraph_sparsemat_t *A, igraph_sparsemat_t *sorted); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); - -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A); +IGRAPH_EXPORT int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_cols(igraph_sparsemat_t *sparsemat, - igraph_bool_t allow_zeros); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_rows(igraph_sparsemat_t *sparsemat, - igraph_bool_t allow_zeros); +IGRAPH_EXPORT int igraph_sparsemat_neg(igraph_sparsemat_t *A); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_iterator_init( - igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat -); -IGRAPH_EXPORT igraph_error_t igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, + igraph_sparsemat_t *sparsemat); +IGRAPH_EXPORT int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); IGRAPH_EXPORT igraph_real_t igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_copy( - igraph_sparsemat_t *to, const igraph_sparsemat_t *from); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_diag( - igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, - igraph_bool_t compress); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_eye( - igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, - igraph_real_t value, igraph_bool_t compress); +IGRAPH_EXPORT int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_spmatrix.h b/src/vendor/cigraph/include/igraph_spmatrix.h new file mode 100644 index 00000000000..141c39afb15 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_spmatrix.h @@ -0,0 +1,109 @@ +/* -*- 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_SPMATRIX_H +#define IGRAPH_SPMATRIX_H + +#include "igraph_decls.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Sparse matrix */ +/* -------------------------------------------------- */ + +/** + * \section about_igraph_spmatrix_t_objects About \type igraph_spmatrix_t objects + * + * The \type igraph_spmatrix_t type stores a sparse matrix with the + * assumption that the number of nonzero elements in the matrix scales + * linearly with the row or column count of the matrix (so most of the + * elements are zero). Of course it can store an arbitrary real matrix, + * but if most of the elements are nonzero, one should use \type igraph_matrix_t + * instead. + * + * The elements are stored in column compressed format, so the elements + * in the same column are stored adjacent in the computer's memory. The storage + * requirement for a sparse matrix is O(n) where n is the number of nonzero + * elements. Actually it can be a bit larger, see the documentation of + * the vector type for an explanation. + */ +typedef struct s_spmatrix { + igraph_vector_t ridx, cidx, data; + long int nrow, ncol; +} igraph_spmatrix_t; + +#define IGRAPH_SPMATRIX_INIT_FINALLY(m, nr, nc) \ + do { IGRAPH_CHECK(igraph_spmatrix_init(m, nr, nc)); \ + IGRAPH_FINALLY(igraph_spmatrix_destroy, m); } while (0) + +IGRAPH_EXPORT int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol); +IGRAPH_EXPORT void igraph_spmatrix_destroy(igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col); +IGRAPH_EXPORT int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value); +IGRAPH_EXPORT int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, + igraph_real_t value); +IGRAPH_EXPORT int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from); +IGRAPH_EXPORT long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_size(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m); +IGRAPH_EXPORT long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to); +IGRAPH_EXPORT int igraph_spmatrix_null(igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n); +IGRAPH_EXPORT int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n); +IGRAPH_EXPORT int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col); +IGRAPH_EXPORT int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row); +IGRAPH_EXPORT int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx); +IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, + igraph_real_t *ridx, igraph_real_t *cidx); +IGRAPH_EXPORT void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by); +IGRAPH_EXPORT int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res); +IGRAPH_EXPORT int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res); + +IGRAPH_EXPORT int igraph_spmatrix_print(const igraph_spmatrix_t *matrix); +IGRAPH_EXPORT int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file); + + +typedef struct s_spmatrix_iter { + const igraph_spmatrix_t *m; /* pointer to the matrix we are iterating over */ + long int pos; /* internal index into the data vector */ + long int ri; /* row index */ + long int ci; /* column index */ + igraph_real_t value; /* value at the given cell */ +} igraph_spmatrix_iter_t; + +IGRAPH_EXPORT int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m); +IGRAPH_EXPORT int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit); +IGRAPH_EXPORT void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit); + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_stack.h b/src/vendor/cigraph/include/igraph_stack.h index 30f9b44ac28..a186296ff4b 100644 --- a/src/vendor/cigraph/include/igraph_stack.h +++ b/src/vendor/cigraph/include/igraph_stack.h @@ -25,7 +25,6 @@ #define IGRAPH_STACK_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -40,6 +39,12 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL +#define BASE_LONG +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_LONG + #define BASE_INT #include "igraph_pmt.h" #include "igraph_stack_pmt.h" @@ -58,13 +63,16 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_BOOL +#define BASE_PTR +#include "igraph_pmt.h" +#include "igraph_stack_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_PTR + #define IGRAPH_STACK_NULL { 0,0,0 } -#define IGRAPH_STACK_INIT_FINALLY(s, capacity) \ - do { IGRAPH_CHECK(igraph_stack_init(s, capacity)); \ - IGRAPH_FINALLY(igraph_stack_destroy, s); } while (0) -#define IGRAPH_STACK_INT_INIT_FINALLY(s, capacity) \ - do { IGRAPH_CHECK(igraph_stack_int_init(s, capacity)); \ - IGRAPH_FINALLY(igraph_stack_int_destroy, s); } while (0) + +IGRAPH_EXPORT void igraph_stack_ptr_free_all(igraph_stack_ptr_t* s); +IGRAPH_EXPORT void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* s); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_stack_pmt.h b/src/vendor/cigraph/include/igraph_stack_pmt.h index 18055332027..1a9459b690f 100644 --- a/src/vendor/cigraph/include/igraph_stack_pmt.h +++ b/src/vendor/cigraph/include/igraph_stack_pmt.h @@ -34,14 +34,14 @@ typedef struct TYPE(igraph_stack) { BASE* end; } TYPE(igraph_stack); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity); +IGRAPH_EXPORT int FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, long int size); IGRAPH_EXPORT void FUNCTION(igraph_stack, destroy)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity); +IGRAPH_EXPORT int FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, long int size); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT long int FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); IGRAPH_EXPORT void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); +IGRAPH_EXPORT int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); IGRAPH_EXPORT BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s); IGRAPH_EXPORT BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); +IGRAPH_EXPORT int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); diff --git a/src/vendor/cigraph/include/igraph_statusbar.h b/src/vendor/cigraph/include/igraph_statusbar.h index d3bc8995403..c3cf4f33ba0 100644 --- a/src/vendor/cigraph/include/igraph_statusbar.h +++ b/src/vendor/cigraph/include/igraph_statusbar.h @@ -25,7 +25,6 @@ #define IGRAPH_STATUSBAR_H #include "igraph_decls.h" -#include "igraph_error.h" __BEGIN_DECLS @@ -61,17 +60,15 @@ __BEGIN_DECLS * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. - * \return Error code. The current calculation will abort if you return anything - * else than \c IGRAPH_SUCCESS here. */ -typedef igraph_error_t igraph_status_handler_t(const char *message, void *data); +typedef int igraph_status_handler_t(const char *message, void *data); IGRAPH_EXPORT extern igraph_status_handler_t igraph_status_handler_stderr; -IGRAPH_EXPORT igraph_status_handler_t *igraph_set_status_handler(igraph_status_handler_t new_handler); +IGRAPH_EXPORT igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler); -IGRAPH_EXPORT igraph_error_t igraph_status(const char *message, void *data); +IGRAPH_EXPORT int igraph_status(const char *message, void *data); /** * \define IGRAPH_STATUS @@ -98,7 +95,7 @@ IGRAPH_EXPORT igraph_error_t igraph_status(const char *message, void *data); } \ } while (0) -IGRAPH_EXPORT igraph_error_t igraph_statusf(const char *message, void *data, ...); +IGRAPH_EXPORT int igraph_statusf(const char *message, void *data, ...); /** * \define IGRAPH_STATUSF diff --git a/src/vendor/cigraph/include/igraph_structural.h b/src/vendor/cigraph/include/igraph_structural.h index bdfce8cb90c..100aa476e62 100644 --- a/src/vendor/cigraph/include/igraph_structural.h +++ b/src/vendor/cigraph/include/igraph_structural.h @@ -25,14 +25,14 @@ #define IGRAPH_STRUCTURAL_H #include "igraph_decls.h" -#include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_constants.h" -#include "igraph_iterators.h" -#include "igraph_matrix.h" -#include "igraph_sparsemat.h" #include "igraph_types.h" #include "igraph_vector.h" +#include "igraph_matrix.h" +#include "igraph_datatype.h" +#include "igraph_iterators.h" +#include "igraph_attributes.h" +#include "igraph_sparsemat.h" __BEGIN_DECLS @@ -40,76 +40,70 @@ __BEGIN_DECLS /* Basic query functions */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es); -IGRAPH_EXPORT igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid); -IGRAPH_EXPORT igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es); +IGRAPH_EXPORT int igraph_density(const igraph_t *graph, igraph_real_t *res, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, +IGRAPH_EXPORT int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, const igraph_vs_t vs); -IGRAPH_EXPORT igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, - igraph_vector_int_t *circle); -IGRAPH_EXPORT igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, +IGRAPH_EXPORT int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, + igraph_vector_t *circle); +IGRAPH_EXPORT int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); -IGRAPH_EXPORT igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, +IGRAPH_EXPORT int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); -IGRAPH_EXPORT igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, - igraph_vector_int_t *roots, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); +IGRAPH_EXPORT int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, igraph_bool_t ignore_loops, igraph_reciprocity_t mode); -IGRAPH_EXPORT igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, - igraph_vector_int_t *outvids, +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect); /* -------------------------------------------------- */ /* Structural properties */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, +IGRAPH_EXPORT int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, +IGRAPH_EXPORT int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst); -IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, +IGRAPH_EXPORT int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, +IGRAPH_EXPORT int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t vid); -IGRAPH_EXPORT igraph_error_t igraph_subcomponent(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vid, +IGRAPH_EXPORT int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, - igraph_neimode_t mode, const igraph_vector_int_t *roots, - igraph_vector_int_t *vertex_index); +IGRAPH_EXPORT 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); -IGRAPH_EXPORT igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, - igraph_vector_int_t *alpha, - igraph_vector_int_t *alpham1); -IGRAPH_EXPORT igraph_error_t igraph_is_chordal(const igraph_t *graph, - const igraph_vector_int_t *alpha, - const igraph_vector_int_t *alpham1, +IGRAPH_EXPORT int igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_t *alpha, + igraph_vector_t *alpham1); +IGRAPH_EXPORT int igraph_is_chordal(const igraph_t *graph, + const igraph_vector_t *alpha, + const igraph_vector_t *alpham1, igraph_bool_t *chordal, - igraph_vector_int_t *fill_in, + igraph_vector_t *fill_in, igraph_t *newgraph); -IGRAPH_EXPORT igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, +IGRAPH_EXPORT int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_neimode_t mode, igraph_neimode_t neighbor_degree_mode, @@ -117,49 +111,17 @@ IGRAPH_EXPORT igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t * igraph_vector_t *knnk, const igraph_vector_t *weights); -IGRAPH_EXPORT igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_t *result, +IGRAPH_EXPORT int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, const igraph_vector_t *weights, igraph_fas_algorithm_t algo); /* -------------------------------------------------- */ /* Spectral Properties */ /* -------------------------------------------------- */ -/** - * \typedef igraph_laplacian_normalization_t - * \brief Normalization methods for a Laplacian matrix. - * - * Normalization methods for \ref igraph_get_laplacian() and - * \ref igraph_get_laplacian_sparse(). In the following, \c A refers to the - * (possibly weighted) adjacency matrix and \c D is a diagonal matrix containing - * degrees (unweighted case) or strengths (weighted case). Out-, in- or total degrees - * are used according to the \p mode parameter. - * - * \enumval IGRAPH_LAPLACIAN_UNNORMALIZED Unnormalized Laplacian, L = D - A. - * \enumval IGRAPH_LAPLACIAN_SYMMETRIC Symmetric normalized Laplacian, L = I - D^(-1/2) A D^(-1/2). - * \enumval IGRAPH_LAPLACIAN_LEFT Left-stochastic normalized Laplacian, L = I - D^-1 A. - * \enumval IGRAPH_LAPLACIAN_RIGHT Right-stochastic normalized Laplacian, L = I - A D^-1. - */ -typedef enum { - IGRAPH_LAPLACIAN_UNNORMALIZED = 0, - IGRAPH_LAPLACIAN_SYMMETRIC = 1, - IGRAPH_LAPLACIAN_LEFT = 2, - IGRAPH_LAPLACIAN_RIGHT = 3 -} igraph_laplacian_normalization_t; - -IGRAPH_EXPORT igraph_error_t igraph_get_laplacian( - const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - igraph_laplacian_normalization_t normalization, - const igraph_vector_t *weights -); -IGRAPH_EXPORT igraph_error_t igraph_get_laplacian_sparse( - const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, - igraph_laplacian_normalization_t normalization, - const igraph_vector_t *weights -); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_laplacian( - const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, - igraph_bool_t normalized, const igraph_vector_t *weights -); +IGRAPH_EXPORT int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, + igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, + const igraph_vector_t *weights); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_strvector.h b/src/vendor/cigraph/include/igraph_strvector.h index f434ef894e5..525fef8a837 100644 --- a/src/vendor/cigraph/include/igraph_strvector.h +++ b/src/vendor/cigraph/include/igraph_strvector.h @@ -25,7 +25,6 @@ #define IGRAPH_STRVECTOR_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -36,75 +35,62 @@ __BEGIN_DECLS */ typedef struct s_igraph_strvector { - char **stor_begin; - char **stor_end; - char **end; + char **data; + long int len; } igraph_strvector_t; /** * \define STR - * \brief Indexing string vectors. - * - * This is a macro that allows to query the elements of a string vector, just - * like \ref igraph_strvector_get(), but without the overhead of a function - * call. Note this macro cannot be used to set an element. Use - * \ref igraph_strvector_set() to set an element instead. + * Indexing string vectors * + * This is a macro which allows to query the elements of a string vector in + * simpler way than \ref igraph_strvector_get(). Note this macro cannot be + * used to set an element, for that use \ref igraph_strvector_set(). * \param sv The string vector * \param i The the index of the element. * \return The element at position \p i. * * Time complexity: O(1). */ -#define STR(sv,i) ((const char *)((sv).stor_begin[(i)])) +#define STR(sv,i) ((const char *)((sv).data[(i)])) -#define IGRAPH_STRVECTOR_NULL { 0,0,0 } -#define IGRAPH_STRVECTOR_INIT_FINALLY(sv, size) \ - do { IGRAPH_CHECK(igraph_strvector_init(sv, size)); \ - IGRAPH_FINALLY( igraph_strvector_destroy, sv); } while (0) +#define IGRAPH_STRVECTOR_NULL { 0,0 } +#define IGRAPH_STRVECTOR_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_strvector_init(v, size)); \ + IGRAPH_FINALLY( igraph_strvector_destroy, v); } while (0) -IGRAPH_EXPORT igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t len); +IGRAPH_EXPORT int igraph_strvector_init(igraph_strvector_t *sv, long int len); IGRAPH_EXPORT void igraph_strvector_destroy(igraph_strvector_t *sv); -IGRAPH_EXPORT igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv); -IGRAPH_EXPORT igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv); -IGRAPH_EXPORT const char* igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx); -IGRAPH_EXPORT igraph_error_t igraph_strvector_set( - igraph_strvector_t *sv, igraph_integer_t idx, const char *value); -IGRAPH_EXPORT igraph_error_t igraph_strvector_set_len( - igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len); +IGRAPH_EXPORT long int igraph_strvector_size(const igraph_strvector_t *sv); +IGRAPH_EXPORT void igraph_strvector_get(const igraph_strvector_t *sv, + long int idx, char **value); +IGRAPH_EXPORT int igraph_strvector_set(igraph_strvector_t *sv, long int idx, + const char *value); +IGRAPH_EXPORT int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, + const char *value, int len); IGRAPH_EXPORT void igraph_strvector_clear(igraph_strvector_t *sv); -IGRAPH_EXPORT void igraph_strvector_remove_section( - igraph_strvector_t *v, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT void igraph_strvector_remove( - igraph_strvector_t *v, igraph_integer_t elem); -IGRAPH_EXPORT igraph_error_t igraph_strvector_init_copy( - igraph_strvector_t *to, const igraph_strvector_t *from); -IGRAPH_EXPORT igraph_error_t igraph_strvector_append( - igraph_strvector_t *to, const igraph_strvector_t *from); -IGRAPH_EXPORT igraph_error_t igraph_strvector_merge( - igraph_strvector_t *to, igraph_strvector_t *from); -IGRAPH_EXPORT igraph_error_t igraph_strvector_resize( - igraph_strvector_t* v, igraph_integer_t newsize); -IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back(igraph_strvector_t *v, - const char *value); -IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back_len(igraph_strvector_t *v, - const char *value, igraph_integer_t len); -IGRAPH_EXPORT igraph_error_t igraph_strvector_print(const igraph_strvector_t *v, FILE *file, +IGRAPH_EXPORT void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, + long int to); +IGRAPH_EXPORT void igraph_strvector_remove(igraph_strvector_t *v, long int elem); +IGRAPH_EXPORT void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, + long int end, long int to); +IGRAPH_EXPORT int igraph_strvector_copy(igraph_strvector_t *to, + const igraph_strvector_t *from); +IGRAPH_EXPORT int igraph_strvector_append(igraph_strvector_t *to, + const igraph_strvector_t *from); +IGRAPH_EXPORT int igraph_strvector_resize(igraph_strvector_t* v, long int newsize); +IGRAPH_EXPORT int igraph_strvector_add(igraph_strvector_t *v, const char *value); +IGRAPH_EXPORT void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, + long int nremove); +IGRAPH_EXPORT void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, + long int nremove); +IGRAPH_EXPORT int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, const char *sep); -IGRAPH_EXPORT igraph_error_t igraph_strvector_index(const igraph_strvector_t *v, +IGRAPH_EXPORT int igraph_strvector_index(const igraph_strvector_t *v, igraph_strvector_t *newv, - const igraph_vector_int_t *idx); - -IGRAPH_EXPORT igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, - igraph_integer_t capacity); + const igraph_vector_t *idx); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_add(igraph_strvector_t *v, const char *value); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_copy( - igraph_strvector_t *to, const igraph_strvector_t *from); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_set2( - igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len -); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_topology.h b/src/vendor/cigraph/include/igraph_topology.h index 3afd1e4f5cf..90e656b2d5d 100644 --- a/src/vendor/cigraph/include/igraph_topology.h +++ b/src/vendor/cigraph/include/igraph_topology.h @@ -27,9 +27,8 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -37,10 +36,10 @@ __BEGIN_DECLS /* Directed acyclic graphs */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_topological_sorting( - const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, +IGRAPH_EXPORT int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res, + igraph_neimode_t mode); +IGRAPH_EXPORT int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT int igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure); /* -------------------------------------------------- */ @@ -48,22 +47,22 @@ IGRAPH_EXPORT igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph /* -------------------------------------------------- */ /* Common functions */ -IGRAPH_EXPORT igraph_error_t igraph_simplify_and_colorize( +IGRAPH_EXPORT int igraph_simplify_and_colorize( const igraph_t *graph, igraph_t *res, igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color); /* Generic interface */ -IGRAPH_EXPORT igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); -IGRAPH_EXPORT igraph_error_t igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); /* LAD */ -IGRAPH_EXPORT igraph_error_t igraph_subisomorphic_lad( - const igraph_t *pattern, const igraph_t *target, const igraph_vector_int_list_t *domains, - igraph_bool_t *iso, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, - igraph_bool_t induced, igraph_integer_t time_limit -); +IGRAPH_EXPORT 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); /* VF2 family*/ /** @@ -71,21 +70,18 @@ IGRAPH_EXPORT igraph_error_t igraph_subisomorphic_lad( * Callback type, called when an isomorphism was found * * See the details at the documentation of \ref - * igraph_get_isomorphisms_vf2_callback(). + * igraph_isomorphic_function_vf2(). * \param map12 The mapping from the first graph to the second. * \param map21 The mapping from the second graph to the first, the * inverse of \p map12 basically. * \param arg This extra argument was passed to \ref - * igraph_get_isomorphisms_vf2_callback() when it was called. - * \return \c IGRAPH_SUCCESS to continue the search, \c IGRAPH_STOP to - * terminate the search. Any other return value is interpreted as an - * igraph error code, which will then abort the search and return the - * same error code from the caller function. + * igraph_isomorphic_function_vf2() when it was called. + * \return Boolean, whether to continue with the isomorphism search. */ -typedef igraph_error_t igraph_isohandler_t(const igraph_vector_int_t *map12, - const igraph_vector_int_t *map21, void *arg); +typedef igraph_bool_t igraph_isohandler_t(const igraph_vector_t *map12, + const igraph_vector_t *map21, void *arg); /** * \typedef igraph_isocompat_t @@ -117,18 +113,28 @@ typedef igraph_bool_t igraph_isocompat_t(const igraph_t *graph1, const igraph_integer_t g2_num, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT 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_int_t *map12, - igraph_vector_int_t *map21, + igraph_vector_t *map12, + igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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, @@ -137,47 +143,41 @@ IGRAPH_EXPORT igraph_error_t igraph_count_isomorphisms_vf2(const igraph_t *graph igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_get_isomorphisms_vf2(const igraph_t *graph1, +IGRAPH_EXPORT 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_int_list_t *maps, + igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_get_isomorphisms_vf2_callback( - 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -); - -/* Deprecated alias to igraph_get_isomorphisms_vf2_callback(), will be removed in 0.11 */ -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -); - -IGRAPH_EXPORT igraph_error_t igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, + +IGRAPH_EXPORT 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_int_t *map12, - igraph_vector_int_t *map21, + igraph_vector_t *map12, + igraph_vector_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT 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); +IGRAPH_EXPORT 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, @@ -186,44 +186,25 @@ IGRAPH_EXPORT igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *gr igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_get_subisomorphisms_vf2(const igraph_t *graph1, +IGRAPH_EXPORT 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_int_list_t *maps, + igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT igraph_error_t igraph_get_subisomorphisms_vf2_callback( - 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -); - -/* Deprecated alias to igraph_get_subisomorphisms_vf2_callback(), will be removed in 0.11 */ -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -); /* BLISS family */ /** * \struct igraph_bliss_info_t - * \brief Information about a Bliss run. + * Information about a BLISS run * - * Some secondary information found by the Bliss algorithm is stored + * Some secondary information found by the BLISS algorithm is stored * here. It is useful if you wany to study the internal working of the * algorithm. - * * \member nof_nodes The number of nodes in the search tree. * \member nof_leaf_nodes The number of leaf nodes in the search tree. * \member nof_bad_nodes Number of bad nodes. @@ -234,7 +215,7 @@ IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_subisomorphic_function_vf2 * given as a string. It should be deallocated via * \ref igraph_free() if not needed any more. * - * See https://users.aalto.fi/~tjunttil/bliss/ + * See http://www.tcs.hut.fi/Software/bliss/index.html * for details about the algorithm and these parameters. */ typedef struct igraph_bliss_info_t { @@ -272,43 +253,30 @@ typedef enum { IGRAPH_BLISS_F = 0, IGRAPH_BLISS_FL, IGRAPH_BLISS_FLM, IGRAPH_BLISS_FSM } igraph_bliss_sh_t; -IGRAPH_EXPORT igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_t *labeling, +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT 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_int_t *map12, - igraph_vector_int_t *map21, + 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); -IGRAPH_EXPORT igraph_error_t igraph_count_automorphisms( - const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info); +IGRAPH_EXPORT int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_automorphisms( - const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info); +IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_automorphism_group( - const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh, - igraph_bliss_info_t *info -); - -/* Functions for small graphs (<= 4 vertices for directed graphs, <= 6 for undirected graphs) */ -IGRAPH_EXPORT igraph_error_t igraph_isomorphic_small(const igraph_t *graph1, const igraph_t *graph2, +/* Functions for 3-4 graphs */ +IGRAPH_EXPORT int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); -IGRAPH_EXPORT igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); -IGRAPH_EXPORT igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, +IGRAPH_EXPORT int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); +IGRAPH_EXPORT int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, igraph_integer_t *isoclass); -IGRAPH_EXPORT igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, +IGRAPH_EXPORT int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed); -IGRAPH_EXPORT igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count); - -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_isomorphic_34( - const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso -); diff --git a/src/vendor/cigraph/include/igraph_transitivity.h b/src/vendor/cigraph/include/igraph_transitivity.h index 9566561f6a2..33aee5410ac 100644 --- a/src/vendor/cigraph/include/igraph_transitivity.h +++ b/src/vendor/cigraph/include/igraph_transitivity.h @@ -27,32 +27,36 @@ #include "igraph_decls.h" #include "igraph_datatype.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS -IGRAPH_EXPORT igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, +IGRAPH_EXPORT int igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, +IGRAPH_EXPORT int igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, +IGRAPH_EXPORT int igraph_transitivity_local_undirected1(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected2(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vs_t vids, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_local_undirected4(const igraph_t *graph, + igraph_vector_t *res, + igraph_transitivity_mode_t mode); +IGRAPH_EXPORT int igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_transitivity_barrat(const igraph_t *graph, +IGRAPH_EXPORT int igraph_transitivity_barrat(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, const igraph_vector_t *weights, const igraph_transitivity_mode_t mode); -IGRAPH_EXPORT igraph_error_t igraph_ecc(const igraph_t *graph, - igraph_vector_t *res, - igraph_es_t eids, - igraph_integer_t k, - igraph_bool_t offset, - igraph_bool_t normalize); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_typed_list_pmt.h b/src/vendor/cigraph/include/igraph_typed_list_pmt.h deleted file mode 100644 index 20ebba392ee..00000000000 --- a/src/vendor/cigraph/include/igraph_typed_list_pmt.h +++ /dev/null @@ -1,119 +0,0 @@ -/* -*- 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 - -*/ - -#if defined(VECTOR_LIST) - /* It was indicated that every item in a list is a vector of the base type - * so let's define ITEM_TYPE appropriately */ - #define ITEM_TYPE BASE_VECTOR -#elif defined(MATRIX_LIST) - /* It was indicated that every item in a list is a matrix of the base type - * so let's define ITEM_TYPE appropriately */ - #define ITEM_TYPE BASE_MATRIX -#else - #define ITEM_TYPE BASE -#endif - -/** - * Vector list, dealing with lists of typed vectors efficiently. - * \ingroup types - */ - -typedef struct { - ITEM_TYPE* stor_begin; - ITEM_TYPE* stor_end; - ITEM_TYPE* end; -#ifdef EXTRA_TYPE_FIELDS - EXTRA_TYPE_FIELDS -#endif -} TYPE; - -/*--------------------*/ -/* Allocation */ -/*--------------------*/ - -IGRAPH_EXPORT igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size); -IGRAPH_EXPORT void FUNCTION(destroy)(TYPE* v); - -/*--------------------*/ -/* Accessing elements */ -/*--------------------*/ - -IGRAPH_EXPORT ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos); -IGRAPH_EXPORT void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); -IGRAPH_EXPORT ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v); - -/*-----------------*/ -/* List properties */ -/*-----------------*/ - -IGRAPH_EXPORT igraph_integer_t FUNCTION(capacity)(const TYPE* v); -IGRAPH_EXPORT igraph_bool_t FUNCTION(empty)(const TYPE* v); -IGRAPH_EXPORT igraph_integer_t FUNCTION(size)(const TYPE* v); - -/*------------------------*/ -/* Resizing operations */ -/*------------------------*/ - -IGRAPH_EXPORT void FUNCTION(clear)(TYPE* v); -IGRAPH_EXPORT igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity); -IGRAPH_EXPORT igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size); - -/*------------------------*/ -/* Adding/removing items */ -/*------------------------*/ - -IGRAPH_EXPORT void FUNCTION(discard)(TYPE* v, igraph_integer_t index); -IGRAPH_EXPORT void FUNCTION(discard_back)(TYPE* v); -IGRAPH_EXPORT void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index); -IGRAPH_EXPORT igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); -IGRAPH_EXPORT igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e); -IGRAPH_EXPORT igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** result); -IGRAPH_EXPORT igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e); -IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e); -IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** result); -IGRAPH_EXPORT ITEM_TYPE FUNCTION(pop_back)(TYPE* v); -IGRAPH_EXPORT igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); -IGRAPH_EXPORT igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); -IGRAPH_EXPORT void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); -IGRAPH_EXPORT void FUNCTION(remove_consecutive_duplicates)(TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*)); - -/*------------------*/ -/* Exchanging items */ -/*------------------*/ - -IGRAPH_EXPORT igraph_error_t FUNCTION(permute)(TYPE *v, const igraph_vector_int_t *index); -IGRAPH_EXPORT igraph_error_t FUNCTION(reverse)(TYPE *v); -IGRAPH_EXPORT igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2); -IGRAPH_EXPORT igraph_error_t FUNCTION(swap_elements)(TYPE* v, igraph_integer_t i, igraph_integer_t j); - -/*-----------*/ -/* Sorting */ -/*-----------*/ - -IGRAPH_EXPORT void FUNCTION(sort)( - TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)); -IGRAPH_EXPORT igraph_error_t FUNCTION(sort_ind)( - TYPE *v, igraph_vector_int_t *ind, - int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) -); - -#undef ITEM_TYPE diff --git a/src/vendor/cigraph/include/igraph_types.h b/src/vendor/cigraph/include/igraph_types.h index 18eb9b69d37..07e4befaa67 100644 --- a/src/vendor/cigraph/include/igraph_types.h +++ b/src/vendor/cigraph/include/igraph_types.h @@ -32,125 +32,43 @@ __BEGIN_DECLS #define _GNU_SOURCE 1 #endif -#ifdef __cplusplus - #define __STDC_FORMAT_MACROS /* needed for PRId32 and PRId64 from inttypes.h on Linux */ -#endif - -#include "igraph_config.h" - -#include -#include -#include +#include "igraph_error.h" #include +#include #include - -#if !defined(IGRAPH_INTEGER_SIZE) -# error "igraph integer size not defined; check the value of IGRAPH_INTEGER_SIZE when compiling" -#elif IGRAPH_INTEGER_SIZE == 64 -typedef int64_t igraph_integer_t; -typedef uint64_t igraph_uint_t; -#elif IGRAPH_INTEGER_SIZE == 32 -typedef int32_t igraph_integer_t; -typedef uint32_t igraph_uint_t; -#else -# error "Invalid igraph integer size; check the value of IGRAPH_INTEGER_SIZE when compiling" -#endif - +typedef int igraph_integer_t; typedef double igraph_real_t; - -/* IGRAPH_BOOL_TYPE is set to 'bool' by default, and it is not meant to be - * overridden, except for the R interface where we know what we are doing. - * See igraph_config.h for more info */ -typedef IGRAPH_BOOL_TYPE igraph_bool_t; +typedef int igraph_bool_t; /* printf format specifier for igraph_integer_t */ -#if IGRAPH_INTEGER_SIZE == 64 -# define IGRAPH_PRId PRId64 -# define IGRAPH_PRIu PRIu64 -#else -# define IGRAPH_PRId PRId32 -# define IGRAPH_PRIu PRIu32 -#endif - -/* maximum and minimum allowed values for igraph_integer_t */ -#if IGRAPH_INTEGER_SIZE == 64 -# define IGRAPH_INTEGER_MAX INT64_MAX -# define IGRAPH_INTEGER_MIN INT64_MIN -#else -# define IGRAPH_INTEGER_MAX INT32_MAX -# define IGRAPH_INTEGER_MIN INT32_MIN -#endif - -/* maximum and minimum allowed values for igraph_uint_t */ -#if IGRAPH_INTEGER_SIZE == 64 -# define IGRAPH_UINT_MAX UINT64_MAX -# define IGRAPH_UINT_MIN UINT64_MIN -#else -# define IGRAPH_UINT_MAX UINT32_MAX -# define IGRAPH_UINT_MIN UINT32_MIN -#endif - - -/** - * \define IGRAPH_VCOUNT_MAX - * \brief The maximum number of vertices supported in igraph graphs. - * - * The value of this constant is one less than \c IGRAPH_INTEGER_MAX . - * When igraph is compiled in 32-bit mode, this means that you are limited - * to 231 – 2 (about 2.1 billion) vertices. In - * 64-bit mode, the limit is 263 – 2 so you are much - * more likely to hit out-of-memory issues due to other reasons before reaching - * this limit. - */ -#define IGRAPH_VCOUNT_MAX (IGRAPH_INTEGER_MAX-1) -/* The 'os' and 'is' vectors in igraph_t have vcount+1 elements, - * thus this cannot currently be larger than IGRAPH_INTEGER_MAX-1 - */ - -/** - * \define IGRAPH_ECOUNT_MAX - * \brief The maximum number of edges supported in igraph graphs. - * - * The value of this constant is half of \c IGRAPH_INTEGER_MAX . - * When igraph is compiled in 32-bit mode, this means that you are limited - * to approximately 230 (about 1.07 billion) - * vertices. In 64-bit mode, the limit is approximately - * 262 so you are much more likely to hit - * out-of-memory issues due to other reasons before reaching this limit. - */ -#define IGRAPH_ECOUNT_MAX (IGRAPH_INTEGER_MAX/2) -/* The endpoints of edges are often stored in a vector twice the length - * of the edge count, thus this cannot be larger than IGRAPH_INTEGER_MAX/2. - * Some of the overflow checking code relies on this. */ +#define IGRAPH_PRId "d" /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) */ IGRAPH_EXPORT int igraph_real_printf(igraph_real_t val); IGRAPH_EXPORT int igraph_real_fprintf(FILE *file, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_printf_aligned(int width, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_snprintf(char *str, size_t size, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_snprintf(char* str, size_t size, igraph_real_t val); /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) with the largest possible precision */ IGRAPH_EXPORT int igraph_real_printf_precise(igraph_real_t val); IGRAPH_EXPORT int igraph_real_fprintf_precise(FILE *file, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val); -#define IGRAPH_INFINITY ((double)INFINITY) -#define IGRAPH_POSINFINITY IGRAPH_INFINITY -#define IGRAPH_NEGINFINITY (-IGRAPH_INFINITY) +#define IGRAPH_INFINITY INFINITY +#define IGRAPH_POSINFINITY INFINITY +#define IGRAPH_NEGINFINITY (-INFINITY) -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_finite(double x); +IGRAPH_EXPORT int igraph_finite(double x); #define IGRAPH_FINITE(x) igraph_finite(x) -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_nan(double x); -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_inf(double x); -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_posinf(double x); -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_neginf(double x); +IGRAPH_EXPORT int igraph_is_nan(double x); +IGRAPH_EXPORT int igraph_is_inf(double x); +IGRAPH_EXPORT int igraph_is_posinf(double x); +IGRAPH_EXPORT int igraph_is_neginf(double x); -#define IGRAPH_NAN ((double)NAN) +#define IGRAPH_NAN NAN __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_vector.h b/src/vendor/cigraph/include/igraph_vector.h index 567c835b882..80773289643 100644 --- a/src/vendor/cigraph/include/igraph_vector.h +++ b/src/vendor/cigraph/include/igraph_vector.h @@ -24,11 +24,9 @@ #ifndef IGRAPH_VECTOR_H #define IGRAPH_VECTOR_H -#include "igraph_complex.h" -#include "igraph_constants.h" #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" +#include "igraph_complex.h" __BEGIN_DECLS @@ -42,6 +40,18 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL +#define BASE_FLOAT +#include "igraph_pmt.h" +#include "igraph_vector_type.h" +#include "igraph_pmt_off.h" +#undef BASE_FLOAT + +#define BASE_LONG +#include "igraph_pmt.h" +#include "igraph_vector_type.h" +#include "igraph_pmt_off.h" +#undef BASE_LONG + #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_type.h" @@ -72,6 +82,18 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL +#define BASE_FLOAT +#include "igraph_pmt.h" +#include "igraph_vector_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_FLOAT + +#define BASE_LONG +#include "igraph_pmt.h" +#include "igraph_vector_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_LONG + #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_pmt.h" @@ -124,41 +146,38 @@ __BEGIN_DECLS do { IGRAPH_CHECK(igraph_vector_int_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_int_destroy, v); } while (0) #endif +#ifndef IGRAPH_VECTOR_LONG_INIT_FINALLY +#define IGRAPH_VECTOR_LONG_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_long_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_long_destroy, v); } while (0) +#endif /* -------------------------------------------------- */ /* Type-specific vector functions */ /* -------------------------------------------------- */ -IGRAPH_EXPORT igraph_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to); -IGRAPH_EXPORT igraph_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to); - -IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, - const igraph_vector_t *rhs, - igraph_real_t tol); - -IGRAPH_EXPORT igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, - const igraph_vector_t *rhs, - igraph_real_t eps); - -IGRAPH_EXPORT igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) ; - -IGRAPH_EXPORT igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, +IGRAPH_EXPORT int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to); +IGRAPH_EXPORT int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to); + +IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t tol); + +IGRAPH_EXPORT int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); + +IGRAPH_EXPORT int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2, + igraph_vector_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order1(const igraph_vector_t* v, + igraph_vector_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order1_int(const igraph_vector_t* v, + igraph_vector_int_t* res, igraph_real_t maxval); +IGRAPH_EXPORT int igraph_vector_order2(igraph_vector_t *v); +IGRAPH_EXPORT int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, + long int nodes); +IGRAPH_EXPORT int igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan); IGRAPH_EXPORT igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_order2(igraph_vector_t *v); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_rank(const igraph_vector_t *v, igraph_vector_int_t *res, - igraph_integer_t nodes); - -IGRAPH_EXPORT igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, const igraph_vector_int_t *v2, - igraph_vector_int_t* res, igraph_integer_t maxval); - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, - igraph_vector_int_t* res, igraph_integer_t maxval); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_rank(const igraph_vector_int_t *v, igraph_vector_int_t *res, - igraph_integer_t nodes); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_vector_list.h b/src/vendor/cigraph/include/igraph_vector_list.h deleted file mode 100644 index f1d299dc948..00000000000 --- a/src/vendor/cigraph/include/igraph_vector_list.h +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- 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_VECTOR_LIST_H -#define IGRAPH_VECTOR_LIST_H - -#include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Flexible list of vectors */ -/* -------------------------------------------------- */ - -/* Indicate to igraph_typed_list_pmt.h that we are going to work with _vectors_ - * of the base type, not the base type directly */ -#define VECTOR_LIST - -#define BASE_IGRAPH_REAL -#include "igraph_pmt.h" -#include "igraph_typed_list_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_IGRAPH_REAL - -#define BASE_INT -#include "igraph_pmt.h" -#include "igraph_typed_list_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_INT - -#undef VECTOR_LIST - -/* -------------------------------------------------- */ -/* Helper macros */ -/* -------------------------------------------------- */ - -#define IGRAPH_VECTOR_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_vector_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_list_destroy, v); } while (0) -#define IGRAPH_VECTOR_BOOL_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_vector_bool_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_bool_list_destroy, v); } while (0) -#define IGRAPH_VECTOR_CHAR_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_vector_char_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_char_list_destroy, v); } while (0) -#define IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_vector_int_list_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_int_list_destroy, v); } while (0) - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_vector_pmt.h b/src/vendor/cigraph/include/igraph_vector_pmt.h index 04e0707dc7e..1962d29256f 100644 --- a/src/vendor/cigraph/include/igraph_vector_pmt.h +++ b/src/vendor/cigraph/include/igraph_vector_pmt.h @@ -25,23 +25,19 @@ /* Allocation */ /*--------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init)( - TYPE(igraph_vector)* v, igraph_integer_t size); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_array)( - TYPE(igraph_vector)* v, const BASE* data, igraph_integer_t length); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_copy)( - TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector)* v, + const BASE* data, long int length); #ifndef NOTORDERED -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector)*v, BASE start, BASE end); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); #endif -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, copy)( - TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); +IGRAPH_EXPORT int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from); IGRAPH_EXPORT void FUNCTION(igraph_vector, destroy)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v); /*--------------------*/ /* Accessing elements */ @@ -69,11 +65,9 @@ IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igra #define VECTOR(v) ((v).stor_begin) #endif -IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos); -IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); -IGRAPH_EXPORT BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos); -IGRAPH_EXPORT BASE* FUNCTION(igraph_vector, get_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); -IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, BASE value); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, long int pos); +IGRAPH_EXPORT BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, long int pos); +IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, long int pos, BASE value); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v); /*-----------------------*/ @@ -83,39 +77,33 @@ IGRAPH_EXPORT BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT void FUNCTION(igraph_vector, null)(TYPE(igraph_vector)* v); IGRAPH_EXPORT void FUNCTION(igraph_vector, fill)(TYPE(igraph_vector)* v, BASE e); -#ifndef NOTORDERED -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector)*v, BASE start, BASE end); -#endif - /*-----------------------*/ /* Vector views */ /*-----------------------*/ IGRAPH_EXPORT const TYPE(igraph_vector) *FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v, const BASE *data, - igraph_integer_t length); + long int length); /*-----------------------*/ /* Copying vectors */ /*-----------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE* to); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); +IGRAPH_EXPORT int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); /*-----------------------*/ /* Exchanging elements */ /*-----------------------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap_elements)( - TYPE(igraph_vector) *v, igraph_integer_t i, igraph_integer_t j); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector) *v, - const igraph_vector_int_t *ind); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, + long int i, long int j); +IGRAPH_EXPORT int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); /*-----------------------*/ /* Vector operations */ @@ -123,19 +111,19 @@ IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector IGRAPH_EXPORT void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus); IGRAPH_EXPORT void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); #ifndef NOABS - IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); + IGRAPH_EXPORT int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); #endif /*------------------------------*/ @@ -153,12 +141,10 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_ve const TYPE(igraph_vector) *rhs); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp)( - const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)( - const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, + const void *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, + const void *rhs); #endif /*------------------------------*/ @@ -168,20 +154,20 @@ IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, co #ifndef NOTORDERED IGRAPH_EXPORT BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT void FUNCTION(igraph_vector, minmax)( - const TYPE(igraph_vector) *v, BASE *min, BASE *max); -IGRAPH_EXPORT void FUNCTION(igraph_vector, which_minmax)( - const TYPE(igraph_vector) *v, igraph_integer_t *which_min, igraph_integer_t *which_max); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, + BASE *min, BASE *max); +IGRAPH_EXPORT int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, + long int *which_min, long int *which_max); #endif /*-------------------*/ /* Vector properties */ /*-------------------*/ -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, empty)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, size)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v); @@ -204,16 +190,17 @@ IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(ig /*------------------------*/ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, BASE e); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, search)( - const TYPE(igraph_vector) *v, igraph_integer_t from, BASE what, igraph_integer_t *pos); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, + long int from, BASE what, + long int *pos); #ifndef NOTORDERED -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)( - const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos, - igraph_integer_t start, igraph_integer_t end); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)( - const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)( - const TYPE(igraph_vector) *v, BASE what); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) *v, + BASE what, long int *pos, + long int start, long int end); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, + BASE what, long int *pos); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, + BASE what); #endif /*------------------------*/ @@ -221,21 +208,15 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)( /*------------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, resize)( - TYPE(igraph_vector)* v, igraph_integer_t new_size); -IGRAPH_EXPORT void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reserve)( - TYPE(igraph_vector)* v, igraph_integer_t capacity); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e); +IGRAPH_EXPORT int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize); +IGRAPH_EXPORT int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, long int size); +IGRAPH_EXPORT int FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, insert)( - TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value); -IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)( - TYPE(igraph_vector) *v, igraph_integer_t elem); -IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_fast)( - TYPE(igraph_vector) *v, igraph_integer_t elem); -IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)( - TYPE(igraph_vector) *v, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, BASE value); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, + long int from, long int to); /*-----------*/ /* Sorting */ @@ -245,8 +226,8 @@ IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)( IGRAPH_EXPORT void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v); IGRAPH_EXPORT void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, qsort_ind)( - const TYPE(igraph_vector) *v, igraph_vector_int_t *inds, igraph_order_t order); +IGRAPH_EXPORT long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, + igraph_vector_t *inds, igraph_bool_t descending); #endif @@ -254,65 +235,56 @@ IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, qsort_ind)( /* Printing */ /*-----------*/ -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); +IGRAPH_EXPORT int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, + const char *format); +IGRAPH_EXPORT int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); -#ifdef OUT_FORMAT -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format); -#endif /* OUT_FORMAT */ +#ifdef BASE_COMPLEX -/*----------------------------------------*/ -/* Operations specific to complex vectors */ -/*----------------------------------------*/ +IGRAPH_EXPORT int igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real); +IGRAPH_EXPORT int igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag); +IGRAPH_EXPORT int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta); -#ifdef BASE_COMPLEX +#endif -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, - igraph_vector_t *real); -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, - igraph_vector_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, - igraph_vector_t *real, - igraph_vector_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, - const igraph_vector_t *real, - const igraph_vector_t *imag); -IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, - const igraph_vector_t *r, - const igraph_vector_t *theta); - -IGRAPH_EXPORT igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, - const igraph_vector_complex_t *rhs, - igraph_real_t eps); - -#endif /* BASE_COMPLEX */ - -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); - -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, move_interval)( - TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, - igraph_integer_t to); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, move_interval2)( - TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, - igraph_integer_t to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); +IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); + +IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, + long int begin, long int end, long int to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, + long int begin, long int end, long int to); +IGRAPH_EXPORT void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v, + const igraph_vector_t *index, + long int nremove); #ifndef NOTORDERED -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); +IGRAPH_EXPORT int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); #endif -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, get_interval)( - const TYPE(igraph_vector) *v, TYPE(igraph_vector) *res, - igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *res, + long int from, long int to); #ifndef NOTORDERED -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, +IGRAPH_EXPORT int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); #endif -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, +IGRAPH_EXPORT int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, - const igraph_vector_int_t *idx); + const igraph_vector_t *idx); -IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, +IGRAPH_EXPORT int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, const igraph_vector_int_t *idx); diff --git a/src/vendor/cigraph/include/igraph_vector_ptr.h b/src/vendor/cigraph/include/igraph_vector_ptr.h index ec498a4e5b0..c2667be29ee 100644 --- a/src/vendor/cigraph/include/igraph_vector_ptr.h +++ b/src/vendor/cigraph/include/igraph_vector_ptr.h @@ -25,7 +25,6 @@ #define IGRAPH_VECTOR_PTR_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -49,43 +48,39 @@ typedef struct s_vector_ptr { #define IGRAPH_VECTOR_PTR_NULL { 0,0,0,0 } #define IGRAPH_VECTOR_PTR_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_ptr_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0) + IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0) -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t* v, void *const *data, igraph_integer_t length); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); +IGRAPH_EXPORT int igraph_vector_ptr_init (igraph_vector_ptr_t* v, long int size); +IGRAPH_EXPORT int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length); IGRAPH_EXPORT const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, - void *const *data, igraph_integer_t length); -IGRAPH_EXPORT void igraph_vector_ptr_destroy(igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_free_all(igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_destroy_all(igraph_vector_ptr_t* v); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity); -IGRAPH_EXPORT igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v); -IGRAPH_EXPORT igraph_integer_t igraph_vector_ptr_size(const igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_clear(igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_null(igraph_vector_ptr_t* v); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, - const igraph_vector_ptr_t *from); -IGRAPH_EXPORT void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t *v, igraph_integer_t pos, void* e); -IGRAPH_EXPORT IGRAPH_DEPRECATED void* igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos); -IGRAPH_EXPORT void* igraph_vector_ptr_get(const igraph_vector_ptr_t* v, igraph_integer_t pos); -IGRAPH_EXPORT void igraph_vector_ptr_set(igraph_vector_ptr_t* v, igraph_integer_t pos, void* value); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize); + void *const *data, long int length); +IGRAPH_EXPORT void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v); +IGRAPH_EXPORT int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size); +IGRAPH_EXPORT igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v); +IGRAPH_EXPORT long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_clear (igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_null (igraph_vector_ptr_t* v); +IGRAPH_EXPORT int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e); +IGRAPH_EXPORT int igraph_vector_ptr_append (igraph_vector_ptr_t *to, + const igraph_vector_ptr_t *from); +IGRAPH_EXPORT void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v); +IGRAPH_EXPORT int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e); +IGRAPH_EXPORT void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos); +IGRAPH_EXPORT void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value); +IGRAPH_EXPORT int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize); IGRAPH_EXPORT void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index); -IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t pos); +IGRAPH_EXPORT int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); +IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos); IGRAPH_EXPORT void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); -IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_sort_ind( - igraph_vector_ptr_t *v, igraph_vector_int_t *inds, int(*compar)(const void*, const void*)); +IGRAPH_EXPORT int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, + const igraph_vector_int_t *idx); IGRAPH_EXPORT igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v); IGRAPH_EXPORT igraph_finally_func_t* igraph_vector_ptr_set_item_destructor(igraph_vector_ptr_t *v, igraph_finally_func_t *func); -IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); - /** * \define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR * \brief Sets the item destructor for this pointer vector (macro version). diff --git a/src/vendor/cigraph/include/igraph_version.h.in b/src/vendor/cigraph/include/igraph_version.h.in index fda42278d82..cae3cfa1148 100644 --- a/src/vendor/cigraph/include/igraph_version.h.in +++ b/src/vendor/cigraph/include/igraph_version.h.in @@ -34,10 +34,10 @@ __BEGIN_DECLS #define IGRAPH_VERSION_PATCH @PACKAGE_VERSION_PATCH@ #define IGRAPH_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" -IGRAPH_EXPORT void igraph_version(const char **version_string, - int *major, - int *minor, - int *subminor); +IGRAPH_EXPORT int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_visitor.h b/src/vendor/cigraph/include/igraph_visitor.h index c57cb2bed06..936ff654bf3 100644 --- a/src/vendor/cigraph/include/igraph_visitor.h +++ b/src/vendor/cigraph/include/igraph_visitor.h @@ -26,7 +26,6 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -38,13 +37,12 @@ __BEGIN_DECLS /** * \typedef igraph_bfshandler_t - * \brief Callback type for BFS function. + * Callback type for BFS function * * \ref igraph_bfs() is able to call a callback function, whenever a * new vertex is found, while doing the breadth-first search. This * callback function must be of type \c igraph_bfshandler_t. It has * the following arguments: - * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the breadth-first @@ -60,18 +58,16 @@ __BEGIN_DECLS * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_bfs(). - * \return \c IGRAPH_SUCCESS if the BFS should continue, \c IGRAPH_STOP - * if the BFS should stop and return to the caller normally. Any other - * value is treated as an igraph error code, terminating the search and - * returning to the caller with the same error code. If a BFS is - * is terminated prematurely, then all elements of the result vectors + * \return A logical value, if TRUE (=non-zero), that is interpreted + * as a request to stop the BFS and return to the caller. If a BFS + * is terminated like this, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain NaN. * * \sa \ref igraph_bfs() */ -typedef igraph_error_t igraph_bfshandler_t(const igraph_t *graph, +typedef igraph_bool_t igraph_bfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t pred, igraph_integer_t succ, @@ -79,29 +75,28 @@ typedef igraph_error_t igraph_bfshandler_t(const igraph_t *graph, igraph_integer_t dist, void *extra); -IGRAPH_EXPORT igraph_error_t igraph_bfs(const igraph_t *graph, - igraph_integer_t root, const igraph_vector_int_t *roots, +IGRAPH_EXPORT 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_int_t *restricted, - igraph_vector_int_t *order, igraph_vector_int_t *rank, - igraph_vector_int_t *parents, - igraph_vector_int_t *pred, igraph_vector_int_t *succ, - igraph_vector_int_t *dist, igraph_bfshandler_t *callback, + 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_EXPORT igraph_error_t igraph_bfs_simple(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, - igraph_vector_int_t *order, igraph_vector_int_t *layers, - igraph_vector_int_t *parents); +IGRAPH_EXPORT 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); /** * \function igraph_dfshandler_t - * \brief Callback type for the DFS function. + * Callback type for the DFS function * * \ref igraph_dfs() is able to call a callback function, whenever a * new vertex is discovered, and/or whenever a subtree is * completed. These callbacks must be of type \c * igraph_dfshandler_t. They have the following arguments: - * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the depth-first @@ -110,27 +105,25 @@ IGRAPH_EXPORT igraph_error_t igraph_bfs_simple(const igraph_t *graph, igraph_int * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_dfs(). - * \return \c IGRAPH_SUCCESS if the DFS should continue, \c IGRAPH_STOP - * if the DFS should stop and return to the caller normally. Any other - * value is treated as an igraph error code, terminating the search and - * returning to the caller with the same error code. If a BFS is - * is terminated prematurely, then all elements of the result vectors + * \return A logical value, if TRUE (=non-zero), that is interpreted + * as a request to stop the DFS and return to the caller. If a DFS + * is terminated like this, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain NaN. * * \sa \ref igraph_dfs() */ -typedef igraph_error_t igraph_dfshandler_t(const igraph_t *graph, +typedef igraph_bool_t igraph_dfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra); -IGRAPH_EXPORT igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, +IGRAPH_EXPORT int igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, igraph_bool_t unreachable, - igraph_vector_int_t *order, - igraph_vector_int_t *order_out, igraph_vector_int_t *parents, - igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, + 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); diff --git a/src/vendor/cigraph/interfaces/CMakeLists.txt b/src/vendor/cigraph/interfaces/CMakeLists.txt index 57a89d582f6..1cff0ec9616 100644 --- a/src/vendor/cigraph/interfaces/CMakeLists.txt +++ b/src/vendor/cigraph/interfaces/CMakeLists.txt @@ -21,7 +21,7 @@ if(STIMULUS_COMMAND) add_custom_target( check_stimulus - COMMAND test_stimulus + COMMAND test_stimulus DEPENDS test_stimulus COMMENT "Running C++ checker for Stimulus function and type specifications..." ) diff --git a/src/vendor/cigraph/interfaces/functions.yaml b/src/vendor/cigraph/interfaces/functions.yaml index 53af2c2e904..54623c61318 100644 --- a/src/vendor/cigraph/interfaces/functions.yaml +++ b/src/vendor/cigraph/interfaces/functions.yaml @@ -14,28 +14,17 @@ igraph_empty: PARAMS: OUT GRAPH graph, INTEGER n=0, BOOLEAN directed=True igraph_add_edges: - PARAMS: INOUT GRAPH graph, VERTEX_INDEX_PAIRS edges, ATTRIBUTES attr - DEPS: edges ON graph + PARAMS: INOUT GRAPH graph, VECTOR edges, ATTRIBUTES attr igraph_add_vertices: PARAMS: INOUT GRAPH graph, INTEGER nv, ATTRIBUTES attr -igraph_copy: - PARAMS: OUT GRAPH to, IN GRAPH from - igraph_delete_edges: - PARAMS: INOUT GRAPH graph, EDGE_SELECTOR edges + PARAMS: INOUT GRAPH graph, EDGESET edges DEPS: edges ON graph igraph_delete_vertices: - PARAMS: INOUT GRAPH graph, VERTEX_SELECTOR vertices - DEPS: vertices ON graph - -igraph_delete_vertices_idx: - PARAMS: |- - INOUT GRAPH graph, VERTEX_SELECTOR vertices, - OPTIONAL OUT VECTOR_INT idx, - OPTIONAL OUT VECTOR_INT invidx + PARAMS: INOUT GRAPH graph, VERTEXSET vertices DEPS: vertices ON graph igraph_vcount: @@ -47,7 +36,7 @@ igraph_ecount: RETURN: INTEGER igraph_neighbors: - PARAMS: GRAPH graph, OUT VERTEX_INDICES neis, VERTEX vid, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VECTOR neis, INTEGER vid, NEIMODE mode=ALL igraph_is_directed: PARAMS: GRAPH graph @@ -55,7 +44,7 @@ igraph_is_directed: igraph_degree: PARAMS: |- - GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, NEIMODE mode=ALL, BOOLEAN loops DEPS: vids ON graph @@ -63,30 +52,21 @@ igraph_edge: PARAMS: GRAPH graph, INTEGER eid, OUT INTEGER from, OUT INTEGER to igraph_edges: - PARAMS: GRAPH graph, EDGE_SELECTOR eids, OUT VECTOR_INT edges + PARAMS: GRAPH graph, EDGESET eids, OUT VECTOR edges DEPS: eids ON graph -igraph_empty_attrs: - PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed, ATTRIBUTES attr - igraph_get_eid: PARAMS: |- - GRAPH graph, OUT EDGE eid, VERTEX from, VERTEX to, - BOOLEAN directed=True, BOOLEAN error=True + GRAPH graph, OUT INTEGER eid, INTEGER from, + INTEGER to, BOOLEAN directed=True, BOOLEAN error=True igraph_get_eids: PARAMS: |- - GRAPH graph, OUT EDGE_INDICES eids, VERTEX_INDEX_PAIRS pairs, - BOOLEAN directed=True, BOOLEAN error=True - DEPS: pairs ON graph - -igraph_get_all_eids_between: - PARAMS: |- - GRAPH graph, OUT EDGE_INDICES eids, VERTEX from, VERTEX to, - BOOLEAN directed=True + GRAPH graph, OUT VECTOR eids, OPTIONAL VECTOR pairs, + OPTIONAL VECTOR path, BOOLEAN directed=True, BOOLEAN error=True igraph_incident: - PARAMS: GRAPH graph, OUT EDGE_INDICES eids, VERTEX vid, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VECTOR eids, INTEGER vid, NEIMODE mode=ALL igraph_is_same_graph: PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN res @@ -96,58 +76,32 @@ igraph_is_same_graph: ####################################### igraph_create: - PARAMS: OUT GRAPH graph, VECTOR_INT edges, INTEGER n=0, BOOLEAN directed=True + PARAMS: OUT GRAPH graph, VECTOR edges, INTEGER n=0, BOOLEAN directed=True igraph_adjacency: - PARAMS: |- - OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE - -igraph_sparse_adjacency: - # adjmatrix is declared as INOUT because it might be modified during the - # construction to eliminate duplicate elements from the representation - PARAMS: |- - OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE - -igraph_sparse_weighted_adjacency: - # adjmatrix is declared as INOUT because it might be modified during the - # construction to eliminate duplicate elements from the representation - PARAMS: |- - OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, - OUT EDGEWEIGHTS weights, LOOPS loops=ONCE + PARAMS: OUT GRAPH graph, MATRIX adjmatrix, ADJACENCYMODE mode=DIRECTED igraph_weighted_adjacency: PARAMS: |- - OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, - OUT EDGEWEIGHTS weights, LOOPS loops=ONCE + OUT GRAPH graph, MATRIX adjmatrix, + ADJACENCYMODE mode=DIRECTED, CSTRING attr="weight", + BOOLEAN loops igraph_star: - PARAMS: OUT GRAPH graph, INTEGER n, STAR_MODE mode=OUT, INTEGER center=0 - -igraph_wheel: - PARAMS: OUT GRAPH graph, INTEGER n, WHEEL_MODE mode=OUT, INTEGER center=0 + PARAMS: OUT GRAPH graph, INTEGER n, STARMODE mode=OUT, INTEGER center=0 -igraph_square_lattice: +igraph_lattice: PARAMS: |- - OUT GRAPH graph, VECTOR_INT dimvector, INTEGER nei=1, - BOOLEAN directed=False, BOOLEAN mutual=False, OPTIONAL VECTOR_BOOL periodic - -igraph_triangular_lattice: - PARAMS: |- - OUT GRAPH graph, VECTOR_INT dimvector, BOOLEAN directed=False, BOOLEAN mutual=False + OUT GRAPH graph, VECTOR dimvector, INTEGER nei=1, + BOOLEAN directed=False, BOOLEAN mutual=False, BOOLEAN circular=False igraph_ring: PARAMS: |- OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN mutual=False, BOOLEAN circular=True -igraph_kary_tree: - PARAMS: OUT GRAPH graph, INTEGER n, INTEGER children=2, TREE_MODE type=OUT - -igraph_symmetric_tree: - PARAMS: OUT GRAPH graph, VECTOR_INT branches, TREE_MODE type=OUT - -igraph_regular_tree: - PARAMS: OUT GRAPH graph, INTEGER h, INTEGER k=3, TREE_MODE type=UNDIRECTED +igraph_tree: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER children=2, TREEMODE type=OUT igraph_full: PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN loops=False @@ -156,10 +110,10 @@ igraph_full_citation: PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=True igraph_atlas: - PARAMS: OUT GRAPH graph, INTEGER number=0 + PARAMS: OUT GRAPH graph, INT number=0 igraph_extended_chordal_ring: - PARAMS: OUT GRAPH graph, INTEGER nodes, MATRIX_INT W, BOOLEAN directed=False + PARAMS: OUT GRAPH graph, INTEGER nodes, MATRIX W, BOOLEAN directed=False igraph_connect_neighborhood: PARAMS: INOUT GRAPH graph, INTEGER order=2, NEIMODE mode=ALL @@ -174,10 +128,10 @@ igraph_kautz: PARAMS: OUT GRAPH graph, INTEGER m, INTEGER n igraph_famous: - PARAMS: OUT GRAPH graph, CSTRING name + PARAMS: OUT GRAPH graph, CSTRING name="" igraph_lcf_vector: - PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, INTEGER repeats=1 + PARAMS: OUT GRAPH graph, INTEGER n, VECTOR shifts, INTEGER repeats=1 igraph_adjlist: PARAMS: |- @@ -186,33 +140,14 @@ igraph_adjlist: igraph_full_bipartite: PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, INTEGER n1, + OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, INTEGER n1, INTEGER n2, BOOLEAN directed=False, NEIMODE mode=ALL -igraph_full_multipartite: - PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, VECTOR_INT n, - BOOLEAN directed=False, NEIMODE mode=ALL - igraph_realize_degree_sequence: PARAMS: |- - OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg=NULL, + OUT GRAPH graph, VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, EDGE_TYPE_SW allowed_edge_types=SIMPLE, REALIZE_DEGSEQ_METHOD method=SMALLEST -igraph_circulant: - PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, BOOLEAN directed=False - -igraph_generalized_petersen: - PARAMS: OUT GRAPH graph, INTEGER n, INTEGER k - -igraph_turan: - PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, INTEGER n, INTEGER r - -igraph_weighted_sparsemat: - PARAMS: - OUT GRAPH graph, SPARSEMAT A, BOOLEAN directed, CSTRING attr, BOOLEAN loops=False - ####################################### # Constructors, games ####################################### @@ -220,25 +155,20 @@ igraph_weighted_sparsemat: igraph_barabasi_game: PARAMS: |- OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER m=1, - OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL A=1.0, + VECTOR_OR_0 outseq, BOOLEAN outpref=False, REAL A=1.0, BOOLEAN directed=True, BARABASI_ALGORITHM algo=BAG, - OPTIONAL GRAPH start_from - -igraph_erdos_renyi_game: - PARAMS: |- - OUT GRAPH graph, ERDOS_RENYI_TYPE type, INTEGER n, REAL p_or_m, - BOOLEAN directed=False, BOOLEAN loops=False + GRAPH_OR_0 start_from=0 igraph_erdos_renyi_game_gnp: PARAMS: OUT GRAPH graph, INTEGER n, REAL p, BOOLEAN directed=False, BOOLEAN loops=False igraph_erdos_renyi_game_gnm: - PARAMS: OUT GRAPH graph, INTEGER n, INTEGER m, BOOLEAN directed=False, BOOLEAN loops=False + PARAMS: OUT GRAPH graph, INTEGER n, REAL m, BOOLEAN directed=False, BOOLEAN loops=False igraph_degree_sequence_game: PARAMS: |- - OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, - DEGSEQ_MODE method=CONFIGURATION + OUT GRAPH graph, VECTOR out_deg, VECTOR_OR_0 in_deg, + DEGSEQMODE method=SIMPLE igraph_growing_random_game: PARAMS: |- @@ -247,7 +177,7 @@ igraph_growing_random_game: igraph_barabasi_aging_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, VECTOR_OR_0 outseq, BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, REAL zero_deg_appeal=1.0, REAL zero_age_appeal=0.0, REAL deg_coef=1.0, REAL age_coef=1.0, BOOLEAN directed=True @@ -255,12 +185,12 @@ igraph_barabasi_aging_game: igraph_recent_degree_game: PARAMS: |- OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER window=1, - INTEGER m=1, OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, + INTEGER m=1, VECTOR_OR_0 outseq, BOOLEAN outpref=False, REAL zero_appeal=1.0, BOOLEAN directed=True igraph_recent_degree_aging_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, VECTOR_OR_0 outseq, BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, INTEGER window=1, REAL zero_appeal=1.0, BOOLEAN directed=True @@ -268,13 +198,13 @@ igraph_callaway_traits_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, INTEGER edges_per_step=1, VECTOR type_dist, MATRIX pref_matrix, - BOOLEAN directed=False, OPTIONAL OUT VECTOR_INT node_type_vec + BOOLEAN directed=False, OPTIONAL OUT VECTOR node_type_vec igraph_establishment_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, INTEGER k=1, VECTOR type_dist, MATRIX pref_matrix, BOOLEAN directed=True, - OPTIONAL OUT VECTOR_INT node_type_vec + OPTIONAL OUT VECTOR node_type_vec igraph_grg_game: PARAMS: |- @@ -285,14 +215,14 @@ igraph_preference_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, VECTOR type_dist, BOOLEAN fixed_sizes=False, - MATRIX pref_matrix, OUT VECTOR_INT node_type_vec, + MATRIX pref_matrix, OUT VECTOR node_type_vec, BOOLEAN directed=False, BOOLEAN loops=False igraph_asymmetric_preference_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER out_types, INTEGER in_types, MATRIX type_dist_matrix, MATRIX pref_matrix, - OUT VECTOR_INT node_type_out_vec, OUT VECTOR_INT node_type_in_vec, + OUT VECTOR node_type_in_vec, OUT VECTOR node_type_out_vec, BOOLEAN loops=False igraph_rewire_edges: @@ -317,12 +247,12 @@ igraph_lastcit_game: igraph_cited_type_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, VECTOR pref, + OUT GRAPH graph, INTEGER nodes, VECTOR types, VECTOR pref, INTEGER edges_per_step=1, BOOLEAN directed=True igraph_citing_cited_type_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, MATRIX pref, + OUT GRAPH graph, INTEGER nodes, VECTOR types, MATRIX pref, INTEGER edges_per_step=1, BOOLEAN directed=True igraph_forest_fire_game: @@ -338,7 +268,7 @@ igraph_simple_interconnected_islands_game: igraph_static_fitness_game: PARAMS: |- OUT GRAPH graph, INTEGER no_of_edges, VECTOR fitness_out, - OPTIONAL VECTOR fitness_in, BOOLEAN loops=False, + VECTOR_OR_0 fitness_in=NULL, BOOLEAN loops=False, BOOLEAN multiple=False igraph_static_power_law_game: @@ -369,18 +299,18 @@ igraph_hsbm_list_game: INTERNAL: true PARAMS: |- OUT GRAPH graph, INTEGER n, VECTOR_INT mlist, - VECTOR_LIST rholist, MATRIX_LIST Clist, REAL p + VECTORLIST rholist, MATRIXLIST Clist, REAL p igraph_correlated_game: PARAMS: |- GRAPH old_graph, OUT GRAPH new_graph, - REAL corr, REAL p=edge_density(old.graph), OPTIONAL INDEX_VECTOR permutation=NULL + REAL corr, REAL p=edge_density(old.graph), VECTORM1_OR_0 permutation=NULL igraph_correlated_pair_game: PARAMS: |- OUT GRAPH graph1, OUT GRAPH graph2, INTEGER n, REAL corr, REAL p, BOOLEAN directed=False, - OPTIONAL INDEX_VECTOR permutation=NULL + VECTORM1_OR_0 permutation=NULL igraph_dot_product_game: PARAMS: OUT GRAPH graph, MATRIX vecs, BOOLEAN directed=False @@ -403,7 +333,7 @@ igraph_sample_dirichlet: ####################################### igraph_are_connected: - PARAMS: GRAPH graph, VERTEX v1, VERTEX v2, OUT BOOLEAN res + PARAMS: GRAPH graph, INTEGER v1, INTEGER v2, OUT BOOLEAN res ####################################### # Structural properties @@ -412,240 +342,130 @@ igraph_are_connected: igraph_diameter: PARAMS: |- GRAPH graph, OUT REAL res, OUT INTEGER from, - OUT INTEGER to, OPTIONAL OUT VECTOR_INT vertex_path, - OPTIONAL OUT VECTOR_INT edge_path, - BOOLEAN directed=True, BOOLEAN unconnected=True + OUT INTEGER to, OUT VECTOR_OR_0 path, BOOLEAN directed=True, + BOOLEAN unconnected=True igraph_diameter_dijkstra: PARAMS: |- GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT REAL res, OUT INTEGER from, OUT INTEGER to, - OPTIONAL OUT VECTOR_INT vertex_path, - OPTIONAL OUT VECTOR_INT edge_path, - BOOLEAN directed=True, BOOLEAN unconnected=True + OUT REAL res, OUT INTEGER from, + OUT INTEGER to, OUT VECTOR_OR_0 path, BOOLEAN directed=True, + BOOLEAN unconnected=True DEPS: weights ON graph igraph_closeness: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, - OPTIONAL OUT VECTOR_INT reachable_count, + GRAPH graph, OUT VERTEXINDEX res, + OUT VECTOR_OR_0 reachable_count, OPTIONAL OUT BOOLEAN all_reachable, - VERTEX_SELECTOR vids=ALL, + VERTEXSET vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False DEPS: vids ON graph, weights ON graph, res ON graph vids igraph_closeness_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, - OPTIONAL OUT VECTOR_INT reachable_count, + GRAPH graph, OUT VERTEXINDEX res, + OUT VECTOR_OR_0 reachable_count, OPTIONAL OUT BOOLEAN all_reachable, - VERTEX_SELECTOR vids=ALL, + VERTEXSET vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids -igraph_distances: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph - -igraph_distances_cutoff: +igraph_shortest_paths: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, REAL cutoff=-1 + GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, + VERTEXSET to=ALL, NEIMODE mode=OUT DEPS: from ON graph, to ON graph -igraph_get_shortest_path: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, - VERTEX from, VERTEX to, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, vertices ON graph, edges ON graph - -igraph_get_shortest_path_bellman_ford: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, - VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph - -igraph_get_shortest_path_dijkstra: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, - VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph - -igraph_get_shortest_path_astar: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, - VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - OPTIONAL ASTAR_HEURISTIC_FUNC heuristic=NULL, EXTRA extra=NULL - DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph - igraph_get_shortest_paths: PARAMS: |- GRAPH graph, - OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, - VERTEX from, VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_INT parents, - OPTIONAL OUT VECTOR_INT inbound_edges + OPTIONAL OUT VERTEXSETLIST vertices, OPTIONAL OUT EDGESETLIST edges, + VERTEX from, VERTEXSET to=ALL, NEIMODE mode=OUT, + OPTIONAL OUT VECTOR_LONG predecessors, + OPTIONAL OUT VECTOR_LONG inbound_edges DEPS: edges ON graph, from ON graph, to ON graph igraph_get_all_shortest_paths: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, - OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, - VERTEX from, VERTEX_SELECTOR to, NEIMODE mode=OUT - DEPS: edges ON graph, from ON graph, to ON graph - -igraph_distances_dijkstra: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, weights ON graph + GRAPH graph, OUT VERTEXSETLIST res, OUT VECTOR nrgeo, + VERTEX from, VERTEXSET to, NEIMODE mode=OUT + DEPS: res ON graph, from ON graph, to ON graph -igraph_distances_dijkstra_cutoff: +igraph_shortest_paths_dijkstra: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT, REAL cutoff=-1 + GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, + VERTEXSET to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT DEPS: from ON graph, to ON graph, weights ON graph igraph_get_shortest_paths_dijkstra: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, - OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, + GRAPH graph, OPTIONAL OUT VERTEXSETLIST vertices, + OPTIONAL OUT EDGESETLIST edges, VERTEX from, VERTEXSET to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_INT parents=0, - OPTIONAL OUT VECTOR_INT inbound_edges=0 + OPTIONAL OUT VECTOR_LONG predecessors=0, + OPTIONAL OUT VECTOR_LONG inbound_edges=0 DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph igraph_get_shortest_paths_bellman_ford: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, - OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, + GRAPH graph, OPTIONAL OUT VERTEXSETLIST vertices, + OPTIONAL OUT EDGESETLIST edges, VERTEX from, VERTEXSET to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_INT parents=0, - OPTIONAL OUT VECTOR_INT inbound_edges=0 + OPTIONAL OUT VECTOR_LONG predecessors=0, + OPTIONAL OUT VECTOR_LONG inbound_edges=0 DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph igraph_get_all_shortest_paths_dijkstra: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, - OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, - VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, + GRAPH graph, OUT VERTEXSETLIST res, OUT VECTOR nrgeo, + VERTEX from, VERTEXSET to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT - DEPS: |- - weights ON graph, from ON graph, to ON graph, vertices ON graph, edges ON graph + DEPS: weights ON graph, to ON graph, res ON graph, from ON graph -igraph_distances_bellman_ford: +igraph_shortest_paths_bellman_ford: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, + VERTEXSET to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT DEPS: from ON graph, to ON graph, weights ON graph -igraph_distances_johnson: +igraph_shortest_paths_johnson: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights + GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, + VERTEXSET to=ALL, EDGEWEIGHTS weights DEPS: from ON graph, to ON graph, weights ON graph -igraph_distances_floyd_warshall: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - FWALGORITHM method - DEPS: from ON graph, to ON graph, weights ON graph - -igraph_voronoi: - PARAMS: |- - GRAPH graph, OUT VECTOR_INT membership, OUT VECTOR distances, - VERTEX_INDICES generators, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, VORONOI_TIEBREAKER tiebreaker=RANDOM - DEPS: weights ON graph, generators ON graph - igraph_get_all_simple_paths: PARAMS: |- - GRAPH graph, OUT VERTEX_INDICES res, VERTEX from, - VERTEX_SELECTOR to=ALL, INTEGER cutoff=-1, NEIMODE mode=OUT + GRAPH graph, OUT VERTEXSET_INT res, VERTEX from, + VERTEXSET to=ALL, INTEGER cutoff=-1, NEIMODE mode=OUT DEPS: from ON graph, to ON graph, res ON graph -igraph_get_k_shortest_paths: - PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, - OPTIONAL OUT VERTEXSET_LIST vertex_paths, - OPTIONAL OUT EDGESET_LIST edge_paths, - INTEGER k, VERTEX from, VERTEX to, NEIMODE mode=OUT - DEPS: |- - from ON graph, to ON graph, weights ON graph, vertex_paths ON graph, edge_paths ON graph - -igraph_get_widest_path: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, - VERTEX from, VERTEX to, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT - DEPS: |- - from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph - -igraph_get_widest_paths: - PARAMS: |- - GRAPH graph, - OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, - VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, - NEIMODE mode=OUT, OPTIONAL OUT VECTOR_INT parents, - OPTIONAL OUT VECTOR_INT inbound_edges - DEPS: |- - from ON graph, to ON graph, weights ON graph, vertices ON graph, - edges ON graph - -igraph_widest_path_widths_dijkstra: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, weights ON graph - -igraph_widest_path_widths_floyd_warshall: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, - VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT - DEPS: from ON graph, to ON graph, weights ON graph - -igraph_spanner: - PARAMS: |- - GRAPH graph, OUT EDGE_INDICES spanner, REAL stretch, EDGEWEIGHTS weights - DEPS: weights ON graph - igraph_subcomponent: - PARAMS: GRAPH graph, OUT VERTEX_INDICES res, VERTEX vid, NEIMODE mode=ALL + # TODO(ntamas): vid is a double; this is correct but this is actually + # a mistake in 0.9.x. This should be fixed in 0.10. + PARAMS: GRAPH graph, OUT VERTEXSET res, REAL vid, NEIMODE mode=ALL DEPS: vid ON graph, res ON graph igraph_betweenness: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, BOOLEAN directed=True, EDGEWEIGHTS weights=NULL DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_betweenness_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, BOOLEAN directed=True, EDGEWEIGHTS weights=NULL, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids -igraph_betweenness_subset: - PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, - BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, - EDGEWEIGHTS weights=NULL - DEPS: |- - vids ON graph, weights ON graph, res ON graph, sources ON graph, targets ON graph - igraph_edge_betweenness: PARAMS: |- GRAPH graph, OUT VECTOR res, BOOLEAN directed=True, @@ -658,23 +478,15 @@ igraph_edge_betweenness_cutoff: EDGEWEIGHTS weights=NULL, REAL cutoff=-1 DEPS: weights ON graph -igraph_edge_betweenness_subset: - PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, EDGE_SELECTOR eids=ALL, - BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, - EDGEWEIGHTS weights=NULL - DEPS: |- - eids ON graph, weights ON graph, res ON graph, sources ON graph, targets ON graph - igraph_harmonic_centrality: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_harmonic_centrality_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -682,52 +494,39 @@ igraph_harmonic_centrality_cutoff: igraph_pagerank: PARAMS: |- GRAPH graph, PAGERANKALGO algo=PRPACK, - OUT VERTEX_QTY vector, OUT REAL value, - VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + OUT VERTEXINDEX vector, OUT REAL value, + VERTEXSET vids=ALL, BOOLEAN directed=True, REAL damping=0.85, EDGEWEIGHTS weights=NULL, INOUT PAGERANKOPT options=NULL - DEPS: |- - vids ON graph, weights ON graph, vector ON graph, - options ON algo - -igraph_personalized_pagerank: - PARAMS: |- - GRAPH graph, PAGERANKALGO algo=PRPACK, - OUT VERTEX_QTY vector, OUT REAL value, - VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, - REAL damping=0.85, OPTIONAL VECTOR personalized, - OPTIONAL EDGEWEIGHTS weights, - INOUT PAGERANKOPT options=NULL DEPS: |- vids ON graph, weights ON graph, vector ON graph vids, options ON algo -igraph_personalized_pagerank_vs: +igraph_personalized_pagerank: PARAMS: |- GRAPH graph, PAGERANKALGO algo=PRPACK, - PRIMARY OUT VERTEX_QTY vector, OUT REAL value, - VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, - REAL damping=0.85, - VERTEX_SELECTOR reset_vids, - OPTIONAL EDGEWEIGHTS weights=NULL, + OUT VERTEXINDEX vector, OUT REAL value, + VERTEXSET vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, VECTOR_OR_0 personalized=NULL, + EDGEWEIGHTS weights=NULL, INOUT PAGERANKOPT options=NULL DEPS: |- vids ON graph, weights ON graph, vector ON graph vids, options ON algo igraph_rewire: - PARAMS: INOUT GRAPH rewire, INTEGER n, REWIRING_MODE mode=SIMPLE + PARAMS: INOUT GRAPH rewire, INTEGER n, REWIRINGMODE mode=SIMPLE igraph_induced_subgraph: - PARAMS: GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl=AUTO + PARAMS: GRAPH graph, OUT GRAPH res, VERTEXSET vids, SUBGRAPH_IMPL impl=AUTO DEPS: vids ON graph -igraph_subgraph_from_edges: - PARAMS: GRAPH graph, OUT GRAPH res, EDGE_SELECTOR eids, BOOLEAN delete_vertices=True +igraph_subgraph_edges: + PARAMS: GRAPH graph, OUT GRAPH res, EDGESET eids, BOOLEAN delete_vertices=True DEPS: eids ON graph igraph_reverse_edges: - PARAMS: INOUT GRAPH graph, EDGE_SELECTOR eids=ALL + PARAMS: INOUT GRAPH graph, EDGESET eids=ALL DEPS: eids ON graph igraph_average_path_length: @@ -751,39 +550,32 @@ igraph_simplify: EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default igraph_transitivity_undirected: - PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITYMODE mode=NAN igraph_transitivity_local_undirected: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, TRANSITIVITY_MODE mode=NAN - DEPS: vids ON graph + PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, TRANSITIVITYMODE mode=NAN igraph_transitivity_avglocal_undirected: - PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITYMODE mode=NAN igraph_transitivity_barrat: PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, - EDGEWEIGHTS weights=NULL, TRANSITIVITY_MODE mode=NAN + GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, + EDGEWEIGHTS weights=NULL, TRANSITIVITYMODE mode=NAN DEPS: res ON graph, vids ON graph, weights ON graph -igraph_ecc: - PARAMS: |- - GRAPH graph, OUT VECTOR res, EDGE_SELECTOR eids=ALL, - INTEGER k=3, BOOLEAN offset=False, BOOLEAN normalize=True - DEPS: res ON graph, eids ON graph - igraph_reciprocity: PARAMS: |- GRAPH graph, OUT REAL res, BOOLEAN ignore_loops=True, - RECIP mode=DEFAULT + RECIP mode=Default igraph_constraint: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, OPTIONAL EDGEWEIGHTS weights - DEPS: vids ON graph, weights ON graph + PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, VECTOR_OR_0 weights + DEPS: vids ON graph igraph_maxdegree: PARAMS: |- - GRAPH graph, OUT INTEGER res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT INTEGER res, VERTEXSET vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=True DEPS: vids ON graph @@ -792,48 +584,45 @@ igraph_density: igraph_neighborhood_size: PARAMS: |- - GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids, INTEGER order, + GRAPH graph, OUT VECTOR res, VERTEXSET vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: vids ON graph igraph_neighborhood: PARAMS: |- - GRAPH graph, OUT VERTEXSET_LIST res, - VERTEX_SELECTOR vids, INTEGER order, + GRAPH graph, OUT VERTEXSETLIST res, + VERTEXSET vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: res ON graph, vids ON graph igraph_neighborhood_graphs: PARAMS: |- - GRAPH graph, OUT GRAPH_LIST res, VERTEX_SELECTOR vids, + GRAPH graph, OUT GRAPHLIST res, VERTEXSET vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: vids ON graph igraph_topological_sorting: - PARAMS: GRAPH graph, OUT VECTOR_INT res, NEIMODE mode=OUT + PARAMS: GRAPH graph, OUT VECTOR res, NEIMODE mode=OUT igraph_feedback_arc_set: # Default algorithm is the approximate method because it is faster and the # function is _not_ called igraph_minimum_feedback_arc_set - PARAMS: GRAPH graph, OUT EDGE_INDICES result, EDGEWEIGHTS weights=NULL, FAS_ALGORITHM algo=APPROX_EADES + PARAMS: GRAPH graph, OUT EDGESET result, EDGEWEIGHTS weights=NULL, FAS_ALGORITHM algo=APPROX_EADES DEPS: result ON graph, weights ON graph igraph_is_loop: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL DEPS: es ON graph igraph_is_dag: PARAMS: GRAPH graph, OUT BOOLEAN res -igraph_is_acyclic: - PARAMS: GRAPH graph, OUT BOOLEAN res - igraph_is_simple: PARAMS: GRAPH graph, OUT BOOLEAN res igraph_is_multiple: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL DEPS: es ON graph igraph_has_loop: @@ -843,22 +632,19 @@ igraph_has_multiple: PARAMS: GRAPH graph, OUT BOOLEAN res igraph_count_multiple: - PARAMS: GRAPH graph, OUT VECTOR_INT res, EDGE_SELECTOR es=ALL + PARAMS: GRAPH graph, OUT VECTOR res, EDGESET es=ALL DEPS: es ON graph igraph_girth: - PARAMS: GRAPH graph, OUT REAL girth, OUT VERTEX_INDICES circle + PARAMS: GRAPH graph, OUT INTEGER girth, OUT VERTEXSET circle DEPS: circle ON graph -igraph_is_perfect: - PARAMS: GRAPH graph, OUT BOOLEAN res - igraph_add_edge: PARAMS: INOUT GRAPH graph, INTEGER from, INTEGER to igraph_eigenvector_centrality: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, BOOLEAN directed=False, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults @@ -866,58 +652,68 @@ igraph_eigenvector_centrality: igraph_hub_score: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults DEPS: weights ON graph, vector ON graph igraph_authority_score: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults DEPS: weights ON graph, vector ON graph -igraph_hub_and_authority_scores: +igraph_arpack_rssolve: + # TODO(ntamas): this should probably not be exposed to higher-level + # interfaces; igraph's goal is not to provide an ARPACK wrapper PARAMS: |- - GRAPH graph, OUT VERTEX_QTY hub_vector, OUT VERTEX_QTY authority_vector, - OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, - INOUT ARPACKOPT options=arpack_defaults + ARPACKFUNC fun, EXTRA extra, INOUT ARPACKOPT options=arpack_defaults, + INOUT ARPACKSTORAGE storage, OPTIONAL OUT VECTOR values, OPTIONAL OUT MATRIX vectors + +igraph_arpack_rnsolve: + # TODO(ntamas): this should probably not be exposed to higher-level + # interfaces; igraph's goal is not to provide an ARPACK wrapper + PARAMS: |- + ARPACKFUNC fun, EXTRA extra, INOUT ARPACKOPT options=arpack_defaults, + INOUT ARPACKSTORAGE storage, OPTIONAL OUT MATRIX values, OPTIONAL OUT MATRIX vectors + +igraph_arpack_unpack_complex: + # TODO(ntamas): this should probably not be exposed to higher-level + # interfaces; igraph's goal is not to provide an ARPACK wrapper + PARAMS: INOUT MATRIX vectors, INOUT MATRIX values, LONGINT nev igraph_unfold_tree: PARAMS: |- - GRAPH graph, OUT GRAPH tree, NEIMODE mode=ALL, VECTOR_INT roots, - OPTIONAL OUT INDEX_VECTOR vertex_index + GRAPH graph, OUT GRAPH tree, NEIMODE mode=ALL, VECTOR roots, + OUT VECTORM1_OR_0 vertex_index igraph_is_mutual: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL, BOOLEAN loops=True + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL DEPS: es ON graph -igraph_has_mutual: - PARAMS: GRAPH graph, OUT BOOLEAN res, BOOLEAN loops=True - igraph_maximum_cardinality_search: - PARAMS: GRAPH graph, OPTIONAL OUT INDEX_VECTOR alpha, OPTIONAL OUT VERTEX_INDICES alpham1 + PARAMS: GRAPH graph, OPTIONAL OUT VECTORM1 alpha, OPTIONAL OUT VERTEXSET alpham1 DEPS: alpham1 ON graph igraph_is_chordal: PARAMS: |- - GRAPH graph, OPTIONAL INDEX_VECTOR alpha=NULL, OPTIONAL VERTEX_INDICES alpham1=NULL, - OPTIONAL OUT BOOLEAN chordal, OPTIONAL OUT VECTOR_INT fillin, - OPTIONAL OUT GRAPH newgraph + GRAPH graph, VECTORM1 alpha=NULL, OPTIONAL VECTORM1 alpham1=NULL, + OPTIONAL OUT BOOLEAN chordal, OUT VECTORM1_OR_0 fillin, + OUT GRAPH_OR_0 newgraph DEPS: alpham1 ON graph igraph_avg_nearest_neighbor_degree: PARAMS: |- - GRAPH graph, VERTEX_SELECTOR vids=ALL, + GRAPH graph, VERTEXSET vids=ALL, NEIMODE mode=ALL, NEIMODE neighbor_degree_mode=ALL, - OPTIONAL OUT VERTEX_QTY knn, OPTIONAL OUT VECTOR knnk, + OPTIONAL OUT VERTEXINDEX knn, OUT VECTOR_OR_0 knnk, EDGEWEIGHTS weights=NULL DEPS: vids ON graph, weights ON graph, knn ON graph vids igraph_strength: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=True, EDGEWEIGHTS weights=NULL DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -937,7 +733,7 @@ igraph_centralization_degree_tmax: # should not have a default value; see this comment from @torfason: # https://github.com/igraph/rigraph/issues/369#issuecomment-939893681 PARAMS: |- - OPTIONAL GRAPH graph, INTEGER nodes=0, NEIMODE mode=ALL, + GRAPH_OR_0 graph=NULL, INTEGER nodes=0, NEIMODE mode=ALL, BOOLEAN loops, OUT REAL res igraph_centralization_betweenness: @@ -950,7 +746,7 @@ igraph_centralization_betweenness: igraph_centralization_betweenness_tmax: PARAMS: |- - OPTIONAL GRAPH graph, INTEGER nodes=0, + GRAPH_OR_0 graph=NULL, INTEGER nodes=0, BOOLEAN directed=True, OUT REAL res igraph_centralization_closeness: @@ -962,7 +758,7 @@ igraph_centralization_closeness: igraph_centralization_closeness_tmax: PARAMS: |- - OPTIONAL GRAPH graph, INTEGER nodes=0, + GRAPH_OR_0 graph=NULL, INTEGER nodes=0, NEIMODE mode=OUT, OUT REAL res igraph_centralization_eigenvector_centrality: @@ -975,80 +771,52 @@ igraph_centralization_eigenvector_centrality: igraph_centralization_eigenvector_centrality_tmax: PARAMS: |- - OPTIONAL GRAPH graph, INTEGER nodes=0, + GRAPH_OR_0 graph=NULL, INTEGER nodes=0, BOOLEAN directed=False, BOOLEAN scale=True, OUT REAL res igraph_assortativity_nominal: PARAMS: |- - GRAPH graph, INDEX_VECTOR types, OUT REAL res, - BOOLEAN directed=True, BOOLEAN normalized=True + GRAPH graph, VECTORM1 types, OUT REAL res, + BOOLEAN directed=True igraph_assortativity: PARAMS: |- - GRAPH graph, VECTOR values, OPTIONAL VECTOR values_in, - OUT REAL res, BOOLEAN directed=True, BOOLEAN normalized=True + GRAPH graph, VECTOR types1, VECTOR_OR_0 types2=NULL, + OUT REAL res, BOOLEAN directed=True igraph_assortativity_degree: PARAMS: GRAPH graph, OUT REAL res, BOOLEAN directed=True igraph_contract_vertices: PARAMS: |- - INOUT GRAPH graph, INDEX_VECTOR mapping, + INOUT GRAPH graph, VECTORM1 mapping, VERTEX_ATTRIBUTE_COMBINATION vertex_attr_comb=Default igraph_eccentricity: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, NEIMODE mode=ALL DEPS: vids ON graph, res ON graph vids -igraph_eccentricity_dijkstra: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, - NEIMODE mode=ALL - DEPS: weights ON graph, vids ON graph, res ON graph vids - -igraph_graph_center: - PARAMS: |- - GRAPH graph, OUT VERTEX_INDICES res, NEIMODE mode=ALL - DEPS: res ON graph - igraph_radius: PARAMS: GRAPH graph, OUT REAL radius, NEIMODE mode=ALL -igraph_pseudo_diameter: - NAME-R: pseudo_diameter - PARAMS: |- - GRAPH graph, OUT REAL diameter, VERTEX start_vid, - OUT INTEGER from=NULL, OUT INTEGER to=NULL, - BOOLEAN directed=True, BOOLEAN unconnected=True - -igraph_pseudo_diameter_dijkstra: - NAME-R: pseudo_diameter - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT REAL diameter, VERTEX start_vid, - OUT INTEGER from=NULL, OUT INTEGER to=NULL, - BOOLEAN directed=True, BOOLEAN unconnected=True - DEPS: weights ON graph - igraph_diversity: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_QTY res, - VERTEX_SELECTOR vids=ALL + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEXINDEX res, + VERTEXSET vids=ALL DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_random_walk: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_INDICES vertices, OUT EDGE_INDICES edges, - VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN - DEPS: start ON graph, weights ON graph, vertices ON graph, edges ON graph + GRAPH graph, OUT VERTEXSET walk, VERTEX start, NEIMODE mode=OUT, + INTEGER steps, RWSTUCK stuck=RETURN + DEPS: start ON graph, walk ON graph igraph_random_edge_walk: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, OUT EDGE_INDICES edgewalk, + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT EDGESET edgewalk, VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN DEPS: start ON graph, weights ON graph, edgewalk ON graph @@ -1058,7 +826,7 @@ igraph_global_efficiency: igraph_local_efficiency: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, EDGEWEIGHTS weights=NULL, BOOLEAN directed=True, NEIMODE mode=ALL DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -1068,53 +836,47 @@ igraph_average_local_efficiency: BOOLEAN directed=True, NEIMODE mode=ALL DEPS: weights ON graph -igraph_transitive_closure_dag: - PARAMS: GRAPH graph, OUT GRAPH closure - -igraph_trussness: - PARAMS: GRAPH graph, OUT VECTOR_INT trussness - ####################################### # Degree sequences ####################################### igraph_is_bigraphical: PARAMS: |- - VECTOR_INT degrees1, VECTOR_INT degrees2, + VECTOR degrees1, VECTOR degrees2, EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res +igraph_is_degree_sequence: + PARAMS: VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, OUT BOOLEAN res + FLAGS: DEPRECATED + igraph_is_graphical: PARAMS: |- - VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, + VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res +igraph_is_graphical_degree_sequence: + PARAMS: VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, OUT BOOLEAN res + FLAGS: DEPRECATED + ####################################### # Visitors ####################################### igraph_bfs: PARAMS: |- - GRAPH graph, VERTEX root, OPTIONAL VERTEX_INDICES roots, + GRAPH graph, INTEGER root, VECTOR_OR_0 roots, NEIMODE mode=OUT, BOOLEAN unreachable, - VERTEX_INDICES restricted, - OUT VERTEX_INDICES order, OUT VECTOR_INT rank, - OUT VECTOR_INT parents, - OUT VECTOR_INT pred, OUT VECTOR_INT succ, - OUT VECTOR_INT dist, BFS_FUNC callback, EXTRA extra - -igraph_bfs_simple: - PARAMS: |- - GRAPH graph, VERTEX root, - NEIMODE mode=OUT, - OUT VERTEX_INDICES order, - OUT VECTOR_INT layers, - OUT VECTOR_INT parents + VECTOR_OR_0 restricted, + OUT VECTOR_OR_0 order, OUT VECTOR_OR_0 rank, + OUT VECTOR_OR_0 father, + OUT VECTOR_OR_0 pred, OUT VECTOR_OR_0 succ, + OUT VECTOR_OR_0 dist, BFS_FUNC callback, EXTRA extra igraph_dfs: PARAMS: |- - GRAPH graph, VERTEX root, NEIMODE mode=OUT, BOOLEAN unreachable, - OUT VERTEX_INDICES order, OUT VERTEX_INDICES order_out, - OUT VECTOR_INT father, OUT VECTOR_INT dist, + GRAPH graph, INTEGER root, NEIMODE mode=OUT, BOOLEAN unreachable, + OUT VECTOR_OR_0 order, OUT VECTOR_OR_0 order_out, + OUT VECTOR_OR_0 father, OUT VECTOR_OR_0 dist, DFS_FUNC in_callback, DFS_FUNC out_callback, EXTRA extra ####################################### @@ -1132,72 +894,60 @@ igraph_bipartite_projection: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT GRAPH proj1, OUT GRAPH proj2, - OPTIONAL OUT VECTOR_INT multiplicity1, - OPTIONAL OUT VECTOR_INT multiplicity2, INTEGER probe1=-1 + OUT VECTOR_OR_0 multiplicity1, + OUT VECTOR_OR_0 multiplicity2, INTEGER probe1=-1 DEPS: types ON graph igraph_create_bipartite: PARAMS: |- - OUT GRAPH graph, IN BIPARTITE_TYPES types, - VECTOR_INT edges, BOOLEAN directed=False + OUT GRAPH graph, IN VECTOR_BOOL types, + VECTORM1 edges, BOOLEAN directed=False -igraph_biadjacency: +igraph_incidence: PARAMS: |- - OUT GRAPH graph, OUT BIPARTITE_TYPES types, MATRIX incidence, + OUT GRAPH graph, OUT VECTOR_BOOL types, MATRIX incidence, BOOLEAN directed=False, NEIMODE mode=ALL, BOOLEAN multiple=False -igraph_get_biadjacency: +igraph_get_incidence: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, - OPTIONAL OUT INDEX_VECTOR row_ids, OPTIONAL OUT INDEX_VECTOR col_ids + OUT VECTOR_OR_0 row_ids, OUT VECTOR_OR_0 col_ids DEPS: types ON graph igraph_is_bipartite: - PARAMS: GRAPH graph, OUT BOOLEAN res, OPTIONAL OUT BIPARTITE_TYPES type + PARAMS: GRAPH graph, OUT BOOLEAN res, OUT VECTOR_BOOL_OR_0 type igraph_bipartite_game_gnp: PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, - INTEGER n1, INTEGER n2, REAL p, BOOLEAN directed=False, - NEIMODE mode=ALL + OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, + INTEGER n1, INTEGER n2, REAL p, BOOLEAN directed, + NEIMODE mode igraph_bipartite_game_gnm: PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, - INTEGER n1, INTEGER n2, INTEGER m, BOOLEAN directed=False, - NEIMODE mode=ALL - -igraph_bipartite_game: - PARAMS: |- - OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, - ERDOS_RENYI_TYPE type, INTEGER n1, INTEGER n2, REAL p=0.0, - INTEGER m=0, BOOLEAN directed=False, NEIMODE mode=ALL - + OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, + INTEGER n1, INTEGER n2, INTEGER m, BOOLEAN directed, + NEIMODE mode ####################################### # Spectral properties ####################################### -igraph_get_laplacian: +igraph_laplacian: PARAMS: |- - GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, - LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL - DEPS: weights ON graph - -igraph_get_laplacian_sparse: - PARAMS: |- - GRAPH graph, OUT SPARSEMAT sparseres, NEIMODE mode=OUT, - LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL + GRAPH graph, OUT MATRIX_OR_0 res, + OUT SPARSEMAT_OR_0 sparseres, BOOLEAN normalized=False, + EDGEWEIGHTS weights=NULL DEPS: weights ON graph ####################################### # Components ####################################### -igraph_connected_components: +igraph_clusters: PARAMS: |- - GRAPH graph, PRIMARY OUT VECTOR_INT membership, OUT VECTOR_INT csize, + GRAPH graph, OUT VECTOR membership, OUT VECTOR csize, OUT INTEGER no, CONNECTEDNESS mode=WEAK igraph_is_connected: @@ -1205,26 +955,26 @@ igraph_is_connected: igraph_decompose: PARAMS: |- - GRAPH graph, OUT GRAPH_LIST components, CONNECTEDNESS mode=WEAK, - INTEGER maxcompno=-1, INTEGER minelements=1 + GRAPH graph, OUT GRAPHLIST components, CONNECTEDNESS mode=WEAK, + LONGINT maxcompno=-1, LONGINT minelements=1 igraph_articulation_points: - PARAMS: GRAPH graph, OUT VERTEX_INDICES res + PARAMS: GRAPH graph, OUT VERTEXSET res DEPS: res ON graph igraph_biconnected_components: PARAMS: |- GRAPH graph, OUT INTEGER no, - OPTIONAL OUT EDGESET_LIST tree_edges, - OPTIONAL OUT EDGESET_LIST component_edges, - OPTIONAL OUT VERTEXSET_LIST components, - OUT VERTEX_INDICES articulation_points + OPTIONAL OUT EDGESETLIST tree_edges, + OPTIONAL OUT EDGESETLIST component_edges, + OPTIONAL OUT VERTEXSETLIST components, + OUT VERTEXSET articulation_points DEPS: |- tree_edges ON graph, component_edges ON graph, components ON graph, articulation_points ON graph igraph_bridges: - PARAMS: GRAPH graph, OUT EDGE_INDICES res + PARAMS: GRAPH graph, OUT EDGESET res DEPS: res ON graph ####################################### @@ -1233,7 +983,7 @@ igraph_bridges: igraph_cliques: PARAMS: |- - GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, + GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph @@ -1247,19 +997,13 @@ igraph_clique_size_hist: GRAPH graph, OUT VECTOR hist, INTEGER min_size=0, INTEGER max_size=0 igraph_largest_cliques: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + PARAMS: GRAPH graph, OUT VERTEXSETLIST res DEPS: res ON graph igraph_maximal_cliques: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, INTEGER max_size=0 + PARAMS: GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph -igraph_maximal_cliques_subset: - PARAMS: |- - GRAPH graph, VERTEX_INDICES subset, PRIMARY OUT VERTEXSET_LIST res, - OUT INTEGER no, OUTFILE outfile=NULL, INTEGER min_size=0, INTEGER max_size=0 - DEPS: subset ON graph, res ON graph - igraph_maximal_cliques_callback: PARAMS: |- GRAPH graph, CLIQUE_FUNC cliquehandler_fn, EXTRA arg, @@ -1282,13 +1026,13 @@ igraph_clique_number: igraph_weighted_cliques: PARAMS: |- - GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res, + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSETLIST res, REAL min_weight=0, REAL max_weight=0, BOOLEAN maximal=False DEPS: vertex_weights ON graph, res ON graph igraph_largest_weighted_cliques: PARAMS: |- - GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSETLIST res DEPS: vertex_weights ON graph, res ON graph igraph_weighted_clique_number: @@ -1297,16 +1041,16 @@ igraph_weighted_clique_number: igraph_independent_vertex_sets: PARAMS: |- - GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, + GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph igraph_largest_independent_vertex_sets: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + PARAMS: GRAPH graph, OUT VERTEXSETLIST res DEPS: res ON graph igraph_maximal_independent_vertex_sets: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST res + PARAMS: GRAPH graph, OUT VERTEXSETLIST res DEPS: res ON graph igraph_independence_number: @@ -1320,20 +1064,20 @@ igraph_layout_random: PARAMS: GRAPH graph, OUT MATRIX res igraph_layout_circle: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR order=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET order=ALL DEPS: order ON graph igraph_layout_star: PARAMS: |- GRAPH graph, OUT MATRIX res, VERTEX center=V(graph)[1], - OPTIONAL INDEX_VECTOR order=NULL + VECTORM1_OR_0 order=NULL DEPS: center ON graph igraph_layout_grid: - PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0 + PARAMS: GRAPH graph, OUT MATRIX res, LONGINT width=0 igraph_layout_grid_3d: - PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0, INTEGER height=0 + PARAMS: GRAPH graph, OUT MATRIX res, LONGINT width=0, LONGINT height=0 igraph_layout_fruchterman_reingold: PARAMS: |- @@ -1341,8 +1085,8 @@ igraph_layout_fruchterman_reingold: BOOLEAN use_seed=False, INTEGER niter=500, REAL start_temp=sqrt(vcount(graph)), LAYOUT_GRID grid=AUTO, EDGEWEIGHTS weights=NULL, - OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, - OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, + VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, DEPRECATED repulserad DEPS: weights ON graph @@ -1352,8 +1096,8 @@ igraph_layout_kamada_kawai: GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, INTEGER maxiter=500, REAL epsilon=0.0, REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, - OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, - OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy + VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, + VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL DEPS: weights ON graph igraph_layout_lgl: @@ -1365,17 +1109,12 @@ igraph_layout_lgl: igraph_layout_reingold_tilford: PARAMS: |- GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, - OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel + OPTIONAL VECTOR roots, OPTIONAL VECTOR rootlevel igraph_layout_reingold_tilford_circular: PARAMS: |- GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, - OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel - -igraph_roots_for_tree_layout: - PARAMS: |- - GRAPH graph, NEIMODE mode=OUT, OUT VERTEX_INDICES roots, ROOTCHOICE heuristic - DEPS: roots ON graph + OPTIONAL VECTOR roots, OPTIONAL VECTOR rootlevel igraph_layout_random_3d: PARAMS: GRAPH graph, OUT MATRIX res @@ -1389,9 +1128,9 @@ igraph_layout_fruchterman_reingold_3d: BOOLEAN use_seed=False, INTEGER niter=500, REAL start_temp=sqrt(vcount(graph)), EDGEWEIGHTS weights=NULL, - OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, - OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, - OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz, + VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, + VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, + VECTOR_OR_0 minz=NULL, VECTOR_OR_0 maxz=NULL, DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, DEPRECATED repulserad DEPS: weights ON graph @@ -1401,9 +1140,9 @@ igraph_layout_kamada_kawai_3d: GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, INTEGER maxiter=500, REAL epsilon=0.0, REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, - OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, - OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, - OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz + VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, + VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, + VECTOR_OR_0 minz=NULL, VECTOR_OR_0 maxz=NULL DEPS: weights ON graph igraph_layout_graphopt: @@ -1416,33 +1155,35 @@ igraph_layout_graphopt: igraph_layout_drl: PARAMS: |- GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights + DRL_OPTIONS options=drl_defaults$default, VECTOR_OR_0 weights=NULL, + VECTOR_BOOL_OR_0 fixed=NULL igraph_layout_drl_3d: PARAMS: |- GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights + DRL_OPTIONS options=drl_defaults$default, VECTOR_OR_0 weights=NULL, + VECTOR_BOOL_OR_0 fixed=NULL igraph_layout_merge_dla: - PARAMS: GRAPH_PTR_LIST graphs, MATRIX_LIST coords, OUT MATRIX res + PARAMS: GRAPHLIST graphs, MATRIXLIST coords, OUT MATRIX res igraph_layout_sugiyama: PARAMS: |- - GRAPH graph, OUT MATRIX res, OPTIONAL OUT GRAPH extd_graph, - OPTIONAL OUT INDEX_VECTOR extd_to_orig_eids, - OPTIONAL INDEX_VECTOR layers=NULL, - REAL hgap=1, REAL vgap=1, INTEGER maxiter=100, + GRAPH graph, OUT MATRIX res, OUT GRAPH_OR_0 extd_graph, + OUT VECTORM1_OR_0 extd_to_orig_eids, + VECTORM1_OR_0 layers=NULL, + REAL hgap=1, REAL vgap=1, LONGINT maxiter=100, EDGEWEIGHTS weights=NULL DEPS: weights ON graph igraph_layout_mds: PARAMS: |- - GRAPH graph, OUT MATRIX res, OPTIONAL MATRIX dist, INTEGER dim=2 + GRAPH graph, OUT MATRIX res, MATRIX_OR_0 dist=NULL, LONGINT dim=2 igraph_layout_bipartite: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, - REAL hgap=1, REAL vgap=1, INTEGER maxiter=100 + REAL hgap=1, REAL vgap=1, LONGINT maxiter=100 DEPS: types ON graph igraph_layout_gem: @@ -1463,73 +1204,33 @@ igraph_layout_davidson_harel: REAL weight_edge_crossings=ECROSSW, REAL weight_node_edge_dist=NEDISTW -igraph_layout_umap: - PARAMS: |- - GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, - BOOLEAN distances_are_weights=False - -igraph_layout_umap_3d: - PARAMS: |- - GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, - BOOLEAN distances_are_weights=False - -igraph_layout_umap_compute_weights: - PARAMS: |- - GRAPH graph, VECTOR distances, INOUT VECTOR weights - ####################################### # Cocitation and other similarity measures ####################################### igraph_cocitation: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL DEPS: vids ON graph igraph_bibcoupling: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL - DEPS: vids ON graph - -igraph_similarity_dice: - PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, - BOOLEAN loops=False - DEPS: vids ON graph - -igraph_similarity_dice_es: - PARAMS: |- - GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, - BOOLEAN loops=False - DEPS: es ON graph - -igraph_similarity_dice_pairs: - PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, - BOOLEAN loops=False - DEPS: pairs ON graph - -igraph_similarity_inverse_log_weighted: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL DEPS: vids ON graph igraph_similarity_jaccard: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=False DEPS: vids ON graph res, mode ON vids -igraph_similarity_jaccard_es: +igraph_similarity_dice: PARAMS: |- - GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=False - DEPS: es ON graph + DEPS: vids ON graph -igraph_similarity_jaccard_pairs: - PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, - BOOLEAN loops=False - DEPS: pairs ON graph +igraph_similarity_inverse_log_weighted: + PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL + DEPS: vids ON graph ####################################### # Community structure @@ -1537,72 +1238,67 @@ igraph_similarity_jaccard_pairs: igraph_compare_communities: PARAMS: |- - VECTOR_INT comm1, VECTOR_INT comm2, OUT REAL res, COMMCMP method=VI + VECTOR comm1, VECTOR comm2, OUT REAL res, COMMCMP method=VI igraph_community_spinglass: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT REAL modularity, - OUT REAL temperature, OUT VECTOR_INT membership, OUT VECTOR_INT csize, + GRAPH graph, VECTOR_OR_0 weights, OUT REAL modularity, + OUT REAL temperature, OUT VECTOR membership, OUT VECTOR csize, INTEGER spins=25, BOOLEAN parupdate=False, REAL starttemp=1, REAL stoptemp=0.01, REAL coolfact=0.99, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0, SPINGLASS_IMPLEMENTATION implementation=ORIG, REAL lambda=1.0 - DEPS: weights ON graph igraph_community_spinglass_single: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER vertex, - OUT VECTOR_INT community, OUT REAL cohesion, OUT REAL adhesion, + GRAPH graph, VECTOR_OR_0 weights, INTEGER vertex, OUT VECTOR community, + OUT REAL cohesion, OUT REAL adhesion, OUT INTEGER inner_links, OUT INTEGER outer_links, INTEGER spins=25, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0 - DEPS: weights ON graph igraph_community_walktrap: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER steps=4, - OUT MATRIX_INT merges, OUT VECTOR modularity, OUT VECTOR_INT membership - DEPS: weights ON graph + GRAPH graph, VECTOR weights, INT steps=4, OUT MATRIX merges, + OUT VECTOR modularity, OUT VECTOR membership igraph_community_edge_betweenness: PARAMS: |- - GRAPH graph, OUT VECTOR_INT result, OPTIONAL OUT VECTOR edge_betweenness, - OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, - OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership, - BOOLEAN directed=True, OPTIONAL EDGEWEIGHTS weights=NULL + GRAPH graph, OUT VECTOR result, OUT VECTOR edge_betweenness, + OUT MATRIX merges, OUT VECTORM1 bridges, + OUT VECTOR_OR_0 modularity, OUT VECTOR_OR_0 membership, + BOOLEAN directed=True, EDGEWEIGHTS weights=NULL DEPS: weights ON graph igraph_community_eb_get_merges: PARAMS: |- - GRAPH graph, BOOLEAN directed, EDGE_INDICES edges, OPTIONAL EDGEWEIGHTS weights, - OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, - OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership + GRAPH graph, BOOLEAN directed, VECTOR edges, EDGEWEIGHTS weights=NULL, + OUT MATRIX merges, OUT VECTORM1 bridges, + OUT VECTOR_OR_0 modularity, OUT VECTOR_OR_0 membership DEPS: weights ON graph igraph_community_fastgreedy: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT MATRIX_INT merges, - OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership - DEPS: weights ON graph + GRAPH graph, VECTOR_OR_0 weights, OUT MATRIX merges, OUT VECTOR modularity, + OUT VECTOR_OR_0 membership igraph_community_to_membership: PARAMS: |- - MATRIX_INT merges, INTEGER nodes, INTEGER steps, - OPTIONAL OUT VECTOR_INT membership, OPTIONAL OUT VECTOR_INT csize + MATRIX merges, INTEGER nodes, INTEGER steps, + OUT VECTOR membership, OUT VECTOR csize igraph_le_community_to_membership: PARAMS: |- - MATRIX_INT merges, INTEGER steps, INOUT VECTOR_INT membership, - OPTIONAL OUT VECTOR_INT csize + MATRIX merges, INTEGER steps, INOUT VECTOR membership, + OUT VECTOR_OR_0 csize igraph_modularity: PARAMS: |- - GRAPH graph, VECTOR_INT membership, OPTIONAL EDGEWEIGHTS weights=NULL, + GRAPH graph, VECTOR membership, IN VECTOR_OR_0 weights=NULL, REAL resolution=1.0, BOOLEAN directed=True, OUT REAL modularity - DEPS: weights ON graph igraph_modularity_matrix: PARAMS: |- GRAPH graph, - OPTIONAL EDGEWEIGHTS weights, + EDGEWEIGHTS weights=NULL, REAL resolution=1.0, OUT MATRIX modmat, BOOLEAN directed=True @@ -1610,103 +1306,66 @@ igraph_modularity_matrix: igraph_reindex_membership: PARAMS: |- - INOUT VECTOR_INT membership, OUT INDEX_VECTOR new_to_old, + INOUT VECTOR membership, OUT VECTOR_OR_0 new_to_old, OUT INTEGER nb_clusters igraph_community_leading_eigenvector: PARAMS: |- GRAPH graph, EDGEWEIGHTS weights=NULL, - OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT VECTOR_INT membership, + OUT MATRIX merges, OUT VECTOR membership, INTEGER steps=-1, INOUT ARPACKOPT options=arpack_defaults, - OPTIONAL OUT REAL modularity, BOOLEAN start=False, - OPTIONAL OUT VECTOR eigenvalues, - OPTIONAL OUT VECTOR_LIST eigenvectors, - OPTIONAL OUT VECTOR history, + OUT REAL modularity, BOOLEAN start=False, + OUT VECTOR_OR_0 eigenvalues, + OPTIONAL OUT VECTORLIST eigenvectors, + OUT VECTOR_OR_0 history, LEVCFUNC callback, EXTRA callback_extra igraph_community_fluid_communities: PARAMS: |- - GRAPH graph, INTEGER no_of_communities, OUT VECTOR_INT membership + GRAPH graph, INTEGER no_of_communities, + OUT VECTOR membership, OUT REAL modularity igraph_community_label_propagation: PARAMS: |- - GRAPH graph, OUT VECTOR_INT membership, NEIMODE mode=ALL, - OPTIONAL EDGEWEIGHTS weights, OPTIONAL INDEX_VECTOR initial, - OPTIONAL VECTOR_BOOL fixed + GRAPH graph, OUT VECTOR membership, EDGEWEIGHTS weights=NULL, + VECTOR_OR_0 initial=NULL, VECTOR_BOOL_OR_0 fixed=NULL, + OUT REAL modularity DEPS: weights ON graph igraph_community_multilevel: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, REAL resolution=1.0, - OUT VECTOR_INT membership, OPTIONAL OUT MATRIX_INT memberships, - OPTIONAL OUT VECTOR modularity + GRAPH graph, EDGEWEIGHTS weights=NULL, + REAL resolution=1.0, + OUT VECTOR membership, + OUT MATRIX_OR_0 memberships, OUT VECTOR_OR_0 modularity DEPS: weights ON graph igraph_community_optimal_modularity: PARAMS: |- - GRAPH graph, OUT REAL modularity, OPTIONAL OUT VECTOR_INT membership, - OPTIONAL EDGEWEIGHTS weights + GRAPH graph, OUT REAL modularity, + OUT VECTOR_OR_0 membership, + EDGEWEIGHTS weights=NULL DEPS: weights ON graph igraph_community_leiden: PARAMS: |- - GRAPH graph, OPTIONAL EDGEWEIGHTS weights, - OPTIONAL VERTEXWEIGHTS vertex_weights, - REAL resolution, REAL beta=0.01, BOOLEAN start, INTEGER n_iterations=2, - OPTIONAL INOUT VECTOR_INT membership, + GRAPH graph, EDGEWEIGHTS weights=NULL, + VERTEXWEIGHTS vertex_weights=NULL, + REAL resolution_parameter, REAL beta, + BOOLEAN start, INOUT VECTOR_OR_0 membership, OUT INTEGER nb_clusters, OUT REAL quality DEPS: weights ON graph, vertex_weights ON graph igraph_split_join_distance: PARAMS: |- - VECTOR_INT comm1, VECTOR_INT comm2, OUT INTEGER distance12, + VECTOR comm1, VECTOR comm2, OUT INTEGER distance12, OUT INTEGER distance21 -igraph_community_infomap: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS e_weights=NULL, - VERTEXWEIGHTS v_weights=NULL, INTEGER nb_trials=10, - OUT VECTOR_INT membership, OUT REAL codelength - DEPS: e_weights ON graph, v_weights ON graph - -####################################### -# Graphlets -####################################### - -igraph_graphlets: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT VERTEXSET_LIST cliques, OUT VECTOR Mu, INTEGER niter=1000 - DEPS: weights ON graph, cliques ON graph - -igraph_graphlets_candidate_basis: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT VERTEXSET_LIST cliques, OUT VECTOR thresholds - DEPS: weights ON graph, cliques ON graph - -igraph_graphlets_project: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - VERTEXSET_LIST cliques, INOUT VECTOR Muc, - BOOLEAN startMu=False, INTEGER niter=1000 - DEPS: weights ON graph - -####################################### -# Hierarchical random graphs -####################################### - igraph_hrg_fit: PARAMS: |- GRAPH graph, INOUT HRG hrg=Default, BOOLEAN start=False, - INTEGER steps=0 - -igraph_hrg_sample: - PARAMS: HRG hrg, OUT GRAPH sample - -igraph_hrg_sample_many: - PARAMS: HRG hrg, OUT GRAPH_LIST samples, INTEGER num_samples + INT steps=0 igraph_hrg_game: PARAMS: OUT GRAPH graph, HRG hrg @@ -1716,27 +1375,46 @@ igraph_hrg_dendrogram: igraph_hrg_consensus: PARAMS: |- - GRAPH graph, OUT VECTOR_INT parents, OUT VECTOR weights, + GRAPH graph, OUT VECTOR parents, OUT VECTOR weights, INOUT HRG hrg=Default, BOOLEAN start=False, - INTEGER num_samples=10000 + INT num_samples=10000 igraph_hrg_predict: PARAMS: |- - GRAPH graph, OUT VERTEX_INDICES edges, OUT VECTOR prob, + GRAPH graph, OUT VERTEXSET edges, OUT VECTOR prob, INOUT HRG hrg=Default, BOOLEAN start=False, - INTEGER num_samples=10000, INTEGER num_bins=25 + INT num_samples=10000, INT num_bins=25 DEPS: edges ON graph igraph_hrg_create: PARAMS: OUT HRG hrg, GRAPH graph, VECTOR prob DEPS: prob ON graph -igraph_hrg_resize: - PARAMS: INOUT HRG hrg, INTEGER newsize +igraph_community_infomap: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS e_weights=NULL, + VERTEXWEIGHTS v_weights=NULL, INT nb_trials=10, + OUT VECTOR membership, OUT REAL codelength + DEPS: e_weights ON graph, v_weights ON graph -igraph_hrg_size: - PARAMS: HRG hrg - RETURN: INTEGER +igraph_graphlets: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSETLIST cliques, OUT VECTOR Mu, INT niter=1000 + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_candidate_basis: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSETLIST cliques, OUT VECTOR thresholds + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_project: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + VERTEXSETLIST cliques, INOUT VECTOR Muc, + BOOLEAN startMu=False, INT niter=1000 + DEPS: weights ON graph ####################################### # Conversion @@ -1745,42 +1423,27 @@ igraph_hrg_size: igraph_get_adjacency: PARAMS: |- GRAPH graph, OUT MATRIX res, GETADJACENCY type=BOTH, - EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE - DEPS: - weights ON graph - -igraph_get_adjacency_sparse: - PARAMS: |- - GRAPH graph, OUT SPARSEMAT sparsemat, GETADJACENCY type=BOTH, - EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE - DEPS: - weights ON graph + BOOLEAN eids=False igraph_get_edgelist: - PARAMS: GRAPH graph, OUT VECTOR_INT res, BOOLEAN bycol=False - -igraph_get_stochastic: - PARAMS: |- - GRAPH graph, OUT MATRIX res, BOOLEAN column_wise=False, - EDGEWEIGHTS weights=NULL - DEPS: - weights ON graph - -igraph_get_stochastic_sparse: - PARAMS: |- - GRAPH graph, OUT SPARSEMAT sparsemat, BOOLEAN column_wise=False, - EDGEWEIGHTS weights=NULL - DEPS: - weights ON graph + PARAMS: GRAPH graph, OUT VECTOR res, BOOLEAN bycol=False igraph_to_directed: - PARAMS: INOUT GRAPH graph, TODIRECTED mode=MUTUAL + PARAMS: INOUT GRAPH graph, TODIRECTED flags=MUTUAL igraph_to_undirected: PARAMS: |- INOUT GRAPH graph, TOUNDIRECTED mode=COLLAPSE, EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default +igraph_get_stochastic: + PARAMS: GRAPH graph, OUT MATRIX res, BOOLEAN column_wise=False + +igraph_get_stochastic_sparsemat: + PARAMS: |- + GRAPH graph, OUT SPARSEMATPTR sparsemat, + BOOLEAN column_wise=False + ####################################### # Read and write foreign formats ####################################### @@ -1790,7 +1453,7 @@ igraph_read_graph_edgelist: igraph_read_graph_ncol: PARAMS: |- - OUT GRAPH graph, INFILE instream, OPTIONAL VECTOR_STR predefnames, + OUT GRAPH graph, INFILE instream, OPTIONAL STRVECTOR predefnames, BOOLEAN names=True, ADD_WEIGHTS weights=True, BOOLEAN directed=True igraph_read_graph_lgl: @@ -1802,12 +1465,12 @@ igraph_read_graph_pajek: PARAMS: OUT GRAPH graph, INFILE instream igraph_read_graph_graphml: - PARAMS: OUT GRAPH graph, INFILE instream, INTEGER index=0 + PARAMS: OUT GRAPH graph, INFILE instream, INT index=0 -igraph_read_graph_dimacs_flow: +igraph_read_graph_dimacs: PARAMS: |- OUT GRAPH graph, INFILE instream, - OPTIONAL OUT VECTOR_STR problem, OPTIONAL OUT VECTOR_INT label, + OPTIONAL OUT STRVECTOR problem, OPTIONAL OUT VECTOR label, OPTIONAL OUT INTEGER source, OPTIONAL OUT INTEGER target, OPTIONAL OUT VECTOR capacity, BOOLEAN directed=True @@ -1840,13 +1503,13 @@ igraph_write_graph_graphml: igraph_write_graph_pajek: PARAMS: GRAPH graph, OUTFILE outstream -igraph_write_graph_dimacs_flow: +igraph_write_graph_dimacs: PARAMS: |- - GRAPH graph, OUTFILE outstream, VERTEX source=0, VERTEX target=0, + GRAPH graph, OUTFILE outstream, LONGINT source=0, LONGINT target=0, VECTOR capacity igraph_write_graph_gml: - PARAMS: GRAPH graph, OUTFILE outstream, WRITE_GML_SW options=DEFAULT, VECTOR id, CSTRING creator=NULL + PARAMS: GRAPH graph, OUTFILE outstream, VECTOR id, CSTRING creator="igraph" igraph_write_graph_dot: PARAMS: GRAPH graph, OUTFILE outstream @@ -1856,18 +1519,18 @@ igraph_write_graph_dot: ####################################### igraph_motifs_randesu: - PARAMS: GRAPH graph, OUT VECTOR hist, INTEGER size=3, VECTOR cut_prob + PARAMS: GRAPH graph, OUT VECTOR hist, INT size=3, VECTOR cut_prob igraph_motifs_randesu_estimate: PARAMS: |- - GRAPH graph, OUT INTEGER est, INTEGER size=3, VECTOR cut_prob, - INTEGER sample_size, OPTIONAL VECTOR_INT sample + GRAPH graph, OUT INTEGER est, INT size=3, VECTOR cut_prob, + INTEGER sample_size, VECTOR_OR_0 sample igraph_motifs_randesu_no: - PARAMS: GRAPH graph, OUT INTEGER no, INTEGER size=3, VECTOR cut_prob + PARAMS: GRAPH graph, OUT INTEGER no, INT size=3, VECTOR cut_prob igraph_dyad_census: - PARAMS: GRAPH graph, OUT REAL mut, OUT REAL asym, OUT REAL null + PARAMS: GRAPH graph, OUT INTEGER mut, OUT INTEGER asym, OUT INTEGER null RETURN: ERROR igraph_triad_census: @@ -1875,7 +1538,7 @@ igraph_triad_census: RETURN: ERROR igraph_adjacent_triangles: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL + PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL DEPS: vids ON graph igraph_local_scan_0: @@ -1904,30 +1567,24 @@ igraph_local_scan_1_ecount_them: igraph_local_scan_k_ecount: PARAMS: |- - GRAPH graph, INTEGER k, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + GRAPH graph, INT k, OUT VECTOR res, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT DEPS: weights ON graph igraph_local_scan_k_ecount_them: PARAMS: |- - GRAPH us, GRAPH them, INTEGER k, OUT VECTOR res, + GRAPH us, GRAPH them, INT k, OUT VECTOR res, EDGEWEIGHTS weights_them=NULL, NEIMODE mode=OUT DEPS: weights_them ON them igraph_local_scan_neighborhood_ecount: PARAMS: |- GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, - VERTEXSET_LIST neighborhoods - DEPS: weights ON graph - -igraph_local_scan_subset_ecount: - PARAMS: |- - GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, - VERTEXSET_LIST subsets + VERTEXSETLIST_INT neighborhoods DEPS: weights ON graph igraph_list_triangles: - PARAMS: GRAPH graph, OUT VERTEX_INDICES res + PARAMS: GRAPH graph, OUT VERTEXSET_INT res DEPS: res ON graph ####################################### @@ -1938,25 +1595,25 @@ igraph_disjoint_union: PARAMS: OUT GRAPH res, GRAPH left, GRAPH right igraph_disjoint_union_many: - PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs + PARAMS: OUT GRAPH res, GRAPHLIST graphs igraph_union: PARAMS: |- OUT GRAPH res, GRAPH left, GRAPH right, - OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right + OUT VECTORM1 edge_map_left, OUT VECTORM1 edge_map_right DEPS: edge_map_left ON left, edge_map_right ON right igraph_union_many: - PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps + PARAMS: OUT GRAPH res, GRAPHLIST graphs, OUT VECTORLIST edgemaps igraph_intersection: PARAMS: |- OUT GRAPH res, GRAPH left, GRAPH right, - OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right + OUT VECTORM1 edge_map_left, OUT VECTORM1 edge_map_right DEPS: edge_map_left ON left, edge_map_right ON right igraph_intersection_many: - PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps + PARAMS: OUT GRAPH res, GRAPHLIST graphs, OUT VECTORLIST edgemaps igraph_difference: PARAMS: OUT GRAPH res, GRAPH orig, GRAPH sub @@ -1967,29 +1624,19 @@ igraph_complementer: igraph_compose: PARAMS: |- OUT GRAPH res, GRAPH g1, GRAPH g2, - OUT INDEX_VECTOR edge_map1, OUT INDEX_VECTOR edge_map2 + OUT VECTORM1 edge_map1, OUT VECTORM1 edge_map2 DEPS: edge_map1 ON g1, edge_map2 ON g2 -igraph_induced_subgraph_map: - PARAMS: |- - GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl, - OPTIONAL OUT INDEX_VECTOR map, OPTIONAL OUT INDEX_VECTOR invmap - DEPS: vids ON graph - ####################################### # Maximum flows, minimum cuts ####################################### -igraph_gomory_hu_tree: - PARAMS: GRAPH graph, OUT GRAPH tree, OPTIONAL OUT VECTOR flows, OPTIONAL EDGE_CAPACITY capacity - DEPS: capacity ON graph - igraph_maxflow: PARAMS: |- - GRAPH graph, OUT REAL value, OPTIONAL OUT VECTOR flow, - OUT EDGE_INDICES cut, OPTIONAL OUT VERTEX_INDICES partition1, - OPTIONAL OUT VERTEX_INDICES partition2, VERTEX source, VERTEX target, - OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats + GRAPH graph, OUT REAL value, OUT VECTOR_OR_0 flow, + OUT VECTORM1_OR_0 cut, OPTIONAL OUT VERTEXSET partition1, + OPTIONAL OUT VERTEXSET partition2, VERTEX source, VERTEX target, + EDGECAPACITY capacity=NULL, OUT MAXFLOW_STATS stats DEPS: |- capacity ON graph, source ON graph, target ON graph, partition1 ON graph, partition2 ON graph, flow ON graph, @@ -1998,55 +1645,28 @@ igraph_maxflow: igraph_maxflow_value: PARAMS: |- GRAPH graph, OUT REAL value, VERTEX source, VERTEX target, - OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats - DEPS: source ON graph, target ON graph, capacity ON graph + VECTOR_OR_0 capacity, OUT MAXFLOW_STATS stats + DEPS: source ON graph, target ON graph igraph_mincut_value: - PARAMS: GRAPH graph, OUT REAL res, OPTIONAL EDGE_CAPACITY capacity - DEPS: - capacity ON graph - -igraph_st_mincut: - PARAMS: |- - GRAPH graph, OUT REAL value, OUT EDGE_INDICES cut, - OUT VERTEX_INDICES partition1, OUT VERTEX_INDICES partition2, - VERTEX source, VERTEX target, - OPTIONAL EDGE_CAPACITY capacity - DEPS: |- - source ON graph, target ON graph, capacity ON graph, - partition1 ON graph, partition2 ON graph, cut ON graph + PARAMS: GRAPH graph, OUT REAL res, VECTOR_OR_0 capacity igraph_st_mincut_value: PARAMS: |- GRAPH graph, OUT REAL res, VERTEX source, VERTEX target, - OPTIONAL EDGE_CAPACITY capacity - DEPS: source ON graph, target ON graph, capacity ON graph + VECTOR_OR_0 capacity + DEPS: source ON graph, target ON graph igraph_mincut: PARAMS: |- - GRAPH graph, OUT REAL value, OUT VERTEX_INDICES partition1, - OUT VERTEX_INDICES partition2, OUT EDGE_INDICES cut, - OPTIONAL EDGE_CAPACITY capacity - DEPS: capacity ON graph, partition1 ON graph, partition2 ON graph, cut ON graph - -igraph_residual_graph: - PARAMS: |- - GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, - OUT EDGE_CAPACITY residual_capacity, VECTOR flow - DEPS: capacity ON graph, flow ON graph, residual_capacity ON residual - -igraph_reverse_residual_graph: - PARAMS: |- - GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, - VECTOR flow - DEPS: capacity ON graph, flow ON graph + GRAPH graph, OUT REAL value, OUT VECTORM1 partition1, + OUT VECTORM1 partition2, OUT VECTORM1 cut, VECTOR_OR_0 capacity igraph_st_mincut: PARAMS: |- - GRAPH graph, OUT REAL value, OUT EDGE_INDICES cut, - OPTIONAL OUT VERTEX_INDICES partition1, - OPTIONAL OUT VERTEX_INDICES partition2, - VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity + GRAPH graph, OUT REAL value, OUT VECTORM1_OR_0 cut, + OPTIONAL OUT VERTEXSET partition1, OPTIONAL OUT VERTEXSET partition2, + VERTEX source, VERTEX target, EDGECAPACITY capacity=NULL DEPS: |- capacity ON graph, source ON graph, target ON graph, partition1 ON graph, partition2 ON graph, cut ON graph @@ -2087,15 +1707,15 @@ igraph_cohesion: igraph_dominator_tree: PARAMS: |- - GRAPH graph, VERTEX root, OUT INDEX_VECTOR dom, - OPTIONAL OUT GRAPH domtree, OUT VERTEX_INDICES leftout, + GRAPH graph, VERTEX root, OUT VECTORM1 dom, + OUT GRAPH_OR_0 domtree, OUT VERTEXSET leftout, NEIMODE mode=OUT DEPS: root ON graph, leftout ON graph igraph_all_st_cuts: PARAMS: |- - GRAPH graph, OPTIONAL OUT EDGESET_LIST cuts, - OPTIONAL OUT VERTEXSET_LIST partition1s, + GRAPH graph, OPTIONAL OUT EDGESETLIST cuts, + OPTIONAL OUT VERTEXSETLIST partition1s, VERTEX source, VERTEX target DEPS: |- source ON graph, target ON graph, cuts ON graph, @@ -2104,38 +1724,33 @@ igraph_all_st_cuts: igraph_all_st_mincuts: PARAMS: |- GRAPH graph, OUT REAL value, - OPTIONAL OUT EDGESET_LIST cuts, - OPTIONAL OUT VERTEXSET_LIST partition1s, - VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity + OPTIONAL OUT EDGESETLIST cuts, + OPTIONAL OUT VERTEXSETLIST partition1s, + VERTEX source, VERTEX target, EDGEWEIGHTS capacity=NULL DEPS: |- capacity ON graph, source ON graph, target ON graph, cuts ON graph, partition1s ON graph -igraph_even_tarjan_reduction: - PARAMS: GRAPH graph, OUT GRAPH graphbar, OPTIONAL OUT EDGE_CAPACITY capacity - DEPS: |- - capacity ON graphbar - igraph_is_separator: - PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + PARAMS: GRAPH graph, VERTEXSET candidate, OUT BOOLEAN res DEPS: candidate ON graph igraph_is_minimal_separator: - PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + PARAMS: GRAPH graph, VERTEXSET candidate, OUT BOOLEAN res DEPS: candidate ON graph igraph_all_minimal_st_separators: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators + PARAMS: GRAPH graph, OUT VERTEXSETLIST separators DEPS: separators ON graph igraph_minimum_size_separators: - PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators + PARAMS: GRAPH graph, OUT VERTEXSETLIST separators DEPS: separators ON graph igraph_cohesive_blocks: PARAMS: |- - GRAPH graph, OUT VERTEXSET_LIST blocks, - OUT VECTOR_INT cohesion, OUT INDEX_VECTOR parent, + GRAPH graph, OUT VERTEXSETLIST blocks, + OUT VECTOR cohesion, OUT VECTORM1 parent, OUT GRAPH blockTree DEPS: blocks ON graph @@ -2144,7 +1759,7 @@ igraph_cohesive_blocks: ####################################### igraph_coreness: - PARAMS: GRAPH graph, OUT VECTOR_INT cores, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VECTOR cores, NEIMODE mode=ALL ####################################### # Graph isomorphism @@ -2157,7 +1772,7 @@ igraph_isomorphic: PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso igraph_isoclass_subgraph: - PARAMS: GRAPH graph, VECTOR_INT vids, OUT INTEGER isoclass + PARAMS: GRAPH graph, VECTOR vids, OUT INTEGER isoclass DEPS: vids ON graph igraph_isoclass_create: @@ -2171,7 +1786,7 @@ igraph_isomorphic_vf2: OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, OUT BOOLEAN iso, - OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + OUT VECTORM1_OR_0 map12, OUT VECTORM1_OR_0 map21, OPTIONAL ISOCOMPAT_FUNC node_compat_fn, OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra @@ -2195,40 +1810,25 @@ igraph_get_isomorphisms_vf2: GRAPH graph1, GRAPH graph2, VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, - OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, + OUT VECTORLIST maps, ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 -igraph_subisomorphic: - PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso - igraph_subisomorphic_vf2: PARAMS: |- GRAPH graph1, GRAPH graph2, - OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, - OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, + VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, + EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, OUT BOOLEAN iso, - OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, - OPTIONAL ISOCOMPAT_FUNC node_compat_fn, OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, + OUT VECTORM1_OR_0 map12, OUT VECTORM1_OR_0 map21, + ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 -igraph_subisomorphic_function_vf2: - PARAMS: |- - GRAPH graph1, GRAPH graph2, - OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, - OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, - OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, - ISOMORPHISM_FUNC ishohandler_fn, OPTIONAL ISOCOMPAT_FUNC node_compat_fn, - OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA arg - DEPS: |- - vertex_color1 ON graph1, vertex_color2 ON graph2, - edge_color1 ON graph1, edge_color2 ON graph2 - igraph_count_subisomorphisms_vf2: PARAMS: |- GRAPH graph1, GRAPH graph2, @@ -2245,28 +1845,31 @@ igraph_get_subisomorphisms_vf2: GRAPH graph1, GRAPH graph2, VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, - OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, + OUT VECTORLIST maps, ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 +igraph_isomorphic_34: + PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso + igraph_canonical_permutation: PARAMS: |- GRAPH graph, OPTIONAL VERTEX_COLOR colors, - OUT INDEX_VECTOR labeling, BLISSSH sh="fm", OUT BLISSINFO info + OUT VECTORM1 labeling, BLISSSH sh="fm", OUT BLISSINFO info DEPS: colors ON graph igraph_permute_vertices: - PARAMS: GRAPH graph, OUT GRAPH res, INDEX_VECTOR permutation + PARAMS: GRAPH graph, OUT GRAPH res, VECTORM1 permutation igraph_isomorphic_bliss: PARAMS: |- GRAPH graph1, GRAPH graph2, OPTIONAL VERTEX_COLOR colors1, OPTIONAL VERTEX_COLOR colors2, - OUT BOOLEAN iso, OPTIONAL OUT INDEX_VECTOR map12, - OPTIONAL OUT INDEX_VECTOR map21, BLISSSH sh="fm", - OPTIONAL OUT BLISSINFO info1, OPTIONAL OUT BLISSINFO info2 + OUT BOOLEAN iso, OUT VECTORM1_OR_0 map12, + OUT VECTORM1_OR_0 map21, BLISSSH sh="fm", + OUT BLISSINFO info1, OUT BLISSINFO info2 DEPS: colors1 ON graph1, colors2 ON graph2 igraph_automorphisms: @@ -2276,15 +1879,15 @@ igraph_automorphisms: igraph_automorphism_group: PARAMS: |- - GRAPH graph, OPTIONAL VERTEX_COLOR colors, PRIMARY OUT VERTEXSET_LIST generators, + GRAPH graph, OPTIONAL VERTEX_COLOR colors, PRIMARY OUT VERTEXSETLIST generators, BLISSSH sh="fm", OUT BLISSINFO info DEPS: colors ON graph, generators ON graph igraph_subisomorphic_lad: PARAMS: |- - GRAPH pattern, GRAPH target, OPTIONAL VERTEXSET_LIST domains, - OPTIONAL OUT BOOLEAN iso, OUT INDEX_VECTOR map, - OPTIONAL OUT VECTOR_INT_LIST maps, BOOLEAN induced, INTEGER time_limit + GRAPH pattern, GRAPH target, OPTIONAL VERTEXSETLIST domains, + OPTIONAL OUT BOOLEAN iso, OUT VECTOR_OR_0 map, + OPTIONAL OUT VECTORLIST maps, BOOLEAN induced, INT time_limit igraph_simplify_and_colorize: # Despite their names, vertex_color and edge_color are not really colors @@ -2293,8 +1896,73 @@ igraph_simplify_and_colorize: GRAPH graph, OUT GRAPH res, OUT VECTOR_INT vertex_color, OUT VECTOR_INT edge_color DEPS: vertex_color ON graph, edge_color ON graph -igraph_graph_count: - PARAMS: INTEGER n, BOOLEAN directed=False, OUT INTEGER count +####################################### +# SCG +####################################### + +igraph_scg_grouping: + PARAMS: |- + MATRIX V, OUT VECTORM1 groups, INTEGER nt, + VECTOR_OR_0 nt_vec, SCGMAT mtype=Default, + SCGALGO algo=Default, VECTOR_OR_0 p=NULL, + INTEGER maxiter=100 + +igraph_scg_semiprojectors: + PARAMS: |- + VECTORM1 groups, SCGMAT mtype=Default, + OUT MATRIX_OR_0 L, OUT MATRIX_OR_0 R, + OPTIONAL OUT SPARSEMATPTR Lsparse, OPTIONAL OUT SPARSEMATPTR Rsparse, + VECTOR_OR_0 p=NULL, SCGNORM norm=Default + +igraph_scg_norm_eps: + PARAMS: |- + MATRIX V, VECTORM1 groups, OUT VECTOR eps, + SCGMAT mtype=Default, VECTOR_OR_0 p=NULL, + SCGNORM norm=Default + +igraph_scg_adjacency: + PARAMS: |- + GRAPH_OR_0 graph, MATRIX_OR_0 matrix, + SPARSEMAT_OR_0 sparsemat, VECTOR ev, + INTEGER nt, VECTOR_OR_0 ntvec, + SCGALGO algo, INOUT VECTOR_OR_0 values, + INOUT MATRIX_OR_0 vectors, INOUT VECTORM1_OR_0 groups, + BOOLEAN use_arpack=False, INTEGER maxiter, + OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, + OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, + OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, + OPTIONAL OUT SPARSEMATPTR Rsparse + +igraph_scg_stochastic: + PARAMS: |- + GRAPH_OR_0 graph, MATRIX_OR_0 matrix, + SPARSEMAT_OR_0 sparsemat, VECTOR ev, + INTEGER nt, VECTOR_OR_0 nt_vec, + SCGALGO algo, SCGNORM norm=Default, + OPTIONAL INOUT VECTOR_COMPLEX values, + OPTIONAL INOUT MATRIX_COMPLEX vectors, + INOUT VECTORM1_OR_0 groups, INOUT VECTOR_OR_0 p, + BOOLEAN use_arpack=False, INTEGER maxiter, + OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, + OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, + OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, + OPTIONAL OUT SPARSEMATPTR Rsparse + +igraph_scg_laplacian: + PARAMS: |- + GRAPH_OR_0 graph, MATRIX_OR_0 matrix, + SPARSEMAT_OR_0 sparsemat, VECTOR ev, + INTEGER nt, VECTOR_OR_0 nt_vec, + SCGALGO algo, SCGNORM norm=Default, + SCGDIR direction=Default, + OPTIONAL INOUT VECTOR_COMPLEX values, + OPTIONAL INOUT MATRIX_COMPLEX vectors, + INOUT VECTORM1_OR_0 groups, + BOOLEAN use_arpack=False, INTEGER maxiter, + OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, + OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, + OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, + OPTIONAL OUT SPARSEMATPTR Rsparse ####################################### # Matching @@ -2302,23 +1970,23 @@ igraph_graph_count: igraph_is_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types, - INDEX_VECTOR matching, OUT BOOLEAN res + GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, + VECTOR_LONG_M1 matching, OUT BOOLEAN res DEPS: types ON graph, matching ON graph igraph_is_maximal_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types, - INDEX_VECTOR matching, OUT BOOLEAN res + GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, + VECTOR_LONG_M1 matching, OUT BOOLEAN res DEPS: types ON graph igraph_maximum_bipartite_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, OPTIONAL OUT INTEGER matching_size, OPTIONAL OUT REAL matching_weight, - OUT INDEX_VECTOR matching, - OPTIONAL EDGEWEIGHTS weights, REAL eps=.Machine$double.eps + OUT VECTOR_LONG_M1 matching, + EDGEWEIGHTS weights=NULL, REAL eps=.Machine$double.eps DEPS: types ON graph, weights ON graph ####################################### @@ -2329,7 +1997,7 @@ igraph_adjacency_spectral_embedding: PARAMS: |- GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, EIGENWHICHPOS which=ASE, BOOLEAN scaled=True, OUT MATRIX X, - OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, + OUT MATRIX_OR_0 Y, OUT VECTOR_OR_0 D, VECTOR cvec=AsmDefaultCvec, INOUT ARPACKOPT options=igraph.arpack.default DEPS: weights ON graph, cvec ON graph @@ -2339,7 +2007,7 @@ igraph_laplacian_spectral_embedding: GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, EIGENWHICHPOS which=ASE, LSETYPE type=Default, BOOLEAN scaled=True, OUT MATRIX X, - OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, + OUT MATRIX_OR_0 Y, OUT VECTOR_OR_0 D, INOUT ARPACKOPT options=igraph.arpack.default DEPS: weights ON graph, type ON graph @@ -2371,7 +2039,7 @@ igraph_power_law_fit: igraph_sir: PARAMS: |- GRAPH graph, REAL beta, REAL gamma, INTEGER no_sim=100, - OUT SIR_LIST res + OUT SIRLIST res ####################################### # Other, not graph related @@ -2381,37 +2049,14 @@ igraph_running_mean: PARAMS: VECTOR data, OUT VECTOR res, INTEGER binwidth igraph_random_sample: - PARAMS: OUT VECTOR_INT res, INTEGER l, INTEGER h, INTEGER length + PARAMS: OUT VECTOR res, REAL l, REAL h, INTEGER length igraph_convex_hull: - PARAMS: MATRIX data, OUT INDEX_VECTOR resverts, OUT MATRIX rescoords + PARAMS: MATRIX data, OUT VECTOR resverts, OUT MATRIX rescoords igraph_dim_select: PARAMS: VECTOR sv, OUT INTEGER dim -igraph_almost_equals: - PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps - RETURN: BOOLEAN - -igraph_cmp_epsilon: - PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps - RETURN: INT - -igraph_eigen_matrix: - PARAMS: |- - MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, - EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=igraph.arpack.default, - INOUT ARPACKSTORAGE storage, OUT VECTOR_COMPLEX values, OUT MATRIX_COMPLEX vectors - -igraph_eigen_matrix_symmetric: - PARAMS: |- - MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, - EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=igraph.arpack.default, - INOUT ARPACKSTORAGE storage, OUT VECTOR values, OUT MATRIX vectors - -igraph_solve_lsap: - PARAMS: MATRIX c, INTEGER n, OUT VECTOR_INT p - ####################################### # Eulerian functions ####################################### @@ -2420,25 +2065,13 @@ igraph_is_eulerian: PARAMS: GRAPH graph, OUT BOOLEAN has_path, OUT BOOLEAN has_cycle igraph_eulerian_path: - PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res + PARAMS: GRAPH graph, OPTIONAL OUT EDGESET edge_res, OPTIONAL OUT VERTEXSET vertex_res DEPS: edge_res ON graph, vertex_res ON graph igraph_eulerian_cycle: - PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res + PARAMS: GRAPH graph, OPTIONAL OUT EDGESET edge_res, OPTIONAL OUT VERTEXSET vertex_res DEPS: edge_res ON graph, vertex_res ON graph -####################################### -# Cycle bases -####################################### - -igraph_fundamental_cycles: - PARAMS: GRAPH graph, OUT EDGESET_LIST basis, OPTIONAL VERTEX start, INTEGER bfs_cutoff, EDGEWEIGHTS weights=NULL - DEPS: weights ON graph, basis ON graph, start ON graph - -igraph_minimum_cycle_basis: - PARAMS: GRAPH graph, OUT EDGESET_LIST basis, INTEGER bfs_cutoff, BOOLEAN complete, BOOLEAN use_cycle_order, EDGEWEIGHTS weights=NULL - DEPS: weights ON graph, basis ON graph - ####################################### # Trees ####################################### @@ -2447,32 +2080,24 @@ igraph_is_tree: PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX root, NEIMODE mode=OUT DEPS: root ON graph -igraph_is_forest: - PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX_INDICES roots, NEIMODE mode=OUT - DEPS: roots ON graph - igraph_from_prufer: PARAMS: OUT GRAPH graph, INDEX_VECTOR prufer igraph_to_prufer: PARAMS: GRAPH graph, OUT INDEX_VECTOR prufer -igraph_tree_from_parent_vector: - PARAMS: OUT GRAPH graph, INDEX_VECTOR parents, TREE_MODE type=OUT - igraph_minimum_spanning_tree: - PARAMS: GRAPH graph, OUT EDGE_INDICES res, EDGEWEIGHTS weights=NULL - DEPS: res ON graph, weights ON graph + PARAMS: GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph igraph_minimum_spanning_tree_unweighted: PARAMS: GRAPH graph, OUT GRAPH mst igraph_minimum_spanning_tree_prim: - PARAMS: GRAPH graph, OUT GRAPH mst, EDGEWEIGHTS weights - DEPS: weights ON graph + PARAMS: GRAPH graph, OUT GRAPH mst, VECTOR weights igraph_random_spanning_tree: - PARAMS: GRAPH graph, OUT EDGE_INDICES res, OPTIONAL VERTEX vid + PARAMS: GRAPH graph, OUT EDGESET res, OPTIONAL VERTEX vid=-1 DEPS: res ON graph, vid ON graph igraph_tree_game: @@ -2486,86 +2111,9 @@ igraph_vertex_coloring_greedy: PARAMS: GRAPH graph, OUT VERTEX_COLOR colors, GREEDY_COLORING_HEURISTIC heuristic=NEIGHBORS DEPS: colors ON graph -####################################### -# Microscopic update -####################################### - -igraph_deterministic_optimal_imitation: - PARAMS: |- - GRAPH graph, VERTEX vid, OPTIMALITY optimality=MAXIMUM, VERTEX_QTY quantities, - INOUT VECTOR_INT strategies, NEIMODE mode=OUT - DEPS: vid ON graph, quantities ON graph, strategies ON graph - -igraph_stochastic_imitation: - PARAMS: |- - GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, VERTEX_QTY quantities, - INOUT VECTOR_INT strategies, NEIMODE mode=OUT - DEPS: vid ON graph, quantities ON graph, strategies ON graph - -igraph_moran_process: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, INOUT VERTEX_QTY quantities, - INOUT VECTOR_INT strategies, NEIMODE mode=OUT - DEPS: weights ON graph, quantities ON graph, strategies ON graph - -igraph_roulette_wheel_imitation: - PARAMS: |- - GRAPH graph, VERTEX vid, BOOLEAN is_local, VERTEX_QTY quantities, - INOUT VECTOR_INT strategies, NEIMODE mode=OUT - DEPS: vid ON graph, quantities ON graph, strategies ON graph - -igraph_stochastic_imitation: - PARAMS: |- - GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, VERTEX_QTY quantities, - INOUT VECTOR_INT strategies, NEIMODE mode=OUT - DEPS: vid ON graph, quantities ON graph, strategies ON graph - ####################################### # Other, (yet) undocumented functions ####################################### igraph_convergence_degree: PARAMS: GRAPH graph, OUT VECTOR result, OUT VECTOR in, OUT VECTOR out - -igraph_has_attribute_table: - RETURN: BOOLEAN - -####################################### -# Progress, status handling -####################################### - -igraph_progress: - PARAMS: CSTRING message, REAL percent, EXTRA data - -igraph_status: - PARAMS: CSTRING message, EXTRA data - -igraph_strerror: - PARAMS: ERROR igraph_errno - RETURN: CSTRING - -####################################### -# Other functions, documented, graph related -####################################### - -igraph_expand_path_to_pairs: - PARAMS: INOUT VERTEX_INDICES path - -igraph_invalidate_cache: - PARAMS: GRAPH graph - RETURN: VOID - -igraph_vertex_path_from_edge_path: - PARAMS: |- - GRAPH graph, VERTEX start, EDGE_INDICES edge_path, - OUT VERTEX_INDICES vertex_path, NEIMODE mode=OUT - -####################################### -# Meta info -####################################### - -igraph_version: - PARAMS: |- - OPTIONAL OUT CSTRING version_string, OPTIONAL OUT INT major, - OPTIONAL OUT INT minor, OPTIONAL OUT INT subminor - RETURN: VOID diff --git a/src/vendor/cigraph/interfaces/types.yaml b/src/vendor/cigraph/interfaces/types.yaml index aeb62480e57..65634af4f81 100644 --- a/src/vendor/cigraph/interfaces/types.yaml +++ b/src/vendor/cigraph/interfaces/types.yaml @@ -28,7 +28,7 @@ COMPLEX: ERROR: # An igraph error code - CTYPE: igraph_error_t + CTYPE: int ############################################################################### ## C data types @@ -54,14 +54,6 @@ OUTFILE: # A file, already open for writing CTYPE: FILE* -DOUBLE: - # A C double - CTYPE: double - -VOID: - # C void - CTYPE: void - ############################################################################### # Vectors, matrices and other template types ############################################################################### @@ -83,6 +75,11 @@ VECTOR_INT: CTYPE: igraph_vector_int_t FLAGS: BY_REF +VECTOR_LONG: + # A vector of C long integers. Deprecated, will be removed in 0.10. + CTYPE: igraph_vector_long_t + FLAGS: BY_REF + VECTOR_BOOL: # A vector of Boolean values CTYPE: igraph_vector_bool_t @@ -92,19 +89,24 @@ VECTOR_COMPLEX: # A vector of igraph complex numbers CTYPE: igraph_vector_complex_t -VECTOR_STR: +STRVECTOR: # A vector of strings + # TODO(ntamas): maybe rename this to igraph_vector_str_t and VECTOR_STR + # for consistency? CTYPE: igraph_strvector_t FLAGS: BY_REF -VECTOR_LIST: - # A list containing vectors of floating-point numbers - CTYPE: igraph_vector_list_t +VECTORLIST: + # A vector containing pointers to vectors of floating-point numbers + CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VECTOR_INT_LIST: - # A list containing vectors of integers - CTYPE: igraph_vector_int_list_t +VECTORM1: + # A vector of integer indices that should adapt to the conventions of the + # host language (i.e. 1-based for R, Mathematica, Octave etc, 0-based for + # Python and similar). + # TODO(ntamas): should be replaced with INDEX_VECTOR + CTYPE: igraph_vector_t FLAGS: BY_REF MATRIX: @@ -112,18 +114,13 @@ MATRIX: CTYPE: igraph_matrix_t FLAGS: BY_REF -MATRIX_INT: - # A matrix of igraph integers - CTYPE: igraph_matrix_int_t - FLAGS: BY_REF - MATRIX_COMPLEX: # A matrix of igraph complex numbers CTYPE: igraph_matrix_complex_t -MATRIX_LIST: - # A list containing matrices of floating-point numbers - CTYPE: igraph_matrix_list_t +MATRIXLIST: + # A vector containing pointers to matrices of floating-point numbers + CTYPE: igraph_vector_ptr_t FLAGS: BY_REF SPARSEMAT: @@ -131,41 +128,86 @@ SPARSEMAT: CTYPE: igraph_sparsemat_t FLAGS: BY_REF +SPARSEMATPTR: + # A sparse matrix of floating-point numbers. The specialty of this type + # is that it is uninitialized upon calling the function that uses it; the + # function will initialize it instead. + # TODO(ntamas): check whether we could merge this with SPARSEMAT in 0.10 + CTYPE: igraph_sparsemat_t + FLAGS: BY_REF + +# SOMETHING_OR_0 variants -- these will be phased out in favour of the +# OPTIONAL modifier + +VECTOR_OR_0: + # A vector of floating-point numbers values where a null pointer is also a valid value + CTYPE: igraph_vector_t + FLAGS: BY_REF + +VECTOR_BOOL_OR_0: + # A vector of Boolean values where a null pointer is also a valid value + CTYPE: igraph_vector_bool_t + FLAGS: BY_REF + +VECTORM1_OR_0: + # A vector of integer indices that should adapt to the conventions of the + # host language (i.e. 1-based for R, Mathematica, Octave etc, 0-based for + # Python and similar). A null pointer is also a valid value here. + # TODO(ntamas): should be replaced with INDEX_VECTOR + CTYPE: igraph_vector_t + FLAGS: BY_REF + +VECTOR_LONG_M1: + # A vector of integer indices (as C long ints) that should adapt to the + # conventions of the host language (i.e. 1-based for R, Mathematica, Octave + # etc, 0-based for Python and similar). Deprecated, will be removed in 0.10. + # + # TODO(ntamas): should be replaced with INDEX_VECTOR + CTYPE: igraph_vector_long_t + FLAGS: BY_REF + +MATRIX_OR_0: + # A matrix of floating-point numbers values where a null pointer is also a valid value + CTYPE: igraph_matrix_t + FLAGS: BY_REF + +SPARSEMAT_OR_0: + # A sparse matrix of floating-point numbers where a null pointer is also a valid value + CTYPE: igraph_sparsemat_t + FLAGS: BY_REF + ############################################################################### -# Vertices, edges, vertex and edge selectors +# Vertices, edges, vertex and edge sequences ############################################################################### EDGE: # A single edge index CTYPE: igraph_integer_t -EDGE_INDICES: - # An integer vector containing edge indices. - CTYPE: igraph_vector_int_t - FLAGS: BY_REF - -EDGE_SELECTOR: - # An igraph edge selector. Typically used only as an input argument type. - CTYPE: igraph_es_t +EDGESET: + # An igraph edge sequence. This is an ugly hybrid type; when it is an + # IN argument in generated code, it is an igraph_es_t, but when it is an + # OUT argument, it is an igraph_vector_t. This should be fixed for 0.10. + CTYPE: + IN: igraph_es_t + OUT: igraph_vector_t VERTEX: # A single vertex index CTYPE: igraph_integer_t -VERTEX_INDICES: - # An integer vector containing vertex indices. +VERTEXSET: + # An igraph vertex sequence. This is an ugly hybrid type; when it is an + # IN argument in generated code, it is an igraph_vs_t, but when it is an + # OUT argument, it is an igraph_vector_t. This should be fixed for 0.10. + CTYPE: + IN: igraph_vs_t + OUT: igraph_vector_t + +VERTEXSET_INT: + # An igraph vertex sequence where each vertex is represented as an integer, + # hence the entire vector is an igraph_vector_int_t. CTYPE: igraph_vector_int_t - FLAGS: BY_REF - -VERTEX_INDEX_PAIRS: - # An integer vector containing pairs of vertex indices, in a flattened - # representation - CTYPE: igraph_vector_int_t - FLAGS: BY_REF - -VERTEX_SELECTOR: - # An igraph vertex selector. Typically used only as an input argument type. - CTYPE: igraph_vs_t ############################################################################### # Specialized vectors with semantic meaning @@ -177,7 +219,7 @@ BIPARTITE_TYPES: CTYPE: igraph_vector_bool_t FLAGS: BY_REF -EDGE_CAPACITY: +EDGECAPACITY: # A vector containing edge capacities (typically for max-flow algorithms) CTYPE: igraph_vector_t FLAGS: BY_REF @@ -192,40 +234,55 @@ EDGEWEIGHTS: CTYPE: igraph_vector_t FLAGS: BY_REF -EDGESET_LIST: - # A list containing vectors of igraph integers where each such +EDGESETLIST: + # A vector containing vectors of floating-point numbers where each such # vector represents a sequence of edge indices. - CTYPE: igraph_vector_int_list_t - FLAGS: BY_REF - -GRAPH_LIST: - # A list containing graphs (owned by the list itself) - CTYPE: igraph_graph_list_t + # + # TODO(ntamas): the name is slightly inconsistent because EDGESET is + # the abstract type for igraph_es_t, but an EDGESETLIST is _not_ a + # vector of igraph_es_t objects + CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -GRAPH_PTR_LIST: - # A vector containing pointers to graph objects (not owned by the vector) +GRAPHLIST: + # A vector containing pointers to graph objects CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VERTEX_QTY: +VERTEXINDEX: # A vector of floating-point numbers where each entry corresponds to - # one of the vertices in a graph and its value represents some quantity - # associated to the vertex with the same index. Higher-level interfaces may - # use this type to provide a "named vector" such that each entry can be - # indexed either by the vertex index or by the vertex name. + # one of the vertices in a graph. Higher-level interfaces may use this + # type to provide a "named vector" such that each entry can be indexed + # either by the vertex index or by the vertex name. + # + # TODO(ntamas): this is a misleading name; we should find a better name + # for this type CTYPE: igraph_vector_t FLAGS: BY_REF -SIR_LIST: +SIRLIST: # A vector containing pointers to igraph_sir_t objects CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VERTEXSET_LIST: - # A list containing vectors of igraph integers where each such +VERTEXSETLIST: + # A vector containing vectors of floating-point numbers where each such # vector represents a sequence of vertex indices. - CTYPE: igraph_vector_int_list_t + # + # TODO(ntamas): the name is slightly inconsistent because VERTEXSET is + # the abstract type for igraph_vs_t, but a VERTEXSETLIST is _not_ a + # vector of igraph_vs_t objects + CTYPE: igraph_vector_ptr_t + FLAGS: BY_REF + +VERTEXSETLIST_INT: + # A vector containing vectors of igraph integers where each such vector + # represents a sequence of vertex indices. + # + # TODO(ntamas): the name is slightly inconsistent because VERTEXSET is + # the abstract type for igraph_vs_t, but a VERTEXSETLIST is _not_ a + # vector of igraph_vs_t objects + CTYPE: igraph_vector_ptr_t FLAGS: BY_REF VERTEX_COLOR: @@ -257,6 +314,14 @@ INCLIST: CTYPE: igraph_inclist_t FLAGS: BY_REF +# SOMETHING_OR_0 variants -- these will be phased out in favour of the +# OPTIONAL modifier + +GRAPH_OR_0: + # An igraph graph where a null pointer is also a valid value + CTYPE: igraph_t + FLAGS: BY_REF + ############################################################################### # Enums ############################################################################### @@ -267,7 +332,7 @@ ADD_WEIGHTS: CTYPE: igraph_add_weights_t FLAGS: ENUM -ADJACENCY_MODE: +ADJACENCYMODE: # Enum that describes how an adjacency matrix should be constructed CTYPE: igraph_adjacency_t FLAGS: ENUM @@ -293,7 +358,7 @@ CONNECTEDNESS: CTYPE: igraph_connectedness_t FLAGS: ENUM -DEGSEQ_MODE: +DEGSEQMODE: # Enum that describes the various implementations of generating a graph # with an arbitrary degree sequence CTYPE: igraph_degseq_t @@ -311,24 +376,11 @@ EIGENWHICHPOS: CTYPE: igraph_eigen_which_position_t FLAGS: ENUM -ERDOS_RENYI_TYPE: - # Enum that says wheter a GNM (n vertices, m edges) or - # GNP (n vertices, every edge exists with probability p) - # graph is created - CTYPE: igraph_erdos_renyi_t - FLAGS: ENUM - FAS_ALGORITHM: # Enum representing feedback arc set algorithms CTYPE: igraph_fas_algorithm_t FLAGS: ENUM -FWALGORITHM: - # Enum that describes the variant of the Floyd-Warshall algorithm to use in - # Floyd-Warshall graph distances computing function - CTYPE: igraph_floyd_warshall_algorithm_t - FLAGS: ENUM - GETADJACENCY: # Enum storing how to retrieve the adjacency matrix from a graph CTYPE: igraph_get_adjacency_t @@ -339,17 +391,6 @@ GREEDY_COLORING_HEURISTIC: CTYPE: igraph_coloring_greedy_t FLAGS: ENUM -IMITATE_ALGORITHM: - # This enum controls which algorithm to use in stochastic imitation - CTYPE: igraph_imitate_algorithm_t - FLAGS: ENUM - -LAPLACIAN_NORMALIZATION: - # Enum representing the possible normalization methods of a Laplacian - # matrix - CTYPE: igraph_laplacian_normalization_t - FLAGS: ENUM - LAYOUT_GRID: # Whether to use the fast (but less accurate) grid-based version of a # layout algorithm that supports it (typically the Fruchterman-Reingold @@ -357,13 +398,6 @@ LAYOUT_GRID: CTYPE: igraph_layout_grid_t FLAGS: ENUM -LOOPS: - # Enum that describes how loop edges should be handled in undirected graphs - # in functions that support it. Possible options are: no loops, loops - # counted once, loops counted twice - CTYPE: igraph_loops_t - FLAGS: ENUM - LSETYPE: # Enum storing the possible types (definitions) of the Laplacian matrix # to use in the Laplacian spectral embedding algorithms @@ -376,16 +410,6 @@ NEIMODE: CTYPE: igraph_neimode_t FLAGS: ENUM -OPTIMALITY: - # This enum controls which algorithm to use in deterministic optimal imitation - CTYPE: igraph_optimal_t - FLAGS: ENUM - -ORDER: - # Whether ordering should be ascending or descending - CTYPE: igraph_order_t - FLAGS: ENUM - PAGERANKALGO: # Enum that describes the various implementations of the PageRank algorithm CTYPE: igraph_pagerank_algo_t @@ -408,22 +432,39 @@ RECIP: CTYPE: igraph_reciprocity_t FLAGS: ENUM -REWIRING_MODE: +REWIRINGMODE: # Enum for the rewiring modes of igraph_rewire() CTYPE: igraph_rewiring_t FLAGS: ENUM -ROOTCHOICE: - # Enum for the heuristic of igraph_roots_for_tree_layout() - CTYPE: igraph_root_choice_t - FLAGS: ENUM - RWSTUCK: # Enum that describes what igraph should do when a random walk gets stuck # in a sink vertex CTYPE: igraph_random_walk_stuck_t FLAGS: ENUM +SCGALGO: + # Enum representing the algorithms that may be used for spectral coarse + # graining of graphs + CTYPE: igraph_scg_algorithm_t + FLAGS: ENUM + +SCGDIR: + # Enum storing whether the spectral coarse graining algorithm should work + # with left or right eigenvectors + CTYPE: igraph_scg_direction_t + FLAGS: ENUM + +SCGMAT: + # Enum representing the possible types of semiprojections used in the + # spectral coarse graining algorithm + CTYPE: igraph_scg_matrix_t + FLAGS: ENUM + +SCGNORM: + CTYPE: igraph_scg_norm_t + FLAGS: ENUM + SPINCOMMUPDATE: # Enum containing update modes for the spinglass community detection # algorithm @@ -436,7 +477,7 @@ SPINGLASS_IMPLEMENTATION: CTYPE: igraph_spinglass_implementation_t FLAGS: ENUM -STAR_MODE: +STARMODE: # Enum that describes how a star graph should be constructed CTYPE: igraph_star_mode_t FLAGS: ENUM @@ -459,34 +500,21 @@ TOUNDIRECTED: CTYPE: igraph_to_undirected_t FLAGS: ENUM -TRANSITIVITY_MODE: +TRANSITIVITYMODE: # Enum that specifies how isolated vertices should be handled in transitivity # calcuations CTYPE: igraph_transitivity_mode_t FLAGS: ENUM -TREE_MODE: +TREEMODE: # Enum that describes how a tree graph should be constructed CTYPE: igraph_tree_mode_t FLAGS: ENUM VCONNNEI: - # Enum specifying what to do in vertex connectivity tests when the two - # vertices being tested are already connected CTYPE: igraph_vconn_nei_t FLAGS: ENUM -VORONOI_TIEBREAKER: - # Enum specifying what to do when two vertices are at equal distance from - # multiple generators while computing Voronoi partitionings - CTYPE: igraph_voronoi_tiebreaker_t - FLAGS: ENUM - -WHEEL_MODE: - # Enum that describes how a star graph should be constructed - CTYPE: igraph_wheel_mode_t - FLAGS: ENUM - ############################################################################### # Switches / flags / bits ############################################################################### @@ -497,11 +525,6 @@ EDGE_TYPE_SW: CTYPE: igraph_edge_type_sw_t FLAGS: BITS -WRITE_GML_SW: - # Flag bitfield that specifies how to write GML files. - CTYPE: igraph_write_gml_sw_t - FLAGS: BITS - ############################################################################### # Callbacks ############################################################################### @@ -559,10 +582,6 @@ ARPACKSTORAGE: CTYPE: igraph_arpack_storage_t FLAGS: BY_REF -ASTAR_HEURISTIC_FUNC: - # A* heuristic function - CTYPE: igraph_astar_heuristic_func_t - ATTRIBUTES: # An opaque data structure that a high-level interface may use to pass # information about graph/vertex/edge attributes to a low-level igraph diff --git a/src/vendor/cigraph/src/CMakeLists.txt b/src/vendor/cigraph/src/CMakeLists.txt index 261f2b95111..d2353841113 100644 --- a/src/vendor/cigraph/src/CMakeLists.txt +++ b/src/vendor/cigraph/src/CMakeLists.txt @@ -17,17 +17,13 @@ foreach(FORMAT dl gml lgl ncol pajek) if (BISON_VERSION VERSION_GREATER_EQUAL 3) set(bison_no_deprecated -Wno-deprecated) endif() - if (NOT FLEX_KEEP_LINE_NUMBERS) - set(bison_hide_line_numbers --no-lines) - set(flex_hide_line_numbers --noline) - endif() bison_target( ${FORMAT}_parser io/${FORMAT}-parser.y ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-parser.c - COMPILE_FLAGS "${bison_hide_line_numbers} ${bison_no_deprecated}" + COMPILE_FLAGS "--no-lines ${bison_no_deprecated}" ) flex_target( ${FORMAT}_lexer io/${FORMAT}-lexer.l ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.c - COMPILE_FLAGS "${flex_hide_line_numbers}" + COMPILE_FLAGS "--noline" DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.h ) add_flex_bison_dependency(${FORMAT}_lexer ${FORMAT}_parser) @@ -46,30 +42,28 @@ add_library( core/error.c core/estack.c core/fixed_vectorlist.c - core/genheap.c core/grid.c core/heap.c core/indheap.c core/interruption.c core/marked_queue.c core/matrix.c - core/matrix_list.c core/memory.c core/printing.c core/progress.c core/psumtree.c core/set.c core/sparsemat.c + core/spmatrix.c core/stack.c core/statusbar.c core/strvector.c core/trie.c - core/vector.c - core/vector_list.c core/vector_ptr.c + core/vector.c + math/bfgs.c math/complex.c - math/safe_intop.c math/utils.c linalg/arpack.c @@ -78,47 +72,37 @@ add_library( linalg/lapack.c random/random.c - random/rng_glibc2.c - random/rng_mt19937.c - random/rng_pcg32.c - random/rng_pcg64.c graph/adjlist.c graph/attributes.c graph/basic_query.c - graph/caching.c graph/cattributes.c - graph/graph_list.c graph/iterators.c - graph/type_common.c graph/type_indexededgelist.c graph/visitors.c constructors/adjacency.c constructors/atlas.c constructors/basic_constructors.c - constructors/circulant.c constructors/de_bruijn.c constructors/famous.c constructors/full.c - constructors/generalized_petersen.c constructors/kautz.c - constructors/lattices.c constructors/lcf.c constructors/linegraph.c constructors/prufer.c constructors/regular.c - constructors/trees.c - games/barabasi.c games/callaway_traits.c games/citations.c games/correlated.c + games/degree_sequence_vl/gengraph_box_list.cpp games/degree_sequence_vl/gengraph_degree_sequence.cpp games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp games/degree_sequence_vl/gengraph_mr-connected.cpp + games/degree_sequence_vl/gengraph_powerlaw.cpp games/degree_sequence.c games/dotproduct.c games/erdos_renyi.c @@ -140,10 +124,6 @@ add_library( centrality/centralization.c centrality/closeness.c centrality/coreness.c - centrality/eigenvector.c - centrality/hub_authority.c - centrality/pagerank.c - centrality/truss.cpp centrality/prpack.cpp cliques/cliquer_wrapper.c @@ -157,6 +137,7 @@ add_library( community/fluid.c community/infomap/infomap_FlowGraph.cc community/infomap/infomap_Greedy.cc + community/infomap/infomap_Node.cc community/infomap/infomap.cc community/label_propagation.c community/leading_eigenvector.c @@ -178,7 +159,6 @@ add_library( connectivity/separators.c flow/flow.c - flow/flow_conversion.c flow/st-cuts.c hrg/hrg_types.cc @@ -196,7 +176,6 @@ add_library( io/lgl.c io/ncol.c io/pajek.c - io/parse_utils.c ${PARSER_SOURCES} layout/circular.c @@ -220,7 +199,6 @@ add_library( layout/merge_grid.c layout/reingold_tilford.c layout/sugiyama.c - layout/umap.c operators/add_edge.c operators/complementer.c @@ -240,37 +218,37 @@ add_library( operators/union.c paths/all_shortest_paths.c - paths/astar.c paths/bellman_ford.c paths/dijkstra.c paths/distances.c paths/eulerian.c - paths/floyd_warshall.c paths/histogram.c paths/johnson.c paths/random_walk.c paths/shortest_paths.c paths/simple_paths.c - paths/sparsifier.c paths/unweighted.c - paths/voronoi.c - paths/widest_paths.c properties/basic_properties.c properties/constraint.c properties/convergence_degree.c properties/dag.c properties/degrees.c - properties/ecc.c properties/girth.c properties/loops.c properties/multiplicity.c properties/neighborhood.c - properties/perfect.c properties/spectral.c properties/trees.c properties/triangles.c + scg/scg_approximate_methods.c + scg/scg_exact_scg.c + scg/scg_kmeans.c + scg/scg_optimal_method.c + scg/scg_utils.c + scg/scg.c + isomorphism/bliss.cc isomorphism/isoclasses.c isomorphism/lad.c @@ -283,7 +261,6 @@ add_library( misc/cocitation.c misc/coloring.c misc/conversion.c - misc/cycle_bases.c misc/degree_sequence.cpp misc/embedding.c misc/feedback_arc_set.c @@ -292,9 +269,7 @@ add_library( misc/microscopic_update.c misc/mixing.c misc/motifs.c - misc/order_cycle.cpp misc/other.c - misc/power_law_fit.c misc/scan.c misc/sir.c misc/spanning_trees.c @@ -304,7 +279,6 @@ add_library( internal/lsap.c internal/qsort_r.c internal/qsort.c - internal/utils.c internal/zeroin.c version.c @@ -313,18 +287,16 @@ add_library( $,$,$>,$,> $,$,> $,$,> + $,$,> $,$,> $,$,> $,$,> $,$,> ) -# Required by Xcode new build system -add_dependencies(igraph parsersources) - # Set soname for the library -set_target_properties(igraph PROPERTIES VERSION "3.0.0") -set_target_properties(igraph PROPERTIES SOVERSION 3) +set_target_properties(igraph PROPERTIES VERSION "0.0.0") +set_target_properties(igraph PROPERTIES SOVERSION 0) # Add extra compiler definitions if needed target_compile_definitions( @@ -332,10 +304,6 @@ target_compile_definitions( PRIVATE IGRAPH_VERIFY_FINALLY_STACK=$,1,0> ) -# target_compile_options( -# # -Wconversion could be useful? -# igraph PRIVATE -Wshorten-64-to-32 -# ) # Make sure that a macro named IGRAPH_FILE_BASENAME is provided in every # compiler call so we can use these in debug messages without revealing the @@ -358,10 +326,12 @@ target_include_directories( ${PROJECT_SOURCE_DIR}/vendor # Vendored library include paths + "$<$:$>" "$<$:$>" "$<$:$>" # Include paths for dependencies + "$<$:${CXSPARSE_INCLUDE_DIRS}>" "$<$:${GLPK_INCLUDE_DIR}>" "$<$:${GMP_INCLUDE_DIR}>" "$<$:${LIBXML2_INCLUDE_DIRS}>" @@ -380,6 +350,10 @@ if(BLAS_LIBRARIES) target_link_libraries(igraph PUBLIC ${BLAS_LIBRARIES}) endif() +if(CXSPARSE_LIBRARIES) + target_link_libraries(igraph PUBLIC ${CXSPARSE_LIBRARIES}) +endif() + if(GLPK_LIBRARIES) target_link_libraries(igraph PUBLIC ${GLPK_LIBRARIES}) endif() @@ -404,9 +378,16 @@ endif() target_link_libraries( igraph PRIVATE - bliss cliquer cxsparse_vendored pcg prpack + bliss cliquer prpack ) +# Disable complex number support for CXSparse because: +# - It is necessary to compile with MSVC +# - igraph does not need complex number support from CXSparse on any platform +# This is needed here (in addition to the cxsparse_vendored target) because +# igraph may be compiled with an external CXSparse. +target_compile_definitions(igraph PRIVATE NCOMPLEX) + if (NOT BUILD_SHARED_LIBS) target_compile_definitions(igraph PRIVATE IGRAPH_STATIC) else() @@ -453,7 +434,7 @@ write_basic_package_version_file( # Define how to install the library install( - TARGETS igraph bliss cliquer cxsparse_vendored pcg prpack + TARGETS igraph bliss cliquer prpack EXPORT igraph_targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/vendor/cigraph/src/centrality/betweenness.c b/src/vendor/cigraph/src/centrality/betweenness.c index 9adff201e2b..8c9f9d80cb1 100644 --- a/src/vendor/cigraph/src/centrality/betweenness.c +++ b/src/vendor/cigraph/src/centrality/betweenness.c @@ -1,6 +1,8 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2007-2021 The igraph development team + 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 @@ -12,442 +14,22 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 . + 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_dqueue.h" #include "igraph_interface.h" -#include "igraph_memory.h" -#include "igraph_nongraph.h" #include "igraph_progress.h" #include "igraph_stack.h" +#include "igraph_dqueue.h" #include "core/indheap.h" #include "core/interruption.h" - -/* - * We provide separate implementations of single-source shortest path searches, - * one with incidence lists and one with adjacency lists. We use the implementation - * based on adjacency lists when possible (i.e. when weights are not needed) to - * avoid an expensive IGRAPH_OTHER() lookup on edge IDs. The cost of this macro - * comes from the inability of the branch predictor to predict accurately whether - * the condition in the macro will be true or not. - * - * The following four functions are very similar in their structure. If you make - * a modification to one of them, consider whether the same modification makes - * sense in the context of the remaining three functions as well. - */ - -/** - * Internal function to calculate the single source shortest paths for the - * vertex unweighted case. - * - * \param graph the graph to calculate the single source shortest paths on - * \param source the source node - * \param dist distance of each node from the source node \em plus one; - * must be filled with zeros initially - * \param nrgeo vector storing the number of geodesics from the source node - * to each node; must be filled with zeros initially - * \param stack stack in which the nodes are pushed in the order they are - * discovered during the traversal - * \param parents adjacent list that starts empty and that stores the IDs - * of the vertices that lead to a given node during the traversal - * \param adjlist the adjacency list of the graph - * \param cutoff cutoff length of shortest paths - */ -static igraph_error_t igraph_i_sspf( - igraph_integer_t source, - igraph_vector_t *dist, - igraph_real_t *nrgeo, - igraph_stack_int_t *stack, - igraph_adjlist_t *parents, - const igraph_adjlist_t *adjlist, - igraph_real_t cutoff) { - - igraph_dqueue_int_t queue; - const igraph_vector_int_t *neis; - igraph_vector_int_t *v; - igraph_integer_t nlen; - - IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); - - IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); - VECTOR(*dist)[source] = 1.0; - nrgeo[source] = 1; - - while (!igraph_dqueue_int_empty(&queue)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); - - /* Ignore vertices that are more distant than the cutoff */ - if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { - /* Reset variables if node is too distant */ - VECTOR(*dist)[actnode] = 0; - nrgeo[actnode] = 0; - igraph_vector_int_clear(igraph_adjlist_get(parents, actnode)); - continue; - } - - /* Record that we have visited this node */ - IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); - - /* Examine the neighbors of this node */ - neis = igraph_adjlist_get(adjlist, actnode); - nlen = igraph_vector_int_size(neis); - for (igraph_integer_t j = 0; j < nlen; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; - - if (VECTOR(*dist)[neighbor] == 0) { - /* We have found 'neighbor' for the first time */ - VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); - } - - if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && - (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { - /* Only add if the node is not more distant than the cutoff */ - v = igraph_adjlist_get(parents, neighbor); - IGRAPH_CHECK(igraph_vector_int_push_back(v, actnode)); - nrgeo[neighbor] += nrgeo[actnode]; - } - } - } - - igraph_dqueue_int_destroy(&queue); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * Internal function to calculate the single source shortest paths for the - * edge unweighted case. - * - * \param graph the graph to calculate the single source shortest paths on - * \param source the source node - * \param dist distance of each node from the source node \em plus one; - * must be filled with zeros initially - * \param nrgeo vector storing the number of geodesics from the source node - * to each node; must be filled with zeros initially - * \param stack stack in which the nodes are pushed in the order they are - * discovered during the traversal - * \param parents incidence list that starts empty and that stores the IDs - * of the edges that lead to a given node during the traversal - * \param inclist the incidence list of the graph - * \param cutoff cutoff length of shortest paths - */ -static igraph_error_t igraph_i_sspf_edge( - const igraph_t *graph, - igraph_integer_t source, - igraph_vector_t *dist, - igraph_real_t *nrgeo, - igraph_stack_int_t *stack, - igraph_inclist_t *parents, - const igraph_inclist_t *inclist, - igraph_real_t cutoff) { - - igraph_dqueue_int_t queue; - const igraph_vector_int_t *neis; - igraph_vector_int_t *v; - igraph_integer_t nlen; - - IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); - - IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); - VECTOR(*dist)[source] = 1.0; - nrgeo[source] = 1; - - while (!igraph_dqueue_int_empty(&queue)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); - - /* Ignore vertices that are more distant than the cutoff */ - if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { - /* Reset variables if node is too distant */ - VECTOR(*dist)[actnode] = 0; - nrgeo[actnode] = 0; - igraph_vector_int_clear(igraph_inclist_get(parents, actnode)); - continue; - } - - /* Record that we have visited this node */ - IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); - - /* Examine the neighbors of this node */ - neis = igraph_inclist_get(inclist, actnode); - nlen = igraph_vector_int_size(neis); - for (igraph_integer_t j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); - - if (VECTOR(*dist)[neighbor] == 0) { - /* We have found 'neighbor' for the first time */ - VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); - } - - if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && - (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { - /* Only add if the node is not more distant than the cutoff */ - v = igraph_inclist_get(parents, neighbor); - IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); - nrgeo[neighbor] += nrgeo[actnode]; - } - } - } - - igraph_dqueue_int_destroy(&queue); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * Internal function to calculate the single source shortest paths for the vertex - * weighted case. - * - * \param graph the graph to calculate the single source shortest paths on - * \param weights the weights of the edges - * \param source the source node - * \param dist distance of each node from the source node \em plus one; - * must be filled with zeros initially - * \param nrgeo vector storing the number of geodesics from the source node - * to each node; must be filled with zeros initially - * \param stack stack in which the nodes are pushed in the order they are - * discovered during the traversal - * \param parents adjacency list that starts empty and that stores the IDs - * of the vertices that lead to a given node during the traversal - * \param inclist the incidence list of the graph - * \param cutoff cutoff length of shortest paths - */ -static igraph_error_t igraph_i_sspf_weighted( - const igraph_t *graph, - igraph_integer_t source, - igraph_vector_t *dist, - igraph_real_t *nrgeo, - const igraph_vector_t *weights, - igraph_stack_int_t *stack, - igraph_adjlist_t *parents, - const igraph_inclist_t *inclist, - igraph_real_t cutoff) { - - const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; - - int cmp_result; - igraph_2wheap_t queue; - const igraph_vector_int_t *neis; - igraph_vector_int_t *v; - igraph_integer_t nlen; - - /* TODO: this is an O|V| step here. We could save some time by pre-allocating - * the two-way heap in the caller and re-using it here */ - IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); - IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); - - igraph_2wheap_push_with_index(&queue, source, -1.0); - VECTOR(*dist)[source] = 1.0; - nrgeo[source] = 1; - - while (!igraph_2wheap_empty(&queue)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&queue); - igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); - - /* Ignore vertices that are more distant than the cutoff */ - if (cutoff >= 0 && mindist > cutoff + 1.0) { - /* Reset variables if node is too distant */ - VECTOR(*dist)[minnei] = 0; - nrgeo[minnei] = 0; - igraph_vector_int_clear(igraph_adjlist_get(parents, minnei)); - continue; - } - - /* Record that we have visited this node */ - IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); - - /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_inclist_get(inclist, minnei); - nlen = igraph_vector_int_size(neis); - for (igraph_integer_t j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t 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 */ - v = igraph_adjlist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); - VECTOR(*v)[0] = minnei; - nrgeo[to] = nrgeo[minnei]; - VECTOR(*dist)[to] = altdist; - IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); - } else if (cmp_result < 0) { - /* This is a shorter path */ - v = igraph_adjlist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); - VECTOR(*v)[0] = minnei; - nrgeo[to] = nrgeo[minnei]; - VECTOR(*dist)[to] = altdist; - igraph_2wheap_modify(&queue, 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 */ - v = igraph_adjlist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_push_back(v, minnei)); - nrgeo[to] += nrgeo[minnei]; - } - } - } - - igraph_2wheap_destroy(&queue); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * Internal function to calculate the single source shortest paths for the edge - * weighted case. - * - * \param graph the graph to calculate the single source shortest paths on - * \param weights the weights of the edges - * \param source the source node - * \param dist distance of each node from the source node \em plus one; - * must be filled with zeros initially - * \param nrgeo vector storing the number of geodesics from the source node - * to each node; must be filled with zeros initially - * \param stack stack in which the nodes are pushed in the order they are - * discovered during the traversal - * \param parents incidence list that starts empty and that stores the IDs - * of the edges that lead to a given node during the traversal - * \param inclist the incidence list of the graph - * \param cutoff cutoff length of shortest paths - */ -static igraph_error_t igraph_i_sspf_weighted_edge( - const igraph_t *graph, - igraph_integer_t source, - igraph_vector_t *dist, - igraph_real_t *nrgeo, - const igraph_vector_t *weights, - igraph_stack_int_t *stack, - igraph_inclist_t *parents, - const igraph_inclist_t *inclist, - igraph_real_t cutoff) { - - const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; - - int cmp_result; - igraph_2wheap_t queue; - const igraph_vector_int_t *neis; - igraph_vector_int_t *v; - igraph_integer_t nlen; - - /* TODO: this is an O|V| step here. We could save some time by pre-allocating - * the two-way heap in the caller and re-using it here */ - IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); - IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); - - igraph_2wheap_push_with_index(&queue, source, -1.0); - VECTOR(*dist)[source] = 1.0; - nrgeo[source] = 1; - - while (!igraph_2wheap_empty(&queue)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&queue); - igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); - - /* Ignore vertices that are more distant than the cutoff */ - if (cutoff >= 0 && mindist > cutoff + 1.0) { - /* Reset variables if node is too distant */ - VECTOR(*dist)[minnei] = 0; - nrgeo[minnei] = 0; - igraph_vector_int_clear(igraph_inclist_get(parents, minnei)); - continue; - } - - /* Record that we have visited this node */ - IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); - - /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_inclist_get(inclist, minnei); - nlen = igraph_vector_int_size(neis); - for (igraph_integer_t j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t 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 */ - v = igraph_inclist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); - VECTOR(*v)[0] = edge; - nrgeo[to] = nrgeo[minnei]; - VECTOR(*dist)[to] = altdist; - IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); - } else if (cmp_result < 0) { - /* This is a shorter path */ - v = igraph_inclist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); - VECTOR(*v)[0] = edge; - nrgeo[to] = nrgeo[minnei]; - VECTOR(*dist)[to] = altdist; - igraph_2wheap_modify(&queue, 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 */ - v = igraph_inclist_get(parents, to); - IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); - nrgeo[to] += nrgeo[minnei]; - } - } - } - - igraph_2wheap_destroy(&queue); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_betweenness_check_weights( - const igraph_vector_t* weights, igraph_integer_t no_of_edges -) { - igraph_real_t minweight; - - if (weights) { - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length must match the number of edges.", IGRAPH_EINVAL); - } - if (no_of_edges > 0) { - minweight = igraph_vector_min(weights); - if (minweight <= 0) { - IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } else if (minweight <= IGRAPH_SHORTEST_PATH_EPSILON) { - IGRAPH_WARNING( - "Some weights are smaller than epsilon, calculations may " - "suffer from numerical precision issues." - ); - } - } - } - - return IGRAPH_SUCCESS; -} +#include "core/math.h" /***** Vertex betweenness *****/ @@ -460,7 +42,6 @@ static igraph_error_t igraph_i_betweenness_check_weights( * 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. @@ -474,7 +55,7 @@ static igraph_error_t igraph_i_betweenness_check_weights( * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex ID passed in + * \c IGRAPH_EINVVID, invalid vertex id passed in * \p vids. * * Time complexity: O(|V||E|), @@ -489,12 +70,200 @@ static igraph_error_t igraph_i_betweenness_check_weights( * of the edges in a graph. See \ref igraph_betweenness_cutoff() to * calculate the range-limited betweenness of the vertices in a graph. */ -igraph_error_t igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, +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 @@ -520,7 +289,7 @@ igraph_error_t igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex ID passed in + * \c IGRAPH_EINVVID, invalid vertex id passed in * \p vids. * * Time complexity: O(|V||E|), @@ -534,126 +303,176 @@ igraph_error_t igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, * \ref igraph_edge_betweenness_cutoff() to calculate the range-limited * edge betweenness. */ -igraph_error_t igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_adjlist_t adjlist, parents; - igraph_inclist_t inclist; - igraph_integer_t source, j, neighbor; - igraph_stack_int_t S; - igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; - igraph_vector_t dist; + 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 'igraph_real_t' instead. While a 'igraph_real_t' 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 'igraph_real_t', even - * for a 101*101 grid graph. */ - igraph_real_t *nrgeo = 0; - igraph_real_t *tmpscore; + * 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_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + igraph_adjlist_t adjlist_out, adjlist_in; + igraph_adjlist_t *adjlist_out_p, *adjlist_in_p; if (weights) { - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - } else { - IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + return igraph_i_betweenness_cutoff_weighted(graph, res, vids, directed, + cutoff, weights); } - IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); - - IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &S); - - IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - - nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for betweenness calculation."); - IGRAPH_FINALLY(igraph_free, nrgeo); - - tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for betweenness calculation."); - IGRAPH_FINALLY(igraph_free, tmpscore); - - if (igraph_vs_is_all(&vids)) { - /* result covers all vertices */ + 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 { - /* result needed only for a subset of the vertices */ - IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + 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)); } - for (source = 0; source < no_of_nodes; source++) { + 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); - /* Loop invariant that is valid at this point: - * - * - the stack S is empty - * - the 'dist' vector contains zeros only - * - the 'nrgeo' array contains zeros only - * - the 'tmpscore' array contains zeros only - * - the 'parents' adjacency list contains empty vectors only - */ + /* 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(); - /* Conduct a single-source shortest path search from the source node */ - if (weights) { - IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); - } else { - IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, cutoff)); - } - - /* Aggregate betweenness scores for the nodes we have reached in this - * traversal */ - while (!igraph_stack_int_empty(&S)) { - igraph_integer_t actnode = igraph_stack_int_pop(&S); - igraph_vector_int_t *neis = igraph_adjlist_get(&parents, actnode); - igraph_integer_t nneis = igraph_vector_int_size(neis); - igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + 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++) { - neighbor = VECTOR(*neis)[j]; - tmpscore[neighbor] += nrgeo[neighbor] * coeff; + 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 to ensure that the 'for' loop invariant will - * still be valid in the next iteration */ - - VECTOR(dist)[actnode] = 0; + /* Reset variables */ + distance[actnode] = 0; nrgeo[actnode] = 0; tmpscore[actnode] = 0; - igraph_vector_int_clear(neis); + 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 (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); - IGRAPH_VIT_NEXT(vit), j++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - VECTOR(*res)[j] = VECTOR(*tmpres)[node]; + 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); @@ -661,29 +480,254 @@ igraph_error_t igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t IGRAPH_FINALLY_CLEAN(2); } - if (!directed || !igraph_is_directed(graph)) { - igraph_vector_scale(res, 0.5); + /* 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_PROGRESS("Betweenness centrality: ", 100.0, 0); + igraph_adjlist_destroy(&adjlist_out); + igraph_adjlist_destroy(&adjlist_in); + IGRAPH_FINALLY_CLEAN(2); - IGRAPH_FREE(nrgeo); - IGRAPH_FREE(tmpscore); - igraph_vector_destroy(&dist); - igraph_stack_int_destroy(&S); - igraph_adjlist_destroy(&parents); - if (weights) { - igraph_inclist_destroy(&inclist); - } else { - igraph_adjlist_destroy(&adjlist); - } - IGRAPH_FINALLY_CLEAN(6); + 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. + */ - return IGRAPH_SUCCESS; +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 @@ -717,7 +761,7 @@ igraph_error_t igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t * 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. */ -igraph_error_t igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, +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, @@ -756,530 +800,217 @@ igraph_error_t igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *r * \sa \ref igraph_edge_betweenness() to compute the exact edge betweenness and * \ref igraph_betweenness_cutoff() to compute the range-limited vertex betweenness. */ -igraph_error_t igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_inclist_t inclist, parents; - igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; - igraph_vector_t dist; - igraph_real_t *nrgeo; - igraph_real_t *tmpscore; - igraph_integer_t source, j; - igraph_stack_int_t S; - - IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); - - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + 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; - IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); - IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + if (weights) { + return igraph_i_edge_betweenness_cutoff_weighted(graph, result, + directed, cutoff, weights); + } - IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + 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; + } - nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness calculation."); + 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, igraph_real_t); + tmpscore = IGRAPH_CALLOC(no_of_nodes, double); if (tmpscore == 0) { - IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmpscore); - IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + 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); - for (source = 0; source < no_of_nodes; source++) { + igraph_vector_null(result); - /* Loop invariant that is valid at this point: - * - * - the stack S is empty - * - the 'dist' vector contains zeros only - * - the 'nrgeo' array contains zeros only - * - the 'tmpscore' array contains zeros only - * - the 'parents' incidence list contains empty vectors only - */ + /* 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(); - /* Conduct a single-source shortest path search from the source node */ - if (weights) { - IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); - } else { - IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, cutoff)); - } - - /* Aggregate betweenness scores for the edges we have reached in this - * traversal */ - while (!igraph_stack_int_empty(&S)) { - igraph_integer_t actnode = igraph_stack_int_pop(&S); - igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); - igraph_integer_t fatv_len = igraph_vector_int_size(fatv); - igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); - for (j = 0; j < fatv_len; j++) { - igraph_integer_t fedge = VECTOR(*fatv)[j]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, fedge, actnode); - tmpscore[neighbor] += nrgeo[neighbor] * coeff; - VECTOR(*result)[fedge] += nrgeo[neighbor] * coeff; - } + nrgeo[source] = 1; + distance[source] = 0; - /* Reset variables to ensure that the 'for' loop invariant will - * still be valid in the next iteration */ - - VECTOR(dist)[actnode] = 0; - nrgeo[actnode] = 0; - tmpscore[actnode] = 0; - igraph_vector_int_clear(fatv); - } - } /* source < no_of_nodes */ - - if (!directed || !igraph_is_directed(graph)) { - igraph_vector_scale(result, 0.5); - } - - IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); - - igraph_stack_int_destroy(&S); - igraph_inclist_destroy(&inclist); - igraph_inclist_destroy(&parents); - igraph_vector_destroy(&dist); - IGRAPH_FREE(tmpscore); - IGRAPH_FREE(nrgeo); - IGRAPH_FINALLY_CLEAN(6); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup structural - * \function igraph_betweenness_subset - * \brief Betweenness centrality for a subset of source and target vertices. - * - * This function computes the subset-limited version of betweenness centrality - * by considering only those shortest paths that lie between vertices in a given - * source and target subset. - * - * \param graph The graph object. - * \param res The result of the computation, a vector containing the - * betweenness score for the subset of vertices. - * \param vids The vertices for which the subset-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 sources A vertex selector for the sources of the shortest paths taken - * into considuration in the betweenness calculation. - * \param targets A vertex selector for the targets of the shortest paths taken - * into considuration in the betweenness calculation. - * \return Error code: - * \c IGRAPH_ENOMEM, not enough memory for temporary data. - * \c IGRAPH_EINVVID, invalid vertex ID passed in \p vids, - * \p sources or \p targets - * - * Time complexity: O(|S||E|), where - * |S| is the number of vertices in the subset and - * |E| is the number of edges in the graph. - * - * \sa \ref igraph_betweenness() to calculate the exact vertex betweenness and - * \ref igraph_betweenness_cutoff() to calculate the range-limited vertex - * betweenness. - */ -igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, - const igraph_vs_t vids, igraph_bool_t directed, - const igraph_vs_t sources, const igraph_vs_t targets, - const igraph_vector_t *weights) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_sources; - igraph_integer_t no_of_processed_sources; - igraph_adjlist_t adjlist, parents; - igraph_inclist_t inclist; - igraph_integer_t source, j; - igraph_stack_int_t S; - igraph_vector_t v_tmpres, *tmpres = &v_tmpres; - igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; - igraph_integer_t father; - igraph_vector_t dist; - igraph_real_t *nrgeo; - igraph_real_t *tmpscore; - igraph_vit_t vit; - bool *is_target; - - IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); - - IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); - - if (weights) { - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - } else { - IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - } - - IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); - - IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &S); - - IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - - nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset betweenness calculation."); - IGRAPH_FINALLY(igraph_free, nrgeo); - - tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset betweenness calculation."); - IGRAPH_FINALLY(igraph_free, tmpscore); - - is_target = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset betweenness calculation."); - IGRAPH_FINALLY(igraph_free, is_target); - - IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { - is_target[IGRAPH_VIT_GET(vit)] = true; - } - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); - - if (!igraph_vs_is_all(&vids)) { - /* result needed only for a subset of the vertices */ - IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); - } else { - /* result covers all vertices */ - IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); - igraph_vector_null(res); - tmpres = res; - } + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); - IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - - for ( - no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); - !IGRAPH_VIT_END(vit); - IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ - ) { - source = IGRAPH_VIT_GET(vit); - - IGRAPH_PROGRESS( - "Betweenness centrality (subset): ", - 100.0 * no_of_processed_sources / no_of_sources, 0 - ); - IGRAPH_ALLOW_INTERRUPTION(); - - /* Loop invariant that is valid at this point: - * - * - the stack S is empty - * - the 'dist' vector contains zeros only - * - the 'nrgeo' array contains zeros only - * - the 'tmpscore' array contains zeros only - * - the 'parents' adjacency list contains empty vectors only - */ - - /* TODO: there is more room for optimization here; the single-source - * shortest path search runs until it reaches all the nodes in the - * component of the source node even if we are only interested in a - * smaller target subset. We could stop the search when all target - * nodes were reached. - */ - - /* Conduct a single-source shortest path search from the source node */ - if (weights) { - IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); - } else { - IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, -1)); - } - - /* Aggregate betweenness scores for the nodes we have reached in this - * traversal */ - while (!igraph_stack_int_empty(&S)) { - igraph_integer_t actnode = igraph_stack_int_pop(&S); - igraph_vector_int_t *fatv = igraph_adjlist_get(&parents, actnode); - igraph_integer_t fatv_len = igraph_vector_int_size(fatv); - igraph_real_t coeff; - - if (is_target[actnode]) { - coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; - } else { - coeff = tmpscore[actnode] / nrgeo[actnode]; + if (cutoff >= 0 && distance[actnode] > cutoff ) { + /* Reset variables if node is too distant */ + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + continue; } - for (j = 0; j < fatv_len; j++) { - father = VECTOR(*fatv)[j]; - tmpscore[father] += nrgeo[father] * coeff; + 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)); + } } - - if (actnode != source) { - VECTOR(*tmpres)[actnode] += tmpscore[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); + if (distance[actnode] < 1) { + distance[actnode] = 0; + tmpscore[actnode] = 0; + nrgeo[actnode] = 0; + continue; /* skip source node */ } - - /* Reset variables to ensure that the 'for' loop invariant will - * still be valid in the next iteration */ - - VECTOR(dist)[actnode] = 0; - nrgeo[actnode] = 0; + /* 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; - igraph_vector_int_clear(fatv); + nrgeo[actnode] = 0; } - } - - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); - - /* 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); + /* Ok, we've the scores for this source */ + } /* for source <= no_of_nodes */ + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); - 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - VECTOR(*res)[j] = VECTOR(*tmpres)[node]; - } + /* clean and return */ + IGRAPH_FREE(distance); + IGRAPH_FREE(nrgeo); + IGRAPH_FREE(tmpscore); + igraph_dqueue_destroy(&q); + igraph_stack_destroy(&stack); + IGRAPH_FINALLY_CLEAN(5); - igraph_vit_destroy(&vit); - igraph_vector_destroy(tmpres); + 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 (!directed || !igraph_is_directed(graph)) { - igraph_vector_scale(res, 0.5); - } - - IGRAPH_FREE(is_target); - IGRAPH_FREE(tmpscore); - IGRAPH_FREE(nrgeo); - igraph_vector_destroy(&dist); - igraph_stack_int_destroy(&S); - igraph_adjlist_destroy(&parents); - if (weights) { - igraph_inclist_destroy(&inclist); - } else { - igraph_adjlist_destroy(&adjlist); + /* 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; + } } - IGRAPH_FINALLY_CLEAN(7); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup structural - * \function igraph_edge_betweenness_subset - * \brief Edge betweenness centrality for a subset of source and target vertices. + * \function igraph_edge_betweenness_estimate + * \brief Estimated betweenness centrality of the edges. * - * This function computes the subset-limited version of edge betweenness centrality - * by considering only those shortest paths that lie between vertices in a given - * source and target subset. + * \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 res The result of the computation, vector containing the + * \param result The result of the computation, vector containing the * betweenness scores for the edges. - * \param eids The edges for which the subset-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 weight vector for weighted - * betweenness. No edge weight may be NaN. Supply a null - * pointer here for unweighted betweenness. - * \param sources A vertex selector for the sources of the shortest paths taken - * into considuration in the betweenness calculation. - * \param targets A vertex selector for the targets of the shortest paths taken - * into considuration in the betweenness calculation. + * \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. - * \c IGRAPH_EINVVID, invalid vertex ID passed in \p sources or \p targets + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. * - * Time complexity: O(|S||E|), where - * |S| is the number of vertices in the subset and - * |E| is the number of edges in the graph. + * 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_edge_betweenness_cutoff() to compute the range-limited edge betweenness. + * \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. */ -igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_bool_t directed, - const igraph_vs_t sources, const igraph_vs_t targets, +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_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_sources; - igraph_integer_t no_of_processed_sources; - igraph_inclist_t inclist, parents; - igraph_vit_t vit; - igraph_eit_t eit; - igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; - igraph_vector_t dist; - igraph_vector_t v_tmpres, *tmpres = &v_tmpres; - igraph_real_t *nrgeo; - igraph_real_t *tmpscore; - igraph_integer_t source, j; - bool *is_target; - igraph_stack_int_t S; - - IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); - - IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); - - is_target = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset edge betweenness calculation."); - IGRAPH_FINALLY(igraph_free, is_target); - - IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { - is_target[IGRAPH_VIT_GET(vit)] = true; - } - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); - - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); - IGRAPH_FINALLY(igraph_inclist_destroy, &parents); - - IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - - nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset edge betweenness calculation."); - IGRAPH_FINALLY(igraph_free, nrgeo); - - tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); - IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset edge betweenness calculation."); - IGRAPH_FINALLY(igraph_free, tmpscore); - - IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &S); - - if (!igraph_es_is_all(&eids)) { - /* result needed only for a subset of the vertices */ - IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_edges); - } else { - /* result covers all vertices */ - IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges)); - igraph_vector_null(res); - tmpres = res; - } - - IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - - for ( - no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); - !IGRAPH_VIT_END(vit); - IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ - ) { - source = IGRAPH_VIT_GET(vit); - - IGRAPH_PROGRESS( - "Edge betweenness centrality (subset): ", - 100.0 * no_of_processed_sources / no_of_sources, 0 - ); - IGRAPH_ALLOW_INTERRUPTION(); - - /* Loop invariant that is valid at this point: - * - * - the stack S is empty - * - the 'dist' vector contains zeros only - * - the 'nrgeo' array contains zeros only - * - the 'tmpscore' array contains zeros only - * - the 'parents' incidence list contains empty vectors only - */ - - /* TODO: there is more room for optimization here; the single-source - * shortest path search runs until it reaches all the nodes in the - * component of the source node even if we are only interested in a - * smaller target subset. We could stop the search when all target - * nodes were reached. - */ - - /* Conduct a single-source shortest path search from the source node */ - if (weights) { - IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); - } else { - IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, -1)); - } - - /* Aggregate betweenness scores for the nodes we have reached in this - * traversal */ - while (!igraph_stack_int_empty(&S)) { - igraph_integer_t actnode = igraph_stack_int_pop(&S); - igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); - igraph_integer_t fatv_len = igraph_vector_int_size(fatv); - igraph_real_t coeff; - - if (is_target[actnode]) { - coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; - } else { - coeff = tmpscore[actnode] / nrgeo[actnode]; - } - - for (j = 0; j < fatv_len; j++) { - igraph_integer_t father_edge = VECTOR(*fatv)[j]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, father_edge, actnode); - tmpscore[neighbor] += nrgeo[neighbor] * coeff; - VECTOR(*tmpres)[father_edge] += nrgeo[neighbor] * coeff; - } - - /* Reset variables to ensure that the 'for' loop invariant will - * still be valid in the next iteration */ - - VECTOR(dist)[actnode] = 0; - nrgeo[actnode] = 0; - tmpscore[actnode] = 0; - igraph_vector_int_clear(fatv); - } - } - - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); - - /* Keep only the requested edges */ - if (!igraph_es_is_all(&eids)) { - IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); - - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); - - for (j = 0, IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); - IGRAPH_EIT_NEXT(eit), j++) { - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - VECTOR(*res)[j] = VECTOR(*tmpres)[edge]; - } - - igraph_eit_destroy(&eit); - igraph_vector_destroy(tmpres); - IGRAPH_FINALLY_CLEAN(2); - } - - - if (!directed || !igraph_is_directed(graph)) { - igraph_vector_scale(res, 0.5); - } - - igraph_stack_int_destroy(&S); - IGRAPH_FREE(tmpscore); - IGRAPH_FREE(nrgeo); - igraph_vector_destroy(&dist); - igraph_inclist_destroy(&parents); - igraph_inclist_destroy(&inclist); - IGRAPH_FREE(is_target); - IGRAPH_FINALLY_CLEAN(7); - - return IGRAPH_SUCCESS; + 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/vendor/cigraph/src/centrality/centrality_internal.h b/src/vendor/cigraph/src/centrality/centrality_internal.h deleted file mode 100644 index 4479a3466ae..00000000000 --- a/src/vendor/cigraph/src/centrality/centrality_internal.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- 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_CENTRALITY_INTERNAL_H -#define IGRAPH_CENTRALITY_INTERNAL_H - -#include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector); - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/src/centrality/centrality_other.c b/src/vendor/cigraph/src/centrality/centrality_other.c index 6130b253b0c..55d16978426 100644 --- a/src/vendor/cigraph/src/centrality/centrality_other.c +++ b/src/vendor/cigraph/src/centrality/centrality_other.c @@ -18,9 +18,37 @@ along with this program. If not, see . */ -#include "centrality/centrality_internal.h" +#include "igraph_centrality.h" -igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector) { +#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 @@ -31,22 +59,1530 @@ igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector) { * the values are relatively large negative numbers, in which case we should * negate the eigenvector. */ - igraph_integer_t n = igraph_vector_size(vector); + long int n = igraph_vector_size(vector); igraph_real_t mi, ma; if (n == 0) { - return false; + return 0; } igraph_vector_minmax(vector, &mi, &ma); if (mi >= 0) { - return false; + return 0; } if (ma <= 0) { - return true; + 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; + igraph_bool_t negative_weights = 0; + 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; + } + + if (min < 0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative. */ + negative_weights = 1; + IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " + "will be selected, but it may not be the largest in magnitude."); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, options->n); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_LOOPS, weights)); + 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 (vector) { + igraph_real_t amax = 0; + long int which = 0; + long int i; + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (!negative_weights && 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); + VECTOR(values)[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) */ + if (! negative_weights) { + for (i = 0; i < options->n; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + } + + if (value) { + *value = VECTOR(values)[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; +} + + +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; + igraph_bool_t negative_weights = 0; + 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("The 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); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); + + if (min < 0.0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative, or even real-valued. */ + negative_weights = 1; + IGRAPH_WARNING("Negative weights in directed graph, eigenpair may 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, IGRAPH_LOOPS, 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 (vector) { + igraph_real_t amax = 0; + long int which = 0; + long int i; + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (!negative_weights && MATRIX(values, 0, 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) */ + if (! negative_weights) { + for (i = 0; i < options->n; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + + if (value) { + *value = MATRIX(values, 0, 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigenvector_centrality + * \brief 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 (strongly) connected graphs. + * Undirected 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 (indicating no edge weights), or a vector + * giving the weights of the edges. Weights should be positive to guarantee + * a meaningful result. The algorithm might produce complex numbers when some + * weights are negative and the graph is directed. 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_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist); + IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist); + IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); + 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] = 'A'; + + 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] = 'R'; + 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/vendor/cigraph/src/centrality/centralization.c b/src/vendor/cigraph/src/centrality/centralization.c index 544dfca97a0..308e9fd1ad3 100644 --- a/src/vendor/cigraph/src/centrality/centralization.c +++ b/src/vendor/cigraph/src/centrality/centralization.c @@ -21,14 +21,13 @@ #include "igraph_centrality.h" #include "igraph_interface.h" -#include "igraph_structural.h" #include "igraph_vector.h" #include "core/math.h" /** * \function igraph_centralization - * \brief Calculate the centralization score from the node level scores. + * 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 @@ -36,14 +35,12 @@ * 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, + * 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 + * 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. @@ -53,7 +50,8 @@ * 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 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. @@ -76,7 +74,7 @@ igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized) { - igraph_integer_t no_of_nodes = igraph_vector_size(scores); + long int no_of_nodes = igraph_vector_size(scores); igraph_real_t maxscore = 0.0; igraph_real_t cent = 0.0; @@ -95,13 +93,12 @@ igraph_real_t igraph_centralization(const igraph_vector_t *scores, /** * \function igraph_centralization_degree - * \brief Calculate vertex degree and graph centralization. + * 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. @@ -128,7 +125,7 @@ igraph_real_t igraph_centralization(const igraph_vector_t *scores, * score. */ -igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, +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, @@ -147,8 +144,10 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector IGRAPH_VECTOR_INIT_FINALLY(scores, 0); } - IGRAPH_CHECK(igraph_strength(graph, scores, igraph_vss_all(), mode, loops, 0)); - IGRAPH_CHECK(igraph_centralization_degree_tmax(graph, 0, mode, loops, tmax)); + 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); @@ -157,12 +156,12 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_degree_tmax - * \brief Theoretical maximum for graph centralization based on degree. + * Theoretical maximum for graph centralization based on degree * * This function returns the theoretical maximum graph centrality * based on vertex degree. @@ -184,7 +183,6 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector * 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 @@ -205,7 +203,7 @@ igraph_error_t igraph_centralization_degree(const igraph_t *graph, igraph_vector * igraph_centralization(). */ -igraph_error_t igraph_centralization_degree_tmax(const igraph_t *graph, +int igraph_centralization_degree_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_bool_t loops, @@ -247,18 +245,17 @@ igraph_error_t igraph_centralization_degree_tmax(const igraph_t *graph, } } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_betweenness - * \brief Calculate vertex betweenness and graph centralization. + * 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. @@ -282,7 +279,7 @@ igraph_error_t igraph_centralization_degree_tmax(const igraph_t *graph, * centralization score. */ -igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, +int igraph_centralization_betweenness(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t directed, igraph_real_t *centralization, @@ -313,12 +310,12 @@ igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_betweenness_tmax - * \brief Theoretical maximum for graph centralization based on betweenness. + * Theoretical maximum for graph centralization based on betweenness * * This function returns the theoretical maximum graph centrality * based on vertex betweenness. @@ -338,7 +335,6 @@ igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, * * * 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 @@ -355,7 +351,7 @@ igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, * igraph_centralization(). */ -igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, +int igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res) { @@ -374,18 +370,17 @@ igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2) / 2.0; } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_closeness - * \brief Calculate vertex closeness and graph centralization. + * 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. @@ -411,7 +406,7 @@ igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, * centralization score. */ -igraph_error_t igraph_centralization_closeness(const igraph_t *graph, +int igraph_centralization_closeness(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_real_t *centralization, @@ -432,7 +427,7 @@ igraph_error_t igraph_centralization_closeness(const igraph_t *graph, } IGRAPH_CHECK(igraph_closeness(graph, scores, NULL, NULL, igraph_vss_all(), mode, - /*weights=*/ 0, /*normalized=*/ 1)); + /*weights=*/ 0, /*normalize=*/ 1)); IGRAPH_CHECK(igraph_centralization_closeness_tmax(graph, 0, mode, tmax)); @@ -444,12 +439,12 @@ igraph_error_t igraph_centralization_closeness(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_closeness_tmax - * \brief Theoretical maximum for graph centralization based on closeness. + * Theoretical maximum for graph centralization based on closeness * * This function returns the theoretical maximum graph centrality * based on vertex closeness. @@ -487,7 +482,7 @@ igraph_error_t igraph_centralization_closeness(const igraph_t *graph, * igraph_centralization(). */ -igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, +int igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res) { @@ -508,12 +503,12 @@ igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, *res = (real_nodes - 1) * (real_nodes - 2) / (2.0 * real_nodes - 3); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_eigenvector_centrality - * \brief Calculate eigenvector centrality scores and graph centralization. + * 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); @@ -549,7 +544,7 @@ igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, * for the calculating the centralization. */ -igraph_error_t igraph_centralization_eigenvector_centrality( +int igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, @@ -593,12 +588,12 @@ igraph_error_t igraph_centralization_eigenvector_centrality( IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_centralization_eigenvector_centrality_tmax - * \brief Theoretical maximum centralization for eigenvector centrality. + * Theoretical maximum centralization for eigenvector centrality * * This function returns the theoretical maximum graph centrality * based on vertex eigenvector centrality. @@ -637,7 +632,7 @@ igraph_error_t igraph_centralization_eigenvector_centrality( * igraph_centralization(). */ -igraph_error_t igraph_centralization_eigenvector_centrality_tmax( +int igraph_centralization_eigenvector_centrality_tmax( const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, @@ -659,5 +654,5 @@ igraph_error_t igraph_centralization_eigenvector_centrality_tmax( } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/centrality/closeness.c b/src/vendor/cigraph/src/centrality/closeness.c index 408cac4536c..643532121f9 100644 --- a/src/vendor/cigraph/src/centrality/closeness.c +++ b/src/vendor/cigraph/src/centrality/closeness.c @@ -27,6 +27,7 @@ #include "core/indheap.h" #include "core/interruption.h" +#include "core/math.h" /***** Closeness centrality *****/ @@ -100,7 +101,7 @@ * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -115,17 +116,17 @@ * \ref igraph_harmonic_centrality(). * See \ref igraph_closeness_cutoff() for the range-limited closeness centrality. */ -igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_int_t *reachable_count, igraph_bool_t *all_reachable, +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 igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, +static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_int_t *reachable_count, + igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, const igraph_vs_t vids, igraph_neimode_t mode, @@ -133,22 +134,22 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, const igraph_vector_t *weights, igraph_bool_t normalized) { - /* See igraph_distances_dijkstra() for the implementation + /* See igraph_shortest_paths_dijkstra() for the implementation details and the dirty tricks. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t vit; - igraph_integer_t nodes_to_calc; + long int nodes_to_calc; igraph_lazy_inclist_t inclist; - igraph_integer_t i, j; + long int i, j; igraph_vector_t dist; - igraph_vector_int_t which; - igraph_integer_t nodes_reached; + igraph_vector_long_t which; + long int nodes_reached; igraph_real_t mindist = 0; @@ -160,7 +161,7 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, igraph_real_t minweight = igraph_vector_min(weights); if (minweight <= 0) { IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { + } else if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -171,7 +172,7 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, nodes_to_calc = IGRAPH_VIT_SIZE(vit); if (reachable_count) { - IGRAPH_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); + igraph_vector_resize(reachable_count, nodes_to_calc); } if (all_reachable) { @@ -184,15 +185,15 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - IGRAPH_CHECK(igraph_vector_int_init(&which, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &which); + 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++) { - igraph_integer_t source = IGRAPH_VIT_GET(vit); + 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; @@ -200,14 +201,11 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, nodes_reached = 0; while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&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); - igraph_integer_t nlen; + long int nlen = igraph_vector_int_size(neis); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - - nlen = igraph_vector_int_size(neis); mindist = -igraph_2wheap_delete_max(&Q); if (cutoff >= 0 && (mindist - 1.0) > cutoff) { @@ -218,8 +216,8 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, nodes_reached++; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t to = IGRAPH_OTHER(graph, edge, minnei); + 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]; @@ -231,7 +229,7 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, } else if (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { /* This is a shorter path */ VECTOR(dist)[to] = altdist; - igraph_2wheap_modify(&Q, to, -altdist); + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); } } @@ -256,16 +254,94 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, } } /* !IGRAPH_VIT_END(vit) */ - igraph_vector_int_destroy(&which); + 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; + 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 @@ -311,7 +387,7 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -325,25 +401,25 @@ static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, * \sa \ref igraph_closeness() to calculate the exact closeness centrality. */ -igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_int_t *reachable_count, igraph_bool_t *all_reachable, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t already_counted; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t already_counted; igraph_vector_int_t *neis; - igraph_integer_t i, j; - igraph_integer_t nodes_reached; + long int i, j; + long int nodes_reached; igraph_adjlist_t allneis; - igraph_integer_t actdist = 0; + long int actdist = 0; - igraph_dqueue_int_t q; + igraph_dqueue_t q; - igraph_integer_t nodes_to_calc; + long int nodes_to_calc; igraph_vit_t vit; if (weights) { @@ -357,7 +433,7 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r nodes_to_calc = IGRAPH_VIT_SIZE(vit); if (reachable_count) { - IGRAPH_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); + igraph_vector_resize(reachable_count, nodes_to_calc); } if (all_reachable) { @@ -368,8 +444,8 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r IGRAPH_ERROR("Invalid mode for closeness.", IGRAPH_EINVMODE); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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); @@ -382,17 +458,17 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r IGRAPH_VIT_NEXT(vit), i++) { nodes_reached = 0; - igraph_dqueue_int_clear(&q); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(vit))); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - VECTOR(already_counted)[IGRAPH_VIT_GET(vit)] = i + 1; + 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_int_empty(&q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&q); - actdist = igraph_dqueue_int_pop(&q); + 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!!! */ @@ -403,15 +479,14 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r /* check the neighbors */ neis = igraph_adjlist_get(&allneis, act); - igraph_integer_t nei_count = igraph_vector_int_size(neis); - for (j = 0; j < nei_count; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; + 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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } } @@ -437,8 +512,8 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r IGRAPH_PROGRESS("Closeness: ", 100.0, NULL); /* Clean */ - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&already_counted); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&already_counted); igraph_vit_destroy(&vit); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(4); @@ -449,22 +524,22 @@ igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *r /***** Harmonic centrality *****/ -static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph_vector_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vector_t already_counted; igraph_vector_int_t *neis; - igraph_integer_t i, j; + long int i, j; igraph_adjlist_t allneis; - igraph_integer_t actdist = 0; + long int actdist = 0; - igraph_dqueue_int_t q; + igraph_dqueue_t q; - igraph_integer_t nodes_to_calc; + long int nodes_to_calc; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); @@ -478,7 +553,7 @@ static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *gr } IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); @@ -490,19 +565,19 @@ static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *gr !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t source = IGRAPH_VIT_GET(vit); + long int source = IGRAPH_VIT_GET(vit); - igraph_dqueue_int_clear(&q); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + 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_int_empty(&q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&q); - actdist = igraph_dqueue_int_pop(&q); + 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!!! */ @@ -515,15 +590,14 @@ static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *gr /* check the neighbors */ neis = igraph_adjlist_get(&allneis, act); - igraph_integer_t nei_count = igraph_vector_int_size(neis); - for (j = 0; j < nei_count; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; + 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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } } } @@ -535,7 +609,7 @@ static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *gr IGRAPH_PROGRESS("Harmonic centrality: ", 100.0, NULL); /* Clean */ - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_vector_destroy(&already_counted); igraph_vit_destroy(&vit); igraph_adjlist_destroy(&allneis); @@ -545,7 +619,7 @@ static igraph_error_t igraph_i_harmonic_centrality_unweighted(const igraph_t *gr } -static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *graph, +static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, @@ -553,21 +627,21 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap igraph_bool_t normalized, igraph_real_t cutoff) { - /* See igraph_distances_dijkstra() for the implementation + /* See igraph_shortest_paths_dijkstra() for the implementation details and the dirty tricks. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t vit; - igraph_integer_t nodes_to_calc; + long int nodes_to_calc; igraph_lazy_inclist_t inclist; - igraph_integer_t i, j; + long int i, j; igraph_vector_t dist; - igraph_vector_int_t which; + igraph_vector_long_t which; igraph_real_t mindist = 0; @@ -579,7 +653,7 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap igraph_real_t minweight = igraph_vector_min(weights); if (minweight <= 0) { IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { + } else if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -595,29 +669,26 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - IGRAPH_CHECK(igraph_vector_int_init(&which, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &which); + 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++) { - igraph_integer_t source = IGRAPH_VIT_GET(vit); + 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_2wheap_max_index(&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); - igraph_integer_t nlen; - - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + long int nlen = igraph_vector_int_size(neis); - nlen = igraph_vector_int_size(neis); mindist = -igraph_2wheap_delete_max(&Q); if (cutoff >= 0 && (mindist - 1.0) > cutoff) { @@ -630,8 +701,8 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap } for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t to = IGRAPH_OTHER(graph, edge, minnei); + 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]; @@ -643,7 +714,7 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap } else if (curdist == 0 /* this means curdist is infinity */ || altdist < curdist) { /* This is a shorter path */ VECTOR(dist)[to] = altdist; - igraph_2wheap_modify(&Q, to, -altdist); + IGRAPH_CHECK(igraph_2wheap_modify(&Q, to, -altdist)); } } @@ -655,7 +726,7 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap igraph_vector_scale(res, 1.0 / (no_of_nodes - 1)); } - igraph_vector_int_destroy(&which); + igraph_vector_long_destroy(&which); igraph_vector_destroy(&dist); igraph_lazy_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); @@ -709,7 +780,7 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -721,7 +792,7 @@ static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *grap * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). */ -igraph_error_t igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, +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, @@ -786,7 +857,7 @@ igraph_error_t igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_v * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -798,7 +869,7 @@ igraph_error_t igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_v * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_degree(), \ref igraph_betweenness(). */ -igraph_error_t igraph_harmonic_centrality(const igraph_t *graph, igraph_vector_t *res, +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) { diff --git a/src/vendor/cigraph/src/centrality/coreness.c b/src/vendor/cigraph/src/centrality/coreness.c index 34b3fc42a6b..28ac5042b47 100644 --- a/src/vendor/cigraph/src/centrality/coreness.c +++ b/src/vendor/cigraph/src/centrality/coreness.c @@ -40,8 +40,6 @@ * This function implements the algorithm presented in Vladimir * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores * Decomposition of Networks. - * https://arxiv.org/abs/cs/0310049 - * * \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 @@ -57,14 +55,14 @@ * Time complexity: O(|E|), the number of edges. */ -igraph_error_t igraph_coreness(const igraph_t *graph, - igraph_vector_int_t *cores, igraph_neimode_t mode) { +int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, + igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t *bin, *vert, *pos; - igraph_integer_t maxdeg; - igraph_integer_t i, j = 0; - igraph_vector_int_t neis; + 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) { @@ -79,50 +77,50 @@ igraph_error_t igraph_coreness(const igraph_t *graph, } if (no_of_nodes == 0) { - igraph_vector_int_clear(cores); + igraph_vector_clear(cores); return IGRAPH_SUCCESS; } - vert = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + vert = IGRAPH_CALLOC(no_of_nodes, long int); if (vert == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vert); - pos = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + pos = IGRAPH_CALLOC(no_of_nodes, long int); if (pos == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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, /* loops= */ true)); - - maxdeg = igraph_vector_int_max(cores); + IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, + IGRAPH_LOOPS)); + maxdeg = (long int) igraph_vector_max(cores); - bin = IGRAPH_CALLOC(maxdeg + 1, igraph_integer_t); + bin = IGRAPH_CALLOC(maxdeg + 1, long int); if (bin == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, bin); /* degree histogram */ for (i = 0; i < no_of_nodes; i++) { - bin[VECTOR(*cores)[i] ] += 1; + bin[ (long int)VECTOR(*cores)[i] ] += 1; } /* start pointers */ j = 0; for (i = 0; i <= maxdeg; i++) { - igraph_integer_t k = bin[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[VECTOR(*cores)[i]]; + pos[i] = bin[(long int)VECTOR(*cores)[i]]; vert[pos[i]] = i; - bin[VECTOR(*cores)[i]] += 1; + bin[(long int)VECTOR(*cores)[i]] += 1; } /* correct bin */ @@ -132,18 +130,17 @@ igraph_error_t igraph_coreness(const igraph_t *graph, bin[0] = 0; /* this is the main algorithm */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg); + IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t v = vert[i]; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, omode)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); - for (j = 0; j < nei_count; j++) { - igraph_integer_t u = VECTOR(neis)[j]; + 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]) { - igraph_integer_t du = VECTOR(*cores)[u]; - igraph_integer_t pu = pos[u]; - igraph_integer_t pw = bin[du]; - igraph_integer_t w = vert[pw]; + 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; @@ -156,13 +153,12 @@ igraph_error_t igraph_coreness(const igraph_t *graph, } } - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); igraph_free(bin); igraph_free(pos); igraph_free(vert); IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/centrality/eigenvector.c b/src/vendor/cigraph/src/centrality/eigenvector.c deleted file mode 100644 index b4514e718ad..00000000000 --- a/src/vendor/cigraph/src/centrality/eigenvector.c +++ /dev/null @@ -1,549 +0,0 @@ -/* -*- 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_adjlist.h" -#include "igraph_interface.h" -#include "igraph_random.h" -#include "igraph_structural.h" -#include "igraph_topology.h" - -#include "centrality/centrality_internal.h" - -#include - -/* Multiplies vector 'from' by the unweighted adjacency matrix and stores the result in 'to'. */ -static igraph_error_t adjmat_mul_unweighted(igraph_real_t *to, const igraph_real_t *from, - int n, void *extra) { - igraph_adjlist_t *adjlist = extra; - igraph_vector_int_t *neis; - igraph_integer_t 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++) { - igraph_integer_t nei = VECTOR(*neis)[j]; - to[i] += from[nei]; - } - } - - return IGRAPH_SUCCESS; -} - -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; - -/* Multiplies vector 'from' by the weighted adjacency matrix and stores the result in 'to'. */ -static igraph_error_t adjmat_mul_weighted(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; - igraph_integer_t 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++) { - igraph_integer_t edge = VECTOR(*edges)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); - igraph_real_t w = VECTOR(*weights)[edge]; - to[i] += w * from[nei]; - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t 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; - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_bool_t negative_weights = false; - - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); - } - - if (igraph_ecount(graph) == 0) { - /* special case: empty graph */ - if (value) { - *value = 0; - } - if (vector) { - IGRAPH_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " - "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, - igraph_vector_size(weights), igraph_ecount(graph)); - } - /* Safe to call minmax, ecount == 0 case was caught earlier */ - igraph_vector_minmax(weights, &min, &max); - if (min == 0 && max == 0) { - /* special case: all weights are zeros */ - if (value) { - *value = 0; - } - if (vector) { - IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); - igraph_vector_fill(vector, 1); - } - return IGRAPH_SUCCESS; - } - - if (min < 0) { - /* When there are negative weights, the eigenvalue and the eigenvector are no - * longer guaranteed to be non-negative. */ - negative_weights = 1; - IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " - "will be selected, but it may not be the largest in magnitude."); - } - } - - IGRAPH_VECTOR_INIT_FINALLY(&values, 0); - IGRAPH_MATRIX_INIT_FINALLY(&vectors, no_of_nodes, 1); - - IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), - IGRAPH_ALL, IGRAPH_LOOPS, weights)); - RNG_BEGIN(); - for (i = 0; i < no_of_nodes; 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 = (int) no_of_nodes; - 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(adjmat_mul_unweighted, - &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(adjmat_mul_weighted, - &data, options, 0, &values, &vectors)); - - igraph_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(1); - } - - if (vector) { - igraph_real_t amax = 0; - igraph_integer_t which = 0; - IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); - - if (!negative_weights && 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); - VECTOR(values)[0] = 0; - } else { - for (i = 0; i < no_of_nodes; 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) */ - if (! negative_weights) { - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(*vector)[i] < 0) { - VECTOR(*vector)[i] = 0; - } - } - } - } - } - - if (value) { - *value = VECTOR(values)[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; -} - -static igraph_error_t 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; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; - igraph_bool_t negative_weights = false; - - if (igraph_ecount(graph) == 0) { - /* special case: empty graph */ - if (value) { - *value = 0; - } - if (vector) { - IGRAPH_CHECK(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_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " - "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, - igraph_vector_size(weights), igraph_ecount(graph)); - } - if (igraph_is_directed(graph)) { - IGRAPH_WARNING("Weighted directed graph in eigenvector centrality"); - } - - /* Safe to call minmax, ecount == 0 case was caught earlier */ - igraph_vector_minmax(weights, &min, &max); - - if (min < 0.0) { - /* When there are negative weights, the eigenvalue and the eigenvector are no - * longer guaranteed to be non-negative, or even real-valued. */ - negative_weights = true; - IGRAPH_WARNING("Negative weights in directed graph, eigenpair may be complex."); - } - if (min == 0.0 && max == 0.0) { - /* special case: all weights are zeros */ - if (value) { - *value = 0; - } - if (vector) { - IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); - igraph_vector_fill(vector, 1); - } - return IGRAPH_SUCCESS; - } - } - - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); - } - - options->n = (int) no_of_nodes; - 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, no_of_nodes, 1); - - IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); - IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), - IGRAPH_IN, IGRAPH_LOOPS, weights)); - RNG_BEGIN(); - for (i = 0; i < no_of_nodes; 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(adjmat_mul_unweighted, - &adjlist, options, NULL, &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(adjmat_mul_weighted, - &data, options, NULL, &values, &vectors)); - - igraph_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(1); - } - - if (vector) { - igraph_real_t amax = 0; - igraph_integer_t which = 0; - - IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); - - if (!negative_weights && 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 < no_of_nodes; 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) */ - if (! negative_weights) { - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(*vector)[i] < 0) { - VECTOR(*vector)[i] = 0; - } - } - } - } - - if (value) { - *value = MATRIX(values, 0, 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 IGRAPH_SUCCESS; -} - -/** - * \function igraph_eigenvector_centrality - * \brief 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 (strongly) connected graphs. - * Undirected 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. - * - * - * When working with directed graphs, consider using hub and authority - * scores instead, see \ref igraph_hub_and_authority_scores(). - * - * \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 (indicating no edge weights), or a vector - * giving the weights of the edges. Weights should be positive to guarantee - * a meaningful result. The algorithm might produce complex numbers when some - * weights are negative and the graph is directed. In this case only - * the real part is reported. - * \param options Options to ARPACK. See \ref igraph_arpack_options_t - * for details. Supply \c NULL here to use the defaults. 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. - * \ref igraph_hub_and_authority_scores() for a similar pair of measures - * intended for directed graphs. - * - * \example examples/simple/eigenvector_centrality.c - */ - -igraph_error_t 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 (!options) { - options = igraph_arpack_options_get_default(); - } - - 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); - } -} diff --git a/src/vendor/cigraph/src/centrality/hub_authority.c b/src/vendor/cigraph/src/centrality/hub_authority.c deleted file mode 100644 index 896e848127d..00000000000 --- a/src/vendor/cigraph/src/centrality/hub_authority.c +++ /dev/null @@ -1,495 +0,0 @@ -/* -*- 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_adjlist.h" -#include "igraph_interface.h" -#include "igraph_structural.h" -#include "igraph_blas.h" - -#include "centrality/centrality_internal.h" - -#include - -/* 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; - -static igraph_error_t igraph_i_kleinberg_unweighted_hub_to_auth( - igraph_integer_t n, igraph_vector_t *to, const igraph_real_t *from, - igraph_adjlist_t *in) { - igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; - - for (i = 0; i < n; i++) { - neis = igraph_adjlist_get(in, i); - nlen = igraph_vector_int_size(neis); - VECTOR(*to)[i] = 0.0; - for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; - VECTOR(*to)[i] += from[nei]; - } - } - return IGRAPH_SUCCESS; -} - -/* ARPACK auxiliary routine for the unweighted HITS algorithm */ -static igraph_error_t 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 *out = data->out; - igraph_vector_t *tmp = data->tmp; - igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; - - igraph_i_kleinberg_unweighted_hub_to_auth(n, tmp, from, data->in); - - 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++) { - igraph_integer_t nei = VECTOR(*neis)[j]; - to[i] += VECTOR(*tmp)[nei]; - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_kleinberg_weighted_hub_to_auth(igraph_integer_t n, - igraph_vector_t *to, const igraph_real_t *from, igraph_inclist_t *in, - const igraph_t *g, const igraph_vector_t *weights) { - igraph_vector_int_t *neis; - igraph_integer_t nlen, i, j; - for (i = 0; i < n; i++) { - neis = igraph_inclist_get(in, i); - nlen = igraph_vector_int_size(neis); - VECTOR(*to)[i] = 0.0; - for (j = 0; j < nlen; j++) { - igraph_integer_t nei_edge = VECTOR(*neis)[j]; - igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); - VECTOR(*to)[i] += from[nei] * VECTOR(*weights)[nei_edge]; - } - } - return IGRAPH_SUCCESS; -} - -/* ARPACK auxiliary routine for the weighted HITS algorithm */ -static igraph_error_t 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 *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; - igraph_integer_t i, j, nlen; - - igraph_i_kleinberg_weighted_hub_to_auth(n, tmp, from, data->in, g, weights); - - 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++) { - igraph_integer_t nei_edge = VECTOR(*neis)[j]; - igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); - to[i] += VECTOR(*tmp)[nei] * VECTOR(*weights)[nei_edge]; - } - } - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_hub_and_authority_scores - * \brief Kleinberg's hub and authority scores. - * - * Hub and authority scores are a generalization of the ideas behind - * eigenvector centrality to directed graphs. The authority score of - * a vertex is proportional to the sum of the hub scores of vertices - * that point to it. Conversely, the hub score of a vertex is proportional - * to the sum of authority scores of vertices that it points to. - * - * - * The hub and authority scores of the vertices are defined as the principal - * eigenvectors of A A^T and A^T A, respectively, - * where A is the adjacency matrix of the graph and A^T - * is its transposed. - * - * - * The concept of hub and authority scores were developed for \em directed graphs. - * In undirected graphs, both the hub and authority scores are equal to the - * eigenvector centrality, which can be computed using - * \ref igraph_eigenvector_centrality(). - * - * - * 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). - * https://doi.org/10.1145/324133.324140 - * Also appears as IBM Research Report RJ 10076, May - * 1997. - * - * \param graph The input graph. Can be directed and undirected. - * \param hub_vector Pointer to an initialized vector, the hub scores are - * stored here. If a null pointer then it is ignored. - * \param authority_vector Pointer to an initialized vector, the authority scores are - * stored here. If a null pointer then it is ignored. - * \param value If not a null pointer then the eigenvalue - * corresponding to the calculated eigenvectors 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 (meaning 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. Supply \c NULL here to use the defaults. 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(), \ref igraph_authority_score() - * for the separate calculations, - * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), - * \ref igraph_eigenvector_centrality() for a similar measure intended - * for undirected graphs. - */ -igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, - igraph_vector_t *hub_vector, igraph_vector_t *authority_vector, - igraph_real_t *value, igraph_bool_t scale, - const igraph_vector_t *weights, igraph_arpack_options_t *options) { - - igraph_adjlist_t inadjlist, outadjlist; - igraph_inclist_t ininclist, outinclist; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_t tmp; - igraph_vector_t values; - igraph_matrix_t vectors; - igraph_i_kleinberg_data_t extra; - igraph_i_kleinberg_data2_t extra2; - igraph_vector_t *my_hub_vector_p; - igraph_vector_t my_hub_vector; - - - if (igraph_ecount(graph) == 0) { - /* special case: empty graph */ - if (value) { - *value = igraph_ecount(graph) ? 1.0 : IGRAPH_NAN; - } - if (hub_vector) { - IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); - igraph_vector_fill(hub_vector, 1.0); - } - if (authority_vector) { - IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); - igraph_vector_fill(authority_vector, 1.0); - } - return IGRAPH_SUCCESS; - } - - if (weights) { - igraph_real_t min, max; - - if (igraph_vector_size(weights) != igraph_ecount(graph)) { - IGRAPH_ERRORF( - "Weights vector length (%" IGRAPH_PRId ") should match number of " - "edges (%" IGRAPH_PRId ") when calculating " - "hub or authority scores.", - IGRAPH_EINVAL, - igraph_vector_size(weights), - igraph_ecount(graph)); - } - /* Safe to call minmax, ecount == 0 case was caught earlier */ - igraph_vector_minmax(weights, &min, &max); - if (min == 0 && max == 0) { - /* special case: all weights are zeros */ - if (value) { - *value = IGRAPH_NAN; - } - if (hub_vector) { - IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); - igraph_vector_fill(hub_vector, 1); - } - if (authority_vector) { - IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); - igraph_vector_fill(authority_vector, 1); - } - return IGRAPH_SUCCESS; - } - } - - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph has too many vertices for ARPACK", IGRAPH_EOVERFLOW); - } - - if (!options) { - options = igraph_arpack_options_get_default(); - } - - options->n = no_of_nodes; - 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 (weights == NULL) { - IGRAPH_CHECK(igraph_adjlist_init(graph, &inadjlist, IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &inadjlist); - IGRAPH_CHECK(igraph_adjlist_init(graph, &outadjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &outadjlist); - } else { - IGRAPH_CHECK(igraph_inclist_init(graph, &ininclist, IGRAPH_IN, IGRAPH_LOOPS_TWICE)); - IGRAPH_FINALLY(igraph_inclist_destroy, &ininclist); - IGRAPH_CHECK(igraph_inclist_init(graph, &outinclist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); - IGRAPH_FINALLY(igraph_inclist_destroy, &outinclist); - } - - IGRAPH_CHECK(igraph_strength(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0, 0)); - for (igraph_integer_t 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] = 'A'; - - if (weights == NULL) { - IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra, - options, 0, &values, &vectors)); - } else { - IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2, - options, 0, &values, &vectors)); - } - - - if (value) { - *value = VECTOR(values)[0]; - } - - if (hub_vector || authority_vector) { - if (!hub_vector) { - IGRAPH_VECTOR_INIT_FINALLY(&my_hub_vector, options->n); - my_hub_vector_p = &my_hub_vector; - } else { - my_hub_vector_p = hub_vector; - } - igraph_real_t amax = 0; - igraph_integer_t which = 0; - - IGRAPH_CHECK(igraph_vector_resize(my_hub_vector_p, options->n)); - for (igraph_integer_t i = 0; i < options->n; i++) { - igraph_real_t tmp; - VECTOR(*my_hub_vector_p)[i] = MATRIX(vectors, i, 0); - tmp = fabs(VECTOR(*my_hub_vector_p)[i]); - if (tmp > amax) { - amax = tmp; - which = i; - } - } - if (scale && amax != 0) { - igraph_vector_scale(my_hub_vector_p, 1 / VECTOR(*my_hub_vector_p)[which]); - } else if (igraph_i_vector_mostly_negative(my_hub_vector_p)) { - igraph_vector_scale(my_hub_vector_p, -1.0); - } - - /* Correction for numeric inaccuracies (eliminating -0.0) */ - for (igraph_integer_t i = 0; i < options->n; i++) { - if (VECTOR(*my_hub_vector_p)[i] < 0) { - VECTOR(*my_hub_vector_p)[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); - - if (authority_vector) { - igraph_real_t norm; - IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); - igraph_vector_null(authority_vector); - if (weights == NULL) { - igraph_i_kleinberg_unweighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &inadjlist); - } else { - igraph_i_kleinberg_weighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &ininclist, graph, weights); - } - if (!scale) { - norm = 1.0 / igraph_blas_dnrm2(authority_vector); - } else { - norm = 1.0 / igraph_vector_max(authority_vector); - } - igraph_vector_scale(authority_vector, norm); - } - - if (!hub_vector && authority_vector) { - igraph_vector_destroy(&my_hub_vector); - IGRAPH_FINALLY_CLEAN(1); - } - if (weights == NULL) { - igraph_adjlist_destroy(&outadjlist); - igraph_adjlist_destroy(&inadjlist); - IGRAPH_FINALLY_CLEAN(2); - } else { - igraph_inclist_destroy(&outinclist); - igraph_inclist_destroy(&ininclist); - IGRAPH_FINALLY_CLEAN(2); - } - igraph_vector_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_hub_score - * \brief Kleinberg's hub scores. - * - * \deprecated-by igraph_hub_and_authority_scores 0.10.5 - * - * 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_hub_and_authority_scores() to compute - * hub and authrotity scores efficiently at the same time, - * \ref igraph_authority_score() for the companion measure, - * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), - * \ref igraph_eigenvector_centrality() for similar measures. - */ - -igraph_error_t 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_hub_and_authority_scores(graph, vector, NULL, value, scale, weights, options); -} - -/** - * \function igraph_authority_score - * \brief Kleinberg's authority scores. - * - * \deprecated-by igraph_hub_and_authority_scores 0.10.5 - * - * 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_and_authority_scores() to compute - * hub and authrotity scores efficiently at the same time, - * \ref igraph_hub_score() for the companion measure, - * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), - * \ref igraph_eigenvector_centrality() for similar measures. - */ - -igraph_error_t 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_hub_and_authority_scores(graph, NULL, vector, value, scale, weights, options); -} diff --git a/src/vendor/cigraph/src/centrality/pagerank.c b/src/vendor/cigraph/src/centrality/pagerank.c deleted file mode 100644 index a0615bbd374..00000000000 --- a/src/vendor/cigraph/src/centrality/pagerank.c +++ /dev/null @@ -1,708 +0,0 @@ -/* -*- 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_adjlist.h" -#include "igraph_interface.h" -#include "igraph_random.h" -#include "igraph_structural.h" - -#include "centrality/prpack_internal.h" - -#include - -static igraph_error_t 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); - -typedef struct { - const igraph_t *graph; - igraph_adjlist_t *adjlist; - igraph_real_t damping; - igraph_vector_t *outdegree; - igraph_vector_t *tmp; - igraph_vector_t *reset; -} pagerank_data_t; - -typedef struct { - 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; -} pagerank_data_weighted_t; - -/* The two pagerank_operator functions below update the probabilities of a random walker - * being in each of the vertices after one step of the walk. */ - -static igraph_error_t pagerank_operator_unweighted(igraph_real_t *to, const igraph_real_t *from, - int n, void *extra) { - - 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; - igraph_integer_t 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++) { - igraph_integer_t nei = 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 IGRAPH_SUCCESS; -} - -static igraph_error_t pagerank_operator_weighted(igraph_real_t *to, const igraph_real_t *from, - int n, void *extra) { - - pagerank_data_weighted_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; - igraph_integer_t 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; i 0) { - sumfrom += from[i] * fact; - VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i]; - } else { - sumfrom += from[i]; - /* The following value is used only when all outgoing edges have - * weight zero (as opposed to there being no outgoing edges at all). - * We set it to zero to avoid a 0.0*inf situation when computing - * to[i] below. */ - VECTOR(*tmp)[i] = 0; - } - } - - for (i = 0; i < n; i++) { - neis = igraph_inclist_get(inclist, i); - nlen = igraph_vector_int_size(neis); - to[i] = 0.0; - for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); - to[i] += VECTOR(*weights)[edge] * VECTOR(*tmp)[nei]; - } - to[i] *= data->damping; - } - - /* 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, which phrases the PageRank calculation - * as an eigenvalue problem, which is then solved using 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. PRPACK uses an - * algebraic method, i.e. solves a linear system to obtain the PageRank - * scores. - * - * - * 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. - * https://doi.org/10.1016/S0169-7552(98)00110-X - * - * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, - * the eigenvalue corresponding to the PageRank vector is stored here. It is - * expected to be exactly one. Checking this value can be used to diagnose cases - * when ARPACK failed to converge to the leading eigenvector. - * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. - * \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. Supply \c NULL here to use the defaults. 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 - */ - -igraph_error_t 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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, - * the eigenvalue corresponding to the PageRank vector is stored here. It is - * expected to be exactly one. Checking this value can be used to diagnose cases - * when ARPACK failed to converge to the leading eigenvector. - * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. - * \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. Supply \c NULL here to use the defaults. 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. - */ - -igraph_error_t 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)[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 IGRAPH_SUCCESS; -} - -/** - * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, - * the eigenvalue corresponding to the PageRank vector is stored here. It is - * expected to be exactly one. Checking this value can be used to diagnose cases - * when ARPACK failed to converge to the leading eigenvector. - * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. - * \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. Supply \c NULL here to use the defaults. 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. - */ -igraph_error_t 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 ? options : igraph_arpack_options_get_default() - ); - } 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 igraph_error_t 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; - - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - - igraph_real_t reset_sum; /* used only when reset != NULL */ - - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); - } - - if (weights && igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Invalid length of weights vector when calculating PageRank scores.", IGRAPH_EINVAL); - } - - if (reset && igraph_vector_size(reset) != no_of_nodes) { - IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); - } - - if (reset) { - reset_sum = igraph_vector_sum(reset); - if (no_of_nodes > 0 && reset_sum == 0) { - IGRAPH_ERROR("The sum of the elements in the reset vector must not be zero.", IGRAPH_EINVAL); - } - - igraph_real_t reset_min = igraph_vector_min(reset); - if (reset_min < 0) { - IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); - } - if (isnan(reset_min)) { - IGRAPH_ERROR("The reset vector must not contain NaN values.", 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) { - if (reset && no_of_nodes > 0) { - IGRAPH_CHECK(igraph_vector_update(vector, reset)); - igraph_vector_scale(vector, 1.0 / reset_sum); - } else { - IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); - 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] = 'R'; - options->start = 1; /* no random start vector */ - - directed = directed && igraph_is_directed(graph); - - if (weights) { - igraph_real_t min, max; - - /* Safe to call minmax, ecount == 0 case was caught earlier */ - igraph_vector_minmax(weights, &min, &max); - if (min < 0) { - IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); - } - if (isnan(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 */ - IGRAPH_CHECK(igraph_vector_init_copy(&normalized_reset, reset)); - IGRAPH_FINALLY(igraph_vector_destroy, &normalized_reset); - - igraph_vector_scale(&normalized_reset, 1.0 / reset_sum); - } - - IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), - directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS, weights)); - IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), - directed ? IGRAPH_IN : IGRAPH_ALL, IGRAPH_LOOPS, weights)); - - /* Set up an appropriate starting vector. We start from the (possibly weight) in-degrees - * plus some small random noise to avoid convergence problems. */ - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(indegree)[i] > 0) { - MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); - } else { - MATRIX(vectors, i, 0) = 1; - } - } - - if (!weights) { - - igraph_adjlist_t adjlist; - 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_adjlist_init(graph, &adjlist, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - - IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_unweighted, - &data, options, NULL, &values, &vectors)); - - igraph_adjlist_destroy(&adjlist); - IGRAPH_FINALLY_CLEAN(1); - - } else { - - igraph_inclist_t inclist; - pagerank_data_weighted_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); - - IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_weighted, - &data, options, NULL, &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) { - igraph_vit_t vit; - igraph_integer_t 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, 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/vendor/cigraph/src/centrality/prpack.cpp b/src/vendor/cigraph/src/centrality/prpack.cpp index 5c9b32f34a0..ca109cac24e 100644 --- a/src/vendor/cigraph/src/centrality/prpack.cpp +++ b/src/vendor/cigraph/src/centrality/prpack.cpp @@ -24,8 +24,6 @@ #include "centrality/prpack/prpack_solver.h" #include "core/exceptions.h" -#include - using namespace prpack; using namespace std; @@ -34,67 +32,67 @@ using namespace std; * * See \c igraph_personalized_pagerank for the documentation of the parameters. */ -igraph_error_t igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, +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) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; - - igraph_integer_t i, no_of_nodes = igraph_vcount(graph); - + long int i, no_of_nodes = igraph_vcount(graph), nodes_to_calc; + igraph_vit_t vit; double *u = nullptr; - std::unique_ptr v; - - if (reset) { - if (igraph_vector_size(reset) != no_of_nodes) { - IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + 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; } - /* 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 (isnan(reset_min)) { - IGRAPH_ERROR("The reset vector must not contain NaN values.", IGRAPH_EINVAL); + // 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); } - 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 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, ""); - // Construct the personalization vector - v.reset(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.get(); - } - - // 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; - IGRAPH_CHECK(prpack_graph.convert_from_igraph(graph, weights, directed)); - prpack_solver solver(&prpack_graph, false); - std::unique_ptr res( solver.solve(damping, 1e-10, u, v.get(), "") ); - - // Delete the personalization vector - v.reset(); + // 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 @@ -105,30 +103,22 @@ igraph_error_t igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igra */ // Fill the result vector - { - // Use of igraph "finally" stack is safe in this block - // since no exceptions can be thrown from here. - - igraph_vit_t vit; - IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - igraph_integer_t 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[IGRAPH_VIT_GET(vit)]; - } - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); + 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); - // PRPACK calculates PageRank scores by solving a linear system, - // so there is no eigenvalue. We return an exact 1.0 in all cases. + // TODO: can we get the eigenvalue? We'll just fake it until we can. if (value) { *value = 1.0; } + delete res; return IGRAPH_SUCCESS; - - IGRAPH_HANDLE_EXCEPTIONS_END; } diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp index 603567db0a7..b78c6fed4f4 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp @@ -64,7 +64,7 @@ prpack_base_graph::prpack_base_graph(const prpack_csc* g) { prpack_base_graph::prpack_base_graph(const prpack_int64_csc* g) { initialize(); // TODO remove the assert and add better behavior - assert(g->num_vs <= std::numeric_limits::max()); + assert(num_vs <= std::numeric_limits::max()); num_vs = (int)g->num_vs; num_es = (int)g->num_es; // fill in heads and tails @@ -138,7 +138,6 @@ prpack_base_graph::prpack_base_graph(const prpack_edge_list* g) { delete[] osets; } -#if 0 prpack_base_graph::prpack_base_graph(const char* filename, const char* format, const bool weighted) { initialize(); FILE* f = fopen(filename, "r"); @@ -160,7 +159,6 @@ prpack_base_graph::prpack_base_graph(const char* filename, const char* format, c } fclose(f); } -#endif prpack_base_graph::~prpack_base_graph() { delete[] heads; @@ -168,7 +166,6 @@ prpack_base_graph::~prpack_base_graph() { delete[] vals; } -#if 0 void prpack_base_graph::read_smat(FILE* f, const bool weighted) { // read in header double ignore = 0.0; @@ -278,7 +275,6 @@ void prpack_base_graph::read_ascii(FILE* f) { } delete[] al; } -#endif prpack_base_graph::prpack_base_graph(int nverts, int nedges, std::pair* edges) { diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h index 411d57d305f..d51df887c4c 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h @@ -12,11 +12,9 @@ namespace prpack { private: // helper methods void initialize(); -#if 0 void read_smat(std::FILE* f, const bool weighted); void read_edges(std::FILE* f); void read_ascii(std::FILE* f); -#endif public: // instance variables int num_vs; @@ -31,9 +29,7 @@ namespace prpack { prpack_base_graph(const prpack_int64_csc* g); prpack_base_graph(const prpack_csr* g); prpack_base_graph(const prpack_edge_list* g); -#if 0 prpack_base_graph(const char* filename, const char* format, const bool weighted); -#endif prpack_base_graph(int nverts, int nedges, std::pair* edges); // destructor ~prpack_base_graph(); diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp index b09c063ece4..65549367ec5 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp @@ -1,9 +1,6 @@ #include "prpack_igraph_graph.h" -#include -#include #include #include -#include #include "igraph_interface.h" @@ -12,69 +9,53 @@ using namespace std; #ifdef PRPACK_IGRAPH_SUPPORT -igraph_error_t prpack_igraph_graph::convert_from_igraph( - const igraph_t *g, const igraph_vector_t *weights, bool directed) { - - const bool treat_as_directed = igraph_is_directed(g) && directed; - igraph_integer_t vcount = igraph_vcount(g), ecount = igraph_ecount(g); - double *p_weight = nullptr; - - if (vcount > INT_MAX) { - IGRAPH_ERROR("Too many vertices for PRPACK.", IGRAPH_EINVAL); - } - if (ecount > (treat_as_directed ? INT_MAX : INT_MAX/2)) { - IGRAPH_ERROR("Too many edges for PRPACK.", IGRAPH_EINVAL); - } - - if (weights && igraph_vector_size(weights) != ecount) { - IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); - } +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 = (int) vcount; - num_es = (int) ecount; + 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 - int *p_head = heads = new int[num_es]; + 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) { + if (weights != 0) { p_weight = vals = new double[num_es]; } // Count the number of ignored edges (those with negative or zero weight) - int num_ignored_es = 0; + num_ignored_es = 0; if (treat_as_directed) { - // Use of igraph "finally" stack is safe in this block - // since no exceptions can be thrown from here. - // Select all the edges and iterate over them by the source vertices + es = igraph_ess_all(IGRAPH_EDGEORDER_TO); + // Add the edges - igraph_eit_t eit; - IGRAPH_CHECK(igraph_eit_create(g, igraph_ess_all(IGRAPH_EDGEORDER_TO), &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); + igraph_eit_create(g, es, &eit); while (!IGRAPH_EIT_END(eit)) { - igraph_integer_t eid = IGRAPH_EIT_GET(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) { - // Negative weights are disallowed. - IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); - } else if (isnan(VECTOR(*weights)[eid])) { - IGRAPH_ERROR("Edge weights must not be NaN.", IGRAPH_EINVAL); - } else if (VECTOR(*weights)[eid] == 0) { - // Edges with zero weight are ignored. + if (VECTOR(*weights)[eid] <= 0) { + // Ignore it. num_ignored_es++; continue; } @@ -92,32 +73,25 @@ igraph_error_t prpack_igraph_graph::convert_from_igraph( } } igraph_eit_destroy(&eit); - IGRAPH_FINALLY_CLEAN(1); } else { - // Use of igraph "finally" stack is safe in this block - // since no exceptions can be thrown from here. - // Select all the edges and iterate over them by the target vertices - igraph_vector_int_t neis; - IGRAPH_CHECK(igraph_vector_int_init(&neis, 0)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + igraph_vector_init(&neis, 0); - for (int i = 0; i < num_vs; i++) { - IGRAPH_CHECK(igraph_incident(g, &neis, i, IGRAPH_ALL)); - - int temp = igraph_vector_int_size(&neis); + 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? - int *p_head_copy = p_head; - for (int j = 0; j < temp; j++) { + p_head_copy = p_head; + for (j = 0; j < temp; j++) { if (weights != 0) { - if (VECTOR(*weights)[VECTOR(neis)[j]] <= 0) { + if (VECTOR(*weights)[(long int)VECTOR(neis)[j]] <= 0) { // Ignore num_ignored_es++; continue; } - *p_weight = VECTOR(*weights)[VECTOR(neis)[j]]; + *p_weight = VECTOR(*weights)[(long int)VECTOR(neis)[j]]; ++p_weight; } @@ -130,16 +104,15 @@ igraph_error_t prpack_igraph_graph::convert_from_igraph( tails[i] = p_head - p_head_copy; } - igraph_vector_int_destroy(&neis); - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_destroy(&neis); } // Decrease num_es by the number of ignored edges num_es -= num_ignored_es; // Finalize the tails vector - for (int i = 0, sum = 0; i < num_vs; ++i) { - int temp = sum; + for (i = 0, sum = 0; i < num_vs; ++i) { + temp = sum; sum += tails[i]; tails[i] = temp; } @@ -168,8 +141,6 @@ igraph_error_t prpack_igraph_graph::convert_from_igraph( } printf("===========================\n"); */ - - return IGRAPH_SUCCESS; } // PRPACK_IGRAPH_SUPPORT diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h index b245f1ad59e..1bab84d4de6 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h @@ -5,23 +5,18 @@ #include "prpack_base_graph.h" -#include "igraph_datatype.h" -#include "igraph_vector.h" +struct igraph_s; +struct igraph_vector_t; namespace prpack { - class prpack_igraph_graph : public prpack_base_graph { - public: - // constructors - prpack_igraph_graph() { } - - // We use a separate function to carry out the actual construction of the graph. - // The base class constructor sets the heads/tails/vals arrays to NULL, - // so these can safely be delete'ed by the destructor when - // convert_from_igraph() fails. - igraph_error_t convert_from_igraph(const igraph_t *g, - const igraph_vector_t *weights, - bool directed = true); + 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); }; } diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_result.h b/src/vendor/cigraph/src/centrality/prpack/prpack_result.h index 00d96b948a6..87da0f7bfbb 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_result.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_result.h @@ -2,7 +2,6 @@ #define PRPACK_RESULT #include -#include namespace prpack { @@ -16,7 +15,7 @@ namespace prpack { double read_time; double preprocess_time; double compute_time; - int64_t num_es_touched; + long num_es_touched; std::string method; int converged; // constructor diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp index 4d2d1f544cc..b1de4afa2ba 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp @@ -13,7 +13,7 @@ void prpack_solver::initialize() { gsg = NULL; sg = NULL; sccg = NULL; - owns_bg = true; + owns_bg = true; } prpack_solver::prpack_solver(const prpack_csc* g) { @@ -38,21 +38,19 @@ prpack_solver::prpack_solver(const prpack_edge_list* g) { prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) { initialize(); - this->owns_bg = owns_bg; + this->owns_bg = owns_bg; TIME(read_time, bg = g); } -#if 0 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)); } -#endif prpack_solver::~prpack_solver() { - if (owns_bg) { - delete bg; - } + if (owns_bg) { + delete bg; + } delete geg; delete gsg; delete sg; diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h index 91a3cef7c6b..c89d4f301a1 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h @@ -159,9 +159,7 @@ namespace prpack { prpack_solver(const prpack_csr* g); prpack_solver(const prpack_edge_list* g); prpack_solver(prpack_base_graph* g, bool owns_bg=true); -#if 0 prpack_solver(const char* filename, const char* format, const bool weighted); -#endif // destructor ~prpack_solver(); // methods diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp index 15981eed41f..1d425b160f7 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp @@ -41,7 +41,8 @@ double prpack_utils::get_time() { void prpack_utils::validate(const bool condition, const string& msg) { if (!condition) { #ifdef PRPACK_IGRAPH_SUPPORT - IGRAPH_FATAL("Internal error in PRPACK."); + igraph_error("Internal error in PRPACK", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_EINTERNAL); #else cerr << msg << endl; exit(-1); diff --git a/src/vendor/cigraph/src/centrality/prpack_internal.h b/src/vendor/cigraph/src/centrality/prpack_internal.h index 938a41e63ed..01a85f68a9c 100644 --- a/src/vendor/cigraph/src/centrality/prpack_internal.h +++ b/src/vendor/cigraph/src/centrality/prpack_internal.h @@ -21,8 +21,8 @@ */ -#ifndef IGRAPH_PRPACK_H -#define IGRAPH_PRPACK_H +#ifndef IGRAPH_PRPACK +#define IGRAPH_PRPACK #include "igraph_decls.h" #include "igraph_types.h" @@ -33,7 +33,7 @@ __BEGIN_DECLS -igraph_error_t igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, +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, diff --git a/src/vendor/cigraph/src/centrality/truss.cpp b/src/vendor/cigraph/src/centrality/truss.cpp deleted file mode 100644 index d1f817b7936..00000000000 --- a/src/vendor/cigraph/src/centrality/truss.cpp +++ /dev/null @@ -1,286 +0,0 @@ -/* - Copyright 2017 The Johns Hopkins University Applied Physics Laboratory LLC. All Rights Reserved. - Copyright 2021 The igraph team. - - Truss algorithm for cohesive subgroups. - - Author: Alex Perrone - Date: 2017-08-03 - Minor edits: The igraph team, 2021 - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public 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_community.h" - -#include "igraph_adjlist.h" -#include "igraph_error.h" -#include "igraph_interface.h" -#include "igraph_motifs.h" -#include "igraph_structural.h" - -#include "core/exceptions.h" -#include "core/interruption.h" - -using std::vector; -using std::unordered_set; - - -// Unpack the triangles as a vector of vertices to be a vector of edges. -// So, instead of the triangle specified as vertices [1, 2, 3], return the -// edges as [1, 2, 1, 3, 2, 3] so that the support can be computed. -static igraph_error_t igraph_truss_i_unpack(const igraph_vector_int_t *tri, igraph_vector_int_t *unpacked_tri) { - igraph_integer_t num_triangles = igraph_vector_int_size(tri); - - IGRAPH_CHECK(igraph_vector_int_resize(unpacked_tri, 2 * num_triangles)); - - for (igraph_integer_t i = 0, j = 0; i < num_triangles; i += 3, j += 6) { - VECTOR(*unpacked_tri)[j] = VECTOR(*unpacked_tri)[j+2] = VECTOR(*tri)[i]; - VECTOR(*unpacked_tri)[j+1] = VECTOR(*unpacked_tri)[j+4] = VECTOR(*tri)[i+1]; - VECTOR(*unpacked_tri)[j+3] = VECTOR(*unpacked_tri)[j+5] = VECTOR(*tri)[i+2]; - } - - return IGRAPH_SUCCESS; -} - - -// Compute the edge support, i.e. number of triangles each edge occurs in. -// Time complexity: O(m), where m is the number of edges listed in eid. -static void igraph_truss_i_compute_support(const igraph_vector_int_t *eid, igraph_vector_int_t *support) { - igraph_integer_t m = igraph_vector_int_size(eid); - for (igraph_integer_t i = 0; i < m; ++i) { - VECTOR(*support)[VECTOR(*eid)[i]] += 1; - } -} - - -/* internal function doing the computations once the support is defined */ -static igraph_error_t igraph_i_trussness(const igraph_t *graph, igraph_vector_int_t *support, - igraph_vector_int_t *trussness) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; - - igraph_adjlist_t adjlist; - igraph_vector_int_t commonNeighbors; - igraph_vector_bool_t completed; - - // C++ data structures - vector< unordered_set > vec; - - // Allocate memory for result - igraph_integer_t no_of_edges = igraph_vector_int_size(support); - IGRAPH_CHECK(igraph_vector_int_resize(trussness, no_of_edges)); - if (no_of_edges == 0) { - return IGRAPH_SUCCESS; - } - - // Get max possible value = max entry in support. - // This cannot be computed if there are no edges, hence the above check - igraph_integer_t max = igraph_vector_int_max(support); - - // Initialize completed edges. - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&completed, no_of_edges); - - // The vector of levels. Each level of the vector is a set of edges initially - // at that level of support, where support is # of triangles the edge is in. - vec.resize(max + 1); - - // Add each edge to its appropriate level of support. - for (igraph_integer_t i = 0; i < no_of_edges; ++i) { - vec[VECTOR(*support)[i]].insert(i); // insert edge i into its support level - } - - // Record the trussness of edges at level 0. These edges are not part - // of any triangles, so there's not much to do and we "complete" them - for (auto edge : vec[0]) { - VECTOR(*trussness)[edge] = 2; - VECTOR(completed)[edge] = true; - } - - // Initialize variables needed below. - IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - IGRAPH_VECTOR_INT_INIT_FINALLY(&commonNeighbors, 0); - - // Move through the levels, one level at a time, starting at first level. - for (igraph_integer_t level = 1; level <= max; ++level) { - - /* Track down edges one at a time */ - while (!vec[level].empty()) { - IGRAPH_ALLOW_INTERRUPTION(); - - igraph_integer_t seed = *vec[level].begin(); // pull out the first edge - vec[level].erase(seed); // remove the first element - - /* Find the vertices of this edge */ - igraph_integer_t fromVertex = IGRAPH_FROM(graph, seed); - igraph_integer_t toVertex = IGRAPH_TO(graph, seed); - - /* Find neighbors of both vertices. If they run into each other, - * there is a triangle. We rely on the neighbor lists being sorted, - * as guaranteed by igraph_adjlist_init(), when computing intersections. */ - igraph_vector_int_t *fromNeighbors = igraph_adjlist_get(&adjlist, fromVertex); - igraph_vector_int_t *toNeighbors = igraph_adjlist_get(&adjlist, toVertex); - igraph_vector_int_t *q1 = fromNeighbors; - igraph_vector_int_t *q2 = toNeighbors; - - if (igraph_vector_int_size(q1) > igraph_vector_int_size(q2)) { - // case: #fromNeighbors > #toNeigbors, so make q1 the smaller set. - q1 = toNeighbors; - q2 = fromNeighbors; - } - - // Intersect the neighbors. - IGRAPH_CHECK(igraph_vector_int_intersect_sorted(q1, q2, &commonNeighbors)); - - /* Go over the overlapping neighbors and check each */ - igraph_integer_t ncommon = igraph_vector_int_size(&commonNeighbors); - for (igraph_integer_t j = 0; j < ncommon; j++) { - igraph_integer_t n = VECTOR(commonNeighbors)[j]; // the common neighbor - igraph_integer_t e1, e2; - IGRAPH_CHECK(igraph_get_eid(graph, &e1, fromVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); - IGRAPH_CHECK(igraph_get_eid(graph, &e2, toVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); - - bool e1_complete = VECTOR(completed)[e1]; - bool e2_complete = VECTOR(completed)[e2]; - - if (!e1_complete && !e2_complete) { - igraph_integer_t newLevel; - - // Demote this edge, if higher than current level. - if (VECTOR(*support)[e1] > level) { - VECTOR(*support)[e1] -= 1; // decrement the level - newLevel = VECTOR(*support)[e1]; - vec[newLevel].insert(e1); - vec[newLevel + 1].erase(e1); // the old level - } - // Demote this edge, if higher than current level. - if (VECTOR(*support)[e2] > level) { - VECTOR(*support)[e2] -= 1; // decrement the level - newLevel = VECTOR(*support)[e2]; - vec[newLevel].insert(e2); - vec[newLevel + 1].erase(e2); // the old level - } - } - } - // Record this edge; its level is its trussness. - VECTOR(*trussness)[seed] = level + 2; - VECTOR(completed)[seed] = true; // mark as complete - igraph_vector_int_clear(&commonNeighbors); - } // end while - } // end for-loop over levels - - // Clean up. - igraph_vector_int_destroy(&commonNeighbors); - igraph_adjlist_destroy(&adjlist); - igraph_vector_bool_destroy(&completed); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; - - IGRAPH_HANDLE_EXCEPTIONS_END; -} - - -/** - * \function igraph_trussness - * \brief Finding the "trussness" of the edges in a network. - * - * A k-truss is a subgraph in which every edge occurs in at least k-2 triangles - * in the subgraph. The trussness of an edge indicates the highest k-truss that - * the edge occurs in. - * - * - * This function returns the highest \c k for each edge. If you are interested in - * a particular k-truss subgraph, you can subset the graph to those edges - * which are >= k because each k-truss is a subgraph of a (k–1)-truss - * Thus, to get all 4-trusses, take k >= 4 because the 5-trusses, 6-trusses, - * etc. need to be included. - * - * - * The current implementation of this function iteratively decrements support - * of each edge using O(|E|) space and O(|E|^1.5) time. The implementation does - * not support multigraphs; use \ref igraph_simplify() to collapse edges before - * calling this function. - * - * - * Reference: - * - * - * See Algorithm 2 in: - * Wang, Jia, and James Cheng. "Truss decomposition in massive networks." - * Proceedings of the VLDB Endowment 5.9 (2012): 812-823. - * https://doi.org/10.14778/2311906.2311909 - * - * \param graph The input graph. Loop edges are allowed; multigraphs are not. - * \param truss Pointer to initialized vector of truss values that will - * indicate the highest k-truss each edge occurs in. It will be resized as - * needed. - * \return Error code. - * - * Time complexity: It should be O(|E|^1.5) according to the reference. - */ -igraph_error_t igraph_trussness(const igraph_t* graph, igraph_vector_int_t* trussness) { - igraph_vector_int_t triangles, support, unpacked_triangles, eid; - igraph_bool_t is_multigraph; - - /* Check whether the graph is a multigraph; trussness will not work for these */ - IGRAPH_CHECK(igraph_has_multiple(graph, &is_multigraph)); - if (! is_multigraph && igraph_is_directed(graph)) { - /* Directed graphs with mutual edges are effectively multigraphs - * when edge directions are ignored. */ - IGRAPH_CHECK(igraph_has_mutual(graph, &is_multigraph, /* loops */ false)); - } - if (is_multigraph) { - IGRAPH_ERROR("Trussness is not implemented for graph with multi-edges.", IGRAPH_UNIMPLEMENTED); - } - - /* Manage the stack to make it memory safe: do not change the order of - * initialization of the following four vectors */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&support, igraph_ecount(graph)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eid, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&unpacked_triangles, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&triangles, 0); - - // List the triangles as vertex triplets. - IGRAPH_CHECK(igraph_list_triangles(graph, &triangles)); - - // Unpack the triangles from vertex list to edge list. - IGRAPH_CHECK(igraph_truss_i_unpack(&triangles, &unpacked_triangles)); - igraph_vector_int_destroy(&triangles); - IGRAPH_FINALLY_CLEAN(1); - - // Get the edge IDs of the unpacked triangles. Note: a given eid can occur - // multiple times in this list if it is in multiple triangles. - IGRAPH_CHECK(igraph_get_eids(graph, &eid, &unpacked_triangles, /* directed = */ false, /* error = */ true)); - igraph_vector_int_destroy(&unpacked_triangles); - IGRAPH_FINALLY_CLEAN(1); - - // Compute the support of the edges. - igraph_truss_i_compute_support(&eid, &support); - igraph_vector_int_destroy(&eid); - IGRAPH_FINALLY_CLEAN(1); - - // Compute the trussness of the edges. - IGRAPH_CHECK(igraph_i_trussness(graph, &support, trussness)); - igraph_vector_int_destroy(&support); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt b/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt index 7926f4efcd6..e4871a24e98 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt +++ b/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt @@ -24,4 +24,5 @@ endif() # function as is (without visibility specification) target_compile_definitions(cliquer PRIVATE IGRAPH_STATIC) -use_all_warnings(cliquer) +# TODO(ntamas): make sure that this works for Cliquer +# use_all_warnings(cliquer) diff --git a/src/vendor/cigraph/src/cliques/cliquer/cliquer.c b/src/vendor/cigraph/src/cliques/cliquer/cliquer.c index 218e16a1d91..2dd08b934f3 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/cliquer.c +++ b/src/vendor/cigraph/src/cliques/cliquer/cliquer.c @@ -21,7 +21,7 @@ /* Default cliquer options */ IGRAPH_THREAD_LOCAL clique_options clique_default_options = { - reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 + reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 }; @@ -88,18 +88,18 @@ memcpy(&realtimer,&old_realtimer,sizeof(struct timeval));*/ /* Recursion and helper functions */ static boolean sub_unweighted_single(int *table, int size, int min_size, graph_t *g); -static igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int max_size, +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, CLIQUER_LARGE_INT *num_found); -static igraph_error_t sub_weighted_all(int *table, int size, int weight, + 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, int *weight_found); + graph_t *g, clique_options *opts); -static igraph_error_t store_clique(set_t clique, 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 igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opts); +static boolean false_function(set_t clique,graph_t *g,clique_options *opts); @@ -140,10 +140,10 @@ static igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opt */ 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; @@ -181,7 +181,7 @@ static int unweighted_clique_search_single(int *table, int min_size, clique_size[v]=clique_size[w]; } - /* + /* if (opts && opts->time_function) { gettimeofday(&timeval,NULL); times(&tms); @@ -200,7 +200,7 @@ static int unweighted_clique_search_single(int *table, int min_size, return 0; } } - */ + */ if (min_size) { if (clique_size[v]>=min_size) { @@ -289,7 +289,7 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, } } - /* Avoid unnecessary loops (next size == p1-newtable) */ + /* 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 @@ -325,7 +325,6 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, * maximal - requires cliques to be maximal * g - the graph * opts - time printing and clique storage options - * num_found - number of cliques found * * Cliques found are stored as defined by opts->user_function and * opts->clique_list. opts->time_function is called after each @@ -333,22 +332,24 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, * * 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 igraph_error_t unweighted_clique_search_all( - int *table, int start, int min_size, int max_size, boolean maximal, - graph_t *g, clique_options *opts, CLIQUER_LARGE_INT *num_found -) { - /* +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 i, j; int v; int *newtable; int newsize; - CLIQUER_LARGE_INT r; - CLIQUER_LARGE_INT count=0; - igraph_error_t retval = IGRAPH_SUCCESS; + CLIQUER_LARGE_INT r; + CLIQUER_LARGE_INT count=0; if (temp_count) { temp_count--; @@ -372,14 +373,15 @@ static igraph_error_t unweighted_clique_search_all( } SET_ADD_ELEMENT(current_clique,v); - retval=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1, - maximal,g,opts,&r); + r=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1, + maximal,g,opts); SET_DEL_ELEMENT(current_clique,v); - count+=r; - if (retval) { + if (r<0) { /* Abort. */ + count-=r; break; } + count+=r; #if 0 if (opts->time_function) { @@ -403,12 +405,7 @@ static igraph_error_t unweighted_clique_search_all( #endif } temp_list[temp_count++]=newtable; - - if (num_found) { - *num_found = count; - } - - return retval; + return count; } /* @@ -426,7 +423,6 @@ static igraph_error_t unweighted_clique_search_all( * maximal - require cliques to be maximal (passed through) * g - the graph * opts - storage options - * num_found - number of cliques found * * All cliques of suitable size found are stored according to opts. * @@ -437,37 +433,32 @@ static igraph_error_t unweighted_clique_search_all( * clique_size[] for all values in table must be defined and correct, * otherwise inaccurate results may occur. */ -static igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int max_size, +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, CLIQUER_LARGE_INT *num_found) { - igraph_error_t retval = IGRAPH_SUCCESS; + 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 */ + 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++; - retval = store_clique(current_clique, g, opts); - if (retval) { - *num_found = count; - return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; + if (!store_clique(current_clique,g,opts)) { + return -count; } } if (max_size <= 0) { /* If we add another element, size will be too big. */ - *num_found = count; - return IGRAPH_SUCCESS; + return count; } } if (size < min_size) { - *num_found = count; - return IGRAPH_SUCCESS; + return count; } /* Dynamic memory allocation with cache */ @@ -497,25 +488,25 @@ static igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int } } - /* Avoid unnecessary loops (next size == p1-newtable) */ + /* Avoid unneccessary loops (next size == p1-newtable) */ if (p1-newtable < min_size-1) { continue; } SET_ADD_ELEMENT(current_clique,v); - retval = sub_unweighted_all(newtable,p1-newtable, - min_size-1,max_size-1,maximal,g,opts,&n); + n=sub_unweighted_all(newtable,p1-newtable, + min_size-1,max_size-1,maximal,g,opts); SET_DEL_ELEMENT(current_clique,v); - count += n; - if (retval || n < 0) { + if (n < 0) { + /* Abort. */ + count -= n; + count = -count; break; } count+=n; } temp_list[temp_count++]=newtable; - - *num_found = count; - return retval; + return count; } @@ -555,13 +546,13 @@ static igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int * * Note: Does NOT use opts->user_function of opts->clique_list. */ -static igraph_error_t weighted_clique_search_single(int *table, int min_weight, +static int weighted_clique_search_single(int *table, int min_weight, int max_weight, graph_t *g, - clique_options *opts, int *result) { - /* + clique_options *opts) { + /* struct timeval timeval; struct tms tms; - */ + */ int i,j; int v; int *newtable; @@ -570,9 +561,6 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, int search_weight; int min_w; clique_options localopts; - igraph_error_t retval = IGRAPH_SUCCESS; - - ASSERT(result != NULL); if (min_weight==0) min_w=INT_MAX; @@ -588,13 +576,10 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, if (g->weights[table[i]] <= max_weight) { set_empty(best_clique); SET_ADD_ELEMENT(best_clique,table[i]); - *result = g->weights[table[i]]; - return IGRAPH_SUCCESS; + return g->weights[table[i]]; } } - - *result = 0; - return IGRAPH_SUCCESS; + return 0; } localopts.time_function=NULL; @@ -613,8 +598,7 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, if (min_weight && (search_weight >= min_weight)) { if (search_weight <= max_weight) { /* Found suitable clique. */ - *result = search_weight; - return IGRAPH_SUCCESS; + return search_weight; } search_weight=min_weight-1; } @@ -643,14 +627,14 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, SET_ADD_ELEMENT(current_clique,v); - retval=sub_weighted_all(newtable,newsize,newweight, + 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, &search_weight); + g,&localopts); SET_DEL_ELEMENT(current_clique,v); - if (retval || search_weight < 0) { + if (search_weight < 0) { break; } @@ -681,12 +665,9 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, temp_list[temp_count++]=newtable; if (min_weight && (search_weight > 0)) { /* Requested clique has not been found. */ - *result = 0; - } else { - *result = clique_size[table[i-1]]; + return 0; } - - return retval; + return clique_size[table[i-1]]; } @@ -705,7 +686,6 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, * maximal - search only for maximal cliques * g - the graph * opts - time printing and clique storage options - * num_found - number of cliques found * * Cliques found are stored as defined by opts->user_function and * opts->clique_list. opts->time_function is called after each @@ -714,13 +694,13 @@ static igraph_error_t weighted_clique_search_single(int *table, int min_weight, * clique_size[] must be defined and correct for all values of * table[0], ..., table[start-1]. * - * Returns the number of cliques stored (not necessarily number of cliques + * Returns the number of cliques stored (not neccessarily number of cliques * in graph, if user/time_function aborts). */ -static igraph_error_t weighted_clique_search_all(int *table, int start, +static int weighted_clique_search_all(int *table, int start, int min_weight, int max_weight, boolean maximal, graph_t *g, - clique_options *opts, int* num_found) { + clique_options *opts) { /* struct timeval timeval; struct tms tms; @@ -730,7 +710,6 @@ static igraph_error_t weighted_clique_search_all(int *table, int start, int *newtable; int newsize; int newweight; - igraph_error_t retval = IGRAPH_SUCCESS; if (temp_count) { temp_count--; @@ -756,12 +735,12 @@ static igraph_error_t weighted_clique_search_all(int *table, int start, } SET_ADD_ELEMENT(current_clique,v); - retval=sub_weighted_all(newtable,newsize,newweight, + j=sub_weighted_all(newtable,newsize,newweight, g->weights[v],min_weight-1,INT_MAX, - min_weight,max_weight,maximal,g,opts,&j); + min_weight,max_weight,maximal,g,opts); SET_DEL_ELEMENT(current_clique,v); - if (retval || j < 0) { + if (j<0) { /* Abort. */ break; } @@ -790,11 +769,7 @@ static igraph_error_t weighted_clique_search_all(int *table, int start, } temp_list[temp_count++]=newtable; - if (num_found) { - *num_found = clique_list_count; - } - - return retval; + return clique_list_count; } /* @@ -817,12 +792,13 @@ static igraph_error_t weighted_clique_search_all(int *table, int start, * maximal - search only for maximal cliques * g - the graph * opts - storage options - * weight_found - weight of the 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. * * 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. * @@ -836,11 +812,10 @@ static igraph_error_t weighted_clique_search_all(int *table, int start, * searching for all cliques, min_weight should be given the minimum weight * desired. */ -static igraph_error_t sub_weighted_all(int *table, int size, int weight, +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* weight_found) { - igraph_error_t retval = IGRAPH_SUCCESS; + graph_t *g, clique_options *opts) { int i; int v,w; int *newtable; @@ -851,16 +826,13 @@ static igraph_error_t sub_weighted_all(int *table, int size, int weight, if ((current_weight <= max_weight) && ((!maximal) || is_maximal(current_clique,g))) { /* We've found one. Store it. */ - retval = store_clique(current_clique,g,opts); - if (retval) { - *weight_found = -1; - return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; + if (!store_clique(current_clique,g,opts)) { + return -1; } } if (current_weight >= max_weight) { /* Clique too heavy. */ - *weight_found = min_weight-1; - return IGRAPH_SUCCESS; + return min_weight-1; } } if (size <= 0) { @@ -870,16 +842,12 @@ static igraph_error_t sub_weighted_all(int *table, int size, int weight, if (best_clique) { best_clique = set_copy(best_clique,current_clique); } - if (current_weight < min_weight) { - *weight_found = current_weight; - return IGRAPH_SUCCESS; - } else { - *weight_found = min_weight-1; - return IGRAPH_SUCCESS; - } + if (current_weight < min_weight) + return current_weight; + else + return min_weight-1; } else { - *weight_found = prune_low; - return IGRAPH_SUCCESS; + return prune_low; } } @@ -916,28 +884,26 @@ static igraph_error_t sub_weighted_all(int *table, int size, int weight, w=g->weights[v]; weight-=w; - /* Avoid a few unnecessary loops */ + /* Avoid a few unneccessary loops */ if (current_weight+w+newweight <= prune_low) { continue; } SET_ADD_ELEMENT(current_clique,v); - retval=sub_weighted_all(newtable,p1-newtable, + prune_low=sub_weighted_all(newtable,p1-newtable, newweight, current_weight+w, prune_low,prune_high, min_weight,max_weight,maximal, - g,opts, &prune_low); + g,opts); SET_DEL_ELEMENT(current_clique,v); - if (retval || (prune_low<0) || (prune_low>=prune_high)) { + if ((prune_low<0) || (prune_low>=prune_high)) { /* Impossible to find larger clique. */ break; } } temp_list[temp_count++]=newtable; - - *weight_found = prune_low; - return IGRAPH_SUCCESS; + return prune_low; } @@ -954,10 +920,10 @@ static igraph_error_t sub_weighted_all(int *table, int size, int weight, * clique - the clique to store * opts - storage options * - * Returns the same igraph error code as the one returned by - * opts->user_function(). Returns IGRAPH_SUCCESS if no callback is defined. + * Returns FALSE if opts->user_function() returned FALSE; otherwise + * returns TRUE. */ -static igraph_error_t store_clique(set_t clique, graph_t *g, clique_options *opts) { +static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) { clique_list_count++; @@ -978,10 +944,13 @@ static igraph_error_t store_clique(set_t clique, graph_t *g, clique_options *opt /* user_function() */ if (opts->user_function) { - return opts->user_function(clique, g, opts); + if (!opts->user_function(clique,g,opts)) { + /* User function requested abort. */ + return FALSE; + } } - return IGRAPH_SUCCESS; + return TRUE; } /* @@ -1065,10 +1034,10 @@ static boolean is_maximal(set_t clique, graph_t *g) { /* * false_function() * - * Returns IGRAPH_STOP. Can be used as user_function. + * Returns FALSE. Can be used as user_function. */ -static igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opts) { - return IGRAPH_STOP; +static boolean false_function(set_t clique,graph_t *g,clique_options *opts) { + return FALSE; } @@ -1079,31 +1048,31 @@ static igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opt /* * clique_unweighted_max_weight() * + * Returns the size of the maximum (sized) clique in g (or 0 if search + * was aborted). + * * g - the graph * opts - time printing options - * size - the size of the maximum (sized) clique in g * * Note: As we don't have an algorithm faster than actually finding * a maximum clique, we use clique_unweighted_find_single(). * This incurs only very small overhead. */ -igraph_error_t clique_unweighted_max_weight(graph_t *g, clique_options *opts, int* size) { - set_t clique; +int clique_unweighted_max_weight(graph_t *g, clique_options *opts) { + set_t s; + int size; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); - IGRAPH_CHECK(clique_unweighted_find_single(g, 0, 0, FALSE,opts, &clique)); - - if (size != NULL) { - *size = clique ? set_size(clique) : 0; - } - - if (clique) { - set_free(clique); + s=clique_unweighted_find_single(g,0,0,FALSE,opts); + if (s==NULL) { + /* Search was aborted. */ + return 0; } - - return IGRAPH_SUCCESS; + size=set_size(s); + set_free(s); + return size; } @@ -1128,13 +1097,11 @@ igraph_error_t clique_unweighted_max_weight(graph_t *g, clique_options *opts, in * * Note: Does NOT use opts->user_function() or opts->clique_list[]. */ -igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, - boolean maximal, clique_options *opts, set_t *clique) { +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; - igraph_error_t retval = IGRAPH_SUCCESS; - CLIQUER_LARGE_INT found; ENTRANCE_SAVE(); entrance_level++; @@ -1142,7 +1109,6 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz if (opts==NULL) opts=&clique_default_options; - ASSERT(clique!=NULL); ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_size>=0); @@ -1154,8 +1120,7 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz if ((max_size>0) && (min_size>max_size)) { /* state was not changed */ entrance_level--; - *clique = NULL; - return IGRAPH_SUCCESS; + return NULL; } /* @@ -1187,7 +1152,8 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz } ASSERT(reorder_is_bijection(table,g->n)); - if (unweighted_clique_search_single(table,min_size,g,opts) == 0) { + + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { set_free(current_clique); current_clique=NULL; goto cleanreturn; @@ -1208,20 +1174,20 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz for (i=0; i < g->n-1; i++) if (clique_size[table[i]]>=min_size) break; - retval = unweighted_clique_search_all( - table, i, min_size, max_size, maximal, g, &localopts, &found - ); - set_free(current_clique); - if (retval || !found) { - current_clique=NULL; - } else { + 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: - *clique = current_clique; + s=current_clique; /* Free resources */ for (i=0; i < temp_count; i++) @@ -1233,7 +1199,7 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz ENTRANCE_RESTORE(); entrance_level--; - return retval; + return s; } @@ -1249,24 +1215,21 @@ igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_siz * 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 - * num_found - the number of cliques found. This can be less than the number - * of cliques in the graph iff opts->time_function() returns - * FALSE (request abort) or opts->user_function() returns an - * igraph error code + * + * 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(). */ -igraph_error_t clique_unweighted_find_all( - graph_t *g, int min_size, int max_size, boolean maximal, clique_options *opts, - CLIQUER_LARGE_INT *num_found -) { +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; - igraph_error_t retval = IGRAPH_SUCCESS; + CLIQUER_LARGE_INT count; ENTRANCE_SAVE(); entrance_level++; @@ -1285,10 +1248,7 @@ igraph_error_t clique_unweighted_find_all( if ((max_size>0) && (min_size>max_size)) { /* state was not changed */ entrance_level--; - if (num_found) { - *num_found = 0; - } - return IGRAPH_SUCCESS; + return 0; } /* @@ -1326,7 +1286,7 @@ igraph_error_t clique_unweighted_find_all( /* Search as normal until there is a chance to find a suitable * clique. */ - if (unweighted_clique_search_single(table,min_size,g,opts) == 0) { + if (unweighted_clique_search_single(table,min_size,g,opts)==0) { count=0; goto cleanreturn; } @@ -1343,8 +1303,8 @@ igraph_error_t clique_unweighted_find_all( for (i=0; i < g->n-1; i++) if (clique_size[table[i]] >= min_size) break; - - retval = unweighted_clique_search_all(table, i, min_size, max_size, maximal, g, opts, &count); + count=unweighted_clique_search_all(table,i,min_size,max_size, + maximal,g,opts); cleanreturn: /* Free resources */ @@ -1358,11 +1318,7 @@ igraph_error_t clique_unweighted_find_all( ENTRANCE_RESTORE(); entrance_level--; - if (num_found) { - *num_found = count; - } - - return retval; + return count; } @@ -1381,32 +1337,21 @@ igraph_error_t clique_unweighted_find_all( * a maximum weight clique, we use clique_find_single(). * This incurs only very small overhead. */ -igraph_error_t clique_max_weight(graph_t *g, clique_options *opts, int *weight_found) { +int clique_max_weight(graph_t *g,clique_options *opts) { set_t s; - int weight = 0; - igraph_error_t retval; + int weight; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); - retval = clique_find_single(g, 0, 0, FALSE, opts, &s); - - if (retval || s == NULL) { + s=clique_find_single(g,0,0,FALSE,opts); + if (s==NULL) { /* Search was aborted. */ - weight = 0; - } else { - weight = graph_subgraph_weight(g,s); - } - - if (s != NULL) { - set_free(s); - } - - if (weight_found) { - *weight_found = weight; + return 0; } - - return retval; + weight=graph_subgraph_weight(g,s); + set_free(s); + return weight; } @@ -1434,16 +1379,11 @@ igraph_error_t clique_max_weight(graph_t *g, clique_options *opts, int *weight_f * Note: Automatically uses clique_unweighted_find_single if all vertex * weights are the same. */ -igraph_error_t clique_find_single( - graph_t *g, int min_weight, int max_weight, boolean maximal, - clique_options *opts, set_t *clique -) { +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; - igraph_error_t retval = IGRAPH_SUCCESS; - int weight_found; - int num_found; ENTRANCE_SAVE(); entrance_level++; @@ -1451,7 +1391,6 @@ igraph_error_t clique_find_single( if (opts==NULL) opts=&clique_default_options; - ASSERT(clique!=NULL); ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_weight>=0); @@ -1463,8 +1402,7 @@ igraph_error_t clique_find_single( if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; - *clique = NULL; - return IGRAPH_SUCCESS; + return NULL; } /* @@ -1481,17 +1419,16 @@ igraph_error_t clique_find_single( if (max_weight < min_weight) { /* state was not changed */ entrance_level--; - *clique = NULL; - return IGRAPH_SUCCESS; + return NULL; } } weight_multiplier = g->weights[0]; entrance_level--; - retval = clique_unweighted_find_single(g, min_weight, max_weight, maximal, opts, &s); + s=clique_unweighted_find_single(g,min_weight,max_weight, + maximal,opts); ENTRANCE_RESTORE(); - *clique = s; - return retval; + return s; } /* Dynamic allocation */ @@ -1524,15 +1461,13 @@ igraph_error_t clique_find_single( if (max_weight==0) max_weight=INT_MAX; - retval = weighted_clique_search_single(table, min_weight, max_weight, g, opts, &weight_found); - - if (retval || weight_found == 0) { + 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) { @@ -1548,12 +1483,9 @@ igraph_error_t clique_find_single( if ((clique_size[table[i]] >= min_weight) || (clique_size[table[i]] == 0)) break; - - retval = weighted_clique_search_all( - table, i, min_weight, max_weight, maximal, g, &localopts, &num_found - ); - - if (retval || !weight_found) { + if (!weighted_clique_search_all(table,i,min_weight, + max_weight,maximal, + g,&localopts)) { set_free(best_clique); best_clique=NULL; } @@ -1578,9 +1510,7 @@ igraph_error_t clique_find_single( ENTRANCE_RESTORE(); entrance_level--; - *clique = s; - - return retval; + return s; } @@ -1600,10 +1530,10 @@ igraph_error_t clique_find_single( * also be 0. * maximal - require cliques to be maximal cliques * opts - time printing and clique storage options - * num_found - the number of cliques found. This can be less than the number - * of cliques in the graph iff opts->time_function() returns - * FALSE (request abort) or opts->user_function() returns an - * igraph error code + * + * 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 @@ -1613,12 +1543,11 @@ igraph_error_t clique_find_single( * Note: Automatically uses clique_unweighted_find_all if all vertex * weights are the same. */ -igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, - boolean maximal, clique_options *opts, int *num_found) { - int i,n; +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; - igraph_error_t retval = IGRAPH_SUCCESS; + CLIQUER_LARGE_INT r; ENTRANCE_SAVE(); entrance_level++; @@ -1637,10 +1566,7 @@ igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; - if (num_found) { - *num_found = 0; - } - return IGRAPH_SUCCESS; + return 0; } /* @@ -1656,21 +1582,16 @@ igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, if (max_weight < min_weight) { /* state was not changed */ entrance_level--; - if (num_found) { - *num_found = 0; - } - return IGRAPH_SUCCESS; + return 0; } } weight_multiplier = g->weights[0]; entrance_level--; - retval = clique_unweighted_find_all(g, min_weight, max_weight, maximal, opts, &r); + r=clique_unweighted_find_all(g,min_weight,max_weight,maximal, + opts); ENTRANCE_RESTORE(); - if (num_found) { - *num_found = r; - } - return retval; + return r; } /* Dynamic allocation */ @@ -1699,8 +1620,8 @@ igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, ASSERT(reorder_is_bijection(table,g->n)); /* First phase */ - retval = weighted_clique_search_single(table, min_weight, INT_MAX, g, opts, &n); - if (retval || n == 0) { + n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts); + if (n==0) { /* Requested clique has not been found. */ goto cleanreturn; } @@ -1719,9 +1640,10 @@ igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, break; /* Second phase */ - retval = weighted_clique_search_all(table, i, min_weight, max_weight, maximal, g, opts, &n); + n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal, + g,opts); -cleanreturn: + cleanreturn: /* Free resources */ for (i=0; i < temp_count; i++) free(temp_list[i]); @@ -1734,11 +1656,7 @@ igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, ENTRANCE_RESTORE(); entrance_level--; - if (num_found) { - *num_found = n; - } - - return retval; + return n; } diff --git a/src/vendor/cigraph/src/cliques/cliquer/cliquer.h b/src/vendor/cigraph/src/cliques/cliquer/cliquer.h index ec7f9ee37b2..e49636c0784 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/cliquer.h +++ b/src/vendor/cigraph/src/cliques/cliquer/cliquer.h @@ -28,38 +28,27 @@ struct _clique_options { clique_options *); FILE *output; - igraph_error_t (*user_function)(set_t,graph_t *,clique_options *); + boolean (*user_function)(set_t,graph_t *,clique_options *); void *user_data; set_t *clique_list; int clique_list_length; }; /* Weighted clique functions */ -extern igraph_error_t clique_max_weight( - graph_t *g, clique_options *opts, int *weight_found -); -extern igraph_error_t clique_find_single( - graph_t *g, int min_weight, int max_weight, boolean maximal, - clique_options *opts, set_t *clique -); -extern igraph_error_t clique_find_all( - graph_t *g, int req_weight, boolean exact, boolean maximal, - clique_options *opts, int *num_found -); +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 igraph_error_t clique_unweighted_max_weight( - graph_t *g, clique_options *opts, int *weight_found -); -extern igraph_error_t clique_unweighted_find_single( - graph_t *g, int min_size, int max_size, boolean maximal, - clique_options *opts, set_t *clique -); -extern igraph_error_t clique_unweighted_find_all( - graph_t *g, int min_size, int max_size, boolean maximal, - clique_options *opts, CLIQUER_LARGE_INT *num_found -); +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 */ /* diff --git a/src/vendor/cigraph/src/cliques/cliquer/set.h b/src/vendor/cigraph/src/cliques/cliquer/set.h index bca7aa018ce..9727d3024d8 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/set.h +++ b/src/vendor/cigraph/src/cliques/cliquer/set.h @@ -142,8 +142,8 @@ static int set_bit_count[256] = { * Create a new set that can hold values in the range 0,...,size-1. */ UNUSED_FUNCTION -static set_t set_new(size_t size) { - size_t n; +static set_t set_new(int size) { + int n; set_t s; ASSERT(size>0); @@ -270,7 +270,7 @@ static void set_empty(set_t s) { */ UNUSED_FUNCTION INLINE static set_t set_intersection(set_t res,set_t a,set_t b) { - size_t i, max; + int i,max; if (res==NULL) { res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); @@ -303,7 +303,7 @@ static set_t set_intersection(set_t res,set_t a,set_t b) { */ UNUSED_FUNCTION INLINE static set_t set_union(set_t res,set_t a,set_t b) { - size_t i, max; + int i,max; if (res==NULL) { res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); diff --git a/src/vendor/cigraph/src/cliques/cliquer_internal.h b/src/vendor/cigraph/src/cliques/cliquer_internal.h index 93e9897978e..f90b64a218b 100644 --- a/src/vendor/cigraph/src/cliques/cliquer_internal.h +++ b/src/vendor/cigraph/src/cliques/cliquer_internal.h @@ -1,49 +1,27 @@ -/* - IGraph library. - Copyright (C) 2016-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, see . - */ - #ifndef IGRAPH_CLIQUER_H #define IGRAPH_CLIQUER_H -#include "igraph_decls.h" +#include "igraph_interface.h" #include "igraph_cliques.h" -__BEGIN_DECLS - -igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, +int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, +int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -igraph_error_t igraph_i_cliquer_callback(const igraph_t *graph, +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); -igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, +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); -igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); +int igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res); -igraph_error_t igraph_i_weighted_clique_number(const igraph_t *graph, +int igraph_i_weighted_clique_number(const igraph_t *graph, const igraph_vector_t *vertex_weights, igraph_real_t *res); -__END_DECLS - #endif // IGRAPH_CLIQUER_H diff --git a/src/vendor/cigraph/src/cliques/cliquer_wrapper.c b/src/vendor/cigraph/src/cliques/cliquer_wrapper.c index 445faa37425..7b4fa2cd9ce 100644 --- a/src/vendor/cigraph/src/cliques/cliquer_wrapper.c +++ b/src/vendor/cigraph/src/cliques/cliquer_wrapper.c @@ -1,23 +1,7 @@ -/* - IGraph library. - Copyright (C) 2016-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, see . - */ #include "igraph_error.h" -#include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_constants.h" #include "core/interruption.h" #include "cliques/cliquer_internal.h" @@ -25,7 +9,38 @@ #include "config.h" -#include +/* Call this to allow for interruption in Cliquer callback functions */ +#define CLIQUER_ALLOW_INTERRUPTION() \ + { \ + if (igraph_i_interruption_handler) \ + if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \ + cliquer_interrupted = 1; \ + return FALSE; \ + } \ + } + +/* Interruptable Cliquer functions must be wrapped in CLIQUER_INTERRUPTABLE when called */ +#define CLIQUER_INTERRUPTABLE(x) \ + { \ + cliquer_interrupted = 0; \ + x; \ + if (cliquer_interrupted) return IGRAPH_INTERRUPTED; \ + } + + +/* Nonzero value signals interuption from Cliquer callback function */ +static IGRAPH_THREAD_LOCAL int cliquer_interrupted; + + +/* For use with IGRAPH_FINALLY */ +static void free_clique_list(igraph_vector_ptr_t *vp) { + igraph_integer_t i, len; + len = igraph_vector_ptr_size(vp); + for (i = 0; i < len; ++i) { + igraph_vector_destroy((igraph_vector_t *) VECTOR(*vp)[i]); + } + igraph_vector_ptr_free_all(vp); +} /* We shall use this option struct for all calls to Cliquer */ static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { @@ -34,9 +49,9 @@ static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { /* Convert an igraph graph to a Cliquer graph */ -static igraph_error_t igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { +static void igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { igraph_integer_t vcount, ecount; - igraph_integer_t i; + int i; if (igraph_is_directed(ig)) { IGRAPH_WARNING("Edge directions are ignored for clique calculations"); @@ -45,28 +60,22 @@ static igraph_error_t igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { vcount = igraph_vcount(ig); ecount = igraph_ecount(ig); - if (vcount > INT_MAX) { - IGRAPH_ERROR("Graph too large for Cliquer", IGRAPH_EOVERFLOW); - } - - *cg = graph_new((int) vcount); + *cg = graph_new(vcount); for (i = 0; i < ecount; ++i) { - igraph_integer_t s, t; + long s, t; s = IGRAPH_FROM(ig, i); t = IGRAPH_TO(ig, i); if (s != t) { GRAPH_ADD_EDGE(*cg, s, t); } } - - return IGRAPH_SUCCESS; } /* Copy weights to a Cliquer graph */ -static igraph_error_t set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { - igraph_integer_t i; +static int set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { + int i; IGRAPH_ASSERT(vertex_weights != NULL); @@ -90,56 +99,36 @@ static igraph_error_t set_weights(const igraph_vector_t *vertex_weights, graph_t /* Find all cliques. */ -typedef struct { - igraph_vector_int_t clique; - igraph_vector_int_list_t* result; -} igraph_i_cliquer_cliques_user_data_t; - -static igraph_error_t igraph_i_cliquer_cliques_user_data_init( - igraph_i_cliquer_cliques_user_data_t* data, - igraph_vector_int_list_t* result -) { - data->result = result; - igraph_vector_int_list_clear(result); - return igraph_vector_int_init(&data->clique, 0); -} - -static void igraph_i_cliquer_cliques_user_data_destroy( - igraph_i_cliquer_cliques_user_data_t* data -) { - igraph_vector_int_destroy(&data->clique); - data->result = 0; -} - -static igraph_error_t collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) { - int i; - igraph_integer_t j; - igraph_i_cliquer_cliques_user_data_t* data = (igraph_i_cliquer_cliques_user_data_t *) opt->user_data; +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); - IGRAPH_ALLOW_INTERRUPTION(); + CLIQUER_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_vector_int_resize(&data->clique, set_size(s))); + 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(data->clique)[j++] = i; + VECTOR(*clique)[j++] = i; } - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(data->result, &data->clique)); + igraph_vector_ptr_push_back(list, clique); - return IGRAPH_SUCCESS; + return TRUE; } -igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, +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); - igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_int_list_clear(res); + igraph_vector_ptr_clear(res); return IGRAPH_SUCCESS; } @@ -150,28 +139,23 @@ igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int max_size = 0; } - if (max_size > INT_MAX) { - max_size = INT_MAX; - } - if (max_size > 0 && max_size < min_size) { IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); - IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); - - IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); - igraph_cliquer_opt.user_data = &data; + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; igraph_cliquer_opt.user_function = &collect_cliques_callback; - IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + 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_i_cliquer_cliques_user_data_destroy(&data); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -179,23 +163,23 @@ igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int /* Count cliques of each size. */ -static igraph_error_t count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { +static boolean count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { igraph_vector_t *hist; IGRAPH_UNUSED(g); - IGRAPH_ALLOW_INTERRUPTION(); + CLIQUER_ALLOW_INTERRUPTION(); hist = (igraph_vector_t *) opt->user_data; VECTOR(*hist)[set_size(s) - 1] += 1; - return IGRAPH_SUCCESS; + return TRUE; } -igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, +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; - igraph_integer_t i; + int i; igraph_integer_t vcount = igraph_vcount(graph); if (vcount == 0) { @@ -210,32 +194,27 @@ igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t max_size = vcount; /* also used for initial hist vector size, do not set to zero */ } - if (max_size > INT_MAX) { - max_size = INT_MAX; - } - 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_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); - IGRAPH_CHECK(igraph_vector_resize(hist, max_size)); + igraph_vector_resize(hist, max_size); igraph_vector_null(hist); igraph_cliquer_opt.user_data = hist; igraph_cliquer_opt.user_function = &count_cliques_callback; - IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt)); - for (i = max_size; i > 0; --i) { + for (i = max_size; i > 0; --i) if (VECTOR(*hist)[i - 1] > 0) { break; } - } - IGRAPH_CHECK(igraph_vector_resize(hist, i)); + igraph_vector_resize(hist, i); igraph_vector_resize_min(hist); graph_free(g); @@ -248,40 +227,36 @@ igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t /* Call function for each clique. */ struct callback_data { - igraph_vector_int_t *clique; igraph_clique_handler_t *handler; void *arg; }; -static igraph_error_t callback_callback(set_t s, graph_t *g, clique_options *opt) { +static boolean callback_callback(set_t s, graph_t *g, clique_options *opt) { + igraph_vector_t *clique; struct callback_data *cd; - int i; - igraph_integer_t j; - igraph_error_t retval; + int i, j; IGRAPH_UNUSED(g); - IGRAPH_ALLOW_INTERRUPTION(); + CLIQUER_ALLOW_INTERRUPTION(); cd = (struct callback_data *) opt->user_data; - IGRAPH_CHECK(igraph_vector_int_resize(cd->clique, set_size(s))); + 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(*cd->clique)[j++] = i; + VECTOR(*clique)[j++] = i; } - retval = (*(cd->handler))(cd->clique, cd->arg); - - return retval; + return (*(cd->handler))(clique, cd->arg); } -igraph_error_t igraph_i_cliquer_callback(const igraph_t *graph, +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; - igraph_vector_int_t current_clique; struct callback_data cd; igraph_integer_t vcount = igraph_vcount(graph); @@ -296,30 +271,22 @@ igraph_error_t igraph_i_cliquer_callback(const igraph_t *graph, max_size = 0; } - if (max_size > INT_MAX) { - max_size = INT_MAX; - } - if (max_size > 0 && max_size < min_size) { IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); - IGRAPH_VECTOR_INT_INIT_FINALLY(¤t_clique, min_size); - - cd.clique = ¤t_clique; cd.handler = cliquehandler_fn; cd.arg = arg; igraph_cliquer_opt.user_data = &cd; igraph_cliquer_opt.user_function = &callback_callback; - IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + CLIQUER_INTERRUPTABLE(clique_unweighted_find_all(g, min_size, max_size, /* maximal= */ FALSE, &igraph_cliquer_opt)); - igraph_vector_int_destroy(¤t_clique); graph_free(g); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -327,15 +294,14 @@ igraph_error_t igraph_i_cliquer_callback(const igraph_t *graph, /* Find weighted cliques in given weight range. */ -igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, +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); - igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_int_list_clear(res); + igraph_vector_ptr_clear(res); return IGRAPH_SUCCESS; } @@ -360,22 +326,21 @@ igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); - IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); - - IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); IGRAPH_CHECK(set_weights(vertex_weights, g)); - igraph_cliquer_opt.user_data = &data; + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; igraph_cliquer_opt.user_function = &collect_cliques_callback; - IGRAPH_CHECK(clique_find_all(g, (int) min_weight, (int) max_weight, maximal, &igraph_cliquer_opt, NULL)); + 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_i_cliquer_cliques_user_data_destroy(&data); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -383,33 +348,31 @@ igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, /* Find largest weighted cliques. */ -igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res) { +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); - igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_int_list_clear(res); + igraph_vector_ptr_clear(res); return IGRAPH_SUCCESS; } - IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); - IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); - - IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); IGRAPH_CHECK(set_weights(vertex_weights, g)); - igraph_cliquer_opt.user_data = &data; + igraph_vector_ptr_clear(res); + igraph_cliquer_opt.user_data = res; igraph_cliquer_opt.user_function = &collect_cliques_callback; - IGRAPH_CHECK(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt, NULL)); + 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_i_cliquer_cliques_user_data_destroy(&data); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -417,40 +380,28 @@ igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, /* Find weight of largest weight clique. */ -static igraph_error_t check_interruption_callback(set_t s, graph_t *g, clique_options *opt) { - IGRAPH_UNUSED(s); IGRAPH_UNUSED(g); IGRAPH_UNUSED(opt); - IGRAPH_ALLOW_INTERRUPTION(); - return IGRAPH_SUCCESS; -} - -igraph_error_t igraph_i_weighted_clique_number(const igraph_t *graph, +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); - int res_int; if (vcount == 0) { - if (res) { - *res = 0; - } + *res = 0; return IGRAPH_SUCCESS; } - IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); + igraph_to_cliquer(graph, &g); IGRAPH_FINALLY(graph_free, g); IGRAPH_CHECK(set_weights(vertex_weights, g)); - igraph_cliquer_opt.user_function = check_interruption_callback; + igraph_cliquer_opt.user_function = NULL; - IGRAPH_CHECK(clique_max_weight(g, &igraph_cliquer_opt, &res_int)); + /* 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); - if (res) { - *res = res_int; - } - return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/cliques/cliques.c b/src/vendor/cigraph/src/cliques/cliques.c index 0e7d07f2b39..23ce9ae7f4a 100644 --- a/src/vendor/cigraph/src/cliques/cliques.c +++ b/src/vendor/cigraph/src/cliques/cliques.c @@ -28,6 +28,8 @@ #include "igraph_constants.h" #include "igraph_adjlist.h" #include "igraph_interface.h" +#include "igraph_progress.h" +#include "igraph_stack.h" #include "cliques/cliquer_internal.h" #include "core/interruption.h" @@ -35,39 +37,54 @@ #include /* memset */ -static igraph_error_t igraph_i_find_k_indsets( +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, - igraph_integer_t size, - const igraph_integer_t *member_storage, - igraph_integer_t **new_member_storage, - igraph_integer_t old_count, - igraph_integer_t *new_count, - igraph_vector_int_t *neis) { - - igraph_integer_t j, k, l, m, n, new_member_storage_size; - const igraph_integer_t *c1, *c2; - igraph_integer_t v1, v2; + 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_count), - igraph_integer_t); - IGRAPH_CHECK_OOM(*new_member_storage, "igraph_independent_vertex_sets failed"); - - new_member_storage_size = size * old_count; + (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-indsets and see if they can be merged */ - for (j = 0; j < old_count; j++) { - for (k = j + 1; k < old_count; k++) { + /* 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 indsets are represented by their vertex indices in increasing - * order, two indsets can be merged iff they have exactly the same - * indices excluding one AND there is no edge between the two different + /* 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); @@ -75,88 +92,214 @@ static igraph_error_t igraph_i_find_k_indsets( 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 */ - IGRAPH_ASSERT(l != size-1); - /* Assuming that j (*new_member_storage)[m - 1]) { - (*new_member_storage)[m++] = v2; - n = m; + /* Now, if l != size-1, the two vectors had a difference in more than + * one place, so the whole clique is invalid. */ + if (l != size - 1) { + /* Step back in new_member_storage */ + m = n; + } else { + /* v1 and v2 are the two different vertices. Check for an edge + * if we are looking for cliques and check for the absence of an + * edge if we are looking for independent vertex sets */ + IGRAPH_CHECK(igraph_neighbors(graph, neis, (igraph_integer_t) v1, + IGRAPH_ALL)); + l = igraph_vector_search(neis, 0, v2, 0); + if ((l && !independent_vertices) || (!l && independent_vertices)) { + /* Found a new clique, step forward in new_member_storage */ + if (m == n || v2 > (*new_member_storage)[m - 1]) { + (*new_member_storage)[m++] = v2; + n = m; + } else { + m = n; + } } 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_integer_t); - IGRAPH_CHECK_OOM(*new_member_storage, "igraph_independent_vertex_sets failed"); - new_member_storage_size *= 2; - IGRAPH_FINALLY(igraph_free, *new_member_storage); + /* 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 independent vertex sets we have found */ - *new_count = n / size; + /* Calculate how many cliques we have found */ + *clique_count = n / size; IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + 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; } /** @@ -175,11 +318,14 @@ static igraph_error_t igraph_i_find_k_indsets( * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html * * \param graph The input graph. - * \param res Pointer to a list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. - * \param min_size Integer specifying the minimum size of the cliques to be + * \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 specifying the maximum size of the cliques to be + * \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. * @@ -189,7 +335,7 @@ static igraph_error_t igraph_i_find_k_indsets( * * \example examples/simple/igraph_cliques.c */ -igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, +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); } @@ -211,9 +357,9 @@ igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *r * 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 specifying the minimum size of the cliques to be + * \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 specifying the maximum size of the cliques to be + * \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. * @@ -222,7 +368,7 @@ igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *r * Time complexity: Exponential * */ -igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, +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); } @@ -236,8 +382,8 @@ igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *h * 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_int_t. Destroying and - * freeing this vector is left up to the user. Use \ref igraph_vector_int_destroy() + * 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 @@ -245,12 +391,12 @@ igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *h * Patric R. J. Östergård, http://users.aalto.fi/~pat/cliquer.html * * \param graph The input graph. - * \param min_size Integer specifying the minimum size of the cliques to be + * \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 specifying the maximum size of the cliques to be + * \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. + * See also \ref igraph_clique_handler_t. * \param arg Extra argument to supply to \p cliquehandler_fn. * \return Error code. * @@ -259,7 +405,7 @@ igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *h * Time complexity: Exponential * */ -igraph_error_t igraph_cliques_callback(const igraph_t *graph, +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); @@ -285,11 +431,14 @@ igraph_error_t igraph_cliques_callback(const igraph_t *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 list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. - * \param min_weight Integer specifying the minimum weight of the cliques to be + * \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 specifying the maximum weight of the cliques to be + * \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. @@ -299,8 +448,8 @@ igraph_error_t igraph_cliques_callback(const igraph_t *graph, * Time complexity: Exponential * */ -igraph_error_t igraph_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, +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); @@ -329,16 +478,19 @@ igraph_error_t igraph_weighted_cliques(const igraph_t *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 list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. + * \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 */ -igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res) { +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 { @@ -370,7 +522,7 @@ igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, * Time complexity: TODO * */ -igraph_error_t igraph_weighted_clique_number(const igraph_t *graph, +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); @@ -384,16 +536,16 @@ igraph_error_t igraph_weighted_clique_number(const igraph_t *graph, } } -typedef igraph_error_t(*igraph_i_maximal_clique_func_t)(const igraph_vector_int_t*, void*, igraph_bool_t*); +typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*); typedef struct { - igraph_vector_int_list_t* result; + igraph_vector_ptr_t* result; igraph_integer_t min_size; igraph_integer_t max_size; } igraph_i_maximal_clique_data_t; -static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets( +static int igraph_i_maximal_or_largest_cliques_or_indsets( const igraph_t *graph, - igraph_vector_int_list_t *res, + igraph_vector_ptr_t *res, igraph_integer_t *clique_number, igraph_bool_t keep_only_largest, igraph_bool_t complementer); @@ -418,11 +570,14 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets( * 6:505--517, 1977. * * \param graph The input graph. - * \param res Pointer to a list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. - * \param min_size Integer specifying the minimum size of the sets to be + * \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 specifying the maximum size of the sets to be + * \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. * @@ -433,101 +588,11 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets( * * \example examples/simple/igraph_independent_sets.c */ -igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res, +int igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_ptr_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { - igraph_integer_t no_of_nodes; - igraph_vector_int_t neis, *indset; - igraph_integer_t *member_storage = 0, *new_member_storage, *c1; - igraph_vector_int_t new_member_storage_view; - igraph_integer_t i, j, k, indset_count, old_indset_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_int_list_clear(res); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - - /* Will be resized later, if needed. */ - member_storage = IGRAPH_CALLOC(1, igraph_integer_t); - if (member_storage == 0) { - IGRAPH_ERROR("igraph_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - 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_integer_t); - if (new_member_storage == 0) { - IGRAPH_ERROR("igraph_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, new_member_storage); - - for (i = 0; i < no_of_nodes; i++) { - new_member_storage[i] = i; - } - indset_count = no_of_nodes; - old_indset_count = 0; - - /* Add size 1 indsets if requested */ - if (min_size <= 1) { - IGRAPH_CHECK(igraph_vector_int_list_resize(res, no_of_nodes)); - for (i = 0; i < no_of_nodes; i++) { - indset = igraph_vector_int_list_get_ptr(res, i); - IGRAPH_CHECK(igraph_vector_int_push_back(indset, i)); - } - } - - for (i = 2; i <= max_size && indset_count > 1; i++) { - - /* Here new_member_storage contains the independent vertex sets 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_indset_count = indset_count; - - IGRAPH_ALLOW_INTERRUPTION(); - - /* Calculate the independent vertex sets */ - - IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_i_find_k_indsets(graph, i, member_storage, - &new_member_storage, - old_indset_count, - &indset_count, - &neis)); - 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 < indset_count; j++, k += i) { - igraph_vector_int_view(&new_member_storage_view, new_member_storage + k, i); - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &new_member_storage_view)); - } - } - - } /* i <= max_size && clique_count != 0 */ - - igraph_free(member_storage); - igraph_free(new_member_storage); - igraph_vector_int_destroy(&neis); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; + return igraph_i_cliques(graph, res, min_size, max_size, 1); } /** @@ -546,8 +611,8 @@ igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, * 6:505--517, 1977. * * \param graph The input graph. - * \param res Pointer to a list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. + * \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 @@ -556,15 +621,15 @@ igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, * Time complexity: TODO */ -igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res) { +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_int_t deg; /* Degrees of individual nodes */ + 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; @@ -572,51 +637,59 @@ typedef struct igraph_i_max_ind_vsets_data_t { igraph_bool_t keep_only_largest; /* True if we keep only the largest sets */ } igraph_i_max_ind_vsets_data_t; -static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( +static int igraph_i_maximal_independent_vertex_sets_backtrack( const igraph_t *graph, - igraph_vector_int_list_t *res, + igraph_vector_ptr_t *res, igraph_i_max_ind_vsets_data_t *clqdata, igraph_integer_t level) { - igraph_integer_t v1, v2, v3, c, j, k; + long int v1, v2, v3, c, j, k; igraph_vector_int_t *neis1, *neis2; igraph_bool_t f; - igraph_integer_t it_state; - igraph_vector_int_t vec; + igraph_integer_t j1; + long int it_state; IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - if (level >= clqdata->matrix_size - 1) { igraph_integer_t size = 0; if (res) { - igraph_vector_int_clear(&vec); - - for (v1 = 0; v1 < clqdata->matrix_size; v1++) { + 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_int_push_back(&vec, v1)); + IGRAPH_CHECK(igraph_vector_push_back(vec, v1)); } - } - - size = igraph_vector_int_size(&vec); + size = (igraph_integer_t) igraph_vector_size(vec); if (!clqdata->keep_only_largest) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &vec)); + 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 */ - igraph_vector_int_list_clear(res); - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &vec)); + 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_int_list_push_back_copy(res, &vec)); + 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++) { + 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; @@ -629,7 +702,7 @@ static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( c = 0; j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = VECTOR(*neis1)[j]) <= level) { + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { c++; } @@ -640,33 +713,34 @@ static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( /* If there are no such nodes... */ j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = VECTOR(*neis1)[j]) <= level) { + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]++; j++; } - IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); + 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 = VECTOR(*neis1)[j]) <= level) { + (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] = c; - IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, 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 = VECTOR(*neis1)[j]) <= level) { + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { - IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], j)); + 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 = VECTOR(*neis2)[k]) <= level) { + (v3 = (long int) VECTOR(*neis2)[k]) <= level) { clqdata->IS[v3]--; if (clqdata->IS[v3] == 0) { f = 0; @@ -679,23 +753,24 @@ static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( } if (f) { - IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); + 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 = VECTOR(*neis1)[j]) <= level) { + (v2 = (long int) VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]--; j++; } it_state = 0; - while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j)) { - v2 = VECTOR(*neis1)[j]; + 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 = VECTOR(*neis2)[k]) <= level) { + (v3 = (long int) VECTOR(*neis2)[k]) <= level) { clqdata->IS[v3]++; k++; } @@ -704,14 +779,11 @@ static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( } } - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } static void igraph_i_free_set_array(igraph_set_t* array) { - igraph_integer_t i = 0; + long int i = 0; while (igraph_set_inited(array + i)) { igraph_set_destroy(array + i); i++; @@ -743,8 +815,11 @@ static void igraph_i_free_set_array(igraph_set_t* array) { * use \ref igraph_independence_number() instead. * * \param graph The input graph. - * \param res Pointer to a list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. + * \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 @@ -752,10 +827,10 @@ static void igraph_i_free_set_array(igraph_set_t* array) { * * Time complexity: TODO. */ -igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, - igraph_vector_int_list_t *res) { +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_vcount(graph), i; + 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"); @@ -771,18 +846,18 @@ igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) { - IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -790,7 +865,7 @@ igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); } - igraph_vector_int_list_clear(res); + igraph_vector_ptr_clear(res); /* Do the show */ clqdata.largest_set_size = 0; @@ -801,11 +876,11 @@ igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_int_destroy(&clqdata.deg); + igraph_vector_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /** @@ -832,9 +907,9 @@ igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, * * Time complexity: TODO. */ -igraph_error_t igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { +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_vcount(graph), i; + 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"); @@ -850,18 +925,18 @@ igraph_error_t igraph_independence_number(const igraph_t *graph, igraph_integer_ clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) { - IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -879,48 +954,53 @@ igraph_error_t igraph_independence_number(const igraph_t *graph, igraph_integer_ igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_int_destroy(&clqdata.deg); + igraph_vector_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /*************************************************************************/ /* MAXIMAL CLIQUES, LARGEST CLIQUES */ /*************************************************************************/ -static igraph_error_t igraph_i_maximal_cliques_store_max_size(const igraph_vector_int_t* clique, void* data) { +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_int_size(clique)) { - *result = igraph_vector_int_size(clique); + if (*result < igraph_vector_size(clique)) { + *result = (igraph_integer_t) igraph_vector_size(clique); } - return IGRAPH_SUCCESS; + igraph_vector_destroy(clique); + igraph_Free(clique); + return 1; } -static igraph_error_t igraph_i_largest_cliques_store(const igraph_vector_int_t* clique, void* data) { - igraph_vector_int_list_t* result = (igraph_vector_int_list_t*)data; - igraph_integer_t n; +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_int_list_empty(result)) { - igraph_vector_int_t* first; - - n = igraph_vector_int_size(clique); - first = igraph_vector_int_list_get_ptr(result, 0); - if (n < igraph_vector_int_size(first)) { - return IGRAPH_SUCCESS; + 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_int_size(first)) { - igraph_vector_int_list_clear(result); + 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_int_list_push_back_copy(result, clique)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(result, clique)); - return IGRAPH_SUCCESS; + return 1; } /** @@ -946,8 +1026,9 @@ static igraph_error_t igraph_i_largest_cliques_store(const igraph_vector_int_t* * these two versions. * * \param graph The input graph. - * \param res Pointer to a list of integer vectors, the result will be stored - * here. The pointer vector will be resized if needed. + * \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() @@ -955,9 +1036,11 @@ static igraph_error_t igraph_i_largest_cliques_store(const igraph_vector_int_t* * Time complexity: O(3^(|V|/3)) worst case. */ -igraph_error_t igraph_largest_cliques(const igraph_t *graph, igraph_vector_int_list_t *res) { - igraph_vector_int_list_clear(res); +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; } @@ -981,18 +1064,18 @@ igraph_error_t igraph_largest_cliques(const igraph_t *graph, igraph_vector_int_l * * Time complexity: O(3^(|V|/3)) worst case. */ -igraph_error_t igraph_clique_number(const igraph_t *graph, igraph_integer_t *no) { +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 igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, - igraph_vector_int_list_t *res, +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_vcount(graph), i; + 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"); @@ -1012,18 +1095,18 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igrap 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INT_INIT_FINALLY(&clqdata.deg, no_of_nodes); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -1032,7 +1115,7 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igrap } if (res) { - igraph_vector_int_list_clear(res); + igraph_vector_ptr_clear(res); } /* Do the show */ @@ -1044,7 +1127,7 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igrap igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_int_destroy(&clqdata.deg); + igraph_vector_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); @@ -1052,5 +1135,5 @@ static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igrap if (clique_number) { *clique_number = clqdata.largest_set_size; } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/cliques/glet.c b/src/vendor/cigraph/src/cliques/glet.c index 8f0f4b02ceb..74d14add203 100644 --- a/src/vendor/cigraph/src/cliques/glet.c +++ b/src/vendor/cigraph/src/cliques/glet.c @@ -26,7 +26,6 @@ #include "igraph_conversion.h" #include "igraph_constructors.h" #include "igraph_cliques.h" -#include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_operators.h" #include "igraph_qsort.h" @@ -75,12 +74,12 @@ typedef struct { igraph_vector_int_t *resultids; igraph_t *result; igraph_vector_t *resultweights; - igraph_integer_t nc; + 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; - igraph_integer_t i; + int i; if (data->resultids) { for (i = 0; i < data->nc; i++) { igraph_vector_int_destroy(&data->resultids[i]); @@ -107,11 +106,11 @@ static void igraph_i_subclique_next_free(void *ptr) { * * \param graph Input graph. * \param weight Edge weights. - * \param ids The IDs of the vertices in the input graph. - * \param cliques A list of \ref igraph_vector_int_t, vertex IDs for cliques. + * \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 + * \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. @@ -120,10 +119,10 @@ static void igraph_i_subclique_next_free(void *ptr) { * */ -static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, +static int igraph_i_subclique_next(const igraph_t *graph, const igraph_vector_t *weights, const igraph_vector_int_t *ids, - const igraph_vector_int_list_t *cliques, + const igraph_vector_ptr_t *cliques, igraph_t **result, igraph_vector_t **resultweights, igraph_vector_int_t **resultids, @@ -136,11 +135,11 @@ static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, igraph_vector_int_t mark, map; igraph_vector_int_t edges; - igraph_vector_int_t neis, newedges; - igraph_integer_t c, nc = igraph_vector_int_list_size(cliques); + 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 = { NULL, NULL, NULL, nc }; + 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); @@ -151,46 +150,53 @@ static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, } IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata); - *resultids = IGRAPH_CALLOC(nc, igraph_vector_int_t); - IGRAPH_CHECK_OOM(*resultids, "Cannot calculate next cliques."); + if (!*resultids) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } freedata.resultids = *resultids; - *resultweights = IGRAPH_CALLOC(nc, igraph_vector_t); - IGRAPH_CHECK_OOM(*resultweights, "Cannot calculate next cliques."); + if (!*resultweights) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } freedata.resultweights = *resultweights; - *result = IGRAPH_CALLOC(nc, igraph_t); - IGRAPH_CHECK_OOM(*result, "Cannot calculate next cliques."); + if (!*result) { + IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); + } freedata.result = *result; - IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&map, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); + 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_CHECK(igraph_vector_resize(clique_thr, nc)); + igraph_vector_resize(clique_thr, nc); } - if (next_thr) { - IGRAPH_CHECK(igraph_vector_resize(next_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_int_t *clique = igraph_vector_int_list_get_ptr(cliques, 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_int_size(clique); + 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_int_clear(&newedges); + igraph_vector_clear(&newedges); /* --------------------------------------------------- */ @@ -201,15 +207,15 @@ static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, for (v = 0; v < clsize; v++) { igraph_integer_t i, neilen, node = VECTOR(*clique)[v]; - IGRAPH_CHECK(igraph_incident(graph, &neis, node, IGRAPH_ALL)); - neilen = igraph_vector_int_size(&neis); + 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_CHECK(igraph_vector_int_push_back(&edges, edge)); + igraph_vector_int_push_back(&edges, edge); if (w < minweight) { nextweight = minweight; minweight = w; @@ -237,8 +243,8 @@ static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, /* Now we create the subgraph from the edges above the next threshold, and their incident vertices. */ - IGRAPH_CHECK(igraph_vector_int_init(newids, 0)); - IGRAPH_CHECK(igraph_vector_init(neww, 0)); + 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 @@ -250,53 +256,52 @@ static igraph_error_t igraph_i_subclique_next(const igraph_t *graph, igraph_integer_t edge = VECTOR(edges)[e]; igraph_integer_t from, to; igraph_real_t w = VECTOR(*weights)[edge]; - IGRAPH_CHECK(igraph_edge(graph, edge, &from, &to)); + 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_CHECK(igraph_vector_int_push_back(newids, VECTOR(*ids)[from])); + 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_CHECK(igraph_vector_int_push_back(newids, VECTOR(*ids)[to])); + igraph_vector_int_push_back(newids, VECTOR(*ids)[to]); } - IGRAPH_CHECK(igraph_vector_push_back(neww, w)); - IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[from])); - IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[to])); + igraph_vector_push_back(neww, w); + igraph_vector_push_back(&newedges, VECTOR(map)[from]); + igraph_vector_push_back(&newedges, VECTOR(map)[to]); } } - IGRAPH_CHECK(igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED)); + igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED); /* --------------------------------------------------- */ } /* c < nc */ - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); igraph_vector_int_destroy(&edges); igraph_vector_int_destroy(&mark); igraph_vector_int_destroy(&map); - igraph_vector_int_destroy(&newedges); + igraph_vector_destroy(&newedges); IGRAPH_FINALLY_CLEAN(6); /* + freedata */ - return IGRAPH_SUCCESS; + return 0; } -static void igraph_i_graphlets_destroy_clique_list(igraph_vector_ptr_t *vl) { - igraph_integer_t i, n = igraph_vector_ptr_size(vl); +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_int_t *v = (igraph_vector_int_t*) VECTOR(*vl)[i]; + igraph_vector_t *v = (igraph_vector_t*) VECTOR(*vl)[i]; if (v) { - igraph_vector_int_destroy(v); - IGRAPH_FREE(v); + igraph_vector_destroy(v); } } igraph_vector_ptr_destroy(vl); } -static igraph_error_t igraph_i_graphlets(const igraph_t *graph, +static int igraph_i_graphlets(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds, @@ -308,43 +313,45 @@ static igraph_error_t igraph_i_graphlets(const igraph_t *graph, results to 'cliques' and 'thresholds' and uses the supplied 'startthr' */ - igraph_vector_int_list_t mycliques; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t subv; + igraph_vector_ptr_t mycliques; + int no_of_edges = igraph_ecount(graph); + igraph_vector_t subv; igraph_t subg; - igraph_integer_t i, j, nocliques; - igraph_t *newgraphs = NULL; - igraph_vector_t *newweights = NULL; - igraph_vector_int_t *newids = NULL; + 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 = { NULL, NULL, NULL, 0 }; + igraph_i_subclique_next_free_t freedata = { 0, 0, 0, 0 }; - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&mycliques, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&subv, 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_int_push_back(&subv, i)); + IGRAPH_CHECK(igraph_vector_push_back(&subv, i)); } } - IGRAPH_CHECK(igraph_subgraph_from_edges(graph, &subg, igraph_ess_vector(&subv), /*delete_vertices=*/ 0)); + igraph_subgraph_edges(graph, &subg, igraph_ess_vector(&subv), + /*delete_vertices=*/ 0); IGRAPH_FINALLY(igraph_destroy, &subg); - IGRAPH_CHECK(igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0)); + igraph_maximal_cliques(&subg, &mycliques, /*min_size=*/ 0, /*max_size=*/ 0); igraph_destroy(&subg); - igraph_vector_int_destroy(&subv); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); + nocliques = igraph_vector_ptr_size(&mycliques); - nocliques = igraph_vector_int_list_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_CHECK(igraph_i_subclique_next( - graph, weights, ids, &mycliques, &newgraphs, &newweights, &newids, - &clique_thr, &next_thr - )); + igraph_i_subclique_next(graph, weights, ids, &mycliques, + &newgraphs, &newweights, &newids, + &clique_thr, &next_thr); freedata.result = newgraphs; freedata.resultids = newids; @@ -353,45 +360,36 @@ static igraph_error_t igraph_i_graphlets(const igraph_t *graph, IGRAPH_FINALLY(igraph_i_subclique_next_free, &freedata); /* Store cliques at the current level */ - IGRAPH_CHECK(igraph_vector_append(thresholds, &clique_thr)); - IGRAPH_CHECK(igraph_vector_ptr_resize(cliques, igraph_vector_ptr_size(cliques) + nocliques)); - for (i = 0, j = igraph_vector_ptr_size(cliques) - 1; i < nocliques; i++, j--) { - igraph_vector_int_t *cl = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(cl, "Cannot find graphlets."); - IGRAPH_FINALLY(igraph_free, cl); - - *cl = igraph_vector_int_list_pop_back(&mycliques); - - /* From this point onwards, _we_ own the clique and not `mycliques'. - * We pass on the ownership to `cliques' */ - VECTOR(*cliques)[j] = cl; - IGRAPH_FINALLY_CLEAN(1); - - igraph_integer_t k, n = igraph_vector_int_size(cl); - for (k = 0; k < n; k++) { - igraph_integer_t node = VECTOR(*cl)[k]; - VECTOR(*cl)[k] = VECTOR(*ids)[node]; + 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_int_sort(cl); + igraph_vector_sort(cl); } + igraph_vector_ptr_append(cliques, &mycliques); /* Recursive calls for cliques found */ - for (i = 0; i < nocliques; i++) { + 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_CHECK(igraph_i_graphlets(g, w_sub, cliques, thresholds, ids_sub, VECTOR(next_thr)[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_int_list_destroy(&mycliques); /* contents was copied over to `cliques' */ + igraph_vector_ptr_destroy(&mycliques); /* contents was copied over */ IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } typedef struct { @@ -401,12 +399,12 @@ typedef struct { 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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_int_t *v_a, *v_b; - igraph_integer_t s_a, s_b; + igraph_vector_t *v_a, *v_b; + int s_a, s_b; if (t_a < t_b) { return -1; @@ -414,10 +412,10 @@ static int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void * return 1; } - v_a = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*aa]; - v_b = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*bb]; - s_a = igraph_vector_int_size(v_a); - s_b = igraph_vector_int_size(v_b); + 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; @@ -428,7 +426,7 @@ static int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void * } } -static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, +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 @@ -439,27 +437,31 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, superset, we only need to check the cliques next in the list, until their threshold is different. */ - igraph_integer_t i, iptr, nocliques = igraph_vector_ptr_size(cliques); + int i, iptr, nocliques = igraph_vector_ptr_size(cliques); igraph_vector_int_t order; igraph_i_graphlets_filter_t sortdata = { cliques, thresholds }; - IGRAPH_CHECK(igraph_vector_int_init_range(&order, 0, nocliques)); + 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(VECTOR(order)[0]), &sortdata, + igraph_qsort_r(VECTOR(order), nocliques, sizeof(int), &sortdata, igraph_i_graphlets_filter_cmp); for (i = 0; i < nocliques - 1; i++) { - igraph_integer_t ri = VECTOR(order)[i]; - igraph_vector_int_t *needle = VECTOR(*cliques)[ri]; + int ri = VECTOR(order)[i]; + igraph_vector_t *needle = VECTOR(*cliques)[ri]; igraph_real_t thr_i = VECTOR(*thresholds)[ri]; - igraph_integer_t n_i = igraph_vector_int_size(needle); + int n_i = igraph_vector_size(needle); + int j = i + 1; - for (igraph_integer_t j = i + 1; j < nocliques; j++) { - igraph_integer_t rj = VECTOR(order)[j]; + for (j = i + 1; j < nocliques; j++) { + int rj = VECTOR(order)[j]; igraph_real_t thr_j = VECTOR(*thresholds)[rj]; - igraph_vector_int_t *hay; - igraph_integer_t n_j, pi = 0, pj = 0; + igraph_vector_t *hay; + int n_j, pi = 0, pj = 0; /* Done, not found */ if (thr_j != thr_i) { @@ -468,15 +470,15 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, /* Check size of hay */ hay = VECTOR(*cliques)[rj]; - n_j = igraph_vector_int_size(hay); + 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) { - igraph_integer_t ei = VECTOR(*needle)[pi]; - igraph_integer_t ej = VECTOR(*hay)[pj]; + int ei = VECTOR(*needle)[pi]; + int ej = VECTOR(*hay)[pj]; if (ei < ej) { break; } else if (ei > ej) { @@ -487,7 +489,7 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, } if (pi == n_i) { /* Found, delete immediately */ - igraph_vector_int_destroy(needle); + igraph_vector_destroy(needle); igraph_free(needle); VECTOR(*cliques)[ri] = 0; break; @@ -497,20 +499,20 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, /* Remove null pointers from the list of cliques */ for (i = 0, iptr = 0; i < nocliques; i++) { - igraph_vector_int_t *v = VECTOR(*cliques)[i]; + igraph_vector_t *v = VECTOR(*cliques)[i]; if (v) { VECTOR(*cliques)[iptr] = v; VECTOR(*thresholds)[iptr] = VECTOR(*thresholds)[i]; iptr++; } } - IGRAPH_CHECK(igraph_vector_ptr_resize(cliques, iptr)); - IGRAPH_CHECK(igraph_vector_resize(thresholds, iptr)); + igraph_vector_ptr_resize(cliques, iptr); + igraph_vector_resize(thresholds, iptr); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -520,9 +522,10 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, * \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 list of integer vectors. The graphlet basis is - * stored here. Each element of the list is an integer vector of - * vertex IDs, encoding a single basis subgraph. + * \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. @@ -531,18 +534,17 @@ static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, * See also: \ref igraph_graphlets() and \ref igraph_graphlets_project(). */ -igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, +int igraph_graphlets_candidate_basis(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *cliques, + igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_integer_t i, no_of_cliques; - igraph_vector_ptr_t mycliques; + int i; /* Some checks */ if (weights == NULL) { @@ -557,70 +559,39 @@ igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, if (!simple) { IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); } - if (igraph_is_directed(graph)) { - /* When the graph is directed, mutual edges are effectively multi-edges as we - * are ignoring edge directions. */ - igraph_bool_t has_mutual; - IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); - if (has_mutual) { - IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); - } - } - - /* Internally, we will still use igraph_vector_ptr_t instead of - * igraph_vector_int_list_t to manage the list of cliques; this is because - * we are going to append & filter the list and it's more complicated to - * do with an igraph_vector_int_list_t */ - IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0)); - IGRAPH_FINALLY(igraph_i_graphlets_destroy_clique_list, &mycliques); - - igraph_vector_int_list_clear(cliques); - igraph_vector_clear(thresholds); minthr = igraph_vector_min(weights); - - IGRAPH_CHECK(igraph_vector_int_init_range(&ids, 0, no_of_nodes)); + 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_CHECK(igraph_i_graphlets(graph, weights, &mycliques, thresholds, &ids, minthr)); + igraph_i_graphlets(graph, weights, cliques, thresholds, &ids, minthr); igraph_vector_int_destroy(&ids); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(igraph_i_graphlets_filter(&mycliques, thresholds)); + igraph_i_graphlets_filter(cliques, thresholds); - /* Pass ownership of cliques in `mycliques' to `cliques' so the user does - * not have to work with igraph_vector_ptr_t */ - no_of_cliques = igraph_vector_ptr_size(&mycliques); - for (i = 0; i < no_of_cliques; i++) { - IGRAPH_CHECK(igraph_vector_int_list_push_back( - cliques, VECTOR(mycliques)[i] - )); - IGRAPH_FREE(VECTOR(mycliques)[i]); - } - - /* `mycliques' is now empty so we can clear and destroy */ - igraph_vector_ptr_clear(&mycliques); - igraph_vector_ptr_destroy(&mycliques); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /* TODO: not made static because it is used by the R interface */ -igraph_error_t igraph_i_graphlets_project( - const igraph_t *graph, const igraph_vector_t *weights, - const igraph_vector_int_list_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, - igraph_integer_t niter, igraph_integer_t vid1 -) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_cliques = igraph_vector_int_list_size(cliques); +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_int_t edgelist; - igraph_vector_t newweights, normfact; - igraph_integer_t i, total_vertices, e, ptr, total_edges; + igraph_vector_t edgelist, newweights, normfact; + int i, total_vertices, e, ptr, total_edges; igraph_bool_t simple; /* Check arguments */ @@ -640,33 +611,26 @@ igraph_error_t igraph_i_graphlets_project( if (!simple) { IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); } - if (igraph_is_directed(graph)) { - /* When the graph is directed, mutual edges are effectively multi-edges as we - * are ignoring edge directions. */ - igraph_bool_t has_mutual; - IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); - if (has_mutual) { - IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); - } - } if (!startMu) { - IGRAPH_CHECK(igraph_vector_resize(Mu, no_cliques)); + 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_VECTOR_INT_INIT_FINALLY(&vclidx, no_of_nodes + 2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&celidx, no_cliques + 3); + 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_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); - igraph_integer_t j, n = igraph_vector_int_size(v); + 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++) { - igraph_integer_t vv = VECTOR(*v)[j] - vid1; + int vv = VECTOR(*v)[j] - vid1; VECTOR(vclidx)[vv + 2] += 1; } } @@ -678,34 +642,38 @@ igraph_error_t igraph_i_graphlets_project( } /* Create vertex-clique list, the cliques for each vertex. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&vcl, total_vertices); + 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_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); - igraph_integer_t j, n = igraph_vector_int_size(v); + igraph_vector_t *v = VECTOR(*cliques)[i]; + int j, n = igraph_vector_size(v); for (j = 0; j < n; j++) { - igraph_integer_t vv = VECTOR(*v)[j] - vid1; - igraph_integer_t p = VECTOR(vclidx)[vv + 1]; + 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_VECTOR_INT_INIT_FINALLY(&ecl, total_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eclidx, no_of_edges + 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, no_of_edges * 2); + 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++) { - igraph_integer_t from = VECTOR(edgelist)[i++]; - igraph_integer_t to = VECTOR(edgelist)[i++]; - igraph_integer_t from_s = VECTOR(vclidx)[from]; - igraph_integer_t from_e = VECTOR(vclidx)[from + 1]; - igraph_integer_t to_s = VECTOR(vclidx)[to]; - igraph_integer_t to_e = VECTOR(vclidx)[to + 1]; + 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) { - igraph_integer_t from_v = VECTOR(vcl)[from_s]; - igraph_integer_t to_v = VECTOR(vcl)[to_s]; + 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++; @@ -718,47 +686,50 @@ igraph_error_t igraph_i_graphlets_project( } VECTOR(eclidx)[e] = ptr; - igraph_vector_int_destroy(&edgelist); + igraph_vector_destroy(&edgelist); IGRAPH_FINALLY_CLEAN(1); /* Convert the edge-clique list to a clique-edge list */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&cel, total_edges); + IGRAPH_CHECK(igraph_vector_int_init(&cel, total_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &cel); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j; + int ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j; for (j = ecl_s; j < ecl_e; j++) { - igraph_integer_t cl = VECTOR(ecl)[j]; - igraph_integer_t epos = VECTOR(celidx)[cl + 1]; + 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_VECTOR_INIT_FINALLY(&normfact, no_cliques); + IGRAPH_CHECK(igraph_vector_init(&normfact, no_cliques)); + IGRAPH_FINALLY(igraph_vector_destroy, &normfact); for (i = 0; i < no_cliques; i++) { - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); - igraph_integer_t n = igraph_vector_int_size(v); + 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_VECTOR_INIT_FINALLY(&newweights, no_of_edges); + 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++) { - igraph_integer_t start = VECTOR(eclidx)[e]; - igraph_integer_t end = VECTOR(eclidx)[e + 1]; + int start = VECTOR(eclidx)[e]; + int end = VECTOR(eclidx)[e + 1]; VECTOR(newweights)[e] = 0.0001; while (start < end) { - igraph_integer_t clique = VECTOR(ecl)[start++]; + int clique = VECTOR(ecl)[start++]; VECTOR(newweights)[e] += VECTOR(*Mu)[clique]; } } for (e = 0; e < no_cliques; e++) { igraph_real_t sumratio = 0; - igraph_integer_t start = VECTOR(celidx)[e]; - igraph_integer_t end = VECTOR(celidx)[e + 1]; + int start = VECTOR(celidx)[e]; + int end = VECTOR(celidx)[e + 1]; while (start < end) { - igraph_integer_t edge = VECTOR(cel)[start++]; + int edge = VECTOR(cel)[start++]; sumratio += VECTOR(*weights)[edge] / VECTOR(newweights)[edge]; } VECTOR(*Mu)[e] *= sumratio / VECTOR(normfact)[e]; @@ -775,7 +746,7 @@ igraph_error_t igraph_i_graphlets_project( igraph_vector_int_destroy(&vclidx); IGRAPH_FINALLY_CLEAN(8); - return IGRAPH_SUCCESS; + return 0; } /** @@ -784,14 +755,13 @@ igraph_error_t igraph_i_graphlets_project( * * 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 + * 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 An initialized list of integer vectors. The graphlet basis is - * stored here. Each element of the list is an integer vector of - * vertex IDs, encoding a single basis subgraph. + * \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 @@ -806,25 +776,25 @@ igraph_error_t igraph_i_graphlets_project( * \ref igraph_graphlets_candidate_basis(). */ -igraph_error_t igraph_graphlets_project(const igraph_t *graph, +int igraph_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, - const igraph_vector_int_list_t *cliques, + const igraph_vector_ptr_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, - igraph_integer_t niter) { + 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_int_list_t *cliques; + 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -847,9 +817,9 @@ static int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b * \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 list of integer vectors. The graphlet basis is - * stored here. Each element of the list is an integer vector of - * vertex IDs, encoding a single basis subgraph. + * \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 @@ -860,35 +830,38 @@ static int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b * \ref igraph_graphlets_project(). */ -igraph_error_t igraph_graphlets(const igraph_t *graph, +int igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *cliques, - igraph_vector_t *Mu, igraph_integer_t niter) { + igraph_vector_ptr_t *cliques, + igraph_vector_t *Mu, int niter) { - igraph_integer_t nocliques; + int i, nocliques; igraph_vector_t thresholds; igraph_vector_int_t order; igraph_i_graphlets_order_t sortdata = { cliques, Mu }; - IGRAPH_VECTOR_INIT_FINALLY(&thresholds, 0); - IGRAPH_CHECK(igraph_graphlets_candidate_basis(graph, weights, cliques, &thresholds)); + 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_CHECK(igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ false, niter)); + igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ 0, niter); - nocliques = igraph_vector_int_list_size(cliques); - IGRAPH_CHECK(igraph_vector_int_init_range(&order, 0, nocliques)); + nocliques = igraph_vector_ptr_size(cliques); + igraph_vector_int_init(&order, nocliques); IGRAPH_FINALLY(igraph_vector_int_destroy, &order); - - igraph_qsort_r(VECTOR(order), nocliques, sizeof(VECTOR(order)[0]), &sortdata, + 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_CHECK(igraph_vector_int_list_permute(cliques, &order)); - IGRAPH_CHECK(igraph_vector_index_int(Mu, &order)); + igraph_vector_ptr_index_int(cliques, &order); + igraph_vector_index_int(Mu, &order); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/cliques/maximal_cliques.c b/src/vendor/cigraph/src/cliques/maximal_cliques.c index 3e39528a6dc..d4a3dd36038 100644 --- a/src/vendor/cigraph/src/cliques/maximal_cliques.c +++ b/src/vendor/cigraph/src/cliques/maximal_cliques.c @@ -36,108 +36,107 @@ #define CONCAT2(a,b) CONCAT2x(a,b) #define FUNCTION(name,sfx) CONCAT2(name,sfx) -static igraph_error_t igraph_i_maximal_cliques_reorder_adjlists( +static int igraph_i_maximal_cliques_reorder_adjlists( const igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, igraph_adjlist_t *adjlist); -static igraph_error_t igraph_i_maximal_cliques_select_pivot( +static int igraph_i_maximal_cliques_select_pivot( const igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, - igraph_integer_t *pivot, + int *pivot, igraph_vector_int_t *nextv, - igraph_integer_t oldPS, igraph_integer_t oldXE); + int oldPS, int oldXE); -static igraph_error_t igraph_i_maximal_cliques_down( +static int igraph_i_maximal_cliques_down( igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, + int PS, int PE, int XS, int XE, igraph_vector_int_t *pos, - igraph_adjlist_t *adjlist, igraph_integer_t mynextv, + igraph_adjlist_t *adjlist, int mynextv, igraph_vector_int_t *R, - igraph_integer_t *newPS, igraph_integer_t *newXE); + int *newPS, int *newXE); -static igraph_error_t igraph_i_maximal_cliques_PX( - igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t *PE, - igraph_integer_t *XS, igraph_integer_t XE, igraph_vector_int_t *pos, - igraph_adjlist_t *adjlist, igraph_integer_t v, +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 igraph_error_t igraph_i_maximal_cliques_up( - igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, - igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, +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 { \ - igraph_integer_t j; \ - printf("PX="); \ - for (j=0; j= sPS && avneipos <= sPE) { if (pp != avnei) { - igraph_integer_t tmp = *avnei; + int tmp = *avnei; *avnei = *pp; *pp = tmp; } @@ -149,39 +148,37 @@ static igraph_error_t igraph_i_maximal_cliques_reorder_adjlists( return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_maximal_cliques_select_pivot( +static int igraph_i_maximal_cliques_select_pivot( const igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t PE, - igraph_integer_t XS, igraph_integer_t XE, + int PS, int PE, int XS, int XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, - igraph_integer_t *pivot, + int *pivot, igraph_vector_int_t *nextv, - igraph_integer_t oldPS, igraph_integer_t oldXE) { + int oldPS, int oldXE) { igraph_vector_int_t *pivotvectneis; - igraph_integer_t j, pivotvectlen; - igraph_integer_t i, usize = -1; - igraph_integer_t soldPS = oldPS + 1, soldXE = oldXE + 1, sPS = PS + 1, sPE = PE + 1; + 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++) { - igraph_integer_t av = VECTOR(*PX)[i]; + int av = VECTOR(*PX)[i]; igraph_vector_int_t *avneis = igraph_adjlist_get(adjlist, av); - igraph_integer_t *avp = VECTOR(*avneis); - igraph_integer_t avlen = igraph_vector_int_size(avneis); - igraph_integer_t *ave = avp + avlen; - igraph_integer_t *avnei = avp, *pp = avp; + int *avp = VECTOR(*avneis); + int avlen = igraph_vector_int_size(avneis); + int *ave = avp + avlen; + int *avnei = avp, *pp = avp; for (; avnei < ave; avnei++) { - igraph_integer_t avneipos = VECTOR(*pos)[(*avnei)]; + int avneipos = VECTOR(*pos)[(int)(*avnei)]; if (avneipos < soldPS || avneipos > soldXE) { break; } if (avneipos >= sPS && avneipos <= sPE) { if (pp != avnei) { - igraph_integer_t tmp = *avnei; + int tmp = *avnei; *avnei = *pp; *pp = tmp; } @@ -199,12 +196,12 @@ static igraph_error_t igraph_i_maximal_cliques_select_pivot( pivotvectlen = igraph_vector_int_size(pivotvectneis); for (j = PS; j <= PE; j++) { - igraph_integer_t vcand = VECTOR(*PX)[j]; - igraph_bool_t nei = false; - igraph_integer_t k = 0; + int vcand = VECTOR(*PX)[j]; + igraph_bool_t nei = 0; + int k = 0; for (k = 0; k < pivotvectlen; k++) { - igraph_integer_t unv = VECTOR(*pivotvectneis)[k]; - igraph_integer_t unvpos = VECTOR(*pos)[unv]; + int unv = VECTOR(*pivotvectneis)[k]; + int unvpos = VECTOR(*pos)[unv]; if (unvpos < sPS || unvpos > sPE) { break; } @@ -221,31 +218,30 @@ static igraph_error_t igraph_i_maximal_cliques_select_pivot( return IGRAPH_SUCCESS; } -#define SWAP(p1,p2) do { \ - igraph_integer_t v1=VECTOR(*PX)[p1]; \ - igraph_integer_t v2=VECTOR(*PX)[p2]; \ - VECTOR(*PX)[p1] = v2; \ - VECTOR(*PX)[p2] = v1; \ - VECTOR(*pos)[v1] = (p2)+1; \ - VECTOR(*pos)[v2] = (p1)+1; \ +#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 igraph_error_t igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t PE, - igraph_integer_t XS, igraph_integer_t XE, +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, igraph_integer_t mynextv, + igraph_adjlist_t *adjlist, int mynextv, igraph_vector_int_t *R, - igraph_integer_t *newPS, igraph_integer_t *newXE) { + int *newPS, int *newXE) { igraph_vector_int_t *vneis = igraph_adjlist_get(adjlist, mynextv); - igraph_integer_t j, vneislen = igraph_vector_int_size(vneis); - igraph_integer_t sPS = PS + 1, sPE = PE + 1, sXS = XS + 1, sXE = XE + 1; + 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++) { - igraph_integer_t vnei = VECTOR(*vneis)[j]; - igraph_integer_t vneipos = VECTOR(*pos)[vnei]; + int vnei = VECTOR(*vneis)[j]; + int vneipos = VECTOR(*pos)[vnei]; if (vneipos >= sPS && vneipos <= sPE) { (*newPS)--; SWAP(vneipos - 1, *newPS); @@ -262,16 +258,16 @@ static igraph_error_t igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, #undef SWAP -static igraph_error_t igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, - igraph_integer_t PS, igraph_integer_t *PE, igraph_integer_t *XS, igraph_integer_t XE, - igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, igraph_integer_t v, - igraph_vector_int_t *H -) { +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) { - igraph_integer_t vpos = VECTOR(*pos)[v] - 1; - igraph_integer_t tmp = VECTOR(*PX)[*PE]; + int vpos = VECTOR(*pos)[v] - 1; + int tmp = VECTOR(*PX)[*PE]; IGRAPH_UNUSED(PS); + IGRAPH_UNUSED(PE); IGRAPH_UNUSED(XE); IGRAPH_UNUSED(adjlist); @@ -285,14 +281,12 @@ static igraph_error_t igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_maximal_cliques_up( - igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, - igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, - igraph_adjlist_t *adjlist, - igraph_vector_int_t *R, - igraph_vector_int_t *H -) { - igraph_integer_t vv; +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(PE); @@ -302,8 +296,8 @@ static igraph_error_t igraph_i_maximal_cliques_up( igraph_vector_int_pop_back(R); while ((vv = igraph_vector_int_pop_back(H)) != -1) { - igraph_integer_t vvpos = VECTOR(*pos)[vv]; - igraph_integer_t tmp = VECTOR(*PX)[XS]; + int vvpos = VECTOR(*pos)[vv]; + int tmp = VECTOR(*PX)[XS]; VECTOR(*PX)[XS] = vv; VECTOR(*PX)[vvpos - 1] = tmp; VECTOR(*pos)[vv] = XS + 1; @@ -311,7 +305,7 @@ static igraph_error_t igraph_i_maximal_cliques_up( PE++; XS++; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -340,7 +334,7 @@ static igraph_error_t igraph_i_maximal_cliques_up( * * \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_int_t + * 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 @@ -360,10 +354,10 @@ static igraph_error_t igraph_i_maximal_cliques_up( * \example examples/simple/igraph_maximal_cliques.c */ -igraph_error_t igraph_maximal_cliques( - const igraph_t *graph, igraph_vector_int_list_t *res, - igraph_integer_t min_size, igraph_integer_t max_size -); +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" @@ -371,8 +365,9 @@ igraph_error_t igraph_maximal_cliques( /** * \function igraph_maximal_cliques_count - * \brief Count the number of maximal cliques in a graph. + * 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 @@ -396,7 +391,7 @@ igraph_error_t igraph_maximal_cliques( * \example examples/simple/igraph_maximal_cliques.c */ -igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, +int igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); @@ -432,7 +427,7 @@ igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, * */ -igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, +int igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); @@ -443,18 +438,21 @@ igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, /** * \function igraph_maximal_cliques_subset - * \brief Maximal cliques for a subset of initial vertices. + * 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 a list of integer vectors; the cliques will be + * \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. @@ -473,11 +471,13 @@ igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, * */ -igraph_error_t igraph_maximal_cliques_subset( - const igraph_t *graph, const igraph_vector_int_t *subset, - igraph_vector_int_list_t *res, igraph_integer_t *no, - FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size -); +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" @@ -490,14 +490,16 @@ igraph_error_t igraph_maximal_cliques_subset( * * 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_int_t. The vector is - * owned by the maximal clique search routine so users are expected to make a - * copy of the vector using \ref igraph_vector_int_init_copy() if they want to - * hold on to it. + * 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. @@ -515,7 +517,7 @@ igraph_error_t igraph_maximal_cliques_subset( * */ -igraph_error_t igraph_maximal_cliques_callback(const igraph_t *graph, +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); @@ -555,7 +557,7 @@ igraph_error_t igraph_maximal_cliques_callback(const igraph_t *graph, * */ -igraph_error_t igraph_maximal_cliques_hist(const igraph_t *graph, +int igraph_maximal_cliques_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); diff --git a/src/vendor/cigraph/src/cliques/maximal_cliques_template.h b/src/vendor/cigraph/src/cliques/maximal_cliques_template.h index be0824bc258..e3f3da7b53c 100644 --- a/src/vendor/cigraph/src/cliques/maximal_cliques_template.h +++ b/src/vendor/cigraph/src/cliques/maximal_cliques_template.h @@ -22,16 +22,24 @@ */ #ifdef IGRAPH_MC_ORIG -#define RESTYPE igraph_vector_int_list_t *res +#define RESTYPE igraph_vector_ptr_t *res #define RESNAME res #define SUFFIX #define RECORD do { \ - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, R)); \ + 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) { \ - igraph_integer_t hcapacity = igraph_vector_capacity(hist); \ - igraph_integer_t j; \ - igraph_error_t err; \ + 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); /* LCOV_EXCL_LINE */ \ + 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 PREPARE \ +#define FINALLY \ igraph_vector_clear(hist); \ - IGRAPH_CHECK(igraph_vector_reserve(hist, 50)); /* initially reserve space for 50 elements */ + 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 */ - igraph_integer_t clsize = igraph_vector_int_size(R); + 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 */ - igraph_integer_t pivot, mynextv; + 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) { - igraph_integer_t newPS, newXE; + int newPS, newXE; /* Going down, prepare */ IGRAPH_CHECK(igraph_i_maximal_cliques_down( @@ -194,10 +244,10 @@ static igraph_error_t FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)( /* 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 IGRAPH_SUCCESS; + return 0; } -igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( +int FUNCTION(igraph_maximal_cliques, SUFFIX)( const igraph_t *graph, RESTYPE, igraph_integer_t min_size, @@ -206,13 +256,12 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( /* Implementation details. TODO */ igraph_vector_int_t PX, R, H, pos, nextv; - igraph_vector_int_t coreness; - igraph_vector_int_t order; + igraph_vector_t coreness, order; igraph_vector_int_t rank; /* TODO: this is not needed */ - igraph_integer_t i, ii, nn, no_of_nodes = igraph_vcount(graph); + 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; - igraph_error_t err; + int err; IGRAPH_UNUSED(nn); if (igraph_is_directed(graph)) { @@ -220,40 +269,40 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( "calculation"); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&coreness, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&coreness, no_of_nodes); IGRAPH_CHECK(igraph_coreness(graph, &coreness, /*mode=*/ IGRAPH_ALL)); - IGRAPH_CHECK(igraph_vector_int_qsort_ind(&coreness, &order, IGRAPH_ASCENDING)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&coreness, &order, /*descending=*/ 0)); for (ii = 0; ii < no_of_nodes; ii++) { - igraph_integer_t v = VECTOR(order)[ii]; + int v = VECTOR(order)[ii]; VECTOR(rank)[v] = ii; } - igraph_vector_int_destroy(&coreness); + igraph_vector_destroy(&coreness); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - IGRAPH_CHECK(igraph_adjlist_init(graph, &fulladjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + 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(&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); - PREPARE; + FINALLY; FOR_LOOP_OVER_VERTICES { - igraph_integer_t v; - igraph_integer_t vrank; + int v; + int vrank; igraph_vector_int_t *vneis; - igraph_integer_t vdeg; - igraph_integer_t Pptr, Xptr, PS, PE, XS, XE; - igraph_integer_t j; + int vdeg; + int Pptr, Xptr, PS, PE, XS, XE; + int j; FOR_LOOP_OVER_VERTICES_PREPARE; @@ -286,7 +335,7 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( VECTOR(R)[0] = v; for (j = 0; j < vdeg; j++) { - igraph_integer_t vx = VECTOR(*vneis)[j]; + int vx = VECTOR(*vneis)[j]; if (VECTOR(rank)[vx] > vrank) { VECTOR(PX)[Pptr] = vx; VECTOR(pos)[vx] = Pptr + 1; @@ -308,14 +357,14 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( igraph_adjlist_get(&fulladjlist, v) )); for (j = 0; j <= vdeg - 1; j++) { - igraph_integer_t vv = VECTOR(PX)[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); - igraph_integer_t k, fn = igraph_vector_int_size(fadj); + int k, fn = igraph_vector_int_size(fadj); igraph_vector_int_clear(radj); for (k = 0; k < fn; k++) { - igraph_integer_t nei = VECTOR(*fadj)[k]; - igraph_integer_t neipos = VECTOR(pos)[nei] - 1; + 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)); } @@ -350,7 +399,7 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( igraph_adjlist_destroy(&fulladjlist); igraph_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&rank); - igraph_vector_int_destroy(&order); + igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; @@ -360,7 +409,7 @@ igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( #undef RESNAME #undef SUFFIX #undef RECORD -#undef PREPARE +#undef FINALLY #undef CLEANUP #undef FOR_LOOP_OVER_VERTICES #undef FOR_LOOP_OVER_VERTICES_PREPARE diff --git a/src/vendor/cigraph/src/community/community_misc.c b/src/vendor/cigraph/src/community/community_misc.c index 77850b7fcb0..a7fb2e7ef4c 100644 --- a/src/vendor/cigraph/src/community/community_misc.c +++ b/src/vendor/cigraph/src/community/community_misc.c @@ -22,21 +22,39 @@ */ #include "igraph_community.h" +#include "igraph_constructors.h" #include "igraph_memory.h" -#include "igraph_sparsemat.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 Creates a membership vector from a community structure dendrogram. + * \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_connected_components() - * for an example of a membership vector. + * 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 @@ -56,7 +74,6 @@ * 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. @@ -76,28 +93,28 @@ * * Time complexity: O(|V|), the number of vertices in the graph. */ -igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, +int igraph_community_to_membership(const igraph_matrix_t *merges, igraph_integer_t nodes, igraph_integer_t steps, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize) { + igraph_vector_t *membership, + igraph_vector_t *csize) { - igraph_integer_t no_of_nodes = nodes; - igraph_integer_t components = no_of_nodes - steps; - igraph_integer_t i, found = 0; + 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_int_t own_membership; - igraph_bool_t using_own_membership = false; + igraph_vector_t own_membership; + igraph_bool_t using_own_membership = 0; - if (steps > igraph_matrix_int_nrow(merges)) { + if (steps > igraph_matrix_nrow(merges)) { IGRAPH_ERRORF("Number of steps is greater than number of rows in merges matrix: found %" - IGRAPH_PRId " steps, %" IGRAPH_PRId " rows.", IGRAPH_EINVAL, steps, igraph_matrix_int_nrow(merges)); + IGRAPH_PRId " steps, %ld rows.", IGRAPH_EINVAL, steps, igraph_matrix_nrow(merges)); } - if (igraph_matrix_int_ncol(merges) != 2) { - IGRAPH_ERRORF("The merges matrix should have two columns, but has %" IGRAPH_PRId ".", - IGRAPH_EINVAL, igraph_matrix_int_ncol(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); @@ -106,36 +123,36 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, 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_INT_INIT_FINALLY(&own_membership, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&own_membership, no_of_nodes); using_own_membership = 1; membership = &own_membership; } if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_null(membership); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_null(membership); } if (csize) { - IGRAPH_CHECK(igraph_vector_int_resize(csize, components)); - igraph_vector_int_null(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--) { - igraph_integer_t c1 = MATRIX(*merges, i, 0); - igraph_integer_t c2 = MATRIX(*merges, i, 1); + 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 %" IGRAPH_PRId ".", IGRAPH_EINVAL, c1); + 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 %" IGRAPH_PRId ".", IGRAPH_EINVAL, c2); + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %ld.", IGRAPH_EINVAL, c2); } /* new component? */ @@ -145,7 +162,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, } if (c1 < no_of_nodes) { - igraph_integer_t cid = VECTOR(tmp)[i] - 1; + long int cid = (long int) VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c1] = cid + 1; } @@ -157,7 +174,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, } if (c2 < no_of_nodes) { - igraph_integer_t cid = VECTOR(tmp)[i] - 1; + long int cid = (long int) VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c2] = cid + 1; } @@ -174,7 +191,7 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, /* it can never happen that csize != 0 and membership == 0; we have * handled that case above */ for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t tmp = VECTOR(*membership)[i]; + long int tmp = (long int) VECTOR(*membership)[i]; if (tmp != 0) { if (membership) { VECTOR(*membership)[i] = tmp - 1; @@ -196,16 +213,16 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, IGRAPH_FINALLY_CLEAN(2); if (using_own_membership) { - igraph_vector_int_destroy(&own_membership); + igraph_vector_destroy(&own_membership); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_reindex_membership - * \brief Makes the IDs in a membership vector contiguous. + * \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, @@ -217,21 +234,21 @@ igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, * 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 \c NULL, + * 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 \c NULL, this will be + * 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. */ -igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, - igraph_vector_int_t *new_to_old, +int igraph_reindex_membership(igraph_vector_t *membership, + igraph_vector_t *new_to_old, igraph_integer_t *nb_clusters) { - igraph_integer_t i, n = igraph_vector_int_size(membership); + long int i, n = igraph_vector_size(membership); igraph_vector_t new_cluster; igraph_integer_t i_nb_clusters; @@ -240,42 +257,37 @@ igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster); if (new_to_old) { - igraph_vector_int_clear(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++) { - igraph_integer_t c = VECTOR(*membership)[i]; - - if (c < 0) { - IGRAPH_ERRORF("Membership indices should non-negative. " - "Found member of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c); - } + long int c = (long int)VECTOR(*membership)[i]; if (c < 0) { IGRAPH_ERRORF("Membership indices should be non-negative. " - "Found member of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c); + "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 %" IGRAPH_PRId ", but only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL, c, n); + "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_int_push_back(new_to_old, c)); + IGRAPH_CHECK(igraph_vector_push_back(new_to_old, c)); } } } /* Assign new membership */ for (i = 0; i < n; i++) { - igraph_integer_t c = VECTOR(*membership)[i]; + long int c = (long int)VECTOR(*membership)[i]; VECTOR(*membership)[i] = VECTOR(new_cluster)[c] - 1; } if (nb_clusters) { @@ -290,14 +302,14 @@ igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, - const igraph_vector_int_t *v2, igraph_real_t* result); -static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, - const igraph_vector_int_t *v2, igraph_real_t* result); -static igraph_error_t igraph_i_compare_communities_rand(const igraph_vector_int_t *v1, - const igraph_vector_int_t *v2, igraph_real_t* result, igraph_bool_t adjust); -static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, - const igraph_vector_int_t *v2, igraph_integer_t* distance12, +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); /** @@ -423,24 +435,24 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 * * Time complexity: O(n log(n)). */ -igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, - const igraph_vector_int_t *comm2, igraph_real_t* result, +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_int_t c1, c2; + igraph_vector_t c1, c2; - if (igraph_vector_int_size(comm1) != igraph_vector_int_size(comm2)) { + 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_int_init_copy(&c1, comm1)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); + IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_destroy, &c1); - IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); + IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_destroy, &c2); - IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); - IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL)); switch (method) { case IGRAPH_COMMCMP_VI: @@ -469,17 +481,17 @@ igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, } /* Clean up everything */ - igraph_vector_int_destroy(&c1); - igraph_vector_int_destroy(&c2); + igraph_vector_destroy(&c1); + igraph_vector_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup communities * \function igraph_split_join_distance - * \brief Calculates the split-join distance of two community structures. + * \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 @@ -526,34 +538,34 @@ igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, * * Time complexity: O(n log(n)). */ -igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, - const igraph_vector_int_t *comm2, igraph_integer_t *distance12, +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_int_t c1, c2; + igraph_vector_t c1, c2; - if (igraph_vector_int_size(comm1) != igraph_vector_int_size(comm2)) { - IGRAPH_ERRORF("Community membership vectors have different lengths: %" IGRAPH_PRId " and %" IGRAPH_PRId ".", - IGRAPH_EINVAL, igraph_vector_int_size(comm1), igraph_vector_int_size(comm2)); + 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_int_init_copy(&c1, comm1)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); + IGRAPH_CHECK(igraph_vector_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_destroy, &c1); - IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); + IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_destroy, &c2); - IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); - IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, NULL)); + 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_int_destroy(&c1); - igraph_vector_int_destroy(&c2); + igraph_vector_destroy(&c1); + igraph_vector_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -561,25 +573,24 @@ igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, * membership vectors v1 and v2. This is needed by both Meila's and Danon's * community comparison measure. */ -static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vector_int_t* v1, - const igraph_vector_int_t* v2, double* h1, double* h2, double* mut_inf) { - igraph_integer_t i, n; - igraph_integer_t k1; - igraph_integer_t k2; +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_sparsemat_t m; - igraph_sparsemat_t mu; /* uncompressed */ - igraph_sparsemat_iterator_t mit; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; - n = igraph_vector_int_size(v1); + n = igraph_vector_size(v1); if (n == 0) { *h1 = 0; *h2 = 0; *mut_inf = 0; return IGRAPH_SUCCESS; } - k1 = igraph_vector_int_max(v1) + 1; - k2 = igraph_vector_int_max(v2) + 1; + 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("Insufficient memory for computing community entropy.", IGRAPH_ENOMEM); @@ -594,7 +605,7 @@ static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vecto /* Calculate the entropy of v1 */ *h1 = 0.0; for (i = 0; i < n; i++) { - p1[VECTOR(*v1)[i]]++; + p1[(long int)VECTOR(*v1)[i]]++; } for (i = 0; i < k1; i++) { p1[i] /= n; @@ -604,7 +615,7 @@ static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vecto /* Calculate the entropy of v2 */ *h2 = 0.0; for (i = 0; i < n; i++) { - p2[VECTOR(*v2)[i]]++; + p2[(long int)VECTOR(*v2)[i]]++; } for (i = 0; i < k2; i++) { p2[i] /= n; @@ -621,32 +632,27 @@ static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vecto /* Calculate the mutual information of v1 and v2 */ *mut_inf = 0.0; - IGRAPH_CHECK(igraph_sparsemat_init(&mu, k1, k2, n)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + IGRAPH_CHECK(igraph_spmatrix_init(&m, k1, k2)); + IGRAPH_FINALLY(igraph_spmatrix_destroy, &m); for (i = 0; i < n; i++) { - IGRAPH_CHECK(igraph_sparsemat_entry( - &mu, VECTOR(*v1)[i], - VECTOR(*v2)[i], 1 - )); + IGRAPH_CHECK(igraph_spmatrix_add_e(&m, + (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); } - - IGRAPH_CHECK(igraph_sparsemat_compress(&mu, &m)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); - IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); - - IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); - while (!igraph_sparsemat_iterator_end(&mit)) { - double p = igraph_sparsemat_iterator_get(&mit)/ n; - *mut_inf += p * (log(p) - p1[igraph_sparsemat_iterator_row(&mit)] - p2[igraph_sparsemat_iterator_col(&mit)]); - igraph_sparsemat_iterator_next(&mit); + 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_sparsemat_destroy(&m); - igraph_sparsemat_destroy(&mu); + + igraph_spmatrix_iter_destroy(&mit); + igraph_spmatrix_destroy(&m); IGRAPH_FREE(p1); IGRAPH_FREE(p2); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /** @@ -661,7 +667,7 @@ static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vecto * * Time complexity: O(n log(n)) */ -static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, +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; @@ -691,7 +697,7 @@ static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t * * Time complexity: O(n log(n)) */ -static igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, +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; @@ -712,23 +718,23 @@ static igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t * 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 igraph_error_t igraph_i_confusion_matrix(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, - igraph_sparsemat_t *m) { - igraph_integer_t k1, k2, i, n; - - n = igraph_vector_int_size(v1); - if (n == 0) { - IGRAPH_CHECK(igraph_sparsemat_resize(m, 0, 0, 0)); +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 = igraph_vector_int_max(v1) + 1; - k2 = igraph_vector_int_max(v2) + 1; - IGRAPH_CHECK(igraph_sparsemat_resize(m, k1, k2, n)); + 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_sparsemat_entry( - m, VECTOR(*v1)[i], VECTOR(*v2)[i], 1 - )); + IGRAPH_CHECK(igraph_spmatrix_add_e(m, + (int)VECTOR(*v1)[i], (int)VECTOR(*v2)[i], 1)); } return IGRAPH_SUCCESS; @@ -750,13 +756,12 @@ static igraph_error_t igraph_i_confusion_matrix(const igraph_vector_int_t *v1, c * 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 igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, +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) { - igraph_integer_t n = igraph_vector_int_size(v1); + long int n = igraph_vector_size(v1); igraph_vector_t rowmax, colmax; - igraph_sparsemat_t m; - igraph_sparsemat_t mu; /* uncompressed */ - igraph_sparsemat_iterator_t mit; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; if (n == 0) { *distance12 = 0; @@ -764,31 +769,28 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 return IGRAPH_SUCCESS; } /* Calculate the confusion matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); - IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); + 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_sparsemat_nrow(&mu)); - IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_sparsemat_ncol(&mu)); + IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_spmatrix_nrow(&m)); + IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_spmatrix_ncol(&m)); /* Find the row/columnwise maxima */ - igraph_sparsemat_compress(&mu, &m); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); - IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); - IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); - while (!igraph_sparsemat_iterator_end(&mit)) { - igraph_real_t value = igraph_sparsemat_iterator_get(&mit); - igraph_integer_t row = igraph_sparsemat_iterator_row(&mit); - igraph_integer_t col = igraph_sparsemat_iterator_col(&mit); - if (value > VECTOR(rowmax)[row]) { - VECTOR(rowmax)[row] = value; + 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 (value > VECTOR(colmax)[col]) { - VECTOR(colmax)[col] = value; + if (mit.value > VECTOR(colmax)[mit.ci]) { + VECTOR(colmax)[mit.ci] = mit.value; } - igraph_sparsemat_iterator_next(&mit); + 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)); @@ -796,9 +798,8 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 igraph_vector_destroy(&rowmax); igraph_vector_destroy(&colmax); - igraph_sparsemat_destroy(&m); - igraph_sparsemat_destroy(&mu); - IGRAPH_FINALLY_CLEAN(4); + igraph_spmatrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } @@ -825,26 +826,25 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 * 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 igraph_error_t igraph_i_compare_communities_rand( - const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, +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_sparsemat_t m; - igraph_sparsemat_t mu; /* uncompressed */ - igraph_sparsemat_iterator_t mit; + igraph_spmatrix_t m; + igraph_spmatrix_iter_t mit; igraph_vector_t rowsums, colsums; - igraph_integer_t i, nrow, ncol; + long int i, nrow, ncol; double rand, n; double frac_pairs_in_1, frac_pairs_in_2; - if (igraph_vector_int_size(v1) <= 1) { + if (igraph_vector_size(v1) <= 1) { IGRAPH_ERRORF("Rand indices not defined for only zero or one vertices. " - "Found membership vector of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, igraph_vector_int_size(v1)); + "Found membership vector of size %ld", IGRAPH_EINVAL, igraph_vector_size(v1)); } /* Calculate the confusion matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); - IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); + 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: * @@ -876,26 +876,24 @@ static igraph_error_t igraph_i_compare_communities_rand( */ /* Calculate row and column sums */ - nrow = igraph_sparsemat_nrow(&mu); - ncol = igraph_sparsemat_ncol(&mu); - n = igraph_vector_int_size(v1) + 0.0; + 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_sparsemat_rowsums(&mu, &rowsums)); - IGRAPH_CHECK(igraph_sparsemat_colsums(&mu, &colsums)); + IGRAPH_CHECK(igraph_spmatrix_rowsums(&m, &rowsums)); + IGRAPH_CHECK(igraph_spmatrix_colsums(&m, &colsums)); /* Start calculating the unadjusted Rand index */ rand = 0.0; - igraph_sparsemat_compress(&mu, &m); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); - IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); - - IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); - while (!igraph_sparsemat_iterator_end(&mit)) { - igraph_real_t value = igraph_sparsemat_iterator_get(&mit); - rand += (value / n) * (value - 1) / (n - 1); - igraph_sparsemat_iterator_next(&mit); + 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++) { @@ -915,9 +913,8 @@ static igraph_error_t igraph_i_compare_communities_rand( igraph_vector_destroy(&rowsums); igraph_vector_destroy(&colsums); - igraph_sparsemat_destroy(&m); - igraph_sparsemat_destroy(&mu); - IGRAPH_FINALLY_CLEAN(4); + igraph_spmatrix_destroy(&m); + IGRAPH_FINALLY_CLEAN(3); *result = rand; diff --git a/src/vendor/cigraph/src/community/edge_betweenness.c b/src/vendor/cigraph/src/community/edge_betweenness.c index a69bd9e77da..750c183975a 100644 --- a/src/vendor/cigraph/src/community/edge_betweenness.c +++ b/src/vendor/cigraph/src/community/edge_betweenness.c @@ -28,7 +28,6 @@ #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_nongraph.h" #include "igraph_progress.h" #include "igraph_stack.h" @@ -37,15 +36,16 @@ #include -static igraph_error_t igraph_i_rewrite_membership_vector(igraph_vector_int_t *membership) { - const igraph_integer_t no = igraph_vector_int_max(membership) + 1; +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; - igraph_integer_t realno = 0; - const igraph_integer_t len = igraph_vector_int_size(membership); + long int realno = 0; + long int i; + long int len = igraph_vector_size(membership); IGRAPH_VECTOR_INIT_FINALLY(&idx, no); - for (igraph_integer_t i = 0; i < len; i++) { - const igraph_integer_t t = VECTOR(*membership)[i]; + 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 { @@ -56,51 +56,55 @@ static igraph_error_t igraph_i_rewrite_membership_vector(igraph_vector_int_t *me igraph_vector_destroy(&idx); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, +static int igraph_i_community_eb_get_merges2(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_int_t *edges, + const igraph_vector_t *edges, const igraph_vector_t *weights, - igraph_matrix_int_t *res, - igraph_vector_int_t *bridges, + igraph_matrix_t *res, + igraph_vector_t *bridges, igraph_vector_t *modularity, - igraph_vector_int_t *membership) { + igraph_vector_t *membership) { - igraph_vector_int_t mymembership; - const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t mymembership; + long int no_of_nodes = igraph_vcount(graph); + long int i; igraph_real_t maxmod = -1; - igraph_integer_t midx = 0; + long int midx = 0; igraph_integer_t no_comps; - const igraph_bool_t use_directed = directed && igraph_is_directed(graph); - igraph_integer_t max_merges; + igraph_bool_t use_directed = directed && igraph_is_directed(graph); + + IGRAPH_VECTOR_INIT_FINALLY(&mymembership, no_of_nodes); if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); } + if (modularity || res || bridges) { - IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); - max_merges = no_of_nodes - no_comps; + IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, + IGRAPH_WEAK)); if (modularity) { IGRAPH_CHECK(igraph_vector_resize(modularity, - max_merges + 1)); + no_of_nodes - no_comps + 1)); } if (res) { - IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, 2)); } if (bridges) { - IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); + IGRAPH_CHECK(igraph_vector_resize(bridges, + no_of_nodes - no_comps)); } } - IGRAPH_CHECK(igraph_vector_int_init_range(&mymembership, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &mymembership); - + for (i = 0; i < no_of_nodes; i++) { + VECTOR(mymembership)[i] = i; + } if (membership) { - IGRAPH_CHECK(igraph_vector_int_update(membership, &mymembership)); + igraph_vector_update(membership, &mymembership); } IGRAPH_CHECK(igraph_modularity(graph, &mymembership, weights, @@ -110,25 +114,25 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, VECTOR(*modularity)[0] = maxmod; } - for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { - igraph_integer_t edge = VECTOR(*edges)[i]; - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); - igraph_integer_t c1 = VECTOR(mymembership)[from]; - igraph_integer_t c2 = VECTOR(mymembership)[to]; + 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; + VECTOR(*bridges)[midx] = i + 1; } /* The new cluster has id no_of_nodes+midx+1 */ - for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + for (j = 0; j < no_of_nodes; j++) { if (VECTOR(mymembership)[j] == c1 || VECTOR(mymembership)[j] == c2) { VECTOR(mymembership)[j] = no_of_nodes + midx; @@ -143,7 +147,7 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, if (actmod > maxmod) { maxmod = actmod; if (membership) { - IGRAPH_CHECK(igraph_vector_int_update(membership, &mymembership)); + igraph_vector_update(membership, &mymembership); } } } @@ -156,10 +160,10 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership)); } - igraph_vector_int_destroy(&mymembership); + igraph_vector_destroy(&mymembership); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } @@ -167,15 +171,14 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, * \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. Merges happen when the - * edge removal process is run backwards and two components become - * connected. + * dendrogram, via its \p merges argument. * * \param graph The input graph. * \param edges Vector containing the edges to be removed from the @@ -188,21 +191,20 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, * 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 + * 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 n+1, etc. The matrix will be resized as needed. - * \param bridges Pointer to an initialized vector of \c NULL. If not - * \c NULL then the indices into \p edges of all edges which caused - * one of the merges will be put here. This is equal to all edge removals - * which separated the network into more components, in reverse order. + * 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. @@ -216,46 +218,34 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, * Time complexity: O(|E|+|V|log|V|), |V| is the number of vertices, * |E| is the number of edges. */ -igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, +int igraph_community_eb_get_merges(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_int_t *edges, + const igraph_vector_t *edges, const igraph_vector_t *weights, - igraph_matrix_int_t *res, - igraph_vector_int_t *bridges, + igraph_matrix_t *res, + igraph_vector_t *bridges, igraph_vector_t *modularity, - igraph_vector_int_t *membership) { + igraph_vector_t *membership) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t ptr; - igraph_integer_t midx = 0; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t ptr; + long int i, midx = 0; igraph_integer_t no_comps; - const igraph_integer_t no_removed_edges = igraph_vector_int_size(edges); - igraph_integer_t max_merges; - - if (! igraph_vector_int_isininterval(edges, 0, no_of_edges-1)) { - IGRAPH_ERROR("Invalid edge ID.", IGRAPH_EINVAL); - } - if (no_removed_edges < no_of_edges) { - IGRAPH_ERRORF("Number of removed edges (%" IGRAPH_PRId ") should be equal to " - "number of edges in graph (%" IGRAPH_PRId ").", IGRAPH_EINVAL, - no_removed_edges, no_of_edges); - } /* catch null graph early */ if (no_of_nodes == 0) { if (res) { - IGRAPH_CHECK(igraph_matrix_int_resize(res, 0, 2)); + igraph_matrix_resize(res, 0, 2); } if (bridges) { - igraph_vector_int_clear(bridges); + igraph_vector_clear(bridges); } if (modularity) { - IGRAPH_CHECK(igraph_vector_resize(modularity, 1)); + igraph_vector_resize(modularity, 1); VECTOR(*modularity)[0] = IGRAPH_NAN; } if (membership) { - igraph_vector_int_clear(membership); + igraph_vector_clear(membership); } return IGRAPH_SUCCESS; } @@ -267,29 +257,28 @@ igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, res, bridges, modularity, membership); } - IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, IGRAPH_WEAK)); - max_merges = no_of_nodes - no_comps; - IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); + IGRAPH_VECTOR_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); if (res) { - IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, 2)); + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, 2)); } if (bridges) { - IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); + IGRAPH_CHECK(igraph_vector_resize(bridges, no_of_nodes - no_comps)); } - for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { - igraph_integer_t edge = VECTOR(*edges)[i]; + 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_CHECK(igraph_edge(graph, edge, &from, &to)); + igraph_edge(graph, edge, &from, &to); idx = from + 1; while (VECTOR(ptr)[idx - 1] != 0) { - idx = VECTOR(ptr)[idx - 1]; + idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; } c1 = idx - 1; idx = to + 1; while (VECTOR(ptr)[idx - 1] != 0) { - idx = VECTOR(ptr)[idx - 1]; + idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; } c2 = idx - 1; if (c1 != c2) { /* this is a merge */ @@ -298,7 +287,7 @@ igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, MATRIX(*res, midx, 1) = c2; } if (bridges) { - VECTOR(*bridges)[midx] = i; + VECTOR(*bridges)[midx] = i + 1; } VECTOR(ptr)[c1] = no_of_nodes + midx + 1; @@ -310,16 +299,16 @@ igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, } } - igraph_vector_int_destroy(&ptr); + igraph_vector_destroy(&ptr); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /* Find the smallest active element in the vector */ -static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t *v, - const bool *passive) { - igraph_integer_t which, i = 0, size = igraph_vector_size(v); +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++; @@ -365,7 +354,7 @@ static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t * * \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 + * 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 @@ -375,12 +364,12 @@ static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t * 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. + * 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 - * \c NULL then the indices into \p result of all edges which caused - * one of the \p merges will be put here. This is equivalent to all edge removals - * which separated the network into more components, in reverse order. + * \c 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 @@ -405,47 +394,49 @@ static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t * * \example examples/simple/igraph_community_edge_betweenness.c */ -igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, - igraph_vector_int_t *result, +int igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_t *result, igraph_vector_t *edge_betweenness, - igraph_matrix_int_t *merges, - igraph_vector_int_t *bridges, + igraph_matrix_t *merges, + igraph_vector_t *bridges, igraph_vector_t *modularity, - igraph_vector_int_t *membership, + igraph_vector_t *membership, igraph_bool_t directed, const igraph_vector_t *weights) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); + 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, parents; + igraph_inclist_t elist_out, elist_in, fathers; igraph_inclist_t *elist_out_p, *elist_in_p; igraph_vector_int_t *neip; - igraph_integer_t neino; + long int neino; igraph_vector_t eb; - igraph_integer_t maxedge, pos; + long int maxedge, pos; igraph_integer_t from, to; - igraph_bool_t result_owned = false; - igraph_stack_int_t stack; + igraph_bool_t result_owned = 0; + igraph_stack_t stack = IGRAPH_STACK_NULL; igraph_real_t steps, steps_done; - bool *passive; + char *passive; /* Needed only for the unweighted case */ - igraph_dqueue_int_t q; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; /* Needed only for the weighted case */ igraph_2wheap_t heap; - if (result == NULL) { - result = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(result, "Insufficient memory for edge betweenness-based community detection."); + 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_INT_INIT_FINALLY(result, 0); - result_owned = true; + IGRAPH_VECTOR_INIT_FINALLY(result, 0); + result_owned = 1; } directed = directed && igraph_is_directed(graph); @@ -463,19 +454,23 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, } distance = IGRAPH_CALLOC(no_of_nodes, double); - IGRAPH_CHECK_OOM(distance, "Insufficient memory for edge betweenness-based community detection."); + if (distance == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, distance); - nrgeo = IGRAPH_CALLOC(no_of_nodes, double); - IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness-based community detection."); + if (nrgeo == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, nrgeo); - tmpscore = IGRAPH_CALLOC(no_of_nodes, double); - IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for edge betweenness-based community detection."); + if (tmpscore == 0) { + IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, tmpscore); - if (weights == NULL) { - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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); @@ -488,17 +483,17 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_ERROR("Weights must be strictly positive.", IGRAPH_EINVAL); } - if (isnan(minweight)) { + if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } - if (membership != NULL) { + if (membership != 0) { IGRAPH_WARNING("Membership vector will be selected based on the highest " "modularity score."); } - if (modularity != NULL || membership != NULL) { + 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 " @@ -507,13 +502,15 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_CHECK(igraph_2wheap_init(&heap, no_of_nodes)); IGRAPH_FINALLY(igraph_2wheap_destroy, &heap); - IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); - IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + IGRAPH_CHECK(igraph_inclist_init_empty(&fathers, + (igraph_integer_t) no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &fathers); } - IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); + IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_destroy, &stack); - IGRAPH_CHECK(igraph_vector_int_resize(result, no_of_edges)); + 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) { @@ -523,8 +520,10 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_VECTOR_INIT_FINALLY(&eb, no_of_edges); - passive = IGRAPH_CALLOC(no_of_edges, bool); - IGRAPH_CHECK_OOM(passive, "Insufficient memory for edge betweenness-based community detection."); + 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. @@ -536,39 +535,39 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, steps = no_of_edges / 2.0 * (no_of_edges + 1); steps_done = 0; - for (igraph_integer_t e = 0; e < no_of_edges; steps_done += no_of_edges - e, e++) { + 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 == NULL) { + if (weights == 0) { /* Unweighted variant follows */ /* The following for loop is copied almost intact from * igraph_edge_betweenness_cutoff */ - for (igraph_integer_t source = 0; source < no_of_nodes; source++) { + 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_int_clear(&stack); /* it should be empty anyway... */ + igraph_stack_clear(&stack); /* it should be empty anyway... */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_push(&q, source)); nrgeo[source] = 1; distance[source] = 0; - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + 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 (igraph_integer_t i = 0; i < neino; i++) { - igraph_integer_t edge = VECTOR(*neip)[i]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); + 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) { @@ -578,17 +577,17 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, /* we haven't seen this node yet */ nrgeo[neighbor] += nrgeo[actnode]; distance[neighbor] = distance[actnode] + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_stack_push(&stack, neighbor)); } } - } /* while !igraph_dqueue_int_empty */ + } /* 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_int_empty(&stack)) { - igraph_integer_t actnode = igraph_stack_int_pop(&stack); + while (!igraph_stack_empty(&stack)) { + long int actnode = (long int) igraph_stack_pop(&stack); if (distance[actnode] < 1) { continue; /* skip source node */ } @@ -596,9 +595,9 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, /* set the temporary score of the friends */ neip = igraph_inclist_get(elist_in_p, actnode); neino = igraph_vector_int_size(neip); - for (igraph_integer_t i = 0; i < neino; i++) { - igraph_integer_t edge = VECTOR(*neip)[i]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); + 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] += @@ -613,12 +612,9 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, } else { /* Weighted variant follows */ - const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; - int cmp_result; - /* The following for loop is copied almost intact from * igraph_i_edge_betweenness_cutoff_weighted */ - for (igraph_integer_t source = 0; source < no_of_nodes; source++) { + for (source = 0; source < no_of_nodes; source++) { /* This will contain the edge betweenness in the current step */ IGRAPH_ALLOW_INTERRUPTION(); @@ -626,64 +622,59 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, memset(nrgeo, 0, (size_t) no_of_nodes * sizeof(double)); memset(tmpscore, 0, (size_t) no_of_nodes * sizeof(double)); - IGRAPH_CHECK(igraph_2wheap_push_with_index(&heap, source, 0)); + igraph_2wheap_push_with_index(&heap, source, 0); distance[source] = 1.0; nrgeo[source] = 1; while (!igraph_2wheap_empty(&heap)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&heap); + long int minnei = igraph_2wheap_max_index(&heap); igraph_real_t mindist = -igraph_2wheap_delete_max(&heap); - IGRAPH_CHECK(igraph_stack_int_push(&stack, minnei)); + igraph_stack_push(&stack, minnei); neip = igraph_inclist_get(elist_out_p, minnei); neino = igraph_vector_int_size(neip); - for (igraph_integer_t i = 0; i < neino; i++) { - igraph_integer_t edge = VECTOR(*neip)[i]; - igraph_integer_t to = IGRAPH_OTHER(graph, edge, minnei); + 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; - /* Note: curdist == 0 means infinity, and for this case - * cmp_result should be -1. However, this case is handled - * specially below, without referring to cmp_result. */ - cmp_result = igraph_cmp_epsilon(altdist, curdist - 1, eps); - if (curdist == 0) { /* This is the first finite distance to 'to' */ - v = igraph_inclist_get(&parents, 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 (cmp_result < 0) { + } else if (altdist < curdist - 1) { /* This is a shorter path */ - v = igraph_inclist_get(&parents, 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_2wheap_modify(&heap, to, -altdist); - } else if (cmp_result == 0) { + IGRAPH_CHECK(igraph_2wheap_modify(&heap, to, -altdist)); + } else if (altdist == curdist - 1) { /* Another path with the same length */ - v = igraph_inclist_get(&parents, to); - IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + v = igraph_inclist_get(&fathers, to); + igraph_vector_int_push_back(v, edge); nrgeo[to] += nrgeo[minnei]; } } } /* igraph_2wheap_empty(&Q) */ - while (!igraph_stack_int_empty(&stack)) { - igraph_integer_t w = igraph_stack_int_pop(&stack); - igraph_vector_int_t *parv = igraph_inclist_get(&parents, w); - igraph_integer_t parv_len = igraph_vector_int_size(parv); + 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 (igraph_integer_t i = 0; i < parv_len; i++) { - igraph_integer_t fedge = VECTOR(*parv)[i]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, fedge, w); + 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]; } @@ -691,7 +682,7 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, tmpscore[w] = 0; distance[w] = 0; nrgeo[w] = 0; - igraph_vector_int_clear(parv); + igraph_vector_int_clear(fatv); } } /* source < no_of_nodes */ } @@ -706,8 +697,8 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, VECTOR(*edge_betweenness)[e] /= 2.0; } } - passive[maxedge] = true; - IGRAPH_CHECK(igraph_edge(graph, maxedge, &from, &to)); + 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); @@ -724,17 +715,17 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_PROGRESS("Edge betweenness community detection: ", 100.0, NULL); - IGRAPH_FREE(passive); + igraph_free(passive); igraph_vector_destroy(&eb); - igraph_stack_int_destroy(&stack); + igraph_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(3); - if (weights == NULL) { - igraph_dqueue_int_destroy(&q); + if (weights == 0) { + igraph_dqueue_destroy(&q); IGRAPH_FINALLY_CLEAN(1); } else { igraph_2wheap_destroy(&heap); - igraph_inclist_destroy(&parents); + igraph_inclist_destroy(&fathers); IGRAPH_FINALLY_CLEAN(2); } igraph_free(tmpscore); @@ -758,10 +749,10 @@ igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, } if (result_owned) { - igraph_vector_int_destroy(result); + igraph_vector_destroy(result); IGRAPH_FREE(result); IGRAPH_FINALLY_CLEAN(2); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/community/fast_modularity.c b/src/vendor/cigraph/src/community/fast_modularity.c index d3632d05fa6..ff790ccac03 100644 --- a/src/vendor/cigraph/src/community/fast_modularity.c +++ b/src/vendor/cigraph/src/community/fast_modularity.c @@ -37,7 +37,7 @@ #ifdef _MSC_VER /* MSVC does not support variadic macros */ #include -void debug(const char *fmt, ...) { +void debug(const char* fmt, ...) { va_list args; va_start(args, fmt); #ifdef IGRAPH_FASTCOMM_DEBUG @@ -84,8 +84,8 @@ void debug(const char *fmt, ...) { /* Structure storing a pair of communities along with their dQ values */ typedef struct s_igraph_i_fastgreedy_commpair { - igraph_integer_t first; /* first member of the community pair */ - igraph_integer_t second; /* second member of the community pair */ + 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; @@ -96,29 +96,30 @@ 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_commpair* maxdq; /* community pair with maximal dq */ } igraph_i_fastgreedy_community; /* Global community list structure */ typedef struct { - igraph_integer_t 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 */ + 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 true if the maximum is different from the previous one, - * false otherwise. */ -static igraph_bool_t igraph_i_fastgreedy_community_rescan_max(igraph_i_fastgreedy_community *comm) { - igraph_integer_t i, n; + * 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 = NULL; - return true; + comm->maxdq = 0; + return 1; } best = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[0]; @@ -134,36 +135,36 @@ static igraph_bool_t igraph_i_fastgreedy_community_rescan_max(igraph_i_fastgreed if (best != comm->maxdq) { comm->maxdq = best; - return true; + return 1; } else { - return false; + return 0; } } /* Destroys the global community list object */ static void igraph_i_fastgreedy_community_list_destroy( - igraph_i_fastgreedy_community_list *list) { - igraph_integer_t i; + 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 != NULL) { + if (list->heapindex != 0) { IGRAPH_FREE(list->heapindex); } - if (list->heap != NULL) { + 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, igraph_integer_t idx) { - igraph_integer_t root, child, c1, c2; - igraph_i_fastgreedy_community *dummy; + 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; + igraph_integer_t* heapindex = list->heapindex; root = idx; while (root * 2 + 1 < list->no_of_communities) { @@ -193,12 +194,12 @@ static void igraph_i_fastgreedy_community_list_sift_down( /* Community list heap maintenance: sift up */ static void igraph_i_fastgreedy_community_list_sift_up( - igraph_i_fastgreedy_community_list *list, igraph_integer_t idx) { - igraph_integer_t root, parent, c1, c2; - igraph_i_fastgreedy_community *dummy; + 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; + igraph_integer_t* heapindex = list->heapindex; root = idx; while (root > 0) { @@ -224,8 +225,8 @@ static void igraph_i_fastgreedy_community_list_sift_up( /* Builds the community heap for the first time */ static void igraph_i_fastgreedy_community_list_build_heap( - igraph_i_fastgreedy_community_list *list) { - igraph_integer_t i; + 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); } @@ -238,21 +239,21 @@ static void igraph_i_fastgreedy_community_list_build_heap( /* Dumps the heap - for debugging purposes */ /* static void igraph_i_fastgreedy_community_list_dump_heap( - igraph_i_fastgreedy_community_list *list) { - igraph_integer_t i; + 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(" (%" IGRAPH_PRId ", %" IGRAPH_PRId ", %.7f)", list->heap[i]->maxdq->first, + 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("%" IGRAPH_PRId " ", list->heapindex[i]); + debug("%ld ", (long)list->heapindex[i]); } debug("\nEND\n"); } @@ -262,13 +263,13 @@ static void igraph_i_fastgreedy_community_list_dump_heap( * Only useful for debugging. */ /* static void igraph_i_fastgreedy_community_list_check_heap( - igraph_i_fastgreedy_community_list *list) { - igraph_integer_t i; + 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: %" IGRAPH_PRId ", %" IGRAPH_PRId " and %" IGRAPH_PRId "\n", i, 2 * i + 1, 2 * i + 2); + debug("Position: %ld, %ld and %ld\n", i, 2 * i + 1, 2 * i + 2); igraph_i_fastgreedy_community_list_dump_heap(list); } } @@ -277,13 +278,13 @@ static void igraph_i_fastgreedy_community_list_check_heap( /* Removes a given element from the heap */ static void igraph_i_fastgreedy_community_list_remove( - igraph_i_fastgreedy_community_list *list, igraph_integer_t idx) { + igraph_i_fastgreedy_community_list* list, long int idx) { igraph_real_t old; - igraph_integer_t commidx; + long int commidx; /* First adjust the index */ commidx = list->heap[list->no_of_communities - 1]->maxdq->first; - list->heapindex[commidx] = idx; + list->heapindex[commidx] = (igraph_integer_t) idx; commidx = list->heap[idx]->maxdq->first; list->heapindex[commidx] = -1; @@ -303,8 +304,8 @@ static void igraph_i_fastgreedy_community_list_remove( /* 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, igraph_integer_t idx, igraph_integer_t comm) { - igraph_integer_t i; + 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, @@ -316,7 +317,7 @@ static void igraph_i_fastgreedy_community_list_remove2( /* First adjust the index */ i = list->heap[list->no_of_communities - 1]->maxdq->first; - list->heapindex[i] = idx; + list->heapindex[i] = (igraph_integer_t) idx; list->heapindex[comm] = -1; /* Now remove the element */ @@ -332,9 +333,9 @@ static void igraph_i_fastgreedy_community_list_remove2( /* 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, igraph_integer_t c, igraph_integer_t k) { - igraph_integer_t i, n; - igraph_bool_t rescan = false; + 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; @@ -346,7 +347,7 @@ static void igraph_i_fastgreedy_community_remove_nei( if (p->second == k) { /* Check current maxdq */ if (comm->maxdq == p) { - rescan = true; + rescan = 1; } break; } @@ -366,7 +367,7 @@ static void igraph_i_fastgreedy_community_remove_nei( } 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): %" IGRAPH_PRId "\n", i); + debug("REMOVING (NO MORE NEIS): %ld\n", i); igraph_i_fastgreedy_community_list_remove2(list, i, c); } } @@ -375,28 +376,26 @@ static void igraph_i_fastgreedy_community_remove_nei( /* 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) { +static int igraph_i_fastgreedy_commpair_cmp(const void* p1, const void* p2) { igraph_i_fastgreedy_commpair *cp1, *cp2; - igraph_integer_t diff; cp1 = *(igraph_i_fastgreedy_commpair**)p1; cp2 = *(igraph_i_fastgreedy_commpair**)p2; - diff = cp1->second - cp2->second; - return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; + 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, igraph_integer_t index, - igraph_i_fastgreedy_commpair *changed_pair) { - igraph_vector_ptr_t *vec; - igraph_integer_t i, n; - igraph_bool_t can_skip_sort = false; + 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 != NULL) { + if (changed_pair != 0) { /* Optimized sorting */ /* First we look for changed_pair in vec */ @@ -408,40 +407,45 @@ static void igraph_i_fastgreedy_community_sort_neighbors_of( } /* Did we find it? We should have -- otherwise it's a bug */ - IGRAPH_ASSERT(i < n); - - /* 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; + 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; + + /* 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; + VECTOR(*vec)[i] = changed_pair; - /* Mark that we don't need a full sort */ - can_skip_sort = true; + /* Mark that we don't need a full sort */ + can_skip_sort = 1; + } } if (!can_skip_sort) { @@ -454,11 +458,10 @@ static void igraph_i_fastgreedy_community_sort_neighbors_of( * 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 igraph_bool_t igraph_i_fastgreedy_community_update_dq( - igraph_i_fastgreedy_community_list *list, - igraph_i_fastgreedy_commpair *p, igraph_real_t newdq) { - - igraph_integer_t i, j, to, from; +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; @@ -489,7 +492,7 @@ static igraph_bool_t igraph_i_fastgreedy_community_update_dq( j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } - return true; + 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 @@ -512,7 +515,7 @@ static igraph_bool_t igraph_i_fastgreedy_community_update_dq( igraph_i_fastgreedy_community_list_sift_up(list, j); } } - return false; + return 0; } else { /* We got here in two cases: (1) the pair we are modifying right now is the maximum in the given @@ -564,7 +567,7 @@ static igraph_bool_t igraph_i_fastgreedy_community_update_dq( } } } - return true; + return 1; } /** @@ -590,8 +593,8 @@ static igraph_bool_t igraph_i_fastgreedy_community_update_dq( * 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 + * 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. @@ -617,24 +620,24 @@ static igraph_bool_t igraph_i_fastgreedy_community_update_dq( * * \example examples/simple/igraph_community_fastgreedy.c */ -igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, +int igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_int_t *merges, + igraph_matrix_t *merges, igraph_vector_t *modularity, - igraph_vector_int_t *membership) { - igraph_integer_t no_of_edges, no_of_nodes, no_of_joins, total_joins; - igraph_integer_t i, j, k, n, m, from, to, dummy, best_no_of_joins; + 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_vector_int_t degrees; igraph_real_t q, *dq, bestq, weight_sum, loop_weight_sum; igraph_bool_t has_multiple; - igraph_matrix_int_t merges_local; + igraph_matrix_t merges_local; - /*igraph_integer_t 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 };*/ - /*igraph_integer_t 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 };*/ + /*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); @@ -645,7 +648,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, total_joins = no_of_nodes > 0 ? no_of_nodes - 1 : 0; - if (weights) { + 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); } @@ -654,7 +657,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); } - if (isnan(minweight)) { + if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -668,20 +671,20 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, IGRAPH_ERROR("Fast greedy community detection works only on graphs without multi-edges.", IGRAPH_EINVAL); } - if (membership != NULL && merges == NULL) { + 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_int_init(&merges_local, total_joins, 2)); - IGRAPH_FINALLY(igraph_matrix_int_destroy, &merges_local); + IGRAPH_CHECK(igraph_matrix_init(&merges_local, total_joins, 2)); + IGRAPH_FINALLY(igraph_matrix_destroy, &merges_local); merges = &merges_local; } - if (merges != NULL) { - IGRAPH_CHECK(igraph_matrix_int_resize(merges, total_joins, 2)); - igraph_matrix_int_null(merges); + if (merges != 0) { + IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2)); + igraph_matrix_null(merges); } - if (modularity != NULL) { + if (modularity != 0) { IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins + 1)); } @@ -690,63 +693,62 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, if (weights) { debug("Calculating weighted degrees\n"); for (i = 0; i < no_of_edges; i++) { - VECTOR(a)[IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; - VECTOR(a)[IGRAPH_TO(graph, i)] += VECTOR(*weights)[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_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, 1)); - for (i = 0; i < no_of_nodes; i++) { - VECTOR(a)[i] = VECTOR(degrees)[i]; - } - igraph_vector_int_destroy(°rees); - IGRAPH_FINALLY_CLEAN(1); + 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_CALLOC(no_of_nodes, igraph_i_fastgreedy_community); - IGRAPH_CHECK_OOM(communities.e, "Insufficient memory for fast greedy community detection."); + 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_CALLOC(no_of_nodes, igraph_i_fastgreedy_community*); - IGRAPH_CHECK_OOM(communities.heap, "Insufficient memory for fast greedy community detection."); + 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_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(communities.heapindex, "Insufficient memory for fast greedy community detection."); - + 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_CHECK(igraph_vector_ptr_init(&communities.e[i].neis, 0)); - communities.e[i].id = 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_CALLOC(no_of_edges, igraph_real_t); - IGRAPH_CHECK_OOM(dq, "Insufficient memory for fast greedy community detection."); + 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(IGRAPH_EDGEORDER_ID), &edgeit)); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); - pairs = IGRAPH_CALLOC(2 * no_of_edges, igraph_i_fastgreedy_commpair); - IGRAPH_CHECK_OOM(pairs, "Insufficient memory for fast greedy community detection."); + 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)) { - igraph_integer_t eidx = IGRAPH_EIT_GET(edgeit); + long int eidx = IGRAPH_EIT_GET(edgeit); + igraph_edge(graph, (igraph_integer_t) eidx, &ffrom, &fto); /* Create the pairs themselves */ - from = IGRAPH_FROM(graph, eidx); to = IGRAPH_TO(graph, eidx); + from = (long int)ffrom; to = (long int)fto; if (from == to) { loop_weight_sum += weights ? 2 * VECTOR(*weights)[eidx] : 2; continue; @@ -769,13 +771,13 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, pairs[i + 1].dq = pairs[i].dq; pairs[i + 1].opposite = &pairs[i]; /* Link the pair to the communities */ - IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i])); - IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[to].neis, &pairs[i + 1])); + 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 == NULL || *communities.e[from].maxdq->dq < *pairs[i].dq) { + 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 == NULL || *communities.e[to].maxdq->dq < *pairs[i + 1].dq) { + if (communities.e[to].maxdq == 0 || *communities.e[to].maxdq->dq < *pairs[i + 1].dq) { communities.e[to].maxdq = &pairs[i + 1]; } } @@ -785,12 +787,12 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, /* 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, NULL); + 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 == NULL) */ - if (communities.e[i].maxdq != NULL) { + * the heap (to avoid maxdq == 0) */ + if (communities.e[i].maxdq != 0) { communities.heap[j] = &communities.e[i]; - communities.heapindex[i] = j; + communities.heapindex[i] = (igraph_integer_t) j; j++; } else { communities.heapindex[i] = -1; @@ -861,10 +863,10 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, } debug("\n"); #endif - if (communities.heap[0] == NULL) { + if (communities.heap[0] == 0) { break; /* no more communities */ } - if (communities.heap[0]->maxdq == NULL) { + if (communities.heap[0]->maxdq == 0) { break; /* there are only isolated comms */ } to = communities.heap[0]->maxdq->second; @@ -895,13 +897,13 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, 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: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second, + 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: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second); + debug(" Considering: %ld-%ld\n", p1->first, p1->second); if (p1->second == from) { - debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", to, 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", @@ -911,7 +913,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, i++; } else if (p1->second == p2->second) { /* p1->first, p1->second and p2->first form a triangle */ - debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second, + 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", @@ -921,9 +923,9 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, i++; j++; } else { - debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p2->first, p2->second); + debug(" Considering: %ld-%ld\n", p2->first, p2->second); if (p2->second == to) { - debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p2->second, p2->first); + 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", @@ -931,7 +933,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, p2->opposite->second = to; /* p2->opposite->second changed, so it means that * communities.e[p2->second].neis (which contains p2->opposite) is - * not sorted anymore. We have to find the index of p2->opposite in + * 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 @@ -954,11 +956,11 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, } } - p1 = NULL; + p1 = 0; while (i < n) { p1 = (igraph_i_fastgreedy_commpair*)VECTOR(communities.e[to].neis)[i]; if (p1->second == from) { - debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, 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", @@ -994,12 +996,12 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, /* Now, remove community `from` from the neighbors of community `to` */ if (communities.no_of_communities > 2) { - debug(" REMOVING: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", to, from); + 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 = NULL; + communities.e[from].maxdq = 0; /* Update community sizes */ communities.e[to].size += communities.e[from].size; @@ -1013,7 +1015,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, 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 = no_of_nodes + no_of_joins; + communities.e[to].id = (igraph_integer_t) (no_of_nodes + no_of_joins); } /* Update vector a */ @@ -1027,19 +1029,17 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, * smallest decrease in modularity every step. Now we're simply deleting * the excess rows from the merge matrix */ if (no_of_joins < total_joins) { - igraph_integer_t *ivec; - igraph_integer_t merges_nrow = igraph_matrix_int_nrow(merges); - - ivec = IGRAPH_CALLOC(merges_nrow, igraph_integer_t); - IGRAPH_CHECK_OOM(ivec, "Insufficient memory for fast greedy community detection."); + 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_int_permdelete_rows(merges, ivec, total_joins - no_of_joins); - + igraph_matrix_permdelete_rows(merges, ivec, total_joins - no_of_joins); IGRAPH_FREE(ivec); IGRAPH_FINALLY_CLEAN(1); } @@ -1047,7 +1047,7 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, if (modularity) { VECTOR(*modularity)[no_of_joins] = q; - IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_joins + 1)); + igraph_vector_resize(modularity, no_of_joins + 1); } /* Internally, the algorithm does not create NaN values. @@ -1067,18 +1067,18 @@ igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, if (membership) { IGRAPH_CHECK(igraph_community_to_membership(merges, - no_of_nodes, - /*steps=*/ best_no_of_joins, + (igraph_integer_t) no_of_nodes, + /*steps=*/ (igraph_integer_t) best_no_of_joins, membership, /*csize=*/ 0)); } if (merges == &merges_local) { - igraph_matrix_int_destroy(&merges_local); + igraph_matrix_destroy(&merges_local); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } #ifdef IGRAPH_FASTCOMM_DEBUG diff --git a/src/vendor/cigraph/src/community/fluid.c b/src/vendor/cigraph/src/community/fluid.c index d419f7ac12f..e272a06d6a3 100644 --- a/src/vendor/cigraph/src/community/fluid.c +++ b/src/vendor/cigraph/src/community/fluid.c @@ -37,18 +37,18 @@ * 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. Weighted graphs are not supported. + * 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. https://doi.org/10.1007/978-3-319-72150-7_19 + * Springer, vol 689, p 229. * * \param graph The input graph. The graph must be simple and connected. - * Edge directions will be ignored. + * 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 @@ -60,16 +60,17 @@ * * Time complexity: O(|E|) */ -igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, +int igraph_community_fluid_communities(const igraph_t *graph, igraph_integer_t no_of_communities, - igraph_vector_int_t *membership) { + igraph_vector_t *membership, + igraph_real_t *modularity) { /* Declaration of variables */ - igraph_integer_t no_of_nodes, i, j, k, kv1; + long int no_of_nodes, i, j, k, kv1; igraph_adjlist_t al; - igraph_real_t max_density; - igraph_bool_t is_simple, is_connected, running; - igraph_vector_t density, label_counters; - igraph_vector_int_t dominant_labels, node_order, com_to_numvertices; + 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); @@ -77,33 +78,27 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, /* Checking input values */ if (no_of_nodes < 2) { if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + 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 (no_of_communities < 1) { + if ((long int) no_of_communities < 1) { IGRAPH_ERROR("Number of requested communities must be greater than zero.", IGRAPH_EINVAL); } - if (no_of_communities > no_of_nodes) { + 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, &is_simple)); - if (!is_simple) { + IGRAPH_CHECK(igraph_is_simple(graph, &res)); + if (!res) { IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); } - if (igraph_is_directed(graph)) { - /* When the graph is directed, mutual edges are effectively multi-edges as we - * are ignoring edge directions. */ - igraph_bool_t has_mutual; - IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); - if (has_mutual) { - IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); - } - } - IGRAPH_CHECK(igraph_is_connected(graph, &is_connected, IGRAPH_WEAK)); - if (!is_connected) { + 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)) { @@ -114,29 +109,29 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, max_density = 1.0; /* Resize membership vector (number of nodes) */ - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); /* Initialize density and com_to_numvertices vectors */ - IGRAPH_CHECK(igraph_vector_init(&density, no_of_communities)); + 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, no_of_communities)); + 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_int_init_range(&node_order, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + 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_int_null(membership); + 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_int_shuffle(&node_order)); + 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)[VECTOR(node_order)[i]] = i + 1; + 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; } @@ -146,43 +141,42 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, IGRAPH_FINALLY(igraph_adjlist_destroy, &al); /* Create storage space for counting distinct labels and dominant ones */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&dominant_labels, no_of_communities); + 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, no_of_communities)); + IGRAPH_CHECK(igraph_vector_init(&label_counters, (long int) no_of_communities)); IGRAPH_FINALLY(igraph_vector_destroy, &label_counters); - RNG_BEGIN(); - /* running is the convergence boolean variable */ - running = true; + running = 1; while (running) { - /* Declarations of variables used inside main loop */ - igraph_integer_t v1, size, rand_idx; + /* 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 = false; + running = 0; /* Shuffle the node ordering vector */ - IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + 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_int_clear(&dominant_labels); + igraph_vector_clear(&dominant_labels); igraph_vector_null(&label_counters); /* Obtain actual node index */ - v1 = VECTOR(node_order)[i]; + v1 = (long int) VECTOR(node_order)[i]; /* Take into account same label in updating rule */ - kv1 = VECTOR(*membership)[v1]; + 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_int_resize(&dominant_labels, 1)); + IGRAPH_CHECK(igraph_vector_resize(&dominant_labels, 1)); VECTOR(dominant_labels)[0] = kv1; } @@ -190,7 +184,7 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, neis = igraph_adjlist_get(&al, v1); size = igraph_vector_int_size(neis); for (j = 0; j < size; j++) { - k = VECTOR(*membership)[VECTOR(*neis)[j]]; + k = (long int) VECTOR(*membership)[(long)VECTOR(*neis)[j]]; /* skip if it has no label yet */ if (k == 0) { continue; @@ -201,24 +195,25 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, /* 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_int_resize(&dominant_labels, 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_int_push_back(&dominant_labels, k)); + IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k)); } } - if (!igraph_vector_int_empty(&dominant_labels)) { + RNG_BEGIN(); + if (!igraph_vector_empty(&dominant_labels)) { /* Maintain same label if it exists in dominant_labels */ - same_label_in_dominant = igraph_vector_int_contains(&dominant_labels, kv1); + same_label_in_dominant = igraph_vector_contains(&dominant_labels, kv1); if (!same_label_in_dominant) { /* We need at least one more iteration */ - running = true; + running = 1; /* Select randomly from the dominant labels */ - rand_idx = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); - k = VECTOR(dominant_labels)[rand_idx]; + 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 */ @@ -236,27 +231,38 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, VECTOR(density)[k - 1] = max_density / VECTOR(com_to_numvertices)[k - 1]; } } + RNG_END(); } } - 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; - IGRAPH_ASSERT(VECTOR(*membership)[i] >= 0); /* all vertices must have a community assigned */ + /* 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); - igraph_vector_int_destroy(&node_order); + 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_int_destroy(&dominant_labels); - IGRAPH_FINALLY_CLEAN(5); + igraph_vector_destroy(&dominant_labels); + igraph_vector_destroy(&nonzero_labels); + IGRAPH_FINALLY_CLEAN(6); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap.cc b/src/vendor/cigraph/src/community/infomap/infomap.cc index fe690948e17..4985f1ade64 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap.cc @@ -29,43 +29,35 @@ homePage: http://www.irit.fr/~Emmanuel.Navarro/ */ -#include "igraph_community.h" -#include "core/exceptions.h" +#include "igraph_interface.h" +#include "igraph_community.h" #include "core/interruption.h" #include "infomap_Node.h" -#include "infomap_FlowGraph.h" #include "infomap_Greedy.h" -#include -#include - -// This is necessary for GCC 5 and earlier, where including -// makes isnan() unusable without the std:: prefix, even if -// was included as well. -using std::isnan; +#include /****************************************************************************/ -static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { +int infomap_partition(FlowGraph * fgraph, bool rcall) { + Greedy * greedy; // save the original graph - FlowGraph cpy_fgraph(fgraph); + FlowGraph * cpy_fgraph = new FlowGraph(fgraph); + IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); - igraph_integer_t Nnode = cpy_fgraph.Nnode; + int Nnode = cpy_fgraph->Nnode; // "real" number of vertex, ie. number of vertex of the graph - igraph_integer_t iteration = 0; + int iteration = 0; double outer_oldCodeLength, newCodeLength; - std::vector initial_move; + int *initial_move = NULL; bool initial_move_done = true; - // re-use vector in loop for better performance - std::vector subMoveTo; - do { // Main loop - outer_oldCodeLength = fgraph.codeLength; + outer_oldCodeLength = fgraph->codeLength; if (iteration > 0) { /**********************************************************************/ @@ -73,45 +65,62 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { // =========================================== // intial_move indicate current clustering - initial_move.resize(Nnode); + 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; - subMoveTo.clear(); // enventual new partitionment of original graph + int *subMoveTo = NULL; // enventual new partitionment of original graph - if ((iteration % 2 == 0) && (fgraph.Nnode > 1)) { + if ((iteration % 2 == 0) && (fgraph->Nnode > 1)) { // 0/ Submodule movements : partition each module of the // current partition (rec. call) - subMoveTo.resize(Nnode); + subMoveTo = new int[Nnode]; // vid_cpy_fgraph --> new_cluster_id (new partition) - igraph_integer_t subModIndex = 0; + IGRAPH_FINALLY(operator delete [], subMoveTo); + + int subModIndex = 0; - for (igraph_integer_t i = 0 ; i < fgraph.Nnode ; i++) { + for (int i = 0 ; i < fgraph->Nnode ; i++) { // partition each non trivial module - size_t sub_Nnode = fgraph.node[i].members.size(); + int sub_Nnode = fgraph->node[i]->members.size(); if (sub_Nnode > 1) { // If the module is not trivial - const std::vector &sub_members = fgraph.node[i].members; + 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(cpy_fgraph, sub_members); - sub_fgraph.initiate(); + 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 (igraph_integer_t j = 0; j < sub_fgraph.Nnode; j++) { - for (const auto &v : sub_fgraph.node[j].members) { - subMoveTo[sub_members[v]] = subModIndex; + 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; + subMoveTo[fgraph->node[i]->members[0]] = subModIndex; initial_move[subModIndex] = i; subModIndex++; } @@ -119,19 +128,26 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { } else { // 1/ Single-node movements : allows each node to move (again) // save current modules - for (igraph_integer_t i = 0; i < fgraph.Nnode; i++) { // for each module - for (const auto &v : fgraph.node[i].members) { // for each vertex (of the module) - initial_move[v] = i; + 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.empty()) { - Greedy cpy_greedy(&fgraph); + 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); + cpy_greedy->setMove(subMoveTo); + cpy_greedy->apply(false); + + delete_Greedy(cpy_greedy); + IGRAPH_FINALLY_CLEAN(1); + delete [] subMoveTo; + IGRAPH_FINALLY_CLEAN(1); } } /**********************************************************************/ @@ -141,26 +157,29 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { do { // greedy optimizing object creation - Greedy greedy(&fgraph); + greedy = new Greedy(fgraph); + IGRAPH_FINALLY(delete_Greedy, greedy); // Initial move to apply ? - if (!initial_move_done && ! initial_move.empty()) { + if (!initial_move_done && initial_move) { initial_move_done = true; - greedy.setMove(initial_move); + greedy->setMove(initial_move); } - oldCodeLength = greedy.codeLength; + oldCodeLength = greedy->codeLength; bool moved = true; - //igraph_integer_t count = 0; + //int Nloops = 0; + //int count = 0; double inner_oldCodeLength = 1000; while (moved) { // main greedy optimizing loop - inner_oldCodeLength = greedy.codeLength; - moved = greedy.optimize(); + inner_oldCodeLength = greedy->codeLength; + moved = greedy->optimize(); + //Nloops++; //count++; - if (fabs(greedy.codeLength - inner_oldCodeLength) < 1.0e-10) + if (fabs(greedy->codeLength - inner_oldCodeLength) < 1.0e-10) // if the move does'n reduce the codelenght -> exit ! { moved = false; @@ -173,66 +192,72 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { } // transform the network to network of modules: - greedy.apply(true); - newCodeLength = greedy.codeLength; + 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. + * \brief Find community structure that minimizes the expected + * description length of a random walker trajectory. * - * Implementation of the Infomap community detection algorithm of + * Implementation of the InfoMap community detection algorithm.of * Martin Rosvall and Carl T. Bergstrom. * - * - * For more details, see the visualization of the math and the map generator - * at https://www.mapequation.org . The original paper describing the algorithm - * is: 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). - * A more detailed paper about the algorithm is: 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) + * 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 . - * Integration in igraph was done by Emmanuel Navarro (who is grateful to - * Martin Rosvall and Carl T. Bergstrom for providing this source code). + * 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 the original + * 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. - * The random walker will favour edges with high weights over - * edges with low weights; the probability of picking a particular - * outbound edge from a node is directly proportional to its weight. - * If it is \c NULL then all edges will have equal + * If it is a NULL pointer then all edges will have equal * weights. The weights are expected to be non-negative. * \param v_weights Numeric vector giving the weights of the vertices. - * Vertices with higher weights are favoured by the random walker - * when it needs to "teleport" to a new node after getting stuck in - * a sink node (i.e. a node with no outbound edges). The probability - * of picking a vertex when the random walker teleports is directly - * proportional to the weight of the vertex. If this argument is \c NULL - * then all vertices will have equal weights. Weights are expected - * to be positive. + * 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 @@ -246,27 +271,23 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { * * Time complexity: TODO. */ -igraph_error_t igraph_community_infomap(const igraph_t * graph, +int igraph_community_infomap(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights, - igraph_integer_t nb_trials, - igraph_vector_int_t *membership, + int nb_trials, + igraph_vector_t *membership, igraph_real_t *codelength) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; - if (e_weights) { const igraph_integer_t ecount = igraph_ecount(graph); if (igraph_vector_size(e_weights) != ecount) { IGRAPH_ERROR("Invalid edge weight vector length.", IGRAPH_EINVAL); } if (ecount > 0) { - /* Allow both positive and zero weights. - * The conversion to Infomap format will simply skip zero-weight edges/ */ igraph_real_t minweight = igraph_vector_min(e_weights); if (minweight < 0) { IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { + } else if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Edge weights must not be NaN values.", IGRAPH_EINVAL); } } @@ -278,53 +299,58 @@ igraph_error_t igraph_community_infomap(const igraph_t * graph, IGRAPH_ERROR("Invalid vertex weight vector length.", IGRAPH_EINVAL); } if (vcount > 0) { - /* TODO: Currently we require strictly positive. Can this be - * relaxed to non-negative values? */ igraph_real_t minweight = igraph_vector_min(v_weights); if (minweight <= 0) { IGRAPH_ERROR("Vertex weights must be positive.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { + } else if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Vertex weights must not be NaN values.", IGRAPH_EINVAL); } } } - FlowGraph fgraph(graph, e_weights, v_weights); + FlowGraph * fgraph = new FlowGraph(graph, e_weights, v_weights); + IGRAPH_FINALLY(delete_FlowGraph, fgraph); // compute stationary distribution - fgraph.initiate(); + fgraph->initiate(); + FlowGraph * cpy_fgraph ; double shortestCodeLength = 1000.0; // create membership vector - igraph_integer_t Nnode = fgraph.Nnode; - IGRAPH_CHECK(igraph_vector_int_resize(membership, Nnode)); + int Nnode = fgraph->Nnode; + IGRAPH_CHECK(igraph_vector_resize(membership, Nnode)); - for (igraph_integer_t trial = 0; trial < nb_trials; trial++) { - FlowGraph cpy_fgraph(fgraph); + 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; + if (cpy_fgraph->codeLength < shortestCodeLength) { + shortestCodeLength = cpy_fgraph->codeLength; // ... store the partition - for (igraph_integer_t i = 0 ; i < cpy_fgraph.Nnode ; i++) { - size_t Nmembers = cpy_fgraph.node[i].members.size(); - for (size_t k = 0; k < Nmembers; k++) { - //cluster[ cpy_fgraph->node[i].members[k] ] = i; - VECTOR(*membership)[cpy_fgraph.node[i].members[k]] = i; + 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); - IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); + delete fgraph; + IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, 0)); - IGRAPH_HANDLE_EXCEPTIONS_END; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc index 8b77d124e9c..9af7eb3a404 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc @@ -24,127 +24,133 @@ #include "infomap_FlowGraph.h" +#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) + using namespace std; -void FlowGraph::init(igraph_integer_t n, const igraph_vector_t *v_weights) { +void FlowGraph::init(int n, const igraph_vector_t *v_weights) { alpha = 0.15; beta = 1.0 - alpha; Nnode = n; - node.reserve(Nnode); + node = new Node*[Nnode]; if (v_weights) { - for (igraph_integer_t i = 0; i < Nnode; i++) { - node.emplace_back(i, VECTOR(*v_weights)[i]); + for (int i = 0; i < Nnode; i++) { + node[i] = new Node(i, (double)VECTOR(*v_weights)[i]); } } else { - for (igraph_integer_t i = 0; i < Nnode; i++) { - node.emplace_back(i, 1.0); + for (int i = 0; i < Nnode; i++) { + node[i] = new Node(i, 1.0); } } } -FlowGraph::FlowGraph(igraph_integer_t n) { +FlowGraph::FlowGraph(int n) { init(n, NULL); } -FlowGraph::FlowGraph(igraph_integer_t n, const igraph_vector_t *v_weights) { +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, +/* 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) { - igraph_integer_t n = igraph_vcount(graph); + int n = (int)igraph_vcount(graph); init(n, v_weights); - bool directed = igraph_is_directed(graph); + int directed = (int) igraph_is_directed(graph); double linkWeight = 1.0; igraph_integer_t from, to; - igraph_integer_t Nlinks = igraph_ecount(graph); + long int Nlinks = (long int) igraph_ecount(graph); if (!directed) { Nlinks = Nlinks * 2 ; } - for (igraph_integer_t i = 0; i < Nlinks; i++) { + for (int i = 0; i < Nlinks; i++) { if (!directed) { // not directed if (i % 2 == 0) { - linkWeight = e_weights ? VECTOR(*e_weights)[i / 2] : 1.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 ? VECTOR(*e_weights)[i] : 1.0; + linkWeight = e_weights ? (double)VECTOR(*e_weights)[i] : 1.0; igraph_edge(graph, i, &from, &to); } // Populate node from igraph_graph - // Negative edge weights were checked for already. - // We skip adding zero-weight edges. if (linkWeight > 0.0) { if (from != to) { - node[from].outLinks.push_back(make_pair(to, linkWeight)); - node[to].inLinks.push_back(make_pair(from, linkWeight)); + node[(int) from]->outLinks.push_back(make_pair((int)to, linkWeight)); + node[(int) to]->inLinks.push_back(make_pair((int) from, linkWeight)); } } } } -FlowGraph::FlowGraph(const FlowGraph &fgraph) { - igraph_integer_t n = fgraph.Nnode; +FlowGraph::FlowGraph(FlowGraph * fgraph) { + int n = fgraph->Nnode; init(n, NULL); - for (igraph_integer_t i = 0; i < n; i++) { - node[i] = fgraph.node[i]; + 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 ; + 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; + 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; + codeLength = fgraph->codeLength; } /** construct a graph by extracting a subgraph from the given graph */ -FlowGraph::FlowGraph(const FlowGraph &fgraph, const vector &sub_members) { - igraph_integer_t sub_Nnode = sub_members.size(); - +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(sub_members.begin(), sub_members.end()); - - set::iterator it_mem = sub_mem.begin(); + 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(fgraph.Nnode, -1); + vector sub_renumber = vector(fgraph->Nnode); // id --> sub_id - for (igraph_integer_t j = 0; j < sub_Nnode; j++) { + 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]; - igraph_integer_t orig_nr = (*it_mem); + int orig_nr = (*it_mem); - node[j].teleportWeight = fgraph.node[orig_nr].teleportWeight; - node[j].selfLink = fgraph.node[orig_nr].selfLink; + node[j]->teleportWeight = fgraph->node[orig_nr]->teleportWeight; + node[j]->selfLink = fgraph->node[orig_nr]->selfLink; // Take care of self-link - size_t orig_NoutLinks = fgraph.node[orig_nr].outLinks.size(); - size_t orig_NinLinks = fgraph.node[orig_nr].inLinks.size(); + int orig_NoutLinks = fgraph->node[orig_nr]->outLinks.size(); + int orig_NinLinks = fgraph->node[orig_nr]->inLinks.size(); sub_renumber[orig_nr] = j; - for (size_t k = 0; k < orig_NoutLinks; k++) { - igraph_integer_t to = fgraph.node[orig_nr].outLinks[k].first; - igraph_integer_t to_newnr = sub_renumber[to]; - double link_weight = fgraph.node[orig_nr].outLinks[k].second; + 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 @@ -153,21 +159,21 @@ FlowGraph::FlowGraph(const FlowGraph &fgraph, const vector &su 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)); + // 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 (size_t k = 0; k < orig_NinLinks; k++) { - igraph_integer_t to = fgraph.node[orig_nr].inLinks[k].first; - igraph_integer_t to_newnr = sub_renumber[to]; - double link_weight = fgraph.node[orig_nr].inLinks[k].second; + 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)); + node[j]->inLinks.push_back(make_pair(to_newnr, link_weight)); + node[to_newnr]->outLinks.push_back(make_pair(j, link_weight)); } } } @@ -176,15 +182,31 @@ FlowGraph::FlowGraph(const FlowGraph &fgraph, const vector &su } +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.swap(fgraph.node); +void FlowGraph::swap(FlowGraph * fgraph) { + Node ** node_tmp = fgraph->node; + int Nnode_tmp = fgraph->Nnode; + + fgraph->node = node; + fgraph->Nnode = Nnode; - igraph_integer_t Nnode_tmp = fgraph.Nnode; - fgraph.Nnode = Nnode; + node = node_tmp; Nnode = Nnode_tmp; calibrate(); @@ -201,26 +223,26 @@ void FlowGraph::initiate() { // total teleport weight Ndanglings = 0; double totTeleportWeight = 0.0; - for (igraph_integer_t i = 0; i < Nnode; i++) { - totTeleportWeight += node[i].teleportWeight; + for (int i = 0; i < Nnode; i++) { + totTeleportWeight += node[i]->teleportWeight; } - for (igraph_integer_t i = 0; i < Nnode; i++) { - node[i].teleportWeight /= totTeleportWeight; + for (int i = 0; i < Nnode; i++) { + node[i]->teleportWeight /= totTeleportWeight; // normalize teleportation weight - if (node[i].outLinks.empty() && (node[i].selfLink <= 0.0)) { + if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) { danglings.push_back(i); Ndanglings++; } else { // Normalize the weights - size_t NoutLinks = node[i].outLinks.size(); - double sum = node[i].selfLink; // Take care of self-links - for (size_t j = 0; j < NoutLinks; j++) { - sum += node[i].outLinks[j].second; + 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 (size_t j = 0; j < NoutLinks; j++) { - node[i].outLinks[j].second /= sum; + node[i]->selfLink /= sum; + for (int j = 0; j < NoutLinks; j++) { + node[i]->outLinks[j].second /= sum; } } } @@ -229,25 +251,25 @@ void FlowGraph::initiate() { eigenvector(); // Update links to represent flow - for (igraph_integer_t i = 0; i < Nnode; i++) { - node[i].selfLink = beta * node[i].size * node[i].selfLink; + 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()) { - size_t NoutLinks = node[i].outLinks.size(); - for (size_t j = 0; j < NoutLinks; j++) { - node[i].outLinks[j].second = beta * node[i].size * - node[i].outLinks[j].second; + 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 (size_t j = 0; j < NoutLinks; j++) { - size_t NinLinks = node[node[i].outLinks[j].first].inLinks.size(); - for (size_t 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; + 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; } } @@ -256,23 +278,23 @@ void FlowGraph::initiate() { } // To be able to handle dangling nodes efficiently - for (igraph_integer_t i = 0; i < Nnode; i++) - if (node[i].outLinks.empty() && (node[i].selfLink <= 0.0)) { - node[i].danglingSize = node[i].size; + 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; + node[i]->danglingSize = 0.0; } nodeSize_log_nodeSize = 0.0 ; // The exit flow from each node at initiation - for (igraph_integer_t 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); + 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(); @@ -280,10 +302,10 @@ void FlowGraph::initiate() { /* Compute steady state distribution (ie. PageRank) over the network - * (for all i update node[i].size) + * (for all i update node[i]->size) */ void FlowGraph::eigenvector() { - vector size_tmp(Nnode, 1.0 / Nnode); + vector size_tmp = vector(Nnode, 1.0 / Nnode); int Niterations = 0; double danglingSize; @@ -294,35 +316,35 @@ void FlowGraph::eigenvector() { do { // Calculate dangling size danglingSize = 0.0; - for (igraph_integer_t i = 0; i < Ndanglings; i++) { + for (int i = 0; i < Ndanglings; i++) { danglingSize += size_tmp[danglings[i]]; } // Flow from teleportation - for (igraph_integer_t i = 0; i < Nnode; i++) { - node[i].size = (alpha + beta * danglingSize) * node[i].teleportWeight; + for (int i = 0; i < Nnode; i++) { + node[i]->size = (alpha + beta * danglingSize) * node[i]->teleportWeight; } // Flow from network steps - for (igraph_integer_t i = 0; i < Nnode; i++) { - node[i].size += beta * node[i].selfLink * size_tmp[i]; - size_t Nlinks = node[i].outLinks.size(); - for (size_t j = 0; j < Nlinks; j++) - node[node[i].outLinks[j].first].size += beta * - node[i].outLinks[j].second * size_tmp[i]; + 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 (igraph_integer_t i = 0; i < Nnode; i++) { - sum += node[i].size; + for (int i = 0; i < Nnode; i++) { + sum += node[i]->size; } sqdiff_old = sqdiff; sqdiff = 0.0; - for (igraph_integer_t i = 0; i < Nnode; i++) { - node[i].size /= sum; - sqdiff += fabs(node[i].size - size_tmp[i]); - size_tmp[i] = node[i].size; + 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++; @@ -334,7 +356,7 @@ void FlowGraph::eigenvector() { } while ((Niterations < 200) && (sqdiff > 1.0e-15 || Niterations < 50)); danglingSize = 0.0; - for (igraph_integer_t i = 0; i < Ndanglings; i++) { + for (int i = 0; i < Ndanglings; i++) { danglingSize += size_tmp[danglings[i]]; } // cout << "done! (the error is " << sqdiff << " after " << Niterations @@ -350,13 +372,13 @@ void FlowGraph::calibrate() { exitFlow = 0.0; size_log_size = 0.0; - for (igraph_integer_t i = 0; i < Nnode; i++) { // For each module + for (int i = 0; i < Nnode; i++) { // For each module // own node/module codebook - size_log_size += plogp(node[i].exit + node[i].size); + 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); + exitFlow += node[i]->exit; + exit_log_exit += plogp(node[i]->exit); } exit = plogp(exitFlow); @@ -368,20 +390,31 @@ void FlowGraph::calibrate() { /* Restore the data from the given FlowGraph object */ -void FlowGraph::back_to(const FlowGraph &fgraph) { - // delete current nodes and copy original ones - Nnode = fgraph.Nnode; - node = fgraph.node; +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 ; + 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; + 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; + codeLength = fgraph->codeLength; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h index c4c866dee56..937347cdc4a 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h @@ -22,52 +22,47 @@ */ -#ifndef INFOMAP_FLOWGRAPH_H -#define INFOMAP_FLOWGRAPH_H - -#include "infomap_Node.h" - -#include "igraph_datatype.h" -#include "igraph_types.h" -#include "igraph_vector.h" +#ifndef FLOWGRAPH_H +#define FLOWGRAPH_H #include #include -#include -inline double plogp(double x) { - return x > 0.0 ? x*std::log(x) : 0.0; -} +#include "igraph_interface.h" + +#include "infomap_Node.h" class FlowGraph { private: - void init(igraph_integer_t n, const igraph_vector_t *nodeWeights); + void init(int n, const igraph_vector_t *nodeWeights); public: - FlowGraph(igraph_integer_t n); - FlowGraph(igraph_integer_t n, const igraph_vector_t *nodeWeights); - FlowGraph(const FlowGraph &fgraph); - FlowGraph(const FlowGraph &fgraph, const std::vector &sub_members); + 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, + FlowGraph(const igraph_t * graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights); - void swap(FlowGraph &fgraph); + ~FlowGraph(); + + void swap(FlowGraph * fgraph); void initiate(); void eigenvector(); void calibrate(); - void back_to(const FlowGraph &fgraph); + void back_to(FlowGraph * fgraph); /*************************************************************************/ - std::vector node; - igraph_integer_t Nnode; + Node **node; + int Nnode; double alpha, beta; - igraph_integer_t Ndanglings; - std::vector danglings; // id of dangling nodes + int Ndanglings; + std::vector danglings; // id of dangling nodes double exit; // double exitFlow; // @@ -78,4 +73,6 @@ class FlowGraph { double codeLength; }; -#endif // INFOMAP_FLOWGRAPH_H +void delete_FlowGraph(FlowGraph *fgraph); + +#endif diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc index 2714d2e3847..62e2945c1a8 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc @@ -23,49 +23,55 @@ */ #include "infomap_Greedy.h" - -#include #include -#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 +Greedy::Greedy(FlowGraph * fgraph) { + graph = fgraph; + Nnode = graph->Nnode; + + alpha = graph->alpha;// teleportation probability + beta = 1.0 - alpha; // probability to take normal step - node_index(Nnode), + Nempty = 0; + vector(Nnode).swap(mod_empty); - Nempty(0), + 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); - mod_empty(Nnode), - mod_exit(Nnode), - mod_size(Nnode), - mod_danglingSize(Nnode), - mod_teleportWeight(Nnode), - mod_members(Nnode) -{ nodeSize_log_nodeSize = graph->nodeSize_log_nodeSize; exit_log_exit = graph->exit_log_exit; size_log_size = graph->size_log_size; exitFlow = graph->exitFlow; - const std::vector &node = graph->node; - for (igraph_integer_t i = 0; i < Nnode; i++) { // For each module + 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_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(); + 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; + codeLength = exit - 2.0 * exit_log_exit + size_log_size - + nodeSize_log_nodeSize; +} + +Greedy::~Greedy() { +} + +void delete_Greedy(Greedy *greedy) { + delete greedy; } @@ -74,46 +80,46 @@ Greedy::Greedy(FlowGraph * fgraph) : */ bool Greedy::optimize() { bool moved = false; - const std::vector &node = graph->node; + Node ** node = graph->node; RNG_BEGIN(); // Generate random enumeration of nodes - vector randomOrder(Nnode); - for (igraph_integer_t i = 0; i < Nnode; i++) { + vector randomOrder(Nnode); + for (int i = 0; i < Nnode; i++) { randomOrder[i] = i; } - for (igraph_integer_t i = 0; i < Nnode - 1; i++) { + for (int i = 0; i < Nnode - 1; i++) { //int randPos = i ; //XXX - igraph_integer_t randPos = static_cast(RNG_INTEGER(i, Nnode - 1)); + int randPos = RNG_INTEGER(i, Nnode - 1); // swap i & randPos - igraph_integer_t tmp = randomOrder[i]; + int tmp = randomOrder[i]; randomOrder[i] = randomOrder[randPos]; randomOrder[randPos] = tmp; } - igraph_integer_t offset = 1; - vector redirect(Nnode, 0); - vector > > flowNtoM(Nnode); + unsigned int offset = 1; + vector redirect(Nnode, 0); + vector > > flowNtoM(Nnode); - for (igraph_integer_t k = 0; k < Nnode; k++) { + for (int k = 0; k < Nnode; k++) { // Pick nodes in random order - igraph_integer_t flip = randomOrder[k]; - igraph_integer_t oldM = node_index[flip]; + int flip = randomOrder[k]; + int oldM = node_index[flip]; - // Reset offset when igraph_integer_t overflows - if (offset > IGRAPH_INTEGER_MAX) { - for (igraph_integer_t j = 0; j < Nnode; j++) { + // 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 - igraph_integer_t NmodLinks = 0; + int NmodLinks = 0; // For all outLinks - size_t NoutLinks = node[flip].outLinks.size(); + 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; @@ -121,10 +127,10 @@ bool Greedy::optimize() { flowNtoM[NmodLinks].second.second = 0.0; NmodLinks++; } else { - for (size_t j = 0; j < NoutLinks; j++) { - igraph_integer_t nb_M = node_index[node[flip].outLinks[j].first]; + 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; + 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; @@ -138,10 +144,10 @@ bool Greedy::optimize() { } } // For all inLinks - size_t NinLinks = node[flip].inLinks.size(); - for (size_t j = 0; j < NinLinks; j++) { - igraph_integer_t nb_M = node_index[node[flip].inLinks[j].first]; - double nb_flow = node[flip].inLinks[j].second; + 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; @@ -155,42 +161,42 @@ bool Greedy::optimize() { } // For teleportation and dangling nodes - for (igraph_integer_t j = 0; j < NmodLinks; j++) { - igraph_integer_t newM = flowNtoM[j].first; + 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); + (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; + (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) * + (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; + 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) ; + (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; + (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] > node[flip].members.size()) { + 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; @@ -200,10 +206,10 @@ bool Greedy::optimize() { } // Randomize link order for optimized search - for (igraph_integer_t j = 0; j < NmodLinks - 1; j++) { + for (int j = 0; j < NmodLinks - 1; j++) { //int randPos = j ; // XXX - igraph_integer_t randPos = static_cast(RNG_INTEGER(j, NmodLinks - 1)); - igraph_integer_t tmp_M = flowNtoM[j].first; + 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; @@ -214,15 +220,15 @@ bool Greedy::optimize() { flowNtoM[randPos].second.second = tmp_inFlow; } - igraph_integer_t bestM = oldM; + 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 (igraph_integer_t j = 0; j < NmodLinks; j++) { + for (int j = 0; j < NmodLinks; j++) { - igraph_integer_t newM = flowNtoM[j].first; + int newM = flowNtoM[j].first; double outFlowNewM = flowNtoM[j].second.first; double inFlowNewM = flowNtoM[j].second.second; @@ -233,16 +239,16 @@ bool Greedy::optimize() { 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 - + 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); + + 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; @@ -262,7 +268,7 @@ bool Greedy::optimize() { if (mod_members[bestM] == 0) { Nempty--; } - if (mod_members[oldM] == node[flip].members.size()) { + if (mod_members[oldM] == static_cast(node[flip]->members.size())) { mod_empty[Nempty] = oldM; Nempty++; } @@ -273,19 +279,19 @@ bool Greedy::optimize() { size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[bestM] + mod_size[bestM]); - mod_exit[oldM] -= node[flip].exit - outFlowOldM - + 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_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 - + 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(); + 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]; @@ -318,106 +324,127 @@ 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) - modSnode.reserve(Nnode); - - igraph_integer_t Nmod = 0; - for (igraph_integer_t i = 0; i < Nnode; i++) { - if (mod_members[i] > 0) { - Nmod++; - modSnode.push_back(i); - } - } - + vector modSnode; // will give ids of no-empty modules (nodes) + int Nmod = 0; if (sort) { - // sort by mod_size - std::sort(modSnode.begin(), modSnode.end(), - [&](double a, double b) { return mod_size[a] > mod_size[b]; } ); + 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(Nmod); - vector &node_tmp = tmp_fgraph.node ; + FlowGraph * tmp_fgraph = new FlowGraph(Nmod); + IGRAPH_FINALLY(delete_FlowGraph, tmp_fgraph); + Node ** node_tmp = tmp_fgraph->node ; - const vector &node = graph->node; + Node ** node = graph->node; - vector nodeInMod(Nnode); + vector nodeInMod = vector(Nnode); // creation of new nodes - for (igraph_integer_t i = 0; i < Nmod; i++) { + for (int i = 0; i < Nmod; i++) { //node_tmp[i] = new Node(); - node_tmp[i].members.clear(); // 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]]; + 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); + vector > outFlowNtoM(Nmod); + map::iterator it_M; - for (igraph_integer_t i = 0; i < Nnode; i++) { - igraph_integer_t i_M = nodeInMod[node_index[i]]; //final id of the module of the node i + 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 ) ); - - for (const auto &link : node[i].outLinks) { - igraph_integer_t nb = link.first; - igraph_integer_t nb_M = nodeInMod[node_index[nb]]; - double nb_flow = link.second; + 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) { - // inserts key nb_M if it does not exist - outFlowNtoM[i_M][nb_M] += nb_flow; + 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 (igraph_integer_t i = 0; i < Nmod; i++) { - for (const auto &item : outFlowNtoM[i]) { - if (item.first != i) { - node_tmp[i].outLinks.push_back(item); + 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 (igraph_integer_t i = 0; i < Nnode; i++) { - igraph_integer_t i_M = nodeInMod[node_index[i]]; - for (const auto &inLink : node[i].inLinks) { - igraph_integer_t nb = inLink.first; - igraph_integer_t nb_M = nodeInMod[node_index[nb]]; - double nb_flow = inLink.second; + 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) { - // inserts key nb_M if it does not exist - inFlowNtoM[i_M][nb_M] += nb_flow; + 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 (igraph_integer_t i = 0; i < Nmod; i++) { - for (const auto &item : inFlowNtoM[i]) { - if (item.first != i) { - node_tmp[i].inLinks.push_back(item); + 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 - mod_empty.clear(); + 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); } @@ -436,15 +463,13 @@ void Greedy::apply(bool sort) { * - codeLength * according to **node / node[i]->index */ -/* unused */ -/* void Greedy::tune(void) { exit_log_exit = 0.0; size_log_size = 0.0; exitFlow = 0.0; - for (igraph_integer_t i = 0; i < Nnode; i++) { + for (int i = 0; i < Nnode; i++) { mod_exit[i] = 0.0; mod_size[i] = 0.0; mod_danglingSize[i] = 0.0; @@ -452,20 +477,21 @@ void Greedy::tune(void) { mod_members[i] = 0; } - const std::vector &node = graph->node; + Node ** node = graph->node; // Update all values except contribution from teleportation - for (igraph_integer_t i = 0; i < Nnode; i++) { - igraph_integer_t i_M = node_index[i]; // module id of node i + 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_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 (const auto &link : node[i].outLinks) { - igraph_integer_t neighbor = link.first; - double neighbor_w = link.second; - igraph_integer_t neighbor_M = node_index[neighbor]; + 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; } @@ -473,12 +499,12 @@ void Greedy::tune(void) { } // Update contribution from teleportation - for (igraph_integer_t i = 0; i < Nnode; i++) { + for (int i = 0; i < Nnode; i++) { mod_exit[i] += (alpha * mod_size[i] + beta * mod_danglingSize[i]) * (1.0 - mod_teleportWeight[i]); } - for (igraph_integer_t i = 0; i < Nnode; 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]; @@ -488,40 +514,40 @@ void Greedy::tune(void) { 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(const std::vector &moveTo) { +void Greedy::setMove(int *moveTo) { //void Greedy::determMove(int *moveTo) { - const std::vector &node = graph->node; + Node ** node = graph->node; //printf("setMove nNode:%d \n", Nnode); - for (igraph_integer_t i = 0 ; i < Nnode ; i++) { // pour chaque module - igraph_integer_t oldM = i; - igraph_integer_t newM = moveTo[i]; + 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) + + 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) + 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; + node[i]->teleportWeight; // For all outLinks - for (const auto &outLink : node[i].outLinks) { - igraph_integer_t nb_M = node_index[outLink.first]; - double nb_flow = outLink.second; + 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) { @@ -530,9 +556,10 @@ void Greedy::setMove(const std::vector &moveTo) { } // For all inLinks - for (const auto &inLink : node[i].inLinks) { - igraph_integer_t nb_M = node_index[inLink.first]; - double nb_flow = inLink.second; + 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) { @@ -546,7 +573,7 @@ void Greedy::setMove(const std::vector &moveTo) { // si le nouveau etait vide, on a un vide de moins... Nempty--; } - if (mod_members[oldM] == node[i].members.size()) { + 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++; @@ -557,16 +584,16 @@ void Greedy::setMove(const std::vector &moveTo) { 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(); + 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]); diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h index 6c0594e33b9..9769d1d4ca0 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h @@ -22,22 +22,27 @@ */ -#ifndef INFOMAP_GREEDY_H -#define INFOMAP_GREEDY_H +#ifndef GREEDY_H +#define GREEDY_H -#include "infomap_Node.h" -#include "infomap_FlowGraph.h" +#include +#include +#include +#include #include "igraph_random.h" -#include +#include "infomap_Node.h" +#include "infomap_FlowGraph.h" class Greedy { public: Greedy(FlowGraph * fgraph); // initialise les attributs par rapport au graph - void setMove(const std::vector &moveTo); + ~Greedy(); + + void setMove(int *moveTo); //virtual void determMove(int *moveTo); bool optimize(); @@ -46,16 +51,12 @@ class Greedy { void apply(bool sort); //virtual void level(Node ***, bool sort); - /* void tune(void); */ /* unused */ + void tune(void); /**************************************************************************/ -public: - double codeLength; - -private: FlowGraph * graph; - igraph_integer_t Nnode; + int Nnode; double exit; double exitFlow; @@ -63,19 +64,22 @@ class Greedy { 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 + std::vector node_index; // module number of each node - igraph_integer_t Nempty; - std::vector mod_empty; + 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; + std::vector mod_members; }; -#endif // INFOMAP_GREEDY_H +void delete_Greedy(Greedy *greedy); +#endif diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Node.cc b/src/vendor/cigraph/src/community/infomap/infomap_Node.cc new file mode 100644 index 00000000000..21f1b3160dd --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/community/infomap/infomap_Node.h b/src/vendor/cigraph/src/community/infomap/infomap_Node.h index ee3d128f85c..5cd0407caa2 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Node.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_Node.h @@ -22,24 +22,23 @@ */ -#ifndef INFOMAP_NODE_H -#define INFOMAP_NODE_H - -#include "igraph_interface.h" +#ifndef NODE_H +#define NODE_H #include +#include -struct Node { +#include "igraph_interface.h" - Node() : selfLink(0.0), exit(0.0), size(0.0) {} - Node(igraph_integer_t modulenr, double tpweight) : Node() { - teleportWeight = tpweight; - members.push_back(modulenr); // members = [nodenr] - } +class Node { +public: - std::vector members; - std::vector< std::pair > inLinks; - std::vector< std::pair > outLinks; + Node(); + Node(int modulenr, double tpweight); + + std::vector members; + std::vector< std::pair > inLinks; + std::vector< std::pair > outLinks; double selfLink; double teleportWeight; @@ -48,4 +47,6 @@ struct Node { double size; }; -#endif // INFOMAP_NODE_H +void cpyNode(Node *newNode, Node *oldNode); + +#endif diff --git a/src/vendor/cigraph/src/community/label_propagation.c b/src/vendor/cigraph/src/community/label_propagation.c index 5c9f3696826..4fc9e4c5d97 100644 --- a/src/vendor/cigraph/src/community/label_propagation.c +++ b/src/vendor/cigraph/src/community/label_propagation.c @@ -48,42 +48,17 @@ * (among the ones incident on node \c i) have the highest total weight. * * - * For directed graphs, it is important to know that labels can circulate - * freely only within the strongly connected components of the graph and - * may propagate in only one direction (or not at all) \em between strongly - * connected components. You should treat directed edges as directed only - * if you are aware of the consequences. - * - * - * References: + * 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). + * Phys Rev E 76, 036106. (2007). * https://doi.org/10.1103/PhysRevE.76.036106 * - * - * Šubelj, L.: Label propagation for clustering. Chapter in "Advances in - * Network Clustering and Blockmodeling" edited by P. Doreian, V. Batagelj - * & A. Ferligoj (Wiley, New York, 2018). - * https://doi.org/10.1002/9781119483298.ch5 - * https://arxiv.org/abs/1709.05634 - * - * \param graph The input graph. Note that the algorithm wsa originally - * defined for undirected graphs. You are advised to set \p mode to - * \c IGRAPH_ALL if you pass a directed graph here to treat it as - * undirected. + * \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 mode Whether to consider edge directions for the label propagation, - * and if so, which direction the labels should propagate. Ignored for - * undirected graphs. \c IGRAPH_ALL means to ignore edge directions (even - * in directed graphs). \c IGRAPH_OUT means to propagate labels along the - * natural direction of the edges. \c IGRAPH_IN means to propagate labels - * \em backwards (i.e. from head to tail). It is advised to set this to - * \c IGRAPH_ALL unless you are specifically interested in the effect of - * edge directions. * \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 @@ -101,40 +76,36 @@ * \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. Note that vertices without labels - * cannot be fixed. The fixed status will be ignored for these with a + * cannot be fixed. The fixed status will be ignord for these with a * warning. Also note that label numbers by themselves have no meaning, * and igraph may renumber labels. However, co-membership constraints * will be respected: two vertices can be fixed to be in the same or in * different communities. * \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. Note that igraph will calculate the - * \em directed modularity if the input graph is directed, even if - * you set \p mode to \c IGRAPH_ALL + * structure is stored here. * \return Error code. * * Time complexity: O(m+n) * * \example examples/simple/igraph_community_label_propagation.c */ -igraph_error_t igraph_community_label_propagation(const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_neimode_t mode, +int igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_t *membership, const igraph_vector_t *weights, - const igraph_vector_int_t *initial, - const igraph_vector_bool_t *fixed) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_not_fixed_nodes = no_of_nodes; - igraph_integer_t i, j, k; + 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_neimode_t reversed_mode; - igraph_vector_t label_counters; - igraph_vector_int_t dominant_labels, nonzero_labels, node_order; + 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 @@ -160,7 +131,7 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); } - if (isnan(minweight)) { + if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -169,10 +140,10 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, IGRAPH_WARNING("Ignoring fixed vertices as no initial labeling given."); } - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); if (initial) { - if (igraph_vector_int_size(initial) != no_of_nodes) { + 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 */ @@ -180,7 +151,7 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, if (VECTOR(*initial)[i] < 0) { VECTOR(*membership)[i] = 0; } else { - VECTOR(*membership)[i] = VECTOR(*initial)[i] + 1; + VECTOR(*membership)[i] = floor(VECTOR(*initial)[i]) + 1; } } if (fixed) { @@ -192,13 +163,13 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, /* 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); + fixed_copy = igraph_Calloc(1, igraph_vector_bool_t); if (fixed_copy == 0) { - IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, fixed_copy); - IGRAPH_CHECK(igraph_vector_bool_init_copy(fixed_copy, fixed)); + IGRAPH_CHECK(igraph_vector_bool_copy(fixed_copy, fixed)); IGRAPH_FINALLY(igraph_vector_bool_destroy, fixed_copy); } @@ -210,7 +181,7 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, } } - i = igraph_vector_int_max(membership); + 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); } @@ -220,30 +191,28 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, } } - reversed_mode = IGRAPH_REVERSE_MODE(mode); - /* 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, reversed_mode, IGRAPH_LOOPS_ONCE)); + 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, reversed_mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + 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_INT_INIT_FINALLY(&dominant_labels, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&nonzero_labels, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&dominant_labels, 2)); + 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_INT_INIT_FINALLY(&node_order, no_of_not_fixed_nodes); + 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; @@ -251,106 +220,107 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, } } } else { - IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + 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 = true; - running = true; + control_iteration = 1; + running = 1; while (running) { - igraph_integer_t v1, num_neis; + 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 beginning of - the iteration that 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 = false; + /* 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_int_shuffle(&node_order)); + 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 = VECTOR(node_order)[i]; + v1 = (long int) VECTOR(node_order)[i]; /* Count the weights corresponding to different labels */ - igraph_vector_int_clear(&dominant_labels); - igraph_vector_int_clear(&nonzero_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 = VECTOR(*membership)[IGRAPH_OTHER(graph, VECTOR(*ineis)[j], v1)]; + 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)[VECTOR(*ineis)[j]]; + 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_int_push_back(&nonzero_labels, k)); + 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_int_resize(&dominant_labels, 1)); + 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_int_push_back(&dominant_labels, 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 = VECTOR(*membership)[VECTOR(*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_int_push_back(&nonzero_labels, k)); + 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_int_resize(&dominant_labels, 1)); + 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_int_push_back(&dominant_labels, k)); + IGRAPH_CHECK(igraph_vector_push_back(&dominant_labels, k)); } } } - if (igraph_vector_int_size(&dominant_labels) > 0) { + 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)[VECTOR(*membership)[v1]] != max_count) { + if (VECTOR(label_counters)[(long)VECTOR(*membership)[v1]] != max_count) { /* Nope, we need at least one more iteration */ - running = true; + running = 1; } } else { /* Select randomly from the dominant labels */ - k = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); - VECTOR(*membership)[v1] = VECTOR(dominant_labels)[k]; + 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_int_size(&nonzero_labels); + num_neis = igraph_vector_size(&nonzero_labels); for (j = 0; j < num_neis; j++) { - VECTOR(label_counters)[VECTOR(nonzero_labels)[j]] = 0; + VECTOR(label_counters)[(long int)VECTOR(nonzero_labels)[j]] = 0; } } RNG_END(); @@ -367,12 +337,12 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); /* Shift back the membership vector, permute labels in increasing order */ - /* We recycle label_counters here :) and use it as an integer vector from now on */ + /* We recycle label_counters here :) */ igraph_vector_fill(&label_counters, -1); j = 0; - unlabelled_left = false; + unlabelled_left = 0; for (i = 0; i < no_of_nodes; i++) { - k = VECTOR(*membership)[i] - 1; + k = (long)VECTOR(*membership)[i] - 1; if (k >= 0) { if (VECTOR(label_counters)[k] == -1) { /* We have seen this label for the first time */ @@ -380,11 +350,11 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, k = j; j++; } else { - k = (igraph_integer_t) VECTOR(label_counters)[k]; + k = (long int) VECTOR(label_counters)[k]; } } else { /* This is an unlabeled vertex */ - unlabelled_left = true; + unlabelled_left = 1; } VECTOR(*membership)[i] = k; } @@ -401,39 +371,39 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, * with the same label. */ if (unlabelled_left) { - igraph_dqueue_int_t q; - igraph_vector_int_t neis; + 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_int_shuffle(&node_order)); + IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + IGRAPH_CHECK(igraph_dqueue_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); for (i=0; i < no_of_nodes; ++i) { - igraph_integer_t v = VECTOR(node_order)[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_int_push(&q, v); + igraph_dqueue_push(&q, v); VECTOR(*membership)[v] = j; - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t ni, num_neis; - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + while (!igraph_dqueue_empty(&q)) { + long int ni, num_neis; + long int actnode = igraph_dqueue_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - num_neis = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_OUT)); + num_neis = igraph_vector_size(&neis); for (ni = 0; ni < num_neis; ++ni) { - igraph_integer_t neighbor = VECTOR(neis)[ni]; + long int neighbor = VECTOR(neis)[ni]; if (IS_UNLABELLED(neighbor)) { VECTOR(*membership)[neighbor] = j; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); } } } @@ -441,20 +411,26 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, } } - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&q); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_int_destroy(&node_order); + 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_int_destroy(&dominant_labels); - igraph_vector_int_destroy(&nonzero_labels); + 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_Free(fixed_copy); IGRAPH_FINALLY_CLEAN(2); } diff --git a/src/vendor/cigraph/src/community/leading_eigenvector.c b/src/vendor/cigraph/src/community/leading_eigenvector.c index 264d0227775..ca7c2932295 100644 --- a/src/vendor/cigraph/src/community/leading_eigenvector.c +++ b/src/vendor/cigraph/src/community/leading_eigenvector.c @@ -28,14 +28,13 @@ #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_iterators.h" +#include "igraph_memory.h" #include "igraph_random.h" #include "igraph_statusbar.h" #include "igraph_structural.h" #include "core/interruption.h" -#include - /** * \section about_leading_eigenvector_methods * @@ -79,48 +78,47 @@ */ typedef struct igraph_i_community_leading_eigenvector_data_t { - igraph_vector_int_t *idx; - igraph_vector_int_t *idx2; + igraph_vector_t *idx; + igraph_vector_t *idx2; igraph_adjlist_t *adjlist; igraph_inclist_t *inclist; igraph_vector_t *tmp; - igraph_integer_t no_of_edges; - igraph_vector_int_t *mymembership; - igraph_integer_t comm; + 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 igraph_error_t igraph_i_community_leading_eigenvector( - igraph_real_t *to, - const igraph_real_t *from, - int n, void *extra) { +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; - igraph_integer_t j, k, nlen, size = n; - igraph_vector_int_t *idx = data->idx; - igraph_vector_int_t *idx2 = data->idx2; + 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; - igraph_integer_t no_of_edges = data->no_of_edges; - igraph_vector_int_t *mymembership = data->mymembership; - igraph_integer_t comm = data->comm; + 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++) { - igraph_integer_t oldid = VECTOR(*idx)[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++) { - igraph_integer_t nei = VECTOR(*neis)[k]; - igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; + long int nei = (long int) VECTOR(*neis)[k]; + long int neimemb = (long int) VECTOR(*mymembership)[nei]; if (neimemb == comm) { - to[j] += from[ VECTOR(*idx2)[nei] ]; + to[j] += from[ (long int) VECTOR(*idx2)[nei] ]; VECTOR(*tmp)[j] += 1; } } @@ -129,9 +127,9 @@ static igraph_error_t igraph_i_community_leading_eigenvector( /* Now calculate k^Tx/2m */ ktx = 0.0; ktx2 = 0.0; for (j = 0; j < size; j++) { - igraph_integer_t oldid = VECTOR(*idx)[j]; + long int oldid = (long int) VECTOR(*idx)[j]; igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); - igraph_integer_t degree = igraph_vector_int_size(neis); + long int degree = igraph_vector_int_size(neis); ktx += from[j] * degree; ktx2 += degree; } @@ -140,7 +138,7 @@ static igraph_error_t igraph_i_community_leading_eigenvector( /* Now calculate Bx */ for (j = 0; j < size; j++) { - igraph_integer_t oldid = VECTOR(*idx)[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; @@ -152,23 +150,22 @@ static igraph_error_t igraph_i_community_leading_eigenvector( to[j] -= VECTOR(*tmp)[j] * from[j]; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_community_leading_eigenvector_weighted( - igraph_real_t *to, - const igraph_real_t *from, - int n, void *extra) { +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; - igraph_integer_t j, k, nlen, size = n; - igraph_vector_int_t *idx = data->idx; - igraph_vector_int_t *idx2 = data->idx2; + 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_int_t *mymembership = data->mymembership; - igraph_integer_t comm = data->comm; + 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; @@ -176,18 +173,18 @@ static igraph_error_t igraph_i_community_leading_eigenvector_weighted( /* Ax */ for (j = 0; j < size; j++) { - igraph_integer_t oldid = VECTOR(*idx)[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++) { - igraph_integer_t edge = VECTOR(*inc)[k]; + long int edge = (long int) VECTOR(*inc)[k]; igraph_real_t w = VECTOR(*weights)[edge]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, oldid); - igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; + long int nei = IGRAPH_OTHER(graph, edge, oldid); + long int neimemb = (long int) VECTOR(*mymembership)[nei]; if (neimemb == comm) { - to[j] += from[ VECTOR(*idx2)[nei] ] * w; + to[j] += from[ (long int) VECTOR(*idx2)[nei] ] * w; VECTOR(*tmp)[j] += w; } } @@ -196,7 +193,7 @@ static igraph_error_t igraph_i_community_leading_eigenvector_weighted( /* k^Tx/2m */ ktx = 0.0; ktx2 = 0.0; for (j = 0; j < size; j++) { - igraph_integer_t oldid = VECTOR(*idx)[j]; + long int oldid = (long int) VECTOR(*idx)[j]; igraph_real_t str = VECTOR(*strength)[oldid]; ktx += from[j] * str; ktx2 += str; @@ -206,7 +203,7 @@ static igraph_error_t igraph_i_community_leading_eigenvector_weighted( /* Bx */ for (j = 0; j < size; j++) { - igraph_integer_t oldid = VECTOR(*idx)[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; @@ -217,11 +214,22 @@ static igraph_error_t igraph_i_community_leading_eigenvector_weighted( to[j] -= VECTOR(*tmp)[j] * from[j]; } - return IGRAPH_SUCCESS; + 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(VECTOR(*ptr)[i]); + } + } } static void igraph_i_error_handler_none(const char *reason, const char *file, - int line, igraph_error_t igraph_errno) { + int line, int igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); @@ -269,8 +277,8 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * 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. Supply \c NULL here to use the - * defaults. \c n is always overwritten. \c ncv is set to at least 4. + * \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. @@ -282,9 +290,12 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * 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 list of vectors. Each eigenvector is stored in an - * \ref igraph_vector_t object. + * 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: @@ -310,12 +321,10 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * \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 \c IGRAPH_STOP, then the - * community finding algorithm stops. If it returns \c IGRAPH_SUCCESS, - * the algorithm continues normally. Any other return value is considered - * an igraph error code and will terminete the algorithm with the same - * error code. See the arguments passed to the callback at the documentation - * of \ref igraph_community_leading_eigenvector_callback_t. + * 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. @@ -328,46 +337,40 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * |E| the number of edges, steps the number of splits * performed. */ -igraph_error_t igraph_community_leading_eigenvector( - const igraph_t *graph, +int igraph_community_leading_eigenvector(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_int_t *merges, - igraph_vector_int_t *membership, + 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_list_t *eigenvectors, + igraph_vector_ptr_t *eigenvectors, igraph_vector_t *history, igraph_community_leading_eigenvector_callback_t *callback, void *callback_extra) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_dqueue_int_t tosplit; - igraph_vector_int_t idx, idx2; - igraph_vector_t mymerges; + 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; igraph_vector_t start_vec; - igraph_integer_t staken = 0; + long int staken = 0; igraph_adjlist_t adjlist; igraph_inclist_t inclist; - igraph_integer_t i, j, k, l; - igraph_integer_t communities; - igraph_vector_int_t vmembership, *mymembership = membership; + 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; + weights ? igraph_i_community_leading_eigenvector_weighted : + igraph_i_community_leading_eigenvector; igraph_real_t sumweights = 0.0; - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph too large for ARPACK", IGRAPH_EOVERFLOW); - } - if (weights && no_of_edges != igraph_vector_size(weights)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } @@ -378,12 +381,12 @@ igraph_error_t igraph_community_leading_eigenvector( } if (start && membership && - igraph_vector_int_size(membership) != no_of_nodes) { + igraph_vector_size(membership) != no_of_nodes) { IGRAPH_ERROR("Wrong length for vector of predefined memberships", IGRAPH_EINVAL); } - if (start && membership && igraph_vector_int_max(membership) >= no_of_nodes) { + if (start && membership && igraph_vector_max(membership) >= no_of_nodes) { IGRAPH_WARNING("Too many communities in membership start vector"); } @@ -392,22 +395,23 @@ igraph_error_t igraph_community_leading_eigenvector( } if (steps < 0 || steps > no_of_nodes - 1) { - steps = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + steps = (igraph_integer_t) no_of_nodes - 1; } if (!membership) { mymembership = &vmembership; - IGRAPH_VECTOR_INT_INIT_FINALLY(mymembership, 0); + IGRAPH_VECTOR_INIT_FINALLY(mymembership, 0); } IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0); IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps * 2)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, 0); + IGRAPH_VECTOR_INIT_FINALLY(&idx, 0); if (eigenvalues) { igraph_vector_clear(eigenvalues); } if (eigenvectors) { - igraph_vector_list_clear(eigenvectors); + igraph_vector_ptr_clear(eigenvectors); + IGRAPH_FINALLY(igraph_i_levc_free, eigenvectors); } IGRAPH_STATUS("Starting leading eigenvector method.\n", 0); @@ -415,35 +419,35 @@ igraph_error_t igraph_community_leading_eigenvector( if (!start) { /* Calculate the weakly connected components in the graph and use them as * an initial split */ - IGRAPH_CHECK(igraph_connected_components(graph, mymembership, &idx, 0, IGRAPH_WEAK)); - communities = igraph_vector_int_size(&idx); - IGRAPH_STATUSF(("Starting from %" IGRAPH_PRId " component(s).\n", 0, communities)); + 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 = igraph_vector_int_max(mymembership) + 1; - IGRAPH_STATUSF(("Starting from given membership vector with %" IGRAPH_PRId - " communities.\n", 0, communities)); + 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_int_resize(&idx, communities)); - igraph_vector_int_null(&idx); + IGRAPH_CHECK(igraph_vector_resize(&idx, communities)); + igraph_vector_null(&idx); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t t = VECTOR(*mymembership)[i]; + int t = (int) VECTOR(*mymembership)[i]; VECTOR(idx)[t] += 1; } } - IGRAPH_DQUEUE_INT_INIT_FINALLY(&tosplit, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&tosplit, 100); for (i = 0; i < communities; i++) { if (VECTOR(idx)[i] > 2) { - IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, i)); + igraph_dqueue_push(&tosplit, i); } } for (i = 1; i < communities; i++) { @@ -454,10 +458,15 @@ igraph_error_t igraph_community_leading_eigenvector( IGRAPH_CHECK(igraph_vector_push_back(eigenvalues, IGRAPH_NAN)); } if (eigenvectors) { - /* There are no eigenvectors associated to these steps because the - * splits were given by the user (or by the components of the graph) - * so we push empty vectors */ - IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, NULL)); + 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)); @@ -467,9 +476,9 @@ igraph_error_t igraph_community_leading_eigenvector( staken = communities - 1; IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_of_nodes); - IGRAPH_CHECK(igraph_vector_int_resize(&idx, no_of_nodes)); - igraph_vector_int_null(&idx); - IGRAPH_VECTOR_INT_INIT_FINALLY(&idx2, 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); @@ -482,10 +491,6 @@ igraph_error_t igraph_community_leading_eigenvector( sumweights = igraph_vector_sum(weights); } - if (options == 0) { - options = igraph_arpack_options_get_default(); - } - options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->which[0] = 'L'; options->which[1] = 'A'; @@ -507,12 +512,12 @@ igraph_error_t igraph_community_leading_eigenvector( extra.no_of_edges = no_of_edges; extra.mymembership = mymembership; - while (!igraph_dqueue_int_empty(&tosplit) && staken < steps) { - igraph_integer_t comm = igraph_dqueue_int_pop_back(&tosplit); + while (!igraph_dqueue_empty(&tosplit) && staken < steps) { + long int comm = (long int) igraph_dqueue_pop_back(&tosplit); /* depth first search */ - igraph_integer_t size = 0; + long int size = 0; - IGRAPH_STATUSF(("Trying to split community %" IGRAPH_PRId "... ", 0, comm)); + IGRAPH_STATUSF(("Trying to split community %li... ", 0, comm)); IGRAPH_ALLOW_INTERRUPTION(); for (i = 0; i < no_of_nodes; i++) { @@ -554,9 +559,9 @@ igraph_error_t igraph_community_leading_eigenvector( IGRAPH_CHECK(igraph_vector_shuffle(&start_vec)); { - igraph_error_t retval; + int retval; igraph_error_handler_t *errh = - igraph_set_error_handler(igraph_i_error_handler_none); + igraph_set_error_handler(igraph_i_error_handler_none); retval = igraph_arpack_rssolve(arpcb1, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0); igraph_set_error_handler(errh); if (retval != IGRAPH_SUCCESS && retval != IGRAPH_ARPACK_MAXIT && retval != IGRAPH_ARPACK_NOSHIFT) { @@ -601,17 +606,11 @@ igraph_error_t igraph_community_leading_eigenvector( if (callback) { igraph_vector_t vv; - igraph_error_t ret; - + int ret; igraph_vector_view(&vv, storage.v, size); - IGRAPH_CHECK_CALLBACK( - callback( - mymembership, comm, storage.d[0], &vv, arpcb1, - &extra, callback_extra - ), &ret - ); - - if (ret == IGRAPH_STOP) { + ret = callback(mymembership, comm, storage.d[0], &vv, + arpcb1, &extra, callback_extra); + if (ret) { break; } } @@ -621,13 +620,18 @@ igraph_error_t igraph_community_leading_eigenvector( } if (eigenvectors) { - igraph_vector_t *v; - /* TODO: this would be faster if we had an igraph_vector_list_push_back_new_with_size_hint */ - IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, &v)); - IGRAPH_CHECK(igraph_vector_resize(v, size)); + 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) { @@ -682,7 +686,7 @@ igraph_error_t igraph_community_leading_eigenvector( /* Rewrite the mymembership vector */ for (j = 0; j < size; j++) { if (storage.v[j] < 0) { - igraph_integer_t oldid = VECTOR(idx)[j]; + long int oldid = (long int) VECTOR(idx)[j]; VECTOR(*mymembership)[oldid] = communities - 1; } } @@ -697,10 +701,10 @@ igraph_error_t igraph_community_leading_eigenvector( /* Store the resulting communities in the queue if needed */ if (l > 1) { - IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, communities - 1)); + IGRAPH_CHECK(igraph_dqueue_push(&tosplit, communities - 1)); } if (size - l > 1) { - IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, comm)); + IGRAPH_CHECK(igraph_dqueue_push(&tosplit, comm)); } } @@ -715,23 +719,23 @@ igraph_error_t igraph_community_leading_eigenvector( igraph_vector_destroy(&strength); IGRAPH_FINALLY_CLEAN(2); } - igraph_dqueue_int_destroy(&tosplit); + igraph_dqueue_destroy(&tosplit); igraph_vector_destroy(&tmp); - igraph_vector_int_destroy(&idx2); + igraph_vector_destroy(&idx2); IGRAPH_FINALLY_CLEAN(3); IGRAPH_STATUS("Done.\n", 0); /* reform the mymerges vector */ if (merges) { - igraph_vector_int_null(&idx); + igraph_vector_null(&idx); l = igraph_vector_size(&mymerges); k = communities; j = 0; - IGRAPH_CHECK(igraph_matrix_int_resize(merges, l / 2, 2)); + IGRAPH_CHECK(igraph_matrix_resize(merges, l / 2, 2)); for (i = l; i > 0; i -= 2) { - igraph_integer_t from = VECTOR(mymerges)[i - 1]; - igraph_integer_t to = VECTOR(mymerges)[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) { @@ -745,22 +749,25 @@ igraph_error_t igraph_community_leading_eigenvector( } } - igraph_vector_int_destroy(&idx); + 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)); + IGRAPH_CHECK(igraph_modularity(graph, mymembership, weights, + /* resolution */ 1, + /* only undirected */ 0, modularity)); } if (!membership) { - igraph_vector_int_destroy(mymembership); + igraph_vector_destroy(mymembership); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -785,38 +792,37 @@ igraph_error_t igraph_community_leading_eigenvector( * * Time complexity: O(|V|), the number of vertices. */ -igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, - igraph_integer_t steps, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize) { +int igraph_le_community_to_membership(const igraph_matrix_t *merges, + igraph_integer_t steps, + igraph_vector_t *membership, + igraph_vector_t *csize) { - igraph_integer_t no_of_nodes = igraph_vector_int_size(membership); - igraph_vector_int_t fake_memb; - igraph_integer_t components, i; + long int no_of_nodes = igraph_vector_size(membership); + igraph_vector_t fake_memb; + long int components, i; if (no_of_nodes > 0) { - components = igraph_vector_int_max(membership) + 1; + components = (long int) igraph_vector_max(membership) + 1; } else { components = 0; } if (components > no_of_nodes) { - IGRAPH_ERRORF("Invalid membership vector: number of components (%" IGRAPH_PRId ") must " - "not be greater than the number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, 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 (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Number of steps (%" IGRAPH_PRId ") must be smaller than number of components (%ld).", IGRAPH_EINVAL, steps, components); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&fake_memb, 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: %" IGRAPH_PRId ".", IGRAPH_EINVAL, VECTOR(*membership)[i]); + IGRAPH_ERRORF("Invalid membership vector, negative ID found: %g.", IGRAPH_EINVAL, VECTOR(*membership)[i]); } - VECTOR(fake_memb)[ VECTOR(*membership)[i] ] += 1; + VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ] += 1; } for (i = 0; i < components; i++) { if (VECTOR(fake_memb)[i] == 0) { @@ -827,24 +833,26 @@ igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merg } } - IGRAPH_CHECK(igraph_community_to_membership(merges, components, steps, &fake_memb, 0)); + 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_int_resize(csize, components - steps)); - igraph_vector_int_null(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)[ VECTOR(*membership)[i] ]; + VECTOR(*membership)[i] = VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ]; if (csize) { - VECTOR(*csize)[ VECTOR(*membership)[i] ] += 1; + VECTOR(*csize)[ (long int) VECTOR(*membership)[i] ] += 1; } } - igraph_vector_int_destroy(&fake_memb); + igraph_vector_destroy(&fake_memb); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/community/leiden.c b/src/vendor/cigraph/src/community/leiden.c index e9aa9ada241..229b6d9e7de 100644 --- a/src/vendor/cigraph/src/community/leiden.c +++ b/src/vendor/cigraph/src/community/leiden.c @@ -25,14 +25,13 @@ #include "igraph_community.h" #include "igraph_adjlist.h" -#include "igraph_constructors.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_vector_list.h" +#include "igraph_constructors.h" #include "core/interruption.h" @@ -40,7 +39,6 @@ * * This function considers each node and greedily moves it to a neighboring * community that maximizes the improvement in the quality of a partition. - * Only moves that strictly improve the quality are considered. * * 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 @@ -51,96 +49,101 @@ * and is updated in-place. * */ -static igraph_error_t igraph_i_community_leiden_fastmovenodes( +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_int_t *membership, - igraph_bool_t *changed) { + igraph_vector_t *membership) { - igraph_dqueue_int_t unstable_nodes; + 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 cluster_weights, edge_weights_per_cluster; - igraph_vector_int_t neighbor_clusters; - igraph_vector_int_t node_order; + igraph_vector_t node_order, cluster_weights, edge_weights_per_cluster, neighbor_clusters; igraph_vector_int_t nb_nodes_per_cluster; - igraph_stack_int_t empty_clusters; - igraph_integer_t i, j, c, nb_neigh_clusters; + 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_VECTOR_BOOL_INIT_FINALLY(&node_is_stable, n); + IGRAPH_CHECK(igraph_vector_bool_init(&node_is_stable, n)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &node_is_stable); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&unstable_nodes, n); + IGRAPH_CHECK(igraph_dqueue_init(&unstable_nodes, n)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &unstable_nodes); /* Shuffle nodes */ - IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, n)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); - IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + 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_CHECK(igraph_dqueue_int_push(&unstable_nodes, VECTOR(node_order)[i])); + igraph_dqueue_push(&unstable_nodes, (long int)VECTOR(node_order)[i]); } /* Initialize cluster weights and nb nodes */ - IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); - IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); + 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 = VECTOR(*membership)[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_STACK_INT_INIT_FINALLY(&empty_clusters, n); + 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_CHECK(igraph_stack_int_push(&empty_clusters, c)); + igraph_stack_push(&empty_clusters, c); } /* Initialize vectors to be used in calculating differences */ - IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); + IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster); /* Initialize neighboring cluster */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); + 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_int_empty(&unstable_nodes)) { - igraph_integer_t v = igraph_dqueue_int_pop(&unstable_nodes); - igraph_integer_t best_cluster, current_cluster = VECTOR(*membership)[v]; - igraph_integer_t degree; + 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_int_push(&empty_clusters, current_cluster)); + IGRAPH_CHECK(igraph_stack_push(&empty_clusters, current_cluster)); } /* Find out neighboring clusters */ - c = igraph_stack_int_top(&empty_clusters); + c = (long int) igraph_stack_top(&empty_clusters); VECTOR(neighbor_clusters)[0] = c; - VECTOR(neighbor_cluster_added)[c] = true; + 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++) { - igraph_integer_t e = VECTOR(*edges)[i]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + 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] = true; + VECTOR(neighbor_cluster_added)[c] = 1; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -153,38 +156,34 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( 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; - /* Only consider strictly improving moves. - * Note that this is important in considering convergence. - */ if (diff > max_diff) { best_cluster = c; max_diff = diff; } VECTOR(edge_weights_per_cluster)[c] = 0.0; - VECTOR(neighbor_cluster_added)[c] = false; + 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_int_top(&empty_clusters)) { - igraph_stack_int_pop(&empty_clusters); + if (best_cluster == igraph_stack_top(&empty_clusters)) { + igraph_stack_pop(&empty_clusters); } /* Mark node as stable */ - VECTOR(node_is_stable)[v] = true; + 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) { - *changed = true; VECTOR(*membership)[v] = best_cluster; for (i = 0; i < degree; i++) { - igraph_integer_t e = VECTOR(*edges)[i]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + 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_int_push(&unstable_nodes, u)); - VECTOR(node_is_stable)[u] = false; + IGRAPH_CHECK(igraph_dqueue_push(&unstable_nodes, u)); + VECTOR(node_is_stable)[u] = 0; } } } @@ -198,15 +197,16 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters)); - igraph_vector_int_destroy(&neighbor_clusters); + igraph_vector_destroy(&neighbor_clusters); igraph_vector_bool_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weights_per_cluster); - igraph_stack_int_destroy(&empty_clusters); + igraph_stack_destroy(&empty_clusters); igraph_vector_int_destroy(&nb_nodes_per_cluster); igraph_vector_destroy(&cluster_weights); - igraph_vector_int_destroy(&node_order); - igraph_dqueue_int_destroy(&unstable_nodes); + igraph_vector_destroy(&node_order); + igraph_dqueue_destroy(&unstable_nodes); igraph_vector_bool_destroy(&node_is_stable); + IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; @@ -221,37 +221,39 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( * resulting \c nb_refined_clusters, then nodes in \c node_subset are numbered * C, C + 1, ..., C' - 1. */ -static igraph_error_t igraph_i_community_leiden_clean_refined_membership( - const igraph_vector_int_t* node_subset, - igraph_vector_int_t *refined_membership, +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) { - igraph_integer_t i, n = igraph_vector_int_size(node_subset); - igraph_vector_int_t new_cluster; + long int i, n = igraph_vector_size(node_subset); + igraph_vector_t new_cluster; - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_cluster, n); + 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++) { - igraph_integer_t v = VECTOR(*node_subset)[i]; - igraph_integer_t c = VECTOR(*refined_membership)[v]; + 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] = *nb_refined_clusters; + VECTOR(new_cluster)[c] = (igraph_real_t)(*nb_refined_clusters); *nb_refined_clusters += 1; } } /* Assign new cluster */ for (i = 0; i < n; i++) { - igraph_integer_t v = VECTOR(*node_subset)[i]; - igraph_integer_t c = VECTOR(*refined_membership)[v]; + 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_int_destroy(&new_cluster); + igraph_vector_destroy(&new_cluster); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -287,38 +289,40 @@ static igraph_error_t igraph_i_community_leiden_clean_refined_membership( * igraph_i_community_leiden_clean_refined_membership for more information about * this aspect. */ -static igraph_error_t igraph_i_community_leiden_mergenodes( +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_int_t *node_subset, - const igraph_vector_int_t *membership, + 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_int_t *refined_membership) { - igraph_vector_int_t node_order; + 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_int_size(node_subset); - igraph_vector_t cluster_weights, cum_trans_diff, edge_weights_per_cluster, external_edge_weight_per_cluster_in_subset; - igraph_vector_int_t neighbor_clusters; + 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; - igraph_integer_t i, j, degree, nb_neigh_clusters; + long int i, j, degree, nb_neigh_clusters; /* Initialize cluster weights */ - IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); + IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights); /* Initialize number of nodes per cluster */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); + 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_VECTOR_INIT_FINALLY(&external_edge_weight_per_cluster_in_subset, n); + 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++) { - igraph_integer_t v = VECTOR(*node_subset)[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; @@ -328,8 +332,8 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( edges = igraph_inclist_get(edges_per_node, v); degree = igraph_vector_int_size(edges); for (j = 0; j < degree; j++) { - igraph_integer_t e = VECTOR(*edges)[j]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + 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]; } @@ -337,28 +341,33 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( } /* Shuffle nodes */ - IGRAPH_CHECK(igraph_vector_int_init_copy(&node_order, node_subset)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); - IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); + 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_VECTOR_BOOL_INIT_FINALLY(&non_singleton_cluster, n); + 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_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); + IGRAPH_CHECK(igraph_vector_init(&edge_weights_per_cluster, n)); + IGRAPH_FINALLY(igraph_vector_destroy, &edge_weights_per_cluster); /* Initialize neighboring cluster */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); + 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_VECTOR_INIT_FINALLY(&cum_trans_diff, n); + 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++) { - igraph_integer_t v = VECTOR(node_order)[i]; - igraph_integer_t chosen_cluster, best_cluster, current_cluster = VECTOR(*refined_membership)[v]; + 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] >= @@ -374,15 +383,15 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( /* Also add current cluster to ensure it can be chosen. */ VECTOR(neighbor_clusters)[0] = current_cluster; - VECTOR(neighbor_cluster_added)[current_cluster] = true; + VECTOR(neighbor_cluster_added)[current_cluster] = 1; nb_neigh_clusters = 1; for (j = 0; j < degree; j++) { - igraph_integer_t e = VECTOR(*edges)[j]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + 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) { - igraph_integer_t c = VECTOR(*refined_membership)[u]; + long int c = VECTOR(*refined_membership)[u]; if (!VECTOR(neighbor_cluster_added)[c]) { - VECTOR(neighbor_cluster_added)[c] = true; + VECTOR(neighbor_cluster_added)[c] = 1; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -394,7 +403,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( max_diff = 0.0; total_cum_trans_diff = 0.0; for (j = 0; j < nb_neigh_clusters; j++) { - igraph_integer_t c = VECTOR(neighbor_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; @@ -412,7 +421,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( VECTOR(cum_trans_diff)[j] = total_cum_trans_diff; VECTOR(edge_weights_per_cluster)[c] = 0.0; - VECTOR(neighbor_cluster_added)[c] = false; + VECTOR(neighbor_cluster_added)[c] = 0; } /* Determine the neighboring cluster to which the currently selected node @@ -420,7 +429,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( */ if (total_cum_trans_diff < IGRAPH_INFINITY) { igraph_real_t r = RNG_UNIF(0, total_cum_trans_diff); - igraph_integer_t chosen_idx; + 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 { @@ -432,8 +441,8 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( VECTOR(nb_nodes_per_cluster)[chosen_cluster]++; for (j = 0; j < degree; j++) { - igraph_integer_t e = VECTOR(*edges)[j]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + 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]; @@ -447,7 +456,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( if (chosen_cluster != current_cluster) { VECTOR(*refined_membership)[v] = chosen_cluster; - VECTOR(non_singleton_cluster)[chosen_cluster] = true; + VECTOR(non_singleton_cluster)[chosen_cluster] = 1; } } /* end if singleton and may be merged */ } @@ -457,14 +466,15 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( IGRAPH_CHECK(igraph_i_community_leiden_clean_refined_membership(node_subset, refined_membership, nb_refined_clusters)); igraph_vector_destroy(&cum_trans_diff); - igraph_vector_int_destroy(&neighbor_clusters); + 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_int_destroy(&node_order); + 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; @@ -472,21 +482,33 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( /* Create clusters out of a membership vector. * - * It is assumed that the incoming list of integer vectors is already sized - * appropriately (i.e. it has at least as many items as the number of clusters - * in the membership vector), and that each item in the list of integer vectors - * is empty. + * 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 igraph_error_t igraph_i_community_get_clusters(const igraph_vector_int_t *membership, igraph_vector_int_list_t *clusters) { - igraph_integer_t n = igraph_vector_int_size(membership); - igraph_vector_int_t *cluster; - - for (igraph_integer_t i = 0; i < n; i++) { +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 */ - cluster = igraph_vector_int_list_get_ptr(clusters, VECTOR(*membership)[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_int_push_back(cluster, i)); + IGRAPH_CHECK(igraph_vector_push_back(cluster, i)); } return IGRAPH_SUCCESS; @@ -506,59 +528,65 @@ static igraph_error_t igraph_i_community_get_clusters(const igraph_vector_int_t * aggregated_membership are all expected to be initialized. * */ -static igraph_error_t igraph_i_community_leiden_aggregate( +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_int_t *membership, const igraph_vector_int_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_int_t *aggregated_membership) { - igraph_vector_int_t aggregated_edges; - igraph_vector_t edge_weight_to_cluster; - igraph_vector_int_list_t refined_clusters; + 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_int_t neighbor_clusters; + igraph_vector_t neighbor_clusters; igraph_vector_bool_t neighbor_cluster_added; - igraph_integer_t i, j, c, degree, nb_neigh_clusters; + long int i, j, c, degree, nb_neigh_clusters; /* Get refined clusters */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&refined_clusters, nb_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_VECTOR_INT_INIT_FINALLY(&aggregated_edges, 0); + 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 */ + /* 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_int_resize(aggregated_membership, nb_refined_clusters)); + IGRAPH_CHECK(igraph_vector_resize(aggregated_membership, nb_refined_clusters)); - IGRAPH_VECTOR_INIT_FINALLY(&edge_weight_to_cluster, 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_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, nb_refined_clusters); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, nb_refined_clusters); + 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_int_t* refined_cluster = igraph_vector_int_list_get_ptr(&refined_clusters, c); - igraph_integer_t n_c = igraph_vector_int_size(refined_cluster); - igraph_integer_t v = -1; + 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 = VECTOR(*refined_cluster)[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++) { - igraph_integer_t e = VECTOR(*incident_edges)[j]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); - igraph_integer_t c2 = VECTOR(*refined_membership)[u]; + 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] = true; + VECTOR(neighbor_cluster_added)[c2] = 1; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c2; } VECTOR(edge_weight_to_cluster)[c2] += VECTOR(*edge_weights)[e]; @@ -570,34 +598,36 @@ static igraph_error_t igraph_i_community_leiden_aggregate( /* Add actual edges from this cluster to the other clusters */ for (i = 0; i < nb_neigh_clusters; i++) { - igraph_integer_t c2 = VECTOR(neighbor_clusters)[i]; + long int c2 = VECTOR(neighbor_clusters)[i]; /* Add edge */ - IGRAPH_CHECK(igraph_vector_int_push_back(&aggregated_edges, c)); - IGRAPH_CHECK(igraph_vector_int_push_back(&aggregated_edges, c2)); + 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] = false; + VECTOR(neighbor_cluster_added)[c2] = 0; } VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v]; } - igraph_vector_int_destroy(&neighbor_clusters); + igraph_vector_destroy(&neighbor_clusters); igraph_vector_bool_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weight_to_cluster); - igraph_vector_int_list_destroy(&refined_clusters); + 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_int_destroy(&aggregated_edges); + igraph_vector_destroy(&aggregated_edges); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -626,14 +656,14 @@ static igraph_error_t igraph_i_community_leiden_aggregate( * weights inside cluster c. This is how the quality is calculated in practice. * */ -static igraph_error_t igraph_i_community_leiden_quality( +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_int_t *membership, const igraph_integer_t nb_comms, const igraph_real_t resolution_parameter, + 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; - igraph_integer_t i, c, n = igraph_vcount(graph); + long int i, c, n = igraph_vcount(graph);; *quality = 0.0; @@ -642,11 +672,11 @@ static igraph_error_t igraph_i_community_leiden_quality( IGRAPH_FINALLY(igraph_eit_destroy, &eit); while (!IGRAPH_EIT_END(eit)) { - igraph_integer_t e = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, e), to = IGRAPH_TO(graph, e); + 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)[from] == VECTOR(*membership)[to]) { + if (VECTOR(*membership)[(long int) from] == VECTOR(*membership)[(long int) to]) { *quality += 2 * VECTOR(*edge_weights)[e]; } IGRAPH_EIT_NEXT(eit); @@ -655,7 +685,8 @@ static igraph_error_t igraph_i_community_leiden_quality( IGRAPH_FINALLY_CLEAN(1); /* Initialize cluster weights and nb nodes */ - IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); + 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]; @@ -681,56 +712,64 @@ static igraph_error_t igraph_i_community_leiden_quality( * refined partition, using the non-refined partition to create an initial * partition for the aggregate network. */ -static igraph_error_t igraph_i_community_leiden( +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_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality, - igraph_bool_t *changed) { + igraph_vector_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { igraph_integer_t nb_refined_clusters; - igraph_integer_t i, c, n = igraph_vcount(graph); + long int i, c, n = igraph_vcount(graph); igraph_t aggregated_graph, *i_graph; - igraph_vector_t aggregated_edge_weights, aggregated_node_weights; - igraph_vector_int_t aggregated_membership; - igraph_vector_t *i_edge_weights, *i_node_weights; - igraph_vector_int_t *i_membership; - igraph_vector_t tmp_edge_weights, tmp_node_weights; - igraph_vector_int_t tmp_membership; - igraph_vector_int_t refined_membership; + 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_int_list_t clusters; + 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_VECTOR_INIT_FINALLY(&tmp_edge_weights, 0); - IGRAPH_VECTOR_INIT_FINALLY(&tmp_node_weights, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp_membership, 0); + 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_VECTOR_INT_LIST_INIT_FINALLY(&clusters, n); + 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_range(&aggregate_node, 0, n)); + 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_VECTOR_INT_INIT_FINALLY(&refined_membership, 0); + 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_VECTOR_INIT_FINALLY(&aggregated_edge_weights, 0); + IGRAPH_CHECK(igraph_vector_init(&aggregated_edge_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_edge_weights); /* Initialize aggregated node weights */ - IGRAPH_VECTOR_INIT_FINALLY(&aggregated_node_weights, 0); + IGRAPH_CHECK(igraph_vector_init(&aggregated_node_weights, 0)); + IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_node_weights); /* Initialize aggregated membership */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&aggregated_membership, 0); + 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; @@ -743,11 +782,9 @@ static igraph_error_t igraph_i_community_leiden( IGRAPH_CHECK(igraph_reindex_membership(i_membership, NULL, nb_clusters)); if (*nb_clusters > n) { - IGRAPH_ERROR("Too many communities in membership vector.", IGRAPH_EINVAL); + IGRAPH_ERROR("Too many communities in membership vector", IGRAPH_EINVAL); } - /* We start out with no changes, whenever a node is moved, this will be set to true. */ - *changed = false; do { /* Get incidence list for fast iteration */ @@ -760,8 +797,7 @@ static igraph_error_t igraph_i_community_leiden( i_edge_weights, i_node_weights, resolution_parameter, nb_clusters, - i_membership, - changed)); + i_membership)); /* We only continue clustering if not all clusters are represented by a * single node yet @@ -772,7 +808,7 @@ static igraph_error_t igraph_i_community_leiden( /* Set original membership */ if (level > 0) { for (i = 0; i < n; i++) { - igraph_integer_t v_aggregate = VECTOR(aggregate_node)[i]; + long int v_aggregate = VECTOR(aggregate_node)[i]; VECTOR(*membership)[i] = VECTOR(*i_membership)[v_aggregate]; } } @@ -781,12 +817,12 @@ static igraph_error_t igraph_i_community_leiden( IGRAPH_CHECK(igraph_i_community_get_clusters(i_membership, &clusters)); /* Ensure refined membership is correct size */ - IGRAPH_CHECK(igraph_vector_int_resize(&refined_membership, igraph_vcount(i_graph))); + 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_int_t* cluster = igraph_vector_int_list_get_ptr(&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, @@ -794,13 +830,13 @@ static igraph_error_t igraph_i_community_leiden( resolution_parameter, beta, &nb_refined_clusters, &refined_membership)); /* Empty cluster */ - igraph_vector_int_clear(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_CHECK(igraph_vector_int_update(&refined_membership, i_membership)); + igraph_vector_update(&refined_membership, i_membership); nb_refined_clusters = *nb_clusters; } @@ -809,7 +845,7 @@ static igraph_error_t igraph_i_community_leiden( /* Current aggregate node */ igraph_integer_t v_aggregate = VECTOR(aggregate_node)[i]; /* New aggregate node */ - VECTOR(aggregate_node)[i] = VECTOR(refined_membership)[v_aggregate]; + VECTOR(aggregate_node)[i] = (igraph_integer_t)VECTOR(refined_membership)[v_aggregate]; } IGRAPH_CHECK(igraph_i_community_leiden_aggregate( @@ -832,7 +868,7 @@ static igraph_error_t igraph_i_community_leiden( /* 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_int_update(i_membership, &tmp_membership)); + IGRAPH_CHECK(igraph_vector_update(i_membership, &tmp_membership)); level += 1; } @@ -843,17 +879,17 @@ static igraph_error_t igraph_i_community_leiden( } while (continue_clustering); /* Free aggregated graph and associated vectors */ - igraph_vector_int_destroy(&aggregated_membership); + 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_int_destroy(&refined_membership); + igraph_vector_destroy(&refined_membership); igraph_vector_int_destroy(&aggregate_node); - igraph_vector_int_list_destroy(&clusters); - igraph_vector_int_destroy(&tmp_membership); + 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); @@ -883,16 +919,15 @@ static igraph_error_t igraph_i_community_leiden( * 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 + * 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. Only - * moves that strictly improve the quality function are made. 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). + * 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 @@ -900,12 +935,9 @@ static igraph_error_t igraph_i_community_leiden( * 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. Note that even if a single iteration did not result in any change, - * it is still possible that a subsequent iteration might find some - * improvement. Each iteration explores different subsets of nodes to consider - * for moving from one cluster to another. Finally, asymptotically, all subsets - * of all clusters are guaranteed to be locally optimally assigned. For more - * details, please see Traag, Waltman & van Eck (2019). + * 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 @@ -936,14 +968,11 @@ static igraph_error_t igraph_i_community_leiden( * \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 n_iterations Iterate the core Leiden algorithm for the indicated number - * of times. If this is a negative number, it will continue iterating until - * an iteration did not change the clustering. * \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 \ref igraph_vector_int_init_range(). + * using \c igraph_vector_init_seq. * \param nb_clusters The number of clusters contained in \c membership. * If \c NULL, the number of clusters will not be returned. * \param quality The quality of the partition, in terms of the objective @@ -955,11 +984,10 @@ static igraph_error_t igraph_i_community_leiden( * * \example examples/simple/igraph_community_leiden.c */ -igraph_error_t igraph_community_leiden(const igraph_t *graph, +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, - const igraph_integer_t n_iterations, - igraph_vector_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { + 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 i_nb_clusters; igraph_integer_t n = igraph_vcount(graph); @@ -970,29 +998,35 @@ igraph_error_t igraph_community_leiden(const igraph_t *graph, if (start) { if (!membership) { - IGRAPH_ERROR("Cannot start optimization if membership is missing.", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot start optimization if membership is missing", IGRAPH_EINVAL); } - if (igraph_vector_int_size(membership) != n) { - IGRAPH_ERROR("Initial membership length does not equal the number of vertices.", 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); + "even when not starting optimization from it", IGRAPH_EINVAL); - IGRAPH_CHECK(igraph_vector_int_range(membership, 0, n)); + 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); + 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); - IGRAPH_CHECK_OOM(i_edge_weights, "Leiden algorithm failed, could not allocate memory for edge weights."); + 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); @@ -1004,7 +1038,9 @@ igraph_error_t igraph_community_leiden(const igraph_t *graph, /* Check edge weights to possibly use default */ if (!node_weights) { i_node_weights = IGRAPH_CALLOC(1, igraph_vector_t); - IGRAPH_CHECK_OOM(i_node_weights, "Leiden algorithm failed, could not allocate memory for node weights."); + 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); @@ -1013,21 +1049,10 @@ igraph_error_t igraph_community_leiden(const igraph_t *graph, i_node_weights = (igraph_vector_t*)node_weights; } - /* Perform actual Leiden algorithm iteratively. We either - * perform a fixed number of iterations, or we perform - * iterations until the quality remains unchanged. Even if - * a single iteration did not change anything, a subsequent - * iteration may still find some improvement. This is because - * each iteration explores different subsets of nodes. - */ - igraph_bool_t changed = false; - for (igraph_integer_t itr = 0; - n_iterations >= 0 ? itr < n_iterations : !changed; - itr++) { - IGRAPH_CHECK(igraph_i_community_leiden(graph, i_edge_weights, i_node_weights, - resolution_parameter, beta, - membership, nb_clusters, quality, &changed)); - } + /* 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); diff --git a/src/vendor/cigraph/src/community/louvain.c b/src/vendor/cigraph/src/community/louvain.c index 5a3d823340a..5ffa184bad1 100644 --- a/src/vendor/cigraph/src/community/louvain.c +++ b/src/vendor/cigraph/src/community/louvain.c @@ -24,10 +24,10 @@ #include "igraph_community.h" #include "igraph_constructors.h" -#include "igraph_conversion.h" #include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_qsort.h" +#include "igraph_random.h" #include "core/interruption.h" @@ -35,26 +35,28 @@ 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_real_t weight_all; /* Sum of edge weights starting/ending + in the community */ } igraph_i_multilevel_community; /* Global community list structure */ typedef struct { - igraph_integer_t communities_no, vertices_no; /* Number of communities, number of vertices */ + 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_int_t *membership; /* Community IDs */ - igraph_vector_t *weights; /* Graph edge weights */ + 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.0; + 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 (igraph_integer_t i = 0; i < communities->vertices_no; i++) { + 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; } @@ -64,59 +66,52 @@ static igraph_real_t igraph_i_multilevel_community_modularity( } typedef struct { - igraph_integer_t from; - igraph_integer_t to; - igraph_integer_t id; + 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) { - igraph_integer_t diff; - - diff = ((igraph_i_multilevel_link*)a)->from - ((igraph_i_multilevel_link*)b)->from; - - if (diff < 0) { - return -1; - } else if (diff > 0) { - return 1; + long int r = (((igraph_i_multilevel_link*)a)->from - + ((igraph_i_multilevel_link*)b)->from); + if (r != 0) { + return (int) r; } - diff = ((igraph_i_multilevel_link*)a)->to - ((igraph_i_multilevel_link*)b)->to; - - if (diff < 0) { - return -1; - } else if (diff > 0) { - return 1; - } else { - return 0; - } + return (int) (((igraph_i_multilevel_link*)a)->to - + ((igraph_i_multilevel_link*)b)->to); } -/* removes multiple edges and returns new edge IDs for each edge in |E|log|E| */ -static igraph_error_t igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_int_t *eids) { - igraph_integer_t ecount = igraph_ecount(graph); - igraph_integer_t l = -1, last_from = -1, last_to = -1; +/* 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_int_t edges; + 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_int_resize(eids, ecount)); + IGRAPH_CHECK(igraph_vector_resize(eids, ecount)); links = IGRAPH_CALLOC(ecount, igraph_i_multilevel_link); - IGRAPH_CHECK_OOM(links, "Multi-level community structure detection failed."); + if (links == 0) { + IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, links); - for (igraph_integer_t i = 0; i < ecount; i++) { - links[i].from = IGRAPH_FROM(graph, i); - links[i].to = IGRAPH_TO(graph, i); + 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(links, (size_t) ecount, sizeof(igraph_i_multilevel_link), + igraph_qsort((void*)links, (size_t) ecount, sizeof(igraph_i_multilevel_link), igraph_i_multilevel_link_cmp); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - for (igraph_integer_t i = 0; i < ecount; i++) { + 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; @@ -125,8 +120,8 @@ static igraph_error_t igraph_i_multilevel_simplify_multiple(igraph_t *graph, igr last_from = links[i].from; last_to = links[i].to; - igraph_vector_int_push_back(&edges, last_from); - igraph_vector_int_push_back(&edges, last_to); + igraph_vector_push_back(&edges, last_from); + igraph_vector_push_back(&edges, last_to); l++; @@ -139,23 +134,20 @@ static igraph_error_t igraph_i_multilevel_simplify_multiple(igraph_t *graph, igr igraph_destroy(graph); IGRAPH_CHECK(igraph_create(graph, &edges, igraph_vcount(graph), directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } typedef struct { - igraph_integer_t community; + 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) { - igraph_integer_t diff = ( - ((igraph_i_multilevel_community_link*)a)->community - - ((igraph_i_multilevel_community_link*)b)->community - ); - return diff < 0 ? -1 : diff > 0 ? 1 : 0; + return (int) (((igraph_i_multilevel_community_link*)a)->community - + ((igraph_i_multilevel_community_link*)b)->community); } /** @@ -171,34 +163,36 @@ static int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) * communities incident on this vertex and the total weight of edges * pointing to these communities */ -static igraph_error_t igraph_i_multilevel_community_links( +static int igraph_i_multilevel_community_links( const igraph_t *graph, const igraph_i_multilevel_community_list *communities, - igraph_integer_t vertex, igraph_vector_int_t *edges, + 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_int_t *links_community, igraph_vector_t *links_weight) { + igraph_vector_t *links_community, igraph_vector_t *links_weight) { - igraph_integer_t n, last = -1, c = -1; + long int i, n, last = -1, c = -1; igraph_real_t weight = 1; - igraph_integer_t to, to_community; - igraph_integer_t community = VECTOR(*(communities->membership))[vertex]; + 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_int_clear(links_community); + igraph_vector_clear(links_community); igraph_vector_clear(links_weight); /* Get the list of incident edges */ - IGRAPH_CHECK(igraph_incident(graph, edges, vertex, IGRAPH_ALL)); + igraph_incident(graph, edges, vertex, IGRAPH_ALL); - n = igraph_vector_int_size(edges); + n = igraph_vector_size(edges); links = IGRAPH_CALLOC(n, igraph_i_multilevel_community_link); - IGRAPH_CHECK_OOM(links, "Multi-level community structure detection failed."); + if (links == 0) { + IGRAPH_ERROR("multi-level community structure detection failed", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, links); - for (igraph_integer_t i = 0; i < n; i++) { - igraph_integer_t eidx = VECTOR(*edges)[i]; + 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); @@ -212,7 +206,7 @@ static igraph_error_t igraph_i_multilevel_community_links( continue; } - to_community = VECTOR(*(communities->membership))[to]; + to_community = (long int)VECTOR(*(communities->membership))[to]; if (community == to_community) { *weight_inside += weight; } @@ -226,11 +220,11 @@ static igraph_error_t igraph_i_multilevel_community_links( /* 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 (igraph_integer_t i = 0; i < n; i++) { + for (i = 0; i < n; i++) { to_community = links[i].community; if (to_community != last) { - IGRAPH_CHECK(igraph_vector_int_push_back(links_community, to_community)); - IGRAPH_CHECK(igraph_vector_push_back(links_weight, links[i].weight)); + igraph_vector_push_back(links_community, to_community); + igraph_vector_push_back(links_weight, links[i].weight); last = to_community; c++; } else { @@ -241,17 +235,17 @@ static igraph_error_t igraph_i_multilevel_community_links( igraph_free(links); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + 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) { + 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[community].weight_all * weight_all / communities->weight_sum; + resolution * communities->item[(long int)community].weight_all * weight_all / communities->weight_sum; } /* Shrinks communities into single vertices, keeping all the edges. @@ -260,43 +254,58 @@ static igraph_real_t igraph_i_multilevel_community_modularity_gain( * 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 igraph_error_t igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_int_t *membership) { - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); +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); - IGRAPH_ASSERT(igraph_vector_int_size(membership) == no_of_nodes); + long int i; + igraph_eit_t eit; if (no_of_nodes == 0) { - return IGRAPH_SUCCESS; + return 0; + } + + if (igraph_vector_size(membership) < no_of_nodes) { + IGRAPH_ERROR("cannot shrink graph, membership vector too short", + IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); - IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL)); /* Create the new edgelist */ - IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ false)); - for (igraph_integer_t i=0; i < 2*no_of_edges; i++) { - VECTOR(edges)[i] = VECTOR(*membership)[ VECTOR(edges)[i] ]; + 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 = igraph_vector_int_max(membership) + 1; - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup communities * \function igraph_i_community_multilevel_step - * \brief Performs a single step of the multi-level modularity optimization method. + * \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, @@ -320,48 +329,66 @@ static igraph_error_t igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_ * * Time complexity: in average near linear on sparse graphs. */ -static igraph_error_t igraph_i_community_multilevel_step( +static int igraph_i_community_multilevel_step( igraph_t *graph, igraph_vector_t *weights, - igraph_vector_int_t *membership, + igraph_vector_t *membership, igraph_real_t *modularity, const igraph_real_t resolution) { - igraph_integer_t vcount = igraph_vcount(graph); - igraph_integer_t ecount = igraph_ecount(graph); + long int i, j; + long int vcount = igraph_vcount(graph); + long int ecount = igraph_ecount(graph); igraph_real_t q, pass_q; - /* int pass; // used only for debugging */ - igraph_bool_t changed; - igraph_vector_int_t links_community; + /* int pass; */ + igraph_bool_t changed = 0; + igraph_vector_t links_community; igraph_vector_t links_weight; - igraph_vector_int_t edges; - igraph_vector_int_t temp_membership; + igraph_vector_t edges; + igraph_vector_t temp_membership; igraph_i_multilevel_community_list communities; - igraph_vector_int_t node_order; + igraph_vector_t node_order; - IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, vcount)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); - igraph_vector_int_shuffle(&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_INT_INIT_FINALLY(&links_community, 0); + IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0); IGRAPH_VECTOR_INIT_FINALLY(&links_weight, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&temp_membership, vcount); - IGRAPH_CHECK(igraph_vector_int_resize(membership, vcount)); + 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.0 * igraph_vector_sum(weights); + communities.weight_sum = 2 * igraph_vector_sum(weights); communities.membership = membership; communities.item = IGRAPH_CALLOC(vcount, igraph_i_multilevel_community); - IGRAPH_CHECK_OOM(communities.item, "Multi-level community structure detection failed."); + 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 (igraph_integer_t i = 0; i < vcount; i++) { + for (i = 0; i < vcount; i++) { VECTOR(*communities.membership)[i] = i; communities.item[i].size = 1; communities.item[i].weight_inside = 0; @@ -369,15 +396,16 @@ static igraph_error_t igraph_i_community_multilevel_step( } /* Some more initialization :) */ - for (igraph_integer_t i = 0; i < ecount; i++) { - igraph_integer_t ffrom = IGRAPH_FROM(graph, i), fto = IGRAPH_TO(graph, i); + 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[ffrom].weight_all += weight; - communities.item[fto].weight_all += weight; + communities.item[(long int) ffrom].weight_all += weight; + communities.item[(long int) fto].weight_all += weight; if (ffrom == fto) { - communities.item[ffrom].weight_inside += 2 * weight; + communities.item[(long int) ffrom].weight_inside += 2 * weight; } } @@ -385,36 +413,36 @@ static igraph_error_t igraph_i_community_multilevel_step( /* pass = 1; */ do { /* Pass begin */ - igraph_integer_t temp_communities_no = communities.communities_no; + long int temp_communities_no = communities.communities_no; pass_q = q; - changed = false; + changed = 0; /* Save the current membership, it will be restored in case of worse result */ - IGRAPH_CHECK(igraph_vector_int_update(&temp_membership, communities.membership)); + IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership)); - for (igraph_integer_t i = 0; i < vcount; i++) { + 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; - igraph_integer_t old_id, new_id, n, ni; + long int old_id, new_id, n, ni; ni = VECTOR(node_order)[i]; igraph_i_multilevel_community_links(graph, &communities, - ni, &edges, + (igraph_integer_t) ni, &edges, &weight_all, &weight_inside, &weight_loop, &links_community, &links_weight); - old_id = VECTOR(*(communities.membership))[ni]; + old_id = (long int)VECTOR(*(communities.membership))[ni]; new_id = old_id; /* Update old community */ - igraph_vector_int_set(communities.membership, ni, -1); + igraph_vector_set(communities.membership, ni, -1); communities.item[old_id].size--; if (communities.item[old_id].size == 0) { communities.communities_no--; @@ -427,14 +455,16 @@ static igraph_error_t igraph_i_community_multilevel_step( /* Find new community to join with the best modification gain */ max_q_gain = 0; max_weight = weight_inside; - n = igraph_vector_int_size(&links_community); + n = igraph_vector_size(&links_community); - for (igraph_integer_t j = 0; j < n; j++) { - igraph_integer_t c = VECTOR(links_community)[j]; + 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, c, ni, + 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) { @@ -447,7 +477,7 @@ static igraph_error_t igraph_i_community_multilevel_step( /* 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_int_set(communities.membership, ni, new_id); + igraph_vector_set(communities.membership, ni, new_id); if (communities.item[new_id].size == 0) { communities.communities_no++; } @@ -456,7 +486,7 @@ static igraph_error_t igraph_i_community_multilevel_step( communities.item[new_id].weight_inside += 2 * max_weight + weight_loop; if (new_id != old_id) { - changed = true; + changed++; } } @@ -468,7 +498,7 @@ static igraph_error_t igraph_i_community_multilevel_step( /* pass++; */ } else { /* No changes or the modularity became worse, restore last membership */ - IGRAPH_CHECK(igraph_vector_int_update(communities.membership, &temp_membership)); + IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership)); communities.communities_no = temp_communities_no; break; } @@ -483,15 +513,15 @@ static igraph_error_t igraph_i_community_multilevel_step( /* debug("Result Communities: %ld Modularity: %lf\n", communities.communities_no, (double) q); */ - IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); + 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_int_update(&temp_membership, membership)); + IGRAPH_CHECK(igraph_vector_update(&temp_membership, membership)); IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); - igraph_vector_int_destroy(&temp_membership); + igraph_vector_destroy(&temp_membership); IGRAPH_FINALLY_CLEAN(1); /* Update edge weights after shrinking and simplification */ @@ -503,18 +533,18 @@ static igraph_error_t igraph_i_community_multilevel_step( IGRAPH_CHECK(igraph_vector_update(&links_weight, weights)); igraph_vector_fill(weights, 0); - for (igraph_integer_t i = 0; i < ecount; i++) { - VECTOR(*weights)[VECTOR(edges)[i]] += VECTOR(links_weight)[i]; + for (i = 0; i < ecount; i++) { + VECTOR(*weights)[(long int)VECTOR(edges)[i]] += VECTOR(links_weight)[i]; } igraph_free(communities.item); - igraph_vector_int_destroy(&links_community); + igraph_vector_destroy(&links_community); igraph_vector_destroy(&links_weight); - igraph_vector_int_destroy(&edges); - igraph_vector_int_destroy(&node_order); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&node_order); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** @@ -549,8 +579,7 @@ static igraph_error_t igraph_i_community_multilevel_step( * gamma=1. Note that the returned modularity value is calculated using * the indicated resolution parameter. See \ref igraph_modularity() for more details. * - * - * The original version of this function was contributed by Tom Gregorovic. + * 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 @@ -575,75 +604,49 @@ static igraph_error_t igraph_i_community_multilevel_step( * \example examples/simple/igraph_community_multilevel.c */ -igraph_error_t igraph_community_multilevel(const igraph_t *graph, - const igraph_vector_t *weights, - const igraph_real_t resolution, - igraph_vector_int_t *membership, - igraph_matrix_int_t *memberships, - igraph_vector_t *modularity) { +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; - igraph_vector_int_t m; - igraph_vector_int_t level_membership; + igraph_vector_t w, m, level_membership; igraph_real_t prev_q = -1, q = -1; - igraph_integer_t level = 1; - igraph_integer_t vcount = igraph_vcount(graph); - igraph_integer_t ecount = igraph_ecount(graph); - - /* 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 (weights) { - if (igraph_vector_size(weights) != ecount) { - IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); - } - if (ecount > 0) { - igraph_real_t minweight = igraph_vector_min(weights); - if (minweight < 0) { - IGRAPH_ERROR("Weight vector must not be negative.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - } - } - if (resolution < 0.0) { - IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); - } + 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_init_copy(&w, 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_INT_INIT_FINALLY(&m, vcount); - IGRAPH_VECTOR_INT_INIT_FINALLY(&level_membership, vcount); + IGRAPH_VECTOR_INIT_FINALLY(&m, vcount); + IGRAPH_VECTOR_INIT_FINALLY(&level_membership, vcount); if (memberships || membership) { /* Put each vertex in its own community */ - for (igraph_integer_t i = 0; i < vcount; i++) { + 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_int_resize(memberships, 0, vcount)); + IGRAPH_CHECK(igraph_matrix_resize(memberships, 0, vcount)); } if (modularity) { /* Clear the modularity vector */ igraph_vector_clear(modularity); } - while (true) { + while (1) { /* Remember the previous modularity and vertex count, do a single step */ igraph_integer_t step_vcount = igraph_vcount(&g); @@ -656,9 +659,9 @@ igraph_error_t igraph_community_multilevel(const igraph_t *graph, } if (memberships || membership) { - for (igraph_integer_t i = 0; i < vcount; i++) { + for (i = 0; i < vcount; i++) { /* Readjust the membership vector */ - VECTOR(level_membership)[i] = VECTOR(m)[ VECTOR(level_membership)[i] ]; + VECTOR(level_membership)[i] = VECTOR(m)[(long int) VECTOR(level_membership)[i]]; } } @@ -670,11 +673,11 @@ igraph_error_t igraph_community_multilevel(const igraph_t *graph, if (memberships) { /* If we have to return the membership vectors at each level, store the new * membership vector */ - IGRAPH_CHECK(igraph_matrix_int_add_rows(memberships, 1)); - IGRAPH_CHECK(igraph_matrix_int_set_row(memberships, &level_membership, level - 1)); + 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, igraph_vcount(&g), + /* debug("Level: %d Communities: %ld Modularity: %f\n", level, (long int) igraph_vcount(&g), (double) q); */ /* Increase the level counter */ @@ -684,26 +687,25 @@ igraph_error_t igraph_community_multilevel(const igraph_t *graph, /* 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_int_t tmp; + igraph_vector_t tmp; igraph_real_t mod; - - IGRAPH_CHECK(igraph_vector_int_init_range(&tmp, 0, vcount)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp); - + 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 */ false, &mod)); - - igraph_vector_int_destroy(&tmp); + /* 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_int_resize(membership, vcount)); - for (igraph_integer_t i = 0; i < vcount; i++) { + IGRAPH_CHECK(igraph_vector_resize(membership, vcount)); + for (i = 0; i < vcount; i++) { VECTOR(*membership)[i] = VECTOR(level_membership)[i]; } } @@ -712,9 +714,9 @@ igraph_error_t igraph_community_multilevel(const igraph_t *graph, igraph_destroy(&g); /* Destroy the temporary vectors */ - igraph_vector_int_destroy(&m); + igraph_vector_destroy(&m); igraph_vector_destroy(&w); - igraph_vector_int_destroy(&level_membership); + igraph_vector_destroy(&level_membership); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/community/modularity.c b/src/vendor/cigraph/src/community/modularity.c index 9dd0949c0fe..010d0841c67 100644 --- a/src/vendor/cigraph/src/community/modularity.c +++ b/src/vendor/cigraph/src/community/modularity.c @@ -110,24 +110,24 @@ * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ -igraph_error_t igraph_modularity(const igraph_t *graph, - const igraph_vector_int_t *membership, +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; - igraph_integer_t types; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i; + long int types; + long int no_of_edges = igraph_ecount(graph); + long int i; igraph_real_t m; - igraph_integer_t c1, c2; + 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_int_size(membership) != igraph_vcount(graph)) { + if (igraph_vector_size(membership) != igraph_vcount(graph)) { IGRAPH_ERROR("Membership vector size differs from number of vertices.", IGRAPH_EINVAL); } @@ -147,9 +147,9 @@ igraph_error_t igraph_modularity(const igraph_t *graph, /* At this point, the 'membership' vector does not have length zero, thus it is safe to call igraph_vector_max() and min(). */ - types = igraph_vector_int_max(membership) + 1; + types = (long int) igraph_vector_max(membership) + 1; - if (igraph_vector_int_min(membership) < 0) { + if (igraph_vector_min(membership) < 0) { IGRAPH_ERROR("Invalid membership vector: negative entry.", IGRAPH_EINVAL); } @@ -159,7 +159,7 @@ igraph_error_t igraph_modularity(const igraph_t *graph, if (weights) { if (igraph_vector_size(weights) != no_of_edges) - IGRAPH_ERROR("Weight vector size differs from number of edges.", + IGRAPH_ERROR("Vector size differs from number of edges.", IGRAPH_EINVAL); m = 0.0; for (i = 0; i < no_of_edges; i++) { @@ -167,8 +167,8 @@ igraph_error_t igraph_modularity(const igraph_t *graph, if (w < 0) { IGRAPH_ERROR("Negative weight in weight vector.", IGRAPH_EINVAL); } - c1 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; - c2 = VECTOR(*membership)[ IGRAPH_TO(graph, 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 * w; } @@ -179,8 +179,8 @@ igraph_error_t igraph_modularity(const igraph_t *graph, } else { m = no_of_edges; for (i = 0; i < no_of_edges; i++) { - c1 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; - c2 = VECTOR(*membership)[ IGRAPH_TO(graph, 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; } @@ -216,12 +216,12 @@ igraph_error_t igraph_modularity(const igraph_t *graph, return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_modularity_matrix_get_adjacency( +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; - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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)); @@ -242,8 +242,7 @@ static igraph_error_t igraph_i_modularity_matrix_get_adjacency( } else { 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); + igraph_edge(graph, edge, &from, &to); MATRIX(*res, from, to) += 1; if (!directed) { MATRIX(*res, to, from) += 1; @@ -299,18 +298,17 @@ static igraph_error_t igraph_i_modularity_matrix_get_adjacency( * * \sa \ref igraph_modularity() */ -igraph_error_t igraph_modularity_matrix(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_vector_int_t deg_int, in_deg_int, out_deg_int; - igraph_integer_t i, j; + 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); @@ -329,19 +327,10 @@ igraph_error_t igraph_modularity_matrix(const igraph_t *graph, IGRAPH_VECTOR_INIT_FINALLY(&in_deg, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&out_deg, no_of_nodes); if (!weights) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_deg_int, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_deg_int, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, &in_deg_int, igraph_vss_all(), IGRAPH_IN, + IGRAPH_CHECK(igraph_degree(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); - IGRAPH_CHECK(igraph_degree(graph, &out_deg_int, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_CHECK(igraph_degree(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); - for (i = 0; i < no_of_nodes; i++) { - VECTOR(in_deg)[i] = VECTOR(in_deg_int)[i]; - VECTOR(out_deg)[i] = VECTOR(out_deg_int)[i]; - } - igraph_vector_int_destroy(&in_deg_int); - igraph_vector_int_destroy(&out_deg_int); - IGRAPH_FINALLY_CLEAN(2); } else { IGRAPH_CHECK(igraph_strength(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, weights)); @@ -363,21 +352,15 @@ igraph_error_t igraph_modularity_matrix(const igraph_t *graph, } else { IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); if (!weights) { - IGRAPH_VECTOR_INT_INIT_FINALLY(°_int, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °_int, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); - for (i = 0; i < no_of_nodes; i++) { - VECTOR(deg)[i] = VECTOR(deg_int)[i]; - } - igraph_vector_int_destroy(°_int); - IGRAPH_FINALLY_CLEAN(1); } 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_init_copy(°_unscaled, °); + igraph_vector_copy(°_unscaled, °); IGRAPH_FINALLY(igraph_vector_destroy, °_unscaled); scaling_factor = resolution / 2.0 / sw; igraph_vector_scale(°, scaling_factor); diff --git a/src/vendor/cigraph/src/community/optimal_modularity.c b/src/vendor/cigraph/src/community/optimal_modularity.c index ce3eb2d9be7..cdb932891e1 100644 --- a/src/vendor/cigraph/src/community/optimal_modularity.c +++ b/src/vendor/cigraph/src/community/optimal_modularity.c @@ -30,7 +30,6 @@ #include "core/interruption.h" #include "internal/glpk_support.h" -#include "math/safe_intop.h" #include "config.h" @@ -38,11 +37,9 @@ #include #endif -#include - /** * \function igraph_community_optimal_modularity - * \brief Calculate the community structure with the highest modularity value. + * 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. @@ -52,11 +49,10 @@ * 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 - * https://doi.org/10.1109/TKDE.2007.190689. + * Engineering 20(2):172-188, 2008. * * - * Note that exact modularity optimization is an NP-complete problem, and + * 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 @@ -72,7 +68,6 @@ * \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. - * When GLPK is not available, \c IGRAPH_UNIMPLEMENTED is returned. * * \sa \ref igraph_modularity(), \ref igraph_community_fastgreedy() * for an algorithm that finds a local optimum in a greedy way. @@ -82,21 +77,21 @@ * \example examples/simple/igraph_community_optimal_modularity.c */ -igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, +int igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, - igraph_vector_int_t *membership, + igraph_vector_t *membership, const igraph_vector_t *weights) { #ifndef HAVE_GLPK - IGRAPH_ERROR("GLPK is not available.", IGRAPH_UNIMPLEMENTED); + IGRAPH_ERROR("GLPK is not available", + IGRAPH_UNIMPLEMENTED); #else - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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); - igraph_integer_t no_of_variables; - igraph_integer_t i, j, k, l; - int st; + 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; @@ -116,7 +111,7 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Negative weights are not allowed in weight vector.", IGRAPH_EINVAL); } - if (isnan(minweight)) { + if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -125,8 +120,8 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, /* Avoid problems with the null graph */ if (no_of_nodes < 2) { if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + 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)); @@ -134,18 +129,6 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, return IGRAPH_SUCCESS; } - /* no_of_variables = no_of_nodes * (no_of_nodes + 1) / 2; - * - * Here we do not use IGRAPH_SAFE_N_CHOOSE_2 because later we rely on - * (no_of_nodes + 1) * no_of_nodes not overflowing even before the - * division by 2. See IDX() macro. - */ - IGRAPH_SAFE_MULT(no_of_nodes + 1, no_of_nodes, &no_of_variables); - no_of_variables /= 2; - if (no_of_variables > INT_MAX) { - IGRAPH_ERROR("Problem too large for GLPK.", IGRAPH_EOVERFLOW); - } - if (weights) { total_weight = igraph_vector_sum(weights); } else { @@ -161,8 +144,8 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, *modularity = IGRAPH_NAN; } if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_null(membership); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_vector_null(membership); } } @@ -179,14 +162,14 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, IGRAPH_FINALLY(igraph_i_glp_delete_prob, ip); glp_set_obj_dir(ip, GLP_MAX); - st = glp_add_cols(ip, (int) no_of_variables); + st = glp_add_cols(ip, no_of_variables); /* variables are binary */ for (i = 0; i < no_of_variables; i++) { - glp_set_col_kind(ip, (int)(st + i), GLP_BV); + glp_set_col_kind(ip, (st + i), GLP_BV); } -#define IDX(a,b) (int)((b)*((b)+1)/2+(a)) +#define IDX(a,b) ((b)*((b)+1)/2+(a)) /* reflexivity */ for (i = 0; i < no_of_nodes; i++) { @@ -265,8 +248,8 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, } if (membership) { - igraph_integer_t comm = 0; /* id of the last community that was found */ - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + 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(); diff --git a/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp b/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp index aeba0d00f80..29014be0587 100644 --- a/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp +++ b/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp @@ -48,7 +48,7 @@ #include "igraph_interface.h" #include "igraph_conversion.h" -igraph_error_t igraph_i_read_network(const igraph_t *graph, +int igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states) { @@ -58,13 +58,13 @@ igraph_error_t igraph_i_read_network(const igraph_t *graph, char name[255]; NNode *node1, *node2; DLList_Iter iter; - igraph_vector_int_t edgelist; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t ii; + 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_INT_INIT_FINALLY(&edgelist, no_of_edges * 2); + 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++) { @@ -72,8 +72,8 @@ igraph_error_t igraph_i_read_network(const igraph_t *graph, } for (ii = 0; ii < no_of_edges; ii++) { - igraph_integer_t i1 = VECTOR(edgelist)[2 * ii]; - igraph_integer_t i2 = VECTOR(edgelist)[2 * ii + 1]; + 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]; @@ -82,11 +82,11 @@ igraph_error_t igraph_i_read_network(const igraph_t *graph, } node1 = net->node_list->Get(i1); - snprintf(name, sizeof(name) / sizeof(name[0]), "%" IGRAPH_PRId "", i1+1); + snprintf(name, sizeof(name) / sizeof(name[0]), "%li", i1+1); node1->Set_Name(name); node2 = net->node_list->Get(i2); - snprintf(name, sizeof(name) / sizeof(name[0]), "%" IGRAPH_PRId "", i2+1); + snprintf(name, sizeof(name) / sizeof(name[0]), "%li", i2+1); node2->Set_Name(name); node1->Connect_To(node2, Links); @@ -101,7 +101,7 @@ igraph_error_t igraph_i_read_network(const igraph_t *graph, } IGRAPH_FINALLY_CLEAN(1); - igraph_vector_int_destroy(&edgelist); + igraph_vector_destroy(&edgelist); node1 = iter.First(net->node_list); while (!iter.End()) { diff --git a/src/vendor/cigraph/src/community/spinglass/NetRoutines.h b/src/vendor/cigraph/src/community/spinglass/NetRoutines.h index 53a4e51aa34..02030fbcdef 100644 --- a/src/vendor/cigraph/src/community/spinglass/NetRoutines.h +++ b/src/vendor/cigraph/src/community/spinglass/NetRoutines.h @@ -48,7 +48,7 @@ #include "igraph_types.h" #include "igraph_datatype.h" -igraph_error_t igraph_i_read_network(const igraph_t *graph, +int igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states); diff --git a/src/vendor/cigraph/src/community/spinglass/clustertool.cpp b/src/vendor/cigraph/src/community/spinglass/clustertool.cpp index 0a13afc9dc1..c99173a885f 100644 --- a/src/vendor/cigraph/src/community/spinglass/clustertool.cpp +++ b/src/vendor/cigraph/src/community/spinglass/clustertool.cpp @@ -46,21 +46,21 @@ #include "pottsmodel_2.h" #include "igraph_community.h" -#include "igraph_components.h" #include "igraph_error.h" -#include "igraph_interface.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 igraph_error_t igraph_i_community_spinglass_orig( +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_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -69,13 +69,13 @@ static igraph_error_t igraph_i_community_spinglass_orig( igraph_spincomm_update_t update_rule, igraph_real_t gamma); -static igraph_error_t igraph_i_community_spinglass_negative( +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_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -90,7 +90,7 @@ static igraph_error_t igraph_i_community_spinglass_negative( /** * \function igraph_community_spinglass - * \brief Community detection based on statistical mechanics. + * \brief Community detection based on statistical mechanics * * This function implements the community structure detection * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. @@ -104,11 +104,11 @@ static igraph_error_t igraph_i_community_spinglass_negative( * 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 ignored by the algorithm. + * 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. + * 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 @@ -126,17 +126,20 @@ static igraph_error_t igraph_i_community_spinglass_negative( * 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. Even if the number of spins is high the number of - * clusters in the result might be small. + * 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. It is not implemented in the \c IGRAPH_SPINCOMM_INP_NEG - * implementation. - * \param starttemp Real number, the temperature at the start. A reasonable - * default is 1.0. - * \param stoptemp Real number, the algorithm stops at this temperature. A - * reasonable default is 0.01. + * 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. A reasonable default is 0.99. + * 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 @@ -146,20 +149,29 @@ static igraph_error_t igraph_i_community_spinglass_negative( * 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, - * acting as a resolution parameter. Smaller values typically lead to - * larger clusters, larger values typically lead to smaller clusters. + * \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 - * an implementation that allows negative edge weights. - * \param gamma_minus Real number. Parameter for the \c IGRAPH_SPINCOMM_IMP_NEG - * implementation. This acts as a resolution parameter for the negative part - * of the network. Smaller values of \p gamma_minus leads to fewer negative - * edges within clusters. If this argument is set to zero, the algorithm - * reduces to a graph coloring algorithm when all edges have negative - * weights, using the number of spins as the number of colors. + * 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 @@ -169,12 +181,12 @@ static igraph_error_t igraph_i_community_spinglass_negative( * */ -igraph_error_t igraph_community_spinglass(const igraph_t *graph, +int igraph_community_spinglass(const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -182,7 +194,11 @@ igraph_error_t igraph_community_spinglass(const igraph_t *graph, 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( @@ -200,6 +216,8 @@ igraph_error_t igraph_community_spinglass(const igraph_t *graph, spins, parupdate, starttemp, stoptemp, coolfact, update_rule, gamma, + /* adhesion, normalised_adhesion, */ + /* polarization, */ gamma_minus); break; default: @@ -209,13 +227,13 @@ igraph_error_t igraph_community_spinglass(const igraph_t *graph, ); } -static igraph_error_t igraph_i_community_spinglass_orig( +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_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -224,9 +242,9 @@ static igraph_error_t igraph_i_community_spinglass_orig( igraph_spincomm_update_t update_rule, igraph_real_t gamma) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); unsigned long changes, runs; - igraph_bool_t use_weights = false; + igraph_bool_t use_weights = 0; bool zeroT; double kT, acc, prob; @@ -244,9 +262,6 @@ static igraph_error_t igraph_i_community_spinglass_orig( IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } use_weights = 1; - if (igraph_vector_min(weights) < 0) { - IGRAPH_ERROR("Weights must not be negative when using the original implementation of spinglass communities. Select the implementation meant for negative weights.", IGRAPH_EINVAL); - } } if (coolfact < 0 || coolfact >= 1.0) { IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); @@ -263,8 +278,8 @@ static igraph_error_t igraph_i_community_spinglass_orig( null and singleton graphs, so we catch them here. */ if (no_of_nodes < 2) { if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + 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)); @@ -274,8 +289,8 @@ static igraph_error_t igraph_i_community_spinglass_orig( } if (csize) { /* 0 clusters for 0 nodes, 1 cluster for 1 node */ - IGRAPH_CHECK(igraph_vector_int_resize(csize, no_of_nodes)); - igraph_vector_int_fill(csize, 1); + IGRAPH_CHECK(igraph_vector_resize(csize, no_of_nodes)); + igraph_vector_fill(csize, 1); } return IGRAPH_SUCCESS; } @@ -352,12 +367,12 @@ static igraph_error_t igraph_i_community_spinglass_orig( RNG_END(); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_community_spinglass_single - * \brief Community of a single node based on statistical mechanics. + * \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 @@ -373,10 +388,10 @@ static igraph_error_t igraph_i_community_spinglass_orig( * \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 + * \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 + * 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. @@ -415,10 +430,10 @@ static igraph_error_t igraph_i_community_spinglass_orig( * Time complexity: TODO. */ -igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, +int igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, - igraph_vector_int_t *community, + igraph_vector_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -427,7 +442,7 @@ igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, igraph_spincomm_update_t update_rule, igraph_real_t gamma) { IGRAPH_HANDLE_EXCEPTIONS( - igraph_bool_t use_weights = false; + igraph_bool_t use_weights = 0; double prob; char startnode[255]; @@ -450,7 +465,7 @@ igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); } if (vertex < 0 || vertex > igraph_vcount(graph)) { - IGRAPH_ERROR("Invalid vertex ID", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); } /* Check whether we have a single component */ @@ -478,23 +493,23 @@ igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, /* 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, "%" IGRAPH_PRId "", vertex + 1); + snprintf(startnode, 255, "%li", (long int)vertex + 1); pm.FindCommunityFromStart(gamma, prob, startnode, community, cohesion, adhesion, inner_links, outer_links); RNG_END(); ); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_community_spinglass_negative( +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_int_t *membership, - igraph_vector_int_t *csize, + igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -507,9 +522,9 @@ static igraph_error_t igraph_i_community_spinglass_negative( /* igraph_real_t *polarization, */ igraph_real_t gamma_minus) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); unsigned long changes, runs; - igraph_bool_t use_weights = false; + igraph_bool_t use_weights = 0; bool zeroT; double kT, acc; igraph_real_t d_n; @@ -550,8 +565,8 @@ static igraph_error_t igraph_i_community_spinglass_negative( null and singleton graphs, so we catch them here. */ if (no_of_nodes < 2) { if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + 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)); @@ -561,8 +576,8 @@ static igraph_error_t igraph_i_community_spinglass_negative( } if (csize) { /* 0 clusters for 0 nodes, 1 cluster for 1 node */ - IGRAPH_CHECK(igraph_vector_int_resize(csize, no_of_nodes)); - igraph_vector_int_fill(csize, 1); + IGRAPH_CHECK(igraph_vector_resize(csize, no_of_nodes)); + igraph_vector_fill(csize, 1); } return IGRAPH_SUCCESS; } @@ -574,7 +589,7 @@ static igraph_error_t igraph_i_community_spinglass_negative( IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); } - if (weights && igraph_vector_size(weights) > 0) { + if (weights) { igraph_vector_minmax(weights, &d_n, &d_p); } else { d_n = d_p = 1; @@ -645,5 +660,5 @@ static igraph_error_t igraph_i_community_spinglass_negative( RNG_END(); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp index 347d83b15f8..a26b6730e25 100644 --- a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp +++ b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp @@ -43,6 +43,7 @@ ***************************************************************************/ #include "pottsmodel_2.h" +#include "NetRoutines.h" #include "igraph_random.h" #include "core/interruption.h" @@ -53,11 +54,11 @@ using namespace std; //################################################################################################# -PottsModel::PottsModel(network *n, unsigned long qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) +PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) { DLList_Iter iter; NNode *n_cur; - unsigned long *i_ptr; + unsigned int *i_ptr; net = n; q = qvalue; operation_mode = m; @@ -75,16 +76,16 @@ PottsModel::PottsModel(network *n, unsigned long qvalue, int m) : Qmatrix(qvalue 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(); + 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 long; + i_ptr = new unsigned int; *i_ptr = 0; new_spins->Push(i_ptr); - i_ptr = new unsigned long; + i_ptr = new unsigned int; *i_ptr = 0; previous_spins->Push(i_ptr); n_cur = iter.Next(); @@ -112,8 +113,8 @@ PottsModel::~PottsModel() { //when called with positve one. //This may be handy, if you want to warm up the network. //#################################################### -unsigned long PottsModel::assign_initial_conf(igraph_integer_t spin) { - igraph_integer_t s; +unsigned long PottsModel::assign_initial_conf(int spin) { + int s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; @@ -126,7 +127,7 @@ unsigned long PottsModel::assign_initial_conf(igraph_integer_t spin) { // printf("Assigning initial configuration...\n"); // initialize colorfield - for (unsigned long i = 0; i <= q; i++) { + for (unsigned int i = 0; i <= q; i++) { color_field[i] = 0.0; } // @@ -207,7 +208,7 @@ unsigned long PottsModel::initialize_lookup(double kT, double gamma) { double PottsModel::initialize_Qmatrix() { DLList_Iter l_iter; NLink *l_cur; - unsigned long i, j; + unsigned int i, j; //initialize with zeros num_of_links = net->link_list->Size(); for (i = 0; i <= q; i++) { @@ -243,7 +244,7 @@ double PottsModel::initialize_Qmatrix() { //#################################################################### double PottsModel::calculate_Q() { double Q = 0.0; - for (unsigned long i = 0; i <= q; i++) { + 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"); @@ -256,7 +257,7 @@ double PottsModel::calculate_Q() { } double PottsModel::calculate_genQ(double gamma) { double Q = 0.0; - for (unsigned long i = 0; i <= q; i++) { + 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"); @@ -284,7 +285,7 @@ double PottsModel::calculate_energy(double gamma) { l_cur = l_iter.Next(); } //and the penalty term contributes according to cluster sizes - for (unsigned long i = 1; i <= q; i++) { + for (unsigned int i = 1; i <= q; i++) { e += gamma * 0.5 * double(color_field[i]) * double((color_field[i] - 1)); } energy = e; @@ -326,11 +327,10 @@ double PottsModel::FindStartTemp(double gamma, double prob, double ts) { 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; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned int sweep; - unsigned long *SPIN, *P_SPIN, old_spin, spin, new_spin, spin_opt; + 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; @@ -347,9 +347,9 @@ long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsig node = net_iter.First(net->node_list); SPIN = i_iter.First(new_spins); while (!net_iter.End()) { - // How many neighbors of each type? + // How many neigbors of each type? // set them all zero - for (unsigned long i = 0; i <= q; i++) { + for (unsigned int i = 0; i <= q; i++) { neighbours[i] = 0; } degree = node->Get_Weight(); @@ -467,8 +467,7 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned long new_spin, spin_opt, old_spin, spin; - unsigned int sweep; + unsigned int new_spin, spin_opt, old_spin, spin, sweep; long r;// degree; unsigned long changes; double delta = 0, h, deltaE, deltaEmin, w, degree; @@ -488,7 +487,7 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in node = net->node_list->Get(r); // Wir zaehlen, wieviele Nachbarn von jedem spin vorhanden sind // erst mal alles Null setzen - for (unsigned long i = 0; i <= q; i++) { + for (unsigned int i = 0; i <= q; i++) { neighbours[i] = 0; } degree = node->Get_Weight(); @@ -574,17 +573,17 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in 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; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned long new_spin, spin_opt, old_spin; - unsigned long *SPIN, *P_SPIN; + unsigned int new_spin, spin_opt, old_spin; + unsigned int *SPIN, *P_SPIN; unsigned int sweep; long max_q; - unsigned long changes/*, degree, problemcount */; + unsigned long changes /*, degree, problemcount*/; //HugeArray neighbours; double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree; - bool cyclic = false/*, found*/; + bool cyclic = false, found; unsigned long number_of_nodes; sweep = 0; @@ -599,8 +598,8 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un SPIN = i_iter.First(new_spins); while (!net_iter.End()) { // Initialize neighbours and weights - // problemcount = 0; - for (unsigned long i = 0; i <= q; i++) { + //problemcount = 0; + for (unsigned int i = 0; i <= q; i++) { neighbours[i] = 0; weights[i] = 0; } @@ -659,11 +658,11 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un r = RNG_UNIF(0, norm); /* norm*double(rand())/double(RAND_MAX + 1.0); */ new_spin = 1; - //found = false; - while (/*!found &&*/ new_spin <= q) { + found = false; + while (!found && new_spin <= q) { if (r <= weights[new_spin]) { spin_opt = new_spin; - // found = true; + found = true; break; } else { r -= weights[new_spin]; @@ -727,7 +726,7 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un } // while markov max_q = 0; - for (unsigned long i = 1; i <= q; i++) if (color_field[i] > max_q) { + for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { max_q = long(color_field[i]); } @@ -748,25 +747,25 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un 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; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned long new_spin, spin_opt, old_spin; + unsigned int new_spin, spin_opt, old_spin; unsigned int sweep; long max_q, rn; - unsigned long changes/*, degree, problemcount*/; + unsigned long changes /*, degree, problemcount*/; double degree, w, delta = 0, h; //HugeArray neighbours; double norm, r, beta, minweight, prefac = 0; - //bool found; - igraph_integer_t number_of_nodes; + 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 (long n = 0; n < number_of_nodes; n++) { + 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); @@ -776,7 +775,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned node = net->node_list->Get(rn); // initialize the neighbours and the weights // problemcount = 0; - for (unsigned long i = 0; i <= q; i++) { + for (unsigned int i = 0; i <= q; i++) { neighbours[i] = 0.0; weights[i] = 0.0; } @@ -838,11 +837,11 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned /* r = norm*double(rand())/double(RAND_MAX + 1.0); */ r = RNG_UNIF(0, norm); new_spin = 1; - //found = false; - while (/*!found &&*/ new_spin <= q) { + found = false; + while (!found && new_spin <= q) { if (r <= weights[new_spin]) { spin_opt = new_spin; - //found = true; + found = true; break; } else { r -= weights[new_spin]; @@ -887,7 +886,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned } // while markov max_q = 0; - for (unsigned long i = 1; i <= q; i++) if (color_field[i] > max_q) { + for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { max_q = long(color_field[i] + 0.5); } @@ -900,7 +899,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned //############################################################################################### double PottsModel::FindCommunityFromStart(double gamma, double prob, char *nodename, - igraph_vector_int_t *result, + igraph_vector_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *my_inner_links, @@ -1132,17 +1131,17 @@ double PottsModel::FindCommunityFromStart(double gamma, double prob, } if (result) { node = iter.First(community); - igraph_vector_int_clear(result); + 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_int_push_back(result, node->Get_Index())); + 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 long size = community->Size(); + unsigned int size = community->Size(); delete to_do; delete community; return size; @@ -1153,8 +1152,8 @@ double PottsModel::FindCommunityFromStart(double gamma, double prob, //################################################################################################ long PottsModel::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *csize, - igraph_vector_int_t *membership, + igraph_vector_t *csize, + igraph_vector_t *membership, double kT, double gamma) { NNode *n_cur, *n_cur2; /* @@ -1182,7 +1181,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, if (csize || membership || modularity) { // TODO: count the number of clusters - for (unsigned long spin = 1; spin <= q; spin++) { + for (unsigned int spin = 1; spin <= q; spin++) { inner_links[spin] = 0; outer_links[spin] = 0; nodes[spin] = 0; @@ -1206,7 +1205,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, } if (modularity) { *modularity = 0.0; - for (unsigned long spin = 1; spin <= q; spin++) { + 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]) / @@ -1217,8 +1216,8 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, } } if (csize) { - igraph_vector_int_clear(csize); - for (unsigned long spin = 1; spin <= q; spin++) { + 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"); @@ -1246,7 +1245,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, 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_int_push_back(csize, nodes[spin])); + IGRAPH_CHECK(igraph_vector_push_back(csize, nodes[spin])); } } // fprintf(file,"\n"); @@ -1254,9 +1253,9 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, //die Elemente der Cluster if (membership) { - igraph_integer_t no = -1; - IGRAPH_CHECK(igraph_vector_int_resize(membership, num_of_nodes)); - for (unsigned long spin = 1; spin <= q; spin++) { + 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++; } @@ -1553,7 +1552,7 @@ double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, dou //############################################################################## //################################################################################################# -PottsModelN::PottsModelN(network *n, unsigned long num_communities, bool directed) : +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), @@ -1595,7 +1594,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { #ifdef SPINGLASS_DEBUG printf("Start assigning.\n"); #endif - unsigned long s; + unsigned int s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; @@ -1621,7 +1620,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { 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 long[num_nodes]; //The spin state of each node + spin = new unsigned int[num_nodes]; //The spin state of each node } if (is_init) { @@ -1646,11 +1645,11 @@ void PottsModelN::assign_initial_conf(bool init_spins) { //...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 long[q + 1]; //The number of nodes in each community + csize = new unsigned int[q + 1]; //The number of nodes in each community //Initialize communities - for (unsigned long i = 0; i <= q; i++) { + 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; @@ -1661,7 +1660,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { //Initialize vectors if (init_spins) { - for (unsigned long i = 0; i < num_nodes; i++) { + 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; @@ -1684,7 +1683,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { #ifdef SPINGLASS_DEBUG printf("Visiting each node.\n"); #endif - for (unsigned long v = 0; v < num_nodes; v++) { + 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; @@ -1770,7 +1769,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign #endif DLList_Iter iter; DLList_Iter l_iter; - DLList_Iter i_iter, i_iter2; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; /* The new_spin contains the spin to which we will update, @@ -1778,13 +1777,13 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign * the old_spin is the spin of the node we are currently * changing. */ - unsigned long new_spin, spin_opt, old_spin; + 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) - long v; //The node we will be investigating + 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; @@ -1815,7 +1814,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign while (sweep < max_sweeps) { sweep++; //loop over all nodes in network - for (unsigned long n = 0; n < num_nodes; n++) { + 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 @@ -1825,7 +1824,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign /*******************************************/ // initialize the neighbours and the weights // problemcount = 0; - for (unsigned long i = 0; i <= q; i++) { + for (unsigned int i = 0; i <= q; i++) { neighbours[i] = 0.0; weights[i] = 0.0; } @@ -1995,8 +1994,8 @@ double PottsModelN::FindStartTemp(double gamma, double lambda, double ts) { long PottsModelN::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *community_size, - igraph_vector_int_t *membership, + igraph_vector_t *community_size, + igraph_vector_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, @@ -2011,16 +2010,16 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, printf("Start writing clusters.\n"); #endif //Reassign each community so that we retrieve a community assignment 1 through num_communities - unsigned long *cluster_assign = new unsigned long[q + 1]; - for (unsigned long i = 0; i <= q; i++) { + unsigned int *cluster_assign = new unsigned int[q + 1]; + for (unsigned int i = 0; i <= q; i++) { cluster_assign[i] = 0; } - long num_clusters = 0; + int num_clusters = 0; //Find out what the new communities will be - for (unsigned long i = 0; i < num_nodes; i++) { - unsigned long s = spin[i]; + 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; @@ -2039,11 +2038,11 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //And now assign each node to its new community q = num_clusters; - for (unsigned long i = 0; i < num_nodes; i++) { + 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 long s = cluster_assign[spin[i]]; + unsigned int s = cluster_assign[spin[i]]; spin[i] = s; #ifdef SPINGLASS_DEBUG printf("Have set node %d to %d.\n", i, s); @@ -2059,8 +2058,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, if (community_size) { //Initialize the vector - IGRAPH_CHECK(igraph_vector_int_resize(community_size, q)); - for (unsigned long spin_opt = 1; spin_opt <= q; spin_opt++) { + 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]; } @@ -2068,8 +2067,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //Set the membership if (membership) { - IGRAPH_CHECK(igraph_vector_int_resize(membership, num_nodes)); - for (unsigned long i = 0; i < num_nodes; i++) { + IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes)); + for (unsigned int i = 0; i < num_nodes; i++) { VECTOR(*membership)[ i ] = spin[i] - 1; } } @@ -2086,7 +2085,7 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, num_links_neg = new double *[q + 1] ; //memory allocated for elements of each column. - for ( unsigned long i = 0 ; i < q + 1 ; i++) { + 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]; } @@ -2094,8 +2093,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //Init num_links - for (unsigned long i = 0; i <= q; i++) { - for (unsigned long j = 0; j <= q; j++) { + 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; } @@ -2108,8 +2107,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, while (!iter_l.End()) { w = l_cur->Get_Weight(); - unsigned long a = spin[l_cur->Get_Start()->Get_Index()]; - unsigned long b = spin[l_cur->Get_End()->Get_Index()]; + 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 @@ -2140,8 +2139,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //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 long i = 1; i <= q; i++) { - for (unsigned long j = 1; j <= q; j++) { + 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); @@ -2208,7 +2207,7 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, } //for i //free the allocated memory - for ( unsigned long i = 0 ; i < q + 1 ; i++ ) { + for ( unsigned int i = 0 ; i < q + 1 ; i++ ) { delete [] num_links_pos[i] ; delete [] num_links_neg[i]; } @@ -2227,8 +2226,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, if (polarization) { double sum_ad = 0.0; - for (unsigned long i = 0; i < q; i++) { - for (unsigned long j = 0; j < q; j++) { + 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); } diff --git a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h index 27addecfedf..7d0ee28385c 100644 --- a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h +++ b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h @@ -69,11 +69,11 @@ class PottsModel { private: // HugeArray neg_gammalookup; // HugeArray pos_gammalookup; - DL_Indexed_List *new_spins; - DL_Indexed_List *previous_spins; + DL_Indexed_List *new_spins; + DL_Indexed_List *previous_spins; HugeArray*> correlation; network *net; - unsigned long q; + unsigned int q; unsigned int operation_mode; // FILE *Qfile, *Magfile; SimpleMatrix Qmatrix; @@ -87,10 +87,10 @@ class PottsModel { double acceptance; double *neighbours; public: - PottsModel(network *net, unsigned long q, int norm_by_degree); + PottsModel(network *net, unsigned int q, int norm_by_degree); ~PottsModel(); double* color_field; - unsigned long assign_initial_conf(igraph_integer_t spin); + unsigned long assign_initial_conf(int spin); unsigned long initialize_lookup(double kT, double gamma); double initialize_Qmatrix(); double calculate_Q(); @@ -106,14 +106,14 @@ class PottsModel { double calculate_energy(double gamma); long WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *csize, igraph_vector_int_t *membership, + 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_int_t *result, + igraph_vector_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -130,10 +130,10 @@ class PottsModelN { HugeArray*> correlation; network *net; - unsigned long q; //number of communities + 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 long num_nodes; //number of nodes + unsigned int num_nodes; //number of nodes bool is_directed; bool is_init; @@ -148,14 +148,14 @@ class PottsModelN { double *degree_community_pos_out; //Positive sum of outegree for communities double *degree_community_neg_out; //Negative sum of outdegree for communities - unsigned long *csize; //The number of nodes in each community - unsigned long *spin; //The membership of each node + 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 long num_communities, bool directed); + 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); @@ -164,8 +164,8 @@ class PottsModelN { // double HeatBathLookupZeroTemp(double gamma, double lambda, unsigned int max_sweeps); long WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_int_t *community_size, - igraph_vector_int_t *membership, + igraph_vector_t *community_size, + igraph_vector_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap.cpp index 1fb9d09ef02..d35bcb28072 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap.cpp @@ -63,19 +63,10 @@ #include "core/exceptions.h" #include "core/interruption.h" -#include -#include - -// This is necessary for GCC 5 and earlier, where including -// makes isnan() unusable without the std:: prefix, even if -// was included as well. -using std::isnan; - using namespace igraph::walktrap; /** * \function igraph_community_walktrap - * \brief Community finding using a random walk based similarity measure. * * This function is the implementation of the Walktrap community * finding algorithm, see Pascal Pons, Matthieu Latapy: Computing @@ -101,8 +92,8 @@ using namespace igraph::walktrap; * 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 \c NULL). Each merge is a - * row in a two-column matrix and contains the IDs of the merged + * 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 @@ -111,12 +102,13 @@ using namespace igraph::walktrap; * 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 \c NULL then the + * \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 \c NULL pointer, then + * \param membership Pointer to a vector. If not a NULL pointer, then * the membership vector corresponding to the maximal modularity - * score is stored here. + * 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 @@ -128,106 +120,81 @@ using namespace igraph::walktrap; * \example examples/simple/walktrap.c */ -igraph_error_t igraph_community_walktrap(const igraph_t *graph, +int igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, - igraph_integer_t steps, - igraph_matrix_int_t *merges, + int steps, + igraph_matrix_t *merges, igraph_vector_t *modularity, - igraph_vector_int_t *membership) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t comp_count; - igraph_matrix_int_t imerges, *pmerges = merges; - igraph_vector_t imodularity, *pmodularity = modularity; - - if (steps <= 0) { - IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); - } + igraph_vector_t *membership) { - if (steps > INT_MAX) { - IGRAPH_ERROR("Length of random walks too large for walktrap community detection.", IGRAPH_EINVAL); - } - - int length = steps; - - if (weights) { - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + IGRAPH_HANDLE_EXCEPTIONS( + long int no_of_nodes = (long int) igraph_vcount(graph); + long int no_of_edges = (long int) igraph_ecount(graph); + int length = steps; + long max_memory = -1; + igraph_integer_t comp_count; + + if (steps <= 0) { + IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", 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 (isnan(minweight)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } - } - } - - if (membership) { - /* We need both 'modularity' and 'merges' to compute 'membership'. - * If they were not provided by the called, we allocate these here. */ - if (! modularity) { - IGRAPH_VECTOR_INIT_FINALLY(&imodularity, 0); - pmodularity = &imodularity; + 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); + } + } } - if (! merges) { - IGRAPH_MATRIX_INT_INIT_FINALLY(&imerges, 0, 0); - pmerges = &imerges; + if (membership && !(modularity && merges)) { + IGRAPH_ERROR("Cannot calculate membership without modularity or merges", + IGRAPH_EINVAL); } - } - IGRAPH_HANDLE_EXCEPTIONS( Graph G; IGRAPH_CHECK(G.convert_from_igraph(graph, weights)); - if (pmerges || pmodularity) { - IGRAPH_CHECK(igraph_connected_components(graph, /*membership=*/ NULL, /*csize=*/ NULL, - &comp_count, IGRAPH_WEAK)); + if (merges || modularity) { + IGRAPH_CHECK(igraph_clusters(graph, /*membership=*/ 0, /*csize=*/ 0, + &comp_count, IGRAPH_WEAK)); } - if (pmerges) { - IGRAPH_CHECK(igraph_matrix_int_resize(pmerges, no_of_nodes - comp_count, 2)); + if (merges) { + IGRAPH_CHECK(igraph_matrix_resize(merges, no_of_nodes - comp_count, 2)); } - if (pmodularity) { - IGRAPH_CHECK(igraph_vector_resize(pmodularity, no_of_nodes - comp_count + 1)); - igraph_vector_null(pmodularity); + if (modularity) { + IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_nodes - comp_count + 1)); + igraph_vector_null(modularity); } - Communities C(&G, length, pmerges, pmodularity); + Communities C(&G, length, max_memory, merges, modularity); while (!C.H->is_empty()) { IGRAPH_ALLOW_INTERRUPTION(); C.merge_nearest_communities(); } - ); - - if (membership) { - igraph_integer_t m; - m = no_of_nodes > 0 ? igraph_vector_which_max(pmodularity) : 0; - IGRAPH_CHECK(igraph_community_to_membership(pmerges, no_of_nodes, - /*steps=*/ m, - membership, - /*csize=*/ NULL)); - - if (! merges) { - igraph_matrix_int_destroy(&imerges); - IGRAPH_FINALLY_CLEAN(1); - } - if (! modularity) { - igraph_vector_destroy(&imodularity); - IGRAPH_FINALLY_CLEAN(1); + + 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)); } - } - /* The walktrap implementation cannot work with NaN values internally, - * and produces 0 for the modularity of edgeless graphs. We correct - * this to NaN in the last step for consistency. */ - if (modularity && no_of_edges == 0) { - VECTOR(*modularity)[0] = IGRAPH_NAN; - } + /* The walktrap implementation cannot work with NaN values internally, + * and produces 0 for the modularity of edgeless graphs. We correct + * this to NaN in the last step for consistency. */ + if (modularity && no_of_edges == 0) { + VECTOR(*modularity)[0] = IGRAPH_NAN; + } - return IGRAPH_SUCCESS; + return IGRAPH_SUCCESS; + ) } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp index 4e1dd71025e..713135f3573 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp @@ -65,26 +65,31 @@ namespace igraph { namespace walktrap { IGRAPH_THREAD_LOCAL int Probabilities::length = 0; -IGRAPH_THREAD_LOCAL Communities* Probabilities::C = nullptr; -IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector1 = nullptr; -IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector2 = nullptr; -IGRAPH_THREAD_LOCAL int* Probabilities::id = nullptr; -IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = nullptr; -IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = nullptr; +IGRAPH_THREAD_LOCAL Communities* Probabilities::C = 0; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector1 = 0; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector2 = 0; +IGRAPH_THREAD_LOCAL int* Probabilities::id = 0; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = 0; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = 0; IGRAPH_THREAD_LOCAL int Probabilities::current_id = 0; Neighbor::Neighbor() { - next_community1 = nullptr; - previous_community1 = nullptr; - next_community2 = nullptr; - previous_community2 = nullptr; + next_community1 = 0; + previous_community1 = 0; + next_community2 = 0; + previous_community2 = 0; heap_index = -1; } Probabilities::~Probabilities() { - delete[] P; - delete[] vertices; + C->memory_used -= memory(); + if (P) { + delete[] P; + } + if (vertices) { + delete[] vertices; + } } Probabilities::Probabilities(int community) { @@ -92,7 +97,7 @@ Probabilities::Probabilities(int community) { int nb_vertices1 = 0; int nb_vertices2 = 0; - double initial_proba = 1. / static_cast(C->communities[community].size); + double initial_proba = 1. / double(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; @@ -153,7 +158,7 @@ Probabilities::Probabilities(int community) { if (nb_vertices1 > (G->nb_vertices / 2)) { P = new double[G->nb_vertices]; size = G->nb_vertices; - vertices = nullptr; + 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); @@ -179,6 +184,7 @@ Probabilities::Probabilities(int community) { } } } + C->memory_used += memory(); } Probabilities::Probabilities(int community1, int community2) { @@ -187,14 +193,14 @@ Probabilities::Probabilities(int community1, int community2) { Probabilities* P1 = C->communities[community1].P; Probabilities* P2 = C->communities[community2].P; - double w1 = C->communities[community1].size / static_cast(C->communities[community1].size + C->communities[community2].size); - double w2 = C->communities[community2].size / static_cast(C->communities[community1].size + C->communities[community2].size); + double w1 = double(C->communities[community1].size) / double(C->communities[community1].size + C->communities[community2].size); + double w2 = double(C->communities[community2].size) / double(C->communities[community1].size + C->communities[community2].size); if (P1->size == C->G->nb_vertices) { P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = nullptr; + vertices = 0; if (P2->size == C->G->nb_vertices) { // two full vectors for (int i = 0; i < C->G->nb_vertices; i++) { @@ -217,7 +223,7 @@ Probabilities::Probabilities(int community1, int community2) { if (P2->size == C->G->nb_vertices) { // P1 partial vector, P2 full vector P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = nullptr; + vertices = 0; int j = 0; for (int i = 0; i < P1->size; i++) { @@ -267,7 +273,7 @@ Probabilities::Probabilities(int community1, int community2) { if (nb_vertices1 > (C->G->nb_vertices / 2)) { P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = nullptr; + vertices = 0; for (int i = 0; i < C->G->nb_vertices; i++) { P[i] = 0.; } @@ -285,10 +291,12 @@ Probabilities::Probabilities(int community1, int community2) { } } } + + C->memory_used += memory(); } double Probabilities::compute_distance(const Probabilities* P2) const { - double r = 0.0; + double r = 0.; if (vertices) { if (P2->vertices) { // two partial vectors int i = 0; @@ -353,10 +361,18 @@ double Probabilities::compute_distance(const Probabilities* P2) const { return r; } +long Probabilities::memory() { + if (vertices) { + return (sizeof(Probabilities) + long(size) * (sizeof(double) + sizeof(int))); + } else { + return (sizeof(Probabilities) + long(size) * sizeof(double)); + } +} + Community::Community() { - P = nullptr; - first_neighbor = nullptr; - last_neighbor = nullptr; + P = 0; + first_neighbor = 0; + last_neighbor = 0; sub_community_of = -1; sub_communities[0] = -1; sub_communities[1] = -1; @@ -366,13 +382,17 @@ Community::Community() { } Community::~Community() { - delete P; + if (P) { + delete P; + } } Communities::Communities(Graph* graph, int random_walks_length, - igraph_matrix_int_t *pmerges, + long m, igraph_matrix_t *pmerges, igraph_vector_t *pmodularity) { + max_memory = m; + memory_used = 0; G = graph; merges = pmerges; mergeidx = 0; @@ -390,6 +410,7 @@ Communities::Communities(Graph* graph, int random_walks_length, 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; @@ -400,6 +421,12 @@ Communities::Communities(Graph* graph, int random_walks_length, // 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; @@ -425,15 +452,26 @@ Communities::Communities(Graph* graph, int random_walks_length, 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(double) + 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 == nullptr) { + 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++; */ @@ -460,6 +498,9 @@ Communities::~Communities() { delete[] members; delete[] communities; delete H; + if (min_delta_sigma) { + delete min_delta_sigma; + } delete[] Probabilities::tmp_vector1; delete[] Probabilities::tmp_vector2; @@ -468,6 +509,22 @@ Communities::~Communities() { delete[] Probabilities::vertices2; } +double Community::min_delta_sigma() { + double 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) { @@ -484,9 +541,9 @@ void Community::add_neighbor(Neighbor* N) { // add a new neighbor at the end of } else { first_neighbor = N; if (N->community1 == this_community) { - N->previous_community1 = nullptr; + N->previous_community1 = 0; } else { - N->previous_community2 = nullptr; + N->previous_community2 = 0; } } last_neighbor = N; @@ -536,19 +593,96 @@ 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, double new_delta_sigma) { - N->delta_sigma = new_delta_sigma; - H->update(N); + 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); + } + } + + double 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; @@ -577,11 +711,23 @@ void Communities::merge_communities(Neighbor* merge_N) { if (communities[c1].P) { delete communities[c1].P; - communities[c1].P = nullptr; + 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 = nullptr; + 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 @@ -723,6 +869,11 @@ void Communities::merge_communities(Neighbor* merge_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--; } @@ -733,12 +884,18 @@ double Communities::merge_nearest_communities() { 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; @@ -770,12 +927,18 @@ double Communities::merge_nearest_communities() { return d; } -double Communities::compute_delta_sigma(int community1, int community2) const { +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); diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h index 484079f7061..89c5fecedc8 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h @@ -59,6 +59,7 @@ #include "walktrap_graph.h" #include "walktrap_heap.h" +#include "igraph_community.h" #include "config.h" namespace igraph { @@ -83,8 +84,9 @@ class Probabilities { int* vertices; // the vertices corresponding to the stored probabilities, 0 if all the probabilities are stored double* 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 - explicit Probabilities(int community); // compute the probability vector of a community + 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 @@ -109,13 +111,15 @@ class Community { double internal_weight; // sum of the weight of the internal edges double 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 communities, -1 if no sub communities; + 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); + double min_delta_sigma(); // compute the minimal delta sigma among all the neighbors of this community Community(); // create an empty community ~Community(); // destructor @@ -123,11 +127,16 @@ class Community { class Communities { private: - igraph_matrix_int_t *merges; - igraph_integer_t mergeidx; + 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 @@ -141,18 +150,23 @@ class Communities { int nb_active_communities; // number of active communities Communities(Graph* G, int random_walks_length = 3, - igraph_matrix_int_t *merges = nullptr, - igraph_vector_t *modularity = nullptr); // Constructor + 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) const; // compute delta_sigma(c1,c2) + + 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, double new_delta_sigma); + + void manage_memory(); + }; } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp index 39830be363a..a428f18412b 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp @@ -54,12 +54,9 @@ // see readme.txt for more details #include "walktrap_graph.h" - #include "igraph_interface.h" - #include -#include -#include +#include // strlen using namespace std; @@ -74,23 +71,28 @@ bool operator<(const Edge& E1, const Edge& E2) { Vertex::Vertex() { degree = 0; - edges = nullptr; + edges = 0; total_weight = 0.; } Vertex::~Vertex() { - delete[] edges; + if (edges) { + delete[] edges; + } } Graph::Graph() { nb_vertices = 0; nb_edges = 0; - vertices = nullptr; + vertices = 0; + index = 0; total_weight = 0.; } Graph::~Graph () { - delete[] vertices; + if (vertices) { + delete[] vertices; + } } class Edge_list { @@ -110,11 +112,16 @@ class Edge_list { V2 = new int[1024]; W = new double[1024]; } - ~Edge_list() { - delete[] V1; - delete[] V2; - delete[] W; + if (V1) { + delete[] V1; + } + if (V2) { + delete[] V2; + } + if (W) { + delete[] W; + } } }; @@ -142,27 +149,28 @@ void Edge_list::add(int v1, int v2, double w) { size++; } -igraph_error_t Graph::convert_from_igraph(const igraph_t *graph, +int Graph::convert_from_igraph(const igraph_t *graph, const igraph_vector_t *weights) { Graph &G = *this; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - - // Refactoring the walktrap code to support larger graphs is pointless - // as running the algorithm on them would take an impractically long time. - if (no_of_nodes > INT_MAX || no_of_edges > INT_MAX) { - IGRAPH_ERROR("Graph too large for walktrap community detection.", IGRAPH_EINVAL); - } + 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 (igraph_integer_t i = 0; i < no_of_edges; i++) { - igraph_real_t w = weights ? VECTOR(*weights)[i] : 1.0; - EL.add(IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i), w); + 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 = no_of_nodes; + G.nb_vertices = max_vertex + 1; G.vertices = new Vertex[G.nb_vertices]; G.nb_edges = 0; G.total_weight = 0.0; @@ -177,8 +185,8 @@ igraph_error_t Graph::convert_from_igraph(const igraph_t *graph, } for (int i = 0; i < G.nb_vertices; i++) { - int deg = G.vertices[i].degree; - double w = (deg == 0) ? 1.0 : (G.vertices[i].total_weight / double(deg)); + 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; @@ -196,7 +204,7 @@ igraph_error_t Graph::convert_from_igraph(const igraph_t *graph, } for (int i = 0; i < G.nb_vertices; i++) { - /* Check for zero strength, as it may lead to crashes the in walktrap algorithm. + /* Check for zero strength, as it may lead to crashed in walktrap algorithm. * See https://github.com/igraph/igraph/pull/2043 */ if (G.vertices[i].total_weight == 0) { /* G.vertices will be destroyed by Graph::~Graph() */ @@ -221,5 +229,19 @@ igraph_error_t Graph::convert_from_igraph(const igraph_t *graph, return IGRAPH_SUCCESS; } +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/vendor/cigraph/src/community/walktrap/walktrap_graph.h b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h index 2347e8cff83..f0b82455a3a 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h @@ -89,10 +89,13 @@ class Graph { double 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 - igraph_error_t convert_from_igraph(const igraph_t *igraph, const igraph_vector_t *weights); + int convert_from_igraph(const igraph_t * igraph, + const igraph_vector_t *weights); }; } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp index dcb0518f18a..87904614126 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp @@ -92,7 +92,7 @@ void Neighbor_heap::move_down(int index) { Neighbor* Neighbor_heap::get_first() { if (size == 0) { - return nullptr; + return 0; } else { return H[0]; } @@ -127,6 +127,10 @@ void Neighbor_heap::update(Neighbor* N) { 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; @@ -137,6 +141,101 @@ Neighbor_heap::~Neighbor_heap() { delete[] H; } -bool Neighbor_heap::is_empty() const { +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(double))); +} + +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 double[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/vendor/cigraph/src/community/walktrap/walktrap_heap.h b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h index 90b4f31f69b..7df9c2e20ee 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h @@ -95,12 +95,38 @@ class Neighbor_heap { void update(Neighbor* N); // update a distance void remove(Neighbor* N); // remove a distance Neighbor* get_first(); // get the first item - bool is_empty() const; + long memory(); + bool is_empty(); - explicit Neighbor_heap(int max_size); + 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(); + + double* delta_sigma; // the delta_sigma of the stored communities + + Min_delta_sigma_heap(int max_size); + ~Min_delta_sigma_heap(); +}; + } } /* end of namespaces */ diff --git a/src/vendor/cigraph/src/config.h.in b/src/vendor/cigraph/src/config.h.in index 9c5df6d90d6..d6f1ea62f2a 100644 --- a/src/vendor/cigraph/src/config.h.in +++ b/src/vendor/cigraph/src/config.h.in @@ -1,23 +1,19 @@ -#ifndef IGRAPH_PRIVATE_CONFIG_H -#define IGRAPH_PRIVATE_CONFIG_H - -#include "igraph_config.h" - +#ifndef IGRAPH_CONFIG_H +#define IGRAPH_CONFIG_H + +#cmakedefine HAVE_EXPM1 1 +#cmakedefine HAVE_FMIN 1 +#cmakedefine HAVE_FINITE 1 +#cmakedefine HAVE_ISFINITE 1 +#cmakedefine HAVE_LOG2 1 +#cmakedefine HAVE_LOG1P 1 +#cmakedefine HAVE_RINT 1 +#cmakedefine HAVE_RINTF 1 +#cmakedefine HAVE_ROUND 1 +#cmakedefine HAVE_STPCPY 1 #cmakedefine HAVE_STRCASECMP 1 -#cmakedefine HAVE_STRNCASECMP 1 #cmakedefine HAVE__STRICMP 1 -#cmakedefine HAVE__STRNICMP 1 #cmakedefine HAVE_STRDUP 1 -#cmakedefine HAVE_STRNDUP 1 -#cmakedefine HAVE_USELOCALE 1 -#cmakedefine HAVE_XLOCALE 1 -#cmakedefine HAVE__CONFIGTHREADLOCALE 1 - -#cmakedefine HAVE_BUILTIN_OVERFLOW 1 - -#cmakedefine HAVE__UMUL128 1 -#cmakedefine HAVE___UMULH 1 -#cmakedefine HAVE___UINT128_T 1 #cmakedefine HAVE_GLPK 1 #cmakedefine HAVE_LIBXML 1 @@ -30,4 +26,10 @@ #define IGRAPH_F77_SAVE static @TLS_KEYWORD@ #define IGRAPH_THREAD_LOCAL @TLS_KEYWORD@ +#define PACKAGE_VERSION "@PACKAGE_VERSION@" +#define PACKAGE_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ +#define PACKAGE_VERSION_MINOR @PACKAGE_VERSION_MINOR@ +#define PACKAGE_VERSION_PATCH @PACKAGE_VERSION_PATCH@ +#define PACKAGE_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" + #endif diff --git a/src/vendor/cigraph/src/connectivity/cohesive_blocks.c b/src/vendor/cigraph/src/connectivity/cohesive_blocks.c index dadc73e85c2..aa4fa9f4341 100644 --- a/src/vendor/cigraph/src/connectivity/cohesive_blocks.c +++ b/src/vendor/cigraph/src/connectivity/cohesive_blocks.c @@ -36,13 +36,25 @@ #include "core/interruption.h" static void igraph_i_cohesive_blocks_free_graphs(igraph_vector_ptr_t *ptr) { - igraph_integer_t i, n = igraph_vector_ptr_size(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(VECTOR(*ptr)[i]); /* also sets it to NULL */ + IGRAPH_FREE(VECTOR(*ptr)[i]); + } + } +} + +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); } } } @@ -53,23 +65,23 @@ static void igraph_i_cohesive_blocks_free_graphs(igraph_vector_ptr_t *ptr) { * all neighboring components. */ -static igraph_error_t igraph_i_cb_components(igraph_t *graph, +static int igraph_i_cb_components(igraph_t *graph, const igraph_vector_bool_t *excluded, - igraph_vector_int_t *components, - igraph_integer_t *no, + igraph_vector_long_t *components, + long int *no, /* working area follows */ - igraph_vector_int_t *compid, - igraph_dqueue_int_t *Q, - igraph_vector_int_t *neis) { + igraph_vector_long_t *compid, + igraph_dqueue_t *Q, + igraph_vector_t *neis) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; - igraph_integer_t cno = 0; + long int no_of_nodes = igraph_vcount(graph); + long int i; + long int cno = 0; - igraph_vector_int_clear(components); - igraph_dqueue_int_clear(Q); - IGRAPH_CHECK(igraph_vector_int_resize(compid, no_of_nodes)); - igraph_vector_int_null(compid); + 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++) { @@ -80,56 +92,56 @@ static igraph_error_t igraph_i_cb_components(igraph_t *graph, continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(components, i)); + IGRAPH_CHECK(igraph_dqueue_push(Q, i)); + IGRAPH_CHECK(igraph_vector_long_push_back(components, i)); VECTOR(*compid)[i] = ++cno; - while (!igraph_dqueue_int_empty(Q)) { - igraph_integer_t node = igraph_dqueue_int_pop(Q); - igraph_integer_t j, n; + 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_int_size(neis); + n = igraph_vector_size(neis); for (j = 0; j < n; j++) { - igraph_integer_t v = VECTOR(*neis)[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_int_push_back(components, v)); + 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_int_push_back(components, v)); - IGRAPH_CHECK(igraph_dqueue_int_push(Q, v)); + IGRAPH_CHECK(igraph_vector_long_push_back(components, v)); + IGRAPH_CHECK(igraph_dqueue_push(Q, v)); } } } - } /* while !igraph_dqueue_int_empty */ + } /* while !igraph_dqueue_empty */ - IGRAPH_CHECK(igraph_vector_int_push_back(components, -1)); + IGRAPH_CHECK(igraph_vector_long_push_back(components, -1)); } /* for ik. Thus a hiearchy of vertex subsets - * is found, with the entire graph G at its root. - * - * - * This function implements cohesive blocking and - * calculates the complete cohesive block hierarchy of a graph. - * - * - * See the following reference for details: - * - * - * J. Moody and D. R. White. Structural + * 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. - * https://doi.org/10.2307/3088904 + * + * 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 - * list of integers vectors; the cohesive blocks will be stored here. - * Each block is encoded with a vector of type \ref igraph_vector_int_t that - * contains the vertex IDs of the block. + * 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 vector list. + * 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 vector list) of its + * 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. + * -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 + * here as an igraph graph. The vertex ids correspond to the order * of the blocks in the \p blocks vector. * \return Error code. * @@ -190,14 +195,14 @@ static igraph_bool_t igraph_i_cb_isin(const igraph_vector_int_t *needle, * \example examples/simple/cohesive_blocks.c */ -igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, - igraph_vector_int_list_t *blocks, - igraph_vector_int_t *cohesion, - igraph_vector_int_t *parent, +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 + 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. @@ -212,84 +217,90 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, Qptr is an integer and points to the next graph to work on. */ - /* In theory, Q could be an igraph_graph_list_t; however, in that case - * we would not be able to pop off graphs from the front of the list as - * all elements of an igraph_graph_list_t are expected to be initialized, - * valid graphs. That's why we use an igraph_vector_ptr_t instead. */ - igraph_vector_ptr_t Q; - igraph_vector_int_list_t Qmapping; - igraph_vector_int_t Qparent; - igraph_vector_int_t Qcohesion; + igraph_vector_ptr_t Qmapping; + igraph_vector_long_t Qparent; + igraph_vector_long_t Qcohesion; igraph_vector_bool_t Qcheck; - igraph_integer_t Qptr = 0; + long int Qptr = 0; igraph_integer_t conn; igraph_bool_t is_simple; igraph_t *graph_copy; - igraph_vector_int_list_t separators; - igraph_vector_int_t compvertices; - igraph_vector_int_t components; - igraph_vector_int_t newmapping; + igraph_vector_ptr_t separators; + igraph_vector_t compvertices; + igraph_vector_long_t components; igraph_vector_bool_t marked; - igraph_vector_int_t compid; - igraph_dqueue_int_t bfsQ; - igraph_vector_int_t neis; + 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_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_ERROR("Cohesive blocking only works on simple graphs", IGRAPH_EINVAL); } - IGRAPH_STATUS("Starting cohesive block calculation.\n", NULL); + IGRAPH_STATUS("Starting cohesive block calculation.\n", 0); if (blocks) { - igraph_vector_int_list_clear(blocks); + igraph_vector_ptr_clear(blocks); } if (cohesion) { - igraph_vector_int_clear(cohesion); + igraph_vector_clear(cohesion); } if (parent) { - igraph_vector_int_clear(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_VECTOR_INT_LIST_INIT_FINALLY(&Qmapping, 1); + 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_VECTOR_INT_INIT_FINALLY(&Qparent, 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&Qcohesion, 1); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&Qcheck, 1); + IGRAPH_CHECK(igraph_vector_long_init(&Qparent, 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &Qparent); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&separators, 0); + IGRAPH_CHECK(igraph_vector_long_init(&Qcohesion, 1)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &Qcohesion); - IGRAPH_VECTOR_INT_INIT_FINALLY(&compvertices, 0); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&marked, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&bfsQ, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsQ); - IGRAPH_VECTOR_INT_INIT_FINALLY(&compid, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&components, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newmapping, 0); + 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); - IGRAPH_CHECK_OOM(graph_copy, "Insufficient memory for cohesive blocking."); - + 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=*/ true)); + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ 1)); VECTOR(Qcohesion)[0] = conn; VECTOR(Qcheck)[0] = 0; @@ -297,12 +308,12 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, while (Qptr < igraph_vector_ptr_size(&Q)) { igraph_t *mygraph = VECTOR(Q)[Qptr]; igraph_bool_t mycheck = VECTOR(Qcheck)[Qptr]; - igraph_integer_t mynodes = igraph_vcount(mygraph); - igraph_integer_t i, nsep; - igraph_integer_t no, kept = 0; - igraph_integer_t cptr = 0; - igraph_integer_t nsepv = 0; - igraph_bool_t addedsep = false; + 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)); @@ -310,7 +321,8 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, /* Get the separators */ IGRAPH_CHECK(igraph_minimum_size_separators(mygraph, &separators)); - nsep = igraph_vector_int_list_size(&separators); + IGRAPH_FINALLY(igraph_i_cohesive_blocks_free_vectors, &separators); + nsep = igraph_vector_ptr_size(&separators); IGRAPH_STATUSF((" %li separators,", 0, nsep)); @@ -318,13 +330,13 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_bool_resize(&marked, mynodes)); igraph_vector_bool_null(&marked); for (i = 0; i < nsep; i++) { - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&separators, i); - igraph_integer_t j, n = igraph_vector_int_size(v); + igraph_vector_t *v = VECTOR(separators)[i]; + long int j, n = igraph_vector_size(v); for (j = 0; j < n; j++) { - igraph_integer_t vv = VECTOR(*v)[j]; + long int vv = (long int) VECTOR(*v)[j]; if (!VECTOR(marked)[vv]) { nsepv++; - VECTOR(marked)[vv] = true; + VECTOR(marked)[vv] = 1; } } } @@ -343,38 +355,46 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, addedsep = 1; for (i = 0; i < mynodes; i++) { if (VECTOR(marked)[i]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&components, i)); + IGRAPH_CHECK(igraph_vector_long_push_back(&components, i)); } } - IGRAPH_CHECK(igraph_vector_int_push_back(&components, -1)); + 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_int_clear(&compvertices); + igraph_vector_clear(&compvertices); - while (true) { - igraph_integer_t v = VECTOR(components)[cptr++]; + while (1) { + long int v = VECTOR(components)[cptr++]; if (v < 0) { break; } - IGRAPH_CHECK(igraph_vector_int_push_back(&compvertices, v)); + 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); - IGRAPH_CHECK_OOM(newgraph, "Insufficient memory for cohesive blocking."); + 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=*/ NULL, - /*invmap=*/ &newmapping)); + 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(), @@ -384,17 +404,20 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, kept++; IGRAPH_CHECK(igraph_vector_ptr_push_back(&Q, newgraph)); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(&Qmapping, &newmapping)); + 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_int_push_back(&Qcohesion, newconn)); - IGRAPH_CHECK(igraph_vector_int_push_back(&Qparent, Qptr)); + 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_FINALLY_CLEAN(2); + igraph_vector_destroy(newmapping); + igraph_free(newmapping); + IGRAPH_FINALLY_CLEAN(4); } } @@ -402,34 +425,35 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, igraph_destroy(mygraph); igraph_free(mygraph); - VECTOR(Q)[Qptr] = NULL; + VECTOR(Q)[Qptr] = 0; + igraph_i_cohesive_blocks_free_vectors(&separators); + IGRAPH_FINALLY_CLEAN(1); Qptr++; } - igraph_vector_int_destroy(&newmapping); - igraph_vector_int_destroy(&components); - igraph_vector_int_destroy(&compid); - igraph_dqueue_int_destroy(&bfsQ); - igraph_vector_int_destroy(&neis); + 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_int_destroy(&compvertices); - igraph_vector_int_list_destroy(&separators); - IGRAPH_FINALLY_CLEAN(8); + igraph_vector_destroy(&compvertices); + igraph_vector_ptr_destroy(&separators); + IGRAPH_FINALLY_CLEAN(7); if (blocks || cohesion || parent || block_tree) { - igraph_integer_t noblocks = Qptr, badblocks = 0; + igraph_integer_t noblocks = (igraph_integer_t) Qptr, badblocks = 0; igraph_vector_bool_t removed; - igraph_integer_t i, resptr = 0; - igraph_vector_int_t rewritemap; + 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_int_init(&rewritemap, noblocks)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &rewritemap); + IGRAPH_CHECK(igraph_vector_long_init(&rewritemap, noblocks)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &rewritemap); for (i = 1; i < noblocks; i++) { - igraph_integer_t p = VECTOR(Qparent)[i]; + long int p = VECTOR(Qparent)[i]; while (VECTOR(removed)[p]) { p = VECTOR(Qparent)[p]; } @@ -441,19 +465,16 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, /* Rewrite the mappings */ for (i = 1; i < Qptr; i++) { - igraph_integer_t j, n, p = VECTOR(Qparent)[i]; - igraph_vector_int_t *mapping, *pmapping; + 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 (p == 0) { + if (!pmapping) { continue; } - - mapping = igraph_vector_int_list_get_ptr(&Qmapping, i); - pmapping = igraph_vector_int_list_get_ptr(&Qmapping, p); - - n = igraph_vector_int_size(mapping); for (j = 0; j < n; j++) { - igraph_integer_t v = VECTOR(*mapping)[j]; + long int v = (long int) VECTOR(*mapping)[j]; VECTOR(*mapping)[j] = VECTOR(*pmapping)[v]; } } @@ -462,20 +483,20 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, not ensured that the found blocks are not subsets of each other. We check this now. */ for (i = 1; i < noblocks; i++) { - igraph_integer_t j, ic; - igraph_vector_int_t *ivec; + long int j, ic; + igraph_vector_t *ivec; if (!VECTOR(Qcheck)[i] || VECTOR(removed)[i]) { continue; } - ivec = igraph_vector_int_list_get_ptr(&Qmapping, i); + ivec = VECTOR(Qmapping)[i]; ic = VECTOR(Qcohesion)[i]; for (j = 1; j < noblocks; j++) { - igraph_vector_int_t *jvec; - igraph_integer_t jc; + igraph_vector_t *jvec; + long int jc; if (j == i || !VECTOR(Qcheck)[j] || VECTOR(removed)[j]) { continue; } - jvec = igraph_vector_int_list_get_ptr(&Qmapping, j); + jvec = VECTOR(Qmapping)[j]; jc = VECTOR(Qcohesion)[j]; if (igraph_i_cb_isin(ivec, jvec) && jc >= ic) { badblocks++; @@ -488,13 +509,13 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, noblocks -= badblocks; if (blocks) { - IGRAPH_CHECK(igraph_vector_int_list_resize(blocks, noblocks)); + IGRAPH_CHECK(igraph_vector_ptr_resize(blocks, noblocks)); } if (cohesion) { - IGRAPH_CHECK(igraph_vector_int_resize(cohesion, noblocks)); + IGRAPH_CHECK(igraph_vector_resize(cohesion, noblocks)); } if (parent) { - IGRAPH_CHECK(igraph_vector_int_resize(parent, noblocks)); + IGRAPH_CHECK(igraph_vector_resize(parent, noblocks)); } for (i = 0; i < Qptr; i++) { @@ -509,7 +530,7 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, VECTOR(*cohesion)[resptr] = VECTOR(Qcohesion)[i]; } if (parent || block_tree) { - igraph_integer_t p = VECTOR(Qparent)[i]; + long int p = VECTOR(Qparent)[i]; while (p >= 0 && VECTOR(removed)[p]) { p = VECTOR(Qparent)[p]; } @@ -522,31 +543,28 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, } } if (blocks) { - IGRAPH_CHECK( - igraph_vector_int_update( - igraph_vector_int_list_get_ptr(blocks, resptr), - igraph_vector_int_list_get_ptr(&Qmapping, i) - ) - ); - igraph_vector_int_clear(igraph_vector_int_list_get_ptr(&Qmapping, i)); + VECTOR(*blocks)[resptr] = VECTOR(Qmapping)[i]; + VECTOR(Qmapping)[i] = 0; } resptr++; } /* Plus the original graph */ if (blocks) { - igraph_integer_t num_vertices = igraph_vcount(graph); - igraph_vector_int_t *orig = igraph_vector_int_list_get_ptr(blocks, 0); - IGRAPH_CHECK(igraph_vector_int_resize(orig, num_vertices)); - for (i = 0; i < num_vertices; i++) { - VECTOR(*orig)[i] = i; + 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_int_t edges; - igraph_integer_t eptr = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, noblocks * 2 - 2); + 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; @@ -557,26 +575,28 @@ igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, IGRAPH_CHECK(igraph_create(block_tree, &edges, noblocks, IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&rewritemap); + igraph_vector_long_destroy(&rewritemap); igraph_vector_bool_destroy(&removed); IGRAPH_FINALLY_CLEAN(2); } igraph_vector_bool_destroy(&Qcheck); - igraph_vector_int_destroy(&Qcohesion); - igraph_vector_int_destroy(&Qparent); - igraph_vector_int_list_destroy(&Qmapping); + 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(2); /* + the elements of Q, they were already destroyed */ + IGRAPH_FINALLY_CLEAN(3); /* + the elements of Q, they were + already destroyed */ IGRAPH_STATUS("Cohesive blocking done.\n", 0); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/connectivity/components.c b/src/vendor/cigraph/src/connectivity/components.c index 34592300157..7de37d110e8 100644 --- a/src/vendor/cigraph/src/connectivity/components.c +++ b/src/vendor/cigraph/src/connectivity/components.c @@ -27,6 +27,7 @@ #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" @@ -35,32 +36,17 @@ #include "core/interruption.h" #include "operators/subgraph.h" -static igraph_error_t igraph_i_connected_components_weak( - const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no -); -static igraph_error_t igraph_i_connected_components_strong( - const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no -); +#include -/** - * \ingroup structural - * \function igraph_clusters - * \brief Calculates the (weakly or strongly) connected components in a graph (deprecated alias). - * - * \deprecated-by igraph_connected_components 0.10 - */ +static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no); -igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no, - igraph_connectedness_t mode) { - return igraph_connected_components(graph, membership, csize, no, mode); -} +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_connected_components + * \function igraph_clusters * \brief Calculates the (weakly or strongly) connected components in a graph. * * \param graph The graph object to analyze. @@ -89,137 +75,130 @@ igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membe * edges in the graph. */ -igraph_error_t igraph_connected_components( - const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no, igraph_connectedness_t mode -) { +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_connected_components_weak(graph, membership, csize, no); + return igraph_i_clusters_weak(graph, membership, csize, no); } else if (mode == IGRAPH_STRONG) { - return igraph_i_connected_components_strong(graph, membership, csize, no); + return igraph_i_clusters_strong(graph, membership, csize, no); } - IGRAPH_ERROR("Cannot calculate connected components.", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL); } -static igraph_error_t igraph_i_connected_components_weak( - const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no -) { +static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - bool *already_added; - igraph_integer_t first_node, act_cluster_size = 0, no_of_clusters = 0; + 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_int_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - igraph_integer_t i; - igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; + long int i; + igraph_vector_t neis = IGRAPH_VECTOR_NULL; - already_added = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for calculating weakly connected components."); + 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_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_int_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); } if (csize) { - igraph_vector_int_clear(csize); + igraph_vector_clear(csize); } /* The algorithm */ for (first_node = 0; first_node < no_of_nodes; ++first_node) { - if (already_added[first_node]) { + if (already_added[first_node] == 1) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[first_node] = true; + already_added[first_node] = 1; act_cluster_size = 1; if (membership) { - VECTOR(*membership)[first_node] = no_of_clusters; + VECTOR(*membership)[first_node] = no_of_clusters - 1; } - IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node)); - - while ( !igraph_dqueue_int_empty(&q) ) { - igraph_integer_t act_node = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, act_node, IGRAPH_ALL)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); - for (i = 0; i < nei_count; i++) { - igraph_integer_t neighbor = VECTOR(neis)[i]; - if (already_added[neighbor]) { + 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_int_push(&q, neighbor)); - already_added[neighbor] = true; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + already_added[neighbor] = 1; act_cluster_size++; if (membership) { - VECTOR(*membership)[neighbor] = no_of_clusters; + VECTOR(*membership)[neighbor] = no_of_clusters - 1; } } } - no_of_clusters++; if (csize) { - IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_cluster_size)); + IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); } } /* Cleaning up */ if (no) { - *no = no_of_clusters; + *no = (igraph_integer_t) no_of_clusters - 1; } - /* Clean up */ IGRAPH_FREE(already_added); - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&neis); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); - /* Update cache */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_clusters == 1); - - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_connected_components_strong( - const igraph_t *graph, igraph_vector_int_t *membership, - igraph_vector_int_t *csize, igraph_integer_t *no -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; +static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, + igraph_vector_t *csize, igraph_integer_t *no) { - igraph_integer_t i, n, num_seen; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t next_nei = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_clusters = 0; - igraph_integer_t act_cluster_size; + long int i, n, num_seen; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - igraph_vector_int_t out = IGRAPH_VECTOR_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_INT_INIT_FINALLY(&next_nei, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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_int_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); } - IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); - igraph_vector_int_null(&out); + igraph_vector_null(&out); if (csize) { - igraph_vector_int_clear(csize); + igraph_vector_clear(csize); } IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); @@ -234,24 +213,25 @@ static igraph_error_t igraph_i_connected_components_strong( continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t act_node = igraph_dqueue_int_back(&q); + 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 */ - igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; + 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_int_push(&q, neighbor)); + 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_int_push_back(&out, act_node)); - igraph_dqueue_int_pop_back(&q); + IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); + igraph_dqueue_pop_back(&q); num_seen++; if (num_seen % 10000 == 0) { @@ -275,11 +255,11 @@ static igraph_error_t igraph_i_connected_components_strong( /* OK, we've the 'out' values for the nodes, let's use them in decreasing order with the help of a heap */ - igraph_vector_int_null(&next_nei); /* mark already added vertices */ + igraph_vector_null(&next_nei); /* mark already added vertices */ num_seen = 0; - while (!igraph_vector_int_empty(&out)) { - igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); + while (!igraph_vector_empty(&out)) { + long int grandfather = (long int) igraph_vector_pop_back(&out); if (VECTOR(next_nei)[grandfather] != 0) { continue; @@ -287,9 +267,9 @@ static igraph_error_t igraph_i_connected_components_strong( VECTOR(next_nei)[grandfather] = 1; act_cluster_size = 1; if (membership) { - VECTOR(*membership)[grandfather] = no_of_clusters; + VECTOR(*membership)[grandfather] = no_of_clusters - 1; } - IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); + IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); num_seen++; if (num_seen % 10000 == 0) { @@ -299,20 +279,20 @@ static igraph_error_t igraph_i_connected_components_strong( IGRAPH_ALLOW_INTERRUPTION(); } - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q); + 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++) { - igraph_integer_t neighbor = VECTOR(*tmp)[i]; + long int neighbor = (long int) VECTOR(*tmp)[i]; if (VECTOR(next_nei)[neighbor] != 0) { continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); VECTOR(next_nei)[neighbor] = 1; act_cluster_size++; if (membership) { - VECTOR(*membership)[neighbor] = no_of_clusters; + VECTOR(*membership)[neighbor] = no_of_clusters - 1; } num_seen++; @@ -327,33 +307,28 @@ static igraph_error_t igraph_i_connected_components_strong( no_of_clusters++; if (csize) { - IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_cluster_size)); + IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); } } IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); if (no) { - *no = no_of_clusters; + *no = (igraph_integer_t) no_of_clusters - 1; } - /* Clean up */ + /* Clean up, return */ + igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&out); - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&next_nei); + igraph_vector_destroy(&out); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&next_nei); IGRAPH_FINALLY_CLEAN(4); - /* Update cache */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_clusters == 1); - if (no_of_clusters == 1) { - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, 1); - } - - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); +static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); /** * \ingroup structural @@ -372,12 +347,6 @@ static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_boo * argument that led us to change the definition: * https://github.com/igraph/igraph/issues/1539 * - * - * The return value of this function is cached in the graph itself, separately - * for weak and strong connectivity. Calling the function multiple times with - * no modifications to the graph in between will return a cached value in O(1) - * time. - * * \param graph The graph object to analyze. * \param res Pointer to a logical variable, the result will be stored * here. @@ -394,112 +363,101 @@ static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_boo * plus the number of edges in the graph. */ -igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, +int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, igraph_connectedness_t mode) { - igraph_cached_property_t prop; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no; + long int no_of_nodes = igraph_vcount(graph); - if (!igraph_is_directed(graph)) { - mode = IGRAPH_WEAK; + 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; } - switch (mode) { - case IGRAPH_WEAK: - prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED; - break; - - case IGRAPH_STRONG: - prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED; - break; - - default: - IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); + if (no_of_nodes == 1) { + *res = 1; + return IGRAPH_SUCCESS; } - IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res); + 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; - 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 = false; - } else if (no_of_nodes == 1) { - *res = true; - } else if (mode == IGRAPH_WEAK) { - IGRAPH_CHECK(igraph_is_connected_weak(graph, res)); - } else { /* mode == IGRAPH_STRONG */ /* 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 = false; - } else { - IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no)); - *res = (no == 1); + *res = 0; + return IGRAPH_SUCCESS; } - } - /* Cache updates are done in igraph_i_connected_components_strong() and - * igraph_is_connected_weak() because those might be called from other - * places and we want to make use of the caching if so */ + retval = igraph_i_clusters_strong(graph, NULL, NULL, &no); + *res = (no == 1); + return retval; + } - return IGRAPH_SUCCESS; + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); } -static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph), no_of_edges = igraph_ecount(graph); - igraph_integer_t added_count; - bool *already_added; - igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; +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 = false; - goto exit; + *res = 0; + return IGRAPH_SUCCESS; } /* A connected graph has at least |V| - 1 edges. */ if (no_of_edges < no_of_nodes - 1) { - *res = false; - goto exit; + *res = 0; + return IGRAPH_SUCCESS; } - already_added = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for computing weakly connected components."); + 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_INT_INIT_FINALLY(&q, 10); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 10); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); /* Try to find at least two clusters */ - already_added[0] = true; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + already_added[0] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); added_count = 1; - while ( !igraph_dqueue_int_empty(&q)) { + while ( !igraph_dqueue_empty(&q)) { IGRAPH_ALLOW_INTERRUPTION(); - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + long int actnode = (long int) igraph_dqueue_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, IGRAPH_ALL)); + long int nei_count = igraph_vector_size(&neis); - for (igraph_integer_t i = 0; i < nei_count; i++) { - igraph_integer_t neighbor = VECTOR(neis)[i]; + 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_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); added_count++; - already_added[neighbor] = true; + 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_int_clear(&q); + igraph_dqueue_clear(&q); break; } } @@ -509,42 +467,31 @@ static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_boo *res = (added_count == no_of_nodes); IGRAPH_FREE(already_added); - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&neis); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); -exit: - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res); - if (igraph_is_directed(graph) && *res == 0) { - /* If the graph is not weakly connected, it is not strongly connected - * either so we can also cache that */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res); - } - return IGRAPH_SUCCESS; } /** * \function igraph_decompose_destroy - * \brief Frees the contents of a pointer vector holding graphs. + * \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. Use \ref igraph_vector_ptr_destroy() to destroy - * \p complist. + * \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 graphs to destroy. + * \param complist The list of graph components, as returned by + * \ref igraph_decompose(). * - * Time complexity: O(n), n is the number of items. - * - * \deprecated 0.10.0 + * Time complexity: O(c), c is the number of components. */ void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { - igraph_integer_t i, n; - - n = igraph_vector_ptr_size(complist); - for (i = 0; i < n; i++) { + 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]); @@ -552,26 +499,29 @@ void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { } } -static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, - igraph_graph_list_t *components, - igraph_integer_t maxcompno, igraph_integer_t minelements); +static int igraph_i_decompose_weak(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements); -static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, - igraph_graph_list_t *components, - igraph_integer_t maxcompno, igraph_integer_t 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 Decomposes a graph into connected components. + * \brief Decompose a graph into connected components. * - * Creates a 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 when there is only a single component in the original graph. + * 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 list of graphs will contain the individual components. - * It should be initialized before calling this function and will be resized - * to hold the graphs. + * \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 @@ -593,9 +543,9 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, * \example examples/simple/igraph_decompose.c */ -igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, +int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, igraph_connectedness_t mode, - igraph_integer_t maxcompno, igraph_integer_t minelements) { + 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) { @@ -605,37 +555,41 @@ igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *comp IGRAPH_ERROR("Cannot decompose graph", IGRAPH_EINVAL); } -static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, - igraph_graph_list_t *components, - igraph_integer_t maxcompno, igraph_integer_t minelements) { +static int igraph_i_decompose_weak(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements) { - igraph_integer_t actstart; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t resco = 0; /* number of graphs created so far */ - bool *already_added; - igraph_dqueue_int_t q; - igraph_vector_int_t verts; - igraph_vector_int_t neis; - igraph_vector_int_t vids_old2new; - igraph_integer_t i; - igraph_t newg; + 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 = IGRAPH_INTEGER_MAX; + maxcompno = LONG_MAX; } - igraph_graph_list_clear(components); + 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, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for decomponsing graph into connected components."); + 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_DQUEUE_INT_INIT_FINALLY(&q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); + 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, @@ -650,47 +604,49 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, } IGRAPH_ALLOW_INTERRUPTION(); - igraph_vector_int_clear(&verts); + igraph_vector_clear(&verts); /* add the node itself */ - already_added[actstart] = true; - IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart)); + 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_int_empty(&q) ) { + while (!igraph_dqueue_empty(&q) ) { /* pop from the queue of this component */ - igraph_integer_t actvert = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actvert, IGRAPH_ALL)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); + 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 < nei_count; i++) { - igraph_integer_t neighbor = VECTOR(neis)[i]; - if (already_added[neighbor]) { + 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] = true; + already_added[neighbor] = 1; /* recursion: append neighbor to the queues */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); } } /* ok, we have a component */ - if (igraph_vector_int_size(&verts) < minelements) { + 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), + graph, newg, igraph_vss_vector(&verts), IGRAPH_SUBGRAPH_AUTO, &vids_old2new, /* invmap = */ 0, /* map_is_prepared = */ 1 )); - IGRAPH_FINALLY(igraph_destroy, &newg); - IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); - IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ resco++; /* vids_old2new does not have to be cleaned up here; since we are doing @@ -700,57 +656,58 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, } /* for actstart++ */ - igraph_vector_int_destroy(&vids_old2new); - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&verts); - igraph_dqueue_int_destroy(&q); + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&verts); + igraph_dqueue_destroy(&q); IGRAPH_FREE(already_added); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FINALLY_CLEAN(6); /* + components */ - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, - igraph_graph_list_t *components, - igraph_integer_t maxcompno, igraph_integer_t minelements) { +static int igraph_i_decompose_strong(const igraph_t *graph, + igraph_vector_ptr_t *components, + long int maxcompno, long int minelements) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); /* this is a heap used twice for checking what nodes have * been counted already */ - igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; + igraph_vector_t next_nei = IGRAPH_VECTOR_NULL; - igraph_integer_t i, n, num_seen; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + long int i, n, num_seen; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - igraph_integer_t no_of_clusters = 0; + long int no_of_clusters = 0; - igraph_vector_int_t out = IGRAPH_VECTOR_NULL; + igraph_vector_t out = IGRAPH_VECTOR_NULL; const igraph_vector_int_t* tmp; igraph_adjlist_t adjlist; - igraph_vector_int_t verts; - igraph_vector_int_t vids_old2new; - igraph_t newg; + igraph_vector_t verts; + igraph_vector_t vids_old2new; + igraph_t *newg; if (maxcompno < 0) { - maxcompno = IGRAPH_INTEGER_MAX; + maxcompno = LONG_MAX; } - igraph_graph_list_clear(components); + igraph_vector_ptr_clear(components); + IGRAPH_FINALLY(igraph_decompose_destroy, components); /* The result */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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_int_reserve(&out, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); - igraph_vector_int_null(&out); + igraph_vector_null(&out); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); @@ -775,13 +732,13 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, } /* add this node to the queue for this component */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_push(&q, i)); /* consume the tree from this node ("root") recursively * until there is no more */ - while (!igraph_dqueue_int_empty(&q)) { + while (!igraph_dqueue_empty(&q)) { /* this looks up but does NOT consume the queue */ - igraph_integer_t act_node = igraph_dqueue_int_back(&q); + long int act_node = (long int) igraph_dqueue_back(&q); /* get all neighbors of this node */ tmp = igraph_adjlist_get(&adjlist, act_node); @@ -793,17 +750,18 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { /* we've already met this vertex but it has more children */ - igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; + 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_int_push(&q, neighbor)); + 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_int_push_back(&out, act_node)); + IGRAPH_CHECK(igraph_vector_push_back(&out, act_node)); /* this consumes the queue, since there's nowhere to go */ - igraph_dqueue_int_pop_back(&q); + igraph_dqueue_pop_back(&q); num_seen++; if (num_seen % 10000 == 0) { @@ -827,13 +785,13 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, /* 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_int_null(&next_nei); /* mark already added vertices */ + igraph_vector_null(&next_nei); /* mark already added vertices */ /* number of components built */ num_seen = 0; - while (!igraph_vector_int_empty(&out) && no_of_clusters < maxcompno) { + while (!igraph_vector_empty(&out) && no_of_clusters < maxcompno) { /* consume the vector from the last element */ - igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); + long int grandfather = (long int) igraph_vector_pop_back(&out); /* been here, done that * NOTE: next_nei is initialized as [0, 0, ...] */ @@ -842,14 +800,14 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, } /* collect all the members of this component */ - igraph_vector_int_clear(&verts); + igraph_vector_clear(&verts); /* this node is gone for any future components */ VECTOR(next_nei)[grandfather] = 1; /* add to component */ - IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); + IGRAPH_CHECK(igraph_vector_push_back(&verts, grandfather)); + IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); num_seen++; if (num_seen % 10000 == 0) { @@ -859,21 +817,21 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, IGRAPH_ALLOW_INTERRUPTION(); } - while (!igraph_dqueue_int_empty(&q)) { + while (!igraph_dqueue_empty(&q)) { /* consume the queue from this node */ - igraph_integer_t act_node = igraph_dqueue_int_pop_back(&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++) { - igraph_integer_t neighbor = VECTOR(*tmp)[i]; + long int neighbor = (long int) VECTOR(*tmp)[i]; if (VECTOR(next_nei)[neighbor] != 0) { continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); VECTOR(next_nei)[neighbor] = 1; /* add to component */ - IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); + IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); num_seen++; if (num_seen % 10000 == 0) { @@ -886,27 +844,29 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, } /* ok, we have a component */ - if (igraph_vector_int_size(&verts) < minelements) { + 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), + graph, newg, igraph_vss_vector(&verts), IGRAPH_SUBGRAPH_AUTO, &vids_old2new, /* invmap = */ 0, /* map_is_prepared = */ 1 )); - IGRAPH_FINALLY(igraph_destroy, &newg); - IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); - IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ /* vids_old2new has to be cleaned up here because a vertex may appear * in multiple strongly connected components. Simply calling - * igraph_vector_int_fill() would be an O(n) operation where n is the number + * 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_int_size(&verts); + n = igraph_vector_size(&verts); for (i = 0; i < n; i++) { - VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0; + VECTOR(vids_old2new)[(igraph_integer_t) VECTOR(verts)[i]] = 0; } no_of_clusters++; @@ -916,26 +876,25 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, /* Clean up, return */ - igraph_vector_int_destroy(&vids_old2new); - igraph_vector_int_destroy(&verts); + igraph_vector_destroy(&vids_old2new); + igraph_vector_destroy(&verts); igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&out); - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&next_nei); - IGRAPH_FINALLY_CLEAN(6); + igraph_vector_destroy(&out); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(7); /* + components */ - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_articulation_points - * \brief Finds the articulation points in a graph. + * Find the articulation points in a graph. * * A vertex is an articulation point if its removal increases - * the number of (weakly) connected components in the graph. - * - * \param graph The input graph. It will be treated as undirected. + * 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. @@ -945,14 +904,30 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, * \sa \ref igraph_biconnected_components(), \ref igraph_clusters(), \ref igraph_bridges() */ -igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_t *res) { +int igraph_articulation_points(const igraph_t *graph, + igraph_vector_t *res) { - return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, 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 - * \brief Calculates biconnected components. + * \brief Calculate biconnected components. * * A graph is biconnected if the removal of any single vertex (and * its incident edges) does not disconnect it. @@ -974,13 +949,17 @@ igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_i * a single vertex only as being biconnected. Isolated vertices will * not be part of any of the biconnected components. * - * \param graph The input graph. It will be treated as undirected. - * \param no If not a NULL pointer, the number of biconnected components will - * be stored here. + * \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. @@ -1005,60 +984,68 @@ igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_i * \example examples/simple/igraph_biconnected_components.c */ -igraph_error_t igraph_biconnected_components(const igraph_t *graph, +int igraph_biconnected_components(const igraph_t *graph, igraph_integer_t *no, - igraph_vector_int_list_t *tree_edges, - igraph_vector_int_list_t *component_edges, - igraph_vector_int_list_t *components, - igraph_vector_int_t *articulation_points) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t nextptr; - igraph_vector_int_t num, low; + 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_int_t path; - igraph_stack_int_t edgestack; + igraph_stack_t path; + igraph_vector_t edgestack; igraph_inclist_t inclist; - igraph_integer_t counter, rootdfs = 0; - igraph_vector_int_t vertex_added; - igraph_integer_t comps = 0; - igraph_vector_int_list_t *mycomponents = components, vcomponents; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&found, no_of_nodes); - - IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); - IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100); + 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_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes); + 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_int_list_clear(tree_edges); + igraph_vector_ptr_clear(tree_edges); } if (components) { - igraph_vector_int_list_clear(components); + igraph_vector_ptr_clear(components); } if (component_edges) { - igraph_vector_int_list_clear(component_edges); + igraph_vector_ptr_clear(component_edges); } if (articulation_points) { - igraph_vector_int_clear(articulation_points); + igraph_vector_clear(articulation_points); } if (component_edges && !components) { mycomponents = &vcomponents; - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0); + IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0)); + IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents); } - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (i = 0; i < no_of_nodes; i++) { if (VECTOR(low)[i] != 0) { continue; /* already visited */ @@ -1066,27 +1053,27 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_stack_int_push(&path, i)); + IGRAPH_CHECK(igraph_stack_push(&path, i)); counter = 1; rootdfs = 0; VECTOR(low)[i] = VECTOR(num)[i] = counter++; - while (!igraph_stack_int_empty(&path)) { - igraph_integer_t n; - igraph_integer_t act = igraph_stack_int_top(&path); - igraph_integer_t actnext = VECTOR(nextptr)[act]; + 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) */ - igraph_integer_t edge = VECTOR(*adjedges)[actnext]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, act); + 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_stack_int_push(&edgestack, edge)); - IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); + 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 */ @@ -1097,9 +1084,9 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, VECTOR(nextptr)[act] += 1; } else { /* Step up */ - igraph_stack_int_pop(&path); - if (!igraph_stack_int_empty(&path)) { - igraph_integer_t prev = igraph_stack_int_top(&path); + 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]; @@ -1108,7 +1095,7 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, if (VECTOR(low)[act] >= VECTOR(num)[prev]) { if (articulation_points && !VECTOR(found)[prev] && prev != i /* the root */) { - IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev)); + IGRAPH_CHECK(igraph_vector_push_back(articulation_points, prev)); VECTOR(found)[prev] = 1; } if (no) { @@ -1118,30 +1105,40 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, /*------------------------------------*/ /* Record the biconnected component just found */ if (tree_edges || mycomponents) { - igraph_vector_int_t *v, *v2; + igraph_vector_t *v = 0, *v2 = 0; comps++; if (tree_edges) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v)); + 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) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2)); + 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_stack_int_empty(&edgestack)) { - igraph_integer_t e = igraph_stack_int_pop(&edgestack); - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); + 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_int_push_back(v, e)); + 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_int_push_back(v2, from)); + IGRAPH_CHECK(igraph_vector_push_back(v2, from)); } if (VECTOR(vertex_added)[to] != comps) { VECTOR(vertex_added)[to] = comps; - IGRAPH_CHECK(igraph_vector_int_push_back(v2, to)); + IGRAPH_CHECK(igraph_vector_push_back(v2, to)); } } if (from == prev || to == prev) { @@ -1149,63 +1146,76 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, } } + 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_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1); - igraph_integer_t ii, no_vert = igraph_vector_int_size(nodes); - igraph_vector_int_t *vv; - - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv)); + 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++) { - igraph_integer_t vert = VECTOR(*nodes)[ii]; + long int vert = (long int) VECTOR(*nodes)[ii]; igraph_vector_int_t *edges = igraph_inclist_get(&inclist, vert); - igraph_integer_t j, nn = igraph_vector_int_size(edges); + long int j, nn = igraph_vector_int_size(edges); for (j = 0; j < nn; j++) { - igraph_integer_t e = VECTOR(*edges)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, e, vert); + 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_int_push_back(vv, e)); + 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_int_empty(&path) */ + } /* !igraph_stack_empty(&path) */ } - } /* !igraph_stack_int_empty(&path) */ + } /* !igraph_stack_empty(&path) */ if (articulation_points && rootdfs >= 2) { - IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i)); + IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i)); } } /* i < no_of_nodes */ if (mycomponents != components) { - igraph_vector_int_list_destroy(mycomponents); + igraph_i_free_vectorlist(mycomponents); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&vertex_added); + igraph_vector_long_destroy(&vertex_added); igraph_inclist_destroy(&inclist); - igraph_stack_int_destroy(&edgestack); - igraph_stack_int_destroy(&path); + igraph_vector_destroy(&edgestack); + igraph_stack_destroy(&path); igraph_vector_bool_destroy(&found); - igraph_vector_int_destroy(&low); - igraph_vector_int_destroy(&num); - igraph_vector_int_destroy(&nextptr); + igraph_vector_long_destroy(&low); + igraph_vector_long_destroy(&num); + igraph_vector_long_destroy(&nextptr); IGRAPH_FINALLY_CLEAN(8); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_bridges - * \brief Finds all bridges in a graph. + * \brief Find all bridges in a graph. * * An edge is a bridge if its removal increases the number of (weakly) * connected components in the graph. @@ -1220,15 +1230,14 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), \ref igraph_clusters() */ -igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges) { - +int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { /* 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. Additionally, we use explicit stacks instead of recursion to avoid stack overflow. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long no_of_nodes = igraph_vcount(graph); igraph_inclist_t il; igraph_vector_bool_t visited; igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */ @@ -1240,21 +1249,26 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge 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, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes); igraph_vector_int_fill(&incoming_edge, -1); - IGRAPH_STACK_INT_INIT_FINALLY(&su, 0); - IGRAPH_STACK_INT_INIT_FINALLY(&si, 0); + IGRAPH_CHECK(igraph_stack_int_init(&su, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &su); - igraph_vector_int_clear(bridges); + IGRAPH_CHECK(igraph_stack_int_init(&si, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &si); + + igraph_vector_clear(bridges); time = 0; - for (igraph_integer_t start = 0; start < no_of_nodes; ++start) { + for (long start = 0; start < no_of_nodes; ++start) { if (! VECTOR(visited)[start]) { /* Perform a DFS from 'start'. * The top of the su stack is u, the vertex currently being visited. @@ -1265,8 +1279,8 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); while (! igraph_stack_int_empty(&su)) { - igraph_integer_t u = igraph_stack_int_pop(&su); - igraph_integer_t i = igraph_stack_int_pop(&si); + long u = igraph_stack_int_pop(&su); + long i = igraph_stack_int_pop(&si); if (i == 0) { /* We are at the first step of visiting vertex u. */ @@ -1285,7 +1299,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge IGRAPH_CHECK(igraph_stack_int_push(&su, u)); IGRAPH_CHECK(igraph_stack_int_push(&si, i+1)); - igraph_integer_t edge = VECTOR(*incedges)[i]; + long edge = (long) VECTOR(*incedges)[i]; igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); if (! VECTOR(visited)[v]) { @@ -1301,12 +1315,12 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge * We are ready to update the 'low' value of its parent w, and decide * whether its incoming edge is a bridge. */ - igraph_integer_t edge = VECTOR(incoming_edge)[u]; + long edge = VECTOR(incoming_edge)[u]; if (edge >= 0) { - igraph_integer_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ + long w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u]; if (VECTOR(low)[u] > VECTOR(vis)[w]) { - IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge)); + IGRAPH_CHECK(igraph_vector_push_back(bridges, edge)); } } } @@ -1332,7 +1346,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge * \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 + * \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. @@ -1355,7 +1369,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p vertex is an invalid vertex ID + * \p vertex is an invalid vertex id * \cli IGRAPH_EINVMODE * invalid mode argument passed. * \endclist @@ -1368,18 +1382,16 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only * a given set of vertices and the edges between them. */ -igraph_error_t igraph_subcomponent( - const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vertex, - igraph_neimode_t mode -) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - bool *already_added; - igraph_integer_t i, vsize; - igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; - - if (vertex < 0 || vertex >= no_of_nodes) { +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 && @@ -1387,40 +1399,43 @@ igraph_error_t igraph_subcomponent( IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); } - already_added = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for computing subcomponent."); + 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_int_clear(res); + igraph_vector_clear(res); - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex)); - IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex)); - already_added[vertex] = true; + IGRAPH_CHECK(igraph_dqueue_push(&q, vertex)); + IGRAPH_CHECK(igraph_vector_push_back(res, vertex)); + already_added[(long int)vertex] = 1; - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + while (!igraph_dqueue_empty(&q)) { + long int actnode = (long int) igraph_dqueue_pop(&q); IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode)); - vsize = igraph_vector_int_size(&tmp); + IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode, + mode)); + vsize = igraph_vector_size(&tmp); for (i = 0; i < vsize; i++) { - igraph_integer_t neighbor = VECTOR(tmp)[i]; + long int neighbor = (long int) VECTOR(tmp)[i]; if (already_added[neighbor]) { continue; } - already_added[neighbor] = true; - IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + already_added[neighbor] = 1; + IGRAPH_CHECK(igraph_vector_push_back(res, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); } } - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&tmp); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&tmp); IGRAPH_FREE(already_added); IGRAPH_FINALLY_CLEAN(3); diff --git a/src/vendor/cigraph/src/connectivity/separators.c b/src/vendor/cigraph/src/connectivity/separators.c index b9970df13ea..c6723f8e5c0 100644 --- a/src/vendor/cigraph/src/connectivity/separators.c +++ b/src/vendor/cigraph/src/connectivity/separators.c @@ -28,33 +28,34 @@ #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 igraph_error_t igraph_i_is_separator(const igraph_t *graph, +static int igraph_i_is_separator(const igraph_t *graph, igraph_vit_t *vit, - igraph_integer_t except, + long int except, igraph_bool_t *res, igraph_vector_bool_t *removed, - igraph_dqueue_int_t *Q, - igraph_vector_int_t *neis, - igraph_integer_t no_of_nodes) { + igraph_dqueue_t *Q, + igraph_vector_t *neis, + long int no_of_nodes) { - igraph_integer_t start = 0; + 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; - igraph_integer_t nohit = 0; + 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)) { - igraph_integer_t v = IGRAPH_VIT_GET(*vit); + long int v = IGRAPH_VIT_GET(*vit); if (!VECTOR(hit)[v]) { nohit++; VECTOR(hit)[v] = 1; @@ -64,7 +65,7 @@ static igraph_error_t igraph_i_is_separator(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); if (nohit >= no_of_nodes - 1) { *res = 0; - return IGRAPH_SUCCESS; + return 0; } } @@ -75,20 +76,20 @@ static igraph_error_t igraph_i_is_separator(const igraph_t *graph, for (IGRAPH_VIT_RESET(*vit); !IGRAPH_VIT_END(*vit); IGRAPH_VIT_NEXT(*vit)) { - VECTOR(*removed)[ IGRAPH_VIT_GET(*vit) ] = 1; + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; } } else { /* There is an exception */ - igraph_integer_t i; + long int i; for (i = 0, IGRAPH_VIT_RESET(*vit); i < except; i++, IGRAPH_VIT_NEXT(*vit)) { - VECTOR(*removed)[ IGRAPH_VIT_GET(*vit) ] = 1; + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; } for (IGRAPH_VIT_NEXT(*vit); !IGRAPH_VIT_END(*vit); IGRAPH_VIT_NEXT(*vit)) { - VECTOR(*removed)[ IGRAPH_VIT_GET(*vit) ] = 1; + VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; } } @@ -102,17 +103,17 @@ static igraph_error_t igraph_i_is_separator(const igraph_t *graph, IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_dqueue_int_push(Q, start)); + IGRAPH_CHECK(igraph_dqueue_push(Q, start)); VECTOR(*removed)[start] = 1; - while (!igraph_dqueue_int_empty(Q)) { - igraph_integer_t node = igraph_dqueue_int_pop(Q); - igraph_integer_t j, n; - IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL)); - n = igraph_vector_int_size(neis); + 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++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; if (!VECTOR(*removed)[nei]) { - IGRAPH_CHECK(igraph_dqueue_int_push(Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); VECTOR(*removed)[nei] = 1; } } @@ -126,7 +127,7 @@ static igraph_error_t igraph_i_is_separator(const igraph_t *graph, /* If there is another component, then we have a separator */ *res = (start < no_of_nodes); - return IGRAPH_SUCCESS; + return 0; } /** @@ -145,34 +146,34 @@ static igraph_error_t igraph_i_is_separator(const igraph_t *graph, * \example examples/simple/igraph_is_separator.c */ -igraph_error_t igraph_is_separator(const igraph_t *graph, +int igraph_is_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vector_bool_t removed; - igraph_dqueue_int_t Q; - igraph_vector_int_t neis; + 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_int_init(&Q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_int_destroy(&neis); - igraph_dqueue_int_destroy(&Q); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); igraph_vector_bool_destroy(&removed); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /** @@ -191,7 +192,8 @@ igraph_error_t igraph_is_separator(const igraph_t *graph, * * \param graph The input graph. It may be directed, but edge * directions are ignored. - * \param candidate The candidate minimal separators. + * \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. @@ -203,15 +205,15 @@ igraph_error_t igraph_is_separator(const igraph_t *graph, * \example examples/simple/igraph_is_minimal_separator.c */ -igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, +int igraph_is_minimal_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vector_bool_t removed; - igraph_dqueue_int_t Q; - igraph_vector_int_t neis; - igraph_integer_t candsize; + igraph_dqueue_t Q; + igraph_vector_t neis; + long int candsize; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit)); @@ -220,9 +222,9 @@ igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_bool_init(&removed, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &removed); - IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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, @@ -237,7 +239,7 @@ igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, * false for all vertices, then 'candidate' is a minimal * separator. */ - igraph_integer_t i; + 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, @@ -246,13 +248,13 @@ igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, (*res) = (*res) ? 0 : 1; /* opposite */ } - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&Q); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); igraph_vector_bool_destroy(&removed); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /* --------------------------------------------------------------------*/ @@ -260,25 +262,25 @@ igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, #define UPDATEMARK() do { \ (*mark)++; \ if (!(*mark)) { \ - igraph_vector_int_null(leaveout); \ + igraph_vector_null(leaveout); \ (*mark)=1; \ } \ } while (0) -static igraph_error_t igraph_i_connected_components_leaveout(const igraph_adjlist_t *adjlist, - igraph_vector_int_t *components, - igraph_vector_int_t *leaveout, - igraph_integer_t *mark, - igraph_dqueue_int_t *Q) { +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 */ - igraph_integer_t i, no_of_nodes = igraph_adjlist_size(adjlist); + long int i, no_of_nodes = igraph_adjlist_size(adjlist); - igraph_dqueue_int_clear(Q); - igraph_vector_int_clear(components); + igraph_dqueue_clear(Q); + igraph_vector_clear(components); for (i = 0; i < no_of_nodes; i++) { @@ -287,95 +289,114 @@ static igraph_error_t igraph_i_connected_components_leaveout(const igraph_adjlis } VECTOR(*leaveout)[i] = *mark; - IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(components, i)); + igraph_dqueue_push(Q, i); + igraph_vector_push_back(components, i); - while (!igraph_dqueue_int_empty(Q)) { - igraph_integer_t act_node = igraph_dqueue_int_pop(Q); + 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); - igraph_integer_t j, n = igraph_vector_int_size(neis); + long int j, n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei] == *mark) { continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); VECTOR(*leaveout)[nei] = *mark; - IGRAPH_CHECK(igraph_vector_int_push_back(components, nei)); + igraph_vector_push_back(components, nei); } } - IGRAPH_CHECK(igraph_vector_int_push_back(components, -1)); + igraph_vector_push_back(components, -1); } UPDATEMARK(); - return IGRAPH_SUCCESS; + return 0; } -static igraph_bool_t igraph_i_separators_is_not_seen_yet( - const igraph_vector_int_list_t *comps, const igraph_vector_int_t *newc -) { +static igraph_bool_t igraph_i_separators_newsep(const igraph_vector_ptr_t *comps, + const igraph_vector_t *newc) { - igraph_integer_t co, nocomps = igraph_vector_int_list_size(comps); + long int co, nocomps = igraph_vector_ptr_size(comps); for (co = 0; co < nocomps; co++) { - igraph_vector_int_t *act = igraph_vector_int_list_get_ptr(comps, co); - if (igraph_vector_int_all_e(act, newc)) { - return false; + igraph_vector_t *act = VECTOR(*comps)[co]; + if (igraph_vector_all_e(act, newc)) { + return 0; } } /* If not found, then it is new */ - return true; + return 1; } -static igraph_error_t igraph_i_separators_store(igraph_vector_int_list_t *separators, +static int igraph_i_separators_store(igraph_vector_ptr_t *separators, const igraph_adjlist_t *adjlist, - igraph_vector_int_t *components, - igraph_vector_int_t *leaveout, - igraph_integer_t *mark, - igraph_vector_int_t *sorter) { + igraph_vector_t *components, + igraph_vector_t *leaveout, + unsigned long int *mark, + igraph_vector_t *sorter) { - /* We need to store N(C), the neighborhood of C, but only if it is + /* We need to stote N(C), the neighborhood of C, but only if it is * not already stored among the separators. */ - igraph_integer_t cptr = 0, next, complen = igraph_vector_int_size(components); + long int cptr = 0, next, complen = igraph_vector_size(components); while (cptr < complen) { - igraph_integer_t saved = cptr; - igraph_vector_int_clear(sorter); + long int saved = cptr; + igraph_vector_clear(sorter); /* Calculate N(C) for the next C */ - while ( (next = VECTOR(*components)[cptr++]) != -1) { + while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { VECTOR(*leaveout)[next] = *mark; } cptr = saved; - while ( (next = VECTOR(*components)[cptr++]) != -1) { + while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, next); - igraph_integer_t j, nn = igraph_vector_int_size(neis); + long int j, nn = igraph_vector_int_size(neis); for (j = 0; j < nn; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei] != *mark) { - IGRAPH_CHECK(igraph_vector_int_push_back(sorter, nei)); + igraph_vector_push_back(sorter, nei); VECTOR(*leaveout)[nei] = *mark; } } } - igraph_vector_int_sort(sorter); + igraph_vector_sort(sorter); UPDATEMARK(); /* Add it to the list of separators, if it is new */ - if (igraph_i_separators_is_not_seen_yet(separators, sorter)) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(separators, sorter)); + + 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 IGRAPH_SUCCESS; + 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(VECTOR(*separators)[i]); + } + } } /** @@ -406,7 +427,7 @@ static igraph_error_t igraph_i_separators_store(igraph_vector_int_list_t *separa * \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_int_t + * 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 @@ -422,9 +443,8 @@ static igraph_error_t igraph_i_separators_store(igraph_vector_int_list_t *separa * \example examples/simple/igraph_minimal_separators.c */ -igraph_error_t igraph_all_minimal_st_separators( - const igraph_t *graph, igraph_vector_int_list_t *separators -) { +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 @@ -435,7 +455,7 @@ igraph_error_t igraph_all_minimal_st_separators( * 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. + * 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. * @@ -444,31 +464,35 @@ igraph_error_t igraph_all_minimal_st_separators( * the next separator to try as a basis. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t leaveout; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t leaveout; igraph_vector_bool_t already_tried; - igraph_integer_t try_next = 0; - igraph_integer_t mark = 1; - igraph_integer_t v; + long int try_next = 0; + unsigned long int mark = 1; + long int v; igraph_adjlist_t adjlist; - igraph_vector_int_t components; - igraph_dqueue_int_t Q; - igraph_vector_int_t sorter; + igraph_vector_t components; + igraph_dqueue_t Q; + igraph_vector_t sorter; - igraph_vector_int_list_clear(separators); + igraph_vector_ptr_clear(separators); + IGRAPH_FINALLY(igraph_i_separators_free, separators); - IGRAPH_VECTOR_INT_INIT_FINALLY(&leaveout, no_of_nodes); + 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_VECTOR_INT_INIT_FINALLY(&components, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&components, no_of_nodes * 2)); + 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_int_init(&Q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&sorter, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&sorter, no_of_nodes)); + 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 @@ -480,16 +504,16 @@ igraph_error_t igraph_all_minimal_st_separators( /* Mark v and its neighbors */ igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, v); - igraph_integer_t i, n = igraph_vector_int_size(neis); + long int i, n = igraph_vector_int_size(neis); VECTOR(leaveout)[v] = mark; for (i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; + long int nei = (long int) VECTOR(*neis)[i]; VECTOR(leaveout)[nei] = mark; } /* Find the components */ - IGRAPH_CHECK(igraph_i_connected_components_leaveout( - &adjlist, &components, &leaveout, &mark, &Q)); + 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, @@ -502,30 +526,27 @@ igraph_error_t igraph_all_minimal_st_separators( * basis and see if they generate more separators */ - while (try_next < igraph_vector_int_list_size(separators)) { - /* copy "basis" out of the vector_list because we are going to - * mutate the vector_list later, and this can potentially invalidate - * the pointer */ - igraph_vector_int_t basis = *(igraph_vector_int_list_get_ptr(separators, try_next)); - igraph_integer_t b, basislen = igraph_vector_int_size(&basis); + 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 */ - igraph_integer_t x = VECTOR(basis)[b]; + long int x = (long int) VECTOR(*basis)[b]; igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, x); - igraph_integer_t i, n = igraph_vector_int_size(neis); + long int i, n = igraph_vector_int_size(neis); for (i = 0; i < basislen; i++) { - igraph_integer_t sn = VECTOR(basis)[i]; + long int sn = (long int) VECTOR(*basis)[i]; VECTOR(leaveout)[sn] = mark; } for (i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; + long int nei = (long int) VECTOR(*neis)[i]; VECTOR(leaveout)[nei] = mark; } /* Find the components */ - IGRAPH_CHECK(igraph_i_connected_components_leaveout( - &adjlist, &components, &leaveout, &mark, &Q)); + 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, @@ -538,74 +559,85 @@ igraph_error_t igraph_all_minimal_st_separators( /* --------------------------------------------------------------- */ - igraph_vector_int_destroy(&sorter); - igraph_dqueue_int_destroy(&Q); + igraph_vector_destroy(&sorter); + igraph_dqueue_destroy(&Q); igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&components); + igraph_vector_destroy(&components); igraph_vector_bool_destroy(&already_tried); - igraph_vector_int_destroy(&leaveout); - IGRAPH_FINALLY_CLEAN(6); + igraph_vector_destroy(&leaveout); + IGRAPH_FINALLY_CLEAN(7); /* +1 for separators */ - return IGRAPH_SUCCESS; + return 0; } #undef UPDATEMARK -static igraph_error_t igraph_i_minimum_size_separators_append( - igraph_vector_int_list_t *old, igraph_vector_int_list_t *new -) { +static int igraph_i_minimum_size_separators_append(igraph_vector_ptr_t *old, + igraph_vector_ptr_t *new) { - igraph_integer_t olen = igraph_vector_int_list_size(old); - igraph_integer_t j; + long int olen = igraph_vector_ptr_size(old); + long int nlen = igraph_vector_ptr_size(new); + long int i; - while (!igraph_vector_int_list_empty(new)) { - igraph_vector_int_t *newvec = igraph_vector_int_list_tail_ptr(new); - - /* Check whether the separator is already in `old' */ + for (i = 0; i < nlen; i++) { + igraph_vector_t *newvec = VECTOR(*new)[i]; + long int j; for (j = 0; j < olen; j++) { - igraph_vector_int_t *oldvec = igraph_vector_int_list_get_ptr(old, j); - if (igraph_vector_int_all_e(oldvec, newvec)) { + igraph_vector_t *oldvec = VECTOR(*old)[j]; + if (igraph_vector_all_e(oldvec, newvec)) { break; } } - if (j == olen) { - /* We have found a new separator, append it to `old' */ - /* TODO: we should have a more efficient method for moving a vector - * from one vector_list to another */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(old, newvec)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(old, newvec)); olen++; + } else { + igraph_vector_destroy(newvec); + igraph_free(newvec); } - - igraph_vector_int_list_discard_back(new); + VECTOR(*new)[i] = 0; } + igraph_vector_ptr_clear(new); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_minimum_size_separators_topkdeg( - const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t k -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t deg, order; - igraph_integer_t i; +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_INT_INIT_FINALLY(°, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + 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_int_order1(°, &order, no_of_nodes)); - IGRAPH_CHECK(igraph_vector_int_resize(res, k)); + 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_int_destroy(&order); - igraph_vector_int_destroy(°); + igraph_vector_destroy(&order); + igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + 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); } /** @@ -621,10 +653,13 @@ static igraph_error_t igraph_i_minimum_size_separators_topkdeg( * a graph, Networks 23, 533--541, 1993. * * \param graph The input graph, which must be undirected. - * \param separators An initialized list of integer vectors, the separators - * are stored here. It is a list of pointers to igraph_vector_int_t - * objects. Each vector will contain the IDs of the vertices in - * the separator. The separators are returned in an arbitrary order. + * \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. @@ -632,15 +667,14 @@ static igraph_error_t igraph_i_minimum_size_separators_topkdeg( * \example examples/simple/igraph_minimum_size_separators.c */ -igraph_error_t igraph_minimum_size_separators( - const igraph_t *graph, igraph_vector_int_list_t *separators -) { +int igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_ptr_t *separators) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t conn; - igraph_vector_int_t X; - igraph_integer_t i, j, k, n; + 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; @@ -653,59 +687,86 @@ igraph_error_t igraph_minimum_size_separators( IGRAPH_EINVAL); } - igraph_vector_int_list_clear(separators); + 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= */ true)); - k = conn; + 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 */ - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(1); /* separators */ + return 0; } else if (conn == 1) { - igraph_vector_int_t ap; - IGRAPH_VECTOR_INT_INIT_FINALLY(&ap, 0); + igraph_vector_t ap; + long int i, n; + IGRAPH_VECTOR_INIT_FINALLY(&ap, 0); IGRAPH_CHECK(igraph_articulation_points(graph, &ap)); - n = igraph_vector_int_size(&ap); - IGRAPH_CHECK(igraph_vector_int_list_resize(separators, n)); + 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_int_t *v = igraph_vector_int_list_get_ptr(separators, i); - IGRAPH_CHECK(igraph_vector_int_push_back(v, VECTOR(ap)[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_int_destroy(&ap); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + igraph_vector_destroy(&ap); + IGRAPH_FINALLY_CLEAN(2); /* +1 for separators */ + return 0; } else if (conn == no_of_nodes - 1) { - IGRAPH_CHECK(igraph_vector_int_list_resize(separators, no_of_nodes)); + 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_int_t *v = igraph_vector_int_list_get_ptr(separators, i); - IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes - 1)); + 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); } - return IGRAPH_SUCCESS; + 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 */ true, /* loops */ true, NULL)); + 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_int_init(&X, conn)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &X); + 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_CHECK(igraph_vector_int_list_push_back_copy(separators, &X)); + 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 */ @@ -722,14 +783,15 @@ igraph_error_t igraph_minimum_size_separators( IGRAPH_ALLOW_INTERRUPTION(); for (j = 0; j < no_of_nodes; j++) { - igraph_integer_t ii = VECTOR(X)[i]; + 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, ii, j, &conn)); + IGRAPH_CHECK(igraph_are_connected(&graph_copy, (igraph_integer_t) ii, + (igraph_integer_t) j, &conn)); if (conn) { continue; /* they are connected */ } @@ -739,34 +801,41 @@ igraph_error_t igraph_minimum_size_separators( If |phi|=k, then */ IGRAPH_CHECK(igraph_maxflow(&Gbar, &phivalue, &phi, /*cut=*/ 0, /*partition=*/ 0, /*partition2=*/ 0, - /* source= */ ii + no_of_nodes, - /* target= */ j, + /* 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_int_list_t stcuts; - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&stcuts, 0); + 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=*/ ii + no_of_nodes, - /*target=*/ j, + /*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_int_list_destroy(&stcuts); + 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, ii, j)); - IGRAPH_CHECK(igraph_add_edge(&Gbar, ii + no_of_nodes, j)); - IGRAPH_CHECK(igraph_add_edge(&Gbar, j + no_of_nodes, ii)); + 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)); @@ -776,9 +845,9 @@ igraph_error_t igraph_minimum_size_separators( igraph_vector_destroy(&phi); igraph_destroy(&Gbar); igraph_vector_destroy(&capacity); - igraph_vector_int_destroy(&X); + igraph_vector_destroy(&X); igraph_destroy(&graph_copy); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FINALLY_CLEAN(6); /* +1 for separators */ - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/adjacency.c b/src/vendor/cigraph/src/constructors/adjacency.c index 1de5744646f..7fbf9de438d 100644 --- a/src/vendor/cigraph/src/constructors/adjacency.c +++ b/src/vendor/cigraph/src/constructors/adjacency.c @@ -23,229 +23,116 @@ #include "igraph_constructors.h" #include "igraph_adjlist.h" +#include "igraph_attributes.h" #include "igraph_interface.h" -#include "igraph_sparsemat.h" - -static igraph_error_t igraph_i_adjacency_directed( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); -static igraph_error_t igraph_i_adjacency_max( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); -static igraph_error_t igraph_i_adjacency_undirected( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); -static igraph_error_t igraph_i_adjacency_upper( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); -static igraph_error_t igraph_i_adjacency_lower( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); -static igraph_error_t igraph_i_adjacency_min( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -); - -static igraph_error_t igraph_i_adjust_loop_edge_count( - igraph_integer_t* count, igraph_loops_t loops -) { - /* The compiler should be smart enough to figure out that this can be - * inlined */ - switch (loops) { - case IGRAPH_NO_LOOPS: - *count = 0; - break; - case IGRAPH_LOOPS_TWICE: - if (*count & 1) { - IGRAPH_ERROR("Odd number found in the diagonal of the adjacency matrix.", IGRAPH_EINVAL); - } - *count >>= 1; - break; - case IGRAPH_LOOPS_ONCE: - default: - break; - } - return IGRAPH_SUCCESS; -} +static int igraph_i_adjacency_directed(const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges); +static int igraph_i_adjacency_max(const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges); +static int igraph_i_adjacency_upper(const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges); +static int igraph_i_adjacency_lower(const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges); +static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, + igraph_vector_t *edges); -static igraph_error_t igraph_i_adjacency_directed( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { +static int igraph_i_adjacency_directed(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j, k; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j, k; for (i = 0; i < no_of_nodes; i++) { for (j = 0; j < no_of_nodes; j++) { - igraph_integer_t M = MATRIX(*adjmatrix, i, j); - if (i == j) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); - } + long int M = (long int) MATRIX(*adjmatrix, i, j); for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); } } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_adjacency_max( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { +static int igraph_i_adjacency_max(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j, k, M1, M2; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j, k; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - M1 = MATRIX(*adjmatrix, i, i); - if (M1) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); - for (k = 0; k < M1; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { - M1 = MATRIX(*adjmatrix, i, j); - M2 = MATRIX(*adjmatrix, j, i); + for (j = i; j < no_of_nodes; j++) { + long int M1 = (long int) MATRIX(*adjmatrix, i, j); + long int M2 = (long int) MATRIX(*adjmatrix, j, i); if (M1 < M2) { M1 = M2; } for (k = 0; k < M1; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); } } } - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_adjacency_undirected( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - if (!igraph_matrix_is_symmetric(adjmatrix)) { - IGRAPH_ERROR( - "Adjacency matrix should be symmetric to produce an undirected graph.", - IGRAPH_EINVAL - ); - } - return igraph_i_adjacency_max(adjmatrix, edges, loops); + return 0; } -static igraph_error_t igraph_i_adjacency_upper( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { +static int igraph_i_adjacency_upper(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j, k, M; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j, k; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - M = MATRIX(*adjmatrix, i, i); - if (M) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); - for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { - M = MATRIX(*adjmatrix, i, j); + for (j = i; j < no_of_nodes; j++) { + long int M = (long int) MATRIX(*adjmatrix, i, j); for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); } } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_adjacency_lower( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { +static int igraph_i_adjacency_lower(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j, k, M; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j, k; for (i = 0; i < no_of_nodes; i++) { - for (j = 0; j < i; j++) { - M = MATRIX(*adjmatrix, i, j); - for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - } - } - - /* do the loops as well */ - M = MATRIX(*adjmatrix, i, i); - if (M) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + for (j = 0; j <= i; j++) { + long int M = (long int) MATRIX(*adjmatrix, i, j); for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); } } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_adjacency_min( - const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { +static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j, k, M1, M2; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j, k; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - M1 = MATRIX(*adjmatrix, i, i); - if (M1) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); - for (k = 0; k < M1; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { - M1 = MATRIX(*adjmatrix, i, j); - M2 = MATRIX(*adjmatrix, j, i); + for (j = i; j < no_of_nodes; j++) { + long int M1 = (long int) MATRIX(*adjmatrix, i, j); + long int M2 = (long int) MATRIX(*adjmatrix, j, i); if (M1 > M2) { M1 = M2; } for (k = 0; k < M1; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(edges, j)); } } } - return IGRAPH_SUCCESS; + return 0; } - - /** * \ingroup generators * \function igraph_adjacency @@ -258,8 +145,11 @@ static igraph_error_t igraph_i_adjacency_min( * \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): + * 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 @@ -294,368 +184,257 @@ static igraph_error_t igraph_i_adjacency_min( * only the lower left triangle (including the diagonal) is * used for creating the edges. * \endclist - * \param loops Constant to specify how the diagonal of the matrix should be - * treated when creating loop edges. - * \clist - * \cli IGRAPH_NO_LOOPS - * Ignore the diagonal of the input matrix and do not create loops. - * \cli IGRAPH_LOOPS_ONCE - * Treat the diagonal entries as the number of loop edges incident on - * the corresponding vertex. - * \cli IGRAPH_LOOPS_TWICE - * Treat the diagonal entries as \em twice the number of loop edges - * incident on the corresponding vertex. Odd numbers in the diagonal - * will return an error code. - * \endclist * \return Error code, * \c IGRAPH_NONSQUARE: non-square matrix. - * \c IGRAPH_EINVAL: Negative entry was found in adjacency matrix, or an - * odd number was found in the diagonal with \c IGRAPH_LOOPS_TWICE * * Time complexity: O(|V||V|), * |V| is the number of vertices in the graph. * * \example examples/simple/igraph_adjacency.c */ -igraph_error_t igraph_adjacency( - igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, - igraph_loops_t loops -) { +int igraph_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, + igraph_adjacency_t mode) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + 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("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); - } - - if (no_of_nodes != 0 && igraph_matrix_min(adjmatrix) < 0) { - IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, - igraph_matrix_min(adjmatrix)); + IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + 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, loops)); + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges)); break; case IGRAPH_ADJ_MAX: - IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_UNDIRECTED: - IGRAPH_CHECK(igraph_i_adjacency_undirected(adjmatrix, &edges, loops)); + IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges)); break; case IGRAPH_ADJ_UPPER: - IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges, loops)); + IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges)); break; case IGRAPH_ADJ_LOWER: - IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges, loops)); + IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges)); break; case IGRAPH_ADJ_MIN: - IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges, loops)); + IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges)); break; case IGRAPH_ADJ_PLUS: - IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges, loops)); + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges)); break; - default: - IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); - igraph_vector_int_destroy(&edges); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_directed( +static int igraph_i_weighted_adjacency_directed( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); -static igraph_error_t igraph_i_weighted_adjacency_plus( + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_plus( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); -static igraph_error_t igraph_i_weighted_adjacency_max( + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_max( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); -static igraph_error_t igraph_i_weighted_adjacency_upper( + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_upper( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); -static igraph_error_t igraph_i_weighted_adjacency_lower( + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_lower( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); -static igraph_error_t igraph_i_weighted_adjacency_min( + igraph_bool_t loops); +static int igraph_i_weighted_adjacency_min( const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights, - igraph_loops_t loops -); - -static void igraph_i_adjust_loop_edge_weight(igraph_real_t* weight, igraph_loops_t loops) { - /* The compiler should be smart enough to figure out that this can be - * inlined */ - switch (loops) { - case IGRAPH_NO_LOOPS: - *weight = 0.0; - break; - case IGRAPH_LOOPS_TWICE: - *weight /= 2; - break; - case IGRAPH_LOOPS_ONCE: - default: - break; - } -} + igraph_bool_t loops); -static igraph_error_t igraph_i_weighted_adjacency_directed( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; + 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) { - if (i == j) { - igraph_i_adjust_loop_edge_weight(&M, loops); - if (M == 0.0) { - continue; - } - } - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_plus( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; - igraph_real_t M; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; for (i = 0; i < no_of_nodes; i++) { - if (loops != IGRAPH_NO_LOOPS) { - M = MATRIX(*adjmatrix, i, i); - if (M != 0.0) { - igraph_i_adjust_loop_edge_weight(&M, loops); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + 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; } - } - - for (j = i + 1; j < no_of_nodes; j++) { - M = MATRIX(*adjmatrix, i, j) + MATRIX(*adjmatrix, j, i); - if (M != 0.0) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_max( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; - igraph_real_t M1, M2; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - if (loops) { - M1 = MATRIX(*adjmatrix, i, i); - if (M1 != 0.0) { - igraph_i_adjust_loop_edge_weight(&M1, loops); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { - M1 = MATRIX(*adjmatrix, i, j); - M2 = MATRIX(*adjmatrix, j, 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) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_undirected( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_t loops -) { - if (!igraph_matrix_is_symmetric(adjmatrix)) { - IGRAPH_ERROR( - "Adjacency matrix should be symmetric to produce an undirected graph.", - IGRAPH_EINVAL - ); - } - return igraph_i_weighted_adjacency_max(adjmatrix, edges, weights, loops); -} - - -static igraph_error_t igraph_i_weighted_adjacency_upper( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; - igraph_real_t M; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - if (loops) { - M = MATRIX(*adjmatrix, i, i); - if (M != 0.0) { - igraph_i_adjust_loop_edge_weight(&M, loops); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { + for (j = i; j < no_of_nodes; j++) { igraph_real_t M = MATRIX(*adjmatrix, i, j); - if (M != 0.0) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_lower( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; - igraph_real_t M; + 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++) { - M = MATRIX(*adjmatrix, i, j); - if (M != 0.0) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + for (j = 0; j <= i; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M == 0.0) { + continue; } - } - - /* do the loops as well */ - if (loops) { - M = MATRIX(*adjmatrix, i, i); - if (M != 0.0) { - igraph_i_adjust_loop_edge_weight(&M, loops); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_adjacency_min( - const igraph_matrix_t *adjmatrix, - igraph_vector_int_t *edges, - igraph_vector_t *weights, - igraph_loops_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) { - igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); - igraph_integer_t i, j; - igraph_real_t M1, M2; + long int no_of_nodes = igraph_matrix_nrow(adjmatrix); + long int i, j; for (i = 0; i < no_of_nodes; i++) { - /* do the loops first */ - if (loops) { - M1 = MATRIX(*adjmatrix, i, i); - if (M1 != 0.0) { - igraph_i_adjust_loop_edge_weight(&M1, loops); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); - } - } - - /* then the rest */ - for (j = i + 1; j < no_of_nodes; j++) { - M1 = MATRIX(*adjmatrix, i, j); - M2 = MATRIX(*adjmatrix, j, 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) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); - IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -711,19 +490,10 @@ static igraph_error_t igraph_i_weighted_adjacency_min( * only the lower left triangle (including the diagonal) is * used for the edge weights. * \endclist - * \param weights Pointer to an initialized vector, the weights will be stored here. - * \param loops Constant to specify how the diagonal of the matrix should be - * treated when creating loop edges. - * \clist - * \cli IGRAPH_NO_LOOPS - * Ignore the diagonal of the input matrix and do not create loops. - * \cli IGRAPH_LOOPS_ONCE - * Treat the diagonal entries as the weight of the loop edge incident - * on the corresponding vertex. - * \cli IGRAPH_LOOPS_TWICE - * Treat the diagonal entries as \em twice the weight of the loop edge - * incident on the corresponding vertex. - * \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. * @@ -732,70 +502,77 @@ static igraph_error_t igraph_i_weighted_adjacency_min( * * \example examples/simple/igraph_weighted_adjacency.c */ -igraph_error_t igraph_weighted_adjacency( - igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, - igraph_vector_t *weights, igraph_loops_t loops -) { +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_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_nodes; + 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_INT_INIT_FINALLY(&edges, 0); - igraph_vector_clear(weights); + 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)); + &weights, loops)); break; case IGRAPH_ADJ_MAX: IGRAPH_CHECK(igraph_i_weighted_adjacency_max(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_UNDIRECTED: - IGRAPH_CHECK(igraph_i_weighted_adjacency_undirected(adjmatrix, &edges, - weights, loops)); + &weights, loops)); break; case IGRAPH_ADJ_UPPER: IGRAPH_CHECK(igraph_i_weighted_adjacency_upper(adjmatrix, &edges, - weights, loops)); + &weights, loops)); break; case IGRAPH_ADJ_LOWER: IGRAPH_CHECK(igraph_i_weighted_adjacency_lower(adjmatrix, &edges, - weights, loops)); + &weights, loops)); break; case IGRAPH_ADJ_MIN: IGRAPH_CHECK(igraph_i_weighted_adjacency_min(adjmatrix, &edges, - weights, loops)); + &weights, loops)); break; case IGRAPH_ADJ_PLUS: IGRAPH_CHECK(igraph_i_weighted_adjacency_plus(adjmatrix, &edges, - weights, loops)); + &weights, loops)); break; - default: - IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); } + /* 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, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, + (mode == IGRAPH_ADJ_DIRECTED))); IGRAPH_FINALLY(igraph_destroy, graph); - if (igraph_vector_int_size(&edges) > 0) { - IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + if (igraph_vector_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_destroy(&edges); + igraph_vector_destroy(&weights); + igraph_vector_ptr_destroy(&attr_vec); + IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -829,15 +606,15 @@ igraph_error_t igraph_weighted_adjacency( * Time complexity: O(|V|+|E|). * */ -igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, +int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, igraph_neimode_t mode, igraph_bool_t duplicate) { - igraph_integer_t no_of_nodes = igraph_adjlist_size(adjlist); - igraph_integer_t no_of_edges = 0; - igraph_integer_t i; + long int no_of_nodes = igraph_adjlist_size(adjlist); + long int no_of_edges = 0; + long int i; - igraph_vector_int_t edges; - igraph_integer_t edgeptr = 0; + igraph_vector_t edges; + long int edgeptr = 0; duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */ @@ -849,15 +626,15 @@ igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, no_of_edges /= 2; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2 * no_of_edges); + 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); - igraph_integer_t j, n = igraph_vector_int_size(neis); - igraph_integer_t loops = 0; + long int j, n = igraph_vector_int_size(neis); + long int loops = 0; for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; if (nei == i) { loops++; } else { @@ -891,537 +668,14 @@ igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, } if (mode == IGRAPH_ALL) - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, + (igraph_integer_t) no_of_nodes, 0)); else - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 1)); - - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_directed( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_sparsemat_iterator_init(&it, adjmatrix); - - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); - if (to == from) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); - } - for (igraph_integer_t count = 0; count < multi; count++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); - } - } - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_max( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_real_t other; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); - if (to == from) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); - } else { - other = igraph_sparsemat_get(adjmatrix, to, from); - multi = multi > other ? multi : other; - } - for (igraph_integer_t count = 0; count < multi; count++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_min( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_real_t other; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); - if (to == from) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); - } else { - other = igraph_sparsemat_get(adjmatrix, to, from); - multi = multi < other ? multi : other; - } - for (igraph_integer_t count = 0; count < multi; count++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_upper( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); - if (to == from) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); - } - for (igraph_integer_t count = 0; count < multi; count++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_lower( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to > from) { - continue; - } - igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); - if (to == from) { - IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); - } - for (igraph_integer_t count = 0; count < multi; count++) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); - } - } - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_adjacency_undirected( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_loops_t loops -) { - igraph_bool_t sym; - - IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); - if (!sym) { - IGRAPH_ERROR( - "Adjacency matrix should be symmetric to produce an undirected graph.", - IGRAPH_EINVAL - ); - } - return igraph_i_sparse_adjacency_upper(adjmatrix, edges, loops); -} + IGRAPH_CHECK(igraph_create(graph, &edges, + (igraph_integer_t) no_of_nodes, 1)); -/** - * \ingroup generators - * \function igraph_sparse_adjacency - * \brief Creates a graph from a sparse adjacency matrix. - * - * This has the same functionality as \ref igraph_adjacency(), but uses - * a column-compressed adjacency matrix. - * - * Time complexity: O(|E|), - * where |E| is the number of edges in the graph. - */ - -igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, - igraph_adjacency_t mode, igraph_loops_t loops) { - - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); - igraph_integer_t no_of_nonzeros = igraph_sparsemat_count_nonzero(adjmatrix); - igraph_integer_t approx_no_of_edges; - - if (!igraph_sparsemat_is_cc(adjmatrix)) { - IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed " - "form.", IGRAPH_EINVAL); - } - if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { - IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); - } - - if (no_of_nodes != 0 && igraph_sparsemat_min(adjmatrix) < 0) { - IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, - igraph_sparsemat_min(adjmatrix)); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - - /* Approximate the number of edges in the graph based on the number of - * nonzero elements in the matrix */ - switch (mode) { - case IGRAPH_ADJ_DIRECTED: - case IGRAPH_ADJ_PLUS: - case IGRAPH_ADJ_UPPER: - case IGRAPH_ADJ_LOWER: - approx_no_of_edges = no_of_nonzeros; - break; - case IGRAPH_ADJ_UNDIRECTED: - case IGRAPH_ADJ_MAX: - case IGRAPH_ADJ_MIN: - approx_no_of_edges = no_of_nonzeros / 2; - break; - default: - approx_no_of_edges = no_of_nonzeros; - break; - } - - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, approx_no_of_edges * 2)); - - /* Collect the edges */ - switch (mode) { - case IGRAPH_ADJ_DIRECTED: - IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_MAX: - IGRAPH_CHECK(igraph_i_sparse_adjacency_max(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_UNDIRECTED: - IGRAPH_CHECK(igraph_i_sparse_adjacency_undirected(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_UPPER: - IGRAPH_CHECK(igraph_i_sparse_adjacency_upper(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_LOWER: - IGRAPH_CHECK(igraph_i_sparse_adjacency_lower(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_MIN: - IGRAPH_CHECK(igraph_i_sparse_adjacency_min(adjmatrix, &edges, loops)); - break; - case IGRAPH_ADJ_PLUS: - IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); - break; - default: - IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); - } - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_max ( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_sparsemat_iterator_init(&it, adjmatrix); - igraph_integer_t e = 0; - igraph_real_t other; - - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } else { - other = igraph_sparsemat_get(adjmatrix, to, from); - weight = weight > other ? weight : other; - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_min ( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_integer_t e = 0; - igraph_real_t other; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } else { - other = igraph_sparsemat_get(adjmatrix, to, from); - weight = weight < other ? weight : other; - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_plus ( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_integer_t e = 0; - igraph_real_t other; - - igraph_sparsemat_iterator_init(&it, adjmatrix); - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - if (to < from) { - continue; - } - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } else { - other = igraph_sparsemat_get(adjmatrix, to, from); - weight += other; - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_upper( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_sparsemat_iterator_init(&it, adjmatrix); - igraph_integer_t e = 0; - - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to < from) { - continue; - } - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_lower( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_sparsemat_iterator_init(&it, adjmatrix); - igraph_integer_t e = 0; - - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to > from) { - continue; - } - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_sparse_weighted_adjacency_undirected ( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_bool_t sym; - - IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); - if (!sym) { - IGRAPH_ERROR( - "Adjacency matrix should be symmetric to produce an undirected graph.", - IGRAPH_EINVAL - ); - } - return igraph_i_sparse_weighted_adjacency_upper(adjmatrix, edges, weights, loops); -} - - -static igraph_error_t igraph_i_sparse_weighted_adjacency_directed( - igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_sparsemat_iterator_t it; - igraph_sparsemat_iterator_init(&it, adjmatrix); - igraph_integer_t e = 0; - - for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { - igraph_integer_t from = igraph_sparsemat_iterator_row(&it); - igraph_integer_t to = igraph_sparsemat_iterator_col(&it); - igraph_real_t weight = igraph_sparsemat_iterator_get(&it); - if (to == from) { - igraph_i_adjust_loop_edge_weight(&weight, loops); - } - if (weight != 0) { - VECTOR(*weights)[e/2] = weight; - VECTOR(*edges)[e++] = from; - VECTOR(*edges)[e++] = to; - } - } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, e/2); /* shrinks */ - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup generators - * \function igraph_sparse_weighted_adjacency - * \brief Creates a graph from a weighted sparse adjacency matrix. - * - * This has the same functionality as \ref igraph_weighted_adjacency(), but uses - * a column-compressed adjacency matrix. - * - * Time complexity: O(|E|), - * where |E| is the number of edges in the graph. - */ - - -igraph_error_t igraph_sparse_weighted_adjacency( - igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, - igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); - igraph_integer_t no_of_edges = igraph_sparsemat_count_nonzero(adjmatrix); - - if (!igraph_sparsemat_is_cc(adjmatrix)) { - IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed form.", IGRAPH_EINVAL); - } - if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { - IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); - IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); - - /* Collect the edges */ - switch (mode) { - case IGRAPH_ADJ_DIRECTED: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_directed(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_MAX: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_max(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_UNDIRECTED: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_undirected(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_UPPER: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_upper(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_LOWER: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_lower(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_MIN: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_min(adjmatrix, &edges, - weights, loops)); - break; - case IGRAPH_ADJ_PLUS: - IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_plus(adjmatrix, &edges, - weights, loops)); - break; - default: - IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); - } - - /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); - IGRAPH_FINALLY(igraph_destroy, graph); - if (igraph_vector_int_size(&edges) > 0) { - IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); - } - IGRAPH_FINALLY_CLEAN(1); - - /* Cleanup */ - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/atlas-edges.h b/src/vendor/cigraph/src/constructors/atlas-edges.h index 7038ed6488b..229e8f105ba 100644 --- a/src/vendor/cigraph/src/constructors/atlas-edges.h +++ b/src/vendor/cigraph/src/constructors/atlas-edges.h @@ -21,15 +21,21 @@ */ -#ifndef IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H -#define IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H - -#include "igraph_decls.h" -#include "igraph_types.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 -const igraph_integer_t igraph_i_atlas_edges[] = { +#include "igraph_types.h" + +const igraph_real_t igraph_i_atlas_edges[] = { 0, 0, 1, 0, 2, 0, @@ -1285,8 +1291,6 @@ const igraph_integer_t igraph_i_atlas_edges[] = { 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 igraph_integer_t 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}; +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 - -#endif /* IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H */ diff --git a/src/vendor/cigraph/src/constructors/atlas.c b/src/vendor/cigraph/src/constructors/atlas.c index 1ab4615cf1f..ffa2eaeeec3 100644 --- a/src/vendor/cigraph/src/constructors/atlas.c +++ b/src/vendor/cigraph/src/constructors/atlas.c @@ -59,24 +59,24 @@ * * \example examples/simple/igraph_atlas.c */ -igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number) { +int igraph_atlas(igraph_t *graph, int number) { igraph_integer_t pos, n, e; - igraph_vector_int_t v = IGRAPH_VECTOR_NULL; + igraph_vector_t v = IGRAPH_VECTOR_NULL; if (number < 0 || - number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(igraph_i_atlas_edges_pos[0]))) { + number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(long int))) { IGRAPH_ERROR("No such graph in atlas", IGRAPH_EINVAL); } - pos = igraph_i_atlas_edges_pos[number]; - n = igraph_i_atlas_edges[pos]; - e = igraph_i_atlas_edges[pos + 1]; + 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_int_view(&v, igraph_i_atlas_edges + pos + 2, + igraph_vector_view(&v, igraph_i_atlas_edges + pos + 2, e * 2), n, IGRAPH_UNDIRECTED)); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/basic_constructors.c b/src/vendor/cigraph/src/constructors/basic_constructors.c index 93d1f422093..9f16e08c28a 100644 --- a/src/vendor/cigraph/src/constructors/basic_constructors.c +++ b/src/vendor/cigraph/src/constructors/basic_constructors.c @@ -44,17 +44,17 @@ * \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 + * 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. + * 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. + * vertex id. * * Time complexity: O(|V|+|E|), * |V| is the number of vertices, @@ -63,34 +63,33 @@ * * \example examples/simple/igraph_create.c */ -igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, +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_int_size(edges) > 0; - igraph_integer_t max; + 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_int_size(edges) % 2 != 0) { - IGRAPH_ERROR("Invalid (odd) edges vector.", IGRAPH_EINVEVECTOR); + if (! igraph_finite(max)) { + IGRAPH_ERROR("Invalid (non-finite or NaN) vertex index when creating graph.", IGRAPH_EINVAL); } - if (has_edges && !igraph_vector_int_isininterval(edges, 0, IGRAPH_VCOUNT_MAX-1)) { - IGRAPH_ERROR("Invalid (negative or too large) vertex ID.", IGRAPH_EINVVID); + 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); } - - /* The + 1 here cannot overflow as above we have already - * checked that vertex IDs are within range. */ - max = has_edges ? igraph_vector_int_max(edges) + 1 : 0; IGRAPH_CHECK(igraph_empty(graph, n, directed)); IGRAPH_FINALLY(igraph_destroy, graph); if (has_edges) { - n = igraph_vcount(graph); - if (n < max) { - IGRAPH_CHECK(igraph_add_vertices(graph, (max - n), 0)); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -100,16 +99,16 @@ igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, * * 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 + * arguments and a '-1' needs to be given after the last meaningful * edge argument. * - * Note that only graphs which have vertex IDs smaller than - * the largest value representable by the int type can be created this way. - * If you give larger values then the result is undefined. + * 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 non-negative integer. + * \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 @@ -119,9 +118,8 @@ igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, * 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. The \p first parameter is present for - * technical reasons and represents the first variadic argument. + * 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 @@ -130,24 +128,25 @@ igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, * \example examples/simple/igraph_small.c */ -igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - int first, ...) { - igraph_vector_int_t edges; +int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + ...) { + igraph_vector_t edges; va_list ap; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - va_start(ap, first); - int num = first; - while (num != -1) { - igraph_vector_int_push_back(&edges, num); - num = va_arg(ap, int); + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } + igraph_vector_push_back(&edges, num); } - va_end(ap); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/circulant.c b/src/vendor/cigraph/src/constructors/circulant.c deleted file mode 100644 index 34a796fa1b1..00000000000 --- a/src/vendor/cigraph/src/constructors/circulant.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - 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 . -*/ - -#include "igraph_constructors.h" - -#include "igraph_interface.h" - -#include "math/safe_intop.h" - -/** - * \function igraph_circulant - * \brief Creates a circulant graph. - * - * A circulant graph G(n, shifts) consists of \p n vertices v_0, ..., - * v_(n-1) such that for each \c s_i in the list of offsets \p shifts, \c v_j is - * connected to v_((j + s_i) mod n) for all j. - * - * - * The function can generate either directed or undirected graphs. It does not generate - * multi-edges or self-loops. - * - * \param graph Pointer to an uninitialized graph object, the result will - * be stored here. - * \param n Integer, the number of vertices in the circulant graph. - * \param shifts Integer vector, a list of the offsets within the circulant graph. - * \param directed Boolean, whether to create a directed graph. - * \return Error code. - * - * \sa \ref igraph_ring(), \ref igraph_generalized_petersen(), \ref igraph_extended_chordal_ring() - * - * Time complexity: O(|V||shifts|), the number of vertices in the graph times the number - * of shifts. - */ - -igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *shifts, igraph_bool_t directed) { - - igraph_vector_int_t edges; - igraph_vector_bool_t shift_seen; - igraph_integer_t i, j; - igraph_integer_t limit; - igraph_integer_t shift_size = igraph_vector_int_size(shifts); - - if (n < 0) { - IGRAPH_ERRORF("Number of nodes = %" IGRAPH_PRId " must be non-negative.", IGRAPH_EINVAL, n); - } - if (n == 0) { - return igraph_empty(graph, 0, directed); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - { - igraph_integer_t size; - IGRAPH_SAFE_MULT(n, shift_size, &size); - IGRAPH_SAFE_MULT(size, 2, &size); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); - } - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&shift_seen, n); - VECTOR(shift_seen)[0] = 1; /* do not allow self loops */ - - for (i = 0; i < shift_size; i++) { - /* simplify the shift */ - igraph_integer_t shift = VECTOR(*shifts)[i] % n; - if (shift < 0) { - shift += n; - } - if (!directed) { - if (shift >= (n + 1) / 2) { - shift = n - shift; - } - } - - /* only use shift if non-zero and we haven't seen it before */ - if (!VECTOR(shift_seen)[shift]) { - if (n % 2 == 0 && shift == n / 2 && !directed) { - limit = n / 2; /* this to avoid doubling up the n/2 shift for even n and undirected graph */ - } else { - limit = n; - } - for (j = 0; j < limit; j++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (j + shift) % n)); - } - - VECTOR(shift_seen)[shift] = 1; - } - } - - IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - - igraph_vector_int_destroy(&edges); - igraph_vector_bool_destroy(&shift_seen); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/constructors/de_bruijn.c b/src/vendor/cigraph/src/constructors/de_bruijn.c index 080321b3523..1a1abe0cefb 100644 --- a/src/vendor/cigraph/src/constructors/de_bruijn.c +++ b/src/vendor/cigraph/src/constructors/de_bruijn.c @@ -24,8 +24,6 @@ #include "igraph_interface.h" -#include "math/safe_intop.h" - /** * \function igraph_de_bruijn * \brief Generate a de Bruijn graph. @@ -55,14 +53,15 @@ * * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. */ -igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { +int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { /* m - number of symbols */ /* n - length of strings */ - igraph_integer_t no_of_nodes, no_of_edges; - igraph_vector_int_t edges; - igraph_integer_t i, j; + 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", @@ -76,36 +75,25 @@ igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_inte return igraph_empty(graph, 0, IGRAPH_DIRECTED); } - { - igraph_real_t no_of_nodes_real = pow(m, n); - no_of_nodes = no_of_nodes_real; - if (no_of_nodes != no_of_nodes_real) { - IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for De Bruijn graph.", IGRAPH_EINVAL, - m, n); - } - } - /* no_of_edges = m * no_of_nodes */ - IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); - - { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - } + 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++) { - igraph_integer_t basis = (i * m) % no_of_nodes; + long int basis = (i * mm) % no_of_nodes; for (j = 0; j < m; j++) { - igraph_vector_int_push_back(&edges, i); - igraph_vector_int_push_back(&edges, basis + j); + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, basis + j); } } - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/famous.c b/src/vendor/cigraph/src/constructors/famous.c index 31119cd3225..101878d88d4 100644 --- a/src/vendor/cigraph/src/constructors/famous.c +++ b/src/vendor/cigraph/src/constructors/famous.c @@ -24,20 +24,20 @@ #include "igraph_constructors.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "internal/hacks.h" -const igraph_integer_t igraph_i_famous_bull[] = { +const igraph_real_t igraph_i_famous_bull[] = { 5, 5, 0, 0, 1, 0, 2, 1, 2, 1, 3, 2, 4 }; -const igraph_integer_t igraph_i_famous_chvatal[] = { +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_integer_t igraph_i_famous_coxeter[] = { +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, @@ -45,24 +45,24 @@ const igraph_integer_t igraph_i_famous_coxeter[] = { 16, 20, 17, 20, 21, 23, 21, 26, 22, 24, 22, 27, 23, 25, 24, 26, 25, 27 }; -const igraph_integer_t igraph_i_famous_cubical[] = { +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_integer_t igraph_i_famous_diamond[] = { +const igraph_real_t igraph_i_famous_diamond[] = { 4, 5, 0, 0, 1, 0, 2, 1, 2, 1, 3, 2, 3 }; -const igraph_integer_t igraph_i_famous_dodecahedron[] = { +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_integer_t igraph_i_famous_folkman[] = { +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, @@ -70,59 +70,59 @@ const igraph_integer_t igraph_i_famous_folkman[] = { 16, 12, 17, 13, 17, 13, 18, 14, 18, 14, 19 }; -const igraph_integer_t igraph_i_famous_franklin[] = { +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_integer_t igraph_i_famous_frucht[] = { +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_integer_t igraph_i_famous_grotzsch[] = { +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_integer_t igraph_i_famous_heawood[] = { +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_integer_t igraph_i_famous_herschel[] = { +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_integer_t igraph_i_famous_house[] = { +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_integer_t igraph_i_famous_housex[] = { +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_integer_t igraph_i_famous_icosahedron[] = { +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_integer_t igraph_i_famous_krackhardt_kite[] = { +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_integer_t igraph_i_famous_levi[] = { +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, @@ -131,7 +131,7 @@ const igraph_integer_t igraph_i_famous_levi[] = { 28, 28, 29 }; -const igraph_integer_t igraph_i_famous_mcgee[] = { +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, @@ -139,7 +139,7 @@ const igraph_integer_t igraph_i_famous_mcgee[] = { 21, 21, 22, 22, 23 }; -const igraph_integer_t igraph_i_famous_meredith[] = { +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, @@ -157,14 +157,14 @@ const igraph_integer_t igraph_i_famous_meredith[] = { 28, 38, 42, 35, 66, 59, 63, 52, 56, 45, 49 }; -const igraph_integer_t igraph_i_famous_noperfectmatching[] = { +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_integer_t igraph_i_famous_nonline[] = { +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, @@ -175,17 +175,17 @@ const igraph_integer_t igraph_i_famous_nonline[] = { 43, 44, 45, 44, 46, 45, 46, 45, 47, 46, 47, 46, 48, 47, 48, 47, 49, 48, 49 }; -const igraph_integer_t igraph_i_famous_octahedron[] = { +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_integer_t igraph_i_famous_petersen[] = { +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_integer_t igraph_i_famous_robertson[] = { +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, @@ -193,18 +193,18 @@ const igraph_integer_t igraph_i_famous_robertson[] = { 7, 14, 3, 14, 3, 11, 11, 18 }; -const igraph_integer_t igraph_i_famous_smallestcyclicgroup[] = { +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_integer_t igraph_i_famous_tetrahedron[] = { +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_integer_t igraph_i_famous_thomassen[] = { +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, @@ -213,7 +213,7 @@ const igraph_integer_t igraph_i_famous_thomassen[] = { 23, 10, 27, 11, 28, 12, 29, 13, 30, 15, 30, 16, 31, 17, 32, 18, 33 }; -const igraph_integer_t igraph_i_famous_tutte[] = { +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, @@ -224,20 +224,20 @@ const igraph_integer_t igraph_i_famous_tutte[] = { 40, 39, 41, 40, 41, 42, 43, 43, 45, 44, 45 }; -const igraph_integer_t igraph_i_famous_uniquely3colorable[] = { +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_integer_t igraph_i_famous_walther[] = { +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_integer_t igraph_i_famous_zachary[] = { +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, @@ -252,15 +252,16 @@ const igraph_integer_t igraph_i_famous_zachary[] = { 32, 33 }; -static igraph_error_t igraph_i_famous(igraph_t *graph, const igraph_integer_t *data) { - igraph_integer_t no_of_nodes = data[0]; - igraph_integer_t no_of_edges = data[1]; +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_int_t edges; + igraph_vector_t edges; - igraph_vector_int_view(&edges, data + 3, 2 * no_of_edges); - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - return IGRAPH_SUCCESS; + 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; } /** @@ -416,14 +417,14 @@ static igraph_error_t igraph_i_famous(igraph_t *graph, const igraph_integer_t *d * given name. * * \sa Other functions for creating graph structures: - * \ref igraph_ring(), \ref igraph_kary_tree(), \ref igraph_square_lattice(), - * \ref igraph_full(). + * \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. */ -igraph_error_t igraph_famous(igraph_t *graph, const char *name) { +int igraph_famous(igraph_t *graph, const char *name) { if (!strcasecmp(name, "bull")) { return igraph_i_famous(graph, igraph_i_famous_bull); diff --git a/src/vendor/cigraph/src/constructors/full.c b/src/vendor/cigraph/src/constructors/full.c index 53054849b5b..bddcfb526bf 100644 --- a/src/vendor/cigraph/src/constructors/full.c +++ b/src/vendor/cigraph/src/constructors/full.c @@ -24,8 +24,6 @@ #include "igraph_interface.h" -#include "math/safe_intop.h" - /** * \ingroup generators * \function igraph_full @@ -57,275 +55,71 @@ * O(|E|)=O(|V||V|) * here. * - * \sa \ref igraph_square_lattice(), \ref igraph_star(), \ref igraph_kary_tree() + * \sa \ref igraph_lattice(), \ref igraph_star(), \ref igraph_tree() * for creating other regular structures. * * \example examples/simple/igraph_full.c */ -igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_edges2; - igraph_integer_t i, j; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i, j; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("invalid number of vertices", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (directed && loops) { - /* ecount = n * n */ - IGRAPH_SAFE_MULT(n, n, &no_of_edges2); - IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * n)); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, j); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ } } } else if (directed && !loops) { - /* ecount = n * (n - 1) */ - IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); - IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * (n - 1))); for (i = 0; i < n; i++) { for (j = 0; j < i; j++) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, j); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ } for (j = i + 1; j < n; j++) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, j); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ } } } else if (!directed && loops) { - /* ecount = n * (n + 1) / 2 */ - IGRAPH_SAFE_ADD(n, 1, &no_of_edges2); - IGRAPH_SAFE_MULT(n, no_of_edges2, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n + 1))); for (i = 0; i < n; i++) { for (j = i; j < n; j++) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, j); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ } } } else { - /* ecount = n * (n - 1) / 2 */ - IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n - 1))); for (i = 0; i < n; i++) { for (j = i + 1; j < n; j++) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, j); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, j); /* reserved */ } } } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_full_multipartite - * \brief Create a full multipartite graph. - * - * A multipartite graph contains two or more types of vertices and connections - * are only possible between two vertices of different types. This function - * creates a complete multipartite graph. - * - * \param graph Pointer to an igraph_t object, the graph will be - * created here. - * \param types Pointer to an integer vector. If not a null pointer, - * the type of each vertex will be stored here. - * \param n Pointer to an integer vector, the number of vertices - * of each type. - * \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 low-index vertices to high-index vertices; 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_bipartite() for full bipartite graphs. - */ -igraph_error_t igraph_full_multipartite(igraph_t *graph, - igraph_vector_int_t *types, - const igraph_vector_int_t *n, - igraph_bool_t directed, - igraph_neimode_t mode) { - - igraph_vector_int_t edges; - igraph_vector_int_t n_acc; - - igraph_integer_t no_of_types = igraph_vector_int_size(n); - - if (no_of_types == 0) { - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - if (types) { - igraph_vector_int_clear(types); - } - return IGRAPH_SUCCESS; - } - - if (igraph_vector_int_min(n) < 0) { - IGRAPH_ERROR("Number of vertices must not be negative in any partition.", IGRAPH_EINVAL); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&n_acc, no_of_types+1); - VECTOR(n_acc)[0] = 0; - for (igraph_integer_t i = 1; i < no_of_types+1; i++) { - IGRAPH_SAFE_ADD(VECTOR(n_acc)[i-1], VECTOR(*n)[i-1], - &VECTOR(n_acc)[i]); - } - - igraph_integer_t no_of_edges2 = 0; - - for (igraph_integer_t i = 0; i < no_of_types; i++) { - igraph_integer_t v = VECTOR(*n)[i]; - igraph_integer_t partial_sum = VECTOR(n_acc)[no_of_types] - v; - IGRAPH_SAFE_MULT(partial_sum, v, &partial_sum); - IGRAPH_SAFE_ADD(no_of_edges2, partial_sum, &no_of_edges2); - } - - if (directed && mode == IGRAPH_ALL) { - IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - - igraph_integer_t ptr = 0; - - for (igraph_integer_t from_type = 0; from_type < no_of_types-1; from_type++) { - igraph_integer_t edge_from = VECTOR(n_acc)[from_type]; - for (igraph_integer_t i = 0; i < VECTOR(*n)[from_type]; i++) { - for (igraph_integer_t to_type = from_type+1; to_type < no_of_types; to_type++) { - igraph_integer_t edge_to = VECTOR(n_acc)[to_type]; - for (igraph_integer_t j = 0; j < VECTOR(*n)[to_type]; j++) { - if (!directed || mode == IGRAPH_OUT) { - VECTOR(edges)[ptr++] = edge_from; - VECTOR(edges)[ptr++] = edge_to; - } else if (mode == IGRAPH_IN) { - VECTOR(edges)[ptr++] = edge_to; - VECTOR(edges)[ptr++] = edge_from; - } else { - VECTOR(edges)[ptr++] = edge_from; - VECTOR(edges)[ptr++] = edge_to; - VECTOR(edges)[ptr++] = edge_to; - VECTOR(edges)[ptr++] = edge_from; - } - edge_to++; - } - } - edge_from++; - } - } - - IGRAPH_CHECK(igraph_create(graph, &edges, VECTOR(n_acc)[no_of_types], directed)); - - if (types) { - IGRAPH_CHECK(igraph_vector_int_resize(types, VECTOR(n_acc)[no_of_types])); - if (VECTOR(n_acc)[no_of_types] > 0) { - igraph_integer_t v = 1; - for (igraph_integer_t i = 0; i < VECTOR(n_acc)[no_of_types]; i++) { - if (i == VECTOR(n_acc)[v]) { - v++; - } - VECTOR(*types)[i] = v-1; - } - } - } - - igraph_vector_int_destroy(&edges); - igraph_vector_int_destroy(&n_acc); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_turan - * \brief Create a Turán graph. - * - * Turán graphs are complete multipartite graphs with the property - * that the sizes of the partitions are as close to equal as possible. - * - * This function generates undirected graphs. The null graph is - * returned when the number of vertices is zero. A complete graph is - * returned if the number of partitions is greater than the number of - * vertices. - * - * \param graph Pointer to an igraph_t object, the graph will be - * created here. - * \param types Pointer to an integer vector. If not a null pointer, - * the type (partition index) of each vertex will be stored here. - * \param n Integer, the number of vertices in the graph. - * \param r Integer, the number of partitions of the graph, must be - * positive. - * \return Error code. - * - * Time complexity: O(|V|+|E|), linear in the number of vertices and - * edges. - * - * \sa \ref igraph_full_multipartite() for full multipartite graphs. - */ -igraph_error_t igraph_turan(igraph_t *graph, - igraph_vector_int_t *types, - igraph_integer_t n, - igraph_integer_t r) { - igraph_integer_t quotient; - igraph_integer_t remainder; - igraph_vector_int_t subsets; - - if (n < 0) { - IGRAPH_ERRORF("Number of vertices must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); - } - - if (r <= 0) { - IGRAPH_ERRORF("Number of partitions must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, r); - } - - if (n == 0) { - IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_UNDIRECTED)); - if (types) { - igraph_vector_int_clear(types); - } - return IGRAPH_SUCCESS; - } - - if (r > n) { - r = n; - } - - quotient = n / r; - remainder = n % r; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&subsets, r); - - igraph_vector_int_fill(&subsets, quotient); - for (igraph_integer_t i = 0; i < remainder; i++) { - VECTOR(subsets)[i]++; - } - - IGRAPH_CHECK(igraph_full_multipartite(graph, types, &subsets, - IGRAPH_UNDIRECTED, IGRAPH_ALL)); - igraph_vector_int_destroy(&subsets); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_full_citation - * \brief Creates a full citation graph. + * Creates a full citation graph * * This is a directed graph, where every i->j edge is * present if and only if j<i. @@ -340,21 +134,12 @@ igraph_error_t igraph_turan(igraph_t *graph, * * Time complexity: O(|V|^2), as we have many edges. */ -igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, +int igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { - igraph_vector_int_t edges; - igraph_integer_t i, j, ptr = 0; - - if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); - } - - { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(n, n-1, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - } + 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; @@ -363,7 +148,7 @@ igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/generalized_petersen.c b/src/vendor/cigraph/src/constructors/generalized_petersen.c deleted file mode 100644 index 0b34f7e8488..00000000000 --- a/src/vendor/cigraph/src/constructors/generalized_petersen.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - 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 . -*/ - -#include "igraph_constructors.h" - -#include "math/safe_intop.h" - -/** - * \function igraph_generalized_petersen - * \brief Creates a Generalized Petersen graph. - * - * The generalized Petersen graph G(n, k) consists of \p n vertices - * \c v_0, ..., \c v_n forming an "outer" cycle graph, and \p n additional vertices - * \c u_0, ..., \c u_n forming an "inner" circulant graph where u_i - * is connected to u_(i + k mod n). Additionally, all \c v_i are - * connected to \c u_i. - * - * - * G(n, k) has \c 2n vertices and \c 3n edges. The Petersen graph - * itself is G(5, 2). - * - * - * Reference: - * - * - * M. E. Watkins, - * A Theorem on Tait Colorings with an Application to the Generalized Petersen Graphs, - * Journal of Combinatorial Theory 6, 152-164 (1969). - * https://doi.org/10.1016%2FS0021-9800%2869%2980116-X - * - * \param graph Pointer to an uninitialized graph object, the result will - * be stored here. - * \param n Integer, \c n is the number of vertices in the inner and outer - * cycle/circulant graphs. It must be at least 3. - * \param k Integer, \c k is the shift of the circulant graph. It must be - * positive and less than n/2. - * \return Error code. - * - * \sa \ref igraph_famous() for the original Petersen graph. - * - * Time complexity: O(|V|), the number of vertices in the graph. - */ -igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k) { - /* This is a generalized Petersen graph constructor */ - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes, no_of_edges2; - igraph_integer_t i; - - if (n < 3) { - IGRAPH_ERRORF("n = %" IGRAPH_PRId " must be at least 3.", IGRAPH_EINVAL, n); - } - - IGRAPH_SAFE_MULT(n, 2, &no_of_nodes); - - /* The seemingly redundant k < n check avoids integer overflow on 2*k in 2*k < n. - * Note that 2*n has already been checked not to overflow above. */ - if (! (k > 0 && k < n && 2*k < n)) { - IGRAPH_ERRORF("k = %" IGRAPH_PRId " must be positive and less than n/2 with n = %" IGRAPH_PRId ".", IGRAPH_EINVAL, k, n); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_SAFE_MULT(n, 6, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - - for (i = 0; i < n; i++) { - igraph_vector_int_push_back(&edges, i); - igraph_vector_int_push_back(&edges, (i + 1) % n); - igraph_vector_int_push_back(&edges, i); - igraph_vector_int_push_back(&edges, i + n); - igraph_vector_int_push_back(&edges, i + n); - igraph_vector_int_push_back(&edges, ((i + k) % n) + n); - } - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); - - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/constructors/kautz.c b/src/vendor/cigraph/src/constructors/kautz.c index 644037bbf8b..017d05c6a46 100644 --- a/src/vendor/cigraph/src/constructors/kautz.c +++ b/src/vendor/cigraph/src/constructors/kautz.c @@ -24,8 +24,6 @@ #include "igraph_interface.h" -#include "math/safe_intop.h" - /** * \function igraph_kautz * \brief Generate a Kautz graph. @@ -58,19 +56,20 @@ * 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. */ -igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { +int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { /* m+1 - number of symbols */ /* n+1 - length of strings */ - igraph_integer_t no_of_nodes, no_of_edges; - igraph_integer_t allstrings; - igraph_integer_t i, j, idx = 0; - igraph_vector_int_t edges; - igraph_vector_int_t digits, table; - igraph_vector_int_t index1, index2; - igraph_integer_t actb = 0; - igraph_integer_t actvalue = 0; + 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", @@ -84,50 +83,32 @@ igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_ return igraph_empty(graph, 0, IGRAPH_DIRECTED); } - /* no_of_nodes = ((m + 1) * pow(m, n)) */ - { - igraph_real_t m_to_pow_n_real = pow(m, n); - igraph_integer_t m_to_pow_n = m_to_pow_n_real; - if (m_to_pow_n != m_to_pow_n_real) { - IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, - m, n); - } - IGRAPH_SAFE_MULT(m+1, m_to_pow_n, &no_of_nodes); - } - /* no_of_edges = m * no_of_nodes */ - IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); - - { - igraph_real_t allstrings_real = pow(m + 1, n + 1); - allstrings = allstrings_real; - if (allstrings != allstrings_real) { - IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, - m, n); - } - } + 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_init(&table, n + 1)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &table); + 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_int_init(&digits, n + 1)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &digits); - IGRAPH_CHECK(igraph_vector_int_init(&index1, pow(m + 1, n + 1))); - IGRAPH_FINALLY(igraph_vector_int_destroy, &index1); - IGRAPH_CHECK(igraph_vector_int_init(&index2, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &index2); + 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 */ - igraph_integer_t z = 0; + long int z = 0; if (VECTOR(digits)[actb] == 0) { z = 1; } @@ -151,7 +132,7 @@ igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_ /* not yet, we need a valid prefix now */ while (1) { /* try to increase digits at position actb */ - igraph_integer_t next = VECTOR(digits)[actb] + 1; + long int next = VECTOR(digits)[actb] + 1; if (actb != 0 && VECTOR(digits)[actb - 1] == next) { next++; } @@ -168,19 +149,15 @@ igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_ } } - { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - } + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); /* Now come the edges at last */ for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t fromvalue = VECTOR(index2)[i]; - igraph_integer_t lastdigit = fromvalue % (m + 1); - igraph_integer_t basis = (fromvalue * (m + 1)) % allstrings; + 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++) { - igraph_integer_t tovalue, to; + long int tovalue, to; if (j == lastdigit) { continue; } @@ -189,20 +166,21 @@ igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_ if (to < 0) { continue; } - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to); } } - igraph_vector_int_destroy(&index2); - igraph_vector_int_destroy(&index1); - igraph_vector_int_destroy(&digits); - igraph_vector_int_destroy(&table); + 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, no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_DIRECTED)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/lattices.c b/src/vendor/cigraph/src/constructors/lattices.c deleted file mode 100644 index 14755de4e3b..00000000000 --- a/src/vendor/cigraph/src/constructors/lattices.c +++ /dev/null @@ -1,603 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_constructors.h" -#include "igraph_interface.h" - -#include "core/interruption.h" -#include "math/safe_intop.h" - -#define MIN(n, m) (n < m ? n : m) -#define MAX(n, m) (n < m ? m : n) - -#define VERTEX_INDEX(i, j) \ - lex_ordering ? row_count * (i - VECTOR(*row_start_vector)[j]) + j : (VECTOR(row_lengths_prefix_sum_vector)[j] + i - VECTOR(*row_start_vector)[j]) - -#define ROW_END(j) (VECTOR(*row_start_vector)[j] + VECTOR(*row_lengths_vector)[j] - 1) -#define ADD_EDGE_IJ_KL_IF_EXISTS(i, j, k, l) \ - if (VECTOR(*row_start_vector)[l] <= k && k <= ROW_END(l) && 0 <= l && l <= row_count - 1) \ - { \ - igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ - igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ - if (directed && mutual) \ - { \ - igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ - igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ - } \ - } - -#define COMPUTE_NUMBER_OF_VERTICES() \ - do \ - { \ - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_prefix_sum_vector, row_count + 1); \ - VECTOR(row_lengths_prefix_sum_vector)[0] = 0; \ - for (i = 1; i < row_count + 1; i++) \ - { \ - IGRAPH_SAFE_ADD(VECTOR(row_lengths_prefix_sum_vector)[i - 1], VECTOR(*row_lengths_vector)[i - 1], &(VECTOR(row_lengths_prefix_sum_vector)[i])); \ - } \ - no_of_nodes = VECTOR(row_lengths_prefix_sum_vector)[row_count]; \ - } while (0) - -/** - * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j - * and (i, j) is connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1) provided a vertex - * exists. Thus, all vertices have degree at most 6. - * - * - * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being - * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) unless - * \c lex_ordering is set to true in which case the roles of the coordinates are reversed. - * - * \param graph An uninitialized graph object. - * \param directed Boolean, whether to create a directed graph. - * If the \c mutual argument is 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 lex_ordering Boolean, set to true if the vertices of the resulting graph are ordered - * lexicographically with the 1st coordinate being more significant. Use only when all the - * rows have the number of vertices. - * \param row_lengths_vector Integer vector, defines the number of vertices with - * the second coordinate equal to the index. The length of this vector must match - * the length of \p row_start_vector. All coordinates must be non-negative. - * \param row_start_vector Integer vector, defines the leftmost coordinate of - * the vertex with the second coordinate equal to the index. - * - * \return Error code: - * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the - * row_start_vector. - * - * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. - */ -static igraph_error_t triangular_lattice( - igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t lex_ordering, - const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); - igraph_integer_t no_of_nodes; - igraph_vector_int_t row_lengths_prefix_sum_vector; - igraph_integer_t i, j; - - if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { - IGRAPH_ERRORF( - "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " - "row_start_vector (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - igraph_vector_int_size(row_lengths_vector), - igraph_vector_int_size(row_start_vector)); - } - - - if (row_count > 0 && lex_ordering && !igraph_vector_int_isininterval(row_lengths_vector, VECTOR(*row_lengths_vector)[0], VECTOR(*row_lengths_vector)[0])) { - IGRAPH_ERROR( - "row_lengths_vector must have all the coordinates the same", IGRAPH_EINVAL); - } - - for (i = 0; i < row_count; i++) { - if (VECTOR(*row_lengths_vector)[i] < 0) { - IGRAPH_ERRORF( - "row_lengths_vector vector must have non-negative coordinates, " - "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", - IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); - } - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - - COMPUTE_NUMBER_OF_VERTICES(); - - /* computing the number of edges in the constructed triangular lattice */ - igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; - igraph_integer_t multiplier = mutual && directed ? 4 : 2; - for (j = 0; j < row_count - 1; j++) { - IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); - IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1))) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1]) + 1, - &no_of_edges2); - IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1)) + 1) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1] + 1) + 1, - &no_of_edges2); - } - IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - - /* constructing the edge array */ - igraph_integer_t k; - for (j = 0; j < row_count; j++) { - IGRAPH_ALLOW_INTERRUPTION(); - for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { - k = VECTOR(*row_start_vector)[j] + i; - ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); - if (j < row_count - 1) { - ADD_EDGE_IJ_KL_IF_EXISTS(k, j, k, (j + 1)); - ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); - } - } - } - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - - igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t triangular_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { - igraph_integer_t row_count = size; - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); - - for (i = 0; i < row_count; i++) { - VECTOR(row_lengths_vector)[i] = size - i; - VECTOR(row_start_vector)[i] = 0; - } - - IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t triangular_lattice_rectangle_shape( - igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, - igraph_bool_t directed, igraph_bool_t mutual) { - igraph_integer_t row_count = size_x; - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); - - for (i = 0; i < row_count; i++) { - VECTOR(row_lengths_vector)[i] = size_y; - VECTOR(row_start_vector)[i] = (row_count - i) / 2; - } - - IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t triangular_lattice_hex_shape( - igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, - igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual) { - igraph_integer_t row_count = size_y + size_z - 1; - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); - - igraph_integer_t row_length = size_x; - igraph_integer_t row_start = size_y - 1; - igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); - igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); - igraph_integer_t sgn_flag = size_y < size_z ? 0 : -1; - - for (i = 0; i < row_count; i++) { - VECTOR(row_lengths_vector)[i] = row_length; - VECTOR(row_start_vector)[i] = row_start; - - if (i < first_threshold) { - row_length++; - row_start--; - } else if (i < second_threshold) { - row_start += sgn_flag; - } else { - row_length--; - } - } - - IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_triangular_lattice - * \brief A triangular lattice with the given shape. - * - * \experimental - * - * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j - * and (i, j) is generally connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1). - * The function constructs a planar dual of the graph constructed by \ref igraph_hexagonal_lattice(). - * In particular, there a one-to-one correspondence between the vertices in the constructed graph - * and the cycles of length 6 in the graph constructed by \ref igraph_hexagonal_lattice() - * with the same \p dims parameter. - * - * - * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being - * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) - * - * \param graph An uninitialized graph object. - * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) - * If \p dims is of length 1, the resulting lattice has a triangular shape - * where each side of the triangle contains dims[0] vertices. - * If \p dims is of length 2, the resulting lattice has a - * "quasi rectangular" shape with the sides containing dims[0] and - * dims[1] vertices, respectively. - * If \p dims is of length 3, the resulting lattice has a hexagonal shape - * where the sides of the hexagon contain dims[0], dims[1] and - * dims[2] vertices. - * All coordinates must be non-negative. - * \param directed Boolean, whether to create a directed graph. - * If the \c mutual argument is 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. - * \return Error code: - * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components - * at least 1. - * \sa \ref igraph_hexagonal_lattice() for creating a triangular lattice. - * - * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. - * - */ -igraph_error_t igraph_triangular_lattice( - igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, - igraph_bool_t mutual) { - igraph_integer_t num_dims = igraph_vector_int_size(dims); - if (igraph_vector_int_any_smaller(dims, 0)) { - IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); - } - /* If a coordinate of dims is 0 the result is an empty graph. */ - if (igraph_vector_int_contains(dims, 0)) { - return igraph_empty(graph, 0, directed); - } - - switch (num_dims) { - case 1: - IGRAPH_CHECK(triangular_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); - break; - case 2: - IGRAPH_CHECK(triangular_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); - break; - case 3: - IGRAPH_CHECK(triangular_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); - break; - default: - IGRAPH_ERRORF( - "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, num_dims); - } - - return IGRAPH_SUCCESS; -} - - -/** - * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j - * and (i, j) is connected with (i + 1, j), and if i is odd also with (i - 1, j + 1) provided a vertex - * exists. Thus, all vertices have degree at most 3. - * - * - * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being - * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1). - * - * \param graph An uninitialized graph object. - * \param directed Boolean, whether to create a directed graph. - * If the \c mutual argument is 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 row_lengths_vector Integer vector, defines the number of vertices with - * the second coordinate equal to the index. The length of this vector must match - * the length of \p row_start_vector. All coordinates must be non-negative. - * \param row_start_vector Integer vector, defines the leftmost coordinate of - * the vertex with the second coordinate equal to the index. - * - * \return Error code: - * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the - * row_start_vector. - * - * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. - */ -static igraph_error_t hexagonal_lattice( - igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, - const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector -) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); - igraph_integer_t no_of_nodes; - igraph_vector_int_t row_lengths_prefix_sum_vector; - igraph_integer_t i, j; - igraph_bool_t lex_ordering = false; - - if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { - IGRAPH_ERRORF( - "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " - "row_start_vector (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - igraph_vector_int_size(row_lengths_vector), - igraph_vector_int_size(row_start_vector) - ); - } - - for (i = 0; i < row_count; i++) { - if (VECTOR(*row_lengths_vector)[i] < 0) { - IGRAPH_ERRORF( - "row_lengths_vector vector must have non-negative coordinates, " - "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", - IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); - } - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - - COMPUTE_NUMBER_OF_VERTICES(); - - /* computing the number of edges in the constructed hex lattice */ - igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; - igraph_integer_t multiplier = mutual && directed ? 4 : 2, low, high; - for (j = 0; j < row_count - 1; j++) { - IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); - low = MAX((VECTOR(*row_start_vector)[j] - 1), (VECTOR(*row_start_vector)[j + 1])); - low = low % 2 ? low + 1 : low; - high = MIN((ROW_END(j) - 1), (ROW_END(j + 1))); - high = high % 2 ? high - 1 : high; - IGRAPH_SAFE_ADD(no_of_edges2, (high - low) / 2 + 1, &no_of_edges2); - } - IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - - /* constructing the edge array */ - igraph_integer_t k; - for (j = 0; j < row_count; j++) { - IGRAPH_ALLOW_INTERRUPTION(); - for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { - k = VECTOR(*row_start_vector)[j] + i; - ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); - if (j < row_count - 1 && k % 2 == 1) { - ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); - } - } - } - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - - igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t hexagonal_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { - igraph_integer_t row_count; - IGRAPH_SAFE_ADD(size, 2, &row_count); - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count - 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count - 1); - - for (i = 0; i < row_count - 1; i++) { - VECTOR(row_lengths_vector)[i] = 2 * (row_count - i) - (i ? 1 : 3); - VECTOR(row_start_vector)[i] = (i ? 0 : 1); - } - - IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t hexagonal_lattice_rectangle_shape( - igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, - igraph_bool_t directed, igraph_bool_t mutual -) { - igraph_integer_t row_count; - IGRAPH_SAFE_ADD(size_x, 1, &row_count); - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t actual_size_y; - IGRAPH_SAFE_ADD(size_y, 1, &actual_size_y); - IGRAPH_SAFE_MULT(actual_size_y, 2, &actual_size_y); - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); - - igraph_bool_t is_first_row, is_last_row, is_start_odd; - - for (i = 0; i < row_count; i++) { - is_first_row = !i; - is_last_row = i == row_count - 1; - is_start_odd = (row_count - i - 1) % 2; - VECTOR(row_lengths_vector)[i] = actual_size_y - (is_first_row || is_last_row ? 1 : 0); - VECTOR(row_start_vector)[i] = row_count - i - 1 + (is_first_row && !is_start_odd ? 1 : 0); - } - - IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t hexagonal_lattice_hex_shape( - igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, - igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual -) { - igraph_integer_t row_count = size_y + size_z; - igraph_vector_int_t row_lengths_vector; - igraph_vector_int_t row_start_vector; - igraph_integer_t i; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); - - igraph_integer_t row_length; - IGRAPH_SAFE_MULT(size_x, 2, &row_length); - IGRAPH_SAFE_ADD(row_length, 1, &row_length); - igraph_integer_t row_start; - IGRAPH_SAFE_MULT(size_y, 2, &row_start); - IGRAPH_SAFE_ADD(row_start, -1, &row_start); - igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); - igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); - igraph_integer_t sgn_flag = size_y < size_z ? 0 : -2; - - for (i = 0; i < row_count; i++) { - VECTOR(row_lengths_vector)[i] = row_length; - VECTOR(row_start_vector)[i] = row_start; - - if (i < first_threshold) { - row_length += 2; - row_start -= 2; - } else if (i < second_threshold) { - row_start += sgn_flag; - } else { - row_length -= 2; - } - if (i == size_y - 1) { - row_start--; - row_length++; - } - if (i == size_z - 1) { - row_length++; - } - } - - IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); - - igraph_vector_int_destroy(&row_lengths_vector); - igraph_vector_int_destroy(&row_start_vector); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_hexagonal_lattice - * \brief A hexagonal lattice with the given shape. - * - * \experimental - * - * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j - * and (i, j) is generally connected with (i + 1, j), and if i is odd also with (i - 1, j + 1). - * The function constructs a planar dual of the graph constructed by \ref igraph_triangular_lattice(). - * In particular, there a one-to-one correspondence between the cycles of length 6 in the constructed graph - * and the vertices of the graph constructed by \ref igraph_triangular_lattice() function - * with the same \p dims parameter. - * - * - * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being - * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) - * - * \param graph An uninitialized graph object. - * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) - * If \p dims is of length 1, the resulting lattice has a triangular shape - * where each side of the triangle contains dims[0] vertices. - * If \p dims is of length 2, the resulting lattice has a - * "quasi rectangular" shape with the sides containing dims[0] and - * dims[1] vertices, respectively. - * If \p dims is of length 3, the resulting lattice has a hexagonal shape - * where the sides of the hexagon contain dims[0], dims[1] and - * dims[2] vertices. - * All coordinates must be non-negative. - * \param directed Boolean, whether to create a directed graph. - * If the \c mutual argument is 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. - * \return Error code: - * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components - * at least 1. - * \sa \ref igraph_triangular_lattice() for creating a triangular lattice. - * - * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. - * - */ -igraph_error_t igraph_hexagonal_lattice( - igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, - igraph_bool_t mutual -) { - igraph_integer_t num_dims = igraph_vector_int_size(dims); - if (igraph_vector_int_any_smaller(dims, 0)) { - IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); - } - /* If a coordinate of dims is 0 the result is an empty graph. */ - if (igraph_vector_int_any_smaller(dims, 1)) { - return igraph_empty(graph, 0, directed); - } - - switch (num_dims) { - case 1: - IGRAPH_CHECK(hexagonal_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); - break; - case 2: - IGRAPH_CHECK(hexagonal_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); - break; - case 3: - IGRAPH_CHECK(hexagonal_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); - break; - default: - IGRAPH_ERRORF( - "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, num_dims - ); - } - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/constructors/lcf.c b/src/vendor/cigraph/src/constructors/lcf.c index 76534632d6c..b7ee6bed47f 100644 --- a/src/vendor/cigraph/src/constructors/lcf.c +++ b/src/vendor/cigraph/src/constructors/lcf.c @@ -24,8 +24,6 @@ #include "igraph_operators.h" -#include "math/safe_intop.h" - /** * \function igraph_lcf_vector * \brief Creates a graph from LCF notation. @@ -45,27 +43,20 @@ * Time complexity: O(|V|+|E|), linear in the number of vertices plus * the number of edges. */ -igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, - const igraph_vector_int_t *shifts, +int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_t *shifts, igraph_integer_t repeats) { - igraph_vector_int_t edges; - igraph_integer_t no_of_shifts = igraph_vector_int_size(shifts); - igraph_integer_t ptr = 0, i, sptr = 0; - igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_edges = n + no_of_shifts * repeats; - igraph_integer_t no_of_edges2; + 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 not be negative.", IGRAPH_EINVAL); } - - /* no_of_edges = n + no_of_shifts * repeats */ - IGRAPH_SAFE_MULT(no_of_shifts, repeats, &no_of_edges); - IGRAPH_SAFE_ADD(no_of_edges, n, &no_of_edges); - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); if (no_of_nodes > 0) { /* Create a ring first */ @@ -78,20 +69,21 @@ igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, /* Then add the rest */ while (ptr < 2 * no_of_edges) { - igraph_integer_t sh = VECTOR(*shifts)[sptr % no_of_shifts]; - igraph_integer_t from = sptr % no_of_nodes; - igraph_integer_t to = (no_of_nodes + sptr + sh) % no_of_nodes; + 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, no_of_nodes, IGRAPH_UNDIRECTED)); - IGRAPH_CHECK(igraph_simplify(graph, true, true, NULL)); - igraph_vector_int_destroy(&edges); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -120,36 +112,30 @@ igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, * * \example examples/simple/igraph_lcf.c */ -igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { - igraph_vector_int_t shifts; +int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { + igraph_vector_t shifts; igraph_integer_t repeats; va_list ap; - IGRAPH_VECTOR_INT_INIT_FINALLY(&shifts, 0); + IGRAPH_VECTOR_INIT_FINALLY(&shifts, 0); va_start(ap, n); while (1) { - igraph_error_t err; int num = va_arg(ap, int); if (num == 0) { break; } - err = igraph_vector_int_push_back(&shifts, num); - if (err != IGRAPH_SUCCESS) { - va_end(ap); - IGRAPH_ERROR("", err); - } + IGRAPH_CHECK(igraph_vector_push_back(&shifts, num)); } - va_end(ap); - if (igraph_vector_int_size(&shifts) == 0) { + if (igraph_vector_size(&shifts) == 0) { repeats = 0; } else { - repeats = igraph_vector_int_pop_back(&shifts); + repeats = (igraph_integer_t) igraph_vector_pop_back(&shifts); } IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats)); - igraph_vector_int_destroy(&shifts); + igraph_vector_destroy(&shifts); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/constructors/linegraph.c b/src/vendor/cigraph/src/constructors/linegraph.c index a75e74373db..13f69e0e977 100644 --- a/src/vendor/cigraph/src/constructors/linegraph.c +++ b/src/vendor/cigraph/src/constructors/linegraph.c @@ -29,97 +29,98 @@ /* 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 igraph_error_t igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, n; - igraph_vector_int_t adjedges, adjedges2; - igraph_vector_int_t edges; - igraph_integer_t prev = -1; +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_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges2, 0); + 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++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, 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, from, IGRAPH_ALL)); + IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, + IGRAPH_ALL)); } - n = igraph_vector_int_size(&adjedges); + n = igraph_vector_size(&adjedges); for (j = 0; j < n; j++) { - igraph_integer_t e = VECTOR(adjedges)[j]; + long int e = (long int) VECTOR(adjedges)[j]; if (e < i) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); } } - IGRAPH_CHECK(igraph_incident(graph, &adjedges2, to, IGRAPH_ALL)); - n = igraph_vector_int_size(&adjedges2); + IGRAPH_CHECK(igraph_incident(graph, &adjedges2, (igraph_integer_t) to, + IGRAPH_ALL)); + n = igraph_vector_size(&adjedges2); for (j = 0; j < n; j++) { - igraph_integer_t e = VECTOR(adjedges2)[j]; + long int e = (long int) VECTOR(adjedges2)[j]; if (e < i) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); } } prev = from; } - igraph_vector_int_destroy(&adjedges); - igraph_vector_int_destroy(&adjedges2); + igraph_vector_destroy(&adjedges); + igraph_vector_destroy(&adjedges2); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_create(linegraph, &edges, no_of_edges, igraph_is_directed(graph))); - - igraph_vector_int_destroy(&edges); + igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges, + igraph_is_directed(graph)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, n; - igraph_vector_int_t adjedges; - igraph_vector_int_t edges; - igraph_integer_t prev = -1; +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_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); + long int from = IGRAPH_FROM(graph, i); IGRAPH_ALLOW_INTERRUPTION(); if (from != prev) { - IGRAPH_CHECK(igraph_incident(graph, &adjedges, from, IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, + IGRAPH_IN)); } - n = igraph_vector_int_size(&adjedges); + n = igraph_vector_size(&adjedges); for (j = 0; j < n; j++) { - igraph_integer_t e = VECTOR(adjedges)[j]; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + 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_int_destroy(&adjedges); + igraph_vector_destroy(&adjedges); IGRAPH_FINALLY_CLEAN(1); - - IGRAPH_CHECK(igraph_create(linegraph, &edges, no_of_edges, igraph_is_directed(graph))); - - igraph_vector_int_destroy(&edges); + igraph_create(linegraph, &edges, (igraph_integer_t) no_of_edges, igraph_is_directed(graph)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -130,10 +131,10 @@ static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_ * 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 single vertex of an undirected self-loop is counted as two end points. + * The vertex of a loop is counted as two end points. * * - * The line graph L(G) of a G directed graph is slightly different: + * 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. @@ -145,7 +146,6 @@ static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_ * * 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. @@ -154,7 +154,7 @@ static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_ * Time complexity: O(|V|+|E|), the number of edges plus the number of vertices. */ -igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { +int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { if (igraph_is_directed(graph)) { return igraph_i_linegraph_directed(graph, linegraph); diff --git a/src/vendor/cigraph/src/constructors/prufer.c b/src/vendor/cigraph/src/constructors/prufer.c index 489026f9efd..40fa3ea53a1 100644 --- a/src/vendor/cigraph/src/constructors/prufer.c +++ b/src/vendor/cigraph/src/constructors/prufer.c @@ -22,7 +22,7 @@ #include "igraph_constructors.h" -#include "math/safe_intop.h" +#include "igraph_interface.h" /** * \ingroup generators @@ -47,33 +47,29 @@ * invalid Prüfer sequence given * \endclist * - * \sa \ref igraph_to_prufer(), \ref igraph_kary_tree(), \ref igraph_tree_game() + * \sa \ref igraph_to_prufer(), \ref igraph_tree(), \ref igraph_tree_game() * */ -igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { +int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { igraph_vector_int_t degree; - igraph_vector_int_t edges; - igraph_integer_t n; - igraph_integer_t i, k; - igraph_integer_t u, v; /* vertices */ - igraph_integer_t ec; + igraph_vector_t edges; + long n; + long i, k; + long u, v; /* vertices */ + long ec; - IGRAPH_SAFE_ADD(igraph_vector_int_size(prufer), 2, &n); + n = igraph_vector_int_size(prufer) + 2; IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, n); /* initializes vector to zeros */ - { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(n - 1, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - } + 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) { - igraph_integer_t w = VECTOR(*prufer)[i]; - if (w >= n || w < 0) { - IGRAPH_ERROR("Invalid Prufer sequence.", IGRAPH_EINVAL); + long u = VECTOR(*prufer)[i]; + if (u >= n || u < 0) { + IGRAPH_ERROR("Invalid Prufer sequence", IGRAPH_EINVAL); } - VECTOR(degree)[w] += 1; + VECTOR(degree)[u] += 1; } v = 0; /* initialize v now, in case Prufer sequence is empty */ @@ -113,9 +109,9 @@ igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *pr VECTOR(edges)[ec++] = v; VECTOR(edges)[ec++] = u; - IGRAPH_CHECK(igraph_create(graph, &edges, n, IGRAPH_UNDIRECTED)); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) n, /* directed = */ 0)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); diff --git a/src/vendor/cigraph/src/constructors/regular.c b/src/vendor/cigraph/src/constructors/regular.c index 9a3267a054d..ec87710c966 100644 --- a/src/vendor/cigraph/src/constructors/regular.c +++ b/src/vendor/cigraph/src/constructors/regular.c @@ -17,6 +17,7 @@ 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" @@ -26,7 +27,6 @@ #include "igraph_operators.h" #include "core/interruption.h" -#include "math/safe_intop.h" /** * \ingroup generators @@ -66,36 +66,32 @@ * Time complexity: O(|V|), the * number of vertices in the graph. * - * \sa \ref igraph_square_lattice(), \ref igraph_ring(), \ref igraph_kary_tree() + * \sa \ref igraph_lattice(), \ref igraph_ring(), \ref igraph_tree() * for creating other regular structures. * * \example examples/simple/igraph_star.c */ -igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, +int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, igraph_integer_t center) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t i; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int i; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVVID); + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVVID); } if (center < 0 || center > n - 1) { - IGRAPH_ERROR("Invalid center vertex.", IGRAPH_EINVAL); + 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 star mode.", IGRAPH_EINVMODE); + IGRAPH_ERROR("invalid mode", IGRAPH_EINVMODE); } if (mode != IGRAPH_STAR_MUTUAL) { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2); } else { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(n-1, 4, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2 * 2); } if (mode == IGRAPH_STAR_OUT) { @@ -133,184 +129,15 @@ igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode IGRAPH_CHECK(igraph_create(graph, &edges, 0, (mode != IGRAPH_STAR_UNDIRECTED))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup generators - * \function igraph_wheel - * \brief Creates a \em wheel graph, a union of a star and a cycle graph. - * - * A wheel graph on \p n vertices can be thought of as a wheel with - * n - 1 spokes. The cycle graph part makes up the rim, - * while the star graph part adds the spokes. - * - * - * Note that the two and three-vertex wheel graphs are non-simple: - * The two-vertex wheel graph contains a self-loop, while the three-vertex - * wheel graph contains parallel edges (a 1-cycle and a 2-cycle, respectively). - * - * \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_WHEEL_OUT - * directed wheel graph, edges point - * \em from the center to the other vertices. - * \cli IGRAPH_WHEEL_IN - * directed wheel graph, edges point - * \em to the center from the other vertices. - * \cli IGRAPH_WHEEL_MUTUAL - * directed wheel graph with mutual edges. - * \cli IGRAPH_WHEEL_UNDIRECTED - * an undirected wheel 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_square_lattice(), \ref igraph_ring(), \ref igraph_star(), - * \ref igraph_kary_tree() for creating other regular structures. - * - */ - -igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, - igraph_integer_t center) { - - igraph_star_mode_t star_mode; - igraph_vector_int_t rim_edges = IGRAPH_VECTOR_NULL; - igraph_integer_t i; - - /* Firstly creates a star by the function \ref igraph_star() and makes - * use of its existing input parameter checking ability, it can check - * "Invalid number of vertices" and "Invalid center vertex". */ - switch (mode) - { - case IGRAPH_WHEEL_OUT: - star_mode = IGRAPH_STAR_OUT; - break; - case IGRAPH_WHEEL_IN: - star_mode = IGRAPH_STAR_IN; - break; - case IGRAPH_WHEEL_MUTUAL: - star_mode = IGRAPH_STAR_MUTUAL; - break; - case IGRAPH_WHEEL_UNDIRECTED: - star_mode = IGRAPH_STAR_UNDIRECTED; - break; - default: - IGRAPH_ERROR("Invalid wheel graph mode.", IGRAPH_EINVMODE); - } - - IGRAPH_CHECK(igraph_star(graph, n, star_mode, center)); - - /* If n <= 1, wheel graph is identical with star graph, - * no further processing is needed. */ - if (n <= 1) { - return IGRAPH_SUCCESS; - } - - /* Register the star for deallocation in case of error flow before - * the entire wheel is successfully created. */ - IGRAPH_FINALLY(igraph_destroy, graph); - - /* Add edges to the rim. As the rim (or cycle) has n - 1 vertices, - * it will have n - 1 edges. For MUTUAL mode, number of edges - * will be double. */ - if (mode == IGRAPH_WHEEL_MUTUAL) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 4 * (n-1)); - } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 2 * (n-1)); - } - - /* Assign first n-1 edges (MUTUAL will be handled later). */ - for (i = 0; i < n-2; i++) { - if ( i < center ) { - VECTOR(rim_edges)[2 * i] = i; - if ( i + 1 < center ) { - VECTOR(rim_edges)[2 * i + 1] = i + 1; - } else { - VECTOR(rim_edges)[2 * i + 1] = i + 2; - } - } else { - VECTOR(rim_edges)[2 * i] = i + 1; - VECTOR(rim_edges)[2 * i + 1] = i + 2; - } - } - - /* Assign the last edge (MUTUAL will be handled later). */ - if ( n - 2 < center ) { - VECTOR(rim_edges)[2 * n - 4] = n - 2; - } else { - VECTOR(rim_edges)[2 * n - 4] = n - 1; - } - if ( center > 0 ) { - VECTOR(rim_edges)[2 * n - 3] = 0; - } else { - VECTOR(rim_edges)[2 * n - 3] = 1; - } - - /* For MUTUAL mode, add reverse-direction edges. */ - if (mode == IGRAPH_WHEEL_MUTUAL) { - for (i=0; i < 2 * (n-1); i++) { - VECTOR(rim_edges)[4 * (n-1) - 1 - i] = VECTOR(rim_edges)[i]; - } - } - - /* Combine the rim into the star to make it a wheel graph. */ - IGRAPH_CHECK(igraph_add_edges(graph, &rim_edges, NULL)); - - igraph_vector_int_destroy(&rim_edges); - - /* 2 instead of 1 because the star graph is registered before. */ - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup generators * \function igraph_lattice - * \brief Arbitrary dimensional square lattices (deprecated). - * - * \deprecated-by igraph_square_lattice 0.10.0 - */ -igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, - igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, - igraph_bool_t circular) { - igraph_vector_bool_t periodic; - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, igraph_vector_int_size(dimvector)); - igraph_vector_bool_fill(&periodic, circular); - - IGRAPH_CHECK(igraph_square_lattice(graph, dimvector, nei, directed, mutual, &periodic)); - - igraph_vector_bool_destroy(&periodic); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup generators - * \function igraph_square_lattice * \brief Arbitrary dimensional square lattices. * * Creates d-dimensional square lattices of the given size. Optionally, @@ -322,9 +149,9 @@ igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvec * * * The vertices of the resulting graph are ordered such that the - * index of the vertex at position (i_1, i_2, i_3, ..., i_d) - * in a lattice of size (n_1, n_2, ..., n_d) will be - * i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + .... + * 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 @@ -338,110 +165,84 @@ igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvec * higher-index ones. * \param mutual Boolean, if the graph is directed this gives whether * to create all connections as mutual. - * \param periodic Boolean vector, defines whether the generated lattice is - * periodic along each dimension. The length of this vector must match - * the length of \p dimvector. This parameter may also be \c NULL, which - * implies that the lattice will not be periodic. + * \param circular Boolean, defines whether the generated lattice is + * periodic. * \return Error code: - * \c IGRAPH_EINVAL: invalid (negative) dimension vector or mismatch - * between the length of the dimension vector and the periodicity vector. + * \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. */ -igraph_error_t igraph_square_lattice( - igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, - igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *periodic -) { - - igraph_integer_t dims = igraph_vector_int_size(dimvector); - igraph_integer_t no_of_nodes; - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t *coords, *weights; - igraph_integer_t i, j; - int carry, pos; +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) { - if (igraph_vector_int_any_smaller(dimvector, 0)) { - IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); - } + 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 (periodic && igraph_vector_bool_size(periodic) != dims) { - IGRAPH_ERRORF( - "Length of periodicity vector must match the length of the " - "dimension vector (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, dims - ); + if (igraph_vector_any_smaller(dimvector, 0)) { + IGRAPH_ERROR("Invalid dimension vector", IGRAPH_EINVAL); } - /* compute no. of nodes in overflow-safe manner */ - IGRAPH_CHECK(igraph_i_safe_vector_int_prod(dimvector, &no_of_nodes)); - /* init coords & weights */ - coords = IGRAPH_CALLOC(dims, igraph_integer_t); - IGRAPH_CHECK_OOM(coords, "Lattice creation failed."); + 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, igraph_integer_t); - IGRAPH_CHECK_OOM(weights, "Lattice creation failed."); + 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] * VECTOR(*dimvector)[i - 1]; + weights[i] = weights[i - 1] * (long int) VECTOR(*dimvector)[i - 1]; } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - if (mutual && directed) { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); - IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - } else { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); - } - -#define IS_PERIODIC(dim) ((periodic && VECTOR(*periodic)[dim])) + 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(); - - /* Connect the current node to the "next" node along each dimension */ for (j = 0; j < dims; j++) { - igraph_bool_t is_periodic = IS_PERIODIC(j); - - if (is_periodic|| coords[j] != VECTOR(*dimvector)[j] - 1) { - igraph_integer_t new_nei; + 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 - (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + 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_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, new_nei - 1); /* reserved */ } - } /* if is_periodic || coords[j] */ - if (mutual && directed && (is_periodic || coords[j] != 0)) { - igraph_integer_t new_nei; + } /* 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 + (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; + new_nei = i + (long int) (VECTOR(*dimvector)[j] - 1) * weights[j] + 1; } if (new_nei != i + 1 && - (VECTOR(*dimvector)[j] != 2 || !is_periodic)) { - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ + (VECTOR(*dimvector)[j] != 2 || !circular)) { + igraph_vector_push_back(&edges, i); /* reserved */ + igraph_vector_push_back(&edges, new_nei - 1); /* reserved */ } - } /* if is_periodic || coords[0] */ + } /* if circular || coords[0] */ } /* for j= 2) { IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL)); } @@ -468,10 +270,10 @@ igraph_error_t igraph_square_lattice( /* clean up */ IGRAPH_FREE(coords); IGRAPH_FREE(weights); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -485,9 +287,8 @@ igraph_error_t igraph_square_lattice( * in the path graph P_n. This function can generate both. * * - * When \p n is 1 or 2, the result may not be a simple graph: - * the one-cycle contains a self-loop and the undirected or reciprocally - * connected directed two-cycle contains parallel edges. + * 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. @@ -507,57 +308,21 @@ igraph_error_t igraph_square_lattice( * * \example examples/simple/igraph_ring.c */ -igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular) { - igraph_vector_int_t edges; - igraph_integer_t no_of_edges, no_of_edges2; - igraph_integer_t i; + 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); } - if (n == 0) { - return igraph_empty(graph, 0, directed); - } + IGRAPH_VECTOR_INIT_FINALLY(&v, 1); + VECTOR(v)[0] = n; - no_of_edges = circular ? n : n-1; - if (directed && mutual) { - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges); - } - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - - if (directed && mutual) { - for (i=0; i < n-1; ++i) { - VECTOR(edges)[4*i] = i; - VECTOR(edges)[4*i+1] = i+1; - VECTOR(edges)[4*i+2] = i+1; - VECTOR(edges)[4*i+3] = i; - } - if (circular) { - /* Now i == n-1 */ - VECTOR(edges)[4*i] = i; - VECTOR(edges)[4*i+1] = 0; - VECTOR(edges)[4*i+2] = 0; - VECTOR(edges)[4*i+3] = i; - } - } else { - for (i=0; i < n-1; ++i) { - VECTOR(edges)[2*i] = i; - VECTOR(edges)[2*i+1] = i+1; - } - if (circular) { - /* Now i == n-1 */ - VECTOR(edges)[2*i] = i; - VECTOR(edges)[2*i+1] = 0; - } - } + IGRAPH_CHECK(igraph_lattice(graph, &v, 1, directed, mutual, circular)); - IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -565,8 +330,8 @@ igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t di /** * \ingroup generators - * \function igraph_kary_tree - * \brief Creates a k-ary tree in which almost all vertices have k children. + * \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 @@ -587,7 +352,7 @@ igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t di * \clist * \cli IGRAPH_TREE_OUT * directed tree, the edges point - * from the parents to their children. + * from the parents to their children, * \cli IGRAPH_TREE_IN * directed tree, the edges point from * the children to their parents. @@ -605,15 +370,15 @@ igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t di * structures; \ref igraph_from_prufer() for creating arbitrary trees; * \ref igraph_tree_game() for uniform random sampling of trees. * - * \example examples/simple/igraph_kary_tree.c + * \example examples/simple/igraph_tree.c */ -igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, +int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, igraph_tree_mode_t type) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t i, j; - igraph_integer_t idx = 0; - igraph_integer_t to = 1; + 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); @@ -626,15 +391,7 @@ igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_inte IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); } - { - igraph_integer_t no_of_edges2; - if (n > 0) { - IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); - } else { - no_of_edges2 = 0; - } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - } + IGRAPH_VECTOR_INIT_FINALLY(&edges, n > 0 ? 2 * (n - 1) : 0); i = 0; if (type == IGRAPH_TREE_OUT) { @@ -657,180 +414,9 @@ igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_inte IGRAPH_CHECK(igraph_create(graph, &edges, n, type != IGRAPH_TREE_UNDIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup generators - * \function igraph_tree - * \brief Creates a k-ary tree in which almost all vertices have k children (deprecated alias). - * - * \deprecated-by igraph_kary_tree 0.10.0 - */ -igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, - igraph_tree_mode_t type) { - return igraph_kary_tree(graph, n, children, type); -} - -/** - * \ingroup generators - * \function igraph_symmetric_tree - * \brief Creates a symmetric tree with the specified number of branches at each level. - * - * This function creates a tree in which all vertices at distance \c d from the - * root have \p branching_counts[d] children. - * - * \param graph Pointer to an uninitialized graph object. - * \param branches Vector detailing the number of branches at each level. - * \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_INVMODE: invalid mode argument. - * \c IGRAPH_EINVAL: invalid number of children. - * - * Time complexity: O(|V|+|E|), the - * number of vertices plus the number of edges in the graph. - * - * \sa \ref igraph_kary_tree(), \ref igraph_regular_tree() and \ref igraph_star() - * for creating other regular tree structures; - * \ref igraph_from_prufer() for creating arbitrary trees; - * \ref igraph_tree_game() for uniform random sampling of trees. - * - * \example examples/simple/igraph_symmetric_tree.c - */ - -igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, - igraph_tree_mode_t type) { - - igraph_vector_int_t edges; - igraph_integer_t j, k, temp, no_of_nodes, idx, parent, child, level_end; - igraph_integer_t branching_counts_size = igraph_vector_int_size(branches); - - if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && type != IGRAPH_TREE_UNDIRECTED) { - IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); - } - if (!igraph_vector_int_empty(branches) && igraph_vector_int_min(branches) <= 0) { - IGRAPH_ERROR("The number of branches must be positive at each level.", IGRAPH_EINVAL); - } - - /* Compute the number of vertices in the tree. */ - no_of_nodes = 1; - temp = 1; - for (j = 0; j < branching_counts_size; ++j) { - IGRAPH_SAFE_MULT(temp, VECTOR(*branches)[j], &temp); - IGRAPH_SAFE_ADD(no_of_nodes, temp, &no_of_nodes); - } - - /* Trees have precisely |E| = |V| - 1 edges. */ - { - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(no_of_nodes - 1, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - } - - idx = 0; - - /* Current parent and child vertex ids. - * parent -> child edges will be added. */ - child = 1; - parent = 0; - for (k = 0; k < branching_counts_size; ++k) { - level_end = child; /* points to one past the last vertex of the current level of parents */ - while (parent < level_end) { - IGRAPH_ALLOW_INTERRUPTION(); - for (j = 0; j < VECTOR(*branches)[k]; j++) { - if (type == IGRAPH_TREE_IN) { - VECTOR(edges)[idx++] = child++; - VECTOR(edges)[idx++] = parent; - } else { - VECTOR(edges)[idx++] = parent; - VECTOR(edges)[idx++] = child++; - } - } - parent++; - } - } - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, type != IGRAPH_TREE_UNDIRECTED)); - - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_regular_tree - * \brief Creates a regular tree. - * - * All vertices of a regular tree, except its leaves, have the same total degree \p k. - * This is different from a k-ary tree (\ref igraph_kary_tree()), where all - * vertices have the same number of children, thus the degre of the root is - * one less than the degree of the other internal vertices. Regular trees - * are also referred to as Bethe lattices. - * - * \param graph Pointer to an uninitialized graph object. - * \param h The height of the tree, i.e. the distance between the root and the leaves. - * \param k The degree of the regular 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. - * - * Time complexity: O(|V|+|E|), the - * number of vertices plus the number of edges in the graph. - * - * \sa \ref igraph_kary_tree() to create k-ary tree where each vertex has the same - * number of children, i.e. out-degree, instead of the same total degree. - * \ref igraph_symmetric_tree() to use a different number of children at each level. - * - * \example examples/simple/igraph_regular_tree.c - */ - -igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, igraph_tree_mode_t type) { - igraph_vector_int_t branching_counts; - - if (h < 1) { - IGRAPH_ERRORF("Height of regular tree must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, h); - } - if (k < 2 ) { - IGRAPH_ERRORF("Degree of regular tree must be at least 2, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, k); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&branching_counts, h); - igraph_vector_int_fill(&branching_counts, k-1); - if (h > 0) { - VECTOR(branching_counts)[0] += 1; - } - - IGRAPH_CHECK(igraph_symmetric_tree(graph, &branching_counts, type)); - - igraph_vector_int_destroy(&branching_counts); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -872,31 +458,24 @@ igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_i * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ -igraph_error_t igraph_extended_chordal_ring( - igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_int_t *W, +int igraph_extended_chordal_ring( + igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_t *W, igraph_bool_t directed) { - igraph_vector_int_t edges; - igraph_integer_t period = igraph_matrix_int_ncol(W); - igraph_integer_t nrow = igraph_matrix_int_nrow(W); - igraph_integer_t i, j, mpos = 0, epos = 0; + 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); + IGRAPH_ERROR("An extended chordal ring has at least 3 nodes", IGRAPH_EINVAL); } - if (nodes % period != 0) { - IGRAPH_ERROR("The period (number of columns in W) should divide the number of 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); } - { - /* ecount = nodes + nodes * nrow */ - igraph_integer_t no_of_edges2; - IGRAPH_SAFE_MULT(nodes, nrow, &no_of_edges2); - IGRAPH_SAFE_ADD(no_of_edges2, nodes, &no_of_edges2); - IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); - } + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (nodes + nodes * nrow)); for (i = 0; i < nodes - 1; i++) { VECTOR(edges)[epos++] = i; @@ -908,8 +487,8 @@ igraph_error_t igraph_extended_chordal_ring( if (nrow > 0) { for (i = 0; i < nodes; i++) { for (j = 0; j < nrow; j++) { - igraph_integer_t offset = MATRIX(*W, j, mpos); - igraph_integer_t v = (i + offset) % nodes; + long int offset = (long int) MATRIX(*W, j, mpos); + long int v = (i + offset) % nodes; if (v < 0) { v += nodes; /* handle negative offsets */ @@ -926,7 +505,7 @@ igraph_error_t igraph_extended_chordal_ring( } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/trees.c b/src/vendor/cigraph/src/constructors/trees.c deleted file mode 100644 index d747321b6db..00000000000 --- a/src/vendor/cigraph/src/constructors/trees.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - 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 -*/ - -#include "igraph_constructors.h" -#include "igraph_interface.h" -#include "igraph_vector.h" - -/** - * \function igraph_tree_from_parent_vector - * \brief Constructs a tree or forest from a vector encoding the parent of each vertex. - * - * \experimental - * - * Rooted trees and forests are conveniently represented using a \p parents - * vector where the ID of the parent of vertex \c v is stored in parents[v]. - * This function serves to construct an igraph graph from a parent vector representation. - * The result is guaranteed to be a forest or a tree. If the \p parents vector - * is found to encode a cycle or a self-loop, an error is raised. - * - * - * Several igraph functions produce such vectors, such as graph traversal - * functions (\ref igraph_bfs() and \ref igraph_dfs()), shortest path functions - * that construct a shortest path tree, as well as some other specialized - * functions like \ref igraph_dominator_tree() or \ref igraph_cohesive_blocks(). - * Vertices which do not have parents (i.e. roots) get a negative entry in the - * \p parents vector. - * - * - * Use \ref igraph_bfs() or \ref igraph_dfs() to convert a forest into a parent - * vector representation. For trees, i.e. forests with a single root, it is - * more convenient to use \ref igraph_bfs_simple(). - * - * \param graph Pointer to an uninitialized graph object. - * \param parents The parent vector. parents[v] is the ID of - * the parent vertex of \c v. parents[v] < 0 indicates that - * \c v does not have a parent. - * \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. - * - * \sa \ref igraph_bfs(), \ref igraph_bfs_simple() for back-conversion; - * \ref igraph_from_prufer() for creating trees from Prüfer sequences; - * \ref igraph_is_tree() and \ref igraph_is_forest() to check if a graph - * is a tree or forest. - * - * Time complexity: O(n) where n is the length of \p parents. - */ -igraph_error_t igraph_tree_from_parent_vector( - igraph_t *graph, - const igraph_vector_int_t *parents, - igraph_tree_mode_t type) { - - const igraph_integer_t no_of_nodes = igraph_vector_int_size(parents); - igraph_vector_int_t seen; - igraph_vector_int_t edges; - igraph_bool_t directed, intree; - - switch (type) { - case IGRAPH_TREE_OUT: - directed = true; intree = false; break; - case IGRAPH_TREE_IN: - directed = true; intree = true; break; - case IGRAPH_TREE_UNDIRECTED: - directed = false; intree = true; break; - default: - IGRAPH_ERROR("Invalid tree mode.", IGRAPH_EINVAL); - } - - /* Catch null graph case */ - if (no_of_nodes == 0) { - return igraph_empty(graph, 0, directed); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, no_of_nodes); - - /* A tree has no_of_nodes - 1 edges but a forest has fewer. In order to support - * the use case of extracting small sub-trees of large graphs, we only reserve - * the full amount of memory needed for a tree when the graph is small. - * This also eliminates the need to check for integer overflow. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 1024 ? 2048 : 2*(no_of_nodes-1)); - igraph_vector_int_clear(&edges); - - igraph_integer_t c=1; - for (igraph_integer_t i=0; i < no_of_nodes; i++) { - igraph_integer_t v = i; - - if (VECTOR(seen)[v]) continue; - - while (true) { - igraph_integer_t u; - - VECTOR(seen)[v] = c; /* mark v as seen in the current round */ - u = VECTOR(*parents)[v]; - - if (u < 0) { - break; /* v is a root, stop traversal */ - } - if (u >= no_of_nodes) { - IGRAPH_ERROR("Invalid vertex ID in parent vector.", IGRAPH_EINVVID); - } - - if (intree) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); - } else { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); - } - - if (VECTOR(seen)[u]) { - if (VECTOR(seen)[u] == c) { - /* u was already seen in the current round, we found a cycle. - * We distinguish between self-loops, i.e. 1-cycles, and longer - * cycles in order to make the error message more useful. */ - IGRAPH_ERROR( - u==v - ? "Found a self-loop while constructing tree from parent vector." - : "Found a cycle while constructing tree from parent vector.", - IGRAPH_EINVAL); - } - break; /* u was seen in a previous round, stop traversal */ - } - - v = u; - } - - c++; - } - - igraph_vector_int_destroy(&seen); - IGRAPH_FINALLY_CLEAN(1); - - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/core/array.c b/src/vendor/cigraph/src/core/array.c index 07bc67fad5d..4d9904a1fd0 100644 --- a/src/vendor/cigraph/src/core/array.c +++ b/src/vendor/cigraph/src/core/array.c @@ -31,11 +31,11 @@ #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_INT +#define BASE_LONG #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" -#undef BASE_INT +#undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" diff --git a/src/vendor/cigraph/src/core/array.pmt b/src/vendor/cigraph/src/core/array.pmt index 227061f57ab..6d5c7358109 100644 --- a/src/vendor/cigraph/src/core/array.pmt +++ b/src/vendor/cigraph/src/core/array.pmt @@ -23,37 +23,27 @@ #include "igraph_types.h" -#include "math/safe_intop.h" - -igraph_error_t FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, - igraph_integer_t n1, igraph_integer_t n2, igraph_integer_t n3) { - - igraph_integer_t size, n1n2; - - IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); - - IGRAPH_SAFE_MULT(n1, n2, &n1n2); - IGRAPH_SAFE_MULT(n1n2, n3, &size); - - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&a->data, size)); - +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 = n1n2; + a->n1n2 = n1 * n2; - return IGRAPH_SUCCESS; + return ret; } void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a) { FUNCTION(igraph_vector, destroy)(&a->data); } -igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a) { +long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a) { return (a->n1n2) * (a->n3); } -igraph_integer_t FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, igraph_integer_t idx) { +long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx) { switch (idx) { case 1: return a->n1; break; @@ -65,25 +55,15 @@ igraph_integer_t FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, igraph return 0; } -igraph_error_t FUNCTION(igraph_array3, resize)( - TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, - igraph_integer_t n3) { - - igraph_integer_t size, n1n2; - - IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); - - IGRAPH_SAFE_MULT(n1, n2, &n1n2); - IGRAPH_SAFE_MULT(n1n2, n3, &size); - - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&a->data, size)); - +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 = n1n2; + a->n1n2 = n1 * n2; - return IGRAPH_SUCCESS; + return ret; } void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a) { @@ -102,9 +82,9 @@ void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e) { FUNCTION(igraph_vector, fill)(&a->data, e); } -igraph_error_t FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, +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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/buckets.c b/src/vendor/cigraph/src/core/buckets.c index b43fe809c23..67be50764e9 100644 --- a/src/vendor/cigraph/src/core/buckets.c +++ b/src/vendor/cigraph/src/core/buckets.c @@ -45,34 +45,34 @@ * _empty() and _popmax() operations. */ -igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); - IGRAPH_VECTOR_INT_INIT_FINALLY(&b->buckets, size); +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 IGRAPH_SUCCESS; + return 0; } void igraph_buckets_destroy(igraph_buckets_t *b) { - igraph_vector_int_destroy(&b->bptr); - igraph_vector_int_destroy(&b->buckets); + igraph_vector_long_destroy(&b->bptr); + igraph_vector_long_destroy(&b->buckets); } -igraph_integer_t igraph_buckets_popmax(igraph_buckets_t *b) { +long int igraph_buckets_popmax(igraph_buckets_t *b) { /* Precondition: there is at least a non-empty bucket */ /* Search for the highest bucket first */ - igraph_integer_t max; - while ( (max = VECTOR(b->bptr)[b->max]) == 0) { + long int max; + while ( (max = (long int) VECTOR(b->bptr)[(long int) b->max]) == 0) { b->max --; } - VECTOR(b->bptr)[b->max] = VECTOR(b->buckets)[max - 1]; + VECTOR(b->bptr)[(long int) b->max] = VECTOR(b->buckets)[max - 1]; b->no--; return max - 1; } -igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t bucket) { - igraph_integer_t ret = VECTOR(b->bptr)[bucket] - 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; @@ -83,62 +83,62 @@ igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b) { } igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, - igraph_integer_t bucket) { + long int bucket) { return VECTOR(b->bptr)[bucket] == 0; } -void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem) { +void igraph_buckets_add(igraph_buckets_t *b, long int bucket, + long int elem) { - VECTOR(b->buckets)[elem] = VECTOR(b->bptr)[bucket]; - VECTOR(b->bptr)[bucket] = elem + 1; + 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 = bucket; + b->max = (int) bucket; } b->no++; } void igraph_buckets_clear(igraph_buckets_t *b) { - igraph_vector_int_null(&b->bptr); - igraph_vector_int_null(&b->buckets); + igraph_vector_long_null(&b->bptr); + igraph_vector_long_null(&b->buckets); b->max = -1; b->no = 0; } -igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); - IGRAPH_VECTOR_INT_INIT_FINALLY(&b->next, size); - IGRAPH_VECTOR_INT_INIT_FINALLY(&b->prev, size); +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 IGRAPH_SUCCESS; + return 0; } void igraph_dbuckets_destroy(igraph_dbuckets_t *b) { - igraph_vector_int_destroy(&b->bptr); - igraph_vector_int_destroy(&b->next); - igraph_vector_int_destroy(&b->prev); + 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_int_null(&b->bptr); - igraph_vector_int_null(&b->next); - igraph_vector_int_null(&b->prev); + igraph_vector_long_null(&b->bptr); + igraph_vector_long_null(&b->next); + igraph_vector_long_null(&b->prev); b->max = -1; b->no = 0; } -igraph_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b) { - igraph_integer_t max; - while ( (max = VECTOR(b->bptr)[b->max]) == 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); } -igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t bucket) { - igraph_integer_t ret = VECTOR(b->bptr)[bucket] - 1; - igraph_integer_t next = VECTOR(b->next)[ret]; +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; @@ -153,38 +153,38 @@ igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b) { } igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, - igraph_integer_t bucket) { + long int bucket) { return VECTOR(b->bptr)[bucket] == 0; } -void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem) { - igraph_integer_t oldfirst = VECTOR(b->bptr)[bucket]; +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 = bucket; + b->max = (int) bucket; } b->no++; } /* Remove an arbitrary element */ -void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem) { +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 */ - igraph_integer_t next = VECTOR(b->next)[elem]; + long int next = VECTOR(b->next)[elem]; if (next != 0) { VECTOR(b->prev)[next - 1] = 0; } VECTOR(b->bptr)[bucket] = next; } else { - igraph_integer_t next = VECTOR(b->next)[elem]; - igraph_integer_t prev = VECTOR(b->prev)[elem]; + long int next = VECTOR(b->next)[elem]; + long int prev = VECTOR(b->prev)[elem]; if (next != 0) { VECTOR(b->prev)[next - 1] = prev; } diff --git a/src/vendor/cigraph/src/core/buckets.h b/src/vendor/cigraph/src/core/buckets.h index 48e340b63ed..31635f134e4 100644 --- a/src/vendor/cigraph/src/core/buckets.h +++ b/src/vendor/cigraph/src/core/buckets.h @@ -23,7 +23,16 @@ #ifndef IGRAPH_CORE_BUCKETS_H #define IGRAPH_CORE_BUCKETS_H -#include "igraph_decls.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" @@ -32,40 +41,40 @@ __BEGIN_DECLS /* Buckets, needed for the maximum flow algorithm */ typedef struct igraph_buckets_t { - igraph_vector_int_t bptr; - igraph_vector_int_t buckets; + igraph_vector_long_t bptr; + igraph_vector_long_t buckets; igraph_integer_t max, no; } igraph_buckets_t; -igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t size); +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); -igraph_integer_t igraph_buckets_popmax(igraph_buckets_t *b); -igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t bucket); +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, - igraph_integer_t bucket); -void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem); + long int bucket); +void igraph_buckets_add(igraph_buckets_t *b, long int bucket, + long int elem); typedef struct igraph_dbuckets_t { - igraph_vector_int_t bptr; - igraph_vector_int_t next, prev; + igraph_vector_long_t bptr; + igraph_vector_long_t next, prev; igraph_integer_t max, no; } igraph_dbuckets_t; -igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t size); +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); -igraph_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b); -igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t bucket); +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, - igraph_integer_t bucket); -void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem); -void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, - igraph_integer_t elem); + 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 diff --git a/src/vendor/cigraph/src/core/cutheap.c b/src/vendor/cigraph/src/core/cutheap.c index 96dd1be7367..1ab07132323 100644 --- a/src/vendor/cigraph/src/core/cutheap.c +++ b/src/vendor/cigraph/src/core/cutheap.c @@ -32,10 +32,10 @@ #define INDEXINC 1 static void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, - igraph_integer_t hidx1, igraph_integer_t hidx2) { + long int hidx1, long int hidx2) { if (hidx1 != hidx2) { - igraph_integer_t idx1 = VECTOR(ch->index)[hidx1]; - igraph_integer_t idx2 = VECTOR(ch->index)[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]; @@ -49,8 +49,8 @@ static void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, } } -static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, igraph_integer_t hidx) { - igraph_integer_t size = igraph_vector_size(&ch->heap); +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 || @@ -70,7 +70,7 @@ static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, igraph_integer_t hidx) } } -static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, igraph_integer_t 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 { @@ -79,19 +79,19 @@ static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, igraph_integer_t h } } -igraph_error_t igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes) { +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_int_init_range(&ch->index, 0, nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &ch->index); - IGRAPH_CHECK(igraph_vector_init_range(&ch->hptr, INDEXINC, nodes + INDEXINC)); + 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 IGRAPH_SUCCESS; + return 0; } void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) { igraph_vector_destroy(&ch->hptr); - igraph_vector_int_destroy(&ch->index); + igraph_vector_destroy(&ch->index); igraph_vector_destroy(&ch->heap); } @@ -102,13 +102,13 @@ igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch) { /* Number of active vertices */ igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch) { - return igraph_vector_size(&ch->heap); + 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 ch->dnodes; + return (igraph_integer_t) (ch->dnodes); } igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { @@ -116,14 +116,14 @@ igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { } igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) { - igraph_integer_t size = igraph_vector_size(&ch->heap); - igraph_integer_t maxindex = VECTOR(ch->index)[0]; + 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)[ igraph_vector_int_tail(&ch->index)] = INACTIVE; + VECTOR(ch->hptr)[(long int) igraph_vector_tail(&ch->index)] = INACTIVE; igraph_vector_pop_back(&ch->heap); - igraph_vector_int_pop_back(&ch->index); + igraph_vector_pop_back(&ch->index); igraph_i_cutheap_sink(ch, 0); return maxindex; @@ -131,22 +131,23 @@ igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) { /* Update the value of an active vertex, if not active it will be ignored */ -void igraph_i_cutheap_update( - igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add) { - igraph_real_t hidx = VECTOR(ch->hptr)[index]; +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) { - igraph_integer_t hidx2 = (hidx - INDEXINC); - /* printf("updating vertex %li, heap index %li\n", index, hidx2); */ + 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 */ -igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex) { - igraph_integer_t i, j, n = igraph_vector_size(&ch->hptr); +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; @@ -154,7 +155,7 @@ igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_in IGRAPH_CHECK(igraph_vector_resize(&ch->heap, ch->dnodes)); igraph_vector_null(&ch->heap); - IGRAPH_CHECK(igraph_vector_int_resize(&ch->index, ch->dnodes)); + IGRAPH_CHECK(igraph_vector_resize(&ch->index, ch->dnodes)); j = 0; for (i = 0; i < n; i++) { @@ -165,5 +166,5 @@ igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_in } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/cutheap.h b/src/vendor/cigraph/src/core/cutheap.h index 62947c6b879..d7cb4c7d870 100644 --- a/src/vendor/cigraph/src/core/cutheap.h +++ b/src/vendor/cigraph/src/core/cutheap.h @@ -23,7 +23,16 @@ #ifndef IGRAPH_CORE_CUTHEAP_H #define IGRAPH_CORE_CUTHEAP_H -#include "igraph_decls.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" @@ -33,20 +42,21 @@ __BEGIN_DECLS typedef struct igraph_i_cutheap_t { igraph_vector_t heap; - igraph_vector_int_t index; + igraph_vector_t index; igraph_vector_t hptr; - igraph_integer_t dnodes; + long int dnodes; } igraph_i_cutheap_t; -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes); +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 void igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex); +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 diff --git a/src/vendor/cigraph/src/core/dqueue.c b/src/vendor/cigraph/src/core/dqueue.c index 20e8d953f8e..212b2426f51 100644 --- a/src/vendor/cigraph/src/core/dqueue.c +++ b/src/vendor/cigraph/src/core/dqueue.c @@ -30,11 +30,11 @@ #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_INT +#define BASE_LONG #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" -#undef BASE_INT +#undef BASE_LONG #define BASE_CHAR #include "igraph_pmt.h" @@ -47,3 +47,9 @@ #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/vendor/cigraph/src/core/dqueue.pmt b/src/vendor/cigraph/src/core/dqueue.pmt index 9829f6c5eb2..6ef7b678cd5 100644 --- a/src/vendor/cigraph/src/core/dqueue.pmt +++ b/src/vendor/cigraph/src/core/dqueue.pmt @@ -23,29 +23,11 @@ #include "igraph_memory.h" #include "igraph_error.h" +#include "config.h" #include /* memcpy & co. */ #include -/* Notes on the internal representation of dqueue: - * - * 'stor_begin' points at the beginning of the allocated storage. - * 'stor_end' points one past the allocated storage. - * - * 'begin' points at the first element of the queue contents. - * 'end' points one past the last element. - * - * The queue elements are stored "cyclically" within the allocated - * buffer, and arithmetic on 'begin' and 'end' is done modulo - * 'size = stor_end - stor_begin'. Thus the smallest valid value of - * 'begin' and 'end' is 'stor_begin'. Their largest valid value is - * 'stor_end - 1'. - * - * This means that 'begin == end' would be true both when the queue - * is full and when it is empty. To distinguish between these - * two situations, 'end' is set to NULL when the queue is empty. - */ - /** * \section igraph_dqueue * @@ -65,27 +47,27 @@ * \brief Initialize a double ended queue (deque). * * The queue will be always empty. - * * \param q Pointer to an uninitialized deque. - * \param capacity How many elements to allocate memory for. + * \param size How many elements to allocate memory for. * \return Error code. * - * Time complexity: O(\p capacity). + * Time complexity: O(\p size). */ -igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(capacity >= 0); - - if (capacity == 0) capacity = 1; - - q->stor_begin = IGRAPH_CALLOC(capacity, BASE); - IGRAPH_CHECK_OOM(q->stor_begin, "Cannot initialize dqueue."); - q->stor_end = q->stor_begin + capacity; +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 IGRAPH_SUCCESS; + return 0; } /** @@ -93,14 +75,17 @@ igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_inte * \function igraph_dqueue_destroy * \brief Destroy a double ended queue. * - * \param q The queue to destroy. + * \param q The queue to destroy * * Time complexity: O(1). */ -void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_FREE(q->stor_begin); /* sets to NULL */ +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; + } } /** @@ -109,15 +94,15 @@ void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q) { * \brief Decide whether the queue is empty. * * \param q The queue. - * \return Boolean, true if \p q contains at least one element, - * false otherwise. + * \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 != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); +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; } @@ -126,14 +111,14 @@ igraph_bool_t FUNCTION(igraph_dqueue, empty)(const TYPE(igraph_dqueue)* q) { * \function igraph_dqueue_clear * \brief Remove all elements from the queue. * - * \param q The queue. + * \param q The queue * * Time complexity: O(1). */ -void FUNCTION(igraph_dqueue, clear)(TYPE(igraph_dqueue)* q) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); +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; } @@ -143,18 +128,17 @@ void FUNCTION(igraph_dqueue, clear)(TYPE(igraph_dqueue)* q) { * \function igraph_dqueue_full * \brief Check whether the queue is full. * - * If a queue is full the next \ref igraph_dqueue_push() operation will allocate + * 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. + * \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 != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); +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; } @@ -169,9 +153,9 @@ igraph_bool_t FUNCTION(igraph_dqueue, full)(TYPE(igraph_dqueue)* q) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); +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) { @@ -187,17 +171,15 @@ igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q) { * \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 != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); - IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ +BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != 0); + IGRAPH_ASSERT(q->stor_begin != 0); return *(q->begin); } @@ -207,17 +189,15 @@ BASE FUNCTION(igraph_dqueue, head)(const TYPE(igraph_dqueue)* q) { * \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 != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); - IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ +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); } @@ -231,18 +211,16 @@ BASE FUNCTION(igraph_dqueue, back)(const TYPE(igraph_dqueue)* q) { * * 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) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); - IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ +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; @@ -257,22 +235,20 @@ BASE FUNCTION(igraph_dqueue, pop)(TYPE(igraph_dqueue)* q) { /** * \ingroup dqueue * \function igraph_dqueue_pop_back - * \brief Removes the tail. + * \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 FUNCTION(igraph_dqueue, pop_back) (TYPE(igraph_dqueue)* q) { BASE tmp; - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); - IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ + 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; @@ -293,21 +269,20 @@ BASE FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q) { * \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 note that by + * 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.) */ -igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) { - IGRAPH_ASSERT(q != NULL); - IGRAPH_ASSERT(q->stor_begin != NULL); +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) { @@ -322,19 +297,12 @@ igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) /* full, allocate more storage */ BASE *bigger = NULL, *old = q->stor_begin; - igraph_integer_t old_size = q->stor_end - q->stor_begin; - igraph_integer_t new_capacity = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to dqueue, already at maximum size.", IGRAPH_EOVERFLOW); - } - if (new_capacity == 0) { - new_capacity = 1; + bigger = IGRAPH_CALLOC( 2 * (q->stor_end - q->stor_begin) + 1, BASE ); + if (bigger == 0) { + IGRAPH_ERROR("dqueue push failed", IGRAPH_ENOMEM); } - bigger = IGRAPH_CALLOC(new_capacity, BASE); - IGRAPH_CHECK_OOM(bigger, "Cannot push to dqueue."); - if (q->stor_end - q->begin) { memcpy(bigger, q->begin, (size_t)(q->stor_end - q->begin) * sizeof(BASE)); @@ -344,10 +312,10 @@ igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) (size_t)(q->end - q->stor_begin) * sizeof(BASE)); } - q->end = bigger + old_size; - q->stor_end = bigger + new_capacity; + 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->begin = bigger; *(q->end) = elem; (q->end)++; @@ -358,18 +326,18 @@ igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) IGRAPH_FREE(old); } - return IGRAPH_SUCCESS; + return 0; } #if defined (OUT_FORMAT) #ifndef USING_R -igraph_error_t FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) { +int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) { return FUNCTION(igraph_dqueue, fprint)(q, stdout); } #endif -igraph_error_t FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file) { +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; @@ -397,26 +365,12 @@ igraph_error_t FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FIL fprintf(file, "\n"); - return IGRAPH_SUCCESS; + return 0; } #endif -/** - * \ingroup dqueue - * \function igraph_dqueue_get - * \brief Access an element in a queue. - * - * \param q The queue. - * \param idx The index of the element within the queue. - * \return The desired element. - * - * Time complexity: O(1). - */ - -BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { - IGRAPH_ASSERT(idx >= 0); - IGRAPH_ASSERT(idx < FUNCTION(igraph_dqueue, size)(q)); +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]; @@ -424,20 +378,6 @@ BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx = idx - (q->stor_end - q->begin); return q->stor_begin[idx]; } else { - /* The assertions at the top make it impossible to reach here, - but omitting this branch would cause compiler warnings. */ - IGRAPH_FATAL("Out of bounds access in dqueue."); + return 0; /* Error */ } } - -/** - * \ingroup dqueue - * \function igraph_dqueue_e - * \brief Access an element in a queue (deprecated alias). - * - * \deprecated-by igraph_dqueue_get 0.10.2 - */ - -BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { - return FUNCTION(igraph_dqueue, get)(q, idx); -} diff --git a/src/vendor/cigraph/src/core/error.c b/src/vendor/cigraph/src/core/error.c index 6e928cc00f2..214b761b2be 100644 --- a/src/vendor/cigraph/src/core/error.c +++ b/src/vendor/cigraph/src/core/error.c @@ -62,7 +62,7 @@ * 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_FUNCATTR_NORETURN void igraph_abort(void) { +static IGRAPH_NORETURN void igraph_abort(void) { #ifndef USING_R #ifdef IGRAPH_SANITIZER_AVAILABLE fprintf(stderr, "\nStack trace:\n"); @@ -92,7 +92,7 @@ static const char *igraph_i_error_strings[] = { /* 4 */ "Invalid value", /* 5 */ "Already exists", /* 6 */ "Invalid edge vector", - /* 7 */ "Invalid vertex ID", + /* 7 */ "Invalid vertex id", /* 8 */ "Non-square matrix", /* 9 */ "Invalid mode", /* 10 */ "File operation error", @@ -143,29 +143,26 @@ static const char *igraph_i_error_strings[] = { /* 51 */ "Internal attribute handler error", /* 52 */ "Unimplemented attribute combination for this type", /* 53 */ "LAPACK call resulted in an error", - /* 54 */ "Internal DrL error; this error should never be visible to the user, " - "please report this error along with the steps to reproduce it.", + /* 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.", - /* 61 */ "Result too large", - /* 62 */ "Input problem has no solution" + "please report this error along with the steps to reproduce it." }; -const char *igraph_strerror(const igraph_error_t igraph_errno) { - if ((int) igraph_errno < 0 || - (int) igraph_errno >= sizeof(igraph_i_error_strings) / sizeof(igraph_i_error_strings[0])) { - IGRAPH_FATALF("Invalid error code %d; no error string available.", (int) igraph_errno); +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]; } -igraph_error_t igraph_error(const char *reason, const char *file, int line, - igraph_error_t 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); @@ -177,18 +174,17 @@ igraph_error_t igraph_error(const char *reason, const char *file, int line, return igraph_errno; } -igraph_error_t igraph_errorf(const char *reason, const char *file, int line, - igraph_error_t 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); - va_end(ap); return igraph_error(igraph_i_errormsg_buffer, file, line, igraph_errno); } -igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, - igraph_error_t igraph_errno, va_list ap) { +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); @@ -196,7 +192,7 @@ igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, #ifndef USING_R void igraph_error_handler_abort(const char *reason, const char *file, - int line, igraph_error_t igraph_errno) { + int line, int igraph_errno) { fprintf(stderr, "Error at %s:%i : %s - %s.\n", file, line, reason, igraph_strerror(igraph_errno)); igraph_abort(); @@ -204,7 +200,7 @@ void igraph_error_handler_abort(const char *reason, const char *file, #endif void igraph_error_handler_ignore(const char *reason, const char *file, - int line, igraph_error_t igraph_errno) { + int line, int igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); @@ -215,7 +211,7 @@ void igraph_error_handler_ignore(const char *reason, const char *file, #ifndef USING_R void igraph_error_handler_printignore(const char *reason, const char *file, - int line, igraph_error_t igraph_errno) { + int line, int igraph_errno) { fprintf(stderr, "Error at %s:%i : %s - %s.\n", file, line, reason, igraph_strerror(igraph_errno)); IGRAPH_FINALLY_FREE(); @@ -232,104 +228,43 @@ igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_han /***** "Finally" stack *****/ IGRAPH_THREAD_LOCAL struct igraph_i_protectedPtr igraph_i_finally_stack[100]; -IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_size = 0; -IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_level = 0; - -static void igraph_i_reset_finally_stack(void) { - igraph_i_finally_stack_size = 0; - igraph_i_finally_stack_level = 0; -} /* * Adds another element to the free list */ void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr) { - int no = igraph_i_finally_stack_size; - if (no < 0) { - /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ - igraph_i_reset_finally_stack(); - IGRAPH_FATALF("Corrupt finally stack: it contains %d elements.", no); - } - if (no >= (int) (sizeof(igraph_i_finally_stack) / sizeof(igraph_i_finally_stack[0]))) { - /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ - igraph_i_reset_finally_stack(); - IGRAPH_FATALF("Finally stack too large: it contains %d elements.", no); - } + 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[no].level = igraph_i_finally_stack_level; - igraph_i_finally_stack_size++; + 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_size -= minus; - if (igraph_i_finally_stack_size < 0) { - int left = igraph_i_finally_stack_size + minus; - /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ - igraph_i_reset_finally_stack(); + 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) { - for (; igraph_i_finally_stack_size > 0; igraph_i_finally_stack_size--) { - int p = igraph_i_finally_stack_size - 1; - /* Call destructors only up to the current level */ - if (igraph_i_finally_stack[p].level < igraph_i_finally_stack_level) { - break; - } + 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_size; -} - -/** - * \function IGRAPH_FINALLY_ENTER - * - * For internal use only. - * - * Opens a new level in the finally stack. Must have a matching - * IGRAPH_FINALLY_EXIT() call that closes the level and exits it. - * - * The finally stack is divided into "levels". A call to IGRAPH_FINALLY_FREE() - * will only unwind the current level of the finally stack, not any of the lower - * levels. This mechanism is used to allow some functions to pause stack unwinding - * until they can restore their data structures into a consistent state. - * See \ref igraph_add_edges() for an example usage. - */ -void IGRAPH_FINALLY_ENTER(void) { - int no = igraph_i_finally_stack_size; - /* Level indices must always be in increasing order in the finally stack */ - if (no > 0 && igraph_i_finally_stack[no-1].level > igraph_i_finally_stack_level) { - /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ - igraph_i_reset_finally_stack(); - IGRAPH_FATAL("Corrupt finally stack: cannot create new finally stack level before last one is freed."); - } - igraph_i_finally_stack_level++; -} - -/** - * \function IGRAPH_FINALLY_EXIT - * - * For internal use only. - * - * Exists the current level of the finally stack, see IGRAPH_FINALLY_ENTER() - * for details. If an error occured inbetween the last pair of - * IGRAPH_FINALLY_ENTER()/EXIT() calls, a call to igraph_error(), typically - * through IGRAPH_ERROR(), is mandatory directly after IGRAPH_FINALLY_EXIT(). - * This ensures that resource cleanup will properly resume. - */ -void IGRAPH_FINALLY_EXIT(void) { - igraph_i_finally_stack_level--; - if (igraph_i_finally_stack_level < 0) { - /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ - igraph_i_reset_finally_stack(); - IGRAPH_FATAL("Corrupt finally stack: trying to exit outermost finally stack level."); - } + return igraph_i_finally_stack[0].all; } @@ -350,10 +285,12 @@ static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler = * but this is currently not used in igraph. */ -void igraph_warning_handler_ignore(const char *reason, const char *file, int line) { +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 @@ -372,30 +309,34 @@ void igraph_warning_handler_ignore(const char *reason, const char *file, int lin * but this is currently not used in igraph. */ -void igraph_warning_handler_print(const char *reason, const char *file, int line) { +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 -void igraph_warning(const char *reason, const char *file, int line) { +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_i_warning_handler(reason, file, line, igraph_errno); #ifndef USING_R } else { - igraph_warning_handler_print(reason, file, line); + igraph_warning_handler_print(reason, file, line, igraph_errno); #endif } + return igraph_errno; } -void igraph_warningf(const char *reason, const char *file, int line, - ...) { +int igraph_warningf(const char *reason, const char *file, int line, + int igraph_errno, ...) { va_list ap; - va_start(ap, line); + va_start(ap, igraph_errno); vsnprintf(igraph_i_warningmsg_buffer, sizeof(igraph_i_warningmsg_buffer) / sizeof(char), reason, ap); - va_end(ap); - igraph_warning(igraph_i_warningmsg_buffer, file, line); + 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) { diff --git a/src/vendor/cigraph/src/core/estack.c b/src/vendor/cigraph/src/core/estack.c index 5f37d82c322..41a62ed31c0 100644 --- a/src/vendor/cigraph/src/core/estack.c +++ b/src/vendor/cigraph/src/core/estack.c @@ -23,45 +23,45 @@ #include "core/estack.h" -igraph_error_t igraph_estack_init(igraph_estack_t *s, igraph_integer_t setsize, - igraph_integer_t stacksize) { +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_int_init(&s->stack, stacksize)); + IGRAPH_CHECK(igraph_stack_long_init(&s->stack, stacksize)); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } void igraph_estack_destroy(igraph_estack_t *s) { - igraph_stack_int_destroy(&s->stack); + igraph_stack_long_destroy(&s->stack); igraph_vector_bool_destroy(&s->isin); } -igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem) { +int igraph_estack_push(igraph_estack_t *s, long int elem) { if ( !VECTOR(s->isin)[elem] ) { - IGRAPH_CHECK(igraph_stack_int_push(&s->stack, elem)); + IGRAPH_CHECK(igraph_stack_long_push(&s->stack, elem)); VECTOR(s->isin)[elem] = 1; } - return IGRAPH_SUCCESS; + return 0; } -igraph_integer_t igraph_estack_pop(igraph_estack_t *s) { - igraph_integer_t elem = igraph_stack_int_pop(&s->stack); +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, - igraph_integer_t elem) { + long int elem) { return VECTOR(s->isin)[elem]; } -igraph_integer_t igraph_estack_size(const igraph_estack_t *s) { - return igraph_stack_int_size(&s->stack); +long int igraph_estack_size(const igraph_estack_t *s) { + return igraph_stack_long_size(&s->stack); } #ifndef USING_R -igraph_error_t igraph_estack_print(const igraph_estack_t *s) { - return igraph_stack_int_print(&s->stack); +int igraph_estack_print(const igraph_estack_t *s) { + return igraph_stack_long_print(&s->stack); } #endif diff --git a/src/vendor/cigraph/src/core/estack.h b/src/vendor/cigraph/src/core/estack.h index ad315b3edb0..65178baa55d 100644 --- a/src/vendor/cigraph/src/core/estack.h +++ b/src/vendor/cigraph/src/core/estack.h @@ -24,29 +24,24 @@ #ifndef IGRAPH_ESTACK_H #define IGRAPH_ESTACK_H -#include "igraph_decls.h" #include "igraph_stack.h" #include "igraph_vector.h" -__BEGIN_DECLS - typedef struct igraph_estack_t { - igraph_stack_int_t stack; + igraph_stack_long_t stack; igraph_vector_bool_t isin; } igraph_estack_t; -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_init( - igraph_estack_t *s, igraph_integer_t setsize, igraph_integer_t stacksize); +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 igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_estack_pop(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, - igraph_integer_t elem); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_estack_size(const igraph_estack_t *s); - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_print(const igraph_estack_t *s); + long int elem); +IGRAPH_PRIVATE_EXPORT long int igraph_estack_size(const igraph_estack_t *s); -__END_DECLS +IGRAPH_PRIVATE_EXPORT int igraph_estack_print(const igraph_estack_t *s); #endif diff --git a/src/vendor/cigraph/src/core/exceptions.h b/src/vendor/cigraph/src/core/exceptions.h index d48608215e9..f1cb6ec93da 100644 --- a/src/vendor/cigraph/src/core/exceptions.h +++ b/src/vendor/cigraph/src/core/exceptions.h @@ -1,34 +1,19 @@ #ifndef IGRAPH_HANDLE_EXCEPTIONS_H #define IGRAPH_HANDLE_EXCEPTIONS_H -#include "igraph_error.h" - #include #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(). - * - * In some cases IGRAPH_HANDLE_EXCEPTIONS() won't work because the - * C preprocessor gets confused by the code block. In that case one can use - * IGRAPH_HANDLE_EXCEPTIONS_BEGIN; and IGRAPH_HANDLE_EXCEPTIONS_END at the - * beginning and end of the code block. */ -#define IGRAPH_HANDLE_EXCEPTIONS_BEGIN \ - try { -#define IGRAPH_HANDLE_EXCEPTIONS_END \ - } \ - catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } \ - catch (const std::range_error &e) { IGRAPH_ERROR(e.what(), IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ } \ - catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } \ - catch (...) { IGRAPH_ERROR("Unknown exception caught.", IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } - #define IGRAPH_HANDLE_EXCEPTIONS(code) \ - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; \ - code; \ - IGRAPH_HANDLE_EXCEPTIONS_END; + 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/vendor/cigraph/src/core/fixed_vectorlist.c b/src/vendor/cigraph/src/core/fixed_vectorlist.c index 8b247f75599..adc482eb381 100644 --- a/src/vendor/cigraph/src/core/fixed_vectorlist.c +++ b/src/vendor/cigraph/src/core/fixed_vectorlist.c @@ -21,39 +21,61 @@ */ +#include "igraph_memory.h" + #include "core/fixed_vectorlist.h" void igraph_fixed_vectorlist_destroy(igraph_fixed_vectorlist_t *l) { - igraph_vector_int_list_destroy(&l->vecs); + 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); } -igraph_error_t igraph_fixed_vectorlist_convert( - igraph_fixed_vectorlist_t *l, const igraph_vector_int_t *from, - igraph_integer_t size -) { - igraph_vector_int_t sizes; - igraph_integer_t i, no = igraph_vector_int_size(from), to; +int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_t *from, + long int size) { - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&l->vecs, size); - IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, 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++) { - to = VECTOR(*from)[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++) { - to = VECTOR(*from)[i]; + long int to = (long int) VECTOR(*from)[i]; if (to >= 0) { - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&l->vecs, to); - IGRAPH_CHECK(igraph_vector_int_push_back(v, i)); + igraph_vector_t *v = &(l->vecs[to]); + igraph_vector_push_back(v, i); } } - igraph_vector_int_destroy(&sizes); - IGRAPH_FINALLY_CLEAN(2); /* + l->vecs */ + igraph_vector_destroy(&sizes); + IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/fixed_vectorlist.h b/src/vendor/cigraph/src/core/fixed_vectorlist.h index 34e2437873a..4a0907f8c1d 100644 --- a/src/vendor/cigraph/src/core/fixed_vectorlist.h +++ b/src/vendor/cigraph/src/core/fixed_vectorlist.h @@ -27,7 +27,7 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -36,14 +36,15 @@ __BEGIN_DECLS /* -------------------------------------------------- */ typedef struct igraph_fixed_vectorlist_t { - igraph_vector_int_list_t vecs; - igraph_integer_t length; + 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); -igraph_error_t igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, - const igraph_vector_int_t *from, - igraph_integer_t size); +int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_t *from, + long int size); __END_DECLS diff --git a/src/vendor/cigraph/src/core/genheap.c b/src/vendor/cigraph/src/core/genheap.c deleted file mode 100644 index 0f251e8fd64..00000000000 --- a/src/vendor/cigraph/src/core/genheap.c +++ /dev/null @@ -1,302 +0,0 @@ -/* - 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, see . - */ - -#include "igraph_memory.h" - -#include "core/genheap.h" - -#include /* memcpy */ - -#if defined(_MSC_VER) && _MSC_VER < 1927 - /* MSVC does not understand restrict before version 19.27 */ - #define restrict __restrict -#endif - -#define PARENT(x) (((x)+1)/2-1) -#define LEFTCHILD(x) (((x)+1)*2-1) -#define RIGHTCHILD(x) (((x)+1)*2) - -#define ELEM(h, x) ((char *) h->data + x * h->item_size) - -/* This is a smart indexed heap that can hold elements of arbitrary type. - * 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 swapfunc(char * restrict a, char * restrict b, size_t es) { - char t; - - do { - t = *a; - *a++ = *b; - *b++ = t; - } while (--es > 0); -} - -static void igraph_i_gen2wheap_switch(igraph_gen2wheap_t *h, - igraph_integer_t e1, igraph_integer_t e2) { - if (e1 != e2) { - igraph_integer_t tmp1, tmp2; - - swapfunc(ELEM(h, e1), ELEM(h, e2), h->item_size); - - 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_gen2wheap_shift_up(igraph_gen2wheap_t *h, - igraph_integer_t elem) { - if (elem == 0 || h->cmp(ELEM(h, elem), ELEM(h, PARENT(elem))) < 0) { - /* at the top */ - } else { - igraph_i_gen2wheap_switch(h, elem, PARENT(elem)); - igraph_i_gen2wheap_shift_up(h, PARENT(elem)); - } -} - -static void igraph_i_gen2wheap_sink(igraph_gen2wheap_t *h, - igraph_integer_t head) { - igraph_integer_t size = igraph_gen2wheap_size(h); - if (LEFTCHILD(head) >= size) { - /* no subtrees */ - } else if (RIGHTCHILD(head) == size || - h->cmp(ELEM(h, LEFTCHILD(head)), ELEM(h, RIGHTCHILD(head))) >= 0) { - /* sink to the left if needed */ - if (h->cmp(ELEM(h, head), ELEM(h, LEFTCHILD(head))) < 0) { - igraph_i_gen2wheap_switch(h, head, LEFTCHILD(head)); - igraph_i_gen2wheap_sink(h, LEFTCHILD(head)); - } - } else { - /* sink to the right */ - if (h->cmp(ELEM(h, head), ELEM(h, RIGHTCHILD(head))) < 0) { - igraph_i_gen2wheap_switch(h, head, RIGHTCHILD(head)); - igraph_i_gen2wheap_sink(h, RIGHTCHILD(head)); - } - } -} - -/* ------------------ */ -/* These are public */ -/* ------------------ */ - -/** - * Initializes a new two-way heap. The max_size parameter defines the maximum - * number of items that the heap can hold. - */ -igraph_error_t igraph_gen2wheap_init( - igraph_gen2wheap_t *h, - int (*cmp)(const void *, const void *), - size_t item_size, igraph_integer_t max_size -) { - h->max_size = max_size; - /* We start with the biggest */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&h->index2, max_size); - h->cmp = cmp; - h->item_size = item_size; - h->data = igraph_calloc(max_size, item_size); - IGRAPH_CHECK_OOM(h->data, "Cannot initialized generic heap."); - IGRAPH_FINALLY(igraph_free, h->data); - IGRAPH_CHECK(igraph_vector_int_init(&h->index, 0)); - - IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; -} - -/** - * Destroys a two-way heap. - */ -void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h) { - IGRAPH_FREE(h->data); - igraph_vector_int_destroy(&h->index); - igraph_vector_int_destroy(&h->index2); -} - -/** - * Returns the current number of elements in the two-way heap. - */ -igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h) { - return igraph_vector_int_size(&h->index); -} - -/** - * Clears a two-way heap, i.e. removes all the elements from the heap. - */ -void igraph_gen2wheap_clear(igraph_gen2wheap_t *h) { - igraph_vector_int_clear(&h->index); - igraph_vector_int_null(&h->index2); -} - -/** - * Returns whether the two-way heap is empty. - */ -igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h) { - return igraph_vector_int_empty(&h->index); -} - -/** - * Pushes a new element into the two-way heap, with the given associated index. - * The index must be between 0 and the size of the heap minus 1, inclusive. It - * is assumed (and not checked) that the heap does not have another item with - * the same index. - */ -igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, - igraph_integer_t idx, const void *elem) { - - igraph_integer_t size = igraph_vector_int_size(&h->index); - - if (size > IGRAPH_INTEGER_MAX - 2) { - /* to allow size+2 below */ - IGRAPH_ERROR("Cannot push to gen2wheap, already at maximum size.", IGRAPH_EOVERFLOW); - } - - memcpy(ELEM(h, size), elem, h->item_size); - IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); - VECTOR(h->index2)[idx] = size + 2; - - /* maintain heap */ - igraph_i_gen2wheap_shift_up(h, size); - - return IGRAPH_SUCCESS; -} - -/** - * Returns the maximum number of elements that the two-way heap can hold. This - * is also one larger than the maximum allowed index that can be passed to - * \c igraph_gen2wheap_push_with_index . - */ -igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h) { - return h->max_size; -} - -/** - * Returns a pointer to the largest element in the heap. - */ -const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h) { - return ELEM(h, 0); -} - -/** - * Returns the index that was associated to the largest element in the heap - * when it was pushed to the heap. - */ -igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h) { - return VECTOR(h->index)[0]; -} - -/** - * Returns whether the heap contains an element with the given index, even if - * it was deactivated earlier. - */ -igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx) { - return VECTOR(h->index2)[idx] != 0; -} - -/** - * Returns whether the heap contains an element with the given index \em and it - * has not been deactivated yet. - */ -igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx) { - return VECTOR(h->index2)[idx] > 1; -} - -/** - * Returns a pointer to the item at the given index in the two-way heap. - */ -const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx) { - igraph_integer_t i = VECTOR(h->index2)[idx] - 2; - return ELEM(h, i); -} - -/** - * Deletes the largest element from the two-way heap. - * - * This function does \em not change the indices associated to the elements - * that remain in the heap. - */ -void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h) { - igraph_integer_t tmpidx = VECTOR(h->index)[0]; - igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); - igraph_vector_int_pop_back(&h->index); - VECTOR(h->index2)[tmpidx] = 0; - igraph_i_gen2wheap_sink(h, 0); -} - -/** - * Deactivates the largest element from the two-way heap. - * - * This function does \em not change the indices associated to the elements - * that remain in the heap. - */ -void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h) { - igraph_integer_t tmpidx = VECTOR(h->index)[0]; - igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); - igraph_vector_int_pop_back(&h->index); - VECTOR(h->index2)[tmpidx] = 1; - igraph_i_gen2wheap_sink(h, 0); -} - -/** - * Modifies the value associated to the given index in the two-way heap. - */ -void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem) { - - igraph_integer_t pos = VECTOR(h->index2)[idx] - 2; - - memcpy(ELEM(h, pos), elem, h->item_size); - igraph_i_gen2wheap_sink(h, pos); - igraph_i_gen2wheap_shift_up(h, pos); -} - -/** - * Checks that the heap is in a consistent state - */ -igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h) { - igraph_integer_t size = igraph_gen2wheap_size(h); - igraph_integer_t i; - igraph_bool_t error = false; - - /* Check the heap property */ - for (i = 0; i < size; i++) { - if (LEFTCHILD(i) >= size) { - break; - } - if (h->cmp(ELEM(h, LEFTCHILD(i)), ELEM(h, i)) > 0) { - error = true; break; - } - if (RIGHTCHILD(i) >= size) { - break; - } - if (h->cmp(ELEM(h, RIGHTCHILD(i)), ELEM(h, i)) > 0) { - error = true; break; - } - } - - if (error) { - IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); - } - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/core/genheap.h b/src/vendor/cigraph/src/core/genheap.h deleted file mode 100644 index 381631104b9..00000000000 --- a/src/vendor/cigraph/src/core/genheap.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - 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, see . - */ - -#include "igraph_types.h" -#include "igraph_vector.h" - -typedef struct igraph_gen2wheap_t { - /** Maximum number of items in the heap */ - igraph_integer_t max_size; - - /** The size of an individual item */ - size_t item_size; - - /** The items themselves in the heap */ - /* TODO: currently this is always allocated to have max_size */ - void *data; - - /** qsort-style comparison function used to order items */ - int (*cmp)(const void *, const void *); - - /** An integer index associated to each item in the heap; this vector is - * always modified in tandem with \c data . Values in this vector are - * between 0 and size-1 */ - igraph_vector_int_t index; - - /** - * A _reverse_ index that allows O(1) lookup of the position of a given - * value within the \c index member. Note that it uses two special values: - * index2[i] == 0 means that \c i is not in \c index at all, while - * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. - * The semantics of deactivation is up to the user of the data structure - * to decide. Other than these two special values, index2[i] == j means - * that index[j-2] == i and data[j-2] is the corresponding item in the heap - */ - igraph_vector_int_t index2; -} igraph_gen2wheap_t; - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_init( - igraph_gen2wheap_t *h, - int (*cmp)(const void *, const void *), - size_t item_size, igraph_integer_t max_size -); -IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_clear(igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, - igraph_integer_t idx, const void *elem); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h); -IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h); diff --git a/src/vendor/cigraph/src/core/grid.c b/src/vendor/cigraph/src/core/grid.c index f5ffe869dc3..cb5f88130bd 100644 --- a/src/vendor/cigraph/src/core/grid.c +++ b/src/vendor/cigraph/src/core/grid.c @@ -29,16 +29,15 @@ /* internal function */ -static void igraph_i_2dgrid_which( - igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, - igraph_integer_t *x, igraph_integer_t *y -) { +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 = floor((xc - (grid->minx)) / (grid->deltax)); + *x = (long int) floor((xc - (grid->minx)) / (grid->deltax)); } if (yc <= grid->miny) { @@ -46,20 +45,22 @@ static void igraph_i_2dgrid_which( } else if (yc >= grid->maxy) { *y = grid->stepsy - 1; } else { - *y = floor((yc - (grid->miny)) / (grid->deltay)); + *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); } + + return 0; } -igraph_error_t igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, +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) { - igraph_integer_t no_of_points; + long int i; IGRAPH_ASSERT(minx <= maxx); IGRAPH_ASSERT(miny <= maxy); IGRAPH_ASSERT(deltax > 0 && deltay > 0); - IGRAPH_ASSERT(isfinite(minx) && isfinite(maxx) && isfinite(miny) && isfinite(maxy)); - IGRAPH_ASSERT(isfinite(deltax) && isfinite(deltay)); + IGRAPH_ASSERT(igraph_finite(minx) && igraph_finite(maxx) && igraph_finite(miny) && igraph_finite(maxy)); + IGRAPH_ASSERT(igraph_finite(deltax) && igraph_finite(deltay)); grid->coords = coords; grid->minx = minx; @@ -69,44 +70,44 @@ igraph_error_t igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords grid->maxy = maxy; grid->deltay = deltay; - grid->stepsx = ceil((maxx - minx) / deltax); - grid->stepsy = ceil((maxy - miny) / deltay); + grid->stepsx = (long int) ceil((maxx - minx) / deltax); + grid->stepsy = (long int) ceil((maxy - miny) / deltay); - no_of_points = igraph_matrix_nrow(coords); + 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)); - IGRAPH_CHECK(igraph_matrix_int_init(&grid->startidx, grid->stepsx, grid->stepsy)); - IGRAPH_FINALLY(igraph_matrix_int_destroy, &grid->startidx); - IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->next, no_of_points); - IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->prev, no_of_points); - - igraph_vector_int_fill(&grid->prev, 0); - igraph_vector_int_fill(&grid->next, 0); + 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 IGRAPH_SUCCESS; + return 0; } void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) { - igraph_matrix_int_destroy(&grid->startidx); - igraph_vector_int_destroy(&grid->next); - igraph_vector_int_destroy(&grid->prev); + igraph_matrix_destroy(&grid->startidx); + igraph_vector_destroy(&grid->next); + igraph_vector_destroy(&grid->prev); } -void igraph_2dgrid_add(igraph_2dgrid_t *grid, igraph_integer_t elem, +void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc) { - igraph_integer_t x, y; - igraph_integer_t first; + long int x, y; + long int first; MATRIX(*grid->coords, elem, 0) = xc; MATRIX(*grid->coords, elem, 1) = yc; /* add to cell */ - igraph_i_2dgrid_which(grid, xc, yc, &x, &y); - first = MATRIX(grid->startidx, x, y); + 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) { @@ -119,17 +120,17 @@ void igraph_2dgrid_add(igraph_2dgrid_t *grid, igraph_integer_t elem, grid->vertices += 1; } -void igraph_2dgrid_add2(igraph_2dgrid_t *grid, igraph_integer_t elem) { - igraph_integer_t x, y; - igraph_integer_t first; +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_i_2dgrid_which(grid, xc, yc, &x, &y); - first = MATRIX(grid->startidx, x, y); + 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) { @@ -142,33 +143,33 @@ void igraph_2dgrid_add2(igraph_2dgrid_t *grid, igraph_integer_t elem) { grid->vertices += 1; } -void igraph_2dgrid_move(igraph_2dgrid_t *grid, igraph_integer_t elem, +void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, igraph_real_t xc, igraph_real_t yc) { - igraph_integer_t oldx, oldy; - igraph_integer_t newx, newy; + 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); - igraph_integer_t first; + long int first; xc = oldxc + xc; yc = oldyc + yc; - igraph_i_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); - igraph_i_2dgrid_which(grid, xc, yc, &newx, &newy); + 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) [ VECTOR(grid->prev)[elem] - 1 ] = + 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)[ VECTOR(grid->next)[elem] - 1 ] = + VECTOR(grid->prev)[ (long int) VECTOR(grid->next)[elem] - 1 ] = VECTOR(grid->prev)[elem]; } /* add to this cell */ - first = MATRIX(grid->startidx, newx, newy); + first = (long int) MATRIX(grid->startidx, newx, newy); VECTOR(grid->prev)[elem] = 0; VECTOR(grid->next)[elem] = first; if (first != 0) { @@ -191,106 +192,106 @@ void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, *massy = (grid->massy) / (grid->vertices); } -igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, igraph_integer_t elem) { +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, - igraph_integer_t e1, igraph_integer_t e2) { + 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); + return sqrt(x * x + y * y); } igraph_real_t igraph_2dgrid_dist2(const igraph_2dgrid_t *grid, - igraph_integer_t e1, igraph_integer_t e2) { + 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 igraph_error_t igraph_i_2dgrid_addvertices(igraph_2dgrid_t *grid, igraph_vector_int_t *eids, +static int igraph_i_2dgrid_addvertices(igraph_2dgrid_t *grid, igraph_vector_t *eids, igraph_integer_t vid, igraph_real_t r, - igraph_integer_t x, igraph_integer_t y) { - igraph_integer_t act; - igraph_integer_t *v = VECTOR(grid->next); + long int x, long int y) { + long int act; + igraph_real_t *v = VECTOR(grid->next); r = r * r; - act = MATRIX(grid->startidx, x, y); + act = (long int) MATRIX(grid->startidx, x, y); while (act != 0) { if (igraph_2dgrid_dist2(grid, vid, act - 1) < r) { - IGRAPH_CHECK(igraph_vector_int_push_back(eids, act - 1)); + IGRAPH_CHECK(igraph_vector_push_back(eids, act - 1)); } - act = v[act - 1]; + act = (long int) v[act - 1]; } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_int_t *eids, +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, vid, 0); - igraph_real_t yc = MATRIX(*grid->coords, vid, 1); - igraph_integer_t x, y; - igraph_vector_int_clear(eids); + 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_i_2dgrid_which(grid, xc, yc, &x, &y); + igraph_2dgrid_which(grid, xc, yc, &x, &y); /* this cell */ - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y); /* left */ if (x != 0) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y); } /* right */ if (x != grid->stepsx - 1) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y); } /* up */ if (y != 0) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y - 1)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y - 1); } /* down */ if (y != grid->stepsy - 1) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y + 1)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x, y + 1); } /* up & left */ if (x != 0 && y != 0) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y - 1)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y - 1); } /* up & right */ if (x != grid->stepsx - 1 && y != 0) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y - 1)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x + 1, y - 1); } /* down & left */ if (x != 0 && y != grid->stepsy - 1) { - IGRAPH_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 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_CHECK(igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1)); + igraph_i_2dgrid_addvertices(grid, eids, vid, r, x - 1, y + 1); } - return IGRAPH_SUCCESS; + 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 = MATRIX(grid->startidx, 0, 0); + 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 = MATRIX(grid->startidx, it->x, it->y); + 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) { - igraph_integer_t ret = it->vid; + long int ret = it->vid; if (ret == 0) { return 0; @@ -317,37 +318,37 @@ igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, it->nx[it->ncells] = it->x; it->ny[it->ncells] = it->y; - it->nei = VECTOR(grid->next) [ ret - 1 ]; + it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; while (it->ncells > 0 && it->nei == 0 ) { it->ncells -= 1; - it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } /* Next vertex */ - it->vid = VECTOR(grid->next)[ it->vid - 1 ]; + 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 = MATRIX(grid->startidx, it->x, it->y); + it->vid = (long int) MATRIX(grid->startidx, it->x, it->y); } - return ret; + return (igraph_integer_t) ret; } igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { - igraph_integer_t ret = it->nei; + long int ret = it->nei; if (it->nei != 0) { - it->nei = VECTOR(grid->next) [ ret - 1 ]; + it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; } while (it->ncells > 0 && it->nei == 0 ) { it->ncells -= 1; - it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); + it->nei = (long int) MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } - return ret; + return (igraph_integer_t) ret; } diff --git a/src/vendor/cigraph/src/core/grid.h b/src/vendor/cigraph/src/core/grid.h index d7496c9708b..d362ab79fba 100644 --- a/src/vendor/cigraph/src/core/grid.h +++ b/src/vendor/cigraph/src/core/grid.h @@ -35,38 +35,38 @@ __BEGIN_DECLS */ typedef struct igraph_2dgrid_t { - igraph_matrix_t *coords; /* The current coordinates in the grid */ - igraph_real_t minx, maxx, deltax; /* Minimum and maximum X coordinates and X spacing */ - igraph_real_t miny, maxy, deltay; /* Minimum and maximum Y coordinates and Y spacing */ - igraph_integer_t stepsx, stepsy; /* Number of cells in the X and Y directions */ - igraph_matrix_int_t startidx; /* startidx[i, j] is the index of an arbitrary point in that grid cell, plus one; zero means "empty cell" */ - igraph_vector_int_t next; /* next[i] is the index of the point following point i in the same cell, plus one; zero means "last point" */ - igraph_vector_int_t prev; /* prev[i] is the index of the point preceding point i in the same cell, plus one; zero means "first point" */ + 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 */ - igraph_integer_t vertices; /* Number of active vertices */ + long int vertices; /* Number of active vertices */ } igraph_2dgrid_t; -igraph_error_t igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, +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, igraph_integer_t elem, +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, igraph_integer_t elem); -void igraph_2dgrid_move(igraph_2dgrid_t *grid, igraph_integer_t elem, +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, igraph_integer_t elem); +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, - igraph_integer_t e1, igraph_integer_t e2); -igraph_error_t igraph_2dgrid_neighbors(igraph_2dgrid_t *grid, igraph_vector_int_t *eids, + 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 { - igraph_integer_t vid, x, y; - igraph_integer_t nei; - igraph_integer_t nx[4], ny[4], ncells; + 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); diff --git a/src/vendor/cigraph/src/core/heap.c b/src/vendor/cigraph/src/core/heap.c index a202ea64a60..b94a8d122df 100644 --- a/src/vendor/cigraph/src/core/heap.c +++ b/src/vendor/cigraph/src/core/heap.c @@ -37,7 +37,7 @@ #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL -#define BASE_INT +#define BASE_LONG #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "heap.pmt" @@ -48,7 +48,7 @@ #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN -#undef BASE_INT +#undef BASE_LONG #define BASE_CHAR #define HEAP_TYPE_MAX diff --git a/src/vendor/cigraph/src/core/heap.pmt b/src/vendor/cigraph/src/core/heap.pmt index 0aa14c97030..2501c8b1b19 100644 --- a/src/vendor/cigraph/src/core/heap.pmt +++ b/src/vendor/cigraph/src/core/heap.pmt @@ -23,6 +23,7 @@ #include "igraph_memory.h" #include "igraph_error.h" +#include "config.h" #include /* memcpy & co. */ #include @@ -32,39 +33,38 @@ #define RIGHTCHILD(x) (((x)+1)*2) /* Declare internal functions */ -static void FUNCTION(igraph_heap, i_build)(BASE* arr, igraph_integer_t size, igraph_integer_t head); -static void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_integer_t elem); -static void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_integer_t head); -static void FUNCTION(igraph_heap, i_switch)(BASE* arr, igraph_integer_t e1, igraph_integer_t e2); +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 \em empty heap, and also allocates memory - * for some elements. - * + * Creates an empty heap, but allocates size for some elements. * \param h Pointer to an uninitialized heap object. - * \param capacity Number of elements to allocate memory for. + * \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. */ -igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { - IGRAPH_ASSERT(capacity >= 0); - if (capacity == 0 ) { - capacity = 1; +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_begin = IGRAPH_CALLOC(capacity, BASE); - IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap."); - h->stor_end = h->stor_begin + capacity; + h->stor_end = h->stor_begin + alloc_size; h->end = h->stor_begin; - h->destroy = true; + h->destroy = 1; - return IGRAPH_SUCCESS; + return 0; } /** @@ -72,9 +72,8 @@ igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_ * \function igraph_heap_init_array * \brief Build a heap from an array. * - * Initializes a heap object from an array. The heap is also + * 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. @@ -83,18 +82,20 @@ igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_ * Time complexity: O(n), the number of elements in the heap. */ -igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, const BASE *data, igraph_integer_t len) { +int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, BASE* data, long int len) { h->stor_begin = IGRAPH_CALLOC(len, BASE); - IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap from array."); + 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 = true; + h->destroy = 1; - memcpy(h->stor_begin, data, (size_t) len * sizeof(BASE)); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -109,8 +110,9 @@ igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, const BAS void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) { if (h->destroy) { - if (h->stor_begin != NULL) { - IGRAPH_FREE(h->stor_begin); /* sets to NULL */ + if (h->stor_begin != 0) { + IGRAPH_FREE(h->stor_begin); + h->stor_begin = 0; } } } @@ -121,44 +123,23 @@ void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) { * \brief Decides whether a heap object is empty. * * \param h The heap object. - * \return \c true if the heap is empty, \c false otherwise. + * \return \c TRUE if the heap is empty, \c FALSE otherwise. * * TIme complexity: O(1). */ -igraph_bool_t FUNCTION(igraph_heap, empty)(const TYPE(igraph_heap)* h) { +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_clear - * \brief Removes all elements from a heap. - * - * This function simply sets the size of the heap to zero, it does - * not free any allocated memory. For that you have to call - * \ref igraph_heap_destroy(). - * - * \param h The heap object. - * - * Time complexity: O(1). - */ - -void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h) { - IGRAPH_ASSERT(h != NULL); - IGRAPH_ASSERT(h->stor_begin != NULL); - h->end = h->stor_begin; -} - /** * \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. @@ -168,17 +149,13 @@ void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h) { * that n push operations are performed in O(n log n) time. */ -igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { +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) { - igraph_integer_t old_size = FUNCTION(igraph_heap, size)(h); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to heap, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = FUNCTION(igraph_heap, size)(h) * 2; if (new_size == 0) { new_size = 1; } @@ -192,7 +169,7 @@ igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { FUNCTION(igraph_heap, i_shift_up)(h->stor_begin, FUNCTION(igraph_heap, size)(h), FUNCTION(igraph_heap, size)(h) - 1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -202,14 +179,13 @@ igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { * * 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)(const TYPE(igraph_heap)* h) { +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); @@ -220,11 +196,10 @@ BASE FUNCTION(igraph_heap, top)(const TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_delete_top - * \brief Removes and returns the top element. + * \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. * @@ -249,17 +224,16 @@ BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_size - * \brief Number of elements in the heap. + * \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). */ -igraph_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h) { +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; @@ -268,39 +242,38 @@ igraph_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_reserve - * \brief Reserves memory for a heap. + * \brief Allocate more memory * * Allocates memory for future use. The size of the heap is - * unchanged. If the heap is larger than the \p capacity parameter then + * unchanged. If the heap is larger than the \p size parameter then * nothing happens. - * * \param h The heap object. - * \param capacity The number of elements to allocate memory for. + * \param size The number of elements to allocate memory for. * \return Error code. * - * Time complexity: O(\p capacity) if \p capacity is larger than the current + * Time complexity: O(\p size) if \p size is larger than the current * number of elements. O(1) otherwise. */ -igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { - igraph_integer_t actual_size = FUNCTION(igraph_heap, size)(h); +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); - IGRAPH_ASSERT(capacity >= 0); - if (capacity <= actual_size) { - return IGRAPH_SUCCESS; + if (size <= actual_size) { + return 0; } - tmp = IGRAPH_REALLOC(h->stor_begin, (size_t) capacity, BASE); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for heap."); - + 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 + capacity; + h->stor_end = h->stor_begin + size; h->end = h->stor_begin + actual_size; - return IGRAPH_SUCCESS; + return 0; } /** @@ -309,7 +282,7 @@ igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integ */ void FUNCTION(igraph_heap, i_build)(BASE* arr, - igraph_integer_t size, igraph_integer_t head) { + long int size, long int head) { if (RIGHTCHILD(head) < size) { /* both subtrees */ @@ -331,7 +304,7 @@ void FUNCTION(igraph_heap, i_build)(BASE* arr, * called directly. */ -void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_integer_t elem) { +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 */ @@ -347,7 +320,7 @@ void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_ * called directly. */ -void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_integer_t head) { +void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) { if (LEFTCHILD(head) >= size) { /* no subtrees */ @@ -373,7 +346,7 @@ void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_inte * called directly. */ -void FUNCTION(igraph_heap, i_switch)(BASE* arr, igraph_integer_t e1, igraph_integer_t e2) { +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]; diff --git a/src/vendor/cigraph/src/core/indheap.c b/src/vendor/cigraph/src/core/indheap.c index ea6926bf58f..67592b058f7 100644 --- a/src/vendor/cigraph/src/core/indheap.c +++ b/src/vendor/cigraph/src/core/indheap.c @@ -37,75 +37,71 @@ #define LEFTCHILD(x) (((x)+1)*2-1) #define RIGHTCHILD(x) (((x)+1)*2) -static void igraph_indheap_i_build(igraph_indheap_t* h, igraph_integer_t head); -static void igraph_indheap_i_shift_up(igraph_indheap_t* h, igraph_integer_t elem); -static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head); -static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); +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: + * @return Error code: * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t alloc_size) { - IGRAPH_ASSERT(alloc_size >= 0); - if (alloc_size == 0 ) { +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) { - h->index_begin = NULL; - IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + if (h->stor_begin == 0) { + h->index_begin = 0; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); } - h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); - if (! h->index_begin) { + h->index_begin = IGRAPH_CALLOC(alloc_size, long int); + if (h->index_begin == 0) { IGRAPH_FREE(h->stor_begin); - h->stor_begin = NULL; - IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } -void igraph_indheap_clear(igraph_indheap_t *h) { +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: + * @return Error code: * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_indheap_init_array(igraph_indheap_t *h, const igraph_real_t *data, igraph_integer_t len) { - igraph_integer_t i; - igraph_integer_t alloc_size; +int igraph_indheap_init_array (igraph_indheap_t *h, igraph_real_t* data, long int len) { + long int i; - IGRAPH_ASSERT(len >= 0); - alloc_size = (len <= 0) ? 1 : len; - - h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); - if (! h->stor_begin) { + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); } - h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); - if (! h->index_begin) { + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); } - h->stor_end = h->stor_begin + alloc_size; - h->end = h->stor_begin + len; + 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)); @@ -113,9 +109,9 @@ igraph_error_t igraph_indheap_init_array(igraph_indheap_t *h, const igraph_real_ h->index_begin[i] = i + 1; } - igraph_indheap_i_build(h, 0); + igraph_indheap_i_build (h, 0); - return IGRAPH_SUCCESS; + return 0; } /** @@ -123,7 +119,7 @@ igraph_error_t igraph_indheap_init_array(igraph_indheap_t *h, const igraph_real_ * \brief Destroys an initialized indexed heap. */ -void igraph_indheap_destroy(igraph_indheap_t* h) { +void igraph_indheap_destroy (igraph_indheap_t* h) { IGRAPH_ASSERT(h != 0); if (h->destroy) { if (h->stor_begin != 0) { @@ -142,7 +138,7 @@ void igraph_indheap_destroy(igraph_indheap_t* h) { * \brief Checks whether a heap is empty. */ -igraph_bool_t igraph_indheap_empty(const igraph_indheap_t *h) { +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; @@ -153,17 +149,13 @@ igraph_bool_t igraph_indheap_empty(const igraph_indheap_t *h) { * \brief Adds an element to an indexed heap. */ -igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem) { +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) { - igraph_integer_t old_size = igraph_indheap_size(h); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = igraph_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } @@ -177,7 +169,7 @@ igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem) { /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -185,17 +177,13 @@ igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem) { * \brief Adds an element to an indexed heap with a given index. */ -igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem) { +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) { - igraph_integer_t old_size = igraph_indheap_size(h); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = igraph_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } @@ -209,7 +197,7 @@ igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_intege /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -217,8 +205,8 @@ igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_intege * \brief Modifies an element in an indexed heap. */ -void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem) { - igraph_integer_t i, n; +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); @@ -231,11 +219,13 @@ void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_rea } if (i == n) { - return; + return 0; } /* maintain indheap */ igraph_indheap_i_build(h, 0); + + return 0; } /** @@ -243,7 +233,7 @@ void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_rea * \brief Returns the largest element in an indexed heap. */ -igraph_real_t igraph_indheap_max(const igraph_indheap_t *h) { +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); @@ -275,7 +265,7 @@ igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h) { * \brief Gives the number of elements in an indexed heap. */ -igraph_integer_t igraph_indheap_size(const igraph_indheap_t* h) { +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; @@ -285,33 +275,33 @@ igraph_integer_t igraph_indheap_size(const igraph_indheap_t* h) { * \ingroup indheap * \brief Reserves more memory for an indexed heap. * - * \return Error code: + * @return Error code: * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size) { - igraph_integer_t actual_size = igraph_indheap_size(h); +int igraph_indheap_reserve (igraph_indheap_t* h, long int size) { + long int actual_size = igraph_indheap_size(h); igraph_real_t *tmp1; - igraph_integer_t *tmp2; + long int *tmp2; IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); if (size <= actual_size) { - return IGRAPH_SUCCESS; + return 0; } tmp1 = IGRAPH_CALLOC(size, igraph_real_t); if (tmp1 == 0) { - IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmp1); - tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); + tmp2 = IGRAPH_CALLOC(size, long int); if (tmp2 == 0) { - IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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(igraph_integer_t)); + memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(long int)); IGRAPH_FREE(h->stor_begin); IGRAPH_FREE(h->index_begin); @@ -321,7 +311,7 @@ igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size h->end = h->stor_begin + actual_size; IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -329,7 +319,7 @@ igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size * \brief Returns the index of the largest element in an indexed heap. */ -igraph_integer_t igraph_indheap_max_index(const igraph_indheap_t *h) { +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]; @@ -341,9 +331,9 @@ igraph_integer_t igraph_indheap_max_index(const igraph_indheap_t *h) { * directly. */ -static void igraph_indheap_i_build(igraph_indheap_t* h, igraph_integer_t head) { +static void igraph_indheap_i_build(igraph_indheap_t* h, long int head) { - igraph_integer_t size = igraph_indheap_size(h); + long int size = igraph_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_indheap_i_build(h, LEFTCHILD(head) ); @@ -364,7 +354,7 @@ static void igraph_indheap_i_build(igraph_indheap_t* h, igraph_integer_t head) { * directly. */ -static void igraph_indheap_i_shift_up(igraph_indheap_t *h, igraph_integer_t elem) { +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 */ @@ -380,9 +370,9 @@ static void igraph_indheap_i_shift_up(igraph_indheap_t *h, igraph_integer_t elem * directly. */ -static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head) { +static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) { - igraph_integer_t size = igraph_indheap_size(h); + long int size = igraph_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -407,7 +397,7 @@ static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head) { * directly. */ -static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2) { +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]; @@ -415,7 +405,7 @@ static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, ig tmp = h->index_begin[e1]; h->index_begin[e1] = h->index_begin[e2]; - h->index_begin[e2] = tmp; + h->index_begin[e2] = (long int) tmp; } } @@ -425,50 +415,49 @@ static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, ig /* Doubly indexed heap */ /* -------------------------------------------------- */ -/* static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t head); */ /* Unused function */ -static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, igraph_integer_t elem); -static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, igraph_integer_t head); -static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); +/* 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: + * @return Error code: * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t* h, igraph_integer_t alloc_size) { - IGRAPH_ASSERT(alloc_size >= 0); - if (alloc_size == 0 ) { +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); /* LCOV_EXCL_LINE */ + 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, igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); } - h->index2_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -476,7 +465,7 @@ igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t* h, igraph_integer_t all * \brief Destroys an initialized doubly indexed heap object. */ -void igraph_d_indheap_destroy(igraph_d_indheap_t* h) { +void igraph_d_indheap_destroy (igraph_d_indheap_t* h) { IGRAPH_ASSERT(h != 0); if (h->destroy) { if (h->stor_begin != 0) { @@ -499,7 +488,7 @@ void igraph_d_indheap_destroy(igraph_d_indheap_t* h) { * \brief Decides whether a heap is empty. */ -igraph_bool_t igraph_d_indheap_empty(const igraph_d_indheap_t *h) { +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; @@ -510,18 +499,14 @@ igraph_bool_t igraph_d_indheap_empty(const igraph_d_indheap_t *h) { * \brief Adds an element to the heap. */ -igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t* h, igraph_real_t elem, - igraph_integer_t idx, igraph_integer_t idx2) { +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) { - igraph_integer_t old_size = igraph_d_indheap_size(h); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = igraph_d_indheap_size(h) * 2; if (new_size == 0) { new_size = 1; } @@ -536,7 +521,7 @@ igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t* h, igraph_real_t elem, /* maintain d_indheap */ igraph_d_indheap_i_shift_up(h, igraph_d_indheap_size(h) - 1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -544,7 +529,7 @@ igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t* h, igraph_real_t elem, * \brief Returns the largest element in the heap. */ -igraph_real_t igraph_d_indheap_max(const igraph_d_indheap_t *h) { +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); @@ -576,7 +561,7 @@ igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h) { * \brief Gives the number of elements in the heap. */ -igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t* h) { +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; @@ -586,40 +571,40 @@ igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t* h) { * \ingroup doubleindheap * \brief Allocates memory for a heap. * - * \return Error code: + * @return Error code: * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t* h, igraph_integer_t size) { - igraph_integer_t actual_size = igraph_d_indheap_size(h); +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; - igraph_integer_t *tmp2, *tmp3; + long int *tmp2, *tmp3; IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); if (size <= actual_size) { - return IGRAPH_SUCCESS; + return 0; } tmp1 = IGRAPH_CALLOC(size, igraph_real_t); if (tmp1 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmp1); - tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); + tmp2 = IGRAPH_CALLOC(size, long int); if (tmp2 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmp2); - tmp3 = IGRAPH_CALLOC(size, igraph_integer_t); + tmp3 = IGRAPH_CALLOC(size, long int); if (tmp3 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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(igraph_integer_t)); - memcpy(tmp3, h->index2_begin, (size_t) actual_size * sizeof(igraph_integer_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); @@ -631,7 +616,7 @@ igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t* h, igraph_integer_t h->index2_begin = tmp3; IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -639,7 +624,7 @@ igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t* h, igraph_integer_t * \brief Gives the indices of the maximal element in the heap. */ -void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *idx2) { +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]; @@ -653,9 +638,9 @@ void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, ig /* Unused function, temporarily disabled */ #if 0 -static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t head) { +static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) { - igraph_integer_t size = igraph_d_indheap_size(h); + long int size = igraph_d_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_d_indheap_i_build(h, LEFTCHILD(head) ); @@ -676,7 +661,7 @@ static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t hea * \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, igraph_integer_t elem) { +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 */ @@ -691,9 +676,9 @@ static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, igraph_integer_t * \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, igraph_integer_t head) { +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head) { - igraph_integer_t size = igraph_d_indheap_size(h); + long int size = igraph_d_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -717,9 +702,9 @@ static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, igraph_integer_t head * \brief Switches two elements in the heap, don't call it directly. */ -static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2) { +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long int e2) { if (e1 != e2) { - igraph_integer_t tmpi; + long int tmpi; igraph_real_t tmp = h->stor_begin[e1]; h->stor_begin[e1] = h->stor_begin[e2]; h->stor_begin[e2] = tmp; @@ -753,9 +738,9 @@ static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1 normal heap does this in O(n) time.... */ static void igraph_i_2wheap_switch(igraph_2wheap_t *h, - igraph_integer_t e1, igraph_integer_t e2) { + long int e1, long int e2) { if (e1 != e2) { - igraph_integer_t tmp1, tmp2; + 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; @@ -772,7 +757,7 @@ static void igraph_i_2wheap_switch(igraph_2wheap_t *h, } static void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, - igraph_integer_t elem) { + long int elem) { if (elem == 0 || VECTOR(h->data)[elem] < VECTOR(h->data)[PARENT(elem)]) { /* at the top */ } else { @@ -782,8 +767,8 @@ static void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, } static void igraph_i_2wheap_sink(igraph_2wheap_t *h, - igraph_integer_t head) { - igraph_integer_t size = igraph_2wheap_size(h); + long int head) { + long int size = igraph_2wheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -806,142 +791,87 @@ static void igraph_i_2wheap_sink(igraph_2wheap_t *h, /* These are public */ /* ------------------ */ -/** - * Initializes a new two-way heap. The max_size parameter defines the maximum - * number of items that the heap can hold. - */ -igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t max_size) { - h->max_size = max_size; +int igraph_2wheap_init(igraph_2wheap_t *h, long int size) { + h->size = size; /* We start with the biggest */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&h->index2, max_size); + 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_int_init(&h->index, 0)); - /* IGRAPH_FINALLY(igraph_vector_int_destroy, &h->index); */ + IGRAPH_CHECK(igraph_vector_long_init(&h->index, 0)); + /* IGRAPH_FINALLY(igraph_vector_long_destroy, &h->index); */ IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -/** - * Destroys a two-way heap. - */ void igraph_2wheap_destroy(igraph_2wheap_t *h) { igraph_vector_destroy(&h->data); - igraph_vector_int_destroy(&h->index); - igraph_vector_int_destroy(&h->index2); + igraph_vector_long_destroy(&h->index); + igraph_vector_long_destroy(&h->index2); } -/** - * Clears a two-way heap, i.e. removes all the elements from the heap. - */ -void igraph_2wheap_clear(igraph_2wheap_t *h) { +int igraph_2wheap_clear(igraph_2wheap_t *h) { igraph_vector_clear(&h->data); - igraph_vector_int_clear(&h->index); - igraph_vector_int_null(&h->index2); + igraph_vector_long_clear(&h->index); + igraph_vector_long_null(&h->index2); + return 0; } -/** - * Returns whether the two-way heap is empty. - */ igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h) { return igraph_vector_empty(&h->data); } -/** - * Pushes a new element into the two-way heap, with the given associated index. - * The index must be between 0 and the size of the heap minus 1, inclusive. It - * is assumed (and not checked) that the heap does not have another item with - * the same index. - */ -igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, - igraph_integer_t idx, igraph_real_t elem) { +int igraph_2wheap_push_with_index(igraph_2wheap_t *h, + long int idx, igraph_real_t elem) { /* printf("-> %.2g [%li]\n", elem, idx); */ - igraph_integer_t size = igraph_vector_size(&h->data); - - if (size > IGRAPH_INTEGER_MAX - 2) { - /* to allow size+2 below */ - IGRAPH_ERROR("Cannot push to 2wheap, already at maximum size.", IGRAPH_EOVERFLOW); - } - + long int size = igraph_vector_size(&h->data); IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem)); - IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); + 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 IGRAPH_SUCCESS; + return 0; } -/** - * Returns the current number of elements in the two-way heap. - */ -igraph_integer_t igraph_2wheap_size(const igraph_2wheap_t *h) { +long int igraph_2wheap_size(const igraph_2wheap_t *h) { return igraph_vector_size(&h->data); } -/** - * Returns the maximum number of elements that the two-way heap can hold. This - * is also one larger than the maximum allowed index that can be passed to - * \c igraph_2wheap_push_with_index . - */ -igraph_integer_t igraph_2wheap_max_size(const igraph_2wheap_t *h) { - return h->max_size; +long int igraph_2wheap_max_size(const igraph_2wheap_t *h) { + return h->size; } -/** - * Returns the largest element in the heap. - */ igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h) { return VECTOR(h->data)[0]; } -/** - * Returns the index that was associated to the largest element in the heap - * when it was pushed to the heap. - */ -igraph_integer_t igraph_2wheap_max_index(const igraph_2wheap_t *h) { +long int igraph_2wheap_max_index(const igraph_2wheap_t *h) { return VECTOR(h->index)[0]; } -/** - * Returns whether the heap contains an element with the given index, even if - * it was deactivated earlier. - */ -igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx) { +igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, long int idx) { return VECTOR(h->index2)[idx] != 0; } -/** - * Returns whether the heap contains an element with the given index \em and it - * has not been deactivated yet. - */ -igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx) { +igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx) { return VECTOR(h->index2)[idx] > 1; } -/** - * Returns the item at the given index in the two-way heap. - */ -igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t idx) { - igraph_integer_t i = VECTOR(h->index2)[idx] - 2; +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]; } -/** - * Deletes and returns the largest element from the two-way heap. - * - * This function does \em not change the indices associated to the elements - * that remain in the heap. - */ igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { igraph_real_t tmp = VECTOR(h->data)[0]; - igraph_integer_t tmpidx = VECTOR(h->index)[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_int_pop_back(&h->index); + igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); @@ -950,39 +880,26 @@ igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { return tmp; } -/** - * Deactivates and returns the largest element from the two-way heap. - * - * This function does \em not change the indices associated to the elements - * that remain in the heap. - */ igraph_real_t igraph_2wheap_deactivate_max(igraph_2wheap_t *h) { igraph_real_t tmp = VECTOR(h->data)[0]; - igraph_integer_t tmpidx = VECTOR(h->index)[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_int_pop_back(&h->index); + igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 1; igraph_i_2wheap_sink(h, 0); return tmp; } -/** - * Deletes the largest element from the heap and returns it along with its - * associated index (the latter being returned in an output argument). - * - * This function does \em not change the indices associated to the elements - * that remain in the heap. - */ -igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, igraph_integer_t *idx) { +igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx) { igraph_real_t tmp = VECTOR(h->data)[0]; - igraph_integer_t tmpidx = VECTOR(h->index)[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_int_pop_back(&h->index); + igraph_vector_long_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); @@ -992,27 +909,25 @@ igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, igraph_integer_ return tmp; } -/** - * Modifies the value associated to the given index in the two-way heap. - */ -void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem) { +int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem) { - igraph_integer_t pos = VECTOR(h->index2)[idx] - 2; + 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; } -/** - * Checks that the heap is in a consistent state - */ -igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h) { - igraph_integer_t size = igraph_2wheap_size(h); - igraph_integer_t i; - igraph_bool_t error = false; +/* 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++) { @@ -1020,19 +935,19 @@ igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h) { break; } if (VECTOR(h->data)[LEFTCHILD(i)] > VECTOR(h->data)[i]) { - error = true; break; + error = 1; break; } if (RIGHTCHILD(i) >= size) { break; } if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) { - error = true; break; + error = 1; break; } } if (error) { - IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); + IGRAPH_ERROR("Inconsistent heap", IGRAPH_EINTERNAL); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/indheap.h b/src/vendor/cigraph/src/core/indheap.h index 9f340c33301..16977f9fa62 100644 --- a/src/vendor/cigraph/src/core/indheap.h +++ b/src/vendor/cigraph/src/core/indheap.h @@ -42,25 +42,25 @@ typedef struct s_indheap { igraph_real_t* stor_begin; igraph_real_t* stor_end; igraph_real_t* end; - igraph_bool_t destroy; - igraph_integer_t* index_begin; + int destroy; + long int* index_begin; } igraph_indheap_t; #define IGRAPH_INDHEAP_NULL { 0,0,0,0,0 } -igraph_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t size); -igraph_error_t igraph_indheap_init_array(igraph_indheap_t *t, const igraph_real_t *data, igraph_integer_t len); -void igraph_indheap_destroy(igraph_indheap_t* h); -void igraph_indheap_clear(igraph_indheap_t *h); -igraph_bool_t igraph_indheap_empty(const igraph_indheap_t* h); -igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem); -igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); -void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); -igraph_real_t igraph_indheap_max(const igraph_indheap_t* h); +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); -igraph_integer_t igraph_indheap_size(const igraph_indheap_t *h); -igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size); -igraph_integer_t igraph_indheap_max_index(const 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); /* -------------------------------------------------- */ @@ -81,24 +81,24 @@ typedef struct s_indheap_d { igraph_real_t* stor_begin; igraph_real_t* stor_end; igraph_real_t* end; - igraph_bool_t destroy; - igraph_integer_t* index_begin; - igraph_integer_t* index2_begin; + 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 igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t *h, igraph_integer_t size); +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(const igraph_d_indheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t *h, igraph_real_t elem, - igraph_integer_t idx, igraph_integer_t idx2); -IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_max(const 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 igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t *h, igraph_integer_t size); -IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *idx2); +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 */ @@ -110,47 +110,30 @@ IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igr normal heap does this in O(n) time.... */ typedef struct igraph_2wheap_t { - /** Number of items in the heap */ - igraph_integer_t max_size; - - /** The items themselves in the heap */ + long int size; igraph_vector_t data; - - /** An integer index associated to each item in the heap; this vector is - * always modified in tandem with \c data . Values in this vector are - * between 0 and size-1 */ - igraph_vector_int_t index; - - /** - * A _reverse_ index that allows O(1) lookup of the position of a given - * value within the \c index member. Note that it uses two special values: - * index2[i] == 0 means that \c i is not in \c index at all, while - * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. - * The semantics of deactivation is up to the user of the data structure - * to decide. Other than these two special values, index2[i] == j means - * that index[j-2] == i and data[j-2] is the corresponding item in the heap - */ - igraph_vector_int_t index2; + igraph_vector_long_t index; + igraph_vector_long_t index2; } igraph_2wheap_t; -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t size); +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 void igraph_2wheap_clear(igraph_2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, - igraph_integer_t idx, igraph_real_t elem); +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 igraph_integer_t igraph_2wheap_size(const igraph_2wheap_t *h); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_2wheap_max_size(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 igraph_integer_t igraph_2wheap_max_index(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, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t idx); +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, igraph_integer_t *idx); -IGRAPH_PRIVATE_EXPORT void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_check(const 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 diff --git a/src/vendor/cigraph/src/core/interruption.c b/src/vendor/cigraph/src/core/interruption.c index 794220389ac..d4bf3c91a33 100644 --- a/src/vendor/cigraph/src/core/interruption.c +++ b/src/vendor/cigraph/src/core/interruption.c @@ -24,17 +24,19 @@ #include "igraph_interrupt.h" #include "config.h" -IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler = 0; +IGRAPH_THREAD_LOCAL igraph_interruption_handler_t +*igraph_i_interruption_handler = 0; -igraph_error_t igraph_allow_interruption(void *data) { +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_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/vendor/cigraph/src/core/interruption.h b/src/vendor/cigraph/src/core/interruption.h index a90ac88a804..6afda321451 100644 --- a/src/vendor/cigraph/src/core/interruption.h +++ b/src/vendor/cigraph/src/core/interruption.h @@ -30,7 +30,8 @@ __BEGIN_DECLS -extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler; +extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t +*igraph_i_interruption_handler; /** * \define IGRAPH_ALLOW_INTERRUPTION @@ -45,11 +46,12 @@ extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_ #define IGRAPH_ALLOW_INTERRUPTION() \ do { \ - if (igraph_i_interruption_handler) { \ - if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \ - return IGRAPH_INTERRUPTED; \ - } \ - } \ + 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 diff --git a/src/vendor/cigraph/src/core/marked_queue.c b/src/vendor/cigraph/src/core/marked_queue.c index 29fe4116cba..8166793cbed 100644 --- a/src/vendor/cigraph/src/core/marked_queue.c +++ b/src/vendor/cigraph/src/core/marked_queue.c @@ -25,64 +25,64 @@ #define BATCH_MARKER -1 -igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, - igraph_integer_t size) { - IGRAPH_CHECK(igraph_dqueue_int_init(&q->Q, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q->Q); - IGRAPH_CHECK(igraph_vector_int_init(&q->set, size)); +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 IGRAPH_SUCCESS; + return 0; } -void igraph_marked_queue_int_destroy(igraph_marked_queue_int_t *q) { - igraph_vector_int_destroy(&q->set); - igraph_dqueue_int_destroy(&q->Q); +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_int_reset(igraph_marked_queue_int_t *q) { - igraph_dqueue_int_clear(&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_int_null(&q->set); + igraph_vector_long_null(&q->set); q->mark += 1; } } -igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q) { +igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q) { return q->size == 0; } -igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_t *q) { +long int igraph_marked_queue_size(const igraph_marked_queue_t *q) { return q->size; } -igraph_bool_t igraph_marked_queue_int_iselement(const igraph_marked_queue_int_t *q, - igraph_integer_t elem) { +igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, + long int elem) { return (VECTOR(q->set)[elem] == q->mark); } -igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem) { +int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem) { if (VECTOR(q->set)[elem] != q->mark) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, elem)); + IGRAPH_CHECK(igraph_dqueue_push(&q->Q, elem)); VECTOR(q->set)[elem] = q->mark; q->size += 1; } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, BATCH_MARKER)); - return IGRAPH_SUCCESS; +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_int_pop_back_batch(igraph_marked_queue_int_t *q) { - igraph_integer_t size = igraph_dqueue_int_size(&q->Q); - igraph_integer_t elem; +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 = igraph_dqueue_int_pop_back(&q->Q)) != BATCH_MARKER) { + (elem = (long int) igraph_dqueue_pop_back(&q->Q)) != BATCH_MARKER) { VECTOR(q->set)[elem] = 0; size--; q->size--; @@ -90,26 +90,26 @@ void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q) { } #ifndef USING_R -igraph_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q) { - IGRAPH_CHECK(igraph_dqueue_int_print(&q->Q)); - return IGRAPH_SUCCESS; +int igraph_marked_queue_print(const igraph_marked_queue_t *q) { + IGRAPH_CHECK(igraph_dqueue_print(&q->Q)); + return 0; } #endif -igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file) { - IGRAPH_CHECK(igraph_dqueue_int_fprint(&q->Q, file)); - return IGRAPH_SUCCESS; +int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file) { + IGRAPH_CHECK(igraph_dqueue_fprint(&q->Q, file)); + return 0; } -igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, - igraph_vector_int_t *vec) { - igraph_integer_t i, p, n = igraph_dqueue_int_size(&q->Q); - IGRAPH_CHECK(igraph_vector_int_resize(vec, q->size)); +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_integer_t e = igraph_dqueue_int_get(&q->Q, i); + igraph_real_t e = igraph_dqueue_e(&q->Q, i); if (e != BATCH_MARKER) { VECTOR(*vec)[p++] = e; } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/marked_queue.h b/src/vendor/cigraph/src/core/marked_queue.h index 564fe9f4e52..be36e284327 100644 --- a/src/vendor/cigraph/src/core/marked_queue.h +++ b/src/vendor/cigraph/src/core/marked_queue.h @@ -24,14 +24,11 @@ #ifndef IGRAPH_MARKED_QUEUE_H #define IGRAPH_MARKED_QUEUE_H -#include "igraph_decls.h" #include "igraph_vector.h" #include "igraph_dqueue.h" #include -__BEGIN_DECLS - /* 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. @@ -42,34 +39,32 @@ __BEGIN_DECLS is essentially a stack. */ -typedef struct igraph_marked_queue_int_t { - igraph_dqueue_int_t Q; - igraph_vector_int_t set; - igraph_integer_t mark; - igraph_integer_t size; -} igraph_marked_queue_int_t; - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, - igraph_integer_t size); -IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_destroy(igraph_marked_queue_int_t *q); -IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_reset(igraph_marked_queue_int_t *q); +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 igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_t *q); +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_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file); +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 igraph_bool_t igraph_marked_queue_int_iselement(const igraph_marked_queue_int_t *q, - igraph_integer_t elem); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem); +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_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q); -IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q); +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 igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, - igraph_vector_int_t *vec); +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); -__END_DECLS +IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, + igraph_vector_t *vec); #endif diff --git a/src/vendor/cigraph/src/core/math.h b/src/vendor/cigraph/src/core/math.h index 891abc135b2..c50bd132ef7 100644 --- a/src/vendor/cigraph/src/core/math.h +++ b/src/vendor/cigraph/src/core/math.h @@ -1,6 +1,8 @@ +/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2022 The igraph development team + 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 @@ -13,71 +15,74 @@ GNU 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 . + 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_MATH_H -#define IGRAPH_CORE_MATH_H +#ifndef IGRAPH_MATH_H +#define IGRAPH_MATH_H -/* Use math constants with MSVC */ -#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) -#define _USE_MATH_DEFINES -#endif +#include "igraph_decls.h" + +#include "config.h" #include +#include -/* Math constants are not part of standard C */ +__BEGIN_DECLS -#ifndef M_E -#define M_E 2.71828182845904523536028747135266250 -#endif +/** + * \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 -#ifndef M_LOG2E -#define M_LOG2E 1.44269504088896340735992468100189214 +/* + * 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 M_LOG10E -#define M_LOG10E 0.434294481903251827651128918916605082 +#ifndef HAVE_LOG1P + #define log1p(a) igraph_log1p(a) #endif - -#ifndef M_LN2 -#define M_LN2 0.693147180559945309417232121458176568 +#ifndef HAVE_FMIN + #define fmin(a,b) igraph_fmin((a),(b)) #endif - -#ifndef M_LN10 -#define M_LN10 2.30258509299404568401799145468436421 +#ifndef HAVE_ROUND + #define round igraph_i_round #endif #ifndef M_PI -#define M_PI 3.14159265358979323846264338327950288 + #define M_PI 3.14159265358979323846 #endif - #ifndef M_PI_2 -#define M_PI_2 1.57079632679489661923132169163975144 + #define M_PI_2 1.57079632679489661923 #endif - -#ifndef M_PI_4 -#define M_PI_4 0.785398163397448309615660845819875721 +#ifndef M_LN2 + #define M_LN2 0.69314718055994530942 #endif - -#ifndef M_1_PI -#define M_1_PI 0.318309886183790671537767526745028724 +#ifndef M_SQRT2 + #define M_SQRT2 1.4142135623730950488016887 #endif - -#ifndef M_2_PI -#define M_2_PI 0.636619772367581343075535053490057448 +#ifndef M_LN_SQRT_2PI + #define M_LN_SQRT_2PI 0.918938533204672741780329736406 /* log(sqrt(2*pi)) + == log(2*pi)/2 */ #endif -#ifndef M_2_SQRTPI -#define M_2_SQRTPI 1.12837916709551257389615890312154517 -#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); -#ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880168872420969808 -#endif +__END_DECLS -#ifndef M_SQRT1_2 -#define M_SQRT1_2 0.707106781186547524400844362104849039 #endif - -#endif /* IGRAPH_CORE_MATH_H */ diff --git a/src/vendor/cigraph/src/core/matrix.c b/src/vendor/cigraph/src/core/matrix.c index 62a1c3c2fde..80bd31e471b 100644 --- a/src/vendor/cigraph/src/core/matrix.c +++ b/src/vendor/cigraph/src/core/matrix.c @@ -21,8 +21,8 @@ */ -#include "igraph_matrix.h" #include "igraph_types.h" +#include "igraph_matrix.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" @@ -36,6 +36,12 @@ #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" @@ -54,251 +60,99 @@ #include "igraph_pmt_off.h" #undef BASE_COMPLEX -/** - * \ingroup matrix - * \function igraph_matrix_complex_real - * \brief Gives the real part of a complex matrix. - * - * \param m Pointer to a complex matrix. - * \param real Pointer to an initialized matrix. The result will be stored here. - * \return Error code. - * - * Time complexity: O(n), - * n is the - * number of elements in the matrix. - */ +#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; +} -igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *m, +int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, igraph_matrix_t *real) { - igraph_integer_t nrow = igraph_matrix_complex_nrow(m); - igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + 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(&m->data, &real->data)); - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_vector_complex_real(&v->data, &real->data)); + return 0; } -/** - * \ingroup matrix - * \function igraph_matrix_complex_imag - * \brief Gives the imaginary part of a complex matrix. - * - * \param m Pointer to a complex matrix. - * \param imag Pointer to an initialized matrix. The result will be stored here. - * \return Error code. - * - * Time complexity: O(n), - * n is the - * number of elements in the matrix. - */ - -igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *m, +int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, igraph_matrix_t *imag) { - igraph_integer_t nrow = igraph_matrix_complex_nrow(m); - igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + 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(&m->data, &imag->data)); - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_vector_complex_imag(&v->data, &imag->data)); + return 0; } -/** - * \ingroup matrix - * \function igraph_matrix_complex_realimag - * \brief Gives the real and imaginary parts of a complex matrix. - * - * \param m Pointer to a complex matrix. - * \param real Pointer to an initialized matrix. The real part will be stored here. - * \param imag Pointer to an initialized matrix. The imaginary part will be stored here. - * \return Error code. - * - * Time complexity: O(n), - * n is the - * number of elements in the matrix. - */ - -igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *m, +int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, igraph_matrix_t *real, igraph_matrix_t *imag) { - igraph_integer_t nrow = igraph_matrix_complex_nrow(m); - igraph_integer_t ncol = igraph_matrix_complex_ncol(m); + 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(&m->data, &real->data, + IGRAPH_CHECK(igraph_vector_complex_realimag(&v->data, &real->data, &imag->data)); - return IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup matrix - * \function igraph_matrix_complex_create - * \brief Creates a complex matrix from a real and imaginary part. - * - * \param m Pointer to an uninitialized complex matrix. - * \param real Pointer to the real part of the complex matrix. - * \param imag Pointer to the imaginary part of the complex matrix. - * \return Error code. - * - * Time complexity: O(n), - * n is the - * number of elements in the matrix. - */ - -igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *m, +int igraph_matrix_complex_create(igraph_matrix_complex_t *v, const igraph_matrix_t *real, const igraph_matrix_t *imag) { - igraph_integer_t nrowr = igraph_matrix_nrow(real); - igraph_integer_t ncolr = igraph_matrix_ncol(real); - igraph_integer_t nrowi = igraph_matrix_nrow(imag); - igraph_integer_t ncoli = igraph_matrix_ncol(imag); - - if (nrowr != nrowi || ncolr != ncoli) { - IGRAPH_ERRORF("Dimensions of real (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " - "imaginary (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", - IGRAPH_EINVAL, nrowr, ncolr, nrowi, ncoli); - } - - IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); - - for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { - VECTOR(m->data)[i] = igraph_complex(VECTOR(real->data)[i], VECTOR(imag->data)[i]); - } - - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_vector_complex_create(&v->data, &real->data, + &imag->data)); + return 0; } -/** - * \ingroup matrix - * \function igraph_matrix_complex_create_polar - * \brief Creates a complex matrix from a magnitude and an angle. - * - * \param m Pointer to an uninitialized complex matrix. - * \param r Pointer to a real matrix containing magnitudes. - * \param theta Pointer to a real matrix containing arguments (phase angles). - * \return Error code. - * - * Time complexity: O(n), - * n is the - * number of elements in the matrix. - */ - -igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *m, +int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, const igraph_matrix_t *r, const igraph_matrix_t *theta) { - igraph_integer_t nrowr = igraph_matrix_nrow(r); - igraph_integer_t ncolr = igraph_matrix_ncol(r); - igraph_integer_t nrowt = igraph_matrix_nrow(theta); - igraph_integer_t ncolt = igraph_matrix_ncol(theta); - - if (nrowr != nrowt || ncolr != ncolt) { - IGRAPH_ERRORF("Dimensions of magnitude (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " - "angle (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", - IGRAPH_EINVAL, nrowr, ncolr, nrowt, ncolt); - } - - IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); - - for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { - VECTOR(m->data)[i] = igraph_complex_polar(VECTOR(r->data)[i], VECTOR(theta->data)[i]); - } - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_matrix_complex_all_almost_e - * \brief Are all elements almost equal? - * - * Checks if the elements of two complex matrices are equal within a relative tolerance. - * - * \param lhs The first matrix. - * \param rhs The second matrix. - * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. - * \return True if the two matrices are almost equal, false if there is at least - * one differing element or if the matrices are not of the same dimensions. - */ -igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, - igraph_matrix_complex_t *rhs, - igraph_real_t eps) { - return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && - igraph_vector_complex_all_almost_e(&lhs->data, &rhs->data, eps); + IGRAPH_CHECK(igraph_vector_complex_create_polar(&v->data, &r->data, + &theta->data)); + return 0; } -/** - * Deprecated in favour of \ref igraph_matrix_all_almost_e() which uses - * relative tolerances. Will be removed in 0.11. - * - * Checks if two matrices are equal within an absolute tolerance. - */ igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, const igraph_matrix_t *rhs, igraph_real_t tol) { - return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && - igraph_vector_e_tol(&lhs->data, &rhs->data, tol); + return igraph_vector_e_tol(&lhs->data, &rhs->data, tol); } - -/** - * \function igraph_matrix_all_almost_e - * \brief Are all elements almost equal? - * - * Checks if the elements of two matrices are equal within a relative tolerance. - * - * \param lhs The first matrix. - * \param rhs The second matrix. - * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. - * \return True if the two matrices are almost equal, false if there is at least - * one differing element or if the matrices are not of the same dimensions. - */ -igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, - const igraph_matrix_t *rhs, - igraph_real_t eps) { - return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && - igraph_vector_all_almost_e(&lhs->data, &rhs->data, eps); -} - - -/** - * \function igraph_matrix_zapsmall - * \brief Replaces small elements of a matrix by exact zeros. - * - * Matrix elements which are smaller in magnitude than the given absolute - * tolerance will be replaced by exact zeros. The default tolerance - * corresponds to two-thirds of the representable digits of \type igraph_real_t, - * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. - * - * \param m The matrix to process, it will be changed in-place. - * \param tol Tolerance value. Numbers smaller than this in magnitude will - * be replaced by zeros. Pass in zero to use the default tolerance. - * Must not be negative. - * \return Error code. - * - * \sa \ref igraph_matrix_all_almost_e() and \ref igraph_almost_equals() to - * perform comparisons with relative tolerances. - */ -igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { +int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { return igraph_vector_zapsmall(&m->data, tol); } - -/** - * \function igraph_matrix_complex_zapsmall - * \brief Replaces small elements of a complex matrix by exact zeros. - * - * Similarly to \ref igraph_matrix_zapsmall(), small elements will be replaced - * by zeros. The operation is performed separately on the real and imaginary - * parts of the numbers. This way, complex numbers with a large real part and - * tiny imaginary part will effectively be transformed to real numbers. - * The default tolerance - * corresponds to two-thirds of the representable digits of \type igraph_real_t, - * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. - * - * \param m The matrix to process, it will be changed in-place. - * \param tol Tolerance value. Real and imaginary parts smaller than this in - * magnitude will be replaced by zeros. Pass in zero to use the default - * tolerance. Must not be negative. - * \return Error code. - * - * \sa \ref igraph_matrix_complex_all_almost_e() and - * \ref igraph_complex_almost_equals() to perform comparisons with relative - * tolerances. - */ -igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol) { - return igraph_vector_complex_zapsmall(&m->data, tol); -} diff --git a/src/vendor/cigraph/src/core/matrix.pmt b/src/vendor/cigraph/src/core/matrix.pmt index 693ccce0ca2..27071ace39f 100644 --- a/src/vendor/cigraph/src/core/matrix.pmt +++ b/src/vendor/cigraph/src/core/matrix.pmt @@ -21,10 +21,10 @@ */ +#include "igraph_memory.h" +#include "igraph_random.h" #include "igraph_error.h" -#include "math/safe_intop.h" - #include /* memcpy & co. */ #include @@ -58,116 +58,26 @@ * \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. + * Time complexity: usually O(n), + * n is the + * number of elements in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, init)( - TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { - igraph_integer_t size; - IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); - IGRAPH_SAFE_MULT(nrow, ncol, &size); - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&m->data, size)); +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; } -/** - * \ingroup matrix - * \function igraph_matrix_view - * \brief Creates a matrix view into an existing array. - * - * - * This function lets you treat an existing C array as a matrix. The elements - * of the matrix are assumed to be stored in column-major order in the array, - * i.e. the elements of the first column are stored first, followed by the - * second column and so on. - * - * - * Since this function creates a view into an existing array, you must \em not - * destroy the \c igraph_matrix_t object when you are done with it. Similarly, - * you must \em not call any function on it that may attempt to modify the size - * of the matrix. Modifying an element in the matrix will modify the underlying - * array as the two share the same memory block. - * - * \param m Pointer to a not yet initialized matrix object where the view will - * be created. - * \param data The array that the matrix provides a view into. - * \param nrow The number of rows in the matrix. - * \param ncol The number of columns in the matrix. - * \return Pointer to the matrix object, the same as the \p m parameter, for - * convenience. - * - * Time complexity: O(1). - */ - -const TYPE(igraph_matrix)* FUNCTION(igraph_matrix, view)( - const TYPE(igraph_matrix) *m, const BASE *data, - igraph_integer_t nrow, igraph_integer_t ncol) { - /* temporarily cast away the constness */ - TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; - - /* No overflow checking, as this function does not return igraph_error_t. - * It is the caller's resposibility to ensure that the size of 'data' - * matches nrow*ncol, which also implies that nrow*ncol does not overflow. */ - - FUNCTION(igraph_vector, view)(&m2->data, data, ncol * nrow); - m2->nrow = nrow; - m2->ncol = ncol; - - return m; -} - -/** - * \ingroup matrix - * \function igraph_matrix_view_from_vector - * \brief Creates a matrix view that treats an existing vector as a matrix. - * - * - * This function lets you treat an existing igraph vector as a matrix. The - * elements of the matrix are assumed to be stored in column-major order in the - * vector, i.e. the elements of the first column are stored first, followed by - * the second column and so on. - * - * - * Since this function creates a view into an existing vector, you must \em not - * destroy the \c igraph_matrix_t object when you are done with it. Similarly, - * you must \em not call any function on it that may attempt to modify the size - * of the vector. Modifying an element in the matrix will modify the underlying - * vector as the two share the same memory block. - * - * - * Additionally, you must \em not attempt to grow the underlying vector by any - * vector operation as that may result in a re-allocation of the backing memory - * block of the vector, and the matrix view will not be informed about the - * re-allocation so it will point to an invalid memory area afterwards. - * - * \param m Pointer to a not yet initialized matrix object where the view will - * be created. - * \param v The vector that the matrix will provide a view into. - * \param nrow The number of rows in the matrix. The number of columns will be - * derived implicitly from the size of the vector. If the number of - * items in the vector is not divisible by the number of rows, the - * last few elements of the vector will not be covered by the view. - * \return Error code. - * - * Time complexity: O(1). - */ - -IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( - const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, - igraph_integer_t nrow -) { - /* temporarily cast away the constness */ +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; - - igraph_integer_t size = FUNCTION(igraph_vector, size)(v); - igraph_integer_t ncol = nrow > 0 ? size / nrow : 0; - - FUNCTION(igraph_vector, view)(&m2->data, VECTOR(*v), ncol * nrow); + FUNCTION(igraph_vector, view)(&m2->data, data, nrow * ncol); m2->nrow = nrow; m2->ncol = ncol; - return m; } @@ -207,7 +117,7 @@ void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { +long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector, capacity)(&m->data); } @@ -238,14 +148,11 @@ igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) * number of elements in the resized matrix. */ -igraph_error_t FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { - igraph_integer_t size; - IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); - IGRAPH_SAFE_MULT(nrow, ncol, &size); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, size)); +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 IGRAPH_SUCCESS; + return 0; } /** @@ -253,18 +160,36 @@ igraph_error_t FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, igraph_in * \function igraph_matrix_resize_min * \brief Deallocates unused memory for a matrix. * - * This function attempts to deallocate the unused reserved storage - * of 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, O(n) at worst. + * Time complexity: operating system dependent. */ -void FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { - FUNCTION(igraph_vector, resize_min)(&m->data); +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; } @@ -279,7 +204,7 @@ void FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { +long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { return (m->nrow) * (m->ncol); } @@ -294,7 +219,7 @@ igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { +long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { return m->nrow; } @@ -309,7 +234,7 @@ igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) { +long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) { return m->ncol; } @@ -365,11 +290,9 @@ void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m) { * resized matrix. */ -igraph_error_t FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, igraph_integer_t n) { - igraph_integer_t new_ncol; - IGRAPH_SAFE_ADD(m->ncol, n, &new_ncol); - IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow, new_ncol)); - return IGRAPH_SUCCESS; +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; } /** @@ -385,17 +308,15 @@ igraph_error_t FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, igraph_ * resized matrix. */ -igraph_error_t FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, igraph_integer_t n) { - igraph_integer_t new_nrow, new_size; - IGRAPH_SAFE_ADD(m->nrow, n, &new_nrow); - IGRAPH_SAFE_MULT(m->ncol, new_nrow, &new_size); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, new_size)); - for (igraph_integer_t i = m->ncol - 1; i >= 0; i--) { - FUNCTION(igraph_vector, move_interval)(&m->data, (m->nrow)*i, (m->nrow) * (i + 1), - new_nrow * i); +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 = new_nrow; - return IGRAPH_SUCCESS; + m->nrow += n; + return 0; } /** @@ -411,10 +332,10 @@ igraph_error_t FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, igraph_ * resized matrix. */ -igraph_error_t FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, igraph_integer_t col) { +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 IGRAPH_SUCCESS; + return 0; } /** @@ -426,9 +347,8 @@ igraph_error_t FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, igrap * matrix. */ -igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( - TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove) { - igraph_integer_t i, j; +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++) { @@ -442,91 +362,37 @@ igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( (i + 1) * (m->nrow - nremove), (i + 1) * (m->nrow - nremove) + nremove); IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); - return IGRAPH_SUCCESS; + return 0; } /** - * Copies matrix data stored contiguously while transposing. Applications include implementing - * matrix transposition as well as changing between row-major and column-major storage formats. + * \ingroup matrix + * \function igraph_matrix_delete_rows_neg + * \brief Removes columns from a matrix (for internal use). * - * \param dst Data will be copied into this vector. It must have length m-by-n. It will not be resized. - * \param src Vector containing the data to be copied. It is assumed to have length m-by-n. - * Must not be the same as \p dst . - * \param m The size of contiguous blocks. This is the number of columns when using column-major - * storage format, or the number of rows when using row-major format. - * \param n The number of blocks. The is the number of rows when using column-major format, - * or the number of column when using row-major format. + * Time complexity: linear with the number of elements of the original + * matrix. */ -static void FUNCTION(igraph_i, transpose_copy)( - TYPE(igraph_vector) *dst, const TYPE(igraph_vector) *src, - size_t m, size_t n) { - - IGRAPH_ASSERT(dst != src); - /* Block size of 4 was found to yield the best performance when benchmarking with: - * - Intel Core i7-7920HQ on macOS. - * - AMD Ryzen Threadripper 3990X on Linux. - */ - const size_t blocksize = 4; - for (size_t i=0; i < m; i += blocksize) { - for (size_t j=0; j < n; j++) { - for (size_t k=0; k < blocksize && i+k < m; k++) { - VECTOR(*dst)[j + (i+k)*n] = VECTOR(*src)[i+k + j*m]; +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; } -} - -/** - * \ingroup matrix - * \function igraph_matrix_init_array - * \brief Initializes a matrix from an ordinary C array (constructor). - * - * The array is assumed to store the matrix data contiguously, either in - * a column-major or row-major format. In other words, \p data may - * store concatenated matrix columns or concatenated matrix rows. - * Constructing a matrix from column-major data is faster, as this is - * igraph's native storage format. - * - * \param v Pointer to an uninitialized matrix object. - * \param data A regular C array, storing the elements of the matrix in - * column-major order, i.e. the elements of the first column are stored - * first, followed by the second column and so on. - * \param nrow The number of rows in the matrix. - * \param ncol The number of columns in the matrix. - * \param storage \c IGRAPH_ROW_MAJOR if the array is in row-major format, - \c IGRAPH_COLUMN_MAJOR if the array is in column-major format. - * \return Error code: - * \c IGRAPH_ENOMEM if there is not enough memory. - * - * Time complexity: operating system specific, usually - * O(\p nrow \p ncol). - */ - -igraph_error_t FUNCTION(igraph_matrix, init_array)( - TYPE(igraph_matrix) *m, const BASE *data, - igraph_integer_t nrow, igraph_integer_t ncol, - igraph_matrix_storage_t storage) { - igraph_integer_t length; - TYPE(igraph_vector) v; - - IGRAPH_SAFE_MULT(nrow, ncol, &length); - IGRAPH_CHECK(FUNCTION(igraph_matrix, init)(m, nrow, ncol)); - FUNCTION(igraph_vector, view)(&v, data, length); - if (storage == IGRAPH_COLUMN_MAJOR) { - IGRAPH_CHECK(FUNCTION(igraph_vector, update)(&m->data, &v)); - } else if (storage == IGRAPH_ROW_MAJOR) { - FUNCTION(igraph_i, transpose_copy)(&m->data, &v, ncol, nrow); - } else { - IGRAPH_ERROR("Invalid storage type argument", IGRAPH_EINVAL); - } + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup matrix - * \function igraph_matrix_init_copy + * \function igraph_matrix_copy * \brief Copies a matrix. * * @@ -540,25 +406,13 @@ igraph_error_t FUNCTION(igraph_matrix, init_array)( * of elements in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, init_copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { - IGRAPH_CHECK(FUNCTION(igraph_vector, init_copy)(&to->data, &from->data)); +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; } -/** - * \ingroup matrix - * \function igraph_matrix_copy - * \brief Copies a matrix (deprecated alias). - * - * \deprecated-by igraph_matrix_init_copy 0.10 - */ - -igraph_error_t FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { - return FUNCTION(igraph_matrix, init_copy)(to, from); -} - #ifndef NOTORDERED /** @@ -615,20 +469,20 @@ void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by) { * columns of the result matrix. */ -igraph_error_t FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, - const igraph_vector_int_t *rows) { - igraph_integer_t norows = igraph_vector_int_size(rows); - igraph_integer_t i, j, ncols = FUNCTION(igraph_matrix, ncol)(m); + 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, VECTOR(*rows)[i], j); + MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i], j); } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -651,22 +505,23 @@ igraph_error_t FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m * columns of the result matrix. */ -igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, - const igraph_vector_int_t *rows, - const igraph_vector_int_t *cols) { - igraph_integer_t nrows = igraph_vector_int_size(rows); - igraph_integer_t ncols = igraph_vector_int_size(cols); - igraph_integer_t i, j; + 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, VECTOR(*rows)[i], VECTOR(*cols)[j]); + MATRIX(*res, i, j) = MATRIX(*m, (long int)VECTOR(*rows)[i], + (long int)VECTOR(*cols)[j]); } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -683,17 +538,17 @@ igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matri * Time complexity: O(n), the number of rows in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, - igraph_integer_t index) { - igraph_integer_t nrow = FUNCTION(igraph_matrix, nrow)(m); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -715,9 +570,6 @@ BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m) { * \function igraph_matrix_all_e * \brief Are all elements equal? * - * Checks element-wise equality of two matrices. For matrices containing floating - * point values, consider using \ref igraph_matrix_all_almost_e(). - * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all @@ -842,10 +694,10 @@ FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs, igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2) { - igraph_integer_t col1 = FUNCTION(igraph_matrix, ncol)(m1); - igraph_integer_t col2 = FUNCTION(igraph_matrix, ncol)(m2); - igraph_integer_t row1 = FUNCTION(igraph_matrix, nrow)(m1); - igraph_integer_t row2 = FUNCTION(igraph_matrix, nrow)(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"); } @@ -854,67 +706,42 @@ igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) * #endif -#define SWAP(TYPE,a,b) do { TYPE igraph_i_tmp = (a); (a) = (b); (b) = igraph_i_tmp; } while (0) - /** * \function igraph_matrix_transpose - * \brief Transpose of a matrix. - * - * Calculates the transpose of a matrix. When the matrix is non-square, - * this function reallocates the storage used for the matrix. + * \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. */ -igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { - if (m->nrow > 1 && m->ncol > 1) { - if (m->nrow == m->ncol) { - /* In-place transpose for square matrices. */ - - /* Block size of 4 was found to yield the best performance during benchmarking, - * see igraph_i_transpose_copy() */ - const size_t blocksize = 4; - const size_t n = m->nrow; - size_t k=0; - for (k=0; k + blocksize - 1 < n; k += blocksize) { - for (size_t i = k; i < k + blocksize; ++i) { - for (size_t j = i + 1; j < k + blocksize; ++j) { - SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); - } - } - for (size_t i = k + blocksize; i < n; ++i) { - for (size_t j = k; j < k + blocksize; ++j) { - SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); - } - } - } - for (size_t i = k; i < n; ++i) { - for (size_t j = i + 1; j < n; ++j) { - SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); - } - } - } else { - /* Allocate new storage for non-square matrices. */ - TYPE(igraph_vector) newdata; - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&newdata, m->nrow * m->ncol)); - FUNCTION(igraph_i, transpose_copy)(&newdata, &m->data, m->nrow, m->ncol); - FUNCTION(igraph_vector, destroy)(&m->data); - m->data = newdata; +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; - SWAP(igraph_integer_t, m->nrow, m->ncol); - - return IGRAPH_SUCCESS; + return 0; } -#undef SWAP - /** - * \function igraph_matrix_get + * \function igraph_matrix_e * Extract an element from a matrix. * * Use this if you need a function for some reason and cannot use the @@ -927,13 +754,13 @@ igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_matrix, get)(const TYPE(igraph_matrix) *m, - igraph_integer_t row, igraph_integer_t col) { +BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + long int row, long int col) { return MATRIX(*m, row, col); } /** - * \function igraph_matrix_get_ptr + * \function igraph_matrix_e_ptr * Pointer to an element of a matrix. * * The function returns a pointer to an element. No range checking is @@ -946,33 +773,9 @@ BASE FUNCTION(igraph_matrix, get)(const TYPE(igraph_matrix) *m, * Time complexity: O(1). */ -BASE* FUNCTION(igraph_matrix, get_ptr)(const TYPE(igraph_matrix) *m, - igraph_integer_t row, igraph_integer_t col) { - return &MATRIX(*m, row, col); -} - -/** - * \function igraph_matrix_e - * Extract an element from a matrix (deprecated alias). - * - * \deprecated-by igraph_matrix_get 0.10.0 - */ - -BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, - igraph_integer_t row, igraph_integer_t col) { - return FUNCTION(igraph_matrix, get)(m, row, col); -} - -/** - * \function igraph_matrix_e_ptr - * Pointer to an element of a matrix. - * - * \deprecated-by igraph_matrix_get_ptr 0.10.0 - */ - BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, - igraph_integer_t row, igraph_integer_t col) { - return FUNCTION(igraph_matrix, get_ptr)(m, row, col); + long int row, long int col) { + return &MATRIX(*m, row, col); } /** @@ -988,9 +791,8 @@ BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, * Time complexity: O(1). */ -void FUNCTION(igraph_matrix, set)( - TYPE(igraph_matrix)* m, igraph_integer_t row, igraph_integer_t col, - BASE value) { +void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, + BASE value) { MATRIX(*m, row, col) = value; } @@ -1022,12 +824,12 @@ void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) { * Time complexity: O(mn), the number of elements. */ -igraph_error_t FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -1045,19 +847,17 @@ igraph_error_t FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, * matrix. */ -igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, +int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { - igraph_integer_t tocols = to->ncol, fromcols = from->ncol; - igraph_integer_t torows = to->nrow, fromrows = from->nrow; - igraph_integer_t offset, c, r, index, offset2; + 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_integer_t new_size; /* new_size = tocols * (fromrows + torows) */ - IGRAPH_SAFE_ADD(fromrows, torows, &new_size); - IGRAPH_SAFE_MULT(tocols, new_size, &new_size); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data, new_size)); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data, + tocols * (fromrows + torows))); to->nrow += fromrows; offset = (tocols - 1) * fromrows; @@ -1072,11 +872,11 @@ igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, offset = torows; offset2 = 0; for (c = 0; c < tocols; c++) { memcpy(VECTOR(to->data) + offset, VECTOR(from->data) + offset2, - sizeof(BASE) * fromrows); + sizeof(BASE) * (size_t) fromrows); offset += fromrows + torows; offset2 += fromrows; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1092,27 +892,22 @@ igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, * Time complexity: O(mn), the number of elements on the new matrix. */ -igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, +int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { - igraph_integer_t tocols = to->ncol, fromcols = from->ncol; - igraph_integer_t torows = to->nrow, fromrows = from->nrow; + 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_integer_t new_tocols; - IGRAPH_SAFE_ADD(tocols, fromcols, &new_tocols); - IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, new_tocols)); - + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, tocols + fromcols)); FUNCTION(igraph_vector, copy_to)(&from->data, VECTOR(to->data) + tocols * torows); - - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_matrix_swap - * \brief Swap two matrices. + * Swap two matrices. * * The contents of the two matrices will be swapped. * \param m1 The first matrix. @@ -1122,20 +917,8 @@ igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, * Time complexity: O(1). */ -igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { - igraph_integer_t tmp; - - tmp = m1->nrow; - m1->nrow = m2->nrow; - m2->nrow = tmp; - - tmp = m1->ncol; - m1->ncol = m2->ncol; - m2->ncol = tmp; - - IGRAPH_CHECK(FUNCTION(igraph_vector, swap)(&m1->data, &m2->data)); - - return IGRAPH_SUCCESS; +int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { + return FUNCTION(igraph_vector, swap)(&m1->data, &m2->data); } /** @@ -1152,10 +935,10 @@ igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igrap * Time complexity: O(n), the number of columns in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res, igraph_integer_t index) { - igraph_integer_t rows = m->nrow, cols = m->ncol; - igraph_integer_t i, j; +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); @@ -1165,7 +948,7 @@ igraph_error_t FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, for (i = index, j = 0; j < cols; i += rows, j++) { VECTOR(*res)[j] = VECTOR(m->data)[i]; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1184,10 +967,10 @@ igraph_error_t FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, * Time complexity: O(n), the number of columns in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, igraph_integer_t index) { - igraph_integer_t rows = m->nrow, cols = m->ncol; - igraph_integer_t i, j; +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); @@ -1198,7 +981,7 @@ igraph_error_t FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, for (i = index, j = 0; j < cols; i += rows, j++) { VECTOR(m->data)[i] = VECTOR(*v)[j]; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1217,10 +1000,10 @@ igraph_error_t FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, * Time complexity: O(m), the number of rows in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, igraph_integer_t index) { - igraph_integer_t rows = m->nrow, cols = m->ncol; - igraph_integer_t i, j; +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); @@ -1231,7 +1014,7 @@ igraph_error_t FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, for (i = index * rows, j = 0; j < rows; i++, j++) { VECTOR(m->data)[i] = VECTOR(*v)[j]; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1247,16 +1030,16 @@ igraph_error_t FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, * Time complexity: O(n), the number of columns. */ -igraph_error_t FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, - igraph_integer_t i, igraph_integer_t j) { - igraph_integer_t ncol = m->ncol, nrow = m->nrow; - igraph_integer_t n = nrow * ncol; - igraph_integer_t index1, index2; +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 IGRAPH_SUCCESS; + return 0; } for (index1 = i, index2 = j; index1 < n; index1 += nrow, index2 += nrow) { BASE tmp; @@ -1264,7 +1047,7 @@ igraph_error_t FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, VECTOR(m->data)[index1] = VECTOR(m->data)[index2]; VECTOR(m->data)[index2] = tmp; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1280,22 +1063,22 @@ igraph_error_t FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, * Time complexity: O(m), the number of rows. */ -igraph_error_t FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, - igraph_integer_t i, igraph_integer_t j) { - igraph_integer_t ncol = m->ncol, nrow = m->nrow; - igraph_integer_t k, index1, index2; +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 IGRAPH_SUCCESS; + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -1325,7 +1108,7 @@ void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus) { * Time complexity: O(mn), the number of elements. */ -igraph_error_t FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, +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); @@ -1346,7 +1129,7 @@ igraph_error_t FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -igraph_error_t FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, +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); @@ -1367,7 +1150,7 @@ igraph_error_t FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -igraph_error_t FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, +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); @@ -1388,7 +1171,7 @@ igraph_error_t FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -igraph_error_t FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, +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); @@ -1417,48 +1200,52 @@ igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m) { * \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 an igraph_integer_t. The row index of the + * \param i Pointer to a long int. The row index of the * minimum is stored here. - * \param j Pointer to an igraph_integer_t. The column index of + * \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. */ -void FUNCTION(igraph_matrix, which_min)( - const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { - igraph_integer_t vmin = FUNCTION(igraph_vector, which_min)(&m->data); +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 an igraph_integer_t. The row index of the + * \param i Pointer to a long int. The row index of the * maximum is stored here. - * \param j Pointer to an igraph_integer_t. The column index of + * \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. */ -void FUNCTION(igraph_matrix, which_max)( - const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { - igraph_integer_t vmax = FUNCTION(igraph_vector, which_max)(&m->data); +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; } /** @@ -1468,52 +1255,54 @@ void FUNCTION(igraph_matrix, which_max)( * 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. */ -void FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, BASE *min, BASE *max) { - FUNCTION(igraph_vector, minmax)(&m->data, min, max); + return FUNCTION(igraph_vector, minmax)(&m->data, min, max); } /** * \function igraph_matrix_which_minmax - * \brief Indices of the minimum and maximum elements. + * \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 an igraph_integer_t, the row index of + * \param imin Pointer to a long int, the row index of * the minimum is stored here. - * \param jmin Pointer to an igraph_integer_t, the column index of + * \param jmin Pointer to a long int, the column index of * the minimum is stored here. - * \param imax Pointer to an igraph_integer_t, the row index of + * \param imax Pointer to a long int, the row index of * the maximum is stored here. - * \param jmax Pointer to an igraph_integer_t, the column index of + * \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. */ -void FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, - igraph_integer_t *imin, igraph_integer_t *jmin, - igraph_integer_t *imax, igraph_integer_t *jmax) { - igraph_integer_t vmin, vmax; +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 @@ -1524,7 +1313,7 @@ void FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, * * Checks whether all elements are zero. * \param m The input matrix. - * \return Boolean, \c true is \p m contains only zeros and \c false + * \return Boolean, \c TRUE is \p m contains only zeros and \c FALSE * otherwise. * * Time complexity: O(mn), the number of elements. @@ -1541,8 +1330,8 @@ igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m) { * 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. + * \return Boolean, \c TRUE if the matrix contains zero elements, and + * \c FALSE otherwise. * * Time complexity: O(1). */ @@ -1557,8 +1346,8 @@ igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) { * * 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. + * \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. @@ -1566,10 +1355,10 @@ igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) { igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m) { - igraph_integer_t n = m->nrow; - igraph_integer_t r, c; + long int n = m->nrow; + long int r, c; if (m->ncol != n) { - return false; + return 0; } for (r = 1; r < n; r++) { for (c = 0; c < r; c++) { @@ -1577,16 +1366,16 @@ igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m BASE a2 = MATRIX(*m, c, r); #ifdef EQ if (!EQ(a1, a2)) { - return false; + return 0; } #else if (a1 != a2) { - return false; + return 0; } #endif } } - return true; + return 1; } /** @@ -1618,13 +1407,14 @@ BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m) { * Time complexity: O(mn), the number of elements in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res) { - igraph_integer_t nrow = m->nrow, ncol = m->ncol; - igraph_integer_t r, c; + 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++) { - BASE sum = ZERO; + sum = ZERO; for (c = 0; c < ncol; c++) { #ifdef SUM SUM(sum, sum, MATRIX(*m, r, c)); @@ -1634,7 +1424,7 @@ igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, } VECTOR(*res)[r] = sum; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1650,13 +1440,14 @@ igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, * Time complexity: O(mn), the number of elements in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res) { - igraph_integer_t nrow = m->nrow, ncol = m->ncol; - igraph_integer_t r, c; + 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++) { - BASE sum = ZERO; + sum = ZERO; for (r = 0; r < nrow; r++) { #ifdef SUM SUM(sum, sum, MATRIX(*m, r, c)); @@ -1666,7 +1457,7 @@ igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, } VECTOR(*res)[c] = sum; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1676,7 +1467,7 @@ igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, * 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 + * \return Boolean, \c TRUE if the matrix contains \p e, \c FALSE * otherwise. * * Time complexity: O(mn), the number of elements. @@ -1697,21 +1488,22 @@ igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, * \param from The position to search from, the positions are * enumerated columnwise. * \param what The element to search for. - * \param pos Pointer to an igraph_integer_t. If the element is + * \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 an igraph_integer_t. If the element is + * \param row Pointer to a long int. If the element is * found, then this is set to its row index. - * \param col Pointer to an igraph_integer_t. If the element is + * \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 + * \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, - igraph_integer_t from, BASE what, igraph_integer_t *pos, - igraph_integer_t *row, igraph_integer_t *col) { + 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; @@ -1732,9 +1524,9 @@ igraph_bool_t FUNCTION(igraph_matrix, search)(const TYPE(igraph_matrix) *m, * Time complexity: O(mn), the number of elements in the matrix. */ -igraph_error_t FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, igraph_integer_t row) { +int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) { - igraph_integer_t c, r, index = row + 1, leap = 1, n = m->nrow * m->ncol; + 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); } @@ -1749,7 +1541,7 @@ igraph_error_t FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, igrap } m->nrow--; IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, m->nrow * m->ncol)); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1770,109 +1562,80 @@ igraph_error_t FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, igrap * columns of the result matrix. */ -igraph_error_t FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, +int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, - const igraph_vector_int_t *cols) { - igraph_integer_t ncols = igraph_vector_int_size(cols); - igraph_integer_t nrows = m->nrow; - igraph_integer_t i, j; + 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, VECTOR(*cols)[j]); + MATRIX(*res, i, j) = MATRIX(*m, i, (long int)VECTOR(*cols)[j]); } } - return IGRAPH_SUCCESS; + return 0; } #ifdef OUT_FORMAT + #ifndef USING_R -igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, - const char *format) { - igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); - igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); - igraph_integer_t i, j; +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(format, MATRIX(*m, i, j)); + printf(OUT_FORMAT, MATRIX(*m, i, j)); } printf("\n"); } - return IGRAPH_SUCCESS; + return 0; } -#endif /* USING_R */ -#endif /* OUT_FORMAT */ - -#if defined(OUT_FORMAT) || defined(FPRINTFUNC) -#ifndef USING_R +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"); + } -igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) { - return FUNCTION(igraph_matrix, fprint)(m, stdout); + return 0; } -#endif /* USING_R */ - -igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file) { - igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); - igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); - igraph_integer_t i, j; - igraph_vector_int_t column_width; - -#ifdef OUT_FORMAT - /* Insert dynamic width specifier '*' in format string. */ - char format[ sizeof(OUT_FORMAT) + 1 ] = "%*"; - strcpy(format + 2, (const char *) OUT_FORMAT + 1); #endif - IGRAPH_VECTOR_INT_INIT_FINALLY(&column_width, nc); - - /* First we detect the width needed for each matrix column. - * snprintf() may be passed a NULL pointer with a buffer size of 0. - * It will then return the number of characters that would have been written, - * without writing anything. */ - for (j = 0; j < nc; j++) { - for (i = 0; i < nr; i++) { - const int min_width = 1; /* minimum field width */ - int width; -#ifdef SNPRINTFUNC - width = SNPRINTFUNC(NULL, 0, MATRIX(*m, i, j)); -#else - width = snprintf(NULL, 0, OUT_FORMAT, MATRIX(*m, i, j)); -#endif - if (width < min_width) { - width = min_width; - } - if (width > VECTOR(column_width)[j]) { - VECTOR(column_width)[j] = width; - } - } - } +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); } -#ifdef FPRINTFUNC_ALIGNED - FPRINTFUNC_ALIGNED(file, VECTOR(column_width)[j], MATRIX(*m, i, j)); -#else - fprintf(file, format, VECTOR(column_width)[j], MATRIX(*m, i, j)); -#endif + fprintf(file, OUT_FORMAT, MATRIX(*m, i, j)); } fprintf(file, "\n"); } - igraph_vector_int_destroy(&column_width); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } -#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ +#endif diff --git a/src/vendor/cigraph/src/core/matrix_list.c b/src/vendor/cigraph/src/core/matrix_list.c deleted file mode 100644 index f00e5d7d1c5..00000000000 --- a/src/vendor/cigraph/src/core/matrix_list.c +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- 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 - -*/ - -#include "igraph_error.h" -#include "igraph_types.h" -#include "igraph_matrix_list.h" - -#define MATRIX_LIST - -#define BASE_IGRAPH_REAL -#define CUSTOM_INIT_DESTROY -#include "igraph_pmt.h" -#include "typed_list.pmt" -#include "igraph_pmt_off.h" -#undef CUSTOM_INIT_DESTROY -#undef BASE_IGRAPH_REAL - -static igraph_error_t igraph_i_matrix_list_init_item( - const igraph_matrix_list_t* list, igraph_matrix_t* item -) { - IGRAPH_UNUSED(list); - return igraph_matrix_init(item, 0, 0); -} - -static igraph_error_t igraph_i_matrix_list_copy_item( - igraph_matrix_t* dest, const igraph_matrix_t* source -) { - return igraph_matrix_init_copy(dest, source); -} - -static void igraph_i_matrix_list_destroy_item(igraph_matrix_t* item) { - igraph_matrix_destroy(item); -} - -#undef MATRIX_LIST diff --git a/src/vendor/cigraph/src/core/memory.c b/src/vendor/cigraph/src/core/memory.c index 490db99a7a0..a59afe712a3 100644 --- a/src/vendor/cigraph/src/core/memory.c +++ b/src/vendor/cigraph/src/core/memory.c @@ -24,64 +24,30 @@ #include "igraph_memory.h" /** - * \section about-alloc-funcs About allocation functions + * \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. \c igraph_vector_ptr_t - * has functions to deallocate the contained pointers on its own, but in this - * case it has to be ensured that these pointers are allocated by a function - * that corresponding to the deallocator function that igraph uses. - * - * - * To this end, igraph exports the memory allocation functions that are used - * internally so the user of the library can ensure that the proper functions - * are used when pointers are moved between the code written by the user and - * the code of the igraph library. + * manually when the user does not need them any more. This can be done + * by calling igraph_free on them. * * - * Additionally, the memory allocator functions used by igraph work around the - * quirk of classical \c malloc(), \c realloc() and \c calloc() implementations - * where the behaviour of allocating zero bytes is undefined. igraph allocator - * functions will always allocate at least one byte. - */ - -/** - * \function igraph_free - * \brief Deallocate memory that was allocated by igraph functions. + * Here is a complete example on how to use \c igraph_free properly. * - * This function exposes the \c free() function used internally by igraph. + * \example examples/simple/igraph_free.c * - * \param ptr Pointer to the piece of memory to be deallocated. + * \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_calloc(), \ref igraph_malloc(), \ref igraph_realloc() - */ - -void igraph_free(void *ptr) { - IGRAPH_FREE(ptr); -} - - -/** - * \function igraph_calloc - * \brief Allocate memory that can be safely deallocated by igraph functions. - * - * This function behaves like \c calloc(), but it ensures that at least one - * byte is allocated even when the caller asks for zero bytes. - * - * \param count Number of items to be allocated. - * \param size Size of a single item to be allocated. - * \return Pointer to the piece of allocated memory; \c NULL if the allocation - * failed. - * - * \sa \ref igraph_malloc(), \ref igraph_realloc(), \ref igraph_free() + * \sa \ref igraph_malloc() */ -void *igraph_calloc(size_t count, size_t size) { - return (void *) IGRAPH_CALLOC(count * size, char); +void igraph_free(void *p) { + IGRAPH_FREE(p); } @@ -89,36 +55,19 @@ void *igraph_calloc(size_t count, size_t size) { * \function igraph_malloc * \brief Allocate memory that can be safely deallocated by igraph functions. * - * This function behaves like \c malloc(), but it ensures that at least one - * byte is allocated even when the caller asks for zero bytes. - * - * \param size Number of bytes to be allocated. Zero is treated as one byte. - * \return Pointer to the piece of allocated memory; \c NULL if the allocation - * failed. - * - * \sa \ref igraph_calloc(), \ref igraph_realloc(), \ref igraph_free() - */ - -void *igraph_malloc(size_t size) { - return IGRAPH_MALLOC(size); -} - - -/** - * \function igraph_realloc - * \brief Reallocate memory that can be safely deallocated by igraph functions. - * - * This function behaves like \c realloc(), but it ensures that at least one - * byte is allocated even when the caller asks for zero bytes. + * 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 ptr The pointer to reallocate. - * \param size Number of bytes to be allocated. - * \return Pointer to the piece of allocated memory; \c NULL if the allocation - * failed. + * \param n Number of bytes to be allocated. + * \return Pointer to the piece of allocated memory. * - * \sa \ref igraph_free(), \ref igraph_malloc() + * \sa \ref igraph_free() */ -void *igraph_realloc(void* ptr, size_t size) { - return (void*) IGRAPH_REALLOC(ptr, size, char); +void *igraph_malloc(size_t n) { + return malloc(n); } diff --git a/src/vendor/cigraph/src/core/printing.c b/src/vendor/cigraph/src/core/printing.c index 106c2a795bd..ac29f23c175 100644 --- a/src/vendor/cigraph/src/core/printing.c +++ b/src/vendor/cigraph/src/core/printing.c @@ -21,213 +21,126 @@ */ -#include "igraph_complex.h" -#include "igraph_error.h" #include "igraph_types.h" #include -/* The number of digits chosen here will be used in all places where - * igraph_real_fprintf_precise() is used, including all textual graph - * formats such as GML, GraphML, Pajek, etc. DBL_DIG digits are sufficient - * to preserve the decimal representation during a - * decimal (textual) -> binary -> decimal (textual) round-trip conversion. - * This many digits are however not sufficient for a lossless - * binary -> decimal -> binary conversion. Thus, writing numerical attributes - * to a file and reading them back in may cause a tiny change in the last - * binary digit of numbers. This change is minute, always smaller than 10^-15 - * times the original number, thus acceptable. - * - * We could output more digits, but that would come with its own problem: - * It would sometimes cause a change in the decimal representation originally - * input by users, which is surprising and confusing. For example, - * - * printf("%.17g\n", 100.1) - * - * outputs 100.09999999999999 instead of 100.1. We can prevent this by - * using DBL_DIG == 15 digits instead of 17, which would be required - * for a lossless binary -> decimal -> binary round-tripping. - * - * This justifies using DBL_DIG digits, and not more, in all places. - */ +#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 15 digits for %g, which is what IEEE-754 doubles require. */ + /* Assume a precision of 15 digits for %g */ #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.15g" #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 (isfinite(val)) { + if (igraph_finite(val)) { return fprintf(file, "%g", val); - } else if (isnan(val)) { + } else if (igraph_is_nan(val)) { return fprintf(file, "NaN"); - } else if (isinf(val)) { + } 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); } - IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ -} - -#ifndef USING_R -int igraph_real_printf(igraph_real_t val) { - return igraph_real_fprintf(stdout, val); } -#endif -int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val) { - if (isfinite(val)) { - return fprintf(file, "%*g", width, val); - } else if (isnan(val)) { - return fprintf(file, "%*s", width, "NaN"); - } else if (isinf(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 fprintf(file, "%*s", width, "-Inf"); + return snprintf(str, size, "-Inf"); } else { - return fprintf(file, "%*s", width, "Inf"); + return snprintf(str, size, "Inf"); } + } else { + /* fallback */ + return snprintf(str, size, "%g", val); } - IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ } #ifndef USING_R -int igraph_real_printf_aligned(int width, igraph_real_t val) { - return igraph_real_fprintf_aligned(stdout, width, val); -} -#endif - -int igraph_real_snprintf(char *str, size_t size, igraph_real_t val) { - if (isfinite(val)) { - return snprintf(str, size, "%g", val); - } else if (isnan(val)) { - return snprintf(str, size, "NaN"); - } else if (isinf(val)) { +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 snprintf(str, size, "-Inf"); + return printf("-Inf"); } else { - return snprintf(str, size, "Inf"); + return printf("Inf"); } + } else { + /* fallback */ + return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } - IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ } +#endif int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { - if (isfinite(val)) { + if (igraph_finite(val)) { return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); - } else if (isnan(val)) { + } else if (igraph_is_nan(val)) { return fprintf(file, "NaN"); - } else if (isinf(val)) { + } 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); } - IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ -} - -#ifndef USING_R -int igraph_real_printf_precise(igraph_real_t val) { - return igraph_real_fprintf_precise(stdout, val); } -#endif -int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val) { - if (isfinite(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 (isnan(val)) { + } else if (igraph_is_nan(val)) { return snprintf(str, size, "NaN"); - } else if (isinf(val)) { + } 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); } - IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ -} - -#define PROPAGATE() \ - do { \ - if (res < 0) { \ - return -1; \ - } \ - cnt += res; \ - } while (0) - -int igraph_complex_fprintf(FILE *file, igraph_complex_t val) { - int res, cnt = 0; - igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); - res = igraph_real_fprintf(file, re); - PROPAGATE(); - if (! signbit(im)) { - res = fprintf(file, "+"); - PROPAGATE(); - } - res = igraph_real_fprintf(file, im); - PROPAGATE(); - res = fprintf(file, "i"); - PROPAGATE(); - return cnt; -} - -#undef PROPAGATE - -#ifndef USING_R -int igraph_complex_printf(igraph_complex_t val) { - return igraph_complex_fprintf(stdout, val); -} -#endif - -#define PROPAGATE() \ - do { \ - if (res < 0) { \ - return -1; \ - } \ - cnt += res; \ - /* remember that 'size' is unsigned, can't check if size - res < 0! */ \ - if (size > res) size -= res; \ - else size = 0; \ - if (size == 0) str = NULL; else str += res; \ - } while (0) - -int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val) { - int res, cnt = 0; - igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); - res = igraph_real_snprintf(str, size, re); - PROPAGATE(); - if (! signbit(im)) { - res = snprintf(str, size, "+"); - PROPAGATE(); - } - res = igraph_real_snprintf(str, size, im); - PROPAGATE(); - res = snprintf(str, size, "i"); - PROPAGATE(); - return cnt; -} - -int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val) { - /* Most characters produces by %g is 13, so including 'i' and null terminator we - * need up to 13 + 13 + 1 + 1 = 28 characters in total. */ - char buf[28]; - - if (igraph_complex_snprintf(buf, sizeof(buf) / sizeof(buf[0]), val) < 0) { - return -1; - } - return fprintf(file, "%*s", width, buf); } - -#ifndef USING_R -int igraph_complex_printf_aligned(int width, igraph_complex_t val) { - return igraph_complex_fprintf_aligned(stdout, width, val); -} -#endif - -#undef PROPAGATE diff --git a/src/vendor/cigraph/src/core/progress.c b/src/vendor/cigraph/src/core/progress.c index 8cf35a8016c..436d403dbb7 100644 --- a/src/vendor/cigraph/src/core/progress.c +++ b/src/vendor/cigraph/src/core/progress.c @@ -52,7 +52,7 @@ static IGRAPH_THREAD_LOCAL char igraph_i_progressmsg_buffer[1000]; * Time complexity: O(1). */ -igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void *data) { +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; @@ -90,13 +90,12 @@ igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void * \return */ -igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void *data, +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); - va_end(ap); return igraph_progress(igraph_i_progressmsg_buffer, percent, data); } @@ -123,7 +122,7 @@ igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void * Time complexity: O(1). */ -igraph_error_t igraph_progress_handler_stderr(const char *message, igraph_real_t percent, +int igraph_progress_handler_stderr(const char *message, igraph_real_t percent, void* data) { IGRAPH_UNUSED(data); fputs(message, stderr); diff --git a/src/vendor/cigraph/src/core/psumtree.c b/src/vendor/cigraph/src/core/psumtree.c index f0bfc73720e..3907efdb5fb 100644 --- a/src/vendor/cigraph/src/core/psumtree.c +++ b/src/vendor/cigraph/src/core/psumtree.c @@ -27,10 +27,12 @@ #include "igraph_psumtree.h" #include "igraph_error.h" -#include "math/safe_intop.h" - #include +static double igraph_i_log2(double f) { + return log(f) / log(2.0); +} + /** * \ingroup psumtree * \section igraph_psumtree @@ -81,27 +83,17 @@ * 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. It must be at least one. - * \return Error code, typically \c IGRAPH_ENOMEM if there is not enough memory. + * \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 */ -igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size) { - igraph_integer_t vecsize; - - IGRAPH_ASSERT(size > 0); - +int igraph_psumtree_init(igraph_psumtree_t *t, long int size) { t->size = size; - - /* offset = 2^ceiling(log2(size)) - 1 */ - IGRAPH_CHECK(igraph_i_safe_next_pow_2(size, &t->offset)); - t->offset -= 1; - - IGRAPH_SAFE_ADD(t->offset, t->size, &vecsize); - IGRAPH_CHECK(igraph_vector_init(&t->v, vecsize)); - - return IGRAPH_SUCCESS; + 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; } /** @@ -112,7 +104,7 @@ igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size) * \param t The tree to reset. */ void igraph_psumtree_reset(igraph_psumtree_t *t) { - igraph_vector_null(&t->v); + igraph_vector_fill(&(t->v), 0); } /** @@ -146,7 +138,7 @@ void igraph_psumtree_destroy(igraph_psumtree_t *t) { * * Time complexity: O(1) */ -igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t idx) { +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]; } @@ -177,11 +169,11 @@ igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t i * * Time complexity: O(log n), where n is the number of items in the tree. */ -igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, +int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, igraph_real_t search) { const igraph_vector_t *tree = &t->v; - igraph_integer_t i = 1; - igraph_integer_t size = igraph_vector_size(tree); + long int i = 1; + long int size = igraph_vector_size(tree); IGRAPH_ASSERT(search >= 0); IGRAPH_ASSERT(search < igraph_psumtree_sum(t)); @@ -216,12 +208,12 @@ igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer * * Time complexity: O(log n), where n is the number of items in the tree. */ -igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, +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 && isfinite(new_value)) { + if (new_value >= 0 && igraph_finite(new_value)) { idx = idx + t->offset + 1; difference = new_value - VECTOR(*tree)[idx - 1]; @@ -249,7 +241,7 @@ igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx * * Time complexity: O(1). */ -igraph_integer_t igraph_psumtree_size(const igraph_psumtree_t *t) { +long int igraph_psumtree_size(const igraph_psumtree_t *t) { return t->size; } diff --git a/src/vendor/cigraph/src/core/set.c b/src/vendor/cigraph/src/core/set.c index a15c9d9151b..7ce264d222d 100644 --- a/src/vendor/cigraph/src/core/set.c +++ b/src/vendor/cigraph/src/core/set.c @@ -21,7 +21,9 @@ */ +#include "igraph_types.h" #include "igraph_memory.h" +#include "igraph_error.h" #include "core/set.h" @@ -34,12 +36,8 @@ * \function igraph_set_init * \brief Initializes a set. * - * Initializes an empty set (with zero elements). Allocates memory for - * the requested capacity. No re-allocation will be necessary until the - * number of elements exceeds this initial capacity. - * - * \param set Pointer to the set to be initialized. - * \param capacity The expected number of elements in the 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. @@ -47,19 +45,18 @@ * Time complexity: operating system dependent, should be around * O(n), n is the expected size of the set. */ -igraph_error_t igraph_set_init(igraph_set_t *set, igraph_integer_t capacity) { - igraph_integer_t alloc_size; +int igraph_set_init(igraph_set_t *set, int long size) { + long int alloc_size; - IGRAPH_ASSERT(capacity >= 0); - alloc_size = capacity > 0 ? capacity : 1; - set->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); - if (! set->stor_begin) { - IGRAPH_ERROR("Cannot initialize set.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -67,14 +64,15 @@ igraph_error_t igraph_set_init(igraph_set_t *set, igraph_integer_t capacity) { * \function igraph_set_destroy * \brief Destroys a set object. * - * \param set Pointer to the set to be destroyed. + * \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 != NULL); - if (set->stor_begin != NULL) { - IGRAPH_FREE(set->stor_begin); /* sets to NULL */ + IGRAPH_ASSERT(set != 0); + if (set->stor_begin != 0) { + IGRAPH_FREE(set->stor_begin); + set->stor_begin = NULL; } } @@ -94,37 +92,38 @@ void igraph_set_destroy(igraph_set_t* set) { * Time complexity: O(1) */ igraph_bool_t igraph_set_inited(igraph_set_t* set) { - return (set->stor_begin != NULL); + return (set->stor_begin != 0); } /** * \ingroup set * \function igraph_set_reserve - * \brief Reserves memory for a set. + * \brief Reserve memory for a set. * * \param set The set object. - * \param capacity the new \em allocated capacity of the set. + * \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. */ -igraph_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t capacity) { - igraph_integer_t actual_size = igraph_set_size(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 (capacity <= actual_size) { - return IGRAPH_SUCCESS; + if (size <= actual_size) { + return 0; } - tmp = IGRAPH_REALLOC(set->stor_begin, capacity, igraph_integer_t); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for set."); - + 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 + capacity; + set->stor_end = set->stor_begin + size; set->end = set->stor_begin + actual_size; - return IGRAPH_SUCCESS; + return 0; } /** @@ -147,14 +146,13 @@ igraph_bool_t igraph_set_empty(const igraph_set_t* set) { /** * \ingroup set * \function igraph_set_clear - * \brief Removes all elements from the set. + * \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 set The set object. + * \param v The set object. * * Time complexity: O(1). */ @@ -168,17 +166,15 @@ void igraph_set_clear(igraph_set_t* set) { /** * \ingroup set * \function igraph_set_size - * \brief Gives the size of the set. + * \brief Gives the size (=length) of the set. * - * The number of elements in the set. - * - * \param set The set object + * \param v The set object * \return The size of the set. * * Time complexity: O(1). */ -igraph_integer_t igraph_set_size(const igraph_set_t* set) { +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; @@ -197,9 +193,9 @@ igraph_integer_t igraph_set_size(const igraph_set_t* set) { * * Time complexity: O(log(n)), n is the number of elements in \p set. */ -igraph_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { - igraph_integer_t left, right, middle; - igraph_integer_t size; +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); @@ -230,10 +226,7 @@ igraph_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { if (left >= size || set->stor_begin[left] != e) { /* full, allocate more storage */ if (set->stor_end == set->end) { - igraph_integer_t new_size = size < IGRAPH_INTEGER_MAX/2 ? size * 2 : IGRAPH_INTEGER_MAX; - if (size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot add to set, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = size * 2; if (new_size == 0) { new_size = 1; } @@ -243,13 +236,13 @@ igraph_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { /* Element should be inserted at position 'left' */ if (left < size) memmove(set->stor_begin + left + 1, set->stor_begin + left, - (size - left) * sizeof(set->stor_begin[0])); + (size_t) (size - left)*sizeof(set->stor_begin[0])); set->stor_begin[left] = e; set->end += 1; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -263,8 +256,8 @@ igraph_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { * * Time complexity: O(log(n)), n is the number of elements in \p set. */ -igraph_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { - igraph_integer_t left, right, middle; +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); @@ -273,7 +266,7 @@ igraph_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { right = igraph_set_size(set) - 1; if (right == -1) { - return false; /* the set is empty */ + return 0; /* the set is empty */ } /* search for the new element */ @@ -284,7 +277,7 @@ igraph_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { } else if (SET(*set)[middle] < e) { left = middle; } else { - return true; + return 1; } } @@ -294,23 +287,23 @@ igraph_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { /** * \ingroup set * \function igraph_set_iterate - * \brief Iterates through the element of the set. + * \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 an \c igraph_integer_t variable + * This should be a pointer to a \c long variable * which must be zero for the first invocation. - * The object must not be adjusted and its value should + * The object should not be adjusted and its value should * not be used for anything during the iteration. - * \param element The next element or 0 (if 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(const igraph_set_t *set, igraph_integer_t *state, - igraph_integer_t *element) { +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); @@ -319,9 +312,9 @@ igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t *stat if (*state < igraph_set_size(set)) { *element = set->stor_begin[*state]; *state = *state + 1; - return true; + return 1; } else { *element = 0; - return false; + return 0; } } diff --git a/src/vendor/cigraph/src/core/set.h b/src/vendor/cigraph/src/core/set.h index 7b435e3e567..95697c0fe32 100644 --- a/src/vendor/cigraph/src/core/set.h +++ b/src/vendor/cigraph/src/core/set.h @@ -24,7 +24,6 @@ #define IGRAPH_CORE_SET_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -49,16 +48,16 @@ typedef struct s_set { do { IGRAPH_CHECK(igraph_set_init(v, size)); \ IGRAPH_FINALLY(igraph_set_destroy, v); } while (0) -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_init(igraph_set_t* set, igraph_integer_t size); +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 igraph_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t size); +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 igraph_integer_t igraph_set_size(const igraph_set_t* set); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_add(igraph_set_t* v, igraph_integer_t e); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_contains(const igraph_set_t *set, igraph_integer_t e); -IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t* state, +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 diff --git a/src/vendor/cigraph/src/core/sparsemat.c b/src/vendor/cigraph/src/core/sparsemat.c index ddee4f14d5a..7bc0166608d 100644 --- a/src/vendor/cigraph/src/core/sparsemat.c +++ b/src/vendor/cigraph/src/core/sparsemat.c @@ -21,30 +21,18 @@ */ -#include "igraph_sparsemat.h" +#include -#include "igraph_attributes.h" -#include "igraph_constructors.h" +#include "igraph_sparsemat.h" +#include "igraph_error.h" #include "igraph_interface.h" +#include "igraph_constructors.h" #include "igraph_memory.h" -#include "igraph_types.h" #include "igraph_vector_ptr.h" +#include "igraph_attributes.h" -#include "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ - -#include #include -#include -#undef cs /* because otherwise it messes up the name of the 'cs' member in igraph_sparsemat_t */ - -/* Returns the number of potential nonzero elements in the given sparse matrix. - * The returned value can be used to iterate over A->cs->x no matter whether the - * matrix is in triplet or column-compressed form */ -static CS_INT igraph_i_sparsemat_count_elements(const igraph_sparsemat_t* A) { - return A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; -} - /** * \section about_sparsemat About sparse matrices * @@ -110,10 +98,7 @@ static CS_INT igraph_i_sparsemat_count_elements(const igraph_sparsemat_t* A) { * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_integer_t rows, - igraph_integer_t cols, igraph_integer_t nzmax) { - IGRAPH_STATIC_ASSERT(sizeof(igraph_integer_t) == sizeof(CS_INT)); - IGRAPH_STATIC_ASSERT(sizeof(igraph_real_t) == sizeof(CS_ENTRY)); +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); @@ -125,14 +110,14 @@ igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_integer_t row A->cs = cs_spalloc( rows, cols, nzmax, /*values=*/ 1, /*triplet=*/ 1); if (!A->cs) { - IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); } - return IGRAPH_SUCCESS; + return 0; } /** - * \function igraph_sparsemat_init_copy + * \function igraph_sparsemat_copy * \brief Copies a sparse matrix. * * Create a sparse matrix object, by copying another one. The source @@ -150,9 +135,8 @@ igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_integer_t row * number of non-zero elements. */ -igraph_error_t igraph_sparsemat_init_copy( - igraph_sparsemat_t *to, const igraph_sparsemat_t *from -) { +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; @@ -165,24 +149,11 @@ igraph_error_t igraph_sparsemat_init_copy( to->cs->n = from->cs->n; to->cs->nz = from->cs->nz; - memcpy(to->cs->p, from->cs->p, sizeof(CS_INT) * (size_t) ne); - memcpy(to->cs->i, from->cs->i, sizeof(CS_INT) * (size_t) (from->cs->nzmax)); - memcpy(to->cs->x, from->cs->x, sizeof(CS_ENTRY) * (size_t) (from->cs->nzmax)); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_sparsemat_copy - * \brief Copies a sparse matrix (deprecated alias). - * - * \deprecated-by igraph_sparsemat_init_copy 0.10 - */ + 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)); -igraph_error_t igraph_sparsemat_copy( - igraph_sparsemat_t *to, const igraph_sparsemat_t *from -) { - return igraph_sparsemat_init_copy(to, from); + return 0; } /** @@ -216,9 +187,9 @@ void igraph_sparsemat_destroy(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax) { +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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); } return IGRAPH_SUCCESS; } @@ -233,7 +204,7 @@ igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { +long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { return A->cs->m; } @@ -247,7 +218,7 @@ igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { +long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { return A->cs->n; } @@ -265,7 +236,7 @@ igraph_integer_t igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { */ igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) { - return igraph_sparsemat_is_cc(A) ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; + return A->cs->nz < 0 ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; } /** @@ -315,59 +286,57 @@ igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) { * columns plus the number of non-zero elements in the matrix. */ -igraph_error_t igraph_sparsemat_permute(const igraph_sparsemat_t *A, - const igraph_vector_int_t *p, - const igraph_vector_int_t *q, - igraph_sparsemat_t *res) { +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; - CS_INT* pinv; + igraph_vector_int_t pinv; CS_INT i; if (nrow != igraph_vector_int_size(p)) { - IGRAPH_ERROR("Invalid row permutation length.", IGRAPH_FAILURE); + IGRAPH_ERROR("Invalid row permutation length", IGRAPH_FAILURE); } if (ncol != igraph_vector_int_size(q)) { - IGRAPH_ERROR("Invalid column permutation length.", IGRAPH_FAILURE); + IGRAPH_ERROR("Invalid column permutation length", IGRAPH_FAILURE); } /* We invert the permutation by hand */ - pinv = IGRAPH_CALLOC(nrow, CS_INT); - if (pinv == 0) { - IGRAPH_ERROR("Cannot allocate index vector for permutation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, pinv); + IGRAPH_CHECK(igraph_vector_int_init(&pinv, nrow)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &pinv); for (i = 0; i < nrow; i++) { - pinv[ VECTOR(*p)[i] ] = i; + VECTOR(pinv)[ VECTOR(*p)[i] ] = (int) i; } /* And call the permutation routine */ - res->cs = cs_permute(A->cs, pinv, (const CS_INT*) VECTOR(*q), /*values=*/ 1); + res->cs = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1); if (!res->cs) { IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); } - IGRAPH_FREE(pinv); + igraph_vector_int_destroy(&pinv); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, +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; - igraph_integer_t idx_rows = igraph_vector_int_size(p); - igraph_integer_t k; + long int idx_rows = igraph_vector_int_size(p); + long int k; /* Create index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, idx_rows)); + 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_CHECK(igraph_sparsemat_entry(&II2, k, VECTOR(*p)[k], 1.0)); + igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0); } IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); igraph_sparsemat_destroy(&II2); @@ -387,24 +356,25 @@ static igraph_error_t igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, +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; - igraph_integer_t idx_cols = igraph_vector_int_size(q); - igraph_integer_t k; + long int idx_cols = igraph_vector_int_size(q); + long int k; /* Create index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, idx_cols)); + 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_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], k, 1.0)); + igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0); } IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); igraph_sparsemat_destroy(&JJ2); @@ -424,7 +394,7 @@ static igraph_error_t igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -454,7 +424,7 @@ static igraph_error_t igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, +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, @@ -463,9 +433,9 @@ igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, igraph_sparsemat_t II, JJ, II2, JJ2, tmp; CS_INT nrow = A->cs->m; CS_INT ncol = A->cs->n; - igraph_integer_t idx_rows = p ? igraph_vector_int_size(p) : -1; - igraph_integer_t idx_cols = q ? igraph_vector_int_size(q) : -1; - igraph_integer_t k; + 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; @@ -490,10 +460,11 @@ igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, } /* Create first index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, idx_rows)); + 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_CHECK(igraph_sparsemat_entry(&II2, k, VECTOR(*p)[k], 1.0)); + igraph_sparsemat_entry(&II2, (int) k, VECTOR(*p)[k], 1.0); } IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); igraph_sparsemat_destroy(&II2); @@ -501,10 +472,11 @@ igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, IGRAPH_FINALLY(igraph_sparsemat_destroy, &II); /* Create second index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, idx_cols)); + 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_CHECK(igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], k, 1.0)); + igraph_sparsemat_entry(&JJ2, VECTOR(*q)[k], (int) k, 1.0); } IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); igraph_sparsemat_destroy(&JJ2); @@ -533,7 +505,7 @@ igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, igraph_sparsemat_destroy(myres); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -554,8 +526,8 @@ igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, * Time complexity: O(1) on average. */ -igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, - igraph_integer_t row, igraph_integer_t col, igraph_real_t elem) { +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); @@ -585,7 +557,7 @@ igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, * Time complexity: O(nz) where \c nz is the number of non-zero elements. */ -igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, +int igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res) { if (! igraph_sparsemat_is_triplet(A)) { @@ -599,75 +571,6 @@ igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, return IGRAPH_SUCCESS; } -static igraph_real_t igraph_i_sparsemat_get_cc( - const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col -) { - /* elements in column 'col' are at indices - * A->cs->p[col] .. A->cs->p[col+1] (open from right) in - * A->cs->x . - * - * Their corresponding row indices are in A->cs->i . - */ - - CS_INT lo = A->cs->p[col]; - CS_INT hi = A->cs->p[col + 1]; - igraph_real_t result = 0.0; - - /* TODO: this could be faster with binary search if A->cs->i - * is sorted, which I think should be */ - for (; lo < hi; lo++) { - if (A->cs->i[lo] == row) { - result += A->cs->x[lo]; - } - } - - return result; -} - -static igraph_real_t igraph_i_sparsemat_get_triplet( - const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col -) { - igraph_sparsemat_iterator_t it; - igraph_real_t result = 0.0; - - igraph_sparsemat_iterator_init(&it, A); - while (!igraph_sparsemat_iterator_end(&it)) { - if ( - igraph_sparsemat_iterator_row(&it) == row && - igraph_sparsemat_iterator_col(&it) == col - ) { - result += igraph_sparsemat_iterator_get(&it); - } - igraph_sparsemat_iterator_next(&it); - } - - return result; -} - -/** - * \function igraph_sparsemat_get - * \brief Return the value of a single element from a sparse matrix. - * - * \param A The input matrix, in triplet or column-compressed format. - * \param row The row index - * \param col The column index - * \return The value of the cell with the given row and column indices in the - * matrix; zero if the indices are out of bounds. - * - * Time complexity: TODO. - */ -igraph_real_t igraph_sparsemat_get( - const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col -) { - if (row < 0 || col < 0 || row >= A->cs->m || col >= A->cs->n) { - return 0.0; - } else if (igraph_sparsemat_is_cc(A)) { - return igraph_i_sparsemat_get_cc(A, row, col); - } else { - return igraph_i_sparsemat_get_triplet(A, row, col); - } -} - /** * \function igraph_sparsemat_transpose * \brief Transposes a sparse matrix. @@ -675,52 +578,55 @@ igraph_real_t igraph_sparsemat_get( * \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. */ -igraph_error_t igraph_sparsemat_transpose( - const igraph_sparsemat_t *A, igraph_sparsemat_t *res -) { +int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, + igraph_sparsemat_t *res, + int values) { - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { /* column-compressed */ - res->cs = cs_transpose(A->cs, /* values = */ 1); + 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_init_copy(res, A)); + IGRAPH_CHECK(igraph_sparsemat_copy(res, A)); tmp = res->cs->p; res->cs->p = res->cs->i; res->cs->i = tmp; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) { +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; - igraph_integer_t nz; + int nz; - IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t)); + 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)); + 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)); + 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(CS_INT) * (size_t) nz) == 0; - res = res && memcmp(t.cs->p, tt.cs->p, sizeof(CS_INT) * + 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(CS_ENTRY) * (size_t)nz) == 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); @@ -731,7 +637,7 @@ static igraph_error_t igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_ return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, igraph_bool_t *result) { +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)); @@ -743,24 +649,24 @@ static igraph_error_t igraph_i_sparsemat_is_symmetric_triplet(const igraph_spars return IGRAPH_SUCCESS; } -/** - * \function igraph_sparsemat_is_symmetric - * \brief Returns whether a sparse matrix is symmetric. - * - * \param A The input matrix - * \param result Pointer to an \c igraph_bool_t ; the result is provided here. - * \return Error code. - */ +igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) { + igraph_bool_t res = 0; -igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result) { if (A->cs->m != A->cs->n) { - *result = false; - } else if (igraph_sparsemat_is_cc(A)) { - IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(A, result)); + 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_CHECK(igraph_i_sparsemat_is_symmetric_triplet(A, result)); + igraph_i_sparsemat_is_symmetric_triplet(A, &res); } - return IGRAPH_SUCCESS; + + return res; } /** @@ -777,25 +683,14 @@ igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A) { +int igraph_sparsemat_dupl(igraph_sparsemat_t *A) { if (!cs_dupl(A->cs)) { IGRAPH_ERROR("Cannot remove duplicates from sparse matrix", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; -} - -struct fkeep_wrapper_data { - igraph_integer_t (*fkeep) (igraph_integer_t, igraph_integer_t, igraph_real_t, void*); - void* data; -}; - -static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) { - return ((struct fkeep_wrapper_data*)data)->fkeep( - row, col, value, ((struct fkeep_wrapper_data*)data)->data - ); + return 0; } /** @@ -808,8 +703,8 @@ static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) { * 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 igraph_integer_t, the row index of the entry, the second is - * another \c igraph_integer_t, the column index. The third is \c igraph_real_t, + * 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 @@ -821,22 +716,18 @@ static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) { * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_fkeep( +int igraph_sparsemat_fkeep( igraph_sparsemat_t *A, igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), void *other ) { - struct fkeep_wrapper_data wrapper_data = { - /* .fkeep = */ fkeep, - /* .data = */ 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_wrapper, &wrapper_data) < 0) { + 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); } @@ -855,13 +746,13 @@ igraph_error_t igraph_sparsemat_fkeep( * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { +int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { if (!cs_dropzeros(A->cs)) { IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -878,7 +769,7 @@ igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { +int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { IGRAPH_ASSERT(A); if (!igraph_sparsemat_is_cc(A)) { @@ -907,7 +798,7 @@ igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, +int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res) { @@ -916,7 +807,7 @@ igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -936,7 +827,7 @@ igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A, +int igraph_sparsemat_add(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_real_t alpha, igraph_real_t beta, @@ -947,7 +838,7 @@ igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -964,7 +855,7 @@ igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, +int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res) { @@ -979,7 +870,7 @@ igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -996,7 +887,7 @@ igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, +int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { @@ -1012,7 +903,7 @@ igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1029,7 +920,7 @@ igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, +int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { @@ -1046,7 +937,7 @@ igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1062,7 +953,7 @@ igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *U, +int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { @@ -1078,7 +969,7 @@ igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *U, IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1095,7 +986,7 @@ igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *U, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, +int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { @@ -1113,7 +1004,7 @@ igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1132,10 +1023,10 @@ igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, +int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - igraph_integer_t order) { + int order) { if (A->cs->m != A->cs->n) { IGRAPH_ERROR("Cannot perform sparse symmetric solve", @@ -1150,7 +1041,7 @@ igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1172,10 +1063,10 @@ igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, +int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - igraph_integer_t order, + int order, igraph_real_t tol) { if (A->cs->m != A->cs->n) { @@ -1191,26 +1082,26 @@ igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, +static int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - igraph_vector_int_t edges; + 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; - igraph_integer_t from = 0; - igraph_integer_t to = 0; - igraph_integer_t e = 0; + 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_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); while (*p < no_of_edges) { while (to < * (p + 1)) { @@ -1224,30 +1115,31 @@ static igraph_error_t igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparse from++; p++; } - igraph_vector_int_resize(&edges, e); + igraph_vector_resize(&edges, e); - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, +static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - igraph_vector_int_t edges; + 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; - igraph_integer_t e; + long int e; if (no_of_nodes != A->cs->n) { IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); for (e = 0; e < 2 * no_of_edges; i++, j++) { if (directed || *i >= *j) { @@ -1255,13 +1147,14 @@ static igraph_error_t igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_s VECTOR(edges)[e++] = (*j); } } - igraph_vector_int_resize(&edges, e); + igraph_vector_resize(&edges, e); - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1283,34 +1176,34 @@ static igraph_error_t igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_s * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { return (igraph_i_sparsemat_cc(graph, A, directed)); } else { return (igraph_i_sparsemat_triplet(graph, A, directed)); } } -static igraph_error_t igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A, +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_int_t *edges, + 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; - igraph_integer_t from = 0; - igraph_integer_t to = 0; - igraph_integer_t e = 0, w = 0; + long int from = 0; + long int to = 0; + long int e = 0, w = 0; IGRAPH_UNUSED(attr); - IGRAPH_CHECK(igraph_vector_int_resize(edges, no_of_edges * 2)); - IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); + igraph_vector_resize(edges, no_of_edges * 2); + igraph_vector_resize(weights, no_of_edges); while (*p < no_of_edges) { while (to < * (p + 1)) { @@ -1327,17 +1220,17 @@ static igraph_error_t igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A p++; } - igraph_vector_int_resize(edges, e); /* shrinks */ - igraph_vector_resize(weights, w); /* shrinks */ + igraph_vector_resize(edges, e); + igraph_vector_resize(weights, w); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, +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_int_t *edges, + igraph_vector_t *edges, igraph_vector_t *weights) { IGRAPH_UNUSED(A); IGRAPH_UNUSED(directed); IGRAPH_UNUSED(attr); @@ -1348,13 +1241,12 @@ static igraph_error_t igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat IGRAPH_UNIMPLEMENTED); } -igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +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_int_t edges; - igraph_vector_t weights; - CS_INT pot_edges = igraph_i_sparsemat_count_elements(A); + 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; @@ -1364,11 +1256,11 @@ igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, pot_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, pot_edges * 2); IGRAPH_VECTOR_INIT_FINALLY(&weights, pot_edges); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attr_vec, 1); - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { IGRAPH_CHECK(igraph_i_weighted_sparsemat_cc(A, directed, attr, loops, &edges, &weights)); } else { @@ -1384,20 +1276,57 @@ igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat VECTOR(attr_vec)[0] = &attr_rec; /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, directed)); + IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, directed)); IGRAPH_FINALLY(igraph_destroy, graph); - if (igraph_vector_int_size(&edges) > 0) { + if (igraph_vector_size(&edges) > 0) { IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&weights); igraph_vector_ptr_destroy(&attr_vec); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + 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); } @@ -1418,10 +1347,10 @@ igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat * n is the number columns in the matrix. */ -igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, +int igraph_sparsemat_print(const igraph_sparsemat_t *A, FILE *outstream) { - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { /* CC */ CS_INT j, p; for (j = 0; j < A->cs->n; j++) { @@ -1440,30 +1369,27 @@ igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } #undef CHECK -static igraph_error_t igraph_i_sparsemat_eye_triplet( - igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, - igraph_real_t value -) { - igraph_integer_t i; +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_CHECK(igraph_sparsemat_entry(A, i, i, value)); + igraph_sparsemat_entry(A, (int) i, (int) i, value); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_eye_cc( - igraph_sparsemat_t *A, igraph_integer_t n, igraph_real_t value -) { - igraph_integer_t i; +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) { @@ -1477,11 +1403,11 @@ static igraph_error_t igraph_i_sparsemat_eye_cc( } A->cs->p [n] = n; - return IGRAPH_SUCCESS; + return 0; } /** - * \function igraph_sparsemat_init_eye + * \function igraph_sparsemat_eye * \brief Creates a sparse identity matrix. * * \param A An uninitialized sparse matrix, the result is stored @@ -1498,48 +1424,32 @@ static igraph_error_t igraph_i_sparsemat_eye_cc( * Time complexity: O(n). */ -igraph_error_t igraph_sparsemat_init_eye( - igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, - igraph_real_t value, igraph_bool_t compress -) { +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); + return (igraph_i_sparsemat_eye_cc(A, n, value)); } else { - return igraph_i_sparsemat_eye_triplet(A, n, nzmax, value); + return (igraph_i_sparsemat_eye_triplet(A, n, nzmax, value)); } } -/** - * \function igraph_sparsemat_eye - * \brief Creates a sparse identity matrix (deprecated alias). - * - * \deprecated-by igraph_sparsemat_init_eye 0.10 - */ +static int igraph_i_sparsemat_diag_triplet(igraph_sparsemat_t *A, int nzmax, + const igraph_vector_t *values) { -igraph_error_t igraph_sparsemat_eye( - igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, - igraph_real_t value, igraph_bool_t compress -) { - return igraph_sparsemat_init_eye(A, n, nzmax, value, compress); -} - -static igraph_error_t igraph_i_sparsemat_init_diag_triplet( - igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values -) { - - CS_INT i, n = igraph_vector_size(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_CHECK(igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i])); + igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i]); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_init_diag_cc(igraph_sparsemat_t *A, +static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, const igraph_vector_t *values) { CS_INT i, n = igraph_vector_size(values); @@ -1556,12 +1466,12 @@ static igraph_error_t igraph_i_sparsemat_init_diag_cc(igraph_sparsemat_t *A, } A->cs->p [n] = n; - return IGRAPH_SUCCESS; + return 0; } /** - * \function igraph_sparsemat_init_diag + * \function igraph_sparsemat_diag * \brief Creates a sparse diagonal matrix. * * \param A An uninitialized sparse matrix, the result is stored @@ -1578,32 +1488,18 @@ static igraph_error_t igraph_i_sparsemat_init_diag_cc(igraph_sparsemat_t *A, * Time complexity: O(n), the length of the diagonal vector. */ -igraph_error_t igraph_sparsemat_init_diag( - igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, - igraph_bool_t compress -) { +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_init_diag_cc(A, values)); + return (igraph_i_sparsemat_diag_cc(A, values)); } else { - return (igraph_i_sparsemat_init_diag_triplet(A, nzmax, values)); + return (igraph_i_sparsemat_diag_triplet(A, nzmax, values)); } } -/** - * \function igraph_sparsemat_diag - * \brief Creates a sparse diagonal matrix (deprecated alias). - * - * \deprecated-by igraph_sparsemat_init_diag 0.10 - */ - -igraph_error_t igraph_sparsemat_diag( - igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, - igraph_bool_t compress -) { - return igraph_sparsemat_init_diag(A, nzmax, values, compress); -} - -static igraph_error_t igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, +static int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -1613,7 +1509,7 @@ static igraph_error_t igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, igraph_vector_view(&vfrom, from, n); igraph_vector_null(&vto); IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto)); - return IGRAPH_SUCCESS; + return 0; } typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { @@ -1623,7 +1519,7 @@ typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { igraph_sparsemat_solve_t method; } igraph_i_sparsemat_arpack_rssolve_data_t; -static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to, +static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -1643,7 +1539,7 @@ static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to, } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1651,10 +1547,10 @@ static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to, * \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(). Supply - * \c NULL here to use the defaults. 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 + * \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 @@ -1677,28 +1573,20 @@ static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, +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) { - igraph_integer_t n = igraph_sparsemat_nrow(A); + int n = (int) igraph_sparsemat_nrow(A); if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } - if (n > INT_MAX) { - IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); - } - - if (options == 0) { - options = igraph_arpack_options_get_default(); - } - - options->n = (int) n; + options->n = n; if (options->mode == 1) { IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply, @@ -1715,7 +1603,7 @@ igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, /*-----------------------------------*/ /* Create (A-sigma*I) */ - IGRAPH_CHECK(igraph_sparsemat_init_eye(&eye, /*n=*/ n, /*nzmax=*/ n, + 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, @@ -1754,7 +1642,7 @@ igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, IGRAPH_FINALLY_CLEAN(3); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1764,8 +1652,8 @@ igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, * 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(). Supply \c NULL here to use the defaults. - * See also \ref igraph_arpack_options_t for details. + * 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. @@ -1781,27 +1669,19 @@ igraph_error_t igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, +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) { - igraph_integer_t n = igraph_sparsemat_nrow(A); - - if (n > INT_MAX) { - IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); - } + int n = (int) igraph_sparsemat_nrow(A); if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } - if (options == 0) { - options = igraph_arpack_options_get_default(); - } - - options->n = (int) n; + options->n = n; return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply, (void*) A, options, storage, @@ -1828,15 +1708,15 @@ igraph_error_t igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, +int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { - dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 1); + dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 1); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1858,15 +1738,15 @@ igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_spar * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_sparsemat_t *A, +int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { - dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 0); + dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 0); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1888,14 +1768,14 @@ igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_spar * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -1917,14 +1797,14 @@ igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -1945,11 +1825,11 @@ igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, +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) { - igraph_integer_t n = din->numeric->L->n; + int n = din->numeric->L->n; igraph_real_t *workspace; if (res != b) { @@ -1958,7 +1838,7 @@ igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, workspace = IGRAPH_CALLOC(n, igraph_real_t); if (!workspace) { - IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, workspace); @@ -1978,7 +1858,7 @@ igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, IGRAPH_FREE(workspace); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -2000,13 +1880,13 @@ igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, +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) { - igraph_integer_t n = din->numeric->L->n; + int n = din->numeric->L->n; igraph_real_t *workspace; - igraph_integer_t k; + int k; if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); @@ -2037,7 +1917,7 @@ igraph_error_t igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, IGRAPH_FREE(workspace); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -2088,12 +1968,12 @@ void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) { * matrix. */ -igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, +int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, const igraph_matrix_t *mat, igraph_real_t tol) { - igraph_integer_t nrow = igraph_matrix_nrow(mat); - igraph_integer_t ncol = igraph_matrix_ncol(mat); - igraph_integer_t i, j, nzmax = 0; + 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++) { @@ -2113,14 +1993,14 @@ igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, +static int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, const igraph_sparsemat_t *spmat) { - igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); - igraph_integer_t ncol = igraph_sparsemat_ncol(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; @@ -2141,13 +2021,13 @@ static igraph_error_t igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, p++; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, +static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, const igraph_sparsemat_t *spmat) { - igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); - igraph_integer_t ncol = igraph_sparsemat_ncol(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; @@ -2161,7 +2041,7 @@ static igraph_error_t igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, MATRIX(*res, *j, *i) += *x; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2178,7 +2058,7 @@ static igraph_error_t igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, * matrix. */ -igraph_error_t igraph_sparsemat_as_matrix(igraph_matrix_t *res, +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)); @@ -2206,7 +2086,7 @@ igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) { IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = igraph_i_sparsemat_count_elements(A); + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n == 0) { return IGRAPH_NEGINFINITY; } @@ -2242,7 +2122,7 @@ igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = igraph_i_sparsemat_count_elements(A); + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n == 0) { return IGRAPH_POSINFINITY; } @@ -2270,7 +2150,7 @@ igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { */ -igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, +int igraph_sparsemat_minmax(igraph_sparsemat_t *A, igraph_real_t *min, igraph_real_t *max) { CS_INT i, n; CS_ENTRY *ptr; @@ -2278,11 +2158,11 @@ igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = igraph_i_sparsemat_count_elements(A); + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n == 0) { *min = IGRAPH_POSINFINITY; *max = IGRAPH_NEGINFINITY; - return IGRAPH_SUCCESS; + return 0; } *min = *max = *ptr; for (i = 1; i < n; i++, ptr++) { @@ -2292,7 +2172,7 @@ igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, *min = *ptr; } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2305,15 +2185,15 @@ igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, * Time complexity: TODO. */ -igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { +long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { CS_INT i, n; CS_ENTRY *ptr; - igraph_integer_t res = 0; + int res = 0; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = igraph_i_sparsemat_count_elements(A); + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n == 0) { return 0; } @@ -2338,16 +2218,16 @@ igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, +long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, igraph_real_t tol) { CS_INT i, n; CS_ENTRY *ptr; - igraph_integer_t res = 0; + int res = 0; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = igraph_i_sparsemat_count_elements(A); + n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; if (n == 0) { return 0; } @@ -2359,7 +2239,7 @@ igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, return res; } -static igraph_error_t igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, +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; @@ -2372,10 +2252,10 @@ static igraph_error_t igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_ VECTOR(*res)[ *pi ] += *px; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, +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; @@ -2388,7 +2268,7 @@ static igraph_error_t igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, VECTOR(*res)[ *pi ] += *px; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2403,7 +2283,7 @@ static igraph_error_t igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, * Time complexity: O(nz), the number of non-zero elements. */ -igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, +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); @@ -2412,14 +2292,15 @@ igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A, +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, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (i = 0; i < A->cs->nz; i++, pi++, px++) { if (*px < VECTOR(*res)[ *pi ]) { @@ -2427,14 +2308,15 @@ static igraph_error_t igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_ } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, +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)); @@ -2443,7 +2325,7 @@ static igraph_error_t igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); - igraph_vector_fill(res, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (; pi < A->cs->i + ne; pi++, px++) { if (*px < VECTOR(*res)[ *pi ]) { @@ -2451,10 +2333,10 @@ static igraph_error_t igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A, +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); @@ -2464,14 +2346,15 @@ igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A, } -static igraph_error_t igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A, +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, -IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (i = 0; i < A->cs->nz; i++, pi++, px++) { if (*px > VECTOR(*res)[ *pi ]) { @@ -2479,14 +2362,15 @@ static igraph_error_t igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_ } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, +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)); @@ -2495,7 +2379,7 @@ static igraph_error_t igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); - igraph_vector_fill(res, -IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (; pi < A->cs->i + ne; pi++, px++) { if (*px > VECTOR(*res)[ *pi ]) { @@ -2503,10 +2387,10 @@ static igraph_error_t igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, +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); @@ -2515,14 +2399,15 @@ igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A, +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, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (i = 0; i < A->cs->nz; i++, pp++, px++) { if (*px < VECTOR(*res)[ *pp ]) { @@ -2530,16 +2415,17 @@ static igraph_error_t igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_ } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, +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)); @@ -2549,7 +2435,7 @@ static igraph_error_t igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); pr = VECTOR(*res); for (; pp < A->cs->p + n; pp++, pr++) { @@ -2559,10 +2445,10 @@ static igraph_error_t igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, } } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A, +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); @@ -2571,14 +2457,15 @@ igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A, +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, -IGRAPH_INFINITY); + igraph_vector_fill(res, inf); for (i = 0; i < A->cs->nz; i++, pp++, px++) { if (*px > VECTOR(*res)[ *pp ]) { @@ -2586,16 +2473,17 @@ static igraph_error_t igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_ } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, +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)); @@ -2605,7 +2493,7 @@ static igraph_error_t igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, -IGRAPH_INFINITY); + igraph_vector_fill(res, inf); pr = VECTOR(*res); for (; pp < A->cs->p + n; pp++, pr++) { @@ -2615,10 +2503,10 @@ static igraph_error_t igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, } } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, +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); @@ -2627,17 +2515,18 @@ igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A, +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, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); igraph_vector_int_null(pos); for (i = 0; i < A->cs->nz; i++, pi++, px++, pp++) { @@ -2647,17 +2536,18 @@ static igraph_error_t igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, +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; - igraph_integer_t j; + double inf = IGRAPH_INFINITY; + int j; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); @@ -2668,7 +2558,7 @@ static igraph_error_t igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); IGRAPH_CHECK(igraph_vector_int_resize(pos, A->cs->m)); - igraph_vector_fill(res, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); igraph_vector_int_null(pos); for (j = 0; pp < A->cs->p + n; pp++, j++) { @@ -2680,10 +2570,10 @@ static igraph_error_t igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, +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)) { @@ -2693,7 +2583,7 @@ igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, +static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos) { @@ -2701,10 +2591,11 @@ static igraph_error_t igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat 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, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); igraph_vector_int_null(pos); for (i = 0; i < A->cs->nz; i++, pi++, pp++, px++) { @@ -2714,16 +2605,17 @@ static igraph_error_t igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, +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)); @@ -2731,7 +2623,7 @@ static igraph_error_t igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A px = A->cs->x; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, IGRAPH_INFINITY); + igraph_vector_fill(res, inf); pr = VECTOR(*res); IGRAPH_CHECK(igraph_vector_int_resize(pos, n)); igraph_vector_int_null(pos); @@ -2745,10 +2637,10 @@ static igraph_error_t igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A } } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, +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)) { @@ -2758,7 +2650,7 @@ igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, } } -static igraph_error_t igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, +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; @@ -2771,10 +2663,10 @@ static igraph_error_t igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_ VECTOR(*res)[ *pp ] += *px; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, +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; @@ -2791,7 +2683,7 @@ static igraph_error_t igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, *pr += *px; } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2808,7 +2700,7 @@ static igraph_error_t igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, * the number of columns. */ -igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A, +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); @@ -2830,16 +2722,17 @@ igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A, * matrix. */ -igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { +int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { CS_ENTRY *px = A->cs->x; - CS_ENTRY *stop = px + igraph_i_sparsemat_count_elements(A); + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -2855,9 +2748,9 @@ igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { * Time complexity: O(1). */ -igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n) { +int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) { A->cs->m += n; - return IGRAPH_SUCCESS; + return 0; } /** @@ -2873,14 +2766,14 @@ igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t * Time complexity: TODO. */ -igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t n) { +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(CS_INT), &realloc_ok); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); } if (newp != A->cs->p) { A->cs->p = newp; @@ -2890,15 +2783,15 @@ igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t } A->cs->n += n; } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sparsemat_resize - * \brief Resizes a sparse matrix and clears all the elements. + * \brief Resizes a sparse matrix. * * This function resizes a sparse matrix. The resized sparse matrix - * will become empty, even if it contained nonzero entries. + * will be empty. * * \param A The initialized sparse matrix to resize. * \param nrow The new number of rows. @@ -2909,21 +2802,21 @@ igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t * Time complexity: O(nzmax), the maximum number of non-zero elements. */ -igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, igraph_integer_t nrow, - igraph_integer_t ncol, igraph_integer_t nzmax) { +int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, + long int ncol, int nzmax) { - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { igraph_sparsemat_t tmp; - IGRAPH_CHECK(igraph_sparsemat_init(&tmp, nrow, ncol, nzmax)); + 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 = nrow; - A->cs->n = ncol; + A->cs->m = (int) nrow; + A->cs->n = (int) ncol; A->cs->nz = 0; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2942,38 +2835,15 @@ igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, igraph_integer_t n * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) { - return igraph_i_sparsemat_count_elements(A); +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; + } } - -/** - * \function igraph_sparsemat_getelements - * \brief Returns all elements of a sparse matrix. - * - * This function will return the elements of a sparse matrix in three vectors. - * Two vectors will indicate where the elements are located, and one will - * specify the elements themselves. - * - * \param A A sparse matrix in either triplet or compressed form. - * \param i An initialized integer vector. This will store the rows of the - * returned elements. - * \param j An initialized integer 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. - */ - -igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, +int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x) { @@ -2983,25 +2853,25 @@ igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, 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(CS_INT)); - memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(CS_INT)); - memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); + 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(CS_INT)); - memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(CS_INT)); - memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); + 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 IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, +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 = igraph_i_sparsemat_count_elements(A); + 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++) { @@ -3009,10 +2879,10 @@ igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, (*x) *= f; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, +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; @@ -3029,10 +2899,10 @@ static igraph_error_t igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, (*x) *= f; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, +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; @@ -3044,26 +2914,26 @@ static igraph_error_t igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t * (*x) *= f; } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, +int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, const igraph_vector_t *fact) { - if (igraph_sparsemat_is_cc(A)) { + if (A->cs->nz < 0) { return igraph_i_sparsemat_scale_cols_cc(A, fact); } else { return igraph_i_sparsemat_scale_cols_triplet(A, fact); } } -igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, +int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, const igraph_matrix_t *B, igraph_matrix_t *res) { - igraph_integer_t m = igraph_sparsemat_nrow(A); - igraph_integer_t n = igraph_sparsemat_ncol(A); - igraph_integer_t p = igraph_matrix_ncol(B); - igraph_integer_t i; + 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", @@ -3080,16 +2950,16 @@ igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, } } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, +int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, const igraph_sparsemat_t *B, igraph_matrix_t *res) { - igraph_integer_t m = igraph_matrix_nrow(A); - igraph_integer_t n = igraph_matrix_ncol(A); - igraph_integer_t p = igraph_sparsemat_ncol(B); - igraph_integer_t r, c; + 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) { @@ -3107,7 +2977,7 @@ igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, for (c = 0; c < p; c++) { for (r = 0; r < m; r++) { - igraph_integer_t idx = *Bp; + int idx = *Bp; while (idx < * (Bp + 1)) { MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx]; idx++; @@ -3116,7 +2986,7 @@ igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, Bp++; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -3169,108 +3039,53 @@ igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, * Time complexity: O(1). */ -igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, - igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz) { +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_igraph); + A->cs = IGRAPH_CALLOC(1, cs_di); A->cs->nzmax = nzmax; A->cs->m = m; A->cs->n = n; - A->cs->p = (CS_INT*) p; - A->cs->i = (CS_INT*) i; + 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); +} -/** - * \function igraph_sparsemat_sort - * \brief Sorts all elements of a sparse matrix by row and column indices. - * - * This function will sort the elements of a sparse matrix such that iterating - * over the entries will return them sorted by column indices; elements in the - * same column are then sorted by row indices. - * - * \param A A sparse matrix in either triplet or compressed form. - * \param sorted An uninitialized sparse matrix; the result will be returned - * here. The result will be in triplet form if the input was in triplet - * form, otherwise it will be in compressed form. Note that sorting is - * more efficient when the matrix is already in compressed form. - * \return Error code. - * - * Time complexity: TODO - */ - -igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, +int igraph_sparsemat_sort(const igraph_sparsemat_t *A, igraph_sparsemat_t *sorted) { - igraph_sparsemat_t tmp; - igraph_sparsemat_t tmp2; - if (igraph_sparsemat_is_cc(A)) { - /* for column-compressed matrices, we will transpose the matrix twice, - * which will sort the indices as a side effect */ - IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); - IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted)); - igraph_sparsemat_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(1); - } else { - igraph_sparsemat_iterator_t it; - - /* for triplet matrices, we convert it to compressed column representation, - * sort it, then we convert back */ - IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); - IGRAPH_CHECK(igraph_sparsemat_sort(&tmp, &tmp2)); - - igraph_sparsemat_destroy(&tmp); - tmp = tmp2; /* tmp is still protected in the FINALLY stack */ - - IGRAPH_CHECK(igraph_sparsemat_init( - sorted, - igraph_sparsemat_nrow(&tmp), - igraph_sparsemat_ncol(&tmp), - igraph_i_sparsemat_count_elements(&tmp) - )); - IGRAPH_FINALLY(igraph_sparsemat_destroy, sorted); - - IGRAPH_CHECK(igraph_sparsemat_iterator_init(&it, &tmp)); - while (!igraph_sparsemat_iterator_end(&it)) { - IGRAPH_CHECK(igraph_sparsemat_entry( - sorted, - igraph_sparsemat_iterator_row(&it), - igraph_sparsemat_iterator_col(&it), - igraph_sparsemat_iterator_get(&it) - )); - igraph_sparsemat_iterator_next(&it); - } + igraph_sparsemat_t tmp; - igraph_sparsemat_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(2); /* tmp + sorted */ - } + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sparsemat_getelements_sorted - * \brief Returns all elements of a sparse matrix, sorted by row and column indices. - * - * This function will sort a sparse matrix and return the elements in three - * vectors. Two vectors will indicate where the elements are located, - * and one will specify the elements themselves. + * \brief Returns the sorted elements of a sparse matrix. * - * - * Sorting is done based on the \em indices of the elements, not their - * numeric values. The returned entries will be sorted by column indices; - * entries in the same column are then sorted by row indices. + * 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 integer vector. This will store the rows of the + * \param i An initialized int vector. This will store the rows of the * returned elements. - * \param j An initialized integer vector. For a triplet matrix this will + * \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 @@ -3282,115 +3097,40 @@ igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, * \param x An initialized vector. The elements will be placed here. * \return Error code. * - * Time complexity: TODO. + * Time complexity: O(n), the number of stored elements in the sparse matrix. */ -igraph_error_t igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, +int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x) { - 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); - - /* TODO: in triplets format, we could in theory sort the entries without - * going through an extra sorting step (which temporarily converts the - * matrix into compressed format). This is not implemented yet. */ + 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; } -igraph_integer_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { +int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { return A->cs->nzmax; } -igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A) { - CS_INT i; - CS_INT nz = igraph_i_sparsemat_count_elements(A); +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 IGRAPH_SUCCESS; -} - -/** - * \function igraph_sparsemat_normalize_cols - * \brief Normalizes the column sums of a sparse matrix to a given value. - * - * \param sparsemat the sparse matrix to normalize - * \param allow_zeros whether to allow columns with zero sums - * \return \c IGRAPH_SUCCESS if everything was successful, - * \c IGRAPH_EINVAL if there is at least one column with zero sum and it - * is disallowed, - * \c IGRAPH_ENOMEM for out-of-memory conditions - */ - -igraph_error_t igraph_sparsemat_normalize_cols( - igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros -) { - igraph_vector_t sum; - igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); - igraph_integer_t i; - - IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); - - IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum)); - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(sum)[i] != 0.0) { - VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; - } else if (!allow_zeros) { - IGRAPH_ERROR("Columns with zero sum are not allowed", IGRAPH_EINVAL); - } - } - IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum)); - - igraph_vector_destroy(&sum); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_sparsemat_normalize_rows - * \brief Normalizes the row sums of a sparse matrix to a given value. - * - * \param sparsemat the sparse matrix to normalize - * \param allow_zeros whether to allow rows with zero sums - * \return \c IGRAPH_SUCCESS if everything was successful, - * \c IGRAPH_EINVAL if there is at least one row with zero sum and it - * is disallowed, - * \c IGRAPH_ENOMEM for out-of-memory conditions - */ - -igraph_error_t igraph_sparsemat_normalize_rows( - igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros -) { - igraph_vector_t sum; - igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); - igraph_integer_t i; - - IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); - - IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum)); - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(sum)[i] != 0.0) { - VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; - } else if (!allow_zeros) { - IGRAPH_ERROR("Rows with zero sum are not allowed", IGRAPH_EINVAL); - } - } - IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum)); - - igraph_vector_destroy(&sum); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -3404,9 +3144,8 @@ igraph_error_t igraph_sparsemat_normalize_rows( * Time complexity: O(n), the number of columns of the sparse matrix. */ -igraph_error_t igraph_sparsemat_iterator_init( - igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat -) { +int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, + igraph_sparsemat_t *sparsemat) { it->mat = sparsemat; igraph_sparsemat_iterator_reset(it); @@ -3423,7 +3162,7 @@ igraph_error_t igraph_sparsemat_iterator_init( * Time complexity: O(n), the number of columns of the sparse matrix. */ -igraph_error_t igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { +int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { it->pos = 0; it->col = 0; if (!igraph_sparsemat_is_triplet(it->mat)) { @@ -3463,7 +3202,7 @@ igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) { * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { +int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { return it->mat->cs->i[it->pos]; } @@ -3477,7 +3216,7 @@ igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) { +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 { @@ -3510,7 +3249,7 @@ igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) { * Time complexity: O(n), the number of columns of the sparse matrix. */ -igraph_integer_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) { +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) { @@ -3529,6 +3268,6 @@ igraph_integer_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) * Time complexity: O(1). */ -igraph_integer_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { +int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { return it->pos; } diff --git a/src/vendor/cigraph/src/core/spmatrix.c b/src/vendor/cigraph/src/core/spmatrix.c new file mode 100644 index 00000000000..7d2a7ec2637 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/core/stack.c b/src/vendor/cigraph/src/core/stack.c index 69b3b3efef9..66ce72939da 100644 --- a/src/vendor/cigraph/src/core/stack.c +++ b/src/vendor/cigraph/src/core/stack.c @@ -21,6 +21,7 @@ */ +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_stack.h" @@ -30,6 +31,12 @@ #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" @@ -47,3 +54,35 @@ #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/vendor/cigraph/src/core/stack.pmt b/src/vendor/cigraph/src/core/stack.pmt index 4be8cff534d..dc57fcc4804 100644 --- a/src/vendor/cigraph/src/core/stack.pmt +++ b/src/vendor/cigraph/src/core/stack.pmt @@ -24,6 +24,7 @@ #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" +#include "config.h" #include /* memcpy & co. */ #include @@ -34,27 +35,28 @@ * \brief Initializes a stack. * * The initialized stack is always empty. - * * \param s Pointer to an uninitialized stack. - * \param capacity The number of elements to allocate memory for. + * \param size The number of elements to allocate memory for. * \return Error code. * * Time complexity: O(\p size). */ -igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { - igraph_integer_t alloc_size; - IGRAPH_ASSERT(capacity >= 0); - alloc_size = capacity > 0 ? capacity : 1; +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 == NULL) { - IGRAPH_ERROR("Cannot initialize stack.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -72,34 +74,12 @@ igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_intege void FUNCTION(igraph_stack, destroy) (TYPE(igraph_stack)* s) { IGRAPH_ASSERT(s != NULL); - if (s->stor_begin != NULL) { + if (s->stor_begin != 0) { IGRAPH_FREE(s->stor_begin); s->stor_begin = NULL; } } -/** - * \ingroup stack - * \function igraph_stack_capacity - * \brief Returns the allocated capacity of the stack. - * - * Note that this might be different from the size of the stack (as - * queried by \ref igraph_stack_size()), and specifies how many elements - * the stack can hold, without reallocation. - * - * \param v Pointer to the (previously initialized) stack object - * to query. - * \return The allocated capacity. - * - * \sa \ref igraph_stack_size(). - * - * Time complexity: O(1). - */ - -igraph_integer_t FUNCTION(igraph_stack, capacity)(const TYPE(igraph_stack) *s) { - return s->stor_end - s->stor_begin; -} - /** * \ingroup stack * \function igraph_stack_reserve @@ -116,28 +96,25 @@ igraph_integer_t FUNCTION(igraph_stack, capacity)(const TYPE(igraph_stack) *s) { * the stack. */ -igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { - igraph_integer_t current_capacity; +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); - IGRAPH_ASSERT(capacity >= 0); - - current_capacity = FUNCTION(igraph_stack, capacity)(s); - if (capacity <= current_capacity) { - return IGRAPH_SUCCESS; + if (size <= actual_size) { + return 0; } - tmp = IGRAPH_REALLOC(s->stor_begin, capacity, BASE); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for stack."); - - s->end = tmp + (s->end - s->stor_begin); + 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 + capacity; + s->stor_end = s->stor_begin + size; + s->end = s->stor_begin + actual_size; - return IGRAPH_SUCCESS; + return 0; } /** @@ -146,15 +123,16 @@ igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_int * \brief Decides whether a stack object is empty. * * \param s The stack object. - * \return Boolean, \c true if the stack is empty, \c false + * \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_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; } @@ -169,7 +147,7 @@ igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s) { +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; @@ -185,7 +163,7 @@ igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s) { +void FUNCTION(igraph_stack, clear) (TYPE(igraph_stack)* s) { IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); s->end = s->stor_begin; @@ -206,27 +184,34 @@ void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s) { * in O(n) time. */ -igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { +int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); - - if (s->stor_end == s->end) { + if (s->end == s->stor_end) { /* full, allocate more storage */ - igraph_integer_t old_size = FUNCTION(igraph_stack, size)(s); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to stack, already at maximum size.", IGRAPH_EOVERFLOW); - } - if (new_size == 0) { - new_size = 1; + + 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); } - IGRAPH_CHECK(FUNCTION(igraph_stack, reserve)(s, new_size)); - } + 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; + *(s->end) = elem; + (s->end) += 1; - return IGRAPH_SUCCESS; + IGRAPH_FREE(old); + } else { + *(s->end) = elem; + (s->end) += 1; + } + return 0; } /** @@ -242,7 +227,8 @@ igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s) { +BASE FUNCTION(igraph_stack, pop) (TYPE(igraph_stack)* s) { + IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); IGRAPH_ASSERT(s->end != NULL); @@ -266,7 +252,8 @@ BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s) { +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); @@ -275,32 +262,32 @@ BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s) { return *(s->end - 1); } -#if defined(OUT_FORMAT) || defined(FPRINTFUNC) - +#if defined (OUT_FORMAT) #ifndef USING_R -igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack) *s) { - return FUNCTION(igraph_stack, fprint)(s, stdout); + +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 /* USING_R */ +#endif -igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { - igraph_integer_t i, n = FUNCTION(igraph_stack, size)(s); +int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { + long int i, n = FUNCTION(igraph_stack, size)(s); if (n != 0) { -#ifdef FPRINTFUNC - FPRINTFUNC(file, s->stor_begin[0]); -#else fprintf(file, OUT_FORMAT, s->stor_begin[0]); -#endif } for (i = 1; i < n; i++) { -#ifdef FPRINTFUNC - fputc(' ', file); fprintf(file, OUT_FORMAT, s->stor_begin[i]); -#else fprintf(file, " " OUT_FORMAT, s->stor_begin[i]); -#endif } fprintf(file, "\n"); - return IGRAPH_SUCCESS; + return 0; } -#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ +#endif diff --git a/src/vendor/cigraph/src/core/statusbar.c b/src/vendor/cigraph/src/core/statusbar.c index 48e47b9492b..07cdad28196 100644 --- a/src/vendor/cigraph/src/core/statusbar.c +++ b/src/vendor/cigraph/src/core/statusbar.c @@ -33,7 +33,7 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; /** * \function igraph_status - * \brief Reports status from an igraph function. + * 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 @@ -41,7 +41,6 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; * \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. @@ -52,7 +51,7 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; * Time complexity: O(1). */ -igraph_error_t igraph_status(const char *message, void *data) { +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; @@ -63,7 +62,7 @@ igraph_error_t igraph_status(const char *message, void *data) { /** * \function igraph_statusf - * \brief Report status, more flexible printf-like version. + * 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. @@ -77,15 +76,14 @@ igraph_error_t igraph_status(const char *message, void *data) { * \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 \ref igraph_status(). + * \c IGRAPH_INTERRUPTED is returned by \c igraph_status(). */ -igraph_error_t igraph_statusf(const char *message, void *data, ...) { +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); - va_end(ap); return igraph_status(buffer, data); } @@ -95,9 +93,8 @@ igraph_error_t igraph_statusf(const char *message, void *data, ...) { * \function igraph_status_handler_stderr * A simple predefined status handler function. * - * A simple status handler function that writes the status - * message to the standard error. - * + * 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. @@ -106,10 +103,10 @@ igraph_error_t igraph_statusf(const char *message, void *data, ...) { * Time complexity: O(1). */ -igraph_error_t igraph_status_handler_stderr(const char *message, void *data) { +int igraph_status_handler_stderr(const char *message, void *data) { IGRAPH_UNUSED(data); fputs(message, stderr); - return IGRAPH_SUCCESS; + return 0; } #endif diff --git a/src/vendor/cigraph/src/core/strvector.c b/src/vendor/cigraph/src/core/strvector.c index 3e268108837..080b3563ed8 100644 --- a/src/vendor/cigraph/src/core/strvector.c +++ b/src/vendor/cigraph/src/core/strvector.c @@ -26,26 +26,17 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "internal/hacks.h" /* strdup */ -#include "math/safe_intop.h" - #include /* memcpy & co. */ #include /** * \section igraph_strvector_t - * - * The igraph_strvector_t type is a vector of null-terminated - * strings. It is used internally for storing graph attribute names as well as - * string attributes in the C attribute handler. - * - * - * - * This container automatically manages the memory of its elements. - * The strings within an igraph_strvector_t should be considered - * constant, and not modified directly. Functions that add new elements - * always make copies of the string passed to them. - * + * 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 @@ -55,12 +46,11 @@ /** * \ingroup strvector * \function igraph_strvector_init - * \brief Initializes a string vector. + * \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. @@ -68,35 +58,29 @@ * Time complexity: O(\p len). */ -igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t size) { - - sv->stor_begin = IGRAPH_CALLOC(size, char*); - IGRAPH_CHECK_OOM(sv->stor_begin, "Cannot initialize string vector."); - - for (igraph_integer_t i = 0; i < size; i++) { - sv->stor_begin[i] = IGRAPH_CALLOC(1, char); - if (sv->stor_begin[i] == NULL) { - /* LCOV_EXCL_START */ - for (igraph_integer_t j = 0; j < i; j++) { - IGRAPH_FREE(sv->stor_begin[j]); - } - IGRAPH_FREE(sv->stor_begin); - IGRAPH_ERROR("Cannot initialize string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - /* LCOV_EXCL_STOP */ +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->stor_begin[i][0] = '\0'; + sv->data[i][0] = '\0'; } + sv->len = len; - sv->stor_end = sv->stor_begin + size; - sv->end = sv->stor_end; - - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup strvector * \function igraph_strvector_destroy - * \brief Frees the memory allocated for the string vector. + * \brief Free allocated memory * * Destroy a string vector. It may be reinitialized with \ref * igraph_strvector_init() later. @@ -107,44 +91,48 @@ igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t si */ void igraph_strvector_destroy(igraph_strvector_t *sv) { - char **ptr; - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - for (ptr = sv->stor_begin; ptr < sv->end; ptr++) { - IGRAPH_FREE(*ptr); + 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); } - IGRAPH_FREE(sv->stor_begin); } /** * \ingroup strvector * \function igraph_strvector_get - * \brief Retrieves an element of the string vector. + * \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). */ -const char *igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx) { - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - IGRAPH_ASSERT(sv->stor_begin[idx] != NULL); - return sv->stor_begin[idx]; +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 Sets an element of the string vector from a string. + * \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. @@ -154,19 +142,38 @@ const char *igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t * depending on the memory management, if reallocation is needed. */ -igraph_error_t igraph_strvector_set(igraph_strvector_t *sv, igraph_integer_t idx, +int igraph_strvector_set(igraph_strvector_t *sv, long int idx, const char *value) { - return igraph_strvector_set_len(sv, idx, value, strlen(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_set_len - * \brief Sets an element of the string vector given a buffer and its size. + * \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. @@ -176,20 +183,27 @@ igraph_error_t igraph_strvector_set(igraph_strvector_t *sv, igraph_integer_t idx * Time complexity: O(l), the length of the new string. Maybe more, * depending on the memory management, if reallocation is needed. */ -igraph_error_t igraph_strvector_set_len(igraph_strvector_t *sv, igraph_integer_t idx, - const char *value, size_t len) { - char *tmp; - - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - IGRAPH_ASSERT(sv->stor_begin[idx] != NULL); - - tmp = IGRAPH_REALLOC(sv->stor_begin[idx], len + 1, char); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new item in string vector."); - - sv->stor_begin[idx] = tmp; - memcpy(sv->stor_begin[idx], value, len * sizeof(char)); - sv->stor_begin[idx][len] = '\0'; +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; } @@ -198,36 +212,33 @@ igraph_error_t igraph_strvector_set_len(igraph_strvector_t *sv, igraph_integer_t * \ingroup strvector * \function igraph_strvector_remove_section * \brief Removes a section from a string vector. - * - * This function removes the range [from, to) from the string vector. - * - * \param sv The string vector. - * \param from The position of the first element to remove. - * \param to The position of the first element \em not to remove. + * \todo repair realloc */ -void igraph_strvector_remove_section( - igraph_strvector_t *sv, igraph_integer_t from, igraph_integer_t to) { - igraph_integer_t size = igraph_strvector_size(sv); - igraph_integer_t i; +void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, + long int to) { + long int i; + /* char **tmp; */ - if (from < 0) { - from = 0; - } + IGRAPH_ASSERT(v != 0); + IGRAPH_ASSERT(v->data != 0); - if (to > size) { - to = size; + 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]; } - if (to > from) { - for (i = from; i < to; i++) { - IGRAPH_FREE(sv->stor_begin[i]); - } + v->len -= (to - from); - memmove(sv->stor_begin + from, sv->stor_begin + to, - sizeof(char*) * (sv->end - sv->stor_begin - to)); - sv->end -= (to - from); - } + /* try to make it smaller */ + /* tmp=IGRAPH_REALLOC(v->data, v->len, char*); */ + /* if (tmp!=0) { */ + /* v->data=tmp; */ + /* } */ } /** @@ -236,163 +247,122 @@ void igraph_strvector_remove_section( * \brief Removes a single element from a string vector. * * The string will be one shorter. - * \param sv The string vector. + * \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 *sv, igraph_integer_t elem) { - igraph_strvector_remove_section(sv, elem, elem + 1); +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_init_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. + * \function igraph_strvector_move_interval + * \brief Copies an interval of a string vector. */ -igraph_error_t igraph_strvector_init_copy(igraph_strvector_t *to, - const igraph_strvector_t *from) { - igraph_integer_t from_size = igraph_strvector_size(from); - - to->stor_begin = IGRAPH_CALLOC(from_size, char*); - IGRAPH_CHECK_OOM(to->stor_begin, "Cannot copy string vector."); - - for (igraph_integer_t i = 0; i < from_size; i++) { - to->stor_begin[i] = strdup(igraph_strvector_get(from, i)); - if (to->stor_begin[i] == NULL) { - /* LCOV_EXCL_START */ - for (igraph_integer_t j = 0; j < i; j++) { - IGRAPH_FREE(to->stor_begin[j]); - } - IGRAPH_FREE(to->stor_begin); - IGRAPH_ERROR("Cannot copy string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - /* LCOV_EXCL_STOP */ +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); } } - - to->stor_end = to->stor_begin + from_size; - to->end = to->stor_end; - - return IGRAPH_SUCCESS; } /** * \ingroup strvector * \function igraph_strvector_copy - * \brief Initialization by copying (deprecated alias). + * \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. * - * \deprecated-by igraph_strvector_init_copy 0.10.0 + * Time complexity: O(l), the total length of the strings in \p from. */ -igraph_error_t igraph_strvector_copy(igraph_strvector_t *to, +int igraph_strvector_copy(igraph_strvector_t *to, const igraph_strvector_t *from) { - return igraph_strvector_init_copy(to, 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 - * \brief Concatenates two string vectors. - * - * Appends the contents of the \p from vector to the \p to vector. - * If the \p from vector is no longer needed after this operation, - * use \ref igraph_strvector_merge() for better performance. + * 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. * - * \sa \ref igraph_strvector_merge() - * * 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. */ -igraph_error_t igraph_strvector_append(igraph_strvector_t *to, +int igraph_strvector_append(igraph_strvector_t *to, const igraph_strvector_t *from) { - igraph_integer_t len1 = igraph_strvector_size(to), len2 = igraph_strvector_size(from); - igraph_integer_t newlen; - igraph_bool_t error = false; - char *tmp; - - IGRAPH_SAFE_ADD(len1, len2, &newlen); - IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); - - for (igraph_integer_t i = 0; i < len2; i++) { - tmp = strdup(igraph_strvector_get(from, i)); - if (!tmp) { - error = true; - break; - } else { - *(to->end) = tmp; - to->end++; + 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); /* always shrinks */ - IGRAPH_ERROR("Cannot append string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup strvector - * \function igraph_strvector_merge - * \brief Moves the contents of a string vector to the end of another. - * - * Transfers the contents of the \p from vector to the end of \p to, clearing - * \p from in the process. If this operation fails, both vectors are left intact. - * This function does not copy or reallocate individual strings, therefore it - * performs better than \ref igraph_strvector_append(). - * - * \param to The target vector. The contents of \p from will be appended to it. - * \param from The source vector. It will be cleared. - * \return Error code. - * - * \sa \ref igraph_strvector_append() - * - * Time complexity: O(l2) if \p to has sufficient capacity, O(2*l1+l2) otherwise, - * where l1 and l2 are the lengths of \p to and \from respectively. - */ -igraph_error_t igraph_strvector_merge(igraph_strvector_t *to, igraph_strvector_t *from) { - char **p1, **p2, **pe; - igraph_integer_t newlen; - - IGRAPH_SAFE_ADD(igraph_strvector_size(to), igraph_strvector_size(from), &newlen); - IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); - - /* transfer contents of 'from' to 'to */ - for (p1 = to->end, p2 = from->stor_begin, pe = to->stor_begin + newlen; - p1 < pe; ++p1, ++p2) - { - *p1 = *p2; + igraph_strvector_resize(to, len1); + IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM); } - to->end = pe; - - /* clear 'from' */ - from->end = from->stor_begin; - - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_strvector_clear - * \brief Removes all elements from a string vector. + * 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, @@ -400,23 +370,28 @@ igraph_error_t igraph_strvector_merge(igraph_strvector_t *to, igraph_strvector_t */ void igraph_strvector_clear(igraph_strvector_t *sv) { - igraph_integer_t n = igraph_strvector_size(sv); + long int i, n = igraph_strvector_size(sv); + char **tmp; - for (igraph_integer_t i = 0; i < n; i++) { - IGRAPH_FREE(sv->stor_begin[i]); + 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; } - sv->end = sv->stor_begin; } /** * \ingroup strvector * \function igraph_strvector_resize - * \brief Resizes a string vector. + * \brief Resize * * If the new size is bigger then empty strings are added, if it is * smaller then the unneeded elements are removed. - * - * \param sv The string vector. + * \param v The string vector. * \param newsize The new size. * \return Error code. * @@ -425,133 +400,64 @@ void igraph_strvector_clear(igraph_strvector_t *sv) { * smaller, maybe less, depending on memory management. */ -igraph_error_t igraph_strvector_resize(igraph_strvector_t *sv, igraph_integer_t newsize) { - igraph_integer_t toadd = newsize - igraph_strvector_size(sv); - igraph_integer_t oldsize = igraph_strvector_size(sv); +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; - if (newsize < oldsize) { - for (igraph_integer_t i = newsize; i < oldsize; i++) { - IGRAPH_FREE(sv->stor_begin[i]); + 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'; } - sv->end = sv->stor_begin + newsize; - } else if (newsize > oldsize) { - IGRAPH_CHECK(igraph_strvector_reserve(sv, newsize)); - - for (igraph_integer_t i = 0; i < toadd; i++) { - sv->stor_begin[oldsize + i] = IGRAPH_CALLOC(1, char); - if (sv->stor_begin[oldsize + i] == NULL) { - /* LCOV_EXCL_START */ - for (igraph_integer_t j = 0; j < i; j++) { - IGRAPH_FREE(sv->stor_begin[oldsize + j]); + 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]); } - IGRAPH_ERROR("Cannot resize string vector.", IGRAPH_ENOMEM); - /* LCOV_EXCL_STOP */ } - sv->stor_begin[oldsize + i][0] = '\0'; + /* 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); } - sv->end = sv->stor_begin + newsize; } + v->len = newsize; - return IGRAPH_SUCCESS; -} - -/** - * \ingroup strvector - * \function igraph_strvector_capacity - * \brief Returns the capacity of a string vector. - * - * \param sv The string vector. - * \return The capacity of the string vector. - * - * Time complexity: O(1). - */ - -igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv) { - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - return sv->stor_end - sv->stor_begin; -} - -/** - * \ingroup strvector - * \function igraph_strvector_reserve - * \brief Reserves memory for a string vector. - * - * - * \a igraph string 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 - * string vector. Let us see a small example to clarify things: if you - * reserve space for 100 strings and the size of your - * vector was (and still is) 60, then you can surely add additional 40 - * strings to your vector before it will be copied. - * - * \param sv The string vector object. - * \param capacity The new \em allocated size of the string 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. - */ - -igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, igraph_integer_t capacity) { - igraph_integer_t current_capacity = igraph_strvector_capacity(sv); - char **tmp; - - if (capacity <= current_capacity) { - return IGRAPH_SUCCESS; - } - - tmp = IGRAPH_REALLOC(sv->stor_begin, capacity, char *); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new items in string vector."); - - sv->end = tmp + (sv->end - sv->stor_begin); - sv->stor_begin = tmp; - sv->stor_end = sv->stor_begin + capacity; - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup strvector - * \function igraph_strvector_resize_min - * \brief Deallocates the unused memory of a string vector. - * - * This function attempts to deallocate the unused reserved storage - * of a string vector. If it succeeds, \ref igraph_strvector_size() and - * \ref igraph_strvector_capacity() will be the same. The data in the - * string vector is always preserved, even if deallocation is not successful. - * - * \param sv The string vector. - * - * Time complexity: Operating system dependent, at most O(n). - */ - -void igraph_strvector_resize_min(igraph_strvector_t *sv) { - igraph_integer_t size; - char **tmp; - if (sv->stor_end == sv->end) { - return; - } - - size = (sv->end - sv->stor_begin); - tmp = IGRAPH_REALLOC(sv->stor_begin, size, char *); - - if (tmp != NULL) { - sv->stor_begin = tmp; - sv->stor_end = sv->end = sv->stor_begin + size; - } + return 0; } /** * \ingroup strvector * \function igraph_strvector_size - * \brief Returns the size of a string vector. + * \brief Gives the size of a string vector. * * \param sv The string vector. * \return The length of the string vector. @@ -559,111 +465,101 @@ void igraph_strvector_resize_min(igraph_strvector_t *sv) { * Time complexity: O(1). */ -igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv) { - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - return sv->end - sv->stor_begin; -} - -/** - * Ensures that the vector has at least one extra slot at the end of its - * allocated storage area. - */ -static igraph_error_t igraph_i_strvector_expand_if_full(igraph_strvector_t *sv) { - IGRAPH_ASSERT(sv != NULL); - IGRAPH_ASSERT(sv->stor_begin != NULL); - - if (sv->stor_end == sv->end) { - igraph_integer_t old_size = igraph_strvector_size(sv); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot add new item to string vector, already at maximum size.", IGRAPH_EOVERFLOW); - } - if (new_size == 0) { - new_size = 1; - } - IGRAPH_CHECK(igraph_strvector_reserve(sv, new_size)); - } - - return IGRAPH_SUCCESS; +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_push_back + * \function igraph_strvector_add * \brief Adds an element to the back of a string vector. * - * \param sv The string vector. - * \param value The string to add; it will be copied. + * \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. */ -igraph_error_t igraph_strvector_push_back(igraph_strvector_t *sv, const char *value) { - IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); - char *tmp = strdup(value); - IGRAPH_CHECK_OOM(tmp, "Cannot push new string to string vector."); - *sv->end = tmp; - sv->end++; +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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup strvector - * \function igraph_strvector_push_back_len - * \brief Adds a string of the given length to the back of a string vector. - * - * \param sv The string vector. - * \param value The start of the string to add. At most \p len characters will be copied. - * \param len The length of the string. - * \return Error code. - * - * Time complexity: O(n+l), n is the total number of strings, l is the - * length of the new string. + * \function igraph_strvector_permdelete + * \brief Removes elements from a string vector (for internal use) */ -igraph_error_t igraph_strvector_push_back_len( - igraph_strvector_t *sv, - const char *value, igraph_integer_t len) { +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); - IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); - char *tmp = strndup(value, len); - if (! tmp) { - IGRAPH_ERROR("Cannot add string to string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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]); + } } - *sv->end = tmp; - sv->end++; - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup strvector - * \function igraph_strvector_add - * \brief Adds an element to the back of a string vector (deprecated alias). - * - * \deprecated-by igraph_strvector_push_back 0.10.0 - */ - -igraph_error_t igraph_strvector_add(igraph_strvector_t *sv, const char *value) { - return igraph_strvector_push_back(sv, value); + /* 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_set2 - * \brief Sets an element of the string vector given a buffer and its size (deprecated alias). - * - * \deprecated-by igraph_strvector_set_len 0.10.0 + * \function igraph_strvector_remove_negidx + * \brief Removes elements from a string vector (for internal use) */ -igraph_error_t igraph_strvector_set2( - igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len -) { - return igraph_strvector_set_len(sv, idx, value, len); +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; } /** @@ -671,46 +567,37 @@ igraph_error_t igraph_strvector_set2( * \function igraph_strvector_print * \brief Prints a string vector. * - * \param sv The 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. */ -igraph_error_t igraph_strvector_print(const igraph_strvector_t *sv, FILE *file, +int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, const char *sep) { - igraph_integer_t n = igraph_strvector_size(sv); + long int i, n = igraph_strvector_size(v); if (n != 0) { - fprintf(file, "%s", STR(*sv, 0)); + fprintf(file, "%s", STR(*v, 0)); } - for (igraph_integer_t i = 1; i < n; i++) { - fprintf(file, "%s%s", sep, STR(*sv, i)); + for (i = 1; i < n; i++) { + fprintf(file, "%s%s", sep, STR(*v, i)); } return IGRAPH_SUCCESS; } -/** - * \ingroup strvector - * \function igraph_strvector_set - * \brief Takes elements at given positions from a string vector. - * - * \param sv The string vector. - * \param newv An initialized string vector, it will be resized as needed. - * \param idx An integer vector of indices to take from \p sv. - * \return Error code. - */ -igraph_error_t igraph_strvector_index(const igraph_strvector_t *sv, +int igraph_strvector_index(const igraph_strvector_t *v, igraph_strvector_t *newv, - const igraph_vector_int_t *idx) { + const igraph_vector_t *idx) { - igraph_integer_t newlen = igraph_vector_int_size(idx); + long int i, newlen = igraph_vector_size(idx); IGRAPH_CHECK(igraph_strvector_resize(newv, newlen)); - for (igraph_integer_t i = 0; i < newlen; i++) { - igraph_integer_t j = VECTOR(*idx)[i]; - const char *str = igraph_strvector_get(sv, j); - IGRAPH_CHECK(igraph_strvector_set(newv, i, str)); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/trie.c b/src/vendor/cigraph/src/core/trie.c index f9971519d2f..1ea96870cf4 100644 --- a/src/vendor/cigraph/src/core/trie.c +++ b/src/vendor/cigraph/src/core/trie.c @@ -23,27 +23,28 @@ #include "igraph_types.h" #include "igraph_memory.h" +#include "igraph_random.h" +#include "igraph_error.h" #include "core/trie.h" -#include "internal/hacks.h" /* strdup */ -#include +#include "config.h" +#include -/* - * igraph_trie_t is a data structures that stores an ordered list of strings. - * It allows an efficient lookup of the index of a string. It has the capability - * to also store the list of strings directly for reverse lookup of strings - * by index. +/** + * \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. */ -/* Allocates memory for a trie node. */ -static igraph_error_t igraph_i_trie_init_node(igraph_trie_node_t *t) { +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_INT_INIT_FINALLY(&t->values, 0); + IGRAPH_VECTOR_INIT_FINALLY(&t->values, 0); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); @@ -51,44 +52,44 @@ static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); /** * \ingroup igraphtrie * \brief Creates a trie. - * - * \param t An uninitialized trie. - * \param storekeys Specifies whether keys are stored for reverse lookup. - * \return Error code: Errors by \ref igraph_strvector_init(), - * \ref igraph_vector_ptr_init() and \ref igraph_vector_init() might be returned. + * \return Error code: errors by igraph_strvector_init(), + * igraph_vector_ptr_init() and igraph_vector_init() might be returned. */ -igraph_error_t igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) { +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(&t->node)); - IGRAPH_FINALLY(igraph_i_trie_destroy_node, &t->node); + 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 IGRAPH_SUCCESS; + 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) { - igraph_integer_t i; + long int i; igraph_strvector_destroy(&t->strs); - igraph_integer_t children_size = igraph_vector_ptr_size(&t->children); - for (i = 0; i < children_size; i++) { + 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_int_destroy(&t->values); + igraph_vector_destroy(&t->values); if (sfree) { IGRAPH_FREE(t); } } -/* Deallocates a trie node. */ static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { igraph_i_trie_destroy_node_helper(t, 0); } @@ -96,21 +97,24 @@ static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { /** * \ingroup igraphtrie * \brief Destroys a trie (frees allocated memory). - * - * \param t The trie. */ void igraph_trie_destroy(igraph_trie_t *t) { if (t->storekeys) { igraph_strvector_destroy(&t->keys); } - igraph_i_trie_destroy_node(&t->node); + igraph_i_trie_destroy_node( (igraph_trie_node_t*) t); } -/* Computes the location (index) of the first difference between 'str' and 'key' */ -static size_t igraph_i_strdiff(const char *str, const char *key) { - size_t diff = 0; +/** + * \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++; } @@ -121,25 +125,23 @@ static size_t igraph_i_strdiff(const char *str, const char *key) { * \ingroup igraphtrie * \brief Search/insert in a trie (not to be called directly). * - * \return Error code, usually \c IGRAPH_ENOMEM. + * @return Error code: + * - IGRAPH_ENOMEM: out of memory */ -static igraph_error_t igraph_i_trie_get_node( - igraph_trie_node_t *t, const char *key, igraph_integer_t newvalue, - igraph_integer_t *id -) { - const char *str; - igraph_integer_t i; +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); - igraph_integer_t strs_size = igraph_strvector_size(&t->strs); - for (i = 0; i < strs_size; i++) { - size_t diff; - str = igraph_strvector_get(&t->strs, i); + 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) { @@ -152,12 +154,12 @@ static igraph_error_t igraph_i_trie_get_node( /* ------------------------------------ */ /* They are exactly the same */ if (VECTOR(t->values)[i] != -1) { - *id = VECTOR(t->values)[i]; - return IGRAPH_SUCCESS; + *id = (long int) VECTOR(t->values)[i]; + return 0; } else { VECTOR(t->values)[i] = newvalue; - *id = newvalue; - return IGRAPH_SUCCESS; + *id = (long int) newvalue; + return 0; } } else if (str[diff] == '\0') { @@ -166,29 +168,27 @@ static igraph_error_t igraph_i_trie_get_node( /* 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_i_trie_get_node(node, key + diff, newvalue, id); + 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) { - IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&node->values, 1); + IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, key + diff)); - IGRAPH_FINALLY_CLEAN(4); - VECTOR(node->children)[0] = 0; VECTOR(node->values)[0] = newvalue; VECTOR(t->children)[i] = node; - *id = newvalue; - return IGRAPH_SUCCESS; + *id = (long int) newvalue; + IGRAPH_FINALLY_CLEAN(3); + return 0; } else { *id = -1; - return IGRAPH_SUCCESS; + return 0; } } else if (key[diff] == '\0' && add) { @@ -198,34 +198,32 @@ static igraph_error_t igraph_i_trie_get_node( char *str2; igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); - if (! node) { - IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&node->values, 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); - IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); - + 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(5); + IGRAPH_FINALLY_CLEAN(4); VECTOR(t->values)[i] = newvalue; VECTOR(t->children)[i] = node; - *id = newvalue; - return IGRAPH_SUCCESS; + *id = (long int) newvalue; + return 0; } else if (add) { @@ -234,13 +232,12 @@ static igraph_error_t igraph_i_trie_get_node( char *str2; igraph_trie_node_t *node = IGRAPH_CALLOC(1, igraph_trie_node_t); - if (! node) { - IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + if (node == 0) { + IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&node->values, 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]; @@ -249,27 +246,26 @@ static igraph_error_t igraph_i_trie_get_node( VECTOR(node->values)[1] = newvalue; str2 = strdup(str); - IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); - + 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(5); + IGRAPH_FINALLY_CLEAN(4); VECTOR(t->values)[i] = -1; VECTOR(t->children)[i] = node; - *id = newvalue; - return IGRAPH_SUCCESS; + *id = (long int) newvalue; + return 0; } else { /* ------------------------------------------------- */ /* No match, but we requested not to add the new key */ *id = -1; - return IGRAPH_SUCCESS; + return 0; } } @@ -277,51 +273,49 @@ static igraph_error_t igraph_i_trie_get_node( /* Nothing matches */ if (add) { - /* Memory saving at the cost of performance may be possible by using the pattern - * CHECK(reserve(vec, size(vec) + 1)); - * push_back(vec, value); - * This was the original pattern used before igraph 0.10. */ - IGRAPH_CHECK(igraph_strvector_push_back(&t->strs, key)); - IGRAPH_CHECK(igraph_vector_ptr_push_back(&t->children, NULL)); - IGRAPH_CHECK(igraph_vector_int_push_back(&t->values, newvalue)); - *id = newvalue; + 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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup igraphtrie - * \brief Search/insert a null-terminated string in a trie. - * - * \param t The trie. - * \param key The string to search for. If not found, it will be inserted. - * \param id The index of the string is stored here. - * \return Error code, usually \c IGRAPH_ENOMEM. + * \brief Search/insert in a trie. */ -igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id) { +int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) { if (!t->storekeys) { - IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, key, t->maxvalue + 1, id)); + 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 { - igraph_error_t ret; - - IGRAPH_FINALLY_ENTER(); + 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_push_back(&t->keys, key); - if (ret != IGRAPH_SUCCESS) { - IGRAPH_FINALLY_EXIT(); + 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_i_trie_get_node(&t->node, key, t->maxvalue + 1, id); - if (ret != IGRAPH_SUCCESS) { - igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); /* shrinks, error safe */ - IGRAPH_FINALLY_EXIT(); + 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); } @@ -329,109 +323,73 @@ igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer if (*id > t->maxvalue) { t->maxvalue = *id; } else { - igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); /* shrinks, error safe */ + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); } - IGRAPH_FINALLY_EXIT(); + igraph_set_error_handler(oldhandler); } - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup igraphtrie - * \brief Search/insert a string of given length in a trie. - * - * This function is identical to \ref igraph_trie_get(), except that - * it takes a string of a given length as input instead of a null-terminated - * string. + * \brief Search/insert in a trie (for internal use). * - * \param t The trie. - * \param key The string to search for. If not found, it will be inserted. - * \param length The length of \p key. - * \param id The index of the string is stored here. - * \return Error code, usually \c IGRAPH_ENOMEM. + * @return Error code: + * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_trie_get_len( - igraph_trie_t *t, const char *key, - igraph_integer_t length, - igraph_integer_t *id) { +int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, + long int *id) { + char *tmp = IGRAPH_CALLOC(length + 1, char); - char *tmp = strndup(key, length); - if (! tmp) { - IGRAPH_ERROR("Cannot get from trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup igraphtrie * \brief Search in a trie. - * - * This variant does not add \p key to the trie if it does not exist. - * In this case, a negative \p id is returned. - * - * \param t The trie. - * \param key The string to search for. - * \param id If \p key is found, its index is stored here. Otherwise, - * a negative value is returned. - * \param Error code. + * This variant does not add \c key to the trie if it does not exist. + * In this case, a negative id is returned. */ -igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id) { - IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, key, -1, id)); - return IGRAPH_SUCCESS; +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. - * - * \param t The trie. - * \param idx The index of the string. It is not checked that it is within range. - * \return The string with the given index. If the trie does not store the keys for - * reverse lookup, \c NULL is returned. */ -const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx) { - if (! t->storekeys) { - return NULL; - } - return igraph_strvector_get(&t->keys, idx); +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. - * - * \param t The trie. - * \return The size of the trie, i.e. one larger than the maximum index. */ -igraph_integer_t igraph_trie_size(igraph_trie_t *t) { +long int igraph_trie_size(igraph_trie_t *t) { return t->maxvalue + 1; } /* Hmmm, very dirty.... */ -/** - * \ingroup igraphtrie - * \brief Retrieves all the keys from the trie. - * - * - * Note that the returned pointer is a \em borrowed reference into the internal - * string vector of the trie. Do \em not modify it and do \em not use it after - * the trie was destroyed. - * - * \param t The trie. - * \return The borrowed reference. - */ - -const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t) { - return &t->keys; +int igraph_trie_getkeys(igraph_trie_t *t, const igraph_strvector_t **strv) { + *strv = &t->keys; + return 0; } diff --git a/src/vendor/cigraph/src/core/trie.h b/src/vendor/cigraph/src/core/trie.h index 9b1e23d7be5..d5f87ffc4c9 100644 --- a/src/vendor/cigraph/src/core/trie.h +++ b/src/vendor/cigraph/src/core/trie.h @@ -39,33 +39,33 @@ __BEGIN_DECLS typedef struct s_igraph_trie_node { igraph_strvector_t strs; igraph_vector_ptr_t children; - igraph_vector_int_t values; + igraph_vector_t values; } igraph_trie_node_t; typedef struct s_igraph_trie { - igraph_trie_node_t node; - igraph_integer_t maxvalue; + 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_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 igraph_error_t igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys); +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 igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_get_len(igraph_trie_t *t, const char *key, igraph_integer_t length, - igraph_integer_t *id); -IGRAPH_PRIVATE_EXPORT const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx); -IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_trie_size(igraph_trie_t *t); - -const igraph_strvector_t* igraph_i_trie_borrow_keys(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 diff --git a/src/vendor/cigraph/src/core/typed_list.pmt b/src/vendor/cigraph/src/core/typed_list.pmt deleted file mode 100644 index 75aaa80e8a4..00000000000 --- a/src/vendor/cigraph/src/core/typed_list.pmt +++ /dev/null @@ -1,1099 +0,0 @@ -/* -*- 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 - -*/ - -#include /* memmove */ - -#include "igraph_error.h" -#include "igraph_memory.h" -#include "igraph_qsort.h" - -#if defined(VECTOR_LIST) - /* It was indicated that every item in a list is a vector of the base type - * so let's define ITEM_TYPE appropriately */ - #define ITEM_TYPE BASE_VECTOR - - /* Define the macro that creates the name of a function that refers to a single - * _item_ in the vector */ - #if defined(BASE_IGRAPH_REAL) - #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector,f) - #elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector_bool,f) - #else - #define ITEM_FUNCTION(f) CONCAT3(igraph_vector,SHORT,f) - #endif -#elif defined(MATRIX_LIST) - /* It was indicated that every item in a list is a matrix of the base type - * so let's define ITEM_TYPE appropriately */ - #define ITEM_TYPE BASE_MATRIX - - /* Define the macro that creates the name of a function that refers to a single - * _item_ in the matrix */ - #if defined(BASE_IGRAPH_REAL) - #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix,f) - #elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix_bool,f) - #else - #define ITEM_FUNCTION(f) CONCAT3(igraph_matrix,SHORT,f) - #endif -#else - #define ITEM_TYPE BASE - - /* Define the macro that creates the name of a function that refers to a single - * _item_ in the vector */ - #if defined(BASE_GRAPH) - #define ITEM_FUNCTION(f) CONCAT2x(igraph,f) - #endif -#endif - -static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item); -static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source); -static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item); - -static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); -static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); -static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* list); -static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2); - -/** - * \ingroup vector_list - * \function igraph_vector_list_init - * \brief Initializes a list of vectors (constructor). - * - * - * This function constructs a list of vectors of the given size, and initializes - * each vector in the newly created list to become an empty vector. - * - * - * Vector objects initialized by this function are \em owned by the list, and - * they will be destroyed automatically when the list is destroyed with - * \ref igraph_vector_list_destroy(). - * - * \param v Pointer to a not yet initialized list of vectors. - * \param size The size of the list. - * \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 and initialize the corresponding vectors; - * n is the number of elements. - */ - -igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size) { - igraph_integer_t alloc_size = size > 0 ? size : 1; - IGRAPH_ASSERT(size >= 0); - v->stor_begin = IGRAPH_CALLOC(alloc_size, ITEM_TYPE); - if (v->stor_begin == 0) { - IGRAPH_ERROR("Cannot initialize list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - v->stor_end = v->stor_begin + alloc_size; - v->end = v->stor_begin + size; - - IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin, v->end)); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_destroy - * \brief Destroys a list of vectors object. - * - * - * All lists initialized by \ref igraph_vector_list_init() should be properly - * destroyed by this function. A destroyed list of vectors needs to be - * reinitialized by \ref igraph_vector_list_init() if you want to use it again. - * - * - * Vectors that are in the list when it is destroyed are also destroyed - * implicitly. - * - * \param v Pointer to the (previously initialized) list object to - * destroy. - * - * Time complexity: operating system dependent. - */ - -void FUNCTION(destroy)(TYPE* v) { - IGRAPH_ASSERT(v != 0); - - if (v->stor_begin != 0) { - FUNCTION(clear)(v); - IGRAPH_FREE(v->stor_begin); - v->stor_begin = NULL; - } -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_capacity - * \brief Returns the allocated capacity of the list. - * - * Note that this might be different from the size of the list (as - * queried by \ref igraph_vector_list_size()), and specifies how many vectors - * the list can hold, without reallocation. - * - * \param v Pointer to the (previously initialized) list object to query. - * \return The allocated capacity. - * - * \sa \ref igraph_vector_list_size(). - * - * Time complexity: O(1). - */ - -igraph_integer_t FUNCTION(capacity)(const TYPE* v) { - return v->stor_end - v->stor_begin; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_reserve - * \brief Reserves memory for a list. - * - * - * \a igraph lists are flexible, they can grow and shrink. Growing - * however occasionally needs the data in the list to be copied. - * In order to avoid this, you can call this function to reserve space for - * future growth of the list. - * - * - * Note that this function does \em not change the size of the list, neither - * does it initialize any new vectors. Let us see a small example to clarify - * things: if you reserve space for 100 elements and the size of your - * list was (and still is) 60, then you can surely add additional 40 - * new vectors to your list before it will be copied. - * \param v The list object. - * \param capacity The new \em allocated size of the list. - * \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 list. - */ - -igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity) { - igraph_integer_t current_capacity; - ITEM_TYPE *tmp; - - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - IGRAPH_ASSERT(capacity >= 0); - - current_capacity = FUNCTION(capacity)(v); - - if (capacity <= current_capacity) { - return IGRAPH_SUCCESS; - } - - tmp = IGRAPH_REALLOC(v->stor_begin, capacity, ITEM_TYPE); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for list."); - - v->end = tmp + (v->end - v->stor_begin); - v->stor_begin = tmp; - v->stor_end = v->stor_begin + capacity; - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_empty - * \brief Decides whether the size of the list is zero. - * - * \param v The list object. - * \return Non-zero number (true) if the size of the list is zero and - * zero (false) otherwise. - * - * Time complexity: O(1). - */ - -igraph_bool_t FUNCTION(empty)(const TYPE* v) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - return v->stor_begin == v->end; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_size - * \brief Returns the size (=length) of the vector. - * - * \param v The list object - * \return The size of the list. - * - * Time complexity: O(1). - */ - -igraph_integer_t FUNCTION(size)(const TYPE* v) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - return v->end - v->stor_begin; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_resize - * \brief Resize the list of vectors. - * - * - * Note that this function does not free any memory, just sets the - * size of the list to the given one. It can on the other hand - * allocate more memory if the new size is larger than the previous - * one. - * - * - * When the new size is larger than the current size, the newly added - * vectors in the list are initialized to empty vectors. When the new - * size is smaller than the current size, the vectors that were removed - * from the end of the list are destroyed automatically. - * - * \param v The list object - * \param new_size The new size of the list. - * \return Error code, - * \c IGRAPH_ENOMEM if there is not enough - * memory. Note that this function \em never returns an error - * if the list is made smaller. - * \sa \ref igraph_vector_list_reserve() for allocating memory for future - * extensions of a list. - * - * Time complexity: O(m) if the new size is smaller (m is the number of items - * that were removed from the list), operating system dependent if the new - * size is larger. In the latter case it is usually around O(n), where n is the - * new size of the vector. - */ -igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size) { - igraph_integer_t old_size; - - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - - IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); - - old_size = FUNCTION(size)(v); - - if (old_size < new_size) { - IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin + old_size, v->stor_begin + new_size)); - } else if (old_size > new_size) { - INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin + new_size, v->stor_begin + old_size); - } - - v->end = v->stor_begin + new_size; - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector - * \function igraph_vector_list_clear - * \brief Removes all elements from a list of vectors. - * - * - * This function sets the size of the list to zero, and it also destroys all - * the vectors that were placed in the list before clearing it. - * - * \param v The list object. - * - * Time complexity: O(n), n is the number of items being deleted. - */ -void FUNCTION(clear)(TYPE* v) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin, v->end); - v->end = v->stor_begin; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_get_ptr - * \brief Retrieve the address of a vector in the vector list. - * \param v The list object. - * \param pos The position of the vector in the list. The position of the first - * vector is zero. - * \return A pointer to the vector. It remains valid as long as the underlying - * list of vectors is not modified. - * - * Time complexity: O(1). - */ -ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - return v->stor_begin + pos; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_set - * \brief Sets the vector at the given index in the list. - * - * - * This function destroys the vector that is already at the given index \p pos - * in the list, and replaces it with the vector pointed to by \p e. - * The ownership of the vector pointed to by \p e is taken by the list so - * the user is not responsible for destroying \p e any more; it will be - * destroyed when the list itself is destroyed or if \p e gets removed from the - * list without passing on the ownership to somewhere else. - * - * \param v The list object. - * \param pos The index to modify in the list. - * \param e The vector to set in the list. - * - * Time complexity: O(1). - */ -void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { - INTERNAL_FUNCTION(destroy_item)(v->stor_begin + pos); - v->stor_begin[pos] = *e; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_replace - * \brief Replaces the vector at the given index in the list with another one. - * - * - * This function replaces the vector that is already at the given index \p pos - * in the list with the vector pointed to by \p e. The ownership of the vector - * pointed to by \p e is taken by the list so the user is not responsible for - * destroying \p e any more. At the same time, the ownership of the vector that - * \em was in the list at position \p pos will be transferred to the caller and - * \p e will be updated to point to it, so the caller becomes responsible for - * destroying it when it does not need the vector any more. - * - * \param v The list object. - * \param pos The index to modify in the list. - * \param e The vector to swap with the one already in the list. - * - * Time complexity: O(1). - */ -void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { - ITEM_TYPE old_value = *(FUNCTION(get_ptr)(v, pos)); - v->stor_begin[pos] = *e; - *e = old_value; -} - -/** - * \function igraph_vector_list_swap - * \brief Swaps all elements of two vector lists. - * - * \param v1 The first list. - * \param v2 The second list. - * \return Error code. - * - * Time complexity: O(1). - */ - -igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2) { - TYPE tmp; - - tmp = *v1; - *v1 = *v2; - *v2 = tmp; - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_vector_list_swap_elements - * \brief Swap two elements in a vector list. - * - * Note that currently no range checking is performed. - * \param v The input list. - * \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). - */ - -igraph_error_t FUNCTION(swap_elements)(TYPE *v1, igraph_integer_t i, igraph_integer_t j) { - ITEM_TYPE tmp = v1->stor_begin[i]; - v1->stor_begin[i] = v1->stor_begin[j]; - v1->stor_begin[j] = tmp; - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_tail_ptr - * \brief Retrieve the address of the last vector in the vector list. - * \param v The list object. - * \return A pointer to the last vector in the list, or \c NULL if the list - * is empty. - * - * Time complexity: O(1). - */ -ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v) { - igraph_integer_t size = FUNCTION(size)(v); - return size > 0 ? FUNCTION(get_ptr)(v, size - 1) : 0; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_discard - * \brief Discard the item at the given index in the vector list. - * - * - * This function removes the vector at the given index from the list, and - * moves all subsequent items in the list by one slot to the left to fill - * the gap. The vector that was removed from the list is destroyed automatically. - * - * \param v The list object. - * \param index Index of the item to be discarded and destroyed. - * \sa \ref igraph_vector_list_discard_fast() if you do not care about the - * order of the items in the list, \ref igraph_vector_list_remove() if you - * want to gain ownership of the item that was removed instead of destroying it. - * - * Time complexity: O(n), where n is the number of items in the list. - */ -void FUNCTION(discard)(TYPE* v, igraph_integer_t index) { - igraph_integer_t size = FUNCTION(size)(v); - - if (size > 0) { - INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); - memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); - v->end -= 1; - } -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_discard_back - * \brief Discard the last item in the vector list. - * - * - * This function removes the last vector from the list and destroys it. - * - * \param v The list object. - * - * Time complexity: O(1). - */ -void FUNCTION(discard_back)(TYPE* v) { - igraph_integer_t size = FUNCTION(size)(v); - if (size > 0) { - INTERNAL_FUNCTION(destroy_item)(v->end - 1); - v->end -= 1; - } -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_discard_fast - * \brief Discard the item at the given index in the vector list and move the last item to its place. - * - * - * This function removes the vector at the given index from the list, and - * moves the last item in the list to \p index to fill the gap. The vector that - * was removed from the list is destroyed automatically. - * - * \param v The list object. - * \param index Index of the item to be discarded and destroyed. - * \sa \ref igraph_vector_list_discard() if you want to preserve the order of the - * items in the list, \ref igraph_vector_list_remove_fast() if you want to gain - * ownership of the item that was removed instead of destroying it. - * - * Time complexity: O(1). - */ -void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index) { - igraph_integer_t size = FUNCTION(size)(v); - - if (size > 0) { - INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); - v->end -= 1; - v->stor_begin[index] = *(v->end); - } -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_push_back - * \brief Append an existing vector to the list, transferring ownership. - * - * - * This function resizes the list to be one element longer, and sets the very last - * element in the list to the specified vector \p e . The list takes ownership - * of the vector so the user is not responsible for freeing \p e any more; - * the vector will be destroyed when the list itself is destroyed or if \p e gets - * removed from the list without passing on the ownership to somewhere else. - * - * \param v The list object. - * \param e Pointer to the vector to append to the list. - * \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_list_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). - */ -igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e) { - IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); - *(v->end) = *e; - v->end += 1; - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_push_back_copy - * \brief Append the copy of a vector to the list. - * - * - * This function resizes the list to be one element longer, and copies the - * specified vector given as an argument to the last element. The newly added - * element is owned by the list, but the ownership of the original vector is - * retained at the caller. - * - * \param v The list object. - * \param e Pointer to the vector to copy to the end of the list. - * \return Error code: - * \c IGRAPH_ENOMEM: not enough memory. - * - * Time complexity: same as \ref igraph_vector_list_push_back() plus the time - * needed to copy the vector (which is O(n) for n elements in the vector). - */ -igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e) { - ITEM_TYPE copy; - IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); - IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); - IGRAPH_CHECK(FUNCTION(push_back)(v, ©)); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_push_back_new - * \brief Append a new vector to the list. - * - * - * This function resizes the list to be one element longer. The newly added - * element will be an empty vector that is owned by the list. A pointer to - * the newly added element is returned in the last argument if it is not - * \c NULL . - * - * \param v The list object. - * \param result Pointer to a vector pointer; this will be updated to point to - * the newly added vector. May be \c NULL if you do not need a pointer - * to the newly added vector. - * \return Error code: - * \c IGRAPH_ENOMEM: not enough memory. - * - * Time complexity: same as \ref igraph_vector_list_push_back(). - */ -igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** e) { - IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); - IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, v->end)); - if (e) { - *e = v->end; - } - v->end += 1; - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_insert - * \brief Insert an existing vector into the list, transferring ownership. - * - * - * This function inserts \p e into the list at the given index, moving other - * items towards the end of the list as needed. The list takes ownership - * of the vector so the user is not responsible for freeing \p e any more; - * the vector will be destroyed when the list itself is destroyed or if \p e gets - * removed from the list without passing on the ownership to somewhere else. - * - * \param v The list object. - * \param pos The position where the new element is to be inserted. - * \param e Pointer to the vector to insert into the list. - * \return Error code: - * \c IGRAPH_ENOMEM: not enough memory. - * - * Time complexity: O(n). - */ -igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { - igraph_integer_t size = FUNCTION(size)(v); - IGRAPH_ASSERT(0 <= pos && pos <= size); - IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); - if (pos < size) { - memmove(v->stor_begin + pos + 1, v->stor_begin + pos, sizeof(ITEM_TYPE) * (size - pos)); - } - v->end += 1; - v->stor_begin[pos] = *e; - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_insert_copy - * \brief Insert the copy of a vector to the list. - * - * - * This function inserts a copy of \p e into the list at the given index, moving - * other items towards the end of the list as needed. The newly added - * element is owned by the list, but the ownership of the original vector is - * retained at the caller. - * - * \param v The list object. - * \param pos The position where the new element is to be inserted. - * \param e Pointer to the vector to copy to the end of the list. - * \return Error code: - * \c IGRAPH_ENOMEM: not enough memory. - * - * Time complexity: same as \ref igraph_vector_list_insert() plus the time - * needed to copy the vector (which is O(n) for n elements in the vector). - */ -igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e) { - ITEM_TYPE copy; - IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); - IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); - IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_insert_new - * \brief Insert a new vector into the list. - * - * - * This function inserts a newly created empty vector into the list at the given - * index, moving other items towards the end of the list as needed. The newly - * added vector is owned by the list. A pointer to the new element is returned - * in the last argument if it is not \c NULL . - * - * \param v The list object. - * \param pos The position where the new element is to be inserted. - * \param result Pointer to a vector pointer; this will be updated to point to - * the newly added vector. May be \c NULL if you do not need a pointer - * to the newly added vector. - * \return Error code: - * \c IGRAPH_ENOMEM: not enough memory. - * - * Time complexity: same as \ref igraph_vector_list_push_back(). - */ -igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** e) { - ITEM_TYPE copy; - IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, ©)); - IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); - IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); - IGRAPH_FINALLY_CLEAN(1); - if (e) { - *e = FUNCTION(get_ptr)(v, pos); - } - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_remove - * \brief Remove the item at the given index from the vector list and transfer ownership to the caller. - * - * - * This function removes the vector at the given index from the list, and - * moves all subsequent items in the list by one slot to the left to fill - * the gap. The vector that was removed from the list is returned in \p e - * and its ownership is passed back to the caller; in other words, the caller - * becomes responsible for destroying the vector when it is not needed any more. - * - * \param v The list object. - * \param index Index of the item to be removed. - * \param result Pointer to an \c igraph_vector_t object; it will be updated to the - * item that was removed from the list. Ownership of this vector is - * passed on to the caller. It is an error to supply a null pointer here. - * \sa \ref igraph_vector_list_discard() if you are not interested in the item - * that was removed, \ref igraph_vector_list_remove_fast() if you do not care - * about the order of the items in the list. - * - * Time complexity: O(n), where n is the number of items in the list. - */ -igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { - igraph_integer_t size = FUNCTION(size)(v); - - IGRAPH_ASSERT(result != 0); - - if (index < 0 || index >= size) { - IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); - } - - *result = *(FUNCTION(get_ptr)(v, index)); - - memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); - v->end -= 1; - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_pop_back - * \brief Remove the last item from the vector list and transfer ownership to the caller. - * - * - * This function removes the last vector from the list. The vector that was - * removed from the list is returned and its ownership is passed back to the - * caller; in other words, the caller becomes responsible for destroying - * the vector when it is not needed any more. - * - * - * It is an error to call this function with an empty vector. - * - * \param v The list object. - * \param result Pointer to an \c igraph_vector_t object; it will be updated to the - * item that was removed from the list. Ownership of this vector is - * passed on to the caller. - * - * Time complexity: O(1). - */ -ITEM_TYPE FUNCTION(pop_back)(TYPE* v) { - IGRAPH_ASSERT(!FUNCTION(empty)(v)); - v->end -= 1; - return *(v->end); -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_remove_fast - * \brief Remove the item at the given index in the vector list, move the last item to its place and transfer ownership to the caller. - * - * - * This function removes the vector at the given index from the list, - * moves the last item in the list to \p index to fill the gap, and then - * transfers ownership of the removed vector back to the caller; in other words, - * the caller becomes responsible for destroying the vector when it is not - * needed any more. - * - * \param v The list object. - * \param index Index of the item to be removed. - * \param result Pointer to an \c igraph_vector_t object; it will be updated to the - * item that was removed from the list. Ownership of this vector is - * passed on to the caller. It is an error to supply a null pointer here. - * \sa \ref igraph_vector_list_remove() if you want to preserve the order of the - * items in the list, \ref igraph_vector_list_discard_fast() if you are not - * interested in the item that was removed. - * - * Time complexity: O(1). - */ -igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { - igraph_integer_t size = FUNCTION(size)(v); - - IGRAPH_ASSERT(result != 0); - - if (index < 0 || index >= size) { - IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); - } - - *result = *(FUNCTION(get_ptr)(v, index)); - - v->end -= 1; - v->stor_begin[index] = *(v->end); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_permute - * \brief Permutes the elements of a list in place according to an index vector. - * - * - * This function takes a list \c v and a corresponding index vector \c index, - * and permutes the elements of \c v such that \c v[index[i]] is moved to become - * \c v[i] after the function is executed. - * - * - * It is an error to call this function with an index vector that does not - * represent a valid permutation. Each element in the index vector must be - * between 0 and the length of the list minus one (inclusive), and each such - * element must appear only once. The function does not attempt to validate the - * index vector. Memory may be leaked if the index vector does not satisfy these - * conditions. - * - * - * The index vector that this function takes is compatible with the index vector - * returned from \ref igraph_vector_list_sort_ind(); passing in the index vector - * from \ref igraph_vector_list_sort_ind() will sort the original vector. - * - * \param v the list to permute - * \param index the index vector - * - * Time complexity: O(n), the number of items in the list. - */ -igraph_error_t FUNCTION(permute)(TYPE* v, const igraph_vector_int_t* index) { - ITEM_TYPE* work; - igraph_integer_t i, size; - - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - IGRAPH_ASSERT(index != NULL); - IGRAPH_ASSERT(index->stor_begin != NULL); - - size = igraph_vector_int_size(index); - IGRAPH_ASSERT(FUNCTION(size)(v) == size); - - work = IGRAPH_CALLOC(size, ITEM_TYPE); - if (work == 0) { - IGRAPH_ERROR("Cannot permute list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - - for (i = 0; i < size; i++) { - work[i] = v->stor_begin[VECTOR(*index)[i]]; - } - - memcpy(v->stor_begin, work, sizeof(ITEM_TYPE) * size); - - IGRAPH_FREE(work); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_sort - * \brief Sorts the elements of the list into ascending order. - * - * \param v Pointer to an initialized list object. - * \param cmp A comparison function that takes pointers to two vectors and - * returns zero if the two vectors are considered equal, any negative - * number if the first vector is smaller and any positive number if the - * second vector is smaller. - * \return Error code. - * - * Time complexity: O(n log n) for n elements. - */ -void FUNCTION(sort)(TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - igraph_qsort( - v->stor_begin, FUNCTION(size)(v), sizeof(ITEM_TYPE), - (int(*)(const void*, const void*))cmp - ); -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_sort_ind - * \brief Returns a permutation of indices that sorts the list. - * - * Takes an unsorted list \p v as input and computes an array of - * indices \p inds such that v[ inds[i] ], with i increasing from 0, is - * an ordered array according to the comparison function \p cmp. The order of - * indices for identical elements is not defined. - * - * \param v the list to be sorted - * \param inds the output array of indices. This must be initialized, - * but will be resized - * \param cmp A comparison function that takes pointers to two vectors and - * returns zero if the two vectors are considered equal, any negative - * number if the first vector is smaller and any positive number if the - * second vector is smaller. - * \return Error code. - * - * Time complexity: O(n log n) for n elements. - */ -igraph_error_t FUNCTION(sort_ind)( - TYPE *v, igraph_vector_int_t *inds, - int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) -) { - igraph_integer_t i, n = FUNCTION(size)(v); - ITEM_TYPE **vind, *first; - - IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); - if (n == 0) { - return IGRAPH_SUCCESS; - } - - vind = IGRAPH_CALLOC(n, ITEM_TYPE*); - if (vind == 0) { - IGRAPH_ERROR("igraph_vector_list_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - for (i = 0; i < n; i++) { - vind[i] = v->stor_begin + i; - } - first = vind[0]; - igraph_qsort_r( - vind, n, sizeof(ITEM_TYPE*), (void*) cmp, - (int(*)(void*, const void*, const void*)) INTERNAL_FUNCTION(sort_ind_cmp) - ); - for (i = 0; i < n; i++) { - VECTOR(*inds)[i] = vind[i] - first; - } - IGRAPH_FREE(vind); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector_list - * \function igraph_vector_list_remove_consecutive_duplicates - * \brief Removes consecutive duplicates from a vector list. - * - * Removes consecutive duplicate vectors from the list. Optionally, a custom - * equivalence relation may be used to determine when two vectors are - * considered to be the same. - * - * - * An efficient way to remove all duplicates, not just consecutive ones, - * is to first sort the vector list using \ref igraph_vector_list_sort(), - * then use this function. This will of course re-order the list. - * - * \param v The list to remove consecutive duplicates from. - * \param eq A comparison function that takes pointers to two vectors and - * returns true if they are equivalent. It is assumed that it implements - * a transitive, but not necessarily symmetric relation. - * Use \ref igraph_vector_all_e() to consider vector equivalent only - * when their contents are identical. - * - * \sa \ref igraph_vector_list_sort() - * - * Time complexity: O(n), the number of items in the list. - */ -void FUNCTION(remove_consecutive_duplicates)( - TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*) -) { - igraph_integer_t i, j, n = FUNCTION(size)(v); - ITEM_TYPE *p = v->stor_begin; - - if (n < 2) { - return; - } - - for (i=0, j=0; i < n-1; ++i) { - if (eq(&p[i], &p[i+1])) { - INTERNAL_FUNCTION(destroy_item)(&p[i]); - } else { - p[j++] = p[i]; - } - } - p[j++] = p[n-1]; - - v->end = p + j; -} - -/** - * \function igraph_vector_list_reverse - * \brief Reverses the elements of a vector list. - * - * The first element will be last, the last element will be - * first, etc. - * \param v The input vector list. - * \return Error code, currently always \c IGRAPH_SUCCESS. - * - * Time complexity: O(n), the number of elements. - */ -igraph_error_t FUNCTION(reverse)(TYPE *v) { - igraph_integer_t n = FUNCTION(size)(v), n2 = n / 2; - igraph_integer_t i, j; - for (i = 0, j = n - 1; i < n2; i++, j--) { - ITEM_TYPE tmp; - tmp = VECTOR(*v)[i]; - VECTOR(*v)[i] = VECTOR(*v)[j]; - VECTOR(*v)[j] = tmp; - } - return IGRAPH_SUCCESS; -} - -/* ************************************************************************ */ - -#ifndef CUSTOM_INIT_DESTROY - -static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item) { - IGRAPH_UNUSED(list); - return ITEM_FUNCTION(init)(item, 0); -} - -static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source) { - return ITEM_FUNCTION(init_copy)(dest, source); -} - -static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item) { - ITEM_FUNCTION(destroy)(item); -} - -#endif - -/* ************************************************************************ */ - -static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { - ITEM_TYPE* current; - igraph_error_t retval; - - for (current = start; current < end; current++) { - retval = INTERNAL_FUNCTION(init_item)(list, current); - if (retval) { - INTERNAL_FUNCTION(destroy_slice)(list, start, current); - IGRAPH_CHECK(retval); - } - } - - return IGRAPH_SUCCESS; -} - -static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { - IGRAPH_UNUSED(list); - for (; start < end; start++) { - INTERNAL_FUNCTION(destroy_item)(start); - } -} - -/** - * Ensures that the vector has at least one extra slot at the end of its - * allocated storage area. - */ -static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* v) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - - if (v->stor_end == v->end) { - igraph_integer_t old_size = FUNCTION(size)(v); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot add new item to list, already at maximum size.", IGRAPH_EOVERFLOW); - } - if (new_size == 0) { - new_size = 1; - } - IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); - } - - return IGRAPH_SUCCESS; -} - -/** - * Helper function passed to qsort from igraph_vector_qsort_ind - */ -static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2) { - int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) = (int (*)(const ITEM_TYPE*, const ITEM_TYPE*)) thunk; - ITEM_TYPE **pa = (ITEM_TYPE **) p1; - ITEM_TYPE **pb = (ITEM_TYPE **) p2; - return cmp(*pa, *pb); -} - -#undef ITEM_FUNCTION diff --git a/src/vendor/cigraph/src/core/vector.c b/src/vendor/cigraph/src/core/vector.c index 92e0dad5ba2..68512db3581 100644 --- a/src/vendor/cigraph/src/core/vector.c +++ b/src/vendor/cigraph/src/core/vector.c @@ -21,11 +21,10 @@ */ +#include "igraph_error.h" +#include "igraph_types.h" #include "igraph_vector.h" - #include "igraph_complex.h" -#include "igraph_types.h" -#include "igraph_nongraph.h" #include @@ -35,6 +34,18 @@ #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" @@ -59,48 +70,51 @@ #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 an integer vector by flooring each element. + * \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 integer vector. The result will be stored here. + * \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. */ -igraph_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to) { - igraph_integer_t i, n = igraph_vector_size(from); +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_int_resize(to, n)); + IGRAPH_CHECK(igraph_vector_long_resize(to, n)); for (i = 0; i < n; i++) { - VECTOR(*to)[i] = floor(VECTOR(*from)[i]); + VECTOR(*to)[i] = (long int) floor(VECTOR(*from)[i]); } - return IGRAPH_SUCCESS; } -igraph_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to) { - igraph_integer_t i, n = igraph_vector_size(from); +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_int_resize(to, n)); + IGRAPH_CHECK(igraph_vector_long_resize(to, n)); for (i = 0; i < n; i++) { - VECTOR(*to)[i] = round(VECTOR(*from)[i]); + VECTOR(*to)[i] = (long int) round(VECTOR(*from)[i]); } - - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_vector_order2(igraph_vector_t *v) { +int igraph_vector_order2(igraph_vector_t *v) { + igraph_indheap_t heap; - IGRAPH_CHECK(igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v))); + igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v)); IGRAPH_FINALLY(igraph_indheap_destroy, &heap); igraph_vector_clear(v); @@ -111,22 +125,22 @@ igraph_error_t igraph_vector_order2(igraph_vector_t *v) { igraph_indheap_destroy(&heap); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup vector - * \function igraph_vector_int_pair_order - * \brief Calculates the order of the elements in a pair of integer vectors of equal length. + * \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 \ref igraph_vector_int_t object. - * \param v2 A secondary key, another \ref igraph_vector_int_t object. - * \param res An initialized \ref igraph_vector_int_t object, it will be - * resized to match the size of \p v. The result of the computation will - * be stored here. + * \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 @@ -134,23 +148,23 @@ igraph_error_t igraph_vector_order2(igraph_vector_t *v) { * Time complexity: O() */ -igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, - const igraph_vector_int_t* v2, - igraph_vector_int_t* res, igraph_integer_t nodes) { - igraph_integer_t edges = igraph_vector_int_size(v); - igraph_vector_int_t ptr; - igraph_vector_int_t rad; - igraph_integer_t i, j; +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_INT_INIT_FINALLY(&ptr, nodes + 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, edges); - IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + 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++) { - igraph_integer_t radix = VECTOR(*v2)[i]; + long int radix = (long int) v2->stor_begin[i]; if (VECTOR(ptr)[radix] != 0) { VECTOR(rad)[i] = VECTOR(ptr)[radix]; } @@ -160,21 +174,21 @@ igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, j = 0; for (i = 0; i < nodes + 1; i++) { if (VECTOR(ptr)[i] != 0) { - igraph_integer_t next = VECTOR(ptr)[i] - 1; - VECTOR(*res)[j++] = next; + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; while (VECTOR(rad)[next] != 0) { - next = VECTOR(rad)[next] - 1; - VECTOR(*res)[j++] = next; + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; } } } - igraph_vector_int_null(&ptr); - igraph_vector_int_null(&rad); + igraph_vector_null(&ptr); + igraph_vector_null(&rad); for (i = 0; i < edges; i++) { - igraph_integer_t edge = VECTOR(*res)[edges - i - 1]; - igraph_integer_t radix = VECTOR(*v)[edge]; + 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]; } @@ -184,39 +198,38 @@ igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, j = 0; for (i = 0; i < nodes + 1; i++) { if (VECTOR(ptr)[i] != 0) { - igraph_integer_t next = VECTOR(ptr)[i] - 1; - VECTOR(*res)[j++] = next; + long int next = (long int) VECTOR(ptr)[i] - 1; + res->stor_begin[j++] = next; while (VECTOR(rad)[next] != 0) { - next = VECTOR(rad)[next] - 1; - VECTOR(*res)[j++] = next; + next = (long int) VECTOR(rad)[next] - 1; + res->stor_begin[j++] = next; } } } - igraph_vector_int_destroy(&ptr); - igraph_vector_int_destroy(&rad); + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, - igraph_vector_int_t* res, - igraph_integer_t nodes) { - igraph_integer_t edges = igraph_vector_int_size(v); - igraph_vector_int_t ptr; - igraph_vector_int_t rad; - igraph_integer_t i, j; +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_INT_INIT_FINALLY(&ptr, nodes + 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, edges); - IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + 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++) { - igraph_integer_t radix = v->stor_begin[i]; + long int radix = (long int) v->stor_begin[i]; if (VECTOR(ptr)[radix] != 0) { VECTOR(rad)[i] = VECTOR(ptr)[radix]; } @@ -226,149 +239,153 @@ igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, j = 0; for (i = 0; i < nodes + 1; i++) { if (VECTOR(ptr)[i] != 0) { - igraph_integer_t next = VECTOR(ptr)[i] - 1; + long int next = (long int) VECTOR(ptr)[i] - 1; res->stor_begin[j++] = next; while (VECTOR(rad)[next] != 0) { - next = VECTOR(rad)[next] - 1; + next = (long int) VECTOR(rad)[next] - 1; res->stor_begin[j++] = next; } } } - igraph_vector_int_destroy(&ptr); - igraph_vector_int_destroy(&rad); + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_vector_rank( - const igraph_vector_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { +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_vector_int_t rad; - igraph_vector_int_t ptr; - igraph_integer_t edges = igraph_vector_size(v); - igraph_integer_t i, c = 0; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); - IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, edges); + 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++) { - igraph_integer_t elem = VECTOR(*v)[i]; - VECTOR(ptr)[i] = VECTOR(rad)[elem]; - VECTOR(rad)[elem] = i + 1; + 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; } - for (i = 0; i < nodes; i++) { - igraph_integer_t p = VECTOR(rad)[i]; - while (p != 0) { - VECTOR(*res)[p - 1] = c++; - p = VECTOR(ptr)[p - 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_int_destroy(&ptr); - igraph_vector_int_destroy(&rad); + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + + return 0; } -igraph_error_t igraph_vector_int_rank( - const igraph_vector_int_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { +int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, + long int nodes) { - igraph_vector_int_t rad; - igraph_vector_int_t ptr; - igraph_integer_t edges = igraph_vector_int_size(v); - igraph_integer_t i, c = 0; + igraph_vector_t rad; + igraph_vector_t ptr; + long int edges = igraph_vector_size(v); + long int i, c = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, edges); - IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); + 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++) { - igraph_integer_t elem = VECTOR(*v)[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++) { - igraph_integer_t p = VECTOR(rad)[i]; + long int p = (long int) VECTOR(rad)[i]; while (p != 0) { VECTOR(*res)[p - 1] = c++; - p = VECTOR(ptr)[p - 1]; + p = (long int) VECTOR(ptr)[p - 1]; } } - igraph_vector_int_destroy(&ptr); - igraph_vector_int_destroy(&rad); + igraph_vector_destroy(&ptr); + igraph_vector_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup vector - * \function igraph_vector_complex_real - * \brief Gives the real part of a complex vector. - * - * \param v Pointer to a complex vector. - * \param real Pointer to an initialized vector. The result will be stored here. - * \return Error code. - * - * Time complexity: O(n), n is the number of elements in the vector. - */ +#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 -igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, +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) { - igraph_integer_t i, n = igraph_vector_complex_size(v); + 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 IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup vector - * \function igraph_vector_complex_imag - * \brief Gives the imaginary part of a complex vector. - * - * \param v Pointer to a complex vector. - * \param real Pointer to an initialized vector. The result will be stored here. - * \return Error code. - * - * Time complexity: O(n), n is the number of elements in the vector. - */ - -igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, +int igraph_vector_complex_imag(const igraph_vector_complex_t *v, igraph_vector_t *imag) { - igraph_integer_t i, n = igraph_vector_complex_size(v); + 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 IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup vector - * \function igraph_vector_complex_realimag - * \brief Gives the real and imaginary parts of a complex vector. - * - * \param v Pointer to a complex vector. - * \param real Pointer to an initialized vector. The real part will be stored here. - * \param imag Pointer to an initialized vector. The imaginary part will be stored here. - * \return Error code. - * - * Time complexity: O(n), n is the number of elements in the vector. - */ - -igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, +int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, igraph_vector_t *real, igraph_vector_t *imag) { - igraph_integer_t i, n = igraph_vector_complex_size(v); + 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++) { @@ -377,26 +394,13 @@ igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, VECTOR(*imag)[i] = IGRAPH_IMAG(z); } - return IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup vector - * \function igraph_vector_complex_create - * \brief Creates a complex vector from a real and imaginary part. - * - * \param v Pointer to an uninitialized complex vector. - * \param real Pointer to the real part of the complex vector. - * \param imag Pointer to the imaginary part of the complex vector. - * \return Error code. - * - * Time complexity: O(n), n is the number of elements in the vector. - */ - -igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, +int igraph_vector_complex_create(igraph_vector_complex_t *v, const igraph_vector_t *real, const igraph_vector_t *imag) { - igraph_integer_t i, n = igraph_vector_size(real); + 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); } @@ -408,26 +412,13 @@ igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, VECTOR(*v)[i] = igraph_complex(VECTOR(*real)[i], VECTOR(*imag)[i]); } - return IGRAPH_SUCCESS; + return 0; } -/** - * \ingroup vector - * \function igraph_vector_complex_create_polar - * \brief Creates a complex matrix from a magnitude and an angle. - * - * \param m Pointer to an uninitialized complex vector. - * \param r Pointer to a real vector containing magnitudes. - * \param theta Pointer to a real vector containing arguments (phase angles). - * \return Error code. - * - * Time complexity: O(n), n is the number of elements in the vector. - */ - -igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, +int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, const igraph_vector_t *r, const igraph_vector_t *theta) { - igraph_integer_t i, n = igraph_vector_size(r); + 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); } @@ -439,53 +430,13 @@ igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, VECTOR(*v)[i] = igraph_complex_polar(VECTOR(*r)[i], VECTOR(*theta)[i]); } - return IGRAPH_SUCCESS; + return 0; } -/** - * \function igraph_vector_complex_all_almost_e - * \brief Are all elements almost equal? - * - * Checks if the elements of two complex vectors are equal within a relative tolerance. - * - * \param lhs The first vector. - * \param rhs The second vector. - * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. - * \return True if the two vectors are almost equal, false if there is at least - * one differing element or if the vectors are not of the same size. - */ -igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, - const igraph_vector_complex_t *rhs, - igraph_real_t eps) { - - igraph_integer_t n = igraph_vector_complex_size(lhs); - - if (lhs == rhs) { - return true; - } - - if (igraph_vector_complex_size(rhs) != n) { - return false; - } - - for (igraph_integer_t i=0; i < n; i++) { - if (! igraph_complex_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) - return false; - } - - return true; -} - -/** - * Deprecated in favour of \ref igraph_vector_all_almost_e() which uses - * relative tolerances. Will be removed in 0.11. - * - * Checks if two vectors are equal within an absolute tolerance. - */ igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, const igraph_vector_t *rhs, igraph_real_t tol) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -493,7 +444,7 @@ igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, s = igraph_vector_size(lhs); if (s != igraph_vector_size(rhs)) { - return false; + return 0; } else { if (tol == 0) { tol = DBL_EPSILON; @@ -502,72 +453,20 @@ igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, igraph_real_t l = VECTOR(*lhs)[i]; igraph_real_t r = VECTOR(*rhs)[i]; if (l < r - tol || l > r + tol) { - return false; + return 0; } } - return true; - } -} - -/** - * \function igraph_vector_all_almost_e - * \brief Are all elements almost equal? - * - * Checks if the elements of two vectors are equal within a relative tolerance. - * - * \param lhs The first vector. - * \param rhs The second vector. - * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. - * \return True if the two vectors are almost equal, false if there is at least - * one differing element or if the vectors are not of the same size. - */ -igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, - const igraph_vector_t *rhs, - igraph_real_t eps) { - - igraph_integer_t n = igraph_vector_size(lhs); - - if (lhs == rhs) { - return true; - } - - if (igraph_vector_size(rhs) != n) { - return false; + return 1; } - - for (igraph_integer_t i=0; i < n; i++) { - if (! igraph_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) - return false; - } - - return true; } -/** - * \function igraph_vector_zapsmall - * \brief Replaces small elements of a vector by exact zeros. - * - * Vector elements which are smaller in magnitude than the given absolute - * tolerance will be replaced by exact zeros. The default tolerance - * corresponds to two-thirds of the representable digits of \type igraph_real_t, - * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. - * - * \param v The vector to process, it will be changed in-place. - * \param tol Tolerance value. Numbers smaller than this in magnitude will - * be replaced by zeros. Pass in zero to use the default tolerance. - * Must not be negative. - * \return Error code. - * - * \sa \ref igraph_vector_all_almost_e() and \ref igraph_almost_equals() to - * perform comparisons with relative tolerances. - */ -igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { - igraph_integer_t i, n = igraph_vector_size(v); +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("Tolerance must be positive or zero.", IGRAPH_EINVAL); + IGRAPH_ERROR("`tol' tolerance must be non-negative", IGRAPH_EINVAL); } if (tol == 0.0) { - tol = pow(DBL_EPSILON, 2.0/3); + tol = sqrt(DBL_EPSILON); } for (i = 0; i < n; i++) { igraph_real_t val = VECTOR(*v)[i]; @@ -575,55 +474,7 @@ igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { VECTOR(*v)[i] = 0.0; } } - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_vector_complex_zapsmall - * \brief Replaces small elements of a complex vector by exact zeros. - * - * Similarly to \ref igraph_vector_zapsmall(), small elements will be replaced - * by zeros. The operation is performed separately on the real and imaginary - * parts of the numbers. This way, complex numbers with a large real part and - * tiny imaginary part will effectively be transformed to real numbers. - * The default tolerance - * corresponds to two-thirds of the representable digits of \type igraph_real_t, - * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. - * - * \param v The vector to process, it will be changed in-place. - * \param tol Tolerance value. Real and imaginary parts smaller than this in - * magnitude will be replaced by zeros. Pass in zero to use the default - * tolerance. Must not be negative. - * \return Error code. - * - * \sa \ref igraph_vector_complex_all_almost_e() and - * \ref igraph_complex_almost_equals() to perform comparisons with relative - * tolerances. - */ -igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) { - igraph_integer_t i, n = igraph_vector_complex_size(v); - if (tol < 0.0) { - IGRAPH_ERROR("Tolerance must be positive or zero.", IGRAPH_EINVAL); - } - if (tol == 0.0) { - tol = pow(DBL_EPSILON, 2.0/3); - } - for (i = 0; i < n; i++) { - igraph_complex_t val = VECTOR(*v)[i]; - igraph_bool_t zapped = false; - if (IGRAPH_REAL(val) < tol && IGRAPH_REAL(val) > -tol) { - IGRAPH_REAL(val) = 0.0; - zapped = true; - } - if (IGRAPH_IMAG(val) < tol && IGRAPH_IMAG(val) > -tol) { - IGRAPH_IMAG(val) = 0.0; - zapped = true; - } - if (zapped) { - VECTOR(*v)[i] = val; - } - } - return IGRAPH_SUCCESS; + return 0; } /** @@ -631,17 +482,17 @@ igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph * \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 + * \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. */ -igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) +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; @@ -651,7 +502,7 @@ igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool 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 = isnan(*ptr); + *ptr_nan = igraph_is_nan(*ptr) ? 1 : 0; } return IGRAPH_SUCCESS; } @@ -661,6 +512,7 @@ igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool * \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. * @@ -673,10 +525,10 @@ igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v) IGRAPH_ASSERT(v->stor_begin != NULL); ptr = v->stor_begin; while (ptr < v->end) { - if (isnan(*ptr)) { - return true; + if (igraph_is_nan(*ptr)) { + return 1; } ptr++; } - return false; + return 0; } diff --git a/src/vendor/cigraph/src/core/vector.pmt b/src/vendor/cigraph/src/core/vector.pmt index ecc45a126f5..5548f588faf 100644 --- a/src/vendor/cigraph/src/core/vector.pmt +++ b/src/vendor/cigraph/src/core/vector.pmt @@ -26,8 +26,6 @@ #include "igraph_random.h" #include "igraph_qsort.h" -#include "math/safe_intop.h" - #include /* memcpy & co. */ #include #include /* va_start & co */ @@ -38,55 +36,46 @@ * \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 to (but much - * simpler than) the \type vector template in the C++ standard library. - * - * There are multiple variants of \type igraph_vector_t; the basic variant - * stores doubles, but there is also \type igraph_vector_int_t for integers (of - * type \type igraph_integer_t), \c igraph_vector_bool_t for booleans (of type - * \type igraph_bool_t) and so on. Vectors are used extensively in \a igraph; all - * functions that expect or return a list of numbers use \type igraph_vector_t or - * \type igraph_vector_int_t to achieve this. Integer vectors are typically used - * when the vector is supposed to hold vertex or edge identifiers, while - * \type igraph_vector_t is used when the vector is expected to hold fractional - * numbers or infinities. - * - * The \type igraph_vector_t type and its variants usually use O(n) space - * to store n elements. Sometimes they use 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 and its variants are - * indexed from zero, we follow the usual C convention here. + * 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. - * - * Almost all of the functions described below for \type igraph_vector_t - * also exist for all the other vector type variants. These variants are not - * documented separately; you can simply replace \c vector with \c vector_int, - * \c vector_bool or something similar if you need a function for another - * variant. For instance, to initialize a vector of type \type igraph_vector_int_t, - * you need to use \ref igraph_vector_int_init() and not \ref igraph_vector_init(). */ /** * \ingroup vector * \section igraph_vector_constructors_and_destructors Constructors and - * destructors + * 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_init_copy() creates a new identical copy + * \ref igraph_vector_copy() creates a new identical copy * of an already existing and initialized vector. \ref - * igraph_vector_init_array() creates a vector by copying a regular C array. - * \ref igraph_vector_init_range() creates a vector containing a regular + * 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 @@ -99,7 +88,7 @@ * \type igraph_vector_t destructor, \ref igraph_vector_destroy(). * * Note that vectors created by \ref igraph_vector_view() are special, - * you must not call \ref igraph_vector_destroy() on these. + * you mustn't call \ref igraph_vector_destroy() on these. */ /** @@ -132,23 +121,17 @@ * n is the number of elements. */ -igraph_error_t FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, igraph_integer_t size) { - igraph_integer_t alloc_size; +int FUNCTION(igraph_vector, init) (TYPE(igraph_vector)* v, int long size) { + long int alloc_size = size > 0 ? size : 1; IGRAPH_ASSERT(size >= 0); - alloc_size = size > 0 ? size : 1; - - /* When this function fails, it should leave stor_begin set to NULL, - * so that vector_destroy() is still safe to call on the vector. - * This simplifies freeing partially initialized data structures, - * such as adjacency lists, when an error occurs mid-initialization. */ v->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); - if (v->stor_begin == NULL) { - IGRAPH_ERROR("Cannot initialize vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -162,8 +145,7 @@ igraph_error_t FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, igraph_inte * 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, except - * when \p length is zero. + * \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. @@ -171,19 +153,12 @@ igraph_error_t FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, igraph_inte * Time complexity: O(1) */ -const TYPE(igraph_vector)* FUNCTION(igraph_vector, view) (const TYPE(igraph_vector) *v, - const BASE *data, igraph_integer_t length) { - const static BASE dummy = ZERO; +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; - /* When the length is zero, we allow 'data' to be NULL. - * An igraph_vector_t may never contain a NULL pointer, - * thus we use a pointer to a dummy variable in this case. */ - if (length == 0) { - data = &dummy; - } else { - IGRAPH_ASSERT(data != NULL); - } + IGRAPH_ASSERT(data != 0); v2->stor_begin = (BASE*)data; v2->stor_end = (BASE*)data + length; @@ -223,7 +198,7 @@ const TYPE(igraph_vector)* FUNCTION(igraph_vector, view) (const TYPE(igraph_vect * elements in the vector. */ -igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) { +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)); @@ -234,7 +209,7 @@ igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no } va_end(ap); - return IGRAPH_SUCCESS; + return 0; } /** @@ -262,7 +237,7 @@ igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no * complexity of the memory allocation. */ -igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, +int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, double endmark, ...) { int i = 0, n = 0; va_list ap; @@ -287,7 +262,7 @@ igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, va_end(ap); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -305,7 +280,7 @@ igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, * \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 \ref igraph_vector_init_int_end(), these are + * \sa \ref igraph_vector_init_real() and igraph_vector_init_int_end(), these are * similar functions. * * Time complexity: at least O(n) for @@ -313,7 +288,7 @@ igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, * complexity of the memory allocation. */ -igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) { +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)); @@ -324,7 +299,7 @@ igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, } va_end(ap); - return IGRAPH_SUCCESS; + return 0; } /** @@ -352,7 +327,7 @@ igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, * complexity of the memory allocation. */ -igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector) *v, int endmark, ...) { +int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, ...) { int i = 0, n = 0; va_list ap; @@ -376,7 +351,7 @@ igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector) *v, int va_end(ap); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } #endif /* ifndef BASE_COMPLEX */ @@ -389,7 +364,7 @@ igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector) *v, int * * 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_array() or + * 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. @@ -398,10 +373,8 @@ igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector) *v, int */ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { - IGRAPH_ASSERT(v != NULL); - /* vector_init() will leave stor_begin set to NULL when it fails. - * We handle these cases gracefully. */ - if (v->stor_begin != NULL) { + IGRAPH_ASSERT(v != 0); + if (v->stor_begin != 0) { IGRAPH_FREE(v->stor_begin); v->stor_begin = NULL; } @@ -410,12 +383,11 @@ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { /** * \ingroup vector * \function igraph_vector_capacity - * \brief Returns the allocated capacity of the vector. + * \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 + * 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. @@ -425,7 +397,7 @@ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { +long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { return v->stor_end - v->stor_begin; } @@ -448,7 +420,7 @@ igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) * 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 capacity The new \em allocated size of the vector. + * \param size The new \em allocated size of the vector. * \return Error code: * \c IGRAPH_ENOMEM if there is not enough memory. * @@ -457,28 +429,24 @@ igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) * is the new allocated size of the vector. */ -igraph_error_t FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, igraph_integer_t capacity) { - igraph_integer_t current_capacity; +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); - IGRAPH_ASSERT(capacity >= 0); - - current_capacity = FUNCTION(igraph_vector, capacity)(v); - - if (capacity <= current_capacity) { - return IGRAPH_SUCCESS; + if (size <= actual_size) { + return 0; } - tmp = IGRAPH_REALLOC(v->stor_begin, capacity, BASE); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for vector."); - - v->end = tmp + (v->end - v->stor_begin); + 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 + capacity; + v->stor_end = v->stor_begin + size; + v->end = v->stor_begin + actual_size; - return IGRAPH_SUCCESS; + return 0; } /** @@ -493,7 +461,7 @@ igraph_error_t FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, igraph_i * Time complexity: O(1). */ -igraph_bool_t FUNCTION(igraph_vector, empty)(const TYPE(igraph_vector)* v) { +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; @@ -510,7 +478,7 @@ igraph_bool_t FUNCTION(igraph_vector, empty)(const TYPE(igraph_vector)* v) { * Time complexity: O(1). */ -igraph_integer_t FUNCTION(igraph_vector, size)(const TYPE(igraph_vector)* v) { +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; @@ -530,7 +498,7 @@ igraph_integer_t FUNCTION(igraph_vector, size)(const TYPE(igraph_vector)* v) { * Time complexity: O(1). */ -void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v) { +void FUNCTION(igraph_vector, clear) (TYPE(igraph_vector)* v) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); v->end = v->stor_begin; @@ -561,17 +529,13 @@ void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v) { * complexity of memory allocation is at most linear.) */ -igraph_error_t FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { +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) { - /* full, allocate more storage */ - igraph_integer_t old_size = FUNCTION(igraph_vector, size)(v); - igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - if (old_size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot push to vector, already at maximum size.", IGRAPH_EOVERFLOW); - } + long int new_size = FUNCTION(igraph_vector, size)(v) * 2; if (new_size == 0) { new_size = 1; } @@ -581,7 +545,7 @@ igraph_error_t FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE *(v->end) = e; v->end += 1; - return IGRAPH_SUCCESS; + return 0; } /** @@ -598,20 +562,19 @@ igraph_error_t FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE * \param pos The position where the new element is to be inserted. * \param value The new element to be inserted. */ -igraph_error_t FUNCTION(igraph_vector, insert)( - TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value) { - igraph_integer_t size = FUNCTION(igraph_vector, size)(v); - IGRAPH_ASSERT(0 <= pos && pos <= size); - if (size == IGRAPH_INTEGER_MAX) { - IGRAPH_ERROR("Cannot insert to vector, already at maximum size.", IGRAPH_EOVERFLOW); +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, size + 1)); - if (pos < size) { + 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 - pos)); + sizeof(BASE) * (size - (size_t) pos)); } v->stor_begin[pos] = value; - return IGRAPH_SUCCESS; + return 0; } /** @@ -621,8 +584,8 @@ igraph_error_t FUNCTION(igraph_vector, insert)( * 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_get() queries and \ref igraph_vector_set() sets an element of a - * vector. \ref igraph_vector_get_ptr() returns the address of an element. + * 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 @@ -632,19 +595,18 @@ igraph_error_t FUNCTION(igraph_vector, insert)( /** * \ingroup vector - * \function igraph_vector_get + * \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_get_ptr() and the \ref VECTOR macro. + * \sa \ref igraph_vector_e_ptr() and the \ref VECTOR macro. * * Time complexity: O(1). */ -BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { +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); @@ -652,66 +614,35 @@ BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t /** * \ingroup vector - * \function igraph_vector_get_ptr - * \brief Get the address of an element of a 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_get() and the \ref VECTOR macro. + * \sa \ref igraph_vector_e() and the \ref VECTOR macro. * * Time complexity: O(1). */ -BASE* FUNCTION(igraph_vector, get_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { +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_e - * \brief Access an element of a vector (deprecated alias). - * - * \deprecated-by igraph_vector_get 0.10.0 - */ - -BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { - return FUNCTION(igraph_vector, get)(v, 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_get() and the \ref VECTOR macro. - * - * Time complexity: O(1). - */ - -BASE* FUNCTION(igraph_vector, e_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { - return FUNCTION(igraph_vector, get_ptr)(v, 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_get(). + * \sa \ref igraph_vector_e(). */ -void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, BASE value) { +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; @@ -727,7 +658,6 @@ void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, * 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 @@ -738,16 +668,16 @@ 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) * FUNCTION(igraph_vector, size)(v)); + 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. + * \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. * @@ -763,48 +693,14 @@ void FUNCTION(igraph_vector, fill) (TYPE(igraph_vector)* v, BASE e) { } } -#ifndef NOTORDERED - -/** - * \ingroup vector - * \function igraph_vector_range - * \brief Updates a vector to store a range. - * - * Sets the elements of the vector to contain the numbers \p start, \p start+1, - * ..., \p end-1. Note that the range is closed from the left and open from the - * right, according to C conventions. The vector will be resized to fit the range. - * - * \param v The vector to update. - * \param start The lower limit in the range (inclusive). - * \param end The upper limit in the range (exclusive). - * \return Error code: - * \c IGRAPH_ENOMEM: out of memory. - * - * Time complexity: O(n), the number of elements in the vector. - */ - -igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector) *v, BASE from, BASE to) { - BASE *p; - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (to - from))); - - for (p = v->stor_begin; p < v->end; p++) { - *p = from; - from = from + ONE; - } - - return IGRAPH_SUCCESS; -} - -#endif - /** * \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. * @@ -822,8 +718,8 @@ BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) { * \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. * @@ -831,89 +727,22 @@ BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) { */ 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 != NULL); IGRAPH_ASSERT(v->end != v->stor_begin); - - (v->end)--; - - return *(v->end); + tmp = FUNCTION(igraph_vector, e)(v, FUNCTION(igraph_vector, size)(v) - 1); + v->end -= 1; + return tmp; } #ifndef NOTORDERED -/** - * \ingroup vector - * \function igraph_vector_permute - * \brief Permutes the elements of a vector in place according to an index vector. - * - * This function takes a vector \c v and a corresponding index vector \c ind, - * and permutes the elements of \c v such that \c v[ind[i]] is moved to become - * \c v[i] after the function is executed. - * - * - * It is an error to call this function with an index vector that does not - * represent a valid permutation. Each element in the index vector must be - * between 0 and the length of the vector minus one (inclusive), and each such - * element must appear only once. The function does not attempt to validate the - * index vector. - * - * - * The index vector that this function takes is compatible with the index vector - * returned from \ref igraph_vector_qsort_ind(); passing in the index vector - * from \ref igraph_vector_qsort_ind() will sort the original vector. - * - * - * As a special case, this function allows the index vector to be \em shorter - * than the vector being permuted, in which case the elements whose indices do - * not occur in the index vector will be removed from the vector. - * - * \param v the vector to permute - * \param ind the index vector - * - * \return Error code: - * \c IGRAPH_ENOMEM if there is not enough memory. - * - * Time complexity: O(n), the size of the vector. - */ -igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector)* v, const igraph_vector_int_t* index) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - IGRAPH_ASSERT(index != NULL); - IGRAPH_ASSERT(index->stor_begin != NULL); - IGRAPH_ASSERT(FUNCTION(igraph_vector, size)(v) >= igraph_vector_int_size(index)); - - TYPE(igraph_vector) v_copy; - BASE *v_ptr; - igraph_integer_t *ind_ptr; - - /* There is a more space-efficient algorithm that needs O(1) space only, - * but it messes up the index vector, which we don't want */ - - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&v_copy, igraph_vector_int_size(index))); - IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), &v_copy); - - for ( - v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; - ind_ptr < index->end; - v_ptr++, ind_ptr++ - ) { - *v_ptr = VECTOR(*v)[*ind_ptr]; - } - - IGRAPH_CHECK(FUNCTION(igraph_vector, update)(v, &v_copy)); - - FUNCTION(igraph_vector, destroy)(&v_copy); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - /** * \ingroup vector * \function igraph_vector_sort_cmp - * \brief Internal comparison function of vector elements, used by \ref igraph_vector_sort(). + * \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) { @@ -926,7 +755,8 @@ static int FUNCTION(igraph_vector, sort_cmp)(const void *a, const void *b) { /** * \ingroup vector * \function igraph_vector_reverse_sort_cmp - * \brief Internal comparison function of vector elements, used by \ref igraph_vector_reverse_sort(). + * \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) { @@ -953,7 +783,7 @@ static int FUNCTION(igraph_vector, reverse_sort_cmp)(const void *a, const void * void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); - igraph_qsort(v->stor_begin, FUNCTION(igraph_vector, size)(v), + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), sizeof(BASE), FUNCTION(igraph_vector, sort_cmp)); } @@ -974,7 +804,7 @@ void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { 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, FUNCTION(igraph_vector, size)(v), + igraph_qsort(v->stor_begin, (size_t) FUNCTION(igraph_vector, size)(v), sizeof(BASE), FUNCTION(igraph_vector, reverse_sort_cmp)); } @@ -1010,7 +840,7 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v /** * \function igraph_vector_qsort_ind - * \brief Returns a permutation of indices that sorts a vector. + * \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 @@ -1021,10 +851,9 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v * * \param v the array to be sorted * \param inds the output array of indices. This must be initialized, - * but will be resized - * \param order whether the output array should be sorted in ascending - * or descending order. Use \c IGRAPH_ASCENDING for ascending and - * \c IGRAPH_DESCENDING for descending order. + * 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. @@ -1034,61 +863,61 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v * position in the array. Use this to set the values of inds. */ -igraph_error_t FUNCTION(igraph_vector, qsort_ind)(const TYPE(igraph_vector) *v, - igraph_vector_int_t *inds, igraph_order_t order) { - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); +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; - IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + size_t n = (size_t) FUNCTION(igraph_vector, size)(v); + IGRAPH_CHECK(igraph_vector_resize(inds, (long) n)); if (n == 0) { - return IGRAPH_SUCCESS; + return 0; } vind = IGRAPH_CALLOC(n, BASE*); if (vind == 0) { - IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); } for (i = 0; i < n; i++) { vind[i] = &VECTOR(*v)[i]; } first = vind[0]; - if (order == IGRAPH_ASCENDING) { - igraph_qsort(vind, n, sizeof(BASE*), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)); + 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_desc)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vector_lex_cmp - * \brief Lexicographical comparison of two vectors (type-safe variant). + * \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_list_sort(). + * This function is typically used together with \ref igraph_vector_ptr_sort(). * - * \param lhs Pointer to the first vector. - * \param rhs Pointer to the second vector. + * \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_lex_cmp_untyped() for an untyped variant of this - * function, or \ref igraph_vector_colex_cmp() to compare vectors starting from + * \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_int_list_sort.c + * \example examples/simple/igraph_vector_ptr_sort.c */ -int FUNCTION(igraph_vector, lex_cmp)( - const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs -) { - igraph_integer_t i, sa, sb; - const TYPE(igraph_vector) *a = lhs, *b = rhs; +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); @@ -1112,63 +941,35 @@ int FUNCTION(igraph_vector, lex_cmp)( return -1; } -/** - * \function igraph_vector_lex_cmp_untyped - * \brief Lexicographical comparison of two vectors (non-type-safe). - * - * - * 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_lex_cmp() for a type-safe variant of this - * function, or \ref igraph_vector_colex_cmp_untyped() to compare vectors starting from - * the last element. - * - * Time complexity: O(n), the number of elements in the smaller vector. - */ -int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs) { - const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; - const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; - return FUNCTION(igraph_vector, lex_cmp)(a, b); -} - /** * \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_list_sort(). + * This function is typically used together with \ref igraph_vector_ptr_sort(). * - * \param lhs Pointer to a pointer to the first vector. - * \param rhs Pointer to a pointer to the second vector. + * \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_colex_cmp_untyped() for an untyped variant of this - * function, or \ref igraph_vector_lex_cmp() to compare vectors starting from + * \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_int_list_sort.c + * \example examples/simple/igraph_vector_ptr_sort.c */ -int FUNCTION(igraph_vector, colex_cmp)( - const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs -) { - igraph_integer_t i, sa, sb, rai, rbi; - const TYPE(igraph_vector) *a = lhs, *b = rhs; +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); @@ -1194,37 +995,6 @@ int FUNCTION(igraph_vector, colex_cmp)( /* a is shorter, and equal to the last part of b */ return -1; } - -/** - * \function igraph_vector_colex_cmp_untyped - * \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_colex_cmp() for a type-safe variant of this - * function, \ref igraph_vector_lex_cmp_untyped() to compare vectors starting from - * the first element. - * - * Time complexity: O(n), the number of elements in the smaller vector. - */ -int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs) { - const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; - const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; - return FUNCTION(igraph_vector, colex_cmp)(a, b); -} - #endif /*NOTORDERED*/ /** @@ -1239,7 +1009,7 @@ int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs) * 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 new_size The new size of the vector. + * \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 @@ -1255,12 +1025,12 @@ int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs) * n is the new size of the vector. */ -igraph_error_t FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, igraph_integer_t new_size) { +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, new_size)); - v->end = v->stor_begin + new_size; - return IGRAPH_SUCCESS; + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, newsize)); + v->end = v->stor_begin + newsize; + return 0; } /** @@ -1268,32 +1038,34 @@ igraph_error_t FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, igraph_in * \function igraph_vector_resize_min * \brief Deallocate the unused memory of a vector. * - * This function attempts to deallocate the unused reserved storage - * of a vector. If it succeeds, \ref igraph_vector_size() and - * \ref igraph_vector_capacity() will be the same. The data in the - * vector is always preserved, even if deallocation is not successful. - * + * + * 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, O(n) at worst. + * Time complexity: operating system dependent. */ -void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { - igraph_integer_t size; +int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { + size_t size; BASE *tmp; if (v->stor_end == v->end) { - return; + return 0; } - size = (v->end - v->stor_begin); + size = (size_t) (v->end - v->stor_begin); tmp = IGRAPH_REALLOC(v->stor_begin, size, BASE); - - if (tmp != NULL) { + 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 @@ -1326,16 +1098,16 @@ BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { IGRAPH_ASSERT(v->stor_begin != NULL); IGRAPH_ASSERT(v->stor_begin != v->end); max = *(v->stor_begin); -#if defined(BASE_IGRAPH_REAL) - if (isnan(max)) { return max; }; /* Result is NaN */ +#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) - else if (isnan(*ptr)) +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) return *ptr; /* Result is NaN */ #endif ptr++; @@ -1359,7 +1131,7 @@ BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { * * Time complexity: O(n), n is the size of the vector. */ -igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { +long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { if (!FUNCTION(igraph_vector, empty)(v)) { BASE *max; BASE *ptr; @@ -1367,16 +1139,16 @@ igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v IGRAPH_ASSERT(v->stor_begin != NULL); IGRAPH_ASSERT(v->stor_begin != v->end); max = ptr = v->stor_begin; -#if defined(BASE_IGRAPH_REAL) - if (isnan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#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) - else if (isnan(*ptr)) { +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; /* Result is NaN */ } #endif @@ -1406,16 +1178,16 @@ BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { IGRAPH_ASSERT(v->stor_begin != NULL); IGRAPH_ASSERT(v->stor_begin != v->end); min = *(v->stor_begin); -#if defined(BASE_IGRAPH_REAL) - if (isnan(min)) { return min; }; /* Result is NaN */ +#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) - else if (isnan(*ptr)) { +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { return *ptr; /* Result is NaN */ } #endif @@ -1438,7 +1210,7 @@ BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { * * Time complexity: O(n), the number of elements. */ -igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { +long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { if (!FUNCTION(igraph_vector, empty)(v)) { BASE *min; BASE *ptr; @@ -1446,16 +1218,16 @@ igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v IGRAPH_ASSERT(v->stor_begin != NULL); IGRAPH_ASSERT(v->stor_begin != v->end); min = ptr = v->stor_begin; -#if defined(BASE_IGRAPH_REAL) - if (isnan(*ptr)) { return ptr - v->stor_begin; } /* Result is NaN */ +#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) - else if (isnan(*ptr)) { +#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { return ptr - v->stor_begin; /* Result is NaN */ } #endif @@ -1474,7 +1246,7 @@ igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v /** * \ingroup vector - * \function igraph_vector_init_array + * \function igraph_vector_init_copy * \brief Initializes a vector from an ordinary C array (constructor). * * \param v Pointer to an uninitialized vector object. @@ -1487,17 +1259,17 @@ igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v * O(\p length). */ -igraph_error_t FUNCTION(igraph_vector, init_array)( - TYPE(igraph_vector) *v, const BASE *data, igraph_integer_t length) { - - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, length)); - - /* Note: memcpy() behaviour is undefined if data==NULL, even if length==0. */ - if (length > 0) { - memcpy(v->stor_begin, data, length * sizeof(BASE)); +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 IGRAPH_SUCCESS; + return 0; } /** @@ -1519,13 +1291,13 @@ 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) * (v->end - v->stor_begin)); + memcpy(to, v->stor_begin, sizeof(BASE) * (size_t) (v->end - v->stor_begin)); } } /** * \ingroup vector - * \function igraph_vector_init_copy + * \function igraph_vector_copy * \brief Initializes a vector from another vector object (constructor). * * @@ -1541,33 +1313,24 @@ void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE *to) { * n is the size of the vector. */ -igraph_error_t FUNCTION(igraph_vector, init_copy)( - TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from -) { - igraph_integer_t from_size; +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); - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(to, from_size)); - - memcpy(to->stor_begin, from->stor_begin, from_size * sizeof(BASE)); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector - * \function igraph_vector_copy - * \brief Initializes a vector from another vector object (deprecated alias). - * - * \deprecated-by igraph_vector_init_copy 0.10 - */ + 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)); -igraph_error_t FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from) { - return FUNCTION(igraph_vector, init_copy)(to, from); + return 0; } /** @@ -1658,7 +1421,7 @@ BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v) { * Time complexity: O(n), the size of the vector. */ -igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, +int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { BASE res = ZERO; BASE *p, *p2; @@ -1679,7 +1442,7 @@ igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, *p2 = res; } - return IGRAPH_SUCCESS; + return 0; } #ifndef NOTORDERED @@ -1687,14 +1450,11 @@ igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, /** * \ingroup vector * \function igraph_vector_init_seq - * \brief Initializes a vector with a sequence, inclusive endpoints (deprecated). + * \brief Initializes a vector with a sequence. * * - * The vector will contain the numbers \p from, \p from+1, ..., \p to. Note that - * both endpoints are \em inclusive, contrary to typical usage of ranges in C. - * - * \deprecated-by igraph_vector_init_range 0.10.0 - * + * 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). @@ -1705,46 +1465,16 @@ igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, * of elements in the vector. */ -igraph_error_t FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, BASE from, BASE to) { +int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, + BASE from, BASE to) { BASE *p; - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from + 1))); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (long int) (to - from + 1))); for (p = v->stor_begin; p < v->end; p++) { *p = from++; } - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vector - * \function igraph_vector_init_range - * \brief Initializes a vector with a range. - * - * - * The vector will contain the numbers \p start, \p start+1, ..., \p end-1. Note - * that the range is closed from the left and open from the right, according to - * C conventions. - * - * \param v Pointer to an uninitialized vector object. - * \param start The lower limit in the range (inclusive). - * \param end The upper limit in the range (exclusive). - * \return Error code: - * \c IGRAPH_ENOMEM: out of memory. - * - * Time complexity: O(n), the number of elements in the vector. - */ - -igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector) *v, BASE from, BASE to) { - BASE *p; - IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from))); - - for (p = v->stor_begin; p < v->end; p++) { - *p = from; - from = from + ONE; - } - - return IGRAPH_SUCCESS; + return 0; } #endif @@ -1755,6 +1485,8 @@ igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector) *v, BASE * \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. @@ -1764,26 +1496,16 @@ igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector) *v, BASE * vector. */ -void FUNCTION(igraph_vector, remove_section)( - TYPE(igraph_vector) *v, igraph_integer_t from, igraph_integer_t to) { - igraph_integer_t size = FUNCTION(igraph_vector, size)(v); - +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); - - if (from < 0) { - from = 0; - } - - if (to > size) { - to = size; - } - - if (to > from) { + /* Not removing from the end? */ + if (to < FUNCTION(igraph_vector, size)(v)) { memmove(v->stor_begin + from, v->stor_begin + to, - sizeof(BASE) * (v->end - v->stor_begin - to)); - v->end -= (to - from); + sizeof(BASE) * (size_t) (v->end - v->stor_begin - to)); } + v->end -= (to - from); } /** @@ -1800,48 +1522,20 @@ void FUNCTION(igraph_vector, remove_section)( * vector. */ -void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, igraph_integer_t elem) { +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_remove_fast - * \brief Removes a single element from a vector, \em not keeping the order of the remaining elements. - * - * This function removes the element with the given element from the vector by - * swapping it with the last element and then popping it off. You can use this - * function instead of \ref igraph_vector_remove() to gain some speed if the - * order of elements does not matter. - * - * - * 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(1). - */ - -void FUNCTION(igraph_vector, remove_fast)(TYPE(igraph_vector) *v, igraph_integer_t elem) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - - igraph_integer_t len = FUNCTION(igraph_vector, size)(v); - VECTOR(*v)[elem] = VECTOR(*v)[len - 1]; - FUNCTION(igraph_vector, pop_back)(v); -} - /** * \ingroup vector * \function igraph_vector_move_interval * \brief Copies a section of a vector. * * - * The source and the destination sections are allowed to overlap; this will - * be handled internally by the function. + * 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. @@ -1852,22 +1546,46 @@ void FUNCTION(igraph_vector, remove_fast)(TYPE(igraph_vector) *v, igraph_integer * Time complexity: O(end-begin). */ -igraph_error_t FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, - igraph_integer_t begin, igraph_integer_t end, igraph_integer_t to) { +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) * (end - begin)); + sizeof(BASE) * (size_t) (end - begin)); - return IGRAPH_SUCCESS; + return 0; } /** - * \deprecated-by igraph_vector_move_interval 0.10.0 + * \ingroup vector + * \function igraph_vector_permdelete + * \brief Remove elements of a vector (for internal use). */ -igraph_error_t FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, - igraph_integer_t begin, igraph_integer_t end, igraph_integer_t to) { - return FUNCTION(igraph_vector, move_interval)(v, begin, end, to); + +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 @@ -1875,13 +1593,14 @@ igraph_error_t FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, /** * \ingroup vector * \function igraph_vector_isininterval - * \brief Checks if all elements of a vector are in the given interval. + * \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 the vector is empty or all vector elements - * are in the interval, false (zero) otherwise. If any element is NaN, it will + * \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 @@ -1939,21 +1658,18 @@ igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v, * \function igraph_vector_all_e * \brief Are all elements equal? * - * Checks element-wise equality of two vectors. For vectors containing floating - * point values, consider using \ref igraph_matrix_all_almost_e(). - * * \param lhs The first vector. * \param rhs The second vector. - * \return True if the elements in the \p lhs are all - * equal to the corresponding elements in \p rhs. Returns - * false if the lengths of the vectors don't match. + * \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) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1961,7 +1677,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return false; + return 0; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; @@ -1971,10 +1687,10 @@ igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, #else if (l != r) { #endif - return false; + return 0; } } - return true; + return 1; } } @@ -2003,7 +1719,7 @@ FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -2011,16 +1727,16 @@ igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return false; + return 0; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l >= r) { - return false; + return 0; } } - return true; + return 1; } } @@ -2042,7 +1758,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -2050,16 +1766,16 @@ igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return false; + return 0; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l <= r) { - return false; + return 0; } } - return true; + return 1; } } @@ -2081,7 +1797,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -2089,16 +1805,16 @@ FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return false; + return 0; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l > r) { - return false; + return 0; } } - return true; + return 1; } } @@ -2120,7 +1836,7 @@ FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - igraph_integer_t i, s; + long int i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -2128,16 +1844,16 @@ FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return false; + return 0; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l < r) { - return false; + return 0; } } - return true; + return 1; } } @@ -2147,7 +1863,8 @@ FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, #ifndef NOTORDERED igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, - BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end); + BASE what, long int *pos, + long int start, long int end); /** * \ingroup vector @@ -2163,7 +1880,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto * * \param v The \type igraph_vector_t object. * \param what The element to search for. - * \param pos Pointer to an \type igraph_integer_t. This is set to the + * \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 @@ -2178,7 +1895,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto */ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, - BASE what, igraph_integer_t *pos) { + BASE what, long int *pos) { return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, 0, FUNCTION(igraph_vector, size)(v)); } @@ -2200,7 +1917,7 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, * * \param v The \type igraph_vector_t object. * \param what The element to search for. - * \param pos Pointer to an \type igraph_integer_t. This is set to the position of an + * \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). @@ -2214,9 +1931,10 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, */ igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) *v, - BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { - igraph_integer_t left = start; - igraph_integer_t right = end - 1; + 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); @@ -2232,13 +1950,14 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) } igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, - BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { - igraph_integer_t left = start; - igraph_integer_t right = end - 1; + 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 */ - igraph_integer_t middle = left + ((right - left) >> 1); + long int middle = left + ((right - left) >> 1); if (VECTOR(*v)[middle] > what) { right = middle - 1; } else if (VECTOR(*v)[middle] < what) { @@ -2247,7 +1966,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto if (pos != 0) { *pos = middle; } - return true; + return 1; } } @@ -2256,7 +1975,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto *pos = left; } - return false; + return 0; } /** @@ -2278,29 +1997,29 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, BASE what) { - igraph_integer_t left = 0; - igraph_integer_t right = FUNCTION(igraph_vector, size)(v) - 1; + 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 */ - igraph_integer_t middle = left + ((right - left) >> 1); + 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 true; + return 1; } } - return false; + return 0; } #endif /** * \function igraph_vector_scale - * \brief Multiplies all elements of a vector by a constant. + * \brief Multiply all elements of a vector by a constant * * \param v The vector. * \param by The constant. @@ -2312,7 +2031,7 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, */ void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) { - igraph_integer_t i; + long int i; for (i = 0; i < FUNCTION(igraph_vector, size)(v); i++) { #ifdef PROD PROD(VECTOR(*v)[i], VECTOR(*v)[i], by); @@ -2335,7 +2054,7 @@ void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) { */ void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) { - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); + 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); @@ -2353,7 +2072,7 @@ void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) { * 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. + * \return \c TRUE if the element is found and \c FALSE otherwise. * * Time complexity: O(n), the length of the vector. */ @@ -2367,28 +2086,27 @@ igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, #else if (*p == e) { #endif - return true; + return 1; } p++; } - return false; + return 0; } /** * \function igraph_vector_search - * \brief Searches in a vector from a given position. + * \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 + * \return Boolean, \c TRUE if the element was found, \c FALSE * otherwise. * * Time complexity: O(m), the number of elements to search, the length @@ -2396,8 +2114,9 @@ igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, */ igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, - igraph_integer_t from, BASE what, igraph_integer_t *pos) { - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(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)) { @@ -2414,9 +2133,9 @@ igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, if (pos != 0) { *pos = i; } - return true; + return 1; } else { - return false; + return 0; } } @@ -2427,10 +2146,10 @@ igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, * \ingroup internal */ -igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, +int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem) { - igraph_integer_t i = 0, n = FUNCTION(igraph_vector, size)(v); - igraph_integer_t s; + long int i = 0, n = FUNCTION(igraph_vector, size)(v); + long int s; while (i < n && VECTOR(*v)[i] < elem) { i++; } @@ -2441,7 +2160,7 @@ igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, } FUNCTION(igraph_vector, remove_section)(v, 0, i + (s - i) / 2); - return IGRAPH_SUCCESS; + return 0; } #endif @@ -2458,39 +2177,38 @@ igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, * Time complexity: O(n), the number of elements in the new vector. */ -igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, +int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { - igraph_integer_t tosize, fromsize; - igraph_integer_t newsize; + long tosize, fromsize; tosize = FUNCTION(igraph_vector, size)(to); fromsize = FUNCTION(igraph_vector, size)(from); - IGRAPH_SAFE_ADD(tosize, fromsize, &newsize); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, newsize)); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, tosize + fromsize)); memcpy(to->stor_begin + tosize, from->stor_begin, - sizeof(BASE) * fromsize); + sizeof(BASE) * (size_t) fromsize); to->end = to->stor_begin + tosize + fromsize; - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vector_get_interval */ -igraph_error_t FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, - TYPE(igraph_vector) *res, igraph_integer_t from, igraph_integer_t to) { +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, - (to - from) * sizeof(BASE)); - return IGRAPH_SUCCESS; + (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. + * \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 @@ -2506,10 +2224,10 @@ igraph_error_t FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) * igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) *m1, const TYPE(igraph_vector) *m2) { - igraph_integer_t n1 = FUNCTION(igraph_vector, size)(m1); - igraph_integer_t n2 = FUNCTION(igraph_vector, size)(m2); - igraph_integer_t n = n1 < n2 ? n1 : n2; - igraph_integer_t i; + 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++) { @@ -2518,8 +2236,8 @@ igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) * if (d > diff) { diff = d; } - #if defined(BASE_IGRAPH_REAL) - else if (isnan(d)) { /* Result is NaN */ + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(d)) { /* Result is NaN */ return d; }; #endif @@ -2544,17 +2262,17 @@ igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) * * Time complexity: O(n), the number of elements in \p from. */ -igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, +int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { - igraph_integer_t n = FUNCTION(igraph_vector, size)(from); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, n)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vector_swap - * \brief Swap all elements of two vectors. + * \brief Swap elements of two vectors. * * \param v1 The first vector. * \param v2 The second vector. @@ -2563,7 +2281,7 @@ igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, * Time complexity: O(1). */ -igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { +int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { TYPE(igraph_vector) tmp; @@ -2588,13 +2306,13 @@ igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igrap * Time complexity: O(1). */ -igraph_error_t FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, - igraph_integer_t i, igraph_integer_t j) { +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 IGRAPH_SUCCESS; + return 0; } /** @@ -2609,23 +2327,23 @@ igraph_error_t FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, * Time complexity: O(n), the number of elements. */ -igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { +int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { - igraph_integer_t n = FUNCTION(igraph_vector, size)(v), n2 = n / 2; - igraph_integer_t i, j; + 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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup vector * \function igraph_vector_shuffle - * \brief Shuffles a vector in-place using the Fisher-Yates method. + * \brief Shuffles a vector in-place using the Fisher-Yates method * * * The Fisher-Yates shuffle ensures that every permutation is @@ -2655,9 +2373,9 @@ igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { * \example examples/simple/igraph_fisher_yates_shuffle.c */ -igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { - igraph_integer_t n = FUNCTION(igraph_vector, size)(v); - igraph_integer_t k; +int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { + long int n = FUNCTION(igraph_vector, size)(v); + long int k; BASE dummy; RNG_BEGIN(); @@ -2686,14 +2404,14 @@ igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { * Time complexity: O(n), the number of elements. */ -igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2) { - igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); - igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); - igraph_integer_t i; + 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 to be added must have the same sizes.", + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } @@ -2705,7 +2423,7 @@ igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, #endif } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2722,14 +2440,14 @@ igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the length of the vectors. */ -igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2) { - igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); - igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); - igraph_integer_t i; + 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 to be subtracted must have the same sizes.", + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } @@ -2741,7 +2459,7 @@ igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, #endif } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2757,14 +2475,14 @@ igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the number of elements. */ -igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2) { - igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); - igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); - igraph_integer_t i; + 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 to be multiplied must have the same sizes.", + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } @@ -2776,7 +2494,7 @@ igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, #endif } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2794,14 +2512,14 @@ igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the length of the vectors. */ -igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2) { - igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); - igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); - igraph_integer_t i; + 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 to be divided must have the same sizes.", + IGRAPH_ERROR("Vectors must have the same number of elements for swapping", IGRAPH_EINVAL); } @@ -2813,23 +2531,23 @@ igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, #endif } - return IGRAPH_SUCCESS; + return 0; } #ifndef NOABS -igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { +int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { #ifdef UNSIGNED /* Nothing do to, unsigned type */ IGRAPH_UNUSED(v); #else - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); + 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 IGRAPH_SUCCESS; + return 0; } #endif @@ -2843,23 +2561,25 @@ igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { * 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. + * \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. */ -void FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, +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) - if (isnan(*min)) { return; }; /* Result is NaN */ + #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) { @@ -2868,38 +2588,40 @@ void FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, } else if (*ptr < *min) { *min = *ptr; } - #if defined(BASE_IGRAPH_REAL) - else if (isnan(*ptr)) { /* Result is NaN */ + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + else if (igraph_is_nan(*ptr)) { /* Result is NaN */ *min = *max = *ptr; - return; + return IGRAPH_SUCCESS; }; #endif ptr++; } + return IGRAPH_SUCCESS; } /** * \function igraph_vector_which_minmax - * \brief Index of the minimum and maximum elements. + * \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. */ -void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, - igraph_integer_t *which_min, igraph_integer_t *which_max) { +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); @@ -2907,10 +2629,10 @@ void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, IGRAPH_ASSERT(v->stor_begin != v->end); ptr = v->stor_begin; min = max = ptr; - #if defined(BASE_IGRAPH_REAL) - if (isnan(*ptr)) { /* Result is NaN */ + #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) + if (igraph_is_nan(*ptr)) { /* Result is NaN */ *which_min = *which_max = 0; - return; + return IGRAPH_SUCCESS; } #endif while (ptr < v->end) { @@ -2919,16 +2641,17 @@ void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, } else if (*ptr < *min) { min = ptr; } -#if defined(BASE_IGRAPH_REAL) - else if (isnan(*ptr)) { /* Result is NaN */ +#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; + return IGRAPH_SUCCESS; } #endif ptr++; } *which_min = min - v->stor_begin; *which_max = max - v->stor_begin; + return IGRAPH_SUCCESS; } #endif @@ -2939,21 +2662,21 @@ void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, * * 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. + * \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) { - igraph_integer_t n = FUNCTION(igraph_vector, size)(v); - igraph_integer_t i = 0; + long int n = FUNCTION(igraph_vector, size)(v); + long int i = 0; #ifdef EQ - while (i < n && EQ(VECTOR(*v)[i], (BASE) ZERO)) { + while (i < n && EQ(VECTOR(*v)[i], ZERO)) { #else - while (i < n && VECTOR(*v)[i] == (BASE) ZERO) { + while (i < n && VECTOR(*v)[i] == ZERO) { #endif i++; } @@ -2963,14 +2686,14 @@ igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v) { #ifndef NOTORDERED -igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( - const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, - const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, +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. + * \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. @@ -2998,9 +2721,9 @@ igraph_error_t FUNCTION(igraph_i_vector, intersect_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. */ -igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { - igraph_integer_t size1, size2; + long int size1, size2; size1 = FUNCTION(igraph_vector, size)(v1); size2 = FUNCTION(igraph_vector, size)(v2); @@ -3008,22 +2731,22 @@ igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vecto FUNCTION(igraph_vector, clear)(result); if (size1 == 0 || size2 == 0) { - return IGRAPH_SUCCESS; + return 0; } IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( v1, 0, size1, v2, 0, size2, result)); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( - const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, - const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, +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) { - igraph_integer_t size1, size2, probe1, probe2; + long int size1, size2, probe1, probe2; if (begin1 == end1 || begin2 == end2) { - return IGRAPH_SUCCESS; + return 0; } size1 = end1 - begin1; @@ -3057,12 +2780,12 @@ igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( )); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vector_difference_sorted - * \brief Calculates the difference between two sorted vectors (considered as sets). + * \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. @@ -3071,9 +2794,9 @@ igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( * \param v2 the second vector * \param result the result vector */ -igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, +int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { - igraph_integer_t i, j, i0, j0; + long int i, j, i0, j0; i0 = FUNCTION(igraph_vector, size)(v1); j0 = FUNCTION(igraph_vector, size)(v2); i = j = 0; @@ -3087,7 +2810,7 @@ igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vect 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) * i0); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i0); return IGRAPH_SUCCESS; } @@ -3099,7 +2822,7 @@ igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vect } if (i > 0) { IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i)); - memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * i); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i); } while (i < i0 && j < j0) { @@ -3120,21 +2843,43 @@ igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vect } } if (i < i0) { - igraph_integer_t oldsize = FUNCTION(igraph_vector, size)(result); + 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) * (i0 - i)); + sizeof(BASE) * (size_t) (i0 - i)); } - return IGRAPH_SUCCESS; + return 0; } #endif -#ifdef OUT_FORMAT +#if defined(OUT_FORMAT) + #ifndef USING_R -igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format) { - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); +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]); } @@ -3142,21 +2887,13 @@ igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, con putchar(' '); printf(format, VECTOR(*v)[i]); } printf("\n"); - return IGRAPH_SUCCESS; + return 0; } -#endif /* USING_R */ -#endif /* OUT_FORMAT */ - -#if defined(OUT_FORMAT) || defined(FPRINTFUNC) -#ifndef USING_R -igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v) { - return FUNCTION(igraph_vector, fprint)(v, stdout); -} #endif -igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { - igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); +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]); @@ -3172,34 +2909,34 @@ igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FIL #endif } fprintf(file, "\n"); - return IGRAPH_SUCCESS; + return 0; } -#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ +#endif -igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, +int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, - const igraph_vector_int_t *idx) { + const igraph_vector_t *idx) { - igraph_integer_t i, j, newlen = igraph_vector_int_size(idx); + long int i, newlen = igraph_vector_size(idx); IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(newv, newlen)); for (i = 0; i < newlen; i++) { - j = VECTOR(*idx)[i]; + long int j = (long int) VECTOR(*idx)[i]; VECTOR(*newv)[i] = VECTOR(*v)[j]; } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, +int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, const igraph_vector_int_t *idx) { BASE *tmp; - igraph_integer_t i, n = igraph_vector_int_size(idx); + int i, n = igraph_vector_int_size(idx); tmp = IGRAPH_CALLOC(n, BASE); if (!tmp) { - IGRAPH_ERROR("Cannot index vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot index vector", IGRAPH_ENOMEM); } for (i = 0; i < n; i++) { @@ -3210,5 +2947,5 @@ igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, v->stor_begin = tmp; v->stor_end = v->end = tmp + n; - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/core/vector_list.c b/src/vendor/cigraph/src/core/vector_list.c deleted file mode 100644 index 5f761b11530..00000000000 --- a/src/vendor/cigraph/src/core/vector_list.c +++ /dev/null @@ -1,171 +0,0 @@ -/* -*- 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 - -*/ - -#include "igraph_vector_list.h" - -#define VECTOR_LIST - -#define BASE_IGRAPH_REAL -#include "igraph_pmt.h" -#include "typed_list.pmt" -#include "igraph_pmt_off.h" -#undef BASE_IGRAPH_REAL - -#define BASE_INT -#include "igraph_pmt.h" -#include "typed_list.pmt" -#include "igraph_pmt_off.h" -#undef BASE_INT - -#undef VECTOR_LIST - -/** - * \ingroup vector_list - * \section about_igraph_vector_list_t_objects About \type igraph_vector_list_t objects - * - * The \type igraph_vector_list_t data type is essentially a list of - * \type igraph_vector_t objects with automatic memory management. It is something - * similar to (but much simpler than) the \type vector template in the C++ - * standard library where the elements are vectors themselves. - * - * There are multiple variants of \type igraph_vector_list_t; the basic variant - * stores vectors of doubles (i.e. each item is an \ref igraph_vector_t), but - * there is also \type igraph_vector_int_list_t for integers (where each item is - * an \type igraph_vector_int_t), \type igraph_matrix_list_t for matrices of - * doubles and so on. The following list summarizes the variants that are - * currently available in the library: - * - * \ilist - * \ili \type igraph_vector_list_t for lists of vectors of floating-point numbers - * (\type igraph_vector_t) - * \ili \type igraph_vector_int_list_t for lists of integer vectors - * (\type igraph_vector_int_t) - * \ili \type igraph_matrix_list_t for lists of matrices of floating-point numbers - * (\type igraph_matrix_t) - * \ili \type igraph_graph_list_t for lists of graphs (\type igraph_t) - * \endilist - * - * Lists of vectors are used in \a igraph in many - * cases, e.g., when returning lists of paths, cliques or vertex sets. - * Functions that expect or return a list of numeric vectors typically use - * \type igraph_vector_list_t or \type igraph_vector_int_list_t to achieve this. - * Lists of integer vectors are used when the vectors in the list are supposed - * to hold vertex or edge identifiers, while lists of floating-point vectors - * are used when the vectors are expected to hold fractional numbers or - * infinities. - * - * The elements in an \type igraph_vector_list_t object and its variants are - * indexed from zero, we follow the usual C convention here. - * - * Almost all of the functions described below for \type igraph_vector_list_t - * also exist for all the other vector list variants. These variants are not - * documented separately; you can simply replace \c vector_list with, say, - * \c vector_int_list if you need a function for another variant. For instance, - * to initialize a list of integer vectors, you need to use - * \c igraph_vector_int_list_init() and not \ref igraph_vector_list_init(). - * - * Before diving into a detailed description of the functions related to - * lists of vectors, we must also talk about the \em ownership rules of these - * objects. The most important rule is that the vectors in the list are - * owned by the list itself, meaning that the user is \em not responsible - * for allocating memory for the vectors or for freeing the memory associated - * to the vectors. It is the responsibility of the list to allocate and initialize - * the vectors when new items are created in the list, and it is also the - * responsibility of the list to destroy the items when they are removed from - * the list without passing on their ownership to the user. As a consequence, - * the list may not contain "uninitialized" or "null" items; each item is - * initialized when it comes to existence. If you create a list containing - * one million vectors, you are not only allocating memory for one million - * \ref igraph_vector_t object but you are also initializing one million - * vectors. Also, if you have a list containing one million vectors and you - * clear the list by calling \ref igraph_vector_list_clear(), the list will - * implicitly destroy these lists, and any pointers that you may hold to the - * items become invalid at once. - * - * Speaking about pointers, the typical way of working with vectors in - * a list is to obtain a pointer to one of the items via the - * \ref igraph_vector_list_get_ptr() method and then passing this pointer - * onwards to functions that manipulate \ref igraph_vector_t objects. However, - * note that the pointers are \em ephemeral in the sense that they may be - * invalidated any time when the list is modified because a modification may - * involve the re-allocation of the internal storage of the list if more space - * is needed, and the pointers that you obtained will not follow the - * reallocation. This limitation does not appear often in real-world usage of - * \c igraph_vector_list_t and in general, the advantages of the automatic - * memory management outweigh this limitation. - */ - -/** - * \ingroup vector_list - * \section igraph_vector_list_constructors_and_destructors Constructors and - * destructors - * - * \type igraph_vector_list_t objects have to be initialized before using - * them, this is analogous to calling a constructor on them. - * \ref igraph_vector_list_init() is the basic constructor; it creates a list - * of the given length and also initializes each vector in the newly created - * list to zero length. - * - * If an \type igraph_vector_list_t object is not needed any more, it - * should be destroyed to free its allocated memory by calling the - * \type igraph_vector_list_t destructor, \ref igraph_vector_list_destroy(). - * Calling the destructor also destroys all the vectors inside the vector - * list due to the ownership rules. If you want to keep a few of the vectors - * in the vector list, you need to copy them with \ref igraph_vector_init_copy() or - * \ref igraph_vector_update(), or you need to remove them from the list and - * take ownership by calling \ref igraph_vector_list_pop_back(), - * \ref igraph_vector_list_remove() or \ref igraph_vector_list_remove_fast() . - */ - - -/** - * \ingroup vector_list - * \section igraph_vector_list_accessing_elements Accessing elements - * - * Elements of a vector list may be accessed with the - * \ref igraph_vector_list_get_ptr() function. The function returns a \em pointer - * to the vector with a given index inside the list, and you may then pass - * this pointer onwards to other functions that can query or manipulate - * vectors. The pointer itself is guaranteed to stay valid as long as the - * list itself is not modified; however, \em any modification to the list - * will invalidate the pointer, even modifications that are seemingly unrelated - * to the vector that the pointer points to (such as adding a new vector at - * the end of the list). This is because the list data structure may be forced - * to re-allocate its internal storage if a new element does not fit into the - * already allocated space, and there are no guarantees that the re-allocated - * block remains at the same memory location (typically it gets moved elsewhere). - * - * - * Note that the standard \ref VECTOR macro that works for ordinary vectors - * does not work for lists of vectors to access the i-th element (but of course - * you can use it to index into an existing vector that you retrieved from the - * vector list with \ref igraph_vector_list_get_ptr() ). This is because the - * macro notation would allow one to overwrite the vector in the list with - * another one without the list knowing about it, so the list would not be able - * to destroy the vector that was overwritten by a new one. - * - * - * \ref igraph_vector_list_tail_ptr() returns a pointer to the last - * vector in the list, or \c NULL if the list is empty. There is no - * igraph_vector_list_head_ptr(), however, as it is easy to - * write igraph_vector_list_get_ptr(v, 0) instead. - */ diff --git a/src/vendor/cigraph/src/core/vector_ptr.c b/src/vendor/cigraph/src/core/vector_ptr.c index ce063fd69da..d3358bc3322 100644 --- a/src/vendor/cigraph/src/core/vector_ptr.c +++ b/src/vendor/cigraph/src/core/vector_ptr.c @@ -21,14 +21,13 @@ */ -#include "igraph_vector_ptr.h" - #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 /* uintptr_t */ #include /** @@ -43,6 +42,11 @@ * 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 @@ -57,7 +61,7 @@ * 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 some function can + * 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(). */ @@ -82,29 +86,28 @@ * time \endquote required to allocate \p size elements. */ -igraph_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size) { - igraph_integer_t alloc_size = size > 0 ? size : 1; +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); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** */ -const igraph_vector_ptr_t *igraph_vector_ptr_view( - const igraph_vector_ptr_t *v, void *const *data, igraph_integer_t length -) { +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; @@ -211,25 +214,25 @@ void igraph_vector_ptr_destroy_all(igraph_vector_ptr_t* v) { * - IGRAPH_ENOMEM: out of memory */ -igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity) { - igraph_integer_t actual_size = igraph_vector_ptr_size(v); +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); - IGRAPH_ASSERT(capacity >= 0); - if (capacity <= igraph_vector_ptr_size(v)) { - return IGRAPH_SUCCESS; + if (size <= igraph_vector_ptr_size(v)) { + return 0; } - tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) capacity, void*); - IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for pointer vector."); - + 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 + capacity; + v->stor_end = v->stor_begin + size; v->end = v->stor_begin + actual_size; - return IGRAPH_SUCCESS; + return 0; } /** @@ -254,7 +257,7 @@ igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v) { * Time complexity: O(1). */ -igraph_integer_t igraph_vector_ptr_size(const igraph_vector_ptr_t* v) { +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; @@ -298,7 +301,7 @@ void igraph_vector_ptr_clear(igraph_vector_ptr_t* v) { * \param v The pointer vector. * \param e The new element to include in the pointer vector. * \return Error code. - * \sa \ref igraph_vector_push_back() for the corresponding operation of + * \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 @@ -306,13 +309,13 @@ void igraph_vector_ptr_clear(igraph_vector_ptr_t* v) { * push_back operations need O(n) time to complete. */ -igraph_error_t igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e) { +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) { - igraph_integer_t new_size = igraph_vector_ptr_size(v) * 2; + long int new_size = igraph_vector_ptr_size(v) * 2; if (new_size == 0) { new_size = 1; } @@ -322,24 +325,9 @@ igraph_error_t igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e) { *(v->end) = e; v->end += 1; - return IGRAPH_SUCCESS; + return 0; } - -/** - * \ingroup vectorptr - * \function igraph_vector_ptr_pop_back - * \brief Removes and returns the last element of a pointer vector. - * - * - * It is an error to call this function with an empty vector. - * - * \param v The pointer vector. - * \return The removed last element. - * - * Time complexity: O(1). - */ - void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); @@ -362,20 +350,20 @@ void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v) { * \param pos The position where the new element is inserted. * \param e The inserted element */ -igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t* v, igraph_integer_t pos, void* e) { - igraph_integer_t size = igraph_vector_ptr_size(v); +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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup vectorptr - * \function igraph_vector_ptr_get + * \function igraph_vector_ptr_e * \brief Access an element of a pointer vector. * * \param v Pointer to a pointer vector. @@ -385,24 +373,12 @@ igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t* v, igraph_integer_t * Time complexity: O(1). */ -void *igraph_vector_ptr_get(const igraph_vector_ptr_t* v, igraph_integer_t pos) { +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_e - * \brief Access an element of a pointer vector (deprecated alias). - * - * \deprecated-by igraph_vector_ptr_get 0.10.0 - */ - -void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos) { - return igraph_vector_ptr_get(v, pos); -} - /** * \ingroup vectorptr * \function igraph_vector_ptr_set @@ -415,7 +391,7 @@ void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos) { * Time complexity: O(1). */ -void igraph_vector_ptr_set(igraph_vector_ptr_t* v, igraph_integer_t pos, void* value) { +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; @@ -454,36 +430,31 @@ void igraph_vector_ptr_null(igraph_vector_ptr_t* v) { * needed to allocate the memory for the vector elements. */ -igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize) { +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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup vectorptr * \brief Initializes a pointer vector from an array (constructor). * - * \param v Pointer to an uninitialized - * igraph_vector_ptr_t object to be initialized. - * \param data The array of pointers that serves as the initial contents of the - * pointer vector. - * \param length Integer, the length of the array. * \return Error code: * \c IGRAPH_ENOMEM if out of memory */ -igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t *v, void *const *data, igraph_integer_t length) { +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 initialize pointer vector from array", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -502,8 +473,8 @@ void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { /** * \ingroup vectorptr - * \function igraph_vector_ptr_init_copy - * \brief Initializes a pointer vector from another one (constructor). + * \function igraph_vector_ptr_copy + * \brief Copy a pointer vector (constructor). * * * This function creates a pointer vector by copying another one. This @@ -526,8 +497,8 @@ void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { * done in O(n) time. */ -igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { - igraph_integer_t from_size; +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 */ @@ -536,7 +507,7 @@ igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph to->stor_begin = IGRAPH_CALLOC(from_size, void*); if (to->stor_begin == 0) { - IGRAPH_ERROR("Cannot copy pointer vector", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("cannot copy ptr vector", IGRAPH_ENOMEM); } to->stor_end = to->stor_begin + igraph_vector_ptr_size(from); to->end = to->stor_end; @@ -544,19 +515,7 @@ igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph memcpy(to->stor_begin, from->stor_begin, (size_t) igraph_vector_ptr_size(from)*sizeof(void*)); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vectorptr - * \function igraph_vector_ptr_copy - * \brief Initializes a pointer vector from another one (deprecated alias). - * - * \deprecated-by igraph_vector_ptr_init_copy 0.10 - */ - -igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { - return igraph_vector_ptr_init_copy(to, from); + return 0; } /** @@ -564,7 +523,7 @@ igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vect * \brief Remove an element from a pointer vector. */ -void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t pos) { +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. */ @@ -594,23 +553,46 @@ void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t pos) { * 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); } -igraph_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { - igraph_integer_t origsize = igraph_vector_ptr_size(to); - igraph_integer_t othersize = igraph_vector_ptr_size(from); - igraph_integer_t i; +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 IGRAPH_SUCCESS; + return 0; } @@ -655,148 +637,3 @@ igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector IGRAPH_ASSERT(v != 0); return v->item_destructor; } - -typedef int cmp_t (const void *, const void *); - -/** - * Comparison function passed to qsort_r from igraph_vector_ptr_sort_ind - */ -static int igraph_vector_ptr_i_sort_ind_cmp(void *thunk, const void *p1, const void *p2) { - cmp_t *cmp = (cmp_t *) thunk; - uintptr_t *pa = (uintptr_t*) p1; - uintptr_t *pb = (uintptr_t*) p2; - void **item_a_ptr = (void**) *pa; - void **item_b_ptr = (void**) *pb; - return cmp(*item_a_ptr, *item_b_ptr); -} - -/** - * \ingroup vectorptr - * \function igraph_vector_ptr_sort_ind - * \brief Returns a permutation of indices that sorts a vector of pointers. - * - * 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. - * - * \param v the array to be sorted - * \param inds the output array of indices. This must be initialized, - * but will be resized - * \param cmp a comparator function that takes two elements of the pointer - * vector being sorted (these are constant pointers on their own) - * and returns a negative value if the item \em "pointed to" by the - * first pointer is smaller than the item \em "pointed to" by the - * second pointer, a positive value if it is larger, or zero if the - * two items are equal - * \return Error code. - * - * This routine uses the C library 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. - */ - -igraph_error_t igraph_vector_ptr_sort_ind(igraph_vector_ptr_t *v, - igraph_vector_int_t *inds, cmp_t *cmp) { - igraph_integer_t i; - uintptr_t *vind, first; - igraph_integer_t n = igraph_vector_ptr_size(v); - - IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); - if (n == 0) { - return IGRAPH_SUCCESS; - } - - vind = IGRAPH_CALLOC(n, uintptr_t); - if (vind == 0) { - IGRAPH_ERROR("igraph_vector_ptr_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - - for (i = 0; i < n; i++) { - vind[i] = (uintptr_t) &VECTOR(*v)[i]; - } - - first = vind[0]; - - igraph_qsort_r(vind, n, sizeof(vind[0]), (void*)cmp, igraph_vector_ptr_i_sort_ind_cmp); - - for (i = 0; i < n; i++) { - VECTOR(*inds)[i] = (vind[i] - first) / sizeof(uintptr_t); - } - - IGRAPH_FREE(vind); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup vectorptr - * \function igraph_vector_ptr_permute - * \brief Permutes the elements of a pointer vector in place according to an index vector. - * - * - * This function takes a vector \c v and a corresponding index vector \c ind, - * and permutes the elements of \c v such that \c v[ind[i]] is moved to become - * \c v[i] after the function is executed. - * - * - * It is an error to call this function with an index vector that does not - * represent a valid permutation. Each element in the index vector must be - * between 0 and the length of the vector minus one (inclusive), and each such - * element must appear only once. The function does not attempt to validate the - * index vector. - * - * - * The index vector that this function takes is compatible with the index vector - * returned from \ref igraph_vector_ptr_sort_ind(); passing in the index vector - * from \ref igraph_vector_ptr_sort_ind() will sort the original vector. - * - * - * As a special case, this function allows the index vector to be \em shorter - * than the vector being permuted, in which case the elements whose indices do - * not occur in the index vector will be removed from the vector. - * - * \param v the vector to permute - * \param ind the index vector - * - * \return Error code: - * \c IGRAPH_ENOMEM if there is not enough memory. - * - * Time complexity: O(n), the size of the vector. - */ -igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); - IGRAPH_ASSERT(index != NULL); - IGRAPH_ASSERT(index->stor_begin != NULL); - IGRAPH_ASSERT(igraph_vector_ptr_size(v) >= igraph_vector_int_size(index)); - - igraph_vector_ptr_t v_copy; - void** v_ptr; - igraph_integer_t *ind_ptr; - - /* There is a more space-efficient algorithm that needs O(1) space only, - * but it messes up the index vector, which we don't want */ - - IGRAPH_CHECK(igraph_vector_ptr_init(&v_copy, igraph_vector_int_size(index))); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &v_copy); - - for ( - v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; - ind_ptr < index->end; - v_ptr++, ind_ptr++ - ) { - *v_ptr = VECTOR(*v)[*ind_ptr]; - } - - IGRAPH_CHECK(igraph_vector_ptr_resize(v, igraph_vector_int_size(index))); - igraph_vector_ptr_copy_to(&v_copy, VECTOR(*v)); - - igraph_vector_ptr_destroy(&v_copy); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/f2c.h b/src/vendor/cigraph/src/f2c.h index db5c073aa3a..b8ec2e24d46 100644 --- a/src/vendor/cigraph/src/f2c.h +++ b/src/vendor/cigraph/src/f2c.h @@ -7,7 +7,6 @@ #ifndef F2C_INCLUDE #define F2C_INCLUDE -#include "igraph_error.h" #include "linalg/blas_internal.h" #include "linalg/lapack_internal.h" #include "linalg/arpack_internal.h" @@ -146,7 +145,7 @@ union Multitype { /* for multiple entry points */ typedef union Multitype Multitype; -/*typedef igraph_integer_t Long;*/ /* No longer used; formerly in Namelist */ +/*typedef long int Long;*/ /* No longer used; formerly in Namelist */ struct Vardesc { /* for Namelist */ char *name; diff --git a/src/vendor/cigraph/src/flow/flow.c b/src/vendor/cigraph/src/flow/flow.c index ba1beb5f6fa..ff82d89643b 100644 --- a/src/vendor/cigraph/src/flow/flow.c +++ b/src/vendor/cigraph/src/flow/flow.c @@ -40,8 +40,9 @@ #include "core/buckets.h" #include "core/cutheap.h" #include "core/interruption.h" -#include "flow/flow_internal.h" -#include "math/safe_intop.h" +#include "core/math.h" + +#include "config.h" /* * Some general remarks about the functions in this file. @@ -156,34 +157,33 @@ * undirected edge. */ -static igraph_error_t igraph_i_maxflow_undirected(const igraph_t *graph, +static int igraph_i_maxflow_undirected(const igraph_t *graph, igraph_real_t *value, igraph_vector_t *flow, - igraph_vector_int_t *cut, - igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, + 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_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t edges; + 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; - igraph_integer_t size; + 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 + sure that the edge ids will be handled properly to build the new capacity vector. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges * 2); - IGRAPH_SAFE_MULT(no_of_edges, 4, &size); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 4)); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); - IGRAPH_CHECK(igraph_vector_int_resize(&edges, size)); - for (igraph_integer_t i = 0; i < no_of_edges; i++) { + 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] = @@ -197,8 +197,8 @@ static igraph_error_t igraph_i_maxflow_undirected(const igraph_t *graph, partition2, source, target, &newcapacity, stats)); if (cut) { - igraph_integer_t cs = igraph_vector_int_size(cut); - for (igraph_integer_t i = 0; i < cs; i++) { + 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; } @@ -208,21 +208,22 @@ static igraph_error_t igraph_i_maxflow_undirected(const igraph_t *graph, /* 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 + from the bigger vertex id to the smaller one. For positive values the direction is the opposite. */ if (flow) { - for (igraph_integer_t i = 0; i < no_of_edges; i++) { + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&newcapacity); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } #define FIRST(i) (VECTOR(*first)[(i)]) @@ -253,33 +254,33 @@ static igraph_error_t igraph_i_maxflow_undirected(const igraph_t *graph, &first, ¤t, &to, &excess, \ &rescap, &rev)) -static void igraph_i_mf_gap(igraph_integer_t b, igraph_maxflow_stats_t *stats, +static void igraph_i_mf_gap(long int b, igraph_maxflow_stats_t *stats, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, - igraph_integer_t no_of_nodes, - igraph_vector_int_t *distance) { + long int no_of_nodes, + igraph_vector_long_t *distance) { IGRAPH_UNUSED(buckets); - igraph_integer_t bo; + long int bo; (stats->nogap)++; for (bo = b + 1; bo <= no_of_nodes; bo++) { while (!igraph_dbuckets_empty_bucket(ibuckets, bo)) { - igraph_integer_t n = igraph_dbuckets_pop(ibuckets, bo); + long int n = igraph_dbuckets_pop(ibuckets, bo); (stats->nogapnodes)++; DIST(n) = no_of_nodes; } } } -static void igraph_i_mf_relabel(igraph_integer_t v, igraph_integer_t no_of_nodes, - igraph_vector_int_t *distance, - igraph_vector_int_t *first, - igraph_vector_t *rescap, igraph_vector_int_t *to, - igraph_vector_int_t *current, - igraph_maxflow_stats_t *stats, igraph_integer_t *nrelabelsince) { +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) { - igraph_integer_t min = no_of_nodes; - igraph_integer_t k, l, min_edge = 0; + 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++) { @@ -295,14 +296,14 @@ static void igraph_i_mf_relabel(igraph_integer_t v, igraph_integer_t no_of_nodes } } -static void igraph_i_mf_push(igraph_integer_t v, igraph_integer_t e, igraph_integer_t n, - igraph_vector_int_t *current, +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, - igraph_integer_t target, igraph_integer_t source, + long int target, long int source, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, - igraph_vector_int_t *distance, - igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, - igraph_integer_t *npushsince) { + igraph_vector_long_t *distance, + igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats, + int *npushsince) { IGRAPH_UNUSED(current); @@ -313,7 +314,7 @@ static void igraph_i_mf_push(igraph_integer_t v, igraph_integer_t e, igraph_inte (stats->nopush)++; (*npushsince)++; if (EXCESS(n) == 0 && n != target) { igraph_dbuckets_delete(ibuckets, DIST(n), n); - igraph_buckets_add(buckets, DIST(n), n); + igraph_buckets_add(buckets, (long int) DIST(n), n); } RESCAP(e) -= delta; RESCAP(REV(e)) += delta; @@ -321,26 +322,26 @@ static void igraph_i_mf_push(igraph_integer_t v, igraph_integer_t e, igraph_inte EXCESS(v) -= delta; } -static void igraph_i_mf_discharge(igraph_integer_t v, - igraph_vector_int_t *current, - igraph_vector_int_t *first, +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_int_t *to, - igraph_vector_int_t *distance, + igraph_vector_long_t *to, + igraph_vector_long_t *distance, igraph_vector_t *excess, - igraph_integer_t no_of_nodes, igraph_integer_t source, - igraph_integer_t target, igraph_buckets_t *buckets, + long int no_of_nodes, long int source, + long int target, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, - igraph_vector_int_t *rev, + igraph_vector_long_t *rev, igraph_maxflow_stats_t *stats, - igraph_integer_t *npushsince, igraph_integer_t *nrelabelsince) { + int *npushsince, int *nrelabelsince) { do { - igraph_integer_t i; - igraph_integer_t start = CURRENT(v); - igraph_integer_t stop = LAST(v); + 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) { - igraph_integer_t nei = HEAD(i); + long int nei = HEAD(i); if (DIST(v) == DIST(nei) + 1) { PUSH((v), i, nei); if (EXCESS(v) == 0) { @@ -350,7 +351,7 @@ static void igraph_i_mf_discharge(igraph_integer_t v, } } if (i == stop) { - igraph_integer_t origdist = DIST(v); + long int origdist = DIST(v); RELABEL(v); if (igraph_buckets_empty_bucket(buckets, origdist) && igraph_dbuckets_empty_bucket(ibuckets, origdist)) { @@ -367,31 +368,31 @@ static void igraph_i_mf_discharge(igraph_integer_t v, } while (1); } -static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, - igraph_integer_t source, igraph_integer_t target, - igraph_integer_t no_of_nodes, igraph_buckets_t *buckets, +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_int_t *distance, - igraph_vector_int_t *first, igraph_vector_int_t *current, - igraph_vector_int_t *to, igraph_vector_t *excess, - igraph_vector_t *rescap, igraph_vector_int_t *rev) { + 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) { - igraph_integer_t k, l; + long int k, l; IGRAPH_UNUSED(source); igraph_buckets_clear(buckets); igraph_dbuckets_clear(ibuckets); - igraph_vector_int_fill(distance, no_of_nodes); + igraph_vector_long_fill(distance, no_of_nodes); DIST(target) = 0; - IGRAPH_CHECK(igraph_dqueue_int_push(bfsq, target)); - while (!igraph_dqueue_int_empty(bfsq)) { - igraph_integer_t node = igraph_dqueue_int_pop(bfsq); - igraph_integer_t ndist = DIST(node) + 1; + 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) { - igraph_integer_t nei = HEAD(k); + long int nei = HEAD(k); if (DIST(nei) == no_of_nodes) { DIST(nei) = ndist; CURRENT(nei) = FIRST(nei); @@ -400,28 +401,24 @@ static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, } else { igraph_dbuckets_add(ibuckets, ndist, nei); } - IGRAPH_CHECK(igraph_dqueue_int_push(bfsq, nei)); + igraph_dqueue_long_push(bfsq, nei); } } } } - - return IGRAPH_SUCCESS; } /** * \function igraph_maxflow - * \brief Maximum network flow between a pair of vertices. + * Maximum network flow between a pair of vertices * - * This function implements the Goldberg-Tarjan algorithm for + * 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 - * https://doi.org/10.1145/48014.61051. + * the ACM, 35(4), 921-940, 1988. * - * - * The input of the function is a graph, a vector + * 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 @@ -439,16 +436,16 @@ static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, * \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, + * 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 + * 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 + * 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 @@ -484,28 +481,28 @@ static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, * \example examples/simple/flow2.c */ -igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, - igraph_vector_t *flow, igraph_vector_int_t *cut, - igraph_vector_int_t *partition, igraph_vector_int_t *partition2, +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_vcount(graph); - igraph_integer_t no_of_orig_edges = igraph_ecount(graph); + 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_int_t from, to, rev, distance; - igraph_vector_int_t edges, rank; - igraph_vector_int_t current, first; + 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_int_t bfsq; + igraph_dqueue_long_t bfsq; - igraph_integer_t i, j, idx; - igraph_integer_t npushsince = 0, nrelabelsince = 0; + 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 */ @@ -517,14 +514,14 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, IGRAPH_CHECK(igraph_i_maxflow_undirected(graph, value, flow, cut, partition, partition2, source, target, capacity, stats)); - return IGRAPH_SUCCESS; + return 0; } if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) { - IGRAPH_ERROR("Capacity vector must match number of edges in length.", IGRAPH_EINVAL); + 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_EINVVID); + IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); } stats->nopush = stats->norelabel = stats->nogap = stats->nogapnodes = @@ -575,26 +572,26 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, #define EXCESS(i) (VECTOR(excess)[(i)]) #define DIST(i) (VECTOR(distance)[(i)]) - IGRAPH_CHECK(igraph_dqueue_int_init(&bfsq, no_of_nodes)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsq); - IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&rev, no_of_edges); - IGRAPH_VECTOR_INIT_FINALLY(&rescap, no_of_edges); - IGRAPH_VECTOR_INIT_FINALLY(&excess, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&distance, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&first, no_of_nodes + 1); + 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_INT_INIT_FINALLY(&rank, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges); + 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_int_rank(&edges, &rank, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_rank(&edges, &rank, no_of_nodes)); for (i = 0; i < no_of_edges; i += 2) { - igraph_integer_t pos = VECTOR(rank)[i]; - igraph_integer_t pos2 = VECTOR(rank)[i + 1]; + 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]; @@ -613,8 +610,8 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, idx++; VECTOR(first)[idx] = 0; } for (i = 1; i < no_of_edges; i++) { - igraph_integer_t n = (VECTOR(from)[i] - - VECTOR(from)[ VECTOR(first)[idx] ]); + 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; } @@ -624,17 +621,17 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, VECTOR(first)[idx++] = no_of_edges; } - igraph_vector_int_destroy(&from); - igraph_vector_int_destroy(&edges); + igraph_vector_long_destroy(&from); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); if (!flow) { - igraph_vector_int_destroy(&rank); + igraph_vector_destroy(&rank); IGRAPH_FINALLY_CLEAN(1); } /* And the current pointers, initially the same as the first */ - IGRAPH_VECTOR_INT_INIT_FINALLY(¤t, no_of_nodes); + IGRAPH_VECTOR_LONG_INIT_FINALLY(¤t, no_of_nodes); for (i = 0; i < no_of_nodes; i++) { VECTOR(current)[i] = VECTOR(first)[i]; } @@ -656,11 +653,11 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, } } - IGRAPH_CHECK(BFS()); + BFS(); (stats->nobfs)++; while (!igraph_buckets_empty(&buckets)) { - igraph_integer_t vertex = igraph_buckets_popmax(&buckets); + long int vertex = igraph_buckets_popmax(&buckets); DISCHARGE(vertex); if (npushsince > no_of_nodes / 2 && nrelabelsince > no_of_nodes) { (stats->nobfs)++; @@ -679,50 +676,50 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, /* 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_int_t Q; + igraph_dqueue_t Q; igraph_vector_bool_t added; - igraph_integer_t marked = 0; + 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_int_init(&Q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_CHECK(igraph_dqueue_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &Q); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); - VECTOR(added)[target] = true; + igraph_dqueue_push(&Q, target); + VECTOR(added)[(long int)target] = 1; marked++; - while (!igraph_dqueue_int_empty(&Q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + while (!igraph_dqueue_empty(&Q)) { + long int actnode = (long int) igraph_dqueue_pop(&Q); for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { - igraph_integer_t nei = HEAD(i); + long int nei = HEAD(i); if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { - VECTOR(added)[nei] = true; + VECTOR(added)[nei] = 1; marked++; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); } } } - igraph_dqueue_int_destroy(&Q); + 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_int_clear(cut); + igraph_vector_clear(cut); for (i = 0; i < no_of_orig_edges; i++) { - igraph_integer_t f = IGRAPH_FROM(graph, i); - igraph_integer_t t = IGRAPH_TO(graph, 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_int_push_back(cut, i)); + IGRAPH_CHECK(igraph_vector_push_back(cut, i)); } } } if (partition2) { - igraph_integer_t x = 0; - IGRAPH_CHECK(igraph_vector_int_resize(partition2, marked)); + 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; @@ -731,8 +728,8 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, } if (partition) { - igraph_integer_t x = 0; - IGRAPH_CHECK(igraph_vector_int_resize(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]) { @@ -748,57 +745,57 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (flow) { /* Initialize the backward distances, with a breadth-first search from the source */ - igraph_dqueue_int_t Q; + igraph_dqueue_t Q; igraph_vector_int_t added; - igraph_integer_t j, k, l; + long int j, k, l; igraph_t flow_graph; - igraph_vector_int_t flow_edges; + 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_int_init(&Q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, source)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); - VECTOR(added)[source] = 1; - while (!igraph_dqueue_int_empty(&Q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); + 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++) { - igraph_integer_t nei = HEAD(i); + long int nei = HEAD(i); if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { VECTOR(added)[nei] = 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, actdist + 1)); } } - } /* !igraph_dqueue_int_empty(&Q) */ + } /* !igraph_dqueue_empty(&Q) */ igraph_vector_int_destroy(&added); - igraph_dqueue_int_destroy(&Q); + 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, DIST(i), i); + igraph_buckets_add(&buckets, (long int) DIST(i), i); } } /* Now we return the flow to the source */ while (!igraph_buckets_empty(&buckets)) { - igraph_integer_t vertex = igraph_buckets_popmax(&buckets); + long int vertex = igraph_buckets_popmax(&buckets); /* DISCHARGE(vertex) comes here */ do { - for (i = CURRENT(vertex), j = LAST(vertex); i < j; i++) { + for (i = (long int) CURRENT(vertex), j = LAST(vertex); i < j; i++) { if (RESCAP(i) > 0) { - igraph_integer_t nei = HEAD(i); + long int nei = HEAD(i); if (DIST(vertex) == DIST(nei) + 1) { igraph_real_t delta = @@ -808,7 +805,7 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (nei != source && EXCESS(nei) == 0.0 && DIST(nei) != no_of_nodes) { - igraph_buckets_add(&buckets, DIST(nei), nei); + igraph_buckets_add(&buckets, (long int) DIST(nei), nei); } EXCESS(nei) += delta; @@ -825,8 +822,8 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (i == j) { /* RELABEL(vertex) comes here */ - igraph_integer_t min; - igraph_integer_t min_edge = 0; + 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) { @@ -843,7 +840,7 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, DIST(vertex) = min; CURRENT(vertex) = min_edge; /* Vertex is still active */ - igraph_buckets_add(&buckets, DIST(vertex), vertex); + igraph_buckets_add(&buckets, (long int) DIST(vertex), vertex); } /* TODO: gap heuristics here ??? */ @@ -854,7 +851,7 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, break; - } while (true); + } while (1); } /* We need to eliminate flow cycles now. Before that we check that @@ -875,19 +872,19 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, before, 1 if it is currently in 'stack', and 2 if it is not in 'stack', but it was visited before. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&flow_edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&flow_edges, 0); for (i = 0, j = 0; i < no_of_edges; i += 2, j++) { - igraph_integer_t pos = VECTOR(rank)[i]; + long int pos = (long int) VECTOR(rank)[i]; if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) { - IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, + IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, IGRAPH_FROM(graph, j))); - IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, + 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_int_destroy(&flow_edges); + igraph_vector_destroy(&flow_edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &flow_graph); IGRAPH_CHECK(igraph_is_dag(&flow_graph, &dag)); @@ -895,11 +892,11 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, IGRAPH_FINALLY_CLEAN(1); if (!dag) { - igraph_vector_int_t stack; + igraph_vector_long_t stack; igraph_vector_t mycap; - IGRAPH_CHECK(igraph_vector_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &stack); + 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); @@ -907,25 +904,25 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, #define MYCAP(i) (VECTOR(mycap)[(i)]) for (i = 0; i < no_of_edges; i += 2) { - igraph_integer_t pos = VECTOR(rank)[i]; - igraph_integer_t pos2 = VECTOR(rank)[i + 1]; + 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_int_null(¤t); - igraph_vector_int_clear(&stack); + igraph_vector_long_null(¤t); + igraph_vector_long_clear(&stack); igraph_vector_int_null(&added); - IGRAPH_CHECK(igraph_vector_int_push_back(&stack, -1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&stack, source)); - VECTOR(added)[source] = 1; - while (!igraph_vector_int_empty(&stack) && - igraph_vector_int_tail(&stack) != target) { - igraph_integer_t actnode = igraph_vector_int_tail(&stack); - igraph_integer_t edge = FIRST(actnode) + CURRENT(actnode); - igraph_integer_t nei; + 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++; } @@ -934,8 +931,8 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (edge < LAST(actnode) && !VECTOR(added)[nei]) { /* Go forward along next edge, if the vertex was not visited before */ - IGRAPH_CHECK(igraph_vector_int_push_back(&stack, edge)); - IGRAPH_CHECK(igraph_vector_int_push_back(&stack, nei)); + 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) { @@ -943,19 +940,19 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, until we find 'nei' again, determine the flow along the cycle. */ igraph_real_t thisflow = MYCAP(edge); - igraph_integer_t idx; - for (idx = igraph_vector_int_size(&stack) - 2; + long int idx; + for (idx = igraph_vector_long_size(&stack) - 2; idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { - igraph_integer_t e = VECTOR(stack)[idx]; + 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_int_size(&stack) - 2; + for (idx = igraph_vector_long_size(&stack) - 2; idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { - igraph_integer_t e = VECTOR(stack)[idx]; + long int e = VECTOR(stack)[idx]; if (e >= 0) { MYCAP(e) -= thisflow; RESCAP(e) += thisflow; @@ -968,35 +965,35 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, CURRENT(actnode) += 1; } else { /* Go backward, take out the node and the edge that leads to it */ - igraph_vector_int_pop_back(&stack); - igraph_vector_int_pop_back(&stack); + 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_int_empty(&stack)) { - igraph_integer_t pl = igraph_vector_int_size(&stack); + 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) { - igraph_integer_t edge = VECTOR(stack)[i]; + long int edge = VECTOR(stack)[i]; igraph_real_t rcap = MYCAP(edge); if (rcap < thisflow) { thisflow = rcap; } } for (i = 2; i < pl; i += 2) { - igraph_integer_t edge = VECTOR(stack)[i]; + long int edge = VECTOR(stack)[i]; MYCAP(edge) -= thisflow; } } - } while (!igraph_vector_int_empty(&stack)); + } while (!igraph_vector_long_empty(&stack)); igraph_vector_destroy(&mycap); igraph_vector_int_destroy(&added); - igraph_vector_int_destroy(&stack); + igraph_vector_long_destroy(&stack); IGRAPH_FINALLY_CLEAN(3); } @@ -1004,43 +1001,41 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, IGRAPH_CHECK(igraph_vector_resize(flow, no_of_orig_edges)); for (i = 0, j = 0; i < no_of_edges; i += 2, j++) { - igraph_integer_t pos = VECTOR(rank)[i]; + long int pos = (long int) VECTOR(rank)[i]; VECTOR(*flow)[j] = (capacity ? VECTOR(*capacity)[j] : 1.0) - RESCAP(pos); } - igraph_vector_int_destroy(&rank); + igraph_vector_destroy(&rank); IGRAPH_FINALLY_CLEAN(1); } igraph_dbuckets_destroy(&ibuckets); igraph_buckets_destroy(&buckets); - igraph_vector_int_destroy(¤t); - igraph_vector_int_destroy(&first); - igraph_vector_int_destroy(&distance); + 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_int_destroy(&rev); - igraph_vector_int_destroy(&to); - igraph_dqueue_int_destroy(&bfsq); + igraph_vector_long_destroy(&rev); + igraph_vector_long_destroy(&to); + igraph_dqueue_long_destroy(&bfsq); IGRAPH_FINALLY_CLEAN(10); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_maxflow_value - * \brief Maximum flow in a network with the push/relabel algorithm. + * \brief Maximum flow in a network with the push/relabel algorithm * - * This function implements the Goldberg-Tarjan algorithm for + * 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 - * https://doi.org/10.1145/48014.61051. + * the ACM, 35(4), 921-940, 1988. * - * - * The input of the function is a graph, a vector + * 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 @@ -1050,21 +1045,18 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, * 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. + * value. * - * - * According to a theorem by Ford and Fulkerson + * 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 \ref - * igraph_maxflow_value(). + * 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 + * 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. @@ -1083,19 +1075,19 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, * properties based on the maximum flow. */ -igraph_error_t igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, +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=*/ NULL, /*cut=*/ NULL, - /*partition=*/ NULL, /*partition1=*/ NULL, + 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. + * \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 @@ -1106,7 +1098,6 @@ igraph_error_t igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, * 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. @@ -1122,7 +1113,7 @@ igraph_error_t igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, * igraph_maxflow_value(), |V| is the number of vertices. */ -igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, +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) { @@ -1132,30 +1123,29 @@ igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *valu IGRAPH_CHECK(igraph_maxflow_value(graph, value, source, target, capacity, 0)); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_st_mincut - * \brief Minimum cut between a source and a target vertex. + * 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 an initialized vector, the edge IDs that are included + * \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 an initialized vector, the vertex IDs of the + * \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 an initialized vector, the vertex IDs of the + * \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. @@ -1171,9 +1161,9 @@ igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *valu * Time complexity: see \ref igraph_maxflow(). */ -igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_t *cut, igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, +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) { @@ -1182,6 +1172,49 @@ igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, 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) { - igraph_integer_t last; + long int last; igraph_real_t acut; - igraph_integer_t a, n; + long int a, n; igraph_vector_int_t *edges, *edges2; igraph_vector_int_t *neis, *neis2; @@ -1290,9 +1323,9 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, neis = igraph_adjlist_get(&adjlist, a); n = igraph_vector_int_size(edges); for (i = 0; i < n; i++) { - igraph_integer_t edge = VECTOR(*edges)[i]; - igraph_integer_t to = VECTOR(*neis)[i]; - igraph_real_t weight = capacity ? VECTOR(*capacity)[edge] : 1.0; + 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); } @@ -1316,8 +1349,8 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, /* Before actually doing that, make some notes */ act_step++; if (calc_cut) { - IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, a)); - IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, last)); + 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 */ @@ -1355,8 +1388,8 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, neis = igraph_adjlist_get(&adjlist, last); n = igraph_vector_int_size(neis); for (i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; - igraph_integer_t n2, j; + 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++) { @@ -1388,31 +1421,32 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(3); if (calc_cut) { - igraph_integer_t bignode = VECTOR(mergehist)[2 * mincut_step + 1]; - igraph_integer_t i, idx; - igraph_integer_t size = 1; - bool *mark; - - mark = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(mark, "Not enough memory for minimum 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] = true; + mark[bignode] = 1; for (i = mincut_step - 1; i >= 0; i--) { - if ( mark[ VECTOR(mergehist)[2 * i] ] ) { + if ( mark[ (long int) VECTOR(mergehist)[2 * i] ] ) { size++; - mark [ VECTOR(mergehist)[2 * i + 1] ] = true; + mark [ (long int) VECTOR(mergehist)[2 * i + 1] ] = 1; } } /* now store them, if requested */ if (partition) { - IGRAPH_CHECK(igraph_vector_int_resize(partition, size)); + IGRAPH_CHECK(igraph_vector_resize(partition, size)); idx = 0; VECTOR(*partition)[idx++] = bignode; for (i = mincut_step - 1; i >= 0; i--) { - if (mark[ VECTOR(mergehist)[2 * i] ]) { + if (mark[ (long int) VECTOR(mergehist)[2 * i] ]) { VECTOR(*partition)[idx++] = VECTOR(mergehist)[2 * i + 1]; } } @@ -1420,7 +1454,7 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, /* The other partition too? */ if (partition2) { - IGRAPH_CHECK(igraph_vector_int_resize(partition2, no_of_nodes - size)); + IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes - size)); idx = 0; for (i = 0; i < no_of_nodes; i++) { if (!mark[i]) { @@ -1435,77 +1469,77 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, need that anymore. Then we copy it to 'cut'; */ if (cut) { igraph_integer_t from, to; - igraph_vector_int_clear(&mergehist); + igraph_vector_clear(&mergehist); for (i = 0; i < no_of_edges; i++) { - igraph_edge(graph, i, &from, &to); - if ((mark[from] && !mark[to]) || - (mark[to] && !mark[from])) { - IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, 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_int_clear(cut); - IGRAPH_CHECK(igraph_vector_int_append(cut, &mergehist)); + igraph_vector_clear(cut); + IGRAPH_CHECK(igraph_vector_append(cut, &mergehist)); } - IGRAPH_FREE(mark); - igraph_vector_int_destroy(&mergehist); + igraph_free(mark); + igraph_vector_destroy(&mergehist); IGRAPH_FINALLY_CLEAN(2); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, +static int igraph_i_mincut_directed(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, - igraph_vector_int_t *cut, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_vector_t *cut, const igraph_vector_t *capacity) { - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int i; + long int no_of_nodes = igraph_vcount(graph); igraph_real_t flow; igraph_real_t minmaxflow = IGRAPH_INFINITY; - igraph_vector_int_t mypartition, mypartition2, mycut; - igraph_vector_int_t *ppartition = 0, *ppartition2 = 0, *pcut = 0; - igraph_vector_int_t bestpartition, bestpartition2, bestcut; + 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_INT_INIT_FINALLY(&bestpartition, 0); + IGRAPH_VECTOR_INIT_FINALLY(&bestpartition, 0); } if (partition2) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&bestpartition2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&bestpartition2, 0); } if (cut) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&bestcut, 0); + IGRAPH_VECTOR_INIT_FINALLY(&bestcut, 0); } if (partition) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition, 0); + IGRAPH_VECTOR_INIT_FINALLY(&mypartition, 0); ppartition = &mypartition; } if (partition2) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&mypartition2, 0); ppartition2 = &mypartition2; } if (cut) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mycut, 0); + 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=*/ i, capacity, 0)); + /*target=*/ (igraph_integer_t) i, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; if (cut) { - IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); + IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); } if (partition) { - IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); + IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); } if (partition2) { - IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); + IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); } if (minmaxflow == 0) { @@ -1514,18 +1548,18 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, } IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0, pcut, ppartition, ppartition2, - /*source=*/ i, + /*source=*/ (igraph_integer_t) i, /*target=*/ 0, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; if (cut) { - IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); + IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); } if (partition) { - IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); + IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); } if (partition2) { - IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); + IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); } if (minmaxflow == 0) { @@ -1539,34 +1573,34 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, } if (cut) { - igraph_vector_int_destroy(&mycut); + igraph_vector_destroy(&mycut); IGRAPH_FINALLY_CLEAN(1); } if (partition) { - igraph_vector_int_destroy(&mypartition); + igraph_vector_destroy(&mypartition); IGRAPH_FINALLY_CLEAN(1); } if (partition2) { - igraph_vector_int_destroy(&mypartition2); + igraph_vector_destroy(&mypartition2); IGRAPH_FINALLY_CLEAN(1); } if (cut) { - IGRAPH_CHECK(igraph_vector_int_update(cut, &bestcut)); - igraph_vector_int_destroy(&bestcut); + IGRAPH_CHECK(igraph_vector_update(cut, &bestcut)); + igraph_vector_destroy(&bestcut); IGRAPH_FINALLY_CLEAN(1); } if (partition2) { - IGRAPH_CHECK(igraph_vector_int_update(partition2, &bestpartition2)); - igraph_vector_int_destroy(&bestpartition2); + IGRAPH_CHECK(igraph_vector_update(partition2, &bestpartition2)); + igraph_vector_destroy(&bestpartition2); IGRAPH_FINALLY_CLEAN(1); } if (partition) { - IGRAPH_CHECK(igraph_vector_int_update(partition, &bestpartition)); - igraph_vector_int_destroy(&bestpartition); + IGRAPH_CHECK(igraph_vector_update(partition, &bestpartition)); + igraph_vector_destroy(&bestpartition); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1588,7 +1622,6 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, * * 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. @@ -1600,7 +1633,7 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, * 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 + * \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 @@ -1618,11 +1651,11 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, * \example examples/simple/igraph_mincut.c */ -igraph_error_t igraph_mincut(const igraph_t *graph, +int igraph_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, - igraph_vector_int_t *cut, + igraph_vector_t *partition, + igraph_vector_t *partition2, + igraph_vector_t *cut, const igraph_vector_t *capacity) { if (igraph_is_directed(graph)) { @@ -1638,11 +1671,11 @@ igraph_error_t igraph_mincut(const igraph_t *graph, return IGRAPH_SUCCESS; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_mincut_value_undirected(const igraph_t *graph, +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); @@ -1650,7 +1683,7 @@ static igraph_error_t igraph_i_mincut_value_undirected(const igraph_t *graph, /** * \function igraph_mincut_value - * \brief The minimum edge cut in a graph. + * \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 @@ -1684,22 +1717,22 @@ static igraph_error_t igraph_i_mincut_value_undirected(const igraph_t *graph, * documentation of \ref igraph_maxflow_value(). */ -igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, +int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_real_t minmaxflow, flow; - igraph_integer_t i; + long int i; minmaxflow = IGRAPH_INFINITY; if (!igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity)); - return IGRAPH_SUCCESS; + return 0; } for (i = 1; i < no_of_nodes; i++) { - IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, i, + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, (igraph_integer_t) i, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; @@ -1707,7 +1740,7 @@ igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, break; } } - IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, i, 0, + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, (igraph_integer_t) i, 0, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; @@ -1721,137 +1754,133 @@ igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, *res = minmaxflow; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_st_vertex_connectivity_check_errors(const igraph_t *graph, +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_bool_t *done, - igraph_integer_t *no_conn) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t eid; - igraph_bool_t conn; - *done = 1; - *no_conn = 0; + igraph_vconn_nei_t neighbors) { - if (source == target) { - IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); - } + 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); + 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("Source and target vertices connected.", IGRAPH_EINVAL); + 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, &conn)); - if (conn) { + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); + if (conn1) { *res = -1; - return IGRAPH_SUCCESS; + return 0; } break; case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: - IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); - if (conn) { + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); + if (conn1) { *res = no_of_nodes; - return IGRAPH_SUCCESS; + return 0; } break; case IGRAPH_VCONN_NEI_IGNORE: - IGRAPH_CHECK(igraph_get_eid(graph, &eid, source, target, IGRAPH_DIRECTED, /*error=*/ false)); - if (eid >= 0) { - IGRAPH_CHECK(igraph_count_multiple_1(graph, no_conn, eid)); - } break; default: - IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'.", IGRAPH_EINVAL); + IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); break; } - *done = 0; - return IGRAPH_SUCCESS; -} -static igraph_error_t 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) { + /* Create the new graph */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges; - igraph_real_t real_res; - igraph_t newgraph; - igraph_integer_t i, len; - igraph_bool_t done; - igraph_integer_t no_conn; - igraph_vector_int_t incs; - igraph_vector_t capacity; - - IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); - if (done) { - return IGRAPH_SUCCESS; + 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; + } } - /* Create the new graph */ - IGRAPH_CHECK(igraph_i_split_vertices(graph, &newgraph)); - IGRAPH_FINALLY(igraph_destroy, &newgraph); + 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; + } - /* Create the capacity vector, fill it with ones */ - no_of_edges = igraph_ecount(&newgraph); - IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); - igraph_vector_fill(&capacity, 1); - - /* "Disable" the edges incident on the input half of the source vertex - * and the output half of the target vertex */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); - IGRAPH_CHECK(igraph_incident(&newgraph, &incs, source + no_of_nodes, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (i = 0; i < len; i++) { - VECTOR(capacity)[VECTOR(incs)[i]] = 0; - } - IGRAPH_CHECK(igraph_incident(&newgraph, &incs, target, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (i = 0; i < len; i++) { - VECTOR(capacity)[VECTOR(incs)[i]] = 0; - } - igraph_vector_int_destroy(&incs); + 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 + no_of_nodes, &capacity, 0)); - *res = (igraph_integer_t) real_res; - *res -= no_conn; + IGRAPH_CHECK(igraph_maxflow_value(&newgraph, &real_res, + source, target, 0, 0)); + *res = (igraph_integer_t)real_res; - igraph_vector_destroy(&capacity); igraph_destroy(&newgraph); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, +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 done; - igraph_integer_t no_conn; + igraph_bool_t conn; - IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); - if (done) { - return IGRAPH_SUCCESS; + 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)); @@ -1865,12 +1894,12 @@ static igraph_error_t igraph_i_st_vertex_connectivity_undirected(const igraph_t igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_st_vertex_connectivity - * \brief The vertex connectivity of a pair of vertices. + * \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 @@ -1883,7 +1912,6 @@ static igraph_error_t igraph_i_st_vertex_connectivity_undirected(const igraph_t * * 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. @@ -1891,9 +1919,9 @@ static igraph_error_t igraph_i_st_vertex_connectivity_undirected(const igraph_t * \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_NEI_NEGATIVE, return -1. - * \c IGRAPH_VCONN_NEI_NUMBER_OF_NODES, return the number of nodes. - * \c IGRAPH_VCONN_NEI_IGNORE, ignore the fact that the two vertices + * \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? @@ -1907,11 +1935,16 @@ static igraph_error_t igraph_i_st_vertex_connectivity_undirected(const igraph_t * \ref igraph_maxflow_value(). */ -igraph_error_t igraph_st_vertex_connectivity(const igraph_t *graph, +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, @@ -1922,91 +1955,28 @@ igraph_error_t igraph_st_vertex_connectivity(const igraph_t *graph, neighbors)); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_vertex_connectivity_directed( - const igraph_t *graph, igraph_integer_t *res, igraph_bool_t all_edges_are_mutual -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges; - igraph_integer_t i, j, k, len; - igraph_integer_t minconn = no_of_nodes - 1, conn = 0; - igraph_t split_graph; - igraph_vector_t capacity; - igraph_bool_t done; - igraph_integer_t dummy_num_connections; - igraph_vector_int_t incs; - igraph_real_t real_res; - - /* Create the new graph */ - IGRAPH_CHECK(igraph_i_split_vertices(graph, &split_graph)); - IGRAPH_FINALLY(igraph_destroy, &split_graph); - - /* Create the capacity vector, fill it with ones */ - no_of_edges = igraph_ecount(&split_graph); - IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); - igraph_vector_fill(&capacity, 1); +static int igraph_i_vertex_connectivity_directed(const igraph_t *graph, + igraph_integer_t *res) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); + 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 = all_edges_are_mutual ? i + 1 : 0; j < no_of_nodes; j++) { + for (j = 0; j < no_of_nodes; j++) { if (i == j) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - /* Check for easy cases */ - IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors( - graph, &conn, i, j, IGRAPH_VCONN_NEI_NUMBER_OF_NODES, &done, - &dummy_num_connections - )); - - /* 'done' will be set to true if the two vertices are already - * connected, and in this case 'res' will be set to the number of - * nodes-1. - * - * Also, since we used IGRAPH_VCONN_NEI_NUMBER_OF_NODES, - * dummy_num_connections will always be zero, no need to deal with - * it */ - IGRAPH_ASSERT(dummy_num_connections == 0); - - if (!done) { - /* "Disable" the edges incident on the input half of the source vertex - * and the output half of the target vertex */ - IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (k = 0; k < len; k++) { - VECTOR(capacity)[VECTOR(incs)[k]] = 0; - } - IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (k = 0; k < len; k++) { - VECTOR(capacity)[VECTOR(incs)[k]] = 0; - } - - /* Do the maximum flow */ - IGRAPH_CHECK(igraph_maxflow_value( - &split_graph, &real_res, i, j + no_of_nodes, &capacity, 0 - )); - - /* Restore the capacities */ - IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (k = 0; k < len; k++) { - VECTOR(capacity)[VECTOR(incs)[k]] = 1; - } - IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); - len = igraph_vector_int_size(&incs); - for (k = 0; k < len; k++) { - VECTOR(capacity)[VECTOR(incs)[k]] = 1; - } - - conn = (igraph_integer_t) real_res; - } - + 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) { @@ -2014,8 +1984,7 @@ static igraph_error_t igraph_i_vertex_connectivity_directed( } } } - - if (minconn == 0) { + if (conn == 0) { break; } } @@ -2024,15 +1993,10 @@ static igraph_error_t igraph_i_vertex_connectivity_directed( *res = minconn; } - igraph_vector_int_destroy(&incs); - igraph_vector_destroy(&capacity); - igraph_destroy(&split_graph); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_vertex_connectivity_undirected(const igraph_t *graph, +static int igraph_i_vertex_connectivity_undirected(const igraph_t *graph, igraph_integer_t *res) { igraph_t newgraph; @@ -2040,16 +2004,16 @@ static igraph_error_t igraph_i_vertex_connectivity_undirected(const igraph_t *gr IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL)); - IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res, /* all_edges_are_mutual = */ 1)); + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ -static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, +static int igraph_i_connectivity_checks(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t *found) { igraph_bool_t conn; @@ -2058,7 +2022,7 @@ static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, if (igraph_vcount(graph) == 0) { *res = 0; *found = 1; - return IGRAPH_SUCCESS; + return 0; } IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_STRONG)); @@ -2066,12 +2030,12 @@ static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, *res = 0; *found = 1; } else { - igraph_vector_int_t degree; - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + 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_int_min(°ree) == 1) { + if (igraph_vector_min(°ree) == 1) { *res = 1; *found = 1; } @@ -2079,38 +2043,35 @@ static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, /* directed, check both in- & out-degree */ IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); - if (igraph_vector_int_min(°ree) == 1) { + 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_int_min(°ree) == 1) { + if (igraph_vector_min(°ree) == 1) { *res = 1; *found = 1; } } } - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vertex_connectivity - * \brief The vertex connectivity of a graph. + * 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 - * https://doi.org/10.1111/0081-1750.00098. - * + * 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 @@ -2128,10 +2089,10 @@ static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, * and \ref igraph_edge_connectivity(). */ -igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, +int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { - igraph_bool_t ret = false; + igraph_bool_t ret = 0; if (checks) { IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); @@ -2140,18 +2101,18 @@ igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_ /* Are we done yet? */ if (!ret) { if (igraph_is_directed(graph)) { - IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res, /* all_edges_are_mutual = */ 0)); + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res)); } else { IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res)); } } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_st_edge_connectivity - * \brief Edge connectivity of a pair of vertices. + * \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 @@ -2160,7 +2121,6 @@ igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_ * * 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. @@ -2174,7 +2134,7 @@ igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_ * igraph_vertex_connectivity(). */ -igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +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; @@ -2186,7 +2146,7 @@ igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); *res = (igraph_integer_t) flow; - return IGRAPH_SUCCESS; + return 0; } @@ -2201,9 +2161,7 @@ igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer * 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 - * https://doi.org/10.1111/0081-1750.00098. - * + * 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 @@ -2223,9 +2181,9 @@ igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer * \ref igraph_vertex_connectivity(). */ -igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { - igraph_bool_t ret = false; + igraph_bool_t ret = 0; igraph_integer_t number_of_nodes = igraph_vcount(graph); /* igraph_mincut_value returns infinity for the singleton graph, @@ -2234,7 +2192,7 @@ igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t * This is consistent with what other software packages return. */ if (number_of_nodes <= 1) { *res = 0; - return IGRAPH_SUCCESS; + return 0; } /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ @@ -2248,7 +2206,7 @@ igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res = (igraph_integer_t)real_res; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2263,7 +2221,6 @@ igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t * * 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. @@ -2278,7 +2235,7 @@ igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t * igraph_st_edge_connectivity(), \ref igraph_maxflow_value(). */ -igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { @@ -2292,7 +2249,7 @@ igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_ *res = (igraph_integer_t) flow; - return IGRAPH_SUCCESS; + return 0; } /** @@ -2306,7 +2263,6 @@ igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_ * 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. @@ -2320,7 +2276,7 @@ igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_ * igraph_vertex_connectivity(), \ref igraph_maxflow_value(). */ -igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { @@ -2335,18 +2291,19 @@ igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_intege 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 each removed connection. */ + graph. Finally we add 1 for the removed connection(s). */ igraph_es_t es; + igraph_vector_t v; igraph_t newgraph; - igraph_integer_t num_removed_edges; - - IGRAPH_CHECK(igraph_es_all_between(&es, source, target, IGRAPH_DIRECTED)); + 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)); - num_removed_edges = igraph_ecount(graph) - igraph_ecount(&newgraph); if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, @@ -2359,25 +2316,29 @@ igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_intege } if (res) { - *res += num_removed_edges; + *res += 1; } - IGRAPH_FINALLY_CLEAN(2); + 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 { - 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)); - } + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, + source, target, + IGRAPH_VCONN_NEI_IGNORE)); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2389,7 +2350,6 @@ igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_intege * 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 @@ -2409,7 +2369,7 @@ igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_intege * igraph_edge_connectivity(), \ref igraph_mincut_value(). */ -igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, +int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { return igraph_edge_connectivity(graph, res, checks); } @@ -2421,8 +2381,8 @@ igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, * 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. - * + * 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. @@ -2442,11 +2402,11 @@ igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, * \ref igraph_maxflow_value(). */ -igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, +int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks)); - return IGRAPH_SUCCESS; + return 0; } /** @@ -2467,12 +2427,8 @@ igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, * Gomory-Hu tree. See the following paper for more details: * * - * Reference: - * - * * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J - * Comput 19(1):143-155, 1990 - * https://doi.org/10.1137/0219009. + * Comput 19(1):143-155, 1990. * * \param graph The input graph. * \param tree Pointer to an uninitialized graph; the result will be @@ -2491,27 +2447,27 @@ igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, * * \sa \ref igraph_maxflow() */ -igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, +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_int_t neighbors; + igraph_vector_t neighbors; igraph_vector_t flow_values; - igraph_vector_int_t partition; - igraph_vector_int_t partition2; + 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_ERROR("Gomory-Hu tree can only be calculated for undirected graphs", IGRAPH_EINVAL); } /* Allocate memory */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&neighbors, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&flow_values, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&partition, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&partition2, 0); + 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 @@ -2523,30 +2479,30 @@ igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, IGRAPH_PROGRESS("Gomory-Hu tree", (100.0 * (source - 1)) / (no_of_nodes - 1), 0); /* Find its current neighbor in the tree */ - target = VECTOR(neighbors)[source]; + 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)[source] = flow_value; + 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_int_size(&partition); + n = igraph_vector_size(&partition); for (i = 0; i < n; i++) { mid = VECTOR(partition)[i]; if (mid != source) { - if (VECTOR(neighbors)[mid] == target) { - VECTOR(neighbors)[mid] = source; - } else if (VECTOR(neighbors)[target] == mid) { - VECTOR(neighbors)[target] = source; - VECTOR(neighbors)[source] = mid; - VECTOR(flow_values)[source] = VECTOR(flow_values)[target]; - VECTOR(flow_values)[target] = flow_value; + 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; } } } @@ -2555,21 +2511,21 @@ igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, IGRAPH_PROGRESS("Gomory-Hu tree", 100.0, 0); /* Re-use the 'partition' vector as an edge list now */ - IGRAPH_CHECK(igraph_vector_int_resize(&partition, no_of_nodes > 0 ? 2 * (no_of_nodes - 1) : 0)); + 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)[mid] = i; - VECTOR(partition)[mid + 1] = VECTOR(neighbors)[i]; + 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_from_edges here to keep the + /* Create the tree graph; we use igraph_subgraph_edges here to keep the * graph and vertex attributes */ - IGRAPH_CHECK(igraph_subgraph_from_edges(graph, tree, igraph_ess_none(), 0)); + 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_int_destroy(&partition2); - igraph_vector_int_destroy(&partition); - igraph_vector_int_destroy(&neighbors); + igraph_vector_destroy(&partition2); + igraph_vector_destroy(&partition); + igraph_vector_destroy(&neighbors); IGRAPH_FINALLY_CLEAN(3); /* Return the flow values to the caller */ diff --git a/src/vendor/cigraph/src/flow/flow_conversion.c b/src/vendor/cigraph/src/flow/flow_conversion.c deleted file mode 100644 index bebd3fc0040..00000000000 --- a/src/vendor/cigraph/src/flow/flow_conversion.c +++ /dev/null @@ -1,93 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2010-2023 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_conversion.h" -#include "igraph_interface.h" - -#include "flow/flow_internal.h" - -/** - * \function igraph_i_split_vertices - * \brief Splits each vertex in the graph into an input and an output vertex. - * - * This function implements a transformation that allows us to calculate the - * vertex connectivity of a directed graph either for a specific s-t pair or - * for all s-t pairs using flows. The transformation splits each vertex into - * an input vertex and an output vertex. All inbound edges of the original - * vertex are rewired to point to the input vertex, and all outbound edges of - * the original vertex are rewired to original from the output vertex, while - * adding a single directed edge from the input vertex to the output vertex. - * - * - * s-t vertex connectivities can then be calculated on this modified graph by - * setting the capacity of each edge to 1, \em except for the following edges: - * the edges incident of the input half of the source vertex and the edges - * incident on the output half of the target vertex. The max flow on this - * modified graph will be equal to the s-t vertex connectivity of the original - * graph. - * - * - * This function prepares the graph only but does not supply a capacity vector; - * it is the responsibility of the caller to provide the capacities. - * - * - * If the original graph had \em n vertices, he function guarantees that the - * first \em n vertices of the result graph will correspond to the \em output - * halves of the vertices and the remaining \em n vertices will correspond to - * the \em input halves, in the same order as in the original graph. - * - * @param graph the input graph - * @param result an uninitialized graph object; the result will be returned here - */ -igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i; - igraph_vector_int_t edges; - - if (!igraph_is_directed(graph)) { - IGRAPH_ERROR("Input graph must be directed.", IGRAPH_EINVAL); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * (no_of_edges + no_of_nodes))); - IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); - IGRAPH_CHECK(igraph_vector_int_resize(&edges, 2 * (no_of_edges + no_of_nodes))); - - for (i = 0; i < 2 * no_of_edges; i += 2) { - igraph_integer_t to = VECTOR(edges)[i + 1]; - 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(result, &edges, 2 * no_of_nodes, IGRAPH_DIRECTED)); - - igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/flow/flow_internal.h b/src/vendor/cigraph/src/flow/flow_internal.h index a269d0090dd..ccb47846252 100644 --- a/src/vendor/cigraph/src/flow/flow_internal.h +++ b/src/vendor/cigraph/src/flow/flow_internal.h @@ -23,21 +23,18 @@ #ifndef IGRAPH_FLOW_INTERNAL_H #define IGRAPH_FLOW_INTERNAL_H -#include "igraph_datatype.h" -#include "igraph_decls.h" #include "igraph_types.h" -#include "core/estack.h" -#include "core/marked_queue.h" - __BEGIN_DECLS -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_all_st_cuts_pivot( - const igraph_t *graph, const igraph_marked_queue_int_t *S, - const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, - igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg); - -igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result); +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 diff --git a/src/vendor/cigraph/src/flow/st-cuts.c b/src/vendor/cigraph/src/flow/st-cuts.c index 2beb3c11651..1fa7a0ab730 100644 --- a/src/vendor/cigraph/src/flow/st-cuts.c +++ b/src/vendor/cigraph/src/flow/st-cuts.c @@ -1,7 +1,8 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2010-2021 The igraph development team + 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 @@ -28,36 +29,35 @@ #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 "flow/flow_internal.h" #include "graph/attributes.h" -#include "math/safe_intop.h" +#include "flow/flow_internal.h" -typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, - const igraph_marked_queue_int_t *S, - const igraph_estack_t *T, - igraph_integer_t source, - igraph_integer_t target, - igraph_integer_t *v, - igraph_vector_int_t *Isv, - void *arg); +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 - * \brief Even-Tarjan reduction of a graph. + * Even-Tarjan reduction of a graph * * A digraph is created with twice as many vertices and edges. For each - * original vertex \c 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 \c i to \c j, two new edges are created, - * from i' to j'' and from i'' - * to j'. + * 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 @@ -66,7 +66,6 @@ typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, * 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. - * https://doi.org/10.1137/0204043 * * \param graph A graph. Although directness is not checked, this function * is commonly used only on directed graphs. @@ -75,7 +74,7 @@ typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, * \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 indicate infinity). + * are equal to |V| (which is used to mean infinity). * \return Error code. * * Time complexity: O(|E|+|V|). @@ -83,28 +82,20 @@ typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, * \example examples/simple/even_tarjan.c */ -igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, +int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, igraph_vector_t *capacity) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - - igraph_integer_t new_no_of_nodes; - igraph_integer_t new_no_of_edges = no_of_edges * 2; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); - igraph_vector_int_t edges; - igraph_integer_t edgeptr = 0, capptr = 0; - igraph_integer_t i; + long int new_no_of_nodes = no_of_nodes * 2; + long int new_no_of_edges = no_of_nodes + no_of_edges * 2; - IGRAPH_SAFE_MULT(no_of_nodes, 2, &new_no_of_nodes); - IGRAPH_SAFE_ADD(new_no_of_edges, no_of_nodes, &new_no_of_edges); - - /* To ensure the size of the edges vector will not overflow. */ - if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); - } + igraph_vector_t edges; + long int edgeptr = 0, capptr = 0; + long int i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, new_no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, new_no_of_edges * 2); if (capacity) { IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges)); @@ -125,8 +116,8 @@ igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *gra /* Two news edges for each original edge (from,to) becomes (from'',to'), (to'',from') */ for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, 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; @@ -137,26 +128,26 @@ igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *gra } } - IGRAPH_CHECK(igraph_create(graphbar, &edges, new_no_of_nodes, - IGRAPH_DIRECTED)); + IGRAPH_CHECK(igraph_create(graphbar, &edges, (igraph_integer_t) + new_no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_residual_graph(const igraph_t *graph, +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_int_t *tmp) { + igraph_vector_t *tmp) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, no_new_edges = 0; - igraph_integer_t edgeptr = 0, capptr = 0; + 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]) { @@ -164,7 +155,7 @@ static igraph_error_t igraph_i_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_vector_int_resize(tmp, no_new_edges * 2)); + IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); if (residual_capacity) { IGRAPH_CHECK(igraph_vector_resize(residual_capacity, no_new_edges)); } @@ -172,8 +163,8 @@ static igraph_error_t igraph_i_residual_graph(const igraph_t *graph, for (i = 0; i < no_of_edges; i++) { igraph_real_t c = VECTOR(*capacity)[i] - VECTOR(*flow)[i]; if (c > 0) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); + 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) { @@ -182,20 +173,20 @@ static igraph_error_t igraph_i_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes, + IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_residual_graph(const igraph_t *graph, +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_int_t tmp; - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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); @@ -204,27 +195,27 @@ igraph_error_t igraph_residual_graph(const igraph_t *graph, IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual, residual_capacity, flow, &tmp)); - igraph_vector_int_destroy(&tmp); + igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_reverse_residual_graph(const igraph_t *graph, +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_int_t *tmp) { + igraph_vector_t *tmp) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, no_new_edges = 0; - igraph_integer_t edgeptr = 0; + 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; @@ -236,11 +227,11 @@ static igraph_error_t igraph_i_reverse_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_vector_int_resize(tmp, no_new_edges * 2)); + IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, 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; @@ -252,18 +243,18 @@ static igraph_error_t igraph_i_reverse_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes, + IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, +int igraph_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow) { - igraph_vector_int_t tmp; - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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); @@ -271,80 +262,81 @@ igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, if (igraph_vector_size(flow) != no_of_edges) { IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual, flow, &tmp)); - igraph_vector_int_destroy(&tmp); + igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } typedef struct igraph_i_dbucket_t { - igraph_vector_int_t head; - igraph_vector_int_t next; + igraph_vector_long_t head; + igraph_vector_long_t next; } igraph_i_dbucket_t; -static igraph_error_t igraph_i_dbucket_init(igraph_i_dbucket_t *buck, igraph_integer_t size) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&buck->head, size); - IGRAPH_CHECK(igraph_vector_int_init(&buck->next, size)); +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 IGRAPH_SUCCESS; + return 0; } static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { - igraph_vector_int_destroy(&buck->head); - igraph_vector_int_destroy(&buck->next); + igraph_vector_long_destroy(&buck->head); + igraph_vector_long_destroy(&buck->next); } -static igraph_error_t igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, igraph_integer_t bid, - igraph_integer_t elem) { +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 IGRAPH_SUCCESS; + return 0; } -static igraph_integer_t igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, - igraph_integer_t bid) { +static long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, + long int bid) { return VECTOR(buck->head)[bid] == 0; } -static igraph_integer_t igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, igraph_integer_t bid) { - igraph_integer_t elem = VECTOR(buck->head)[bid] - 1; +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 igraph_error_t igraph_i_dominator_LINK(igraph_integer_t v, igraph_integer_t w, - igraph_vector_int_t *ancestor) { +static int igraph_i_dominator_LINK(long int v, long int w, + igraph_vector_long_t *ancestor) { VECTOR(*ancestor)[w] = v + 1; - return IGRAPH_SUCCESS; + return 0; } /* TODO: don't always reallocate path */ -static igraph_error_t igraph_i_dominator_COMPRESS(igraph_integer_t v, - igraph_vector_int_t *ancestor, - igraph_vector_int_t *label, - igraph_vector_int_t *semi) { - igraph_stack_int_t path; - igraph_integer_t w = v; - igraph_integer_t top, pretop; +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_int_init(&path, 10)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &path); + IGRAPH_CHECK(igraph_stack_long_init(&path, 10)); + IGRAPH_FINALLY(igraph_stack_long_destroy, &path); while (VECTOR(*ancestor)[w] != 0) { - IGRAPH_CHECK(igraph_stack_int_push(&path, w)); + IGRAPH_CHECK(igraph_stack_long_push(&path, w)); w = VECTOR(*ancestor)[w] - 1; } - top = igraph_stack_int_pop(&path); - while (!igraph_stack_int_empty(&path)) { - pretop = igraph_stack_int_pop(&path); + 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]]) { @@ -355,16 +347,16 @@ static igraph_error_t igraph_i_dominator_COMPRESS(igraph_integer_t v, top = pretop; } - igraph_stack_int_destroy(&path); + igraph_stack_long_destroy(&path); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, - igraph_vector_int_t *ancestor, - igraph_vector_int_t *label, - igraph_vector_int_t *semi) { +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 { @@ -377,7 +369,7 @@ static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, /** * \function igraph_dominator_tree - * \brief Calculates the dominator tree of a flowgraph. + * 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 @@ -394,27 +386,24 @@ static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, * 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. - * https://doi.org/10.1145/357062.357071 * * \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 \p leftout + * 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 + * \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, -2 is stored here. For - * the root vertex itself, -1 is added. - * \param domtree Pointer to an \em uninitialized igraph_t, - * or \c NULL. If not a null pointer, then the dominator tree - * is returned here. The graph contains the vertices that are unreachable + * 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. - * Graph and vertex attributes are preserved, but edge attributes - * are discarded. - * \param leftout Pointer to an initialized vector object, or \c NULL. If - * not \c NULL, then the IDs of the vertices that are unreachable + * \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 @@ -430,58 +419,64 @@ static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, * \example examples/simple/dominator_tree.c */ -igraph_error_t igraph_dominator_tree(const igraph_t *graph, +int igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, - igraph_vector_int_t *dom, + igraph_vector_t *dom, igraph_t *domtree, - igraph_vector_int_t *leftout, + igraph_vector_t *leftout, igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_adjlist_t succ, pred; - igraph_vector_int_t parent; - igraph_vector_int_t semi; /* +1 always */ - igraph_vector_int_t vertex; /* +1 always */ + igraph_vector_t parent; + igraph_vector_long_t semi; /* +1 always */ + igraph_vector_t vertex; /* +1 always */ igraph_i_dbucket_t bucket; - igraph_vector_int_t ancestor; - igraph_vector_int_t label; + igraph_vector_long_t ancestor; + igraph_vector_long_t label; + + igraph_neimode_t invmode = mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN; - igraph_neimode_t invmode = IGRAPH_REVERSE_MODE(mode); + long int i; - igraph_vector_int_t vdom, *mydom = dom; + igraph_vector_t vdom, *mydom = dom; - igraph_integer_t component_size = 0; + long int component_size = 0; if (root < 0 || root >= no_of_nodes) { - IGRAPH_ERROR("Invalid root vertex ID for dominator tree.", - IGRAPH_EINVVID); + 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_ERROR("Dominator tree of an undirected graph requested", IGRAPH_EINVAL); } if (mode == IGRAPH_ALL) { - IGRAPH_ERROR("Invalid neighbor mode for dominator tree.", + IGRAPH_ERROR("Invalid neighbor mode for dominator tree", IGRAPH_EINVAL); } if (dom) { - IGRAPH_CHECK(igraph_vector_int_resize(dom, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(dom, no_of_nodes)); } else { mydom = &vdom; - IGRAPH_VECTOR_INT_INIT_FINALLY(mydom, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(mydom, no_of_nodes); } - igraph_vector_int_fill(mydom, -2); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&semi, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestor, no_of_nodes); - IGRAPH_CHECK(igraph_vector_int_init_range(&label, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &label); + 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)); @@ -491,26 +486,26 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, /* DFS first, to set semi, vertex and parent, step 1 */ - IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ false, + IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ 0, /*order=*/ &vertex, - /*order_out=*/ NULL, /*parents=*/ &parent, - /*dist=*/ NULL, /*in_callback=*/ NULL, - /*out_callback=*/ NULL, /*extra=*/ NULL)); + /*order_out=*/ 0, /*father=*/ &parent, + /*dist=*/ 0, /*in_callback=*/ 0, + /*out_callback=*/ 0, /*extra=*/ 0)); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - if (VECTOR(vertex)[i] >= 0) { - igraph_integer_t t = VECTOR(vertex)[i]; + 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) { - igraph_integer_t n = no_of_nodes - component_size; - igraph_integer_t p = 0, j; - IGRAPH_CHECK(igraph_vector_int_resize(leftout, n)); + 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 (VECTOR(parent)[j] < -1) { + if (!IGRAPH_FINITE(VECTOR(parent)[j])) { VECTOR(*leftout)[p++] = j; } } @@ -518,12 +513,12 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, /* We need to go over 'pred' because it should contain only the edges towards the target vertex. */ - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (i = 0; i < no_of_nodes; i++) { igraph_vector_int_t *v = igraph_adjlist_get(&pred, i); - igraph_integer_t n = igraph_vector_int_size(v); - for (igraph_integer_t j = 0; j < n; ) { - igraph_integer_t v2 = VECTOR(*v)[j]; - if (VECTOR(parent)[v2] >= -1) { + 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]; @@ -535,22 +530,23 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, /* Now comes the main algorithm, steps 2 & 3 */ - for (igraph_integer_t i = component_size - 1; i > 0; i--) { - igraph_integer_t w = VECTOR(vertex)[i] - 1; + 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); - igraph_integer_t j, n = igraph_vector_int_size(predw); + long int j, n = igraph_vector_int_size(predw); for (j = 0; j < n; j++) { - igraph_integer_t v = VECTOR(*predw)[j]; - igraph_integer_t u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi); + 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, VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w); - igraph_i_dominator_LINK(VECTOR(parent)[w], w, &ancestor); - while (!igraph_i_dbucket_empty(&bucket, VECTOR(parent)[w])) { - igraph_integer_t v = igraph_i_dbucket_delete(&bucket, VECTOR(parent)[w]); - igraph_integer_t u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi); + 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]; } @@ -558,30 +554,30 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, /* Finally, step 4 */ - for (igraph_integer_t i = 1; i < component_size; i++) { - igraph_integer_t w = VECTOR(vertex)[i] - 1; + 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)[VECTOR(*mydom)[w]]; + VECTOR(*mydom)[w] = VECTOR(*mydom)[(long int)VECTOR(*mydom)[w]]; } } - VECTOR(*mydom)[root] = -1; + VECTOR(*mydom)[(long int)root] = -1; igraph_i_dbucket_destroy(&bucket); igraph_adjlist_destroy(&pred); igraph_adjlist_destroy(&succ); - igraph_vector_int_destroy(&label); - igraph_vector_int_destroy(&ancestor); - igraph_vector_int_destroy(&vertex); - igraph_vector_int_destroy(&semi); - igraph_vector_int_destroy(&parent); + 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_int_t edges; - igraph_integer_t ptr = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, component_size * 2 - 2); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - if (i != root && VECTOR(*mydom)[i] >= 0) { + 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; @@ -591,96 +587,98 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, } } } - IGRAPH_CHECK(igraph_create(domtree, &edges, no_of_nodes, + IGRAPH_CHECK(igraph_create(domtree, &edges, (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_I_ATTRIBUTE_DESTROY(domtree); - IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, - /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); + IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, /*graph=*/ 1, /*vertex=*/ 1, + /*edge=*/ 0); } if (!dom) { - igraph_vector_int_destroy(&vdom); + igraph_vector_destroy(&vdom); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t { - igraph_stack_int_t *stack; + igraph_stack_t *stack; igraph_vector_bool_t *nomark; const igraph_vector_bool_t *GammaX; - igraph_integer_t root; - const igraph_vector_int_t *map; + long int root; + const igraph_vector_t *map; } igraph_i_all_st_cuts_minimal_dfs_data_t; -static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_incb( +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_int_t *stack = data->stack; + igraph_stack_t *stack = data->stack; igraph_vector_bool_t *nomark = data->nomark; const igraph_vector_bool_t *GammaX = data->GammaX; - const igraph_vector_int_t *map = data->map; - igraph_integer_t realvid = VECTOR(*map)[vid]; + 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)[realvid]) { - if (!igraph_stack_int_empty(stack)) { - igraph_integer_t top = igraph_stack_int_top(stack); + 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_CHECK(igraph_stack_int_push(stack, realvid)); + igraph_stack_push(stack, realvid); /* TODO: error check */ } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_outcb( +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_int_t *stack = data->stack; - const igraph_vector_int_t *map = data->map; - igraph_integer_t realvid = VECTOR(*map)[vid]; + 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_int_empty(stack) && - igraph_stack_int_top(stack) == realvid) { - igraph_stack_int_pop(stack); + if (!igraph_stack_empty(stack) && + igraph_stack_top(stack) == realvid) { + igraph_stack_pop(stack); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, +static int igraph_i_all_st_cuts_minimal(const igraph_t *graph, const igraph_t *domtree, - igraph_integer_t root, - const igraph_marked_queue_int_t *X, + long int root, + const igraph_marked_queue_t *X, const igraph_vector_bool_t *GammaX, - const igraph_vector_int_t *invmap, - igraph_vector_int_t *minimal) { + const igraph_vector_t *invmap, + igraph_vector_t *minimal) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_stack_int_t stack; + 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; - igraph_integer_t i; + long int i; IGRAPH_UNUSED(X); - IGRAPH_STACK_INT_INIT_FINALLY(&stack, 10); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&nomark, no_of_nodes); + 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; @@ -692,87 +690,91 @@ static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, 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); + 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, root, IGRAPH_IN, - /*unreachable=*/ false, /*order=*/ NULL, - /*order_out=*/ NULL, /*parents=*/ NULL, - /*dist=*/ NULL, - /*in_callback=*/ igraph_i_all_st_cuts_minimal_dfs_incb, - /*out_callback=*/ igraph_i_all_st_cuts_minimal_dfs_outcb, + 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_int_clear(minimal); + igraph_vector_clear(minimal); for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(nomark)[i]) { - IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i)); + IGRAPH_CHECK(igraph_vector_push_back(minimal, i)); } } igraph_vector_bool_destroy(&nomark); - igraph_stack_int_destroy(&stack); + igraph_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /* not 'static' because used in igraph_all_st_cuts.c test program */ -igraph_error_t igraph_i_all_st_cuts_pivot( - const igraph_t *graph, const igraph_marked_queue_int_t *S, - const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, - igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg -) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); +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_int_t Sbar_map, Sbar_invmap; - igraph_vector_int_t keep; + igraph_vector_t Sbar_map, Sbar_invmap; + igraph_vector_t keep; igraph_t domtree; - igraph_vector_int_t leftout; - igraph_integer_t i, nomin, n; - igraph_integer_t root; - igraph_vector_int_t M; + igraph_vector_t leftout; + long int i, nomin, n; + long int root; + igraph_vector_t M; igraph_vector_bool_t GammaS; - igraph_vector_int_t Nuv; - igraph_vector_int_t Isv_min; - igraph_vector_int_t GammaS_vec; - igraph_integer_t Sbar_size; + 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_INT_INIT_FINALLY(&Sbar_map, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0); + IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); for (i = 0; i < no_of_nodes; i++) { - if (!igraph_marked_queue_int_iselement(S, i)) { - IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); + if (!igraph_marked_queue_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_push_back(&keep, i)); } } - Sbar_size = igraph_vector_int_size(&keep); + 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_int_destroy(&keep); + igraph_vector_destroy(&keep); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &Sbar); - root = VECTOR(Sbar_map)[target] - 1; + root = (long int) VECTOR(Sbar_map)[target] - 1; /* -------------------------------------------------------------*/ /* Construct the dominator tree of Sbar */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&leftout, 0); - IGRAPH_CHECK(igraph_dominator_tree(&Sbar, root, + 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); @@ -785,24 +787,24 @@ igraph_error_t igraph_i_all_st_cuts_pivot( /* 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_int_size(S) == 0) { - VECTOR(GammaS)[VECTOR(Sbar_map)[source] - 1] = 1; + 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_int_iselement(S, i)) { - igraph_vector_int_t neis; - igraph_integer_t j; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, 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_int_size(&neis); + n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(neis)[j]; - if (!igraph_marked_queue_int_iselement(S, nei)) { + long int nei = (long int) VECTOR(neis)[j]; + if (!igraph_marked_queue_iselement(S, nei)) { VECTOR(GammaS)[nei] = 1; } } - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } } @@ -812,195 +814,180 @@ igraph_error_t igraph_i_all_st_cuts_pivot( 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_int_size(&leftout); + n = igraph_vector_size(&leftout); for (i = 0; i < n; i++) { - VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[VECTOR(leftout)[i]]; - VECTOR(GammaS)[VECTOR(leftout)[i]] = 0; + VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[(long int)VECTOR(leftout)[i]]; + VECTOR(GammaS)[(long int)VECTOR(leftout)[i]] = 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 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_int_clear(Isv); - IGRAPH_VECTOR_INT_INIT_FINALLY(&Nuv, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&GammaS_vec, 0); + 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_int_push_back(&GammaS_vec, i)); + IGRAPH_CHECK(igraph_vector_push_back(&GammaS_vec, i)); } } - nomin = igraph_vector_int_size(&M); + 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. */ - igraph_integer_t min = VECTOR(Sbar_map)[ VECTOR(M)[i] ] - 1; - igraph_integer_t nuvsize, isvlen, j; - IGRAPH_CHECK(igraph_dfs(&domtree, min, IGRAPH_IN, - /*unreachable=*/ NULL, /*order=*/ &Nuv, - /*order_out=*/ NULL, /*parents=*/ NULL, /*dist=*/ NULL, - /*in_callback=*/ NULL, /*out_callback=*/ NULL, - /*extra=*/ NULL)); - /* Remove the negative values from the end of the vector */ + 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_integer_t t = VECTOR(Nuv)[nuvsize]; - if (t >= 0) { - VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[t]; + igraph_real_t t = VECTOR(Nuv)[nuvsize]; + if (IGRAPH_FINITE(t)) { + VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[(long int) t]; } else { break; } } - igraph_vector_int_resize(&Nuv, nuvsize); /* shrinks, error safe */ + 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=*/ false, + /*mode=*/ IGRAPH_OUT, /*unreachable=*/ 0, /*restricted=*/ &Nuv, - /*order=*/ &Isv_min, /*rank=*/ NULL, - /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, - /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); + /*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 (VECTOR(Isv_min)[isvlen] < 0) { + if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { break; } } - igraph_vector_int_resize(&Isv_min, isvlen); + 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++) { - igraph_integer_t u = VECTOR(Isv_min)[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 = VECTOR(M)[i]; + *v = (long int) VECTOR(M)[i]; /* Calculate real Isv */ - IGRAPH_CHECK(igraph_vector_int_append(&Nuv, &leftout)); - IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, - /*roots=*/ NULL, /*mode=*/ IGRAPH_OUT, - /*unreachable=*/ false, /*restricted=*/ &Nuv, - /*order=*/ &Isv_min, /*rank=*/ NULL, - /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, - /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); + 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 (VECTOR(Isv_min)[isvlen] < 0) { + if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { break; } } - igraph_vector_int_resize(&Isv_min, isvlen); - igraph_vector_int_update(Isv, &Isv_min); + igraph_vector_resize(&Isv_min, isvlen); + igraph_vector_update(Isv, &Isv_min); break; } } - igraph_vector_int_destroy(&GammaS_vec); - igraph_vector_int_destroy(&Isv_min); - igraph_vector_int_destroy(&Nuv); + igraph_vector_destroy(&GammaS_vec); + igraph_vector_destroy(&Isv_min); + igraph_vector_destroy(&Nuv); IGRAPH_FINALLY_CLEAN(3); - igraph_vector_int_destroy(&M); + igraph_vector_destroy(&M); igraph_vector_bool_destroy(&GammaS); igraph_destroy(&domtree); - igraph_vector_int_destroy(&leftout); + igraph_vector_destroy(&leftout); igraph_destroy(&Sbar); - igraph_vector_int_destroy(&Sbar_map); - igraph_vector_int_destroy(&Sbar_invmap); + igraph_vector_destroy(&Sbar_map); + igraph_vector_destroy(&Sbar_invmap); IGRAPH_FINALLY_CLEAN(7); - return IGRAPH_SUCCESS; + return 0; } -/* TODO: This is a temporary recursive version */ - -static igraph_error_t igraph_i_provan_shier_list_recursive( - const igraph_t *graph, igraph_marked_queue_int_t *S, - igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, - igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, - igraph_vector_int_t *Isv, void *pivot_arg -) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t v = 0; - igraph_integer_t i, n; - - pivot(graph, S, T, source, target, &v, Isv, pivot_arg); - - if (igraph_vector_int_empty(Isv)) { - if (igraph_marked_queue_int_size(S) != 0 && igraph_marked_queue_int_size(S) != no_of_nodes) { - igraph_vector_int_t *vec; - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(result, &vec)); - IGRAPH_CHECK(igraph_marked_queue_int_as_vector(S, vec)); +/* 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 { - /* Add Isv to S */ - IGRAPH_CHECK(igraph_marked_queue_int_start_batch(S)); - n = igraph_vector_int_size(Isv); - for (i = 0; i < n; i++) { - if (!igraph_marked_queue_int_iselement(S, VECTOR(*Isv)[i])) { - IGRAPH_CHECK(igraph_marked_queue_int_push(S, VECTOR(*Isv)[i])); - } - } - igraph_vector_int_clear(Isv); - - /* Go down right in the search tree */ - IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( - graph, S, T, source, target, result, pivot, Isv, pivot_arg)); - - /* Take out Isv from S */ - igraph_marked_queue_int_pop_back_batch(S); - /* Put v into T */ - IGRAPH_CHECK(igraph_estack_push(T, v)); + igraph_estack_push(T, v); /* Go down left in the search tree */ - IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( - graph, S, T, source, target, result, pivot, Isv, pivot_arg)); + igraph_provan_shier_list(graph, S, T, source, target, + result, pivot, pivot_arg); /* Take out v from T */ igraph_estack_pop(T); - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_provan_shier_list( - const igraph_t *graph, igraph_marked_queue_int_t *S, - igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, - igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, - void *pivot_arg -) { - igraph_vector_int_t Isv; + /* 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]); + } + } - IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv, 0); + /* Go down right in the search tree */ - IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( - graph, S, T, source, target, result, pivot, &Isv, pivot_arg - )); + igraph_provan_shier_list(graph, S, T, source, target, + result, pivot, pivot_arg); - /* Reverse the result to stay compatible with versions before 0.10.3 */ - IGRAPH_CHECK(igraph_vector_int_list_reverse(result)); + /* Take out Isv from S */ + igraph_marked_queue_pop_back_batch(S); + } - igraph_vector_int_destroy(&Isv); - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_destroy(&Isv); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1013,15 +1000,22 @@ static igraph_error_t igraph_provan_shier_list( * (s,t)-cuts in graphs, Algorithmica 15, 351--372, 1996. * * \param graph The input graph, is must be directed. - * \param cuts An initialized list of integer vectors, the cuts are stored - * here. Each vector will contain the IDs of the edges in + * \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. - * \param partition1s An initialized list of integer vectors, 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 + * 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 + * 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. @@ -1030,9 +1024,9 @@ static igraph_error_t igraph_provan_shier_list( * vertices, |E| is the number of edges, and n is the number of cuts. */ -igraph_error_t igraph_all_st_cuts(const igraph_t *graph, - igraph_vector_int_list_t *cuts, - igraph_vector_int_list_t *partition1s, +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) { @@ -1043,13 +1037,12 @@ igraph_error_t igraph_all_st_cuts(const igraph_t *graph, Every element is included at most once. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_marked_queue_int_t S; + 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_int_list_t *mypartition1s = partition1s, vpartition1s; - igraph_vector_int_t cut; - igraph_integer_t i, nocuts; + 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 " @@ -1058,17 +1051,20 @@ igraph_error_t igraph_all_st_cuts(const igraph_t *graph, if (!partition1s) { mypartition1s = &vpartition1s; - IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s); + IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s); } else { - igraph_vector_int_list_clear(mypartition1s); + igraph_vector_ptr_clear(mypartition1s); } - IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S); + 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); + + if (cuts) { + igraph_vector_ptr_clear(cuts); + } /* We call it with S={}, T={} */ IGRAPH_CHECK(igraph_provan_shier_list(graph, &S, &T, @@ -1076,64 +1072,73 @@ igraph_error_t igraph_all_st_cuts(const igraph_t *graph, igraph_i_all_st_cuts_pivot, /*pivot_arg=*/ 0)); - nocuts = igraph_vector_int_list_size(mypartition1s); + nocuts = igraph_vector_ptr_size(mypartition1s); if (cuts) { - igraph_vector_int_t inS; - IGRAPH_CHECK(igraph_vector_int_init(&inS, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &inS); - igraph_vector_int_list_clear(cuts); - IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts)); + 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_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); - igraph_integer_t cutsize = 0; - igraph_integer_t j, partlen = igraph_vector_int_size(part); + 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++) { - igraph_integer_t v = VECTOR(*part)[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++) { - igraph_integer_t from = IGRAPH_FROM(graph, j); - igraph_integer_t to = IGRAPH_TO(graph, j); - igraph_integer_t pfrom = VECTOR(inS)[from]; - igraph_integer_t pto = VECTOR(inS)[to]; + 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 */ - IGRAPH_CHECK(igraph_vector_int_resize(&cut, cutsize)); + 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++) { - igraph_integer_t from = IGRAPH_FROM(graph, j); - igraph_integer_t to = IGRAPH_TO(graph, j); - igraph_integer_t pfrom = VECTOR(inS)[from]; - igraph_integer_t pto = VECTOR(inS)[to]; + 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(*cut)[cutsize++] = j; } } - /* Add the vector to 'cuts' */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); + VECTOR(*cuts)[i] = cut; + IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&inS); + igraph_vector_long_destroy(&inS); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&cut); igraph_estack_destroy(&T); - igraph_marked_queue_int_destroy(&S); - IGRAPH_FINALLY_CLEAN(3); + igraph_marked_queue_destroy(&S); + IGRAPH_FINALLY_CLEAN(2); if (!partition1s) { - igraph_vector_int_list_destroy(mypartition1s); + 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 IGRAPH_SUCCESS; + return 0; } /* We need to find the minimal active elements of Sbar. I.e. all @@ -1148,32 +1153,33 @@ igraph_error_t igraph_all_st_cuts(const igraph_t *graph, zero-indegree vertices. */ -static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, +static int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, const igraph_vector_bool_t *active, - const igraph_vector_int_t *invmap, - igraph_vector_int_t *minimal) { + const igraph_vector_t *invmap, + igraph_vector_t *minimal) { - igraph_integer_t no_of_nodes = igraph_vcount(Sbar); - igraph_vector_int_t indeg; - igraph_integer_t i, minsize; - igraph_vector_int_t neis; + long int no_of_nodes = igraph_vcount(Sbar); + igraph_vector_t indeg; + long int i, minsize; + igraph_vector_t neis; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg, no_of_nodes); + 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)[VECTOR(*invmap)[(x)]]) +#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)) { - igraph_integer_t j, n; - IGRAPH_CHECK(igraph_neighbors(Sbar, &neis, i, IGRAPH_OUT)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; VECTOR(indeg)[nei] -= 1; } } @@ -1185,7 +1191,7 @@ static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, } } - IGRAPH_CHECK(igraph_vector_int_resize(minimal, minsize)); + IGRAPH_CHECK(igraph_vector_resize(minimal, minsize)); for (minsize = 0, i = 0; i < no_of_nodes; i++) { if (ACTIVE(i) && ZEROIN(i)) { @@ -1196,52 +1202,52 @@ static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, #undef ACTIVE #undef ZEROIN - igraph_vector_int_destroy(&indeg); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + 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 igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, - const igraph_marked_queue_int_t *S, +static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, + const igraph_marked_queue_t *S, const igraph_estack_t *T, - igraph_integer_t source, - igraph_integer_t target, - igraph_integer_t *v, - igraph_vector_int_t *Isv, + 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; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j; - igraph_vector_int_t Sbar_map, Sbar_invmap; - igraph_vector_int_t keep; + 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_int_t M; - igraph_integer_t nomin; + igraph_vector_t M; + long int nomin; IGRAPH_UNUSED(source); IGRAPH_UNUSED(target); - if (igraph_marked_queue_int_size(S) == no_of_nodes) { - igraph_vector_int_clear(Isv); - return IGRAPH_SUCCESS; + if (igraph_marked_queue_size(S) == no_of_nodes) { + igraph_vector_clear(Isv); + return 0; } /* Create the graph induced by Sbar */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_map, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&keep, 0); + IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); for (i = 0; i < no_of_nodes; i++) { - if (!igraph_marked_queue_int_iselement(S, i)) { - IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); + if (!igraph_marked_queue_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_push_back(&keep, i)); } } @@ -1257,16 +1263,16 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, /* ------------------------------------------------------------- */ /* Identify the set M of minimal elements that are active */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 0); + 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_int_clear(Isv); - nomin = igraph_vector_int_size(&M); + igraph_vector_clear(Isv); + nomin = igraph_vector_size(&M); for (i = 0; i < nomin; i++) { - igraph_integer_t min = VECTOR(Sbar_invmap)[ VECTOR(M)[i] ]; + long int min = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ]; if (min != target) if (!igraph_estack_iselement(T, min)) { break; @@ -1275,42 +1281,42 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, if (i != nomin) { /* OK, we found a pivot element. I(S,v) contains all elements that can reach the pivot element */ - igraph_vector_int_t Isv_min; - IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); - *v = VECTOR(Sbar_invmap)[ VECTOR(M)[i] ]; + 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=*/ *v,/*roots=*/ NULL, - /*mode=*/ IGRAPH_IN, /*unreachable=*/ false, + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v,/*roots=*/ 0, + /*mode=*/ IGRAPH_IN, /*unreachable=*/ 0, /*restricted=*/ &keep, /*order=*/ &Isv_min, - /*rank=*/ NULL, /*parents=*/ NULL, /*pred=*/ NULL, - /*succ=*/ NULL, /*dist=*/ NULL, /*callback=*/ NULL, - /*extra=*/ NULL)); + /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0, + /*succ=*/ 0, /*dist=*/ 0, /*callback=*/ 0, + /*extra=*/ 0)); for (j = 0; j < no_of_nodes; j++) { - igraph_integer_t u = VECTOR(Isv_min)[j]; - if (u < 0) { + igraph_real_t u = VECTOR(Isv_min)[j]; + if (!IGRAPH_FINITE(u)) { break; } if (!igraph_estack_iselement(T, u)) { - IGRAPH_CHECK(igraph_vector_int_push_back(Isv, u)); + IGRAPH_CHECK(igraph_vector_push_back(Isv, u)); } } - igraph_vector_int_destroy(&Isv_min); + igraph_vector_destroy(&Isv_min); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&M); + igraph_vector_destroy(&M); igraph_destroy(&Sbar); - igraph_vector_int_destroy(&keep); - igraph_vector_int_destroy(&Sbar_invmap); - igraph_vector_int_destroy(&Sbar_map); + igraph_vector_destroy(&keep); + igraph_vector_destroy(&Sbar_invmap); + igraph_vector_destroy(&Sbar_map); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_all_st_mincuts - * \brief All minimum s-t cuts of a directed graph. + * 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 @@ -1325,16 +1331,15 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, * \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 \ref igraph_vector_int_t - * objects. Each vector will contain the IDs of the edges in + * 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_int_destroy() and then \ref igraph_free() on + * \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. It is a list of pointers to \ref igraph_vector_int_t - * objects. Each vector contains a set of vertex IDs. If X is such + * 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. @@ -1353,124 +1358,135 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, * \example examples/simple/igraph_all_st_mincuts.c */ -igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, - igraph_vector_int_list_t *cuts, - igraph_vector_int_list_t *partition1s, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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_int_t NtoL; - igraph_vector_int_t cut; - igraph_integer_t newsource, newtarget; - igraph_marked_queue_int_t S; + 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_integer_t i, nocuts; + 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_int_list_t closedsets; - igraph_vector_int_list_t *mypartition1s = partition1s, vpartition1s; + 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); + 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_EINVVID); + IGRAPH_ERROR("Invalid `source' vertex", IGRAPH_EINVAL); } if (target < 0 || target >= no_of_nodes) { - IGRAPH_ERROR("Invalid target vertex.", IGRAPH_EINVVID); + IGRAPH_ERROR("Invalid `target' vertex", IGRAPH_EINVAL); } if (source == target) { - IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); - } - if (capacity && igraph_vector_size(capacity) != no_of_edges) { - IGRAPH_ERROR("Capacity vector length must agree with number of edges.", IGRAPH_EINVAL); + IGRAPH_ERROR("`source' and 'target' are the same vertex", IGRAPH_EINVAL); } - if (capacity && no_of_edges > 0 && igraph_vector_min(capacity) <= 0) { + 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_int_list_init(mypartition1s, 0)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s); + 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=*/ NULL, - /*partition1=*/ NULL, /*partition2=*/ NULL, + 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_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual, + &flow)); IGRAPH_FINALLY(igraph_destroy, &residual); /* -------------------------------------------------------------------- */ /* We shrink it to its strongly connected components */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&NtoL, 0); - IGRAPH_CHECK(igraph_connected_components( - &residual, /*membership=*/ &NtoL, /*csize=*/ NULL, - /*no=*/ &proj_nodes, IGRAPH_STRONG - )); - IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, /*vertex_comb=*/ NULL)); - IGRAPH_CHECK(igraph_simplify(&residual, /*multiple=*/ true, /*loops=*/ true, /*edge_comb=*/ NULL)); - - newsource = VECTOR(NtoL)[source]; - newtarget = VECTOR(NtoL)[target]; + 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) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - igraph_integer_t pfrom = VECTOR(NtoL)[from]; - igraph_integer_t pto = VECTOR(NtoL)[to]; + 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] = true; + VECTOR(VE1bool)[pfrom] = 1; + VE1size++; } if (!VECTOR(VE1bool)[pto]) { - VECTOR(VE1bool)[pto] = true; + 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_int_list_clear(cuts); + igraph_vector_ptr_clear(cuts); } if (partition1s) { - igraph_vector_int_list_clear(partition1s); + igraph_vector_ptr_clear(partition1s); } /* -------------------------------------------------------------------- */ /* Everything is ready, list the cuts, using the right PIVOT function */ - IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S); + 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); pivot_data.active = &VE1bool; - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&closedsets, 0); + 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, @@ -1481,86 +1497,96 @@ igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value 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++) { - igraph_integer_t id = VECTOR(NtoL)[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_int_list_size(&closedsets); - igraph_vector_int_list_clear(mypartition1s); - IGRAPH_CHECK(igraph_vector_int_list_reserve(mypartition1s, nocuts)); + 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_int_t *supercut = igraph_vector_int_list_get_ptr(&closedsets, i); - igraph_integer_t j, supercutsize = igraph_vector_int_size(supercut); - - igraph_vector_int_clear(&cut); + 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++) { - igraph_integer_t vtx = VECTOR(*supercut)[j]; - igraph_integer_t ovtx = VECTOR(revmap_ptr)[vtx]; + 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_int_push_back(&cut, ovtx)); - ovtx = VECTOR(revmap_next)[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_CHECK(igraph_vector_int_list_push_back_copy(mypartition1s, &cut)); - - /* TODO: we could already reclaim the memory taken by 'supercut' here */ + igraph_vector_destroy(supercut); + igraph_free(supercut); + VECTOR(closedsets)[i] = 0; } igraph_vector_destroy(&revmap_next); igraph_vector_destroy(&revmap_ptr); - igraph_vector_int_list_destroy(&closedsets); + igraph_vector_ptr_destroy(&closedsets); IGRAPH_FINALLY_CLEAN(3); /* Create cuts in original graph */ if (cuts) { - igraph_vector_int_t memb; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&memb, no_of_nodes); - IGRAPH_CHECK(igraph_vector_int_list_reserve(cuts, nocuts)); - + 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_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); - igraph_integer_t j, n = igraph_vector_int_size(part); - - igraph_vector_int_clear(&cut); + 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++) { - igraph_integer_t vtx = VECTOR(*part)[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) { - igraph_integer_t from = IGRAPH_FROM(graph, j); - igraph_integer_t to = IGRAPH_TO(graph, j); + 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_int_push_back(&cut, j)); + IGRAPH_CHECK(igraph_vector_push_back(v, j)); /* TODO: allocation */ } } } - - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); + VECTOR(*cuts)[i] = v; + IGRAPH_FINALLY_CLEAN(1); } - - igraph_vector_int_destroy(&memb); + igraph_vector_long_destroy(&memb); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&cut); igraph_estack_destroy(&T); - igraph_marked_queue_int_destroy(&S); + igraph_marked_queue_destroy(&S); igraph_vector_bool_destroy(&VE1bool); - igraph_vector_int_destroy(&NtoL); + igraph_vector_destroy(&VE1); + igraph_vector_destroy(&NtoL); igraph_destroy(&residual); igraph_vector_destroy(&flow); IGRAPH_FINALLY_CLEAN(7); if (!partition1s) { - igraph_vector_int_list_destroy(mypartition1s); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/games/barabasi.c b/src/vendor/cigraph/src/games/barabasi.c index d18619b9c64..f83d234c030 100644 --- a/src/vendor/cigraph/src/games/barabasi.c +++ b/src/vendor/cigraph/src/games/barabasi.c @@ -31,56 +31,49 @@ #include "igraph_random.h" #include "core/interruption.h" -#include "math/safe_intop.h" -/* Attraction function for barabasi_game. - * We special-case power == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ -static igraph_real_t attraction(igraph_real_t degree, igraph_real_t power, igraph_real_t A) { - return ( power == 0 ? 1.0 : pow(degree, power) ) + A; -} - -static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, +static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_bool_t directed, const igraph_t *start_from); -static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, +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_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from); -static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, +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_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from); -static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, +static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_bool_t directed, const igraph_t *start_from) { - igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_neighbors = m; - igraph_integer_t *bag; - igraph_integer_t bagp = 0; - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t resp; - igraph_integer_t i, j, k; - igraph_integer_t bagsize, start_nodes, start_edges, new_edges, no_of_edges; + 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; @@ -89,52 +82,43 @@ static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer start_nodes = start_from ? igraph_vcount(start_from) : 1; start_edges = start_from ? igraph_ecount(start_from) : 0; if (outseq) { - if (igraph_vector_int_size(outseq) > 1) { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); - new_edges -= VECTOR(*outseq)[0]; + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); } else { new_edges = 0; } } else { - IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); - } - IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + 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; - IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); - if (outpref) { - IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); - } + bagsize = no_of_nodes + no_of_edges + (outpref ? no_of_edges : 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); - bag = IGRAPH_CALLOC(bagsize, igraph_integer_t); + bag = IGRAPH_CALLOC(bagsize, long int); if (bag == 0) { - IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, bag); /* The first node(s) in the bag */ if (start_from) { - igraph_vector_int_t deg; - igraph_integer_t ii, jj, sn = igraph_vcount(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_INT_INIT_FINALLY(°, sn); + IGRAPH_VECTOR_INIT_FINALLY(°, sn); IGRAPH_CHECK(igraph_degree(start_from, °, igraph_vss_all(), mm, IGRAPH_LOOPS)); for (ii = 0; ii < sn; ii++) { - igraph_integer_t d = VECTOR(deg)[ii]; + long int d = (long int) VECTOR(deg)[ii]; for (jj = 0; jj <= d; jj++) { bag[bagp++] = ii; } } - igraph_vector_int_destroy(°); + igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(1); } else { bag[bagp++] = 0; @@ -142,8 +126,8 @@ static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer /* Initialize the edges vector */ if (start_from) { - IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); - igraph_vector_int_resize(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + igraph_vector_resize(&edges, no_of_edges * 2); } RNG_BEGIN(); @@ -157,17 +141,17 @@ static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer /* draw edges */ if (outseq) { - no_of_neighbors = VECTOR(*outseq)[k]; + no_of_neighbors = (long int) VECTOR(*outseq)[k]; } for (j = 0; j < no_of_neighbors; j++) { - igraph_integer_t to = bag[RNG_INTEGER(0, bagp - 1)]; + 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++] = VECTOR(edges)[resp - 2 * j - 1]; + bag[bagp++] = (long int) VECTOR(edges)[resp - 2 * j - 1]; if (outpref) { bag[bagp++] = i; } @@ -177,31 +161,32 @@ static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer RNG_END(); IGRAPH_FREE(bag); - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, +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_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from) { - igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_neighbors = m; - igraph_vector_int_t edges; - igraph_integer_t i, j, k; + 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; - igraph_integer_t edgeptr = 0; - igraph_vector_int_t degree; - igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; + long int edgeptr = 0; + igraph_vector_t degree; + long int start_nodes, start_edges, new_edges, no_of_edges; if (!directed) { outpref = 1; @@ -210,61 +195,54 @@ static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, start_nodes = start_from ? igraph_vcount(start_from) : 1; start_edges = start_from ? igraph_ecount(start_from) : 0; if (outseq) { - if (igraph_vector_int_size(outseq) > 1) { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); - new_edges -= VECTOR(*outseq)[0]; + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); } else { new_edges = 0; } } else { - IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); - } - IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; } + no_of_edges = start_edges + new_edges; edgeptr = start_edges * 2; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_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_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - /* First node(s): */ + /* first node(s) */ if (start_from) { - igraph_integer_t ii, sn = igraph_vcount(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_int_resize(°ree, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); for (ii = 0; ii < sn; ii++) { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); } } else { - /* Any weight may be used for the first node. In the first step, it will be connected to - * with certainty, after which its weight will be set appropriately. */ - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); } /* Initialize the edges vector */ if (start_from) { - IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); - igraph_vector_int_resize(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + igraph_vector_resize(&edges, no_of_edges * 2); } RNG_BEGIN(); - /* And the rest: */ + /* 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); - igraph_integer_t to; + long int to; IGRAPH_ALLOW_INTERRUPTION(); if (outseq) { - no_of_neighbors = VECTOR(*outseq)[k]; + no_of_neighbors = (long int) VECTOR(*outseq)[k]; } for (j = 0; j < no_of_neighbors; j++) { if (sum == 0) { @@ -280,48 +258,48 @@ static igraph_error_t igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, } /* update probabilities */ for (j = 0; j < no_of_neighbors; j++) { - igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(VECTOR(degree)[nn], power, A))); + 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, attraction(VECTOR(degree)[i], power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); } } RNG_END(); igraph_psumtree_destroy(&sumtree); - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, +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_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, const igraph_t *start_from) { - igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_neighbors = m; - igraph_vector_int_t edges; - igraph_integer_t i, j, k; + 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; - igraph_integer_t edgeptr = 0; - igraph_vector_int_t degree; - igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; + long int edgeptr = 0; + igraph_vector_t degree; + long int start_nodes, start_edges, new_edges, no_of_edges; if (!directed) { outpref = 1; @@ -330,70 +308,63 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, start_nodes = start_from ? igraph_vcount(start_from) : 1; start_edges = start_from ? igraph_ecount(start_from) : 0; if (outseq) { - if (igraph_vector_int_size(outseq) > 1) { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); - new_edges -= VECTOR(*outseq)[0]; + if (igraph_vector_size(outseq) > 1) { + new_edges = (long int) (igraph_vector_sum(outseq) - VECTOR(*outseq)[0]); } else { new_edges = 0; } } else { - IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); - } - IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; } + no_of_edges = start_edges + new_edges; edgeptr = start_edges * 2; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_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_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); RNG_BEGIN(); - /* First node(s): */ + /* first node(s) */ if (start_from) { - igraph_integer_t ii, sn = igraph_vcount(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_int_resize(°ree, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(°ree, no_of_nodes)); for (ii = 0; ii < sn; ii++) { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); } } else { - /* Any weight may be used for the first node. In the first step, it will be connected to - * with certainty, after which its weight will be set appropriately. */ - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); } /* Initialize the edges vector */ if (start_from) { - IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); } - /* And the rest: */ + /* 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_integer_t to; + long int to; IGRAPH_ALLOW_INTERRUPTION(); if (outseq) { - no_of_neighbors = VECTOR(*outseq)[k]; + 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_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, 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, attraction(VECTOR(degree)[to], power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, pow(VECTOR(degree)[to], power) + A)); } } else { for (j = 0; j < no_of_neighbors; j++) { @@ -406,36 +377,36 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); } VECTOR(degree)[to]++; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, 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++) { - igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(VECTOR(degree)[nn], power, A))); + 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, attraction(VECTOR(degree)[i], power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, pow(VECTOR(degree)[i], power) + A)); } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); } } RNG_END(); igraph_psumtree_destroy(&sumtree); - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -443,66 +414,39 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, * \function igraph_barabasi_game * \brief Generates a graph based on the Barabási-Albert model. * - * This function implements several variants of the preferential attachment - * process, including linear and non-linear varieties of the Barabási-Albert - * and Price models. The graph construction starts with a single vertex, - * or an existing graph given by the \p start_from parameter. Then new vertices - * are added one at a time. Each new vertex connects to \p m existing vertices, - * choosing them with probabilities proportional to - * - * - * d^power + A, - * - * - * where \c d is the in- or total degree of the existing vertex (controlled - * by the \p outpref argument), while \p power and \p A are given by - * parameters. The constant attractiveness \p A - * is used to ensure that vertices with zero in-degree can also be - * connected to with non-zero probability. - * - * - * Barabási, A.-L. and Albert R. 1999. Emergence of scaling in - * random networks, Science, 286 509--512. - * https://doi.org/10.1126/science.286.5439.509 - * - * - * de Solla Price, D. J. 1965. Networks of Scientific Papers, Science, - * 149 510--515. - * https://doi.org/10.1126/science.149.3683.510 - * * \param graph An uninitialized graph object. * \param n The number of vertices in the graph. - * \param power Power of the preferential attachment. In the classic preferential - * attachment model power=1. Other values allow for - * sampling from a non-linear preferential attachment model. - * Negative values are only allowed when no zero-degree vertices - * are present during the construction process, i.e. when - * the starting graph has no isolated vertices and \p outpref - * is set to \c true. + * \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 used when \p outseq is \c NULL. + * 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 \c NULL pointer or an empty 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. + * 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 constant attractiveness of vertices. When \p outpref - * is set to \c false, it should be positive to ensure that - * zero in-degree vertices can be connected to as well. + * \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. - * When set to \c false, outpref is assumed to be \c true. * \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 + * 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 @@ -517,7 +461,7 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, * 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 \c NULL pointer, or a graph. In the former + * \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 @@ -526,7 +470,8 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, * information on the vertices that are not in the \p * start_from graph. * \return Error code: - * \c IGRAPH_EINVAL: invalid \p n, \p m, \p A or \p outseq parameter. + * \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. @@ -534,28 +479,34 @@ static igraph_error_t igraph_i_barabasi_game_psumtree(igraph_t *graph, * \example examples/simple/igraph_barabasi_game.c * \example examples/simple/igraph_barabasi_game2.c */ -igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, +int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, - const igraph_vector_int_t *outseq, + 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) { - igraph_integer_t start_nodes = start_from ? igraph_vcount(start_from) : 0; - igraph_integer_t newn = start_from ? n - start_nodes : n; + 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_int_empty(outseq)) { - outseq = NULL; + if (outseq && igraph_vector_size(outseq) == 0) { + outseq = 0; } if (!directed) { - outpref = true; + 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) { @@ -564,13 +515,14 @@ igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, if (start_from && start_nodes == 0) { IGRAPH_ERROR("Cannot start from an empty graph.", IGRAPH_EINVAL); } - if (outseq && igraph_vector_int_size(outseq) != newn) { + if (outseq != 0 && igraph_vector_size(outseq) != 0 && + igraph_vector_size(outseq) != newn) { IGRAPH_ERROR("Invalid out-degree sequence length.", IGRAPH_EINVAL); } - if (!outseq && m < 0) { + 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_int_min(outseq) < 0) { + if (outseq && igraph_vector_min(outseq) < 0) { IGRAPH_ERROR("Negative out-degree in sequence.", IGRAPH_EINVAL); } if (!outpref && A <= 0) { @@ -583,7 +535,7 @@ igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, } if (algo == IGRAPH_BARABASI_BAG) { if (power != 1) { - IGRAPH_ERROR("Power must be one for bag algorithm.", IGRAPH_EINVAL); + 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.", @@ -598,37 +550,29 @@ igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, IGRAPH_EINVAL); } + if (power <= 0) { + IGRAPH_ERRORF("Preferential attachment exponent must be positive, got %g.", + IGRAPH_EINVAL, + power); + } + if (n == 0) { return igraph_empty(graph, 0, directed); } - switch (algo) { - case IGRAPH_BARABASI_BAG: - return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed, start_from); - - case IGRAPH_BARABASI_PSUMTREE: + 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); - case IGRAPH_BARABASI_PSUMTREE_MULTIPLE: + } else if (algo == IGRAPH_BARABASI_PSUMTREE_MULTIPLE) { return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m, - outseq, outpref, A, - directed, start_from); - default: - IGRAPH_ERROR("Invalid algorithm for Barabasi game.", IGRAPH_EINVAL); + outseq, outpref, A, + directed, start_from); } -} -/* Attraction function for barabasi_aging_game. - * We special-case deg_exp == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ -static igraph_real_t attraction_aging( - igraph_real_t deg, igraph_real_t age, - igraph_real_t deg_exp, igraph_real_t age_exp, - igraph_real_t deg_A, igraph_real_t age_A, - igraph_real_t deg_coef, igraph_real_t age_coef) { - - igraph_real_t dp = deg_exp == 0 ? 1.0 : pow(deg, deg_exp); - igraph_real_t ap = pow(age, age_exp); - return (deg_coef * dp + deg_A) * (age_coef * ap + age_A); + return 0; } /** @@ -644,7 +588,7 @@ static igraph_real_t attraction_aging( * deg_coef * k^pa_exp + zero_deg_appeal, * while the age-dependent part is * age_coef * l^aging_exp + zero_age_appeal, - * which are multiplied to obtain the final weight. + * which are summed to obtain the final weight. * * * The age \c l is based on the number of vertices in the @@ -682,10 +626,10 @@ static igraph_real_t attraction_aging( * Time complexity: O((|V|+|V|/aging_bins)*log(|V|)+|E|). |V| is the number * of vertices, |E| the number of edges. */ -igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, +int igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -695,25 +639,25 @@ igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, igraph_real_t deg_coef, igraph_real_t age_coef, igraph_bool_t directed) { - igraph_integer_t no_of_nodes = nodes; - igraph_integer_t no_of_neighbors = m; - igraph_integer_t binwidth; - igraph_integer_t no_of_edges; - igraph_vector_int_t edges; - igraph_integer_t i, j, k; + 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; - igraph_integer_t edgeptr = 0; - igraph_vector_int_t degree; + long int edgeptr = 0; + igraph_vector_t degree; if (no_of_nodes < 0) { - IGRAPH_ERRORF("Number of nodes must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + IGRAPH_ERRORF("Number of nodes must not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); } - if (outseq != 0 && igraph_vector_int_size(outseq) != 0 && igraph_vector_int_size(outseq) != no_of_nodes) { - IGRAPH_ERRORF("The length of the out-degree sequence (%" IGRAPH_PRId ") does not agree with the number of nodes (%" IGRAPH_PRId ").", + 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_int_size(outseq), no_of_nodes); + igraph_vector_size(outseq), no_of_nodes); } - if ( (outseq == 0 || igraph_vector_int_size(outseq) == 0) && m < 0) { + 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); @@ -745,45 +689,47 @@ igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, zero_age_appeal); } + if (pa_exp <= 0) { + IGRAPH_ERRORF("Preferential attachment exponent must be positive, got %g.", + IGRAPH_EINVAL, + pa_exp); + } + if (no_of_nodes == 0) { return igraph_empty(graph, 0, directed); } binwidth = no_of_nodes / aging_bins + 1; - if (outseq == 0 || igraph_vector_int_size(outseq) == 0) { + if (outseq == 0 || igraph_vector_size(outseq) == 0) { no_of_neighbors = m; - IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; } else { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); - no_of_edges -= VECTOR(*outseq)[0]; - } - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + no_of_edges = 0; + for (i = 1; i < igraph_vector_size(outseq); i++) { + no_of_edges += VECTOR(*outseq)[i]; + } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_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_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); RNG_BEGIN(); - /* First node: */ - /* Any weight may be used for the first node. In the first step, it will be connected to - * with certainty, after which its weight will be set appropriately. */ - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); + /* first node */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_deg_appeal * (1 + zero_age_appeal))); - /* And the rest: */ + /* and the rest */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - igraph_integer_t to; + long int to; IGRAPH_ALLOW_INTERRUPTION(); - if (outseq != 0 && igraph_vector_int_size(outseq) != 0) { - no_of_neighbors = VECTOR(*outseq)[i]; + 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++) { @@ -800,59 +746,48 @@ igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, } /* update probabilities */ for (j = 0; j < no_of_neighbors; j++) { - igraph_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; - igraph_integer_t age = (i - n) / binwidth; + long int n = (long int) VECTOR(edges)[edgeptr - 2 * j - 1]; + long int age = (i - n) / binwidth; IGRAPH_CHECK(igraph_psumtree_update( &sumtree, n, - attraction_aging(VECTOR(degree)[n], age+1, - pa_exp, aging_exp, - zero_deg_appeal, zero_age_appeal, - deg_coef, age_coef) + (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, - attraction_aging(VECTOR(degree)[i], 1, - pa_exp, aging_exp, - zero_deg_appeal, zero_age_appeal, - deg_coef, age_coef) + (zero_age_appeal + 1) * (deg_coef * pow(VECTOR(degree)[i], pa_exp) + zero_deg_appeal) )); } else { IGRAPH_CHECK(igraph_psumtree_update( - &sumtree, i, - attraction_aging(0, 1, - pa_exp, aging_exp, - zero_deg_appeal, zero_age_appeal, - deg_coef, age_coef) + &sumtree, i, (1 + zero_age_appeal) * zero_deg_appeal )); } /* aging */ for (k = 1; binwidth * k <= i; k++) { - igraph_integer_t shnode = i - binwidth * k; - igraph_integer_t deg = VECTOR(degree)[shnode]; - igraph_integer_t age = (i - shnode) / binwidth; + 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, - attraction_aging(deg, age + 2, - pa_exp, aging_exp, - zero_deg_appeal, zero_age_appeal, - deg_coef, age_coef) + (deg_coef * pow(deg, pa_exp) + zero_deg_appeal) * + (age_coef * pow(age + 2, aging_exp) + zero_age_appeal) )); } } RNG_END(); - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); igraph_psumtree_destroy(&sumtree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/callaway_traits.c b/src/vendor/cigraph/src/games/callaway_traits.c index e4ce24b69f6..3bdb8974b30 100644 --- a/src/vendor/cigraph/src/games/callaway_traits.c +++ b/src/vendor/cigraph/src/games/callaway_traits.c @@ -31,8 +31,9 @@ * \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. + * vertices with a given probability. * * * The simulation goes like this: in each discrete time step a new @@ -70,29 +71,23 @@ * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices, * k is \p edges_per_step. */ -igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, +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_int_t *node_type_vec) { - igraph_integer_t i, j; - igraph_vector_int_t edges; + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; igraph_vector_t cumdist; igraph_real_t maxcum; - igraph_vector_int_t *nodetypes; + igraph_vector_t *nodetypes; /* Argument contracts */ - if (nodes < 0) { + if(nodes < 0){ IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } - if (edges_per_step < 0) { - IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, - edges_per_step); - } - if (types < 1) { IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); } @@ -109,7 +104,7 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (isnan(lo)) { + if (igraph_is_nan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -120,12 +115,12 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ + 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 (isnan(lo) || isnan(hi)) { + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -134,7 +129,7 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); if (type_dist) { @@ -155,35 +150,35 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod if (node_type_vec) { nodetypes = node_type_vec; - IGRAPH_CHECK(igraph_vector_int_resize(nodetypes, nodes)); + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); if (! nodetypes) { - IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); } RNG_BEGIN(); for (i = 0; i < nodes; i++) { igraph_real_t uni = RNG_UNIF(0, maxcum); - igraph_integer_t type; + 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++) { - igraph_integer_t node1 = RNG_INTEGER(0, i); - igraph_integer_t node2 = RNG_INTEGER(0, i); - igraph_integer_t type1 = VECTOR(*nodetypes)[node1]; - igraph_integer_t type2 = VECTOR(*nodetypes)[node2]; + 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_int_push_back(&edges, node1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, node2)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, node1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, node2)); } } } @@ -191,14 +186,14 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod RNG_END(); if (! node_type_vec) { - igraph_vector_int_destroy(nodetypes); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/citations.c b/src/vendor/cigraph/src/games/citations.c index 1cac157c699..48552c21132 100644 --- a/src/vendor/cigraph/src/games/citations.c +++ b/src/vendor/cigraph/src/games/citations.c @@ -29,10 +29,8 @@ #include "igraph_random.h" #include "igraph_interface.h" -#include "math/safe_intop.h" - typedef struct { - igraph_integer_t no; + long int no; igraph_psumtree_t *sumtrees; } igraph_i_citing_cited_type_game_struct_t; @@ -66,7 +64,6 @@ static void igraph_i_citing_cited_type_game_free ( * 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. @@ -74,7 +71,7 @@ static void igraph_i_citing_cited_type_game_free ( * 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 + * \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. @@ -88,38 +85,33 @@ static void igraph_i_citing_cited_type_game_free ( * 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. */ -igraph_error_t igraph_lastcit_game(igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = nodes; + long int no_of_nodes = nodes; igraph_psumtree_t sumtree; - igraph_vector_int_t edges; - igraph_integer_t i, j, k; - igraph_integer_t *lastcit; - igraph_integer_t *index; - igraph_integer_t binwidth; + 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 %" IGRAPH_PRId ".", + "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 ".", + if (nodes < 0 ) { + IGRAPH_ERRORF("Number of nodes should be non-negative, received %"IGRAPH_PRId".", IGRAPH_EINVAL, nodes); } - if (edges_per_node < 0) { - IGRAPH_ERRORF("Number of edges per node should be non-negative, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, - edges_per_node); - } - if (agebins < 1) { - IGRAPH_ERRORF("Number of age bins should be at least 1, received %" IGRAPH_PRId ".", + if (agebins < 1 ) { + IGRAPH_ERRORF("Number of age bins should be at least 1, received %"IGRAPH_PRId".", IGRAPH_EINVAL, agebins); } @@ -140,23 +132,23 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, } binwidth = no_of_nodes / agebins + 1; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - lastcit = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + lastcit = IGRAPH_CALLOC(no_of_nodes, long int); if (!lastcit) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, lastcit); - index = IGRAPH_CALLOC(no_of_nodes + 1, igraph_integer_t); + index = IGRAPH_CALLOC(no_of_nodes + 1, long int); if (!index) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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_int_reserve(&edges, nodes * edges_per_node)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_node)); /* The first node */ IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, VECTOR(*preference)[agebins])); @@ -169,7 +161,7 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, /* Add new edges */ for (j = 0; j < edges_per_node; j++) { - igraph_integer_t to; + long int to; igraph_real_t sum = igraph_psumtree_sum(&sumtree); if (sum == 0) { /* If none of the so-far added nodes have positive weight, @@ -178,8 +170,8 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, } else { igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); } - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, to); /* reserved */ + 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])); } @@ -191,10 +183,10 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, /* 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++) { - igraph_integer_t shnode = i - binwidth * k; - igraph_integer_t m = index[shnode], n = index[shnode + 1]; + long int shnode = i - binwidth * k; + long int m = index[shnode], n = index[shnode + 1]; for (j = 2 * m; j < 2 * n; j += 2) { - igraph_integer_t cnode = VECTOR(edges)[j + 1]; + long int cnode = (long int) VECTOR(edges)[j + 1]; if (lastcit[cnode] == shnode + 1) { IGRAPH_CHECK(igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k])); } @@ -211,10 +203,10 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -252,26 +244,21 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, * vertices and edges, respectively. */ -igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_int_t *types, +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_int_t edges; + igraph_vector_t edges; igraph_vector_t cumsum; igraph_real_t sum, nnval; - igraph_integer_t i, j, type; - igraph_integer_t pref_len = igraph_vector_size(pref); + long int i, j, type; + long int pref_len = igraph_vector_size(pref); - if (igraph_vector_int_size(types) != nodes) { - IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") must match number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(types), nodes); - } - if (edges_per_step < 0) { - IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, - edges_per_step); + 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) { @@ -280,20 +267,20 @@ igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } /* the case of zero-length type vector is caught above, safe to call vector_min here */ - if (igraph_vector_int_min(types) < 0) { - IGRAPH_ERRORF("Types should be non-negative, but found %" IGRAPH_PRId ".", - IGRAPH_EINVAL, igraph_vector_int_min(types)); + if (igraph_vector_min(types) < 0) { + IGRAPH_ERRORF("Types should be non-negative, but found %g.", + IGRAPH_EINVAL, igraph_vector_min(types)); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumsum, 2); IGRAPH_CHECK(igraph_vector_reserve(&cumsum, nodes + 1)); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes * edges_per_step)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); /* first node */ VECTOR(cumsum)[0] = 0; - type = VECTOR(*types)[0]; + type = (long int) VECTOR(*types)[0]; if (type >= pref_len) { goto err_pref_too_short; } @@ -307,16 +294,16 @@ igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, for (i = 1; i < nodes; i++) { for (j = 0; j < edges_per_step; j++) { - igraph_integer_t to; + long int to; if (sum > 0) { igraph_vector_binsearch(&cumsum, RNG_UNIF(0, sum), &to); } else { to = i + 1; } - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, to - 1); /* reserved */ + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to - 1); } - type = VECTOR(*types)[i]; + type = (long int) VECTOR(*types)[i]; if (type >= pref_len) { goto err_pref_too_short; } @@ -325,7 +312,7 @@ igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, goto err_pref_neg; } sum += nnval; - igraph_vector_push_back(&cumsum, sum); /* reserved */ + igraph_vector_push_back(&cumsum, sum); } RNG_END(); @@ -333,14 +320,14 @@ igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, igraph_vector_destroy(&cumsum); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; err_pref_too_short: - IGRAPH_ERRORF("Preference vector should have length at least %" IGRAPH_PRId " with the given types.", IGRAPH_EINVAL, - igraph_vector_int_max(types) + 1); + 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, @@ -348,7 +335,7 @@ igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game_struct_t *s) { - igraph_integer_t i; + long int i; if (!s->sumtrees) { return; } @@ -382,14 +369,12 @@ static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game * \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 vector of length \p nodes, containing the + * \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 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. @@ -398,43 +383,38 @@ static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game * vertices and edges, respectively. */ -igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_int_t *types, +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_int_t edges; + igraph_vector_t edges; igraph_i_citing_cited_type_game_struct_t str = { 0, NULL }; igraph_psumtree_t *sumtrees; igraph_vector_t sums; - igraph_integer_t no_of_types; - igraph_integer_t i, j, no_of_edges, no_of_edge_endpoints; + long int no_of_types; + long int i, j; - if (igraph_vector_int_size(types) != nodes) { - IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") not equal to number" + 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_int_size(types), nodes); - } - if (edges_per_step < 0 ) { - IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, - edges_per_step); + IGRAPH_EINVAL, igraph_vector_size(types), nodes); } /* avoid calling vector_max on empty vector */ - no_of_types = nodes == 0 ? 0 : igraph_vector_int_max(types) + 1; + 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 (%" IGRAPH_PRId ") not " - "equal to number of types (%" IGRAPH_PRId ").", + 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 (%" IGRAPH_PRId ") not " - "equal to number of types (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Number of preference matrix rows (%ld) not " + "equal to number of types (%ld).", IGRAPH_EINVAL, igraph_matrix_nrow(pref), no_of_types); @@ -445,11 +425,11 @@ igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t n return igraph_empty(graph, 0, directed); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Citing-cited type game failed.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_i_citing_cited_type_game_free, &str); @@ -459,13 +439,11 @@ igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t n } IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_types); - IGRAPH_SAFE_MULT(nodes, edges_per_step, &no_of_edges); - IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edge_endpoints); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edge_endpoints)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); /* First node */ for (i = 0; i < no_of_types; i++) { - igraph_integer_t type = VECTOR(*types)[0]; + 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)); } @@ -476,10 +454,10 @@ igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t n RNG_BEGIN(); for (i = 1; i < nodes; i++) { - igraph_integer_t type = VECTOR(*types)[i]; + long int type = (long int) VECTOR(*types)[i]; igraph_real_t sum = VECTOR(sums)[type]; for (j = 0; j < edges_per_step; j++) { - igraph_integer_t to; + long int to; if (sum == 0) { /* If none of the so-far added nodes have positive weight, * we choose one uniformly to connect to. */ @@ -487,8 +465,8 @@ igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t n } else { igraph_psumtree_search(&sumtrees[type], &to, RNG_UNIF(0, sum)); } - igraph_vector_int_push_back(&edges, i); /* reserved */ - igraph_vector_int_push_back(&edges, to); /* reserved */ + igraph_vector_push_back(&edges, i); + igraph_vector_push_back(&edges, to); } /* add i */ @@ -506,11 +484,9 @@ igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t n igraph_i_citing_cited_type_game_free(&str); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - - igraph_vector_int_destroy(&edges); + 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/vendor/cigraph/src/games/correlated.c b/src/vendor/cigraph/src/games/correlated.c index 36a90184315..732d00b75d5 100644 --- a/src/vendor/cigraph/src/games/correlated.c +++ b/src/vendor/cigraph/src/games/correlated.c @@ -34,7 +34,7 @@ /* 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 undirected) are column-wise concatenated into an array. The "code" is + * 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. */ @@ -44,9 +44,9 @@ /* 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_integer_t *a = (const igraph_integer_t *) va; - const igraph_integer_t *b = (const igraph_integer_t *) vb; - const igraph_integer_t no_of_nodes = igraph_vcount((igraph_t *) graph); + 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]); @@ -60,8 +60,8 @@ static int code_cmp(void *graph, const void *va, const void *vb) { } /* Sort an edge vector by edge codes. */ -static void sort_edges(igraph_vector_int_t *edges, const igraph_t *graph) { - igraph_qsort_r(VECTOR(*edges), igraph_vector_int_size(edges) / 2, 2*sizeof(igraph_integer_t), (void *) graph, code_cmp); +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); } /** @@ -87,26 +87,25 @@ static void sort_edges(igraph_vector_int_t *edges, const igraph_t *graph) { * \sa \ref igraph_correlated_pair_game() for generating a pair * of correlated random graphs in one go. */ -igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, +int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, igraph_real_t corr, igraph_real_t p, - const igraph_vector_int_t *permutation) { + const igraph_vector_t *permutation) { - igraph_integer_t no_of_nodes = igraph_vcount(old_graph); - igraph_integer_t no_of_edges = igraph_ecount(old_graph); + 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 ? ((igraph_real_t) no_of_nodes) * (no_of_nodes - 1) : - ((igraph_real_t) no_of_nodes) * (no_of_nodes - 1) / 2; + 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; - igraph_vector_int_t edges, newedges; + igraph_vector_t add, delete, edges, newedges; igraph_real_t last; - igraph_integer_t p_e = 0, p_a = 0, p_d = 0; - igraph_integer_t no_add, no_del; + 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; - igraph_integer_t i, newec; + long int i; igraph_bool_t simple; if (corr < 0 || corr > 1) { @@ -118,7 +117,7 @@ igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_g IGRAPH_EINVAL, p); } if (permutation) { - if (igraph_vector_int_size(permutation) != no_of_nodes) { + if (igraph_vector_size(permutation) != no_of_nodes) { IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game.", IGRAPH_EINVAL); } @@ -138,28 +137,28 @@ igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_g } if (corr == 1) { /* We don't copy, because we don't need the attributes.... */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); if (permutation) { - newec = igraph_vector_int_size(&edges); + int newec = igraph_vector_size(&edges); for (i = 0; i < newec; i++) { - igraph_integer_t tmp = VECTOR(edges)[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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); IGRAPH_VECTOR_INIT_FINALLY(&add, 0); IGRAPH_VECTOR_INIT_FINALLY(&delete, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0)); - /* The sampling method used is analogous to the one in igraph_erdos_renyi_game_gnp(), + /* 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. */ @@ -203,36 +202,36 @@ igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_g /* First we (re)code the edges to delete */ for (i = 0; i < no_del; i++) { - igraph_integer_t td = VECTOR(delete)[i]; - igraph_integer_t from = VECTOR(edges)[2 * td]; - igraph_integer_t to = VECTOR(edges)[2 * td + 1]; + 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_int_reserve(&newedges, + 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 = IGRAPH_INFINITY; } } + { 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 = IGRAPH_INFINITY; } } + 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 = IGRAPH_INFINITY; } } + next_d = VECTOR(delete)[p_d]; } else { next_d = inf; } } UPD_E(); UPD_A(); UPD_D(); - while (next_e != IGRAPH_INFINITY || next_a != IGRAPH_INFINITY || next_d != IGRAPH_INFINITY) { + 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_int_push_back(&newedges, VECTOR(edges)[2 * p_e])); - IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(edges)[2 * p_e + 1])); + 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) { @@ -244,41 +243,41 @@ igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_g } else { /* add an edge */ - igraph_integer_t to, from; - IGRAPH_ASSERT(isfinite(next_a)); + long int to, from; + IGRAPH_ASSERT(igraph_finite(next_a)); if (directed) { - to = floor(next_a / no_of_nodes); - from = next_a - ((igraph_real_t)to) * no_of_nodes; + 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 = floor((sqrt(8 * next_a + 1) + 1) / 2); - from = next_a - (((igraph_real_t)to) * (to - 1)) / 2; + 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_int_push_back(&newedges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&newedges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&newedges, to)); p_a++; UPD_A(); } } - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&add); igraph_vector_destroy(&delete); IGRAPH_FINALLY_CLEAN(3); if (permutation) { - newec = igraph_vector_int_size(&newedges); + long int newec = igraph_vector_size(&newedges); for (i = 0; i < newec; i++) { - igraph_integer_t tmp = VECTOR(newedges)[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_int_destroy(&newedges); + igraph_vector_destroy(&newedges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -315,10 +314,10 @@ igraph_error_t igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_g * \sa \ref igraph_correlated_game() for generating a correlated pair * to a given graph. */ -igraph_error_t igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2, +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_int_t *permutation) { + const igraph_vector_t *permutation) { IGRAPH_CHECK(igraph_erdos_renyi_game(graph1, IGRAPH_ERDOS_RENYI_GNP, n, p, directed, IGRAPH_NO_LOOPS)); diff --git a/src/vendor/cigraph/src/games/degree_sequence.c b/src/vendor/cigraph/src/games/degree_sequence.c index cb5bae49cef..b4e6dd4efab 100644 --- a/src/vendor/cigraph/src/games/degree_sequence.c +++ b/src/vendor/cigraph/src/games/degree_sequence.c @@ -27,29 +27,25 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_graphicality.h" -#include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_operators.h" #include "igraph_random.h" #include "igraph_vector_ptr.h" #include "core/interruption.h" #include "core/set.h" -#include "games/degree_sequence_vl/degree_sequence_vl.h" -#include "math/safe_intop.h" -static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *graph, - const igraph_vector_int_t *out_seq, - const igraph_vector_int_t *in_seq) { +static int igraph_i_degree_sequence_game_simple(igraph_t *graph, + const igraph_vector_t *out_seq, + const igraph_vector_t *in_seq) { - igraph_integer_t outsum = 0, insum = 0; - igraph_bool_t directed = (in_seq != 0 && igraph_vector_int_size(in_seq) != 0); + long int outsum = 0, insum = 0; + igraph_bool_t directed = (in_seq != 0 && igraph_vector_size(in_seq) != 0); igraph_bool_t degseq_ok; - igraph_integer_t no_of_nodes, no_of_edges; - igraph_integer_t *bag1 = 0, *bag2 = 0; - igraph_integer_t bagp1 = 0, bagp2 = 0; - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t i, j; + 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) { @@ -57,17 +53,17 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); + outsum = (long int) igraph_vector_sum(out_seq); if (directed) { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(in_seq, &insum)); + insum = (long int) igraph_vector_sum(in_seq); } - no_of_nodes = igraph_vector_int_size(out_seq); + no_of_nodes = igraph_vector_size(out_seq); no_of_edges = directed ? outsum : outsum / 2; - bag1 = IGRAPH_CALLOC(outsum, igraph_integer_t); + bag1 = IGRAPH_CALLOC(outsum, long int); if (bag1 == 0) { - IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, bag1); @@ -77,9 +73,9 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap } } if (directed) { - bag2 = IGRAPH_CALLOC(insum, igraph_integer_t); + bag2 = IGRAPH_CALLOC(insum, long int); if (bag2 == 0) { - IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, bag2); for (i = 0; i < no_of_nodes; i++) { @@ -89,30 +85,30 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + 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++) { - igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); - igraph_integer_t to = RNG_INTEGER(0, bagp2 - 1); - igraph_vector_int_push_back(&edges, bag1[from]); /* safe, already reserved */ - igraph_vector_int_push_back(&edges, bag2[to]); /* ditto */ + 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++) { - igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); - igraph_integer_t to; - igraph_vector_int_push_back(&edges, bag1[from]); /* safe, already reserved */ + 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_int_push_back(&edges, bag1[to]); /* ditto */ + igraph_vector_push_back(&edges, bag1[to]); /* ditto */ bag1[to] = bag1[bagp1 - 1]; bagp1--; } @@ -127,42 +123,43 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap IGRAPH_FINALLY_CLEAN(1); } - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( - igraph_t *graph, const igraph_vector_int_t *seq) { +static int igraph_i_degree_sequence_game_no_multiple_undirected( + igraph_t *graph, const igraph_vector_t *seq) { - igraph_vector_int_t stubs; + igraph_vector_t stubs = IGRAPH_VECTOR_NULL; igraph_vector_int_t *neis; - igraph_vector_int_t residual_degrees; + 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; - igraph_integer_t i, j, k; - igraph_integer_t no_of_nodes, outsum = 0; + 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_ERROR("No simple undirected graph can realize the given degree sequence", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(seq, &outsum)); - no_of_nodes = igraph_vector_int_size(seq); + 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, no_of_nodes)); + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&stubs, outsum)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_degrees, no_of_nodes); + 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); @@ -182,30 +179,30 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( igraph_adjlist_clear(&al); /* Initialize the residual degrees from the degree sequence */ - IGRAPH_CHECK(igraph_vector_int_update(&residual_degrees, seq)); + 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_int_clear(&stubs); + igraph_vector_clear(&stubs); for (i = 0; i < no_of_nodes; i++) { for (j = 0; j < VECTOR(residual_degrees)[i]; j++) { - igraph_vector_int_push_back(&stubs, i); + igraph_vector_push_back(&stubs, i); } } /* Clear the skipped stub counters and the set of incomplete vertices */ - igraph_vector_int_null(&residual_degrees); + igraph_vector_null(&residual_degrees); igraph_set_clear(&incomplete_vertices); /* Shuffle the stubs in-place */ - igraph_vector_int_shuffle(&stubs); + igraph_vector_shuffle(&stubs); /* Connect the stubs where possible */ - k = igraph_vector_int_size(&stubs); + k = igraph_vector_size(&stubs); for (i = 0; i < k; ) { - from = VECTOR(stubs)[i++]; - to = VECTOR(stubs)[i++]; + from = (igraph_integer_t) VECTOR(stubs)[i++]; + to = (igraph_integer_t) VECTOR(stubs)[i++]; if (from > to) { dummy = from; from = to; to = dummy; @@ -259,8 +256,8 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( /* Clean up */ igraph_set_destroy(&incomplete_vertices); - igraph_vector_int_destroy(&residual_degrees); - igraph_vector_int_destroy(&stubs); + 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 @@ -276,39 +273,39 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t *graph, - const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { +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_int_t in_stubs; - igraph_vector_int_t out_stubs; + igraph_vector_t in_stubs = IGRAPH_VECTOR_NULL; + igraph_vector_t out_stubs = IGRAPH_VECTOR_NULL; igraph_vector_int_t *neis; - igraph_vector_int_t residual_in_degrees = IGRAPH_VECTOR_NULL; - igraph_vector_int_t residual_out_degrees = IGRAPH_VECTOR_NULL; + 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; - igraph_integer_t i, j, k; - igraph_integer_t no_of_nodes, outsum; + 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_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); - no_of_nodes = igraph_vector_int_size(out_seq); + 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, no_of_nodes)); + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_stubs, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&out_stubs, outsum)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&in_stubs, outsum)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_out_degrees, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_in_degrees, no_of_nodes); + 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)); @@ -330,37 +327,37 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t igraph_adjlist_clear(&al); /* Initialize the residual degrees from the degree sequences */ - IGRAPH_CHECK(igraph_vector_int_update(&residual_out_degrees, out_seq)); - IGRAPH_CHECK(igraph_vector_int_update(&residual_in_degrees, in_seq)); + 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_int_clear(&out_stubs); - igraph_vector_int_clear(&in_stubs); + 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_int_push_back(&out_stubs, i); + igraph_vector_push_back(&out_stubs, i); } for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) { - igraph_vector_int_push_back(&in_stubs, i); + igraph_vector_push_back(&in_stubs, i); } } /* Clear the skipped stub counters and the set of incomplete vertices */ - igraph_vector_int_null(&residual_out_degrees); - igraph_vector_int_null(&residual_in_degrees); + 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_int_shuffle(&out_stubs); + igraph_vector_shuffle(&out_stubs); /* Connect the stubs where possible */ - k = igraph_vector_int_size(&out_stubs); + k = igraph_vector_size(&out_stubs); for (i = 0; i < k; i++) { - from = VECTOR(out_stubs)[i]; - to = VECTOR(in_stubs)[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)) { @@ -405,10 +402,10 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t /* Clean up */ igraph_set_destroy(&incomplete_in_vertices); igraph_set_destroy(&incomplete_out_vertices); - igraph_vector_int_destroy(&residual_in_degrees); - igraph_vector_int_destroy(&residual_out_degrees); - igraph_vector_int_destroy(&in_stubs); - igraph_vector_int_destroy(&out_stubs); + 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 */ @@ -430,31 +427,31 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t VECTOR(vec)[j] = temp; \ } -static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirected(igraph_t *graph, const igraph_vector_int_t *degseq) { +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_int_t edges; + igraph_vector_t edges; igraph_bool_t degseq_ok; igraph_vector_ptr_t adjlist; - igraph_integer_t i, j; - igraph_integer_t vcount, ecount, stub_count; + 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); + IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence", IGRAPH_EINVAL); } - stub_count = igraph_vector_int_sum(degseq); + stub_count = (long) igraph_vector_sum(degseq); ecount = stub_count / 2; - vcount = igraph_vector_int_size(degseq); + vcount = igraph_vector_size(degseq); IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, stub_count); + IGRAPH_VECTOR_INIT_FINALLY(&edges, stub_count); /* Fill stubs vector. */ { - igraph_integer_t k = 0; + long k = 0; for (i = 0; i < vcount; ++i) { - igraph_integer_t deg = VECTOR(*degseq)[i]; + long deg = (long) VECTOR(*degseq)[i]; for (j = 0; j < deg; ++j) { VECTOR(stubs)[k++] = i; } @@ -468,21 +465,21 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec for (i = 0; i < vcount; ++i) { igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t); if (! set) { - IGRAPH_ERROR("Cannot sample from configuration model (simple graphs).", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; - IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*degseq)[i])); + IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*degseq)[i])); } RNG_BEGIN(); for (;;) { - igraph_bool_t success = true; + 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) { - igraph_integer_t k, from, to; + long k, from, to; k = RNG_INTEGER(2*i, stub_count-1); SWAP_INT_ELEM(stubs, 2*i, k); @@ -495,13 +492,13 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec /* self-loop, fail */ if (from == to) { - success = false; + success = 0; break; } /* multi-edge, fail */ if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) { - success = false; + success = 0; break; } @@ -534,46 +531,46 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directed( - igraph_t *graph, const igraph_vector_int_t *out_deg, const igraph_vector_int_t *in_deg) { +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_int_t edges; + igraph_vector_t edges; igraph_bool_t degseq_ok; igraph_vector_ptr_t adjlist; - igraph_integer_t i, j; - igraph_integer_t vcount, ecount; + 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 = igraph_vector_int_sum(out_deg); - vcount = igraph_vector_int_size(out_deg); + 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_INT_INIT_FINALLY(&edges, 2 * ecount); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * ecount); /* Fill in- and out-stubs vectors. */ { - igraph_integer_t k = 0, l = 0; + long k = 0, l = 0; for (i = 0; i < vcount; ++i) { - igraph_integer_t dout, din; + long dout, din; - dout = VECTOR(*out_deg)[i]; + dout = (long) VECTOR(*out_deg)[i]; for (j = 0; j < dout; ++j) { VECTOR(out_stubs)[k++] = i; } - din = VECTOR(*in_deg)[i]; + din = (long) VECTOR(*in_deg)[i]; for (j = 0; j < din; ++j) { VECTOR(in_stubs)[l++] = i; } @@ -587,21 +584,21 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; - IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*out_deg)[i])); + IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*out_deg)[i])); } RNG_BEGIN(); for (;;) { - igraph_bool_t success = true; + 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) { - igraph_integer_t k, from, to; + long k, from, to; igraph_set_t *set; k = RNG_INTEGER(i, ecount-1); @@ -612,14 +609,14 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe /* self-loop, fail */ if (to == from) { - success = false; + success = 0; break; } /* multi-edge, fail */ set = (igraph_set_t *) VECTOR(adjlist)[from]; if (igraph_set_contains(set, to)) { - success = false; + success = 0; break; } @@ -652,7 +649,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -660,17 +657,12 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe #undef SWAP_INT_ELEM -igraph_error_t igraph_i_degree_sequence_game_edge_switching( - igraph_t *graph, - const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { - IGRAPH_CHECK(igraph_realize_degree_sequence(graph, out_seq, in_seq, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX)); - IGRAPH_FINALLY(igraph_destroy, graph); - IGRAPH_CHECK(igraph_rewire(graph, 10 * igraph_ecount(graph), IGRAPH_REWIRING_SIMPLE)); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} +/* 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 @@ -687,7 +679,7 @@ igraph_error_t igraph_i_degree_sequence_game_edge_switching( * graph is generated), or the in-degree sequence. * \param method The method to generate the graph. Possible values: * \clist - * \cli IGRAPH_DEGSEQ_CONFIGURATION + * \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 @@ -705,28 +697,23 @@ igraph_error_t igraph_i_degree_sequence_game_edge_switching( * 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_CONFIGURATION_SIMPLE - * This method is identical to \c IGRAPH_DEGSEQ_CONFIGURATION, 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_FAST_HEUR_SIMPLE + * \cli IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE * This method generates simple graphs. - * It is similar to \c IGRAPH_DEGSEQ_CONFIGURATION + * 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_EDGE_SWITCHING_SIMPLE - * This is an MCMC sampler based on degree-preserving edge switches. - * It generates simple undirected or directed graphs. - * It uses \ref igraph_realize_degree_sequence() to construct an initial - * graph, then rewires it using \ref igraph_rewire(). + * \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 switches. + * 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, @@ -754,38 +741,35 @@ igraph_error_t igraph_i_degree_sequence_game_edge_switching( * \example examples/simple/igraph_degree_sequence_game.c */ -igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, - const igraph_vector_int_t *in_deg, +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_int_empty(in_deg) && !igraph_vector_int_empty(out_deg)) { + if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) { in_deg = 0; } switch (method) { - case IGRAPH_DEGSEQ_CONFIGURATION: - return igraph_i_degree_sequence_game_configuration(graph, out_deg, in_deg); + 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_FAST_HEUR_SIMPLE: + case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE: if (in_deg == 0) { - return igraph_i_degree_sequence_game_fast_heur_undirected(graph, out_deg); + return igraph_i_degree_sequence_game_no_multiple_undirected(graph, out_deg); } else { - return igraph_i_degree_sequence_game_fast_heur_directed(graph, out_deg, in_deg); + return igraph_i_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg); } - case IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE: + case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM: if (in_deg == 0) { - return igraph_i_degree_sequence_game_configuration_simple_undirected(graph, out_deg); + return igraph_i_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg); } else { - return igraph_i_degree_sequence_game_configuration_simple_directed(graph, out_deg, in_deg); + return igraph_i_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg); } - case IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE: - return igraph_i_degree_sequence_game_edge_switching(graph, out_deg, in_deg); - default: - IGRAPH_ERROR("Invalid degree sequence game method.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL); } } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h b/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h deleted file mode 100644 index c4e9eeb884a..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - 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, see . -*/ - -#ifndef IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H -#define IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H - -#include "igraph_decls.h" -#include "igraph_datatype.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, - const igraph_vector_int_t *out_seq, - const igraph_vector_int_t *in_seq); - -__END_DECLS - -#endif /* IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H */ diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp new file mode 100644 index 00000000000..11848d29c2d --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h new file mode 100644 index 00000000000..f5c7a415fb5 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h index c96e24b35bc..b52ebd17ed3 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h @@ -25,7 +25,6 @@ #include #include -#include "igraph_types.h" #include "internal/hacks.h" namespace gengraph { @@ -47,12 +46,18 @@ void SET_VERBOSE(int v); // Random number generator void my_srandom(int); -long my_random(); +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 @@ -61,16 +66,19 @@ double my_random01(); // (0,1] defmin(int) defmin(double) defmin(unsigned long) - defmin(long long) #endif //min #ifndef max #define defmax(type) inline type max(type a, type b) { return a>b ? a : b; } defmax(int) defmax(double) defmax(unsigned long) - defmax(long 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 @@ -82,8 +90,8 @@ double my_random01(); // (0,1] //Edge type typedef struct { - igraph_integer_t from; - igraph_integer_t to; + int from; + int to; } edge; // Tag Int @@ -105,20 +113,18 @@ inline double logp(double x) { //Fast search or replace -inline igraph_integer_t* fast_rpl(igraph_integer_t *m, igraph_integer_t a, igraph_integer_t b) { +inline int* fast_rpl(int *m, const int a, const int b) { while (*m != a) { m++; } *m = b; return m; } -inline igraph_integer_t* fast_search(igraph_integer_t *m, igraph_integer_t size, igraph_integer_t a) { - igraph_integer_t *p = m + size; - while (m != p--) { - if (*p == a) { +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; } @@ -158,7 +164,7 @@ inline unsigned char prev_dist(const unsigned char c) { // random number in ]0,1[, _very_ accurate around 0 inline double random_float() { - long r = my_random(); + int r = my_random(); double mul = inv_RANDMAX; while (r <= 0x7FFFFF) { r <<= 8; @@ -173,10 +179,10 @@ inline double random_float() { // Random bit generator, sparwise. static int _random_bits_stored = 0; -static long _random_bits = 0; +static int _random_bits = 0; inline int random_bit() { - long a = _random_bits; + int a = _random_bits; _random_bits = a >> 1; if (_random_bits_stored--) { return a & 0x1; diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp index 2db0a080abd..0f5a784a893 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -19,7 +19,12 @@ * 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 @@ -33,35 +38,80 @@ using namespace std; namespace gengraph { -// shuffle an igraph_integer_t[] randomly -void random_permute(igraph_integer_t *a, igraph_integer_t n); +// 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(igraph_integer_t *q, igraph_integer_t n); +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 (igraph_integer_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { total += deg[i]; } } degree_sequence:: -degree_sequence(igraph_integer_t n0, igraph_integer_t *degs) { +degree_sequence(int n0, int *degs) { deg = degs; n = n0; compute_total(); } degree_sequence:: -degree_sequence(const igraph_vector_int_t *out_seq) { - n = igraph_vector_int_size(out_seq); - deg = &VECTOR(*out_seq)[0]; +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(); } @@ -69,13 +119,177 @@ degree_sequence(const igraph_vector_int_t *out_seq) { #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() { - igraph_integer_t i; - igraph_integer_t dm = dmax() + 1; + int i; + int dm = dmax() + 1; // Sort vertices using basket-sort, in descending degrees - igraph_integer_t *nb = new igraph_integer_t[dm]; - igraph_integer_t *sorted = new igraph_integer_t[n]; + int *nb = new int[dm]; + int *sorted = new int[n]; // init basket for (i = 0; i < dm; i++) { nb[i] = 0; @@ -85,9 +299,9 @@ bool degree_sequence::havelhakimi() { nb[deg[i]]++; } // cumul - igraph_integer_t c = 0; + int c = 0; for (i = dm - 1; i >= 0; i--) { - igraph_integer_t t = nb[i]; + int t = nb[i]; nb[i] = c; c += t; } @@ -97,8 +311,8 @@ bool degree_sequence::havelhakimi() { } // Binding process starts - igraph_integer_t first = 0; // vertex with biggest residual degree - igraph_integer_t d = dm - 1; // maximum residual degree available + 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) @@ -107,14 +321,14 @@ bool degree_sequence::havelhakimi() { d--; } // store it in dv - igraph_integer_t dv = d; + int dv = d; // bind it ! c -= dv; - igraph_integer_t dc = d; // residual degree of vertices we bind to - igraph_integer_t fc = ++first; // position of the first vertex with degree dc + 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) { - igraph_integer_t lc = nb[dc]; + int lc = nb[dc]; if (lc != fc) { while (dv > 0 && lc > fc) { // binds v with sorted[--lc] @@ -137,4 +351,71 @@ bool degree_sequence::havelhakimi() { 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h index ef286bf0f8a..61c287df809 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h @@ -22,61 +22,73 @@ #define DEGREE_SEQUENCE_H #include "igraph_types.h" -#include "igraph_vector.h" +#include "igraph_datatype.h" namespace gengraph { class degree_sequence { private: - igraph_integer_t n; - igraph_integer_t *deg; - igraph_integer_t total; + int n; + int * deg; + int total; public : // #vertices - inline igraph_integer_t size() { + inline int size() { return n; }; - inline igraph_integer_t sum() { + inline int sum() { return total; }; - inline igraph_integer_t operator[](igraph_integer_t i) { + inline int operator[](int i) { return deg[i]; }; - inline igraph_integer_t *seq() { + inline int *seq() { return deg; }; - inline void assign(igraph_integer_t n0, igraph_integer_t* d0) { + inline void assign(int n0, int* d0) { n = n0; deg = d0; }; - inline igraph_integer_t dmax() { - igraph_integer_t dm = deg[0]; - for (igraph_integer_t i = 1; i < n; i++) if (deg[i] > dm) { + inline int dmax() { + int dm = deg[0]; + for (int i = 1; i < n; i++) if (deg[i] > dm) { dm = deg[i]; } return dm; } - degree_sequence(igraph_integer_t n, igraph_integer_t *degs); + 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_int_t *out_seq); + 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(); -#if 0 // raw print (vertex by vertex) void print(); // distribution print (degree frequency) void print_cumul(); -#endif // is degree sequence realizable ? bool havelhakimi(); diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp index 60f3ded632e..dc055a2200d 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -18,29 +18,31 @@ * 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" -#include -#include -#include -#include -#include - namespace gengraph { //_________________________________________________________________________ void graph_molloy_hash::compute_neigh() { - igraph_integer_t *p = links; - for (igraph_integer_t i = 0; i < n; i++) { + int *p = links; + for (int i = 0; i < n; i++) { neigh[i] = p; p += HASH_SIZE(deg[i]); } @@ -49,47 +51,49 @@ void graph_molloy_hash::compute_neigh() { //_________________________________________________________________________ void graph_molloy_hash::compute_size() { size = 0; - for (igraph_integer_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { size += HASH_SIZE(deg[i]); } } //_________________________________________________________________________ void graph_molloy_hash::init() { - for (igraph_integer_t i = 0; i < size; i++) { + for (int i = 0; i < size; i++) { links[i] = HASH_NONE; } } //_________________________________________________________________________ graph_molloy_hash::graph_molloy_hash(degree_sequence °s) { - alloc(degs); + igraph_status("Allocating memory for graph...", 0); + int s = alloc(degs); + igraph_statusf("%d bytes allocated successfully\n", 0, s); } //_________________________________________________________________________ -igraph_integer_t graph_molloy_hash::alloc(degree_sequence °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 igraph_integer_t[n + size]; + deg = new int[n + size]; if (deg == NULL) { return 0; } - igraph_integer_t i; + int i; for (i = 0; i < n; i++) { deg[i] = degs[i]; } links = deg + n; init(); - neigh = new igraph_integer_t*[n]; + neigh = new int*[n]; if (neigh == NULL) { return 0; } compute_neigh(); - return sizeof(igraph_integer_t *)*n + sizeof(igraph_integer_t) * (n + size); + return sizeof(int *)*n + sizeof(int) * (n + size); } //_________________________________________________________________________ @@ -105,7 +109,7 @@ graph_molloy_hash::~graph_molloy_hash() { } //_________________________________________________________________________ -graph_molloy_hash::graph_molloy_hash(igraph_integer_t *svg) { +graph_molloy_hash::graph_molloy_hash(int *svg) { // Read n n = *(svg++); // Read a @@ -115,20 +119,21 @@ graph_molloy_hash::graph_molloy_hash(igraph_integer_t *svg) { degree_sequence dd(n, svg); // Build neigh[] and alloc links[] alloc(dd); + dd.detach(); // Read links[] restore(svg + n); } //_________________________________________________________________________ -igraph_integer_t *graph_molloy_hash::hard_copy() { - igraph_integer_t *hc = new igraph_integer_t[2 + n + a / 2]; // to store n,a,deg[] and links[] +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(igraph_integer_t)*n); - igraph_integer_t *p = hc + 2 + n; - igraph_integer_t *l = links; - for (igraph_integer_t i = 0; i < n; i++) for (igraph_integer_t j = HASH_SIZE(deg[i]); j--; l++) { - igraph_integer_t d; + 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; } @@ -140,20 +145,20 @@ igraph_integer_t *graph_molloy_hash::hard_copy() { //_________________________________________________________________________ bool graph_molloy_hash::is_connected() { bool *visited = new bool[n]; - igraph_integer_t *buff = new igraph_integer_t[n]; - igraph_integer_t comp_size = depth_search(visited, buff); + int *buff = new int[n]; + int comp_size = depth_search(visited, buff); delete[] visited; delete[] buff; return (comp_size == n); } //_________________________________________________________________________ -igraph_integer_t* graph_molloy_hash::backup() { - igraph_integer_t *b = new igraph_integer_t[a / 2]; - igraph_integer_t *c = b; - igraph_integer_t *p = links; - for (igraph_integer_t i = 0; i < n; i++) - for (igraph_integer_t d = HASH_SIZE(deg[i]); d--; p++) if (*p != HASH_NONE && *p > i) { +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)); @@ -161,11 +166,11 @@ igraph_integer_t* graph_molloy_hash::backup() { } //_________________________________________________________________________ -void graph_molloy_hash::restore(igraph_integer_t* b) { +void graph_molloy_hash::restore(int* b) { init(); - igraph_integer_t i; - igraph_integer_t *dd = new igraph_integer_t[n]; - memcpy(dd, deg, sizeof(igraph_integer_t)*n); + int i; + int *dd = new int[n]; + memcpy(dd, deg, sizeof(int)*n); for (i = 0; i < n; i++) { deg[i] = 0; } @@ -179,7 +184,7 @@ void graph_molloy_hash::restore(igraph_integer_t* b) { } //_________________________________________________________________________ -bool graph_molloy_hash::isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { +bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) { if (K < 2) { return false; } @@ -188,18 +193,18 @@ bool graph_molloy_hash::isolated(igraph_integer_t v, igraph_integer_t K, igraph_ return false; } #endif //OPT_ISOLATED - igraph_integer_t *seen = Kbuff; - igraph_integer_t *known = Kbuff; - igraph_integer_t *max = Kbuff + K; + int *seen = Kbuff; + int *known = Kbuff; + int *max = Kbuff + K; *(known++) = v; visited[v] = true; bool is_isolated = true; while (known != seen) { v = *(seen++); - igraph_integer_t *ww = neigh[v]; - igraph_integer_t w; - for (igraph_integer_t d = HASH_SIZE(deg[v]); d--; ww++) if ((w = *ww) != HASH_NONE && !visited[w]) { + 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 @@ -221,19 +226,19 @@ bool graph_molloy_hash::isolated(igraph_integer_t v, igraph_integer_t K, igraph_ } //_________________________________________________________________________ -int graph_molloy_hash::random_edge_swap(igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { +int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) { // Pick two random vertices a and c - igraph_integer_t f1 = pick_random_vertex(); - igraph_integer_t f2 = pick_random_vertex(); + 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) - igraph_integer_t *f1t1 = random_neighbour(f1); - igraph_integer_t t1 = *f1t1; - igraph_integer_t *f2t2 = random_neighbour(f2); - igraph_integer_t t2 = *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; @@ -242,10 +247,10 @@ int graph_molloy_hash::random_edge_swap(igraph_integer_t K, igraph_integer_t *Kb return 0; } // Swap - igraph_integer_t *f1t2 = H_rpl(neigh[f1], deg[f1], f1t1, t2); - igraph_integer_t *f2t1 = H_rpl(neigh[f2], deg[f2], f2t2, t1); - igraph_integer_t *t1f2 = H_rpl(neigh[t1], deg[t1], f1, f2); - igraph_integer_t *t2f1 = H_rpl(neigh[t2], deg[t2], f2, f1); + 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; @@ -262,16 +267,16 @@ int graph_molloy_hash::random_edge_swap(igraph_integer_t K, igraph_integer_t *Kb } //_________________________________________________________________________ -igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, - igraph_integer_t maxtimes, int type) { +unsigned long graph_molloy_hash::shuffle(unsigned long times, + unsigned long maxtimes, int type) { igraph_progress("Shuffle", 0, 0); // assert(verify()); // counters - igraph_integer_t nb_swaps = 0; - igraph_integer_t all_swaps = 0; + unsigned long nb_swaps = 0; + unsigned long all_swaps = 0; unsigned long cost = 0; // window - double T = double(((a < times) ? a : times) / 10); + double T = double(min((unsigned long)(a), times) / 10); if (type == OPTIMAL_HEURISTICS) { T = double(optimal_window()); } @@ -280,14 +285,14 @@ igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, } // isolation test parameter, and buffers double K = 2.4; - igraph_integer_t *Kbuff = new igraph_integer_t[int(K) + 1]; + int *Kbuff = new int[int(K) + 1]; bool *visited = new bool[n]; - for (igraph_integer_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { visited[i] = false; } // Used for monitoring , active only if VERBOSE() - igraph_integer_t failures = 0; - igraph_integer_t successes = 0; + int failures = 0; + int successes = 0; double avg_K = 0; double avg_T = 0; unsigned long next = times; @@ -296,36 +301,36 @@ igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, // Shuffle: while #edge swap attempts validated by connectivity < times ... while (times > nb_swaps && maxtimes > all_swaps) { // Backup graph - igraph_integer_t *save = backup(); + int *save = backup(); // Prepare counters, K, T - igraph_integer_t swaps = 0; - igraph_integer_t K_int = 0; + unsigned long swaps = 0; + int K_int = 0; if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) { - K_int = igraph_integer_t(K); + K_int = int(K); } - igraph_integer_t T_int = (igraph_integer_t)(floor(T)); + 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 += K_int + T_int; + cost += (unsigned long)(K_int) * (unsigned long)(T_int); } // Perform T edge swap attempts - for (igraph_integer_t i = T_int; i > 0; i--) { + for (int i = T_int; i > 0; i--) { // try one swap - swaps += random_edge_swap(K_int, Kbuff, visited); + swaps += (unsigned long)(random_edge_swap(K_int, Kbuff, visited)); all_swaps++; // Verbose if (nb_swaps + swaps > next) { - next = (nb_swaps + swaps) + (times / 1000 > 100 ? times / 1000 : 100); + 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 += (a / 2); + cost += (unsigned long)(a / 2); bool ok = is_connected(); // performance monitor { @@ -378,7 +383,7 @@ igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, } else { K *= 1.35; delete[] Kbuff; - Kbuff = new igraph_integer_t[igraph_integer_t(K) + 1]; + Kbuff = new int[int(K) + 1]; } break; case OPTIMAL_HEURISTICS: @@ -387,7 +392,7 @@ igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, } break; case BRUTE_FORCE_HEURISTICS: - K *= 2; delete[] Kbuff; Kbuff = new igraph_integer_t[igraph_integer_t(K) + 1]; + 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."); @@ -403,42 +408,38 @@ igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, // Status report { - igraph_status("*** Shuffle Monitor ***\n", NULL); - igraph_statusf(" - Average cost : %f / validated edge swap\n", NULL, + 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 : %" IGRAPH_PRId " (%" IGRAPH_PRId " successes, %" IGRAPH_PRId " failures)\n", - NULL, successes + failures, successes, failures); - // %.f rounds to integer - igraph_statusf(" - Average window : %.f\n", NULL, - avg_T / double(successes + failures)); + 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", NULL, + igraph_statusf(" - Average isolation test width : %f\n", 0, avg_K / double(successes + failures)); } return nb_swaps; } //_________________________________________________________________________ - -/* void graph_molloy_hash::print(FILE *f) { - igraph_integer_t i, j; + int i, j; for (i = 0; i < n; i++) { - fprintf(f, "%" IGRAPH_PRId, i); + fprintf(f, "%d", i); for (j = 0; j < HASH_SIZE(deg[i]); j++) if (neigh[i][j] != HASH_NONE) { - fprintf(f, " %" IGRAPH_PRId, neigh[i][j]); + fprintf(f, " %d", neigh[i][j]); } fprintf(f, "\n"); } } -*/ -igraph_error_t graph_molloy_hash::print(igraph_t *graph) { - igraph_integer_t i, j; - igraph_integer_t ptr = 0; - igraph_vector_int_t edges; +int graph_molloy_hash::print(igraph_t *graph) { + int i, j; + long int ptr = 0; + igraph_vector_t edges; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, a); // every edge is counted twice.... + 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++) { @@ -452,25 +453,25 @@ igraph_error_t graph_molloy_hash::print(igraph_t *graph) { } IGRAPH_CHECK(igraph_create(graph, &edges, n, /*undirected=*/ 0)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } //_________________________________________________________________________ -bool graph_molloy_hash::try_shuffle(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *backup_graph) { +bool graph_molloy_hash::try_shuffle(int T, int K, int *backup_graph) { // init all - igraph_integer_t *Kbuff = NULL; + int *Kbuff = NULL; bool *visited = NULL; if (K > 2) { - Kbuff = new igraph_integer_t[K]; + Kbuff = new int[K]; visited = new bool[n]; - for (igraph_integer_t i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { visited[i] = false; } } - igraph_integer_t *back = backup_graph; + int *back = backup_graph; if (back == NULL) { back = backup(); } @@ -521,7 +522,7 @@ bool bernoulli_param_is_lower(int success, int trials, double param) { //_________________________________________________________________________ #define _MIN_SUCCESS_FOR_BERNOULLI_TRUST 100 -double graph_molloy_hash::average_cost(igraph_integer_t T, igraph_integer_t *backup, double min_cost) { +double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) { if (T < 1) { return 1e+99; } @@ -542,11 +543,11 @@ double graph_molloy_hash::average_cost(igraph_integer_t T, igraph_integer_t *bac } //_________________________________________________________________________ -igraph_integer_t graph_molloy_hash::optimal_window() { - igraph_integer_t Tmax; - igraph_integer_t optimal_T = 1; +int graph_molloy_hash::optimal_window() { + int Tmax; + int optimal_T = 1; double min_cost = 1e+99; - igraph_integer_t *back = backup(); + int *back = backup(); // on cherche une borne sup pour Tmax int been_greater = 0; for (Tmax = 1; Tmax <= 5 * a ; Tmax *= 2) { @@ -561,18 +562,18 @@ igraph_integer_t graph_molloy_hash::optimal_window() { min_cost = c; optimal_T = Tmax; } - igraph_statusf("Tmax = %" IGRAPH_PRId " [%f]", 0, Tmax, min_cost); + igraph_statusf("Tmax = %d [%f]", 0, Tmax, min_cost); } // on cree Tmin - igraph_integer_t Tmin = igraph_integer_t(0.5 * double(a) / (min_cost - 1.0)); - igraph_statusf("Optimal T is in [%" IGRAPH_PRId ", %" IGRAPH_PRId "]\n", 0, Tmin, Tmax); + 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]: %" IGRAPH_PRId " [%f]", 0, optimal_T, min_cost); - igraph_integer_t T_low = igraph_integer_t(double(optimal_T) / span); - igraph_integer_t T_high = igraph_integer_t(double(optimal_T) * span); + 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) { @@ -581,7 +582,7 @@ igraph_integer_t graph_molloy_hash::optimal_window() { } { igraph_status("Warning: when looking for optimal T,\n", 0); - igraph_statusf("Low: %" IGRAPH_PRId " [%f] Middle: %" IGRAPH_PRId " [%f] High: %" IGRAPH_PRId " [%f]\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; @@ -619,21 +620,21 @@ double graph_molloy_hash::eval_K(int quality) { } //_________________________________________________________________________ -double graph_molloy_hash::effective_K(igraph_integer_t K, int quality) { +double graph_molloy_hash::effective_K(int K, int quality) { if (K < 3) { return 0.0; } long sum_K = 0; - igraph_integer_t *Kbuff = new igraph_integer_t[K]; + int *Kbuff = new int[K]; bool *visited = new bool[n]; - igraph_integer_t i; + int i; for (i = 0; i < n; i++) { visited[i] = false; } - for (i = 0; i < quality; i++) { + for (int i = 0; i < quality; i++) { // assert(verify()); - igraph_integer_t f1, f2, t1, t2; - igraph_integer_t *f1t1, *f2t2; + int f1, f2, t1, t2; + int *f1t1, *f2t2; do { // Pick two random vertices do { @@ -664,14 +665,14 @@ double graph_molloy_hash::effective_K(igraph_integer_t K, int quality) { } //_________________________________________________________________________ -igraph_integer_t graph_molloy_hash::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { - igraph_integer_t i; +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; } - igraph_integer_t count = 0; - igraph_integer_t left = K; - igraph_integer_t *KB = Kbuff; + 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) { @@ -682,7 +683,7 @@ igraph_integer_t graph_molloy_hash::effective_isolated(igraph_integer_t v, igrap } //_________________________________________________________________________ -void graph_molloy_hash::depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited) { +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; } @@ -699,16 +700,16 @@ void graph_molloy_hash::depth_isolated(igraph_integer_t v, igraph_integer_t &cal // print(); // fflush(stdout); calls++; - igraph_integer_t *copy = NULL; - igraph_integer_t *w = neigh[v]; + int *copy = NULL; + int *w = neigh[v]; if (IS_HASH(deg[v])) { - copy = new igraph_integer_t[deg[v]]; + copy = new int[deg[v]]; H_copy(copy, w, deg[v]); w = copy; } qsort(deg, w, deg[v]); w += deg[v]; - for (igraph_integer_t i = deg[v]; i--; ) { + for (int i = deg[v]; i--; ) { if (visited[*--w]) { calls++; } else { @@ -724,19 +725,19 @@ void graph_molloy_hash::depth_isolated(igraph_integer_t v, igraph_integer_t &cal } //_________________________________________________________________________ -igraph_integer_t graph_molloy_hash::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { - for (igraph_integer_t i = 0; i < n; i++) { +int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) { + for (int i = 0; i < n; i++) { visited[i] = false; } - igraph_integer_t *to_visit = buff; - igraph_integer_t nb_visited = 1; + int *to_visit = buff; + int nb_visited = 1; visited[v0] = true; *(to_visit++) = v0; while (to_visit != buff && nb_visited < n) { - igraph_integer_t v = *(--to_visit); - igraph_integer_t *ww = neigh[v]; - igraph_integer_t w; - for (igraph_integer_t k = HASH_SIZE(deg[v]); k--; ww++) { + 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++; @@ -754,4 +755,418 @@ igraph_integer_t graph_molloy_hash::depth_search(bool *visited, igraph_integer_t // 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 #include // This class handles graphs with a constant degree sequence. @@ -47,35 +45,35 @@ class graph_molloy_hash { private: // Number of vertices - igraph_integer_t n; + int n; //Number of arcs ( = #edges * 2 ) - igraph_integer_t a; + int a; //Total size of links[] - igraph_integer_t size; + int size; // The degree sequence of the graph - igraph_integer_t *deg; + int *deg; // The array containing all links - igraph_integer_t *links; + int *links; // The array containing pointers to adjacency list of every vertices - igraph_integer_t **neigh; + 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!!) - igraph_integer_t alloc(degree_sequence &); + 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(igraph_integer_t u, igraph_integer_t v, igraph_integer_t *realdeg) { - igraph_integer_t deg_u = realdeg[u]; + 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], (u == n - 1 ? links + size : neigh[u + 1]) - neigh[u], v) == NULL); - assert(fast_search(neigh[v], (v == n - 1 ? links + size : neigh[v + 1]) - neigh[v], u) == NULL); + 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); - igraph_integer_t deg_v = realdeg[v]; + int deg_v = realdeg[v]; if (IS_HASH(deg_u)) { *H_add(neigh[u], HASH_EXPAND(deg_u), v) = v; } else { @@ -94,70 +92,70 @@ class graph_molloy_hash { return true; } // Swap edges - inline void swap_edges(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t to2) { + 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(igraph_integer_t) bytes per edge] - igraph_integer_t* backup(); + // Backup graph [sizeof(int) bytes per edge] + int* backup(); // Test if vertex is in an isolated component of size dmax. - void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited); + void depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited); public: //degree of v - inline igraph_integer_t degree(igraph_integer_t v) { + inline int degree(const int v) { return deg[v]; }; // For debug purposes : verify validity of the graph (symetry, simplicity) - //bool verify(); + 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(igraph_integer_t *); + graph_molloy_hash(int *); // Create hard copy of graph - igraph_integer_t *hard_copy(); + int *hard_copy(); // Restore from backup - void restore(igraph_integer_t* back); + void restore(int* back); //Clear hash tables void init(); // nb arcs - inline igraph_integer_t nbarcs() { + inline int nbarcs() { return a; }; // nb vertices - inline igraph_integer_t nbvertices() { + inline int nbvertices() { return n; }; // print graph in SUCC_LIST mode, in stdout - /* void print(FILE *f = stdout); */ - igraph_error_t print(igraph_t *graph); + void print(FILE *f = stdout); + int print(igraph_t *graph); // Test if graph is connected bool is_connected(); // is edge ? - inline bool is_edge(igraph_integer_t u, igraph_integer_t v) { + 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)); @@ -168,19 +166,51 @@ class graph_molloy_hash { } } // Random edge swap ATTEMPT. Return 1 if attempt was a succes, 0 otherwise - int random_edge_swap(igraph_integer_t K = 0, igraph_integer_t *Kbuff = NULL, bool *visited = NULL); + int random_edge_swap(int K = 0, int *Kbuff = NULL, bool *visited = NULL); // Connected Shuffle - igraph_integer_t shuffle(igraph_integer_t, igraph_integer_t, int type); + unsigned long shuffle(unsigned long, unsigned long, int type); // Optimal window for the gkantsidis heuristics - igraph_integer_t optimal_window(); + int optimal_window(); // Average unitary cost per post-validated edge swap, for some window - double average_cost(igraph_integer_t T, igraph_integer_t *back, double min_cost); + 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(igraph_integer_t K, int quality = 10000); + 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(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *back = NULL); + 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 diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp index dc45a2aba4c..2859cdbe4d7 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp @@ -23,13 +23,15 @@ #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" @@ -37,17 +39,44 @@ using namespace std; namespace gengraph { -igraph_integer_t graph_molloy_opt::max_degree() { - igraph_integer_t m = 0; - for (igraph_integer_t k = 0; k < n; k++) if (deg[k] > m) { +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() { - igraph_integer_t *p = links; - for (igraph_integer_t i = 0; i < n; i++) { + int *p = links; + for (int i = 0; i < n; i++) { neigh[i] = p; p += deg[i]; } @@ -57,12 +86,12 @@ void graph_molloy_opt::alloc(degree_sequence °s) { n = degs.size(); a = degs.sum(); assert(a % 2 == 0); - deg = new igraph_integer_t[n + a]; - for (igraph_integer_t i = 0; i < n; i++) { + deg = new int[n + a]; + for (int i = 0; i < n; i++) { deg[i] = degs[i]; } links = deg + n; - neigh = new igraph_integer_t*[n]; + neigh = new int*[n]; compute_neigh(); } @@ -119,7 +148,7 @@ graph_molloy_opt::graph_molloy_opt(degree_sequence °s) { // if(VERBOSE()) fprintf(stderr,"done\n"); // } -graph_molloy_opt::graph_molloy_opt(igraph_integer_t *svg) { +graph_molloy_opt::graph_molloy_opt(int *svg) { // Read n n = *(svg++); // Read a @@ -129,6 +158,7 @@ graph_molloy_opt::graph_molloy_opt(igraph_integer_t *svg) { degree_sequence dd(n, svg); // Build neigh[] and alloc links[] alloc(dd); + dd.detach(); // Read links[] restore(svg + n); } @@ -148,14 +178,14 @@ graph_molloy_opt::~graph_molloy_opt() { detach(); } -igraph_integer_t* graph_molloy_opt::backup(igraph_integer_t *b) { +int* graph_molloy_opt::backup(int *b) { if (b == NULL) { - b = new igraph_integer_t[a / 2]; + b = new int[a / 2]; } - igraph_integer_t *c = b; - for (igraph_integer_t i = 0; i < n; i++) { - igraph_integer_t *p = neigh[i]; - for (igraph_integer_t d = deg[i]; d--; p++) { + int *c = b; + 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; @@ -166,15 +196,15 @@ igraph_integer_t* graph_molloy_opt::backup(igraph_integer_t *b) { return b; } -igraph_integer_t *graph_molloy_opt::hard_copy() { - igraph_integer_t *hc = new igraph_integer_t[2 + n + a / 2]; // to store n,a,deg[] and links[] +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(igraph_integer_t)*n); - igraph_integer_t *c = hc + 2 + n; - for (igraph_integer_t i = 0; i < n; i++) { - igraph_integer_t *p = neigh[i]; - for (igraph_integer_t d = deg[i]; d--; p++) { + 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; @@ -185,15 +215,15 @@ igraph_integer_t *graph_molloy_opt::hard_copy() { return hc; } -void graph_molloy_opt::restore(igraph_integer_t* b) { - igraph_integer_t i; +void graph_molloy_opt::restore(int* b) { + int i; for (i = 0; i < n; i++) { deg[i] = 0; } - igraph_integer_t *p = links; + int *p = links; for (i = 0; i < n - 1; i++) { p += deg[i]; - deg[i] = igraph_integer_t(neigh[i + 1] - neigh[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' @@ -203,107 +233,141 @@ void graph_molloy_opt::restore(igraph_integer_t* b) { } } -igraph_integer_t* graph_molloy_opt::backup_degs(igraph_integer_t *b) { +int* graph_molloy_opt::backup_degs(int *b) { if (b == NULL) { - b = new igraph_integer_t[n]; + b = new int[n]; } - memcpy(b, deg, sizeof(igraph_integer_t)*n); + memcpy(b, deg, sizeof(int)*n); return b; } -void graph_molloy_opt::restore_degs_only(igraph_integer_t *b) { - memcpy(deg, b, sizeof(igraph_integer_t)*n); +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(igraph_integer_t *b) { +void graph_molloy_opt::restore_degs_and_neigh(int *b) { restore_degs_only(b); compute_neigh(); } -void graph_molloy_opt::restore_degs(igraph_integer_t last_degree) { +void graph_molloy_opt::restore_degs(int last_degree) { a = last_degree; deg[n - 1] = last_degree; - for (igraph_integer_t i = n - 2; i >= 0; i--) { - a += (deg[i] = igraph_integer_t(neigh[i + 1] - neigh[i])); + for (int i = n - 2; i >= 0; i--) { + a += (deg[i] = int(neigh[i + 1] - neigh[i])); } refresh_nbarcs(); } void graph_molloy_opt::clean() { - igraph_integer_t *b = hard_copy(); + int *b = hard_copy(); replace(b); delete[] b; } -void graph_molloy_opt::replace(igraph_integer_t *_hardcopy) { +void graph_molloy_opt::replace(int *_hardcopy) { delete[] deg; n = *(_hardcopy++); a = *(_hardcopy++); - deg = new igraph_integer_t[a + n]; - memcpy(deg, _hardcopy, sizeof(igraph_integer_t)*n); + deg = new int[a + n]; + memcpy(deg, _hardcopy, sizeof(int)*n); links = deg + n; compute_neigh(); restore(_hardcopy + n); } -igraph_integer_t* graph_molloy_opt::components(igraph_integer_t *comp) { - igraph_integer_t i; +int* graph_molloy_opt::components(int *comp) { + int i; // breadth-first search buffer - igraph_integer_t *buff = new igraph_integer_t[n]; + int *buff = new int[n]; // comp[i] will contain the index of the component that contains vertex i if (comp == NULL) { - comp = new igraph_integer_t[n]; + comp = new int[n]; } - memset(comp, 0, sizeof(igraph_integer_t)*n); + memset(comp, 0, sizeof(int)*n); // current component index - igraph_integer_t curr_comp = 0; + int curr_comp = 0; // loop over all non-visited vertices... - for (igraph_integer_t v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { + for (int v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { curr_comp++; // initiate breadth-first search - igraph_integer_t *to_visit = buff; - igraph_integer_t *visited = buff; + int *to_visit = buff; + int *visited = buff; *(to_visit++) = v0; comp[v0] = curr_comp; // breadth-first search while (visited != to_visit) { - igraph_integer_t v = *(visited++); - igraph_integer_t d = deg[v]; - for (igraph_integer_t *w = neigh[v]; d--; w++) if (comp[*w] == 0) { + 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[] - igraph_integer_t nb_comp = 0; - memset(buff, 0, sizeof(igraph_integer_t)*n); + 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 - igraph_integer_t offset = 0; - igraph_integer_t *box = pre_boxsort(buff, nb_comp, offset); + 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 (igraph_integer_t *c = comp + n; comp != c--; *c = buff[*c - 1]) { } + 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() { - igraph_integer_t i; - igraph_integer_t dmax = max_degree() + 1; + int i; + int dmax = max_degree() + 1; // Sort vertices using basket-sort, in descending degrees - igraph_integer_t *nb = new igraph_integer_t[dmax]; - igraph_integer_t *sorted = new igraph_integer_t[n]; + int *nb = new int[dmax]; + int *sorted = new int[n]; // init basket for (i = 0; i < dmax; i++) { nb[i] = 0; @@ -313,7 +377,7 @@ bool graph_molloy_opt::havelhakimi() { nb[deg[i]]++; } // cumul - igraph_integer_t c = 0; + int c = 0; for (i = dmax - 1; i >= 0; i--) { c += nb[i]; nb[i] = -nb[i] + c; @@ -324,30 +388,30 @@ bool graph_molloy_opt::havelhakimi() { } // Binding process starts - igraph_integer_t first = 0; // vertex with biggest residual degree - igraph_integer_t d = dmax - 1; // maximum residual degree available + 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 - igraph_integer_t v = sorted[first]; + int v = sorted[first]; // look for current degree of v while (nb[d] <= first) { d--; } // store it in dv - igraph_integer_t dv = d; + int dv = d; // bind it ! c -= dv; - igraph_integer_t dc = d; // residual degree of vertices we bind to - igraph_integer_t fc = ++first; // position of the first vertex with degree dc + 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) { - igraph_integer_t lc = nb[dc]; + int lc = nb[dc]; if (lc != fc) { while (dv > 0 && lc > fc) { // binds v with sorted[--lc] dv--; - igraph_integer_t w = sorted[--lc]; + int w = sorted[--lc]; *(neigh[v]++) = w; *(neigh[w]++) = v; } @@ -364,11 +428,12 @@ bool graph_molloy_opt::havelhakimi() { /* 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 use IGRAPH_FATAL(), as triggering this + * level. Therefore, we report EINTERNAL, as triggering this * indicates a bug. */ - IGRAPH_FATALF("Error in graph_molloy_opt::havelhakimi(): " - "Couldn't bind vertex %" IGRAPH_PRId " entirely (%" IGRAPH_PRId " edges remaining)", - v, dv); + 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; } } @@ -381,22 +446,20 @@ bool graph_molloy_opt::havelhakimi() { bool graph_molloy_opt::is_connected() { bool *visited = new bool[n]; - for (igraph_integer_t i = n; i > 0; visited[--i] = false) { } - igraph_integer_t *to_visit = new igraph_integer_t[n]; - igraph_integer_t *stop = to_visit; - igraph_integer_t left = n - 1; + 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) { - igraph_integer_t v = *(--to_visit); - igraph_integer_t *w = neigh[v]; - for (igraph_integer_t k = deg[v]; k--; w++) { - if (!visited[*w]) { + 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; @@ -411,13 +474,13 @@ bool graph_molloy_opt::make_connected() { // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); return false; } - igraph_integer_t i; + 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) - igraph_integer_t *buff = new igraph_integer_t[MC_BUFF_SIZE]; + int *buff = new int[MC_BUFF_SIZE]; unsigned char * dist = new unsigned char[n]; #define NOT_VISITED 255 #define FORBIDDEN 254 @@ -426,39 +489,43 @@ bool graph_molloy_opt::make_connected() { // 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 - igraph_integer_t *ffub = buff + MC_BUFF_SIZE; + int *ffub = buff + MC_BUFF_SIZE; edge *edges = (edge *) ffub; - igraph_integer_t *trees = ffub; - igraph_integer_t *min_ffub = buff + 1 + (MC_BUFF_SIZE % 2 ? 0 : 1); + 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 (igraph_integer_t v0 = 0; v0 < n; v0++) if (dist[v0] == NOT_VISITED) { + 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; - // 0-degree vertex found, cannot create connected graph + /* 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 - igraph_integer_t *to_visit = buff; - igraph_integer_t *current = buff; + int *to_visit = buff; + int *current = buff; *(to_visit++) = v0; // explore component connected to v0 bool is_a_tree = true; while (current != to_visit) { - igraph_integer_t v = *(current++); + 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; - igraph_integer_t* ww = neigh[v]; - igraph_integer_t w; - for (igraph_integer_t k = deg[v]; k--; ww++) { + 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; @@ -539,7 +606,7 @@ bool graph_molloy_opt::make_connected() { return (trees == ffub || ((trees + 1) == ffub && fatty_edge.from < 0)); } -bool graph_molloy_opt::swap_edges_simple(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t to2) { +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; } @@ -550,27 +617,222 @@ bool graph_molloy_opt::swap_edges_simple(igraph_integer_t from1, igraph_integer_ 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) { - igraph_integer_t i, j; + int i, j; for (i = 0; i < n; i++) { if (!NOZERO || deg[i] > 0) { - fprintf(f, "%" IGRAPH_PRId, i); + fprintf(f, "%d", i); for (j = 0; j < deg[i]; j++) { - fprintf(f, " %" IGRAPH_PRId, neigh[i][j]); + fprintf(f, " %d", neigh[i][j]); } fprintf(f, "\n"); } } } -igraph_integer_t graph_molloy_opt::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { - igraph_integer_t i; +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; } - igraph_integer_t count = 0; - igraph_integer_t left = K; - igraph_integer_t *KB = Kbuff; + 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) { @@ -580,7 +842,7 @@ igraph_integer_t graph_molloy_opt::effective_isolated(igraph_integer_t v, igraph return count; } -void graph_molloy_opt::depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited) { +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; } @@ -595,10 +857,10 @@ void graph_molloy_opt::depth_isolated(igraph_integer_t v, igraph_integer_t &call *(Kbuff++) = v; visited[v] = true; calls++; - igraph_integer_t *w = neigh[v]; + int *w = neigh[v]; qsort(deg, w, deg[v]); w += deg[v]; - for (igraph_integer_t i = deg[v]; i--; ) { + for (int i = deg[v]; i--; ) { if (visited[*--w]) { calls++; } else { @@ -610,19 +872,19 @@ void graph_molloy_opt::depth_isolated(igraph_integer_t v, igraph_integer_t &call } } -igraph_integer_t graph_molloy_opt::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { - for (igraph_integer_t i = 0; i < n; i++) { +int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) { + for (int i = 0; i < n; i++) { visited[i] = false; } - igraph_integer_t *to_visit = buff; - igraph_integer_t nb_visited = 1; + int *to_visit = buff; + int nb_visited = 1; visited[v0] = true; *(to_visit++) = v0; while (to_visit != buff && nb_visited < n) { - igraph_integer_t v = *(--to_visit); - igraph_integer_t *ww = neigh[v]; - igraph_integer_t w; - for (igraph_integer_t k = deg[v]; k--; ww++) if (!visited[w = *ww]) { + 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; @@ -631,23 +893,23 @@ igraph_integer_t graph_molloy_opt::depth_search(bool *visited, igraph_integer_t return nb_visited; } -igraph_integer_t graph_molloy_opt::width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0, igraph_integer_t toclear) { - if (toclear >= 0) for (igraph_integer_t i = 0; i < toclear; i++) { +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 (igraph_integer_t i = 0; i < n; i++) { + } else for (int i = 0; i < n; i++) { dist[i] = 0; } - igraph_integer_t *to_visit = buff; - igraph_integer_t *to_add = buff; - igraph_integer_t nb_visited = 1; + 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) { - igraph_integer_t v = *(to_visit++); - igraph_integer_t *ww = neigh[v]; - igraph_integer_t w; + int v = *(to_visit++); + int *ww = neigh[v]; + int w; unsigned char d = next_dist(dist[v]); - for (igraph_integer_t k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { + for (int k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { dist[w] = d; nb_visited++; *(to_add++) = w; @@ -656,26 +918,83 @@ igraph_integer_t graph_molloy_opt::width_search(unsigned char *dist, igraph_inte 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 !!!! -igraph_integer_t graph_molloy_opt::breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist) { +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; - igraph_integer_t *to_visit = buff; - igraph_integer_t *visited = buff; + int *to_visit = buff; + int *visited = buff; *(to_visit++) = src; paths[src] = 1.0; dist[src] = curr_dist; - igraph_integer_t nb_visited = 1; + int nb_visited = 1; while (visited != to_visit) { - igraph_integer_t v = *(visited++); + int v = *(visited++); if (last_dist == (curr_dist = dist[v])) { break; } unsigned char nd = next_dist(curr_dist); - igraph_integer_t *ww = neigh[v]; + int *ww = neigh[v]; double p = paths[v]; - for (igraph_integer_t k = deg[v]; k--;) { - igraph_integer_t w = *(ww++); + for (int k = deg[v]; k--;) { + int w = *(ww++); unsigned char d = dist[w]; if (d == 0) { // not visited yet ! @@ -688,7 +1007,8 @@ igraph_integer_t graph_molloy_opt::breadth_path_search(igraph_integer_t src, igr } } else if (d == nd) { if ((paths[w] += p) == numeric_limits::infinity()) { - throw std::runtime_error("Fatal error: too many (>MAX_DOUBLE) possible paths in graph."); + IGRAPH_ERROR("Fatal error : too many (>MAX_DOUBLE) possible" + " paths in graph", IGRAPH_EOVERFLOW); } } } @@ -697,8 +1017,467 @@ igraph_integer_t graph_molloy_opt::breadth_path_search(igraph_integer_t src, igr return nb_visited; } -igraph_integer_t *graph_molloy_opt::vertices_real(igraph_integer_t &nb_v) { - igraph_integer_t *yo; +// 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) { @@ -709,13 +1488,14 @@ igraph_integer_t *graph_molloy_opt::vertices_real(igraph_integer_t &nb_v) { IGRAPH_WARNING("graph is empty"); return NULL; } - igraph_integer_t *buff = new igraph_integer_t[nb_v]; + int *buff = new int[nb_v]; yo = buff; - for (igraph_integer_t i = 0; i < n; i++) if (deg[i] > 0) { + 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(%" IGRAPH_PRId ")", 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 { @@ -723,7 +1503,120 @@ igraph_integer_t *graph_molloy_opt::vertices_real(igraph_integer_t &nb_v) { } } -bool graph_molloy_opt::isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { +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; } @@ -732,17 +1625,17 @@ bool graph_molloy_opt::isolated(igraph_integer_t v, igraph_integer_t K, igraph_i return false; } #endif //OPT_ISOLATED - igraph_integer_t *seen = Kbuff; - igraph_integer_t *known = Kbuff; - igraph_integer_t *max = Kbuff + (K - 1); + 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++); - igraph_integer_t *w = neigh[v]; - for (igraph_integer_t d = deg[v]; d--; w++) if (!visited[*w]) { + 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 @@ -763,12 +1656,138 @@ bool graph_molloy_opt::isolated(igraph_integer_t v, igraph_integer_t K, igraph_i 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); // } @@ -776,7 +1795,7 @@ void graph_molloy_opt::sort() { bool graph_molloy_opt::verify(int mode) { IGRAPH_UNUSED(mode); #ifndef NDEBUG - igraph_integer_t i, j, k; + int i, j, k; assert(neigh[0] == links); // verify edges count if ((mode & VERIFY_NOARCS) == 0) { @@ -800,8 +1819,8 @@ bool graph_molloy_opt::verify(int mode) { // assert(neigh[i][j]!=neigh[i][k]); // verify symmetry for (i = 0; i < n; i++) for (j = 0; j < deg[i]; j++) { - igraph_integer_t v = neigh[i][j]; - igraph_integer_t nb = 0; + int v = neigh[i][j]; + int nb = 0; for (k = 0; k < deg[v]; k++) if (neigh[v][k] == i) { nb++; } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h index 657273c6033..f057d606910 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h @@ -23,6 +23,7 @@ #include "gengraph_definitions.h" #include "gengraph_degree_sequence.h" +#include "gengraph_qsort.h" #include #include "gengraph_random.h" @@ -37,28 +38,28 @@ class graph_molloy_opt { // Random generator KW_RNG::RNG rng; // Number of vertices - igraph_integer_t n; + int n; //Number of arcs ( = #edges * 2 ) - igraph_integer_t a; + int a; // The degree sequence of the graph - igraph_integer_t *deg; + int *deg; // The array containing all links - igraph_integer_t *links; + int *links; // The array containing pointers to adjacency list of every vertices - igraph_integer_t **neigh; + 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 (igraph_integer_t* d = deg + n; d != deg; ) { + 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(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t to2) { + 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); @@ -66,86 +67,103 @@ class graph_molloy_opt { } // Swap edges only if they are simple. return false if unsuccessful. - bool swap_edges_simple(igraph_integer_t, igraph_integer_t, igraph_integer_t, igraph_integer_t); + bool swap_edges_simple(int, int, int, int); // Test if vertex is in an isolated component of size dmax. - void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited); + 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. - igraph_integer_t width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0 = 0, igraph_integer_t toclear = -1); + int width_search(unsigned char *dist, int *buff, int v0 = 0, int toclear = -1); // depth-first search. - igraph_integer_t depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0 = 0); + 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 - igraph_integer_t breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist); + 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) - igraph_integer_t *components(igraph_integer_t *comp = NULL); + 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 igraph_integer_t** neighbors() { + inline int** neighbors() { return neigh; }; // deg[] - inline igraph_integer_t* degrees() { + inline int* degrees() { return deg; }; //adjacency list of v - inline igraph_integer_t* operator[](const igraph_integer_t v) { + inline int* operator[](const int v) { return neigh[v]; }; //degree of v - inline igraph_integer_t degree(const igraph_integer_t 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); + 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(igraph_integer_t *); + graph_molloy_opt(int *); // Create hard copy of graph - igraph_integer_t *hard_copy(); + int *hard_copy(); // Remove unused edges, updates neigh[], recreate links[] void clean(); // nb arcs - inline igraph_integer_t nbarcs() { + inline int nbarcs() { return a; }; // last degree - inline igraph_integer_t last_degree() { + inline int last_degree() { return deg[n - 1]; }; // nb vertices - inline igraph_integer_t nbvertices() { + inline int nbvertices() { return n; }; // nb vertices having degree > 0 - inline igraph_integer_t nbvertices_real() { - igraph_integer_t s = 0; - for (igraph_integer_t *d = deg + n; d-- != deg; ) { - if (*d) { + 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. - igraph_integer_t *vertices_real(igraph_integer_t &nb_v); + 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) @@ -155,35 +173,72 @@ class graph_molloy_opt { // Test if graph is connected bool is_connected(); // Maximum degree - igraph_integer_t max_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 igraph_integer_t u, const igraph_integer_t v) { + 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(igraph_integer_t) bytes per edge] - igraph_integer_t* backup(igraph_integer_t *here = NULL); + // Backup graph [sizeof(int) bytes per edge] + int* backup(int *here = NULL); // Restore from backup. Assume that degrees haven't changed - void restore(igraph_integer_t* back); + void restore(int* back); // Resplace with hard backup. - void replace(igraph_integer_t* _hardbackup); + void replace(int* _hardbackup); // Backup degs of graph - igraph_integer_t* backup_degs(igraph_integer_t *here = NULL); + int* backup_degs(int *here = NULL); // Restore degs from neigh[]. Need last degree, though - void restore_degs(igraph_integer_t last_degree); + void restore_degs(int last_degree); // Restore degs[] from backup. Assume that links[] has only been permuted - void restore_degs_only(igraph_integer_t* backup_degs); + void restore_degs_only(int* backup_degs); // Restore degs[] and neigh[]. Assume that links[] has only been permuted - void restore_degs_and_neigh(igraph_integer_t* backup_degs); + 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); + int cycles(int v); // remove vertex (i.e. remove all edges adjacent to vertex) - //void remove_vertex(int v); + 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 diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h index 9689c0a79b0..7084edc6c70 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h @@ -85,15 +85,10 @@ namespace gengraph { #define HASH_NONE (-1) #ifdef HASH_SIZE_IS_POWER2 -inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { - /* Returns pow(2, floor(log2(x)) + 2) if x > 0, 1 otherwise. Works up to - * x == 2^64, starts to break down afterwards */ +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; -#if IGRAPH_INTEGER_SIZE == 64 - x |= x >> 32; -#endif return x + 1; } #define HASH_KEY(x,size) ((x*2198737)&((size)-1)) @@ -109,19 +104,19 @@ inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { #define HASH_REKEY(k,size) ((k)==0 ? (size)-1 : (k)-1) #else //MACRO_RATHER_THAN_INLINE #ifndef HASH_SIZE_IS_POWER2 -inline igraph_integer_t HASH_KEY(igraph_integer_t x, igraph_integer_t size) { +inline int HASH_KEY(const int x, const int size) { assert(x >= 0); return x % size; }; -inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { +inline int HASH_EXPAND(const int x) { _HASH_EXP_CALL(); return x + (x >> 1); }; -inline int HASH_UNEXPAND(igraph_integer_t x) { +inline int HASH_UNEXPAND(const int x) { return ((x << 1) + 1) / 3; }; #endif //HASH_SIZE_IS_POWER2 -inline int HASH_REKEY(igraph_integer_t k, igraph_integer_t s) { +inline int HASH_REKEY(const int k, const int s) { assert(k >= 0); if (k == 0) { return s - 1; @@ -129,7 +124,7 @@ inline int HASH_REKEY(igraph_integer_t k, igraph_integer_t s) { return k - 1; } }; -inline int HASH_SIZE(igraph_integer_t x) { +inline int HASH_SIZE(const int x) { if (IS_HASH(x)) { return HASH_EXPAND(x); } else { @@ -138,7 +133,7 @@ inline int HASH_SIZE(igraph_integer_t x) { }; #endif //MACRO_RATHER_THAN_INLINE -inline igraph_integer_t HASH_PAIR_KEY(igraph_integer_t x, igraph_integer_t y, igraph_integer_t size) { +inline int HASH_PAIR_KEY(const int x, const int y, const int size) { return HASH_KEY(x * 1434879443 + y, size); } @@ -148,19 +143,17 @@ inline igraph_integer_t HASH_PAIR_KEY(igraph_integer_t x, igraph_integer_t y, ig //_________________________________________________________________________ // copy hash table into raw vector -inline void H_copy(igraph_integer_t *mem, igraph_integer_t *h, igraph_integer_t size) { - for (igraph_integer_t i = HASH_EXPAND(size); i--; h++) { - if (*h != HASH_NONE) { +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 igraph_integer_t* H_add(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a) { +inline int* H_add(int* h, const int size, int a) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); - igraph_integer_t k = HASH_KEY(a, size); + int k = HASH_KEY(a, size); if (h[k] == HASH_NONE) { return h + k; } @@ -175,8 +168,8 @@ inline igraph_integer_t* H_add(igraph_integer_t* h, igraph_integer_t size, igrap } // would element be well placed in newk ? -inline bool H_better(igraph_integer_t a, igraph_integer_t size, igraph_integer_t currentk, igraph_integer_t newk) { - igraph_integer_t k = HASH_KEY(a, size); +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 { @@ -185,13 +178,13 @@ inline bool H_better(igraph_integer_t a, igraph_integer_t size, igraph_integer_t } // removes h[k] -inline void H_rm(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t k) { +inline void H_rm(int* h, const int size, int k) { _HASH_RM_CALL(); - igraph_integer_t lasthole = k; + int lasthole = k; do { _HASH_RM_ITER(); k = HASH_REKEY(k, size); - igraph_integer_t next = h[k]; + int next = h[k]; if (next == HASH_NONE) { break; } @@ -204,11 +197,11 @@ inline void H_rm(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t k) } //put a -inline igraph_integer_t* H_put(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t 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(); - igraph_integer_t k = HASH_KEY(a, size); + int k = HASH_KEY(a, size); while (h[k] != HASH_NONE) { k = HASH_REKEY(k, size); _HASH_PUT_ITER(); @@ -219,11 +212,11 @@ inline igraph_integer_t* H_put(igraph_integer_t* h, igraph_integer_t size, igrap } // find A -inline igraph_integer_t H_find(igraph_integer_t *h, igraph_integer_t size, igraph_integer_t 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(); - igraph_integer_t k = HASH_KEY(a, size); + int k = HASH_KEY(a, size); while (h[k] != a) { k = HASH_REKEY(k, size); _HASH_FIND_ITER(); @@ -232,10 +225,10 @@ inline igraph_integer_t H_find(igraph_integer_t *h, igraph_integer_t size, igrap } // Look for the place to add an element. Return NULL if element is already here. -inline bool H_pair_insert(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a, igraph_integer_t b) { +inline bool H_pair_insert(int* h, const int size, int a, int b) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); - igraph_integer_t k = HASH_PAIR_KEY(a, b, size); + int k = HASH_PAIR_KEY(a, b, size); if (h[2 * k] == HASH_NONE) { h[2 * k] = a; h[2 * k + 1] = b; @@ -260,7 +253,7 @@ inline bool H_pair_insert(igraph_integer_t* h, igraph_integer_t size, igraph_int //_________________________________________________________________________ // Look for an element -inline bool H_is(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t elem) { +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 { @@ -269,13 +262,13 @@ inline bool H_is(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t } //pick random location (containing an element) -inline igraph_integer_t* H_random(igraph_integer_t* mem, igraph_integer_t size) { +inline int* H_random(int* mem, int size) { if (!IS_HASH(size)) { return mem + (my_random() % size); } _HASH_RAND_CALL(); size = HASH_EXPAND(size); - igraph_integer_t* yo; + int* yo; do { yo = mem + HASH_KEY(my_random(), size); _HASH_RAND_ITER(); @@ -284,7 +277,7 @@ inline igraph_integer_t* H_random(igraph_integer_t* mem, igraph_integer_t size) } // replace *k by b -inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t* k, igraph_integer_t 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; @@ -298,7 +291,7 @@ inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igr } // replace a by b -inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t a, igraph_integer_t 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)) { diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h index 99e7e14e3ca..50e65d5226b 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h @@ -27,7 +27,7 @@ namespace gengraph { static KW_RNG::RNG _my_random; -long my_random() { +int my_random() { return _my_random.rand_int31(); } void my_srandom(int x) { diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp index 4147117d72d..0417c24ac3a 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -22,6 +22,7 @@ #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" @@ -29,12 +30,11 @@ #include "igraph_error.h" #include "core/exceptions.h" -#include "games/degree_sequence_vl/degree_sequence_vl.h" namespace gengraph { // return negative number if program should exit -// int parse_options(int &argc, char** &argv); +int parse_options(int &argc, char** &argv); // options // static const bool MONITOR_TIME = false; @@ -135,54 +135,58 @@ static const int SHUFFLE_TYPE = FINAL_HEURISTICS; using namespace gengraph; -igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, - const igraph_vector_int_t *out_seq, - const igraph_vector_int_t *in_seq) { - IGRAPH_HANDLE_EXCEPTIONS( - igraph_bool_t is_graphical; +extern "C" { - if (in_seq && igraph_vector_int_size(in_seq) != 0) { - IGRAPH_ERROR("The Viger-Latapy sampler support only undirected graphs.", IGRAPH_EINVAL); - } + 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; - 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); - } + if (in_seq && igraph_vector_size(in_seq) != 0) { + IGRAPH_ERROR("This generator works with undirected graphs only", IGRAPH_EINVAL); + } - RNG_BEGIN(); + 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); + } - degree_sequence *dd = new degree_sequence(out_seq); + RNG_BEGIN(); - graph_molloy_opt *g = new graph_molloy_opt(*dd); - delete dd; + degree_sequence *dd = new degree_sequence(out_seq); - if (!g->havelhakimi()) { - delete g; - RNG_END(); - IGRAPH_FATAL("g->havelhakimi() failed; please report as a bug."); - } + 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()) { + 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; - RNG_END(); - IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence.", - IGRAPH_EINVAL); - } + graph_molloy_hash *gh = new graph_molloy_hash(hc); + delete [] hc; - igraph_integer_t *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); - gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); + IGRAPH_CHECK(gh->print(graph)); + delete gh; - IGRAPH_CHECK(gh->print(graph)); - delete gh; + RNG_END(); + ); - RNG_END(); - ); + return IGRAPH_SUCCESS; + } - return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp new file mode 100644 index 00000000000..8ba0c54faa7 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h new file mode 100644 index 00000000000..57b6b7dd53e --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h index 85df8bc4464..6af1f2c8c7c 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h @@ -21,8 +21,6 @@ #ifndef QSORT_H #define QSORT_H -#include "igraph_types.h" - #include #include @@ -30,19 +28,17 @@ namespace gengraph { //___________________________________________________________________________ // check if every element is zero -inline bool check_zero(igraph_integer_t *mem, igraph_integer_t n) { - for (igraph_integer_t *v = mem + n; v != mem; ) { - if (*(--v) != 0) { +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 igraph_integer_t med3(igraph_integer_t a, igraph_integer_t b, igraph_integer_t c) { +inline int med3(int a, int b, int c) { if (a < b) { if (c < b) { return (a < c) ? c : a; @@ -58,13 +54,13 @@ inline igraph_integer_t med3(igraph_integer_t a, igraph_integer_t b, igraph_inte } } -inline void isort(igraph_integer_t *v, igraph_integer_t t) { +inline void isort(int *v, int t) { if (t < 2) { return; } - for (igraph_integer_t i = 1; i < t; i++) { - igraph_integer_t *w = v + i; - igraph_integer_t tmp = *w; + for (int i = 1; i < t; i++) { + int *w = v + i; + int tmp = *w; while (w != v && *(w - 1) > tmp) { *w = *(w - 1); w--; @@ -73,9 +69,9 @@ inline void isort(igraph_integer_t *v, igraph_integer_t t) { } } -inline igraph_integer_t partitionne(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t p) { - igraph_integer_t i = 0; - igraph_integer_t j = t - 1; +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++; @@ -84,7 +80,7 @@ inline igraph_integer_t partitionne(igraph_integer_t *v, igraph_integer_t t, igr j--; } if (i < j) { - igraph_integer_t tmp = v[i]; + int tmp = v[i]; v[i++] = v[j]; v[j--] = tmp; } @@ -96,22 +92,22 @@ inline igraph_integer_t partitionne(igraph_integer_t *v, igraph_integer_t t, igr return i; } -inline void qsort(igraph_integer_t *v, igraph_integer_t t) { +inline void qsort(int *v, int t) { if (t < 15) { isort(v, t); } else { - igraph_integer_t x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + 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 igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t pos) { +inline int qsort_median(int *v, int t, int pos) { if (t < 10) { isort(v, t); return v[pos]; } - igraph_integer_t x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + 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 { @@ -119,7 +115,7 @@ inline igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t, ig } } -inline igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t) { +inline int qsort_median(int *v, int t) { return qsort_median(v, t, t / 2); } @@ -142,11 +138,11 @@ inline double med3(double a, double b, double c) { } } -inline void isort(double *v, igraph_integer_t t) { +inline void isort(double *v, int t) { if (t < 2) { return; } - for (igraph_integer_t i = 1; i < t; i++) { + for (int i = 1; i < t; i++) { double *w = v + i; double tmp = *w; while (w != v && *(w - 1) > tmp) { @@ -157,9 +153,9 @@ inline void isort(double *v, igraph_integer_t t) { } } -inline igraph_integer_t partitionne(double *v, igraph_integer_t t, double p) { - igraph_integer_t i = 0; - igraph_integer_t j = t - 1; +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++; @@ -180,22 +176,22 @@ inline igraph_integer_t partitionne(double *v, igraph_integer_t t, double p) { return i; } -inline void qsort(double *v, igraph_integer_t t) { +inline void qsort(double *v, int t) { if (t < 15) { isort(v, t); } else { - igraph_integer_t x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + 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, igraph_integer_t t, igraph_integer_t pos) { +inline double qsort_median(double *v, int t, int pos) { if (t < 10) { isort(v, t); return v[pos]; } - igraph_integer_t x = partitionne(v, t, med3(v[t >> 1], v[(t >> 2) + 2], v[t - (t >> 1) - 2])); + 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 { @@ -203,20 +199,20 @@ inline double qsort_median(double *v, igraph_integer_t t, igraph_integer_t pos) } } -inline double qsort_median(double *v, igraph_integer_t t) { +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(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t) { +inline void isort(int *mem, int *v, int t) { if (t < 2) { return; } - for (igraph_integer_t i = 1; i < t; i++) { - igraph_integer_t vtmp = v[i]; - igraph_integer_t tmp = mem[vtmp]; - igraph_integer_t j; + 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]; } @@ -224,13 +220,13 @@ inline void isort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t } } -inline void qsort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t) { +inline void qsort(int *mem, int *v, int t) { if (t < 15) { isort(mem, v, t); } else { - igraph_integer_t p = med3(mem[v[t >> 1]], mem[v[(t >> 2) + 3]], mem[v[t - (t >> 1) - 3]]); - igraph_integer_t i = 0; - igraph_integer_t j = t - 1; + 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++; @@ -239,7 +235,7 @@ inline void qsort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t j--; } if (i < j) { - igraph_integer_t tmp = v[i]; + int tmp = v[i]; v[i++] = v[j]; v[j--] = tmp; } @@ -254,13 +250,13 @@ inline void qsort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t } //Box-Sort 1..n according to value stored in mem[], in DESCENDING order. -inline igraph_integer_t *pre_boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t &offset) { - igraph_integer_t *yo; +inline int *pre_boxsort(int *mem, int n, int &offset) { + int *yo; // maximum and minimum - igraph_integer_t mx = mem[0]; - igraph_integer_t mn = mem[0]; + int mx = mem[0]; + int mn = mem[0]; for (yo = mem + n - 1; yo != mem; yo--) { - igraph_integer_t x = *yo; + int x = *yo; if (x > mx) { mx = x; } @@ -269,12 +265,12 @@ inline igraph_integer_t *pre_boxsort(igraph_integer_t *mem, igraph_integer_t n, } } // box - igraph_integer_t c = mx - mn + 1; - igraph_integer_t *box = new igraph_integer_t[c]; + 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 - igraph_integer_t sum = 0; + int sum = 0; for (yo = box + c; yo != box; ) { sum += *(--yo); *yo = sum; @@ -283,16 +279,16 @@ inline igraph_integer_t *pre_boxsort(igraph_integer_t *mem, igraph_integer_t n, return box; } -inline igraph_integer_t *boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t *buff = NULL) { - igraph_integer_t i; +inline int *boxsort(int *mem, int n, int *buff = NULL) { + int i; if (n <= 0) { return buff; } - igraph_integer_t offset = 0; - igraph_integer_t *box = pre_boxsort(mem, n, offset); + int offset = 0; + int *box = pre_boxsort(mem, n, offset); // sort if (buff == NULL) { - buff = new igraph_integer_t[n]; + buff = new int[n]; } for (i = 0; i < n; i++) { buff[--box[mem[i] - offset]] = i; @@ -302,6 +298,267 @@ inline igraph_integer_t *boxsort(igraph_integer_t *mem, igraph_integer_t n, igra 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp index 67fc0a78aaa..c4d08cb7a74 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp @@ -28,9 +28,11 @@ // 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. diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h index db77c3af538..90d86b59b17 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h @@ -44,7 +44,7 @@ class RNG { IGRAPH_UNUSED(jcong_); } long rand_int31() { - return RNG_INTEGER(0, 0x7fffffff); + return RNG_INT31(); } double rand_halfopen01() { // (0,1] return RNG_UNIF01(); diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h new file mode 100644 index 00000000000..25b8e84e999 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/games/dotproduct.c b/src/vendor/cigraph/src/games/dotproduct.c index 88587afa5c2..c4bf614fa5b 100644 --- a/src/vendor/cigraph/src/games/dotproduct.c +++ b/src/vendor/cigraph/src/games/dotproduct.c @@ -22,10 +22,9 @@ */ #include "igraph_games.h" - -#include "igraph_blas.h" -#include "igraph_constructors.h" #include "igraph_random.h" +#include "igraph_constructors.h" +#include "igraph_blas.h" /** * \function igraph_dot_product_game @@ -57,21 +56,21 @@ * for functions to generate the latent vectors. */ -igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, +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); - igraph_integer_t i, j; - igraph_vector_int_t edges; - igraph_bool_t warned_neg = false, warned_big = false; + int i, j; + igraph_vector_t edges; + igraph_bool_t warned_neg = 0, warned_big = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); RNG_BEGIN(); for (i = 0; i < ncol; i++) { - igraph_integer_t from = directed ? 0 : i + 1; + 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++) { @@ -83,33 +82,34 @@ igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *v igraph_vector_view(&v2, &MATRIX(*vecs, 0, j), nrow); igraph_blas_ddot(&v1, &v2, &prob); if (prob < 0 && ! warned_neg) { - warned_neg = true; - IGRAPH_WARNING("Negative connection probability in dot-product graph."); + warned_neg = 1; + IGRAPH_WARNING("Negative connection probability in " + "dot-product graph"); } else if (prob > 1 && ! warned_big) { - warned_big = true; - IGRAPH_WARNING("Greater than 1 connection probability in dot-product graph."); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + 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_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } } } RNG_END(); - IGRAPH_CHECK(igraph_create(graph, &edges, ncol, directed)); - - igraph_vector_int_destroy(&edges); + igraph_create(graph, &edges, ncol, directed); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sample_sphere_surface - * \brief Sample points uniformly from the surface of a sphere. + * Sample points uniformly from the surface of a sphere * * The center of the sphere is at the origin. * @@ -130,7 +130,7 @@ igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *v * igraph_sample_dirichlet() for other similar samplers. */ -igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, +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) { @@ -138,13 +138,13 @@ igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer if (dim < 2) { IGRAPH_ERROR("Sphere must be at least two dimensional to sample from " - "surface.", IGRAPH_EINVAL); + "surface", IGRAPH_EINVAL); } if (n < 0) { - IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); } if (radius <= 0) { - IGRAPH_ERROR("Sphere radius must be positive.", IGRAPH_EINVAL); + IGRAPH_ERROR("Sphere radius must be positive", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, dim, n)); @@ -171,12 +171,12 @@ igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer RNG_END(); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sample_sphere_volume - * \brief Sample points uniformly from the volume of a sphere. + * Sample points uniformly from the volume of a sphere * * The center of the sphere is at the origin. * @@ -198,7 +198,7 @@ igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer */ -igraph_error_t igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, +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) { @@ -221,12 +221,12 @@ igraph_error_t igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_ RNG_END(); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sample_dirichlet - * \brief Sample points from a Dirichlet distribution. + * Sample points from a Dirichlet distribution * * \param n The number of vectors to sample. * \param alpha The parameters of the Dirichlet distribution. They @@ -245,7 +245,7 @@ igraph_error_t igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_ * latent vectors. */ -igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, +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); @@ -253,17 +253,16 @@ igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t igraph_vector_t vec; if (n < 0) { - IGRAPH_ERRORF("Number of samples should be non-negative, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, n); + IGRAPH_ERROR("Number of samples should be non-negative", + IGRAPH_EINVAL); } if (len < 2) { - IGRAPH_ERRORF("Dirichlet parameter vector too short, must " - "have at least two entries, got %" IGRAPH_PRId - ".", IGRAPH_EINVAL, len); + IGRAPH_ERROR("Dirichlet parameter vector too short, must " + "have at least two entries", IGRAPH_EINVAL); } if (igraph_vector_min(alpha) <= 0) { - IGRAPH_ERRORF("Dirichlet concentration parameters must be positive, got %g.", - IGRAPH_EINVAL, igraph_vector_min(alpha)); + IGRAPH_ERROR("Dirichlet concentration parameters must be positive", + IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, len, n)); @@ -277,5 +276,5 @@ igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t RNG_END(); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/games/erdos_renyi.c b/src/vendor/cigraph/src/games/erdos_renyi.c index cb4bd55f729..c0b1acf2588 100644 --- a/src/vendor/cigraph/src/games/erdos_renyi.c +++ b/src/vendor/cigraph/src/games/erdos_renyi.c @@ -25,11 +25,9 @@ #include "igraph_constructors.h" #include "igraph_interface.h" +#include "igraph_nongraph.h" #include "igraph_random.h" -#include "random/random_internal.h" -#include "math/safe_intop.h" - /** * \section about_games * @@ -37,38 +35,32 @@ * they generate a different graph every time you call them. */ -igraph_error_t igraph_erdos_renyi_game_gnp( +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 ) { - /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. - * This is because on a system with 32-bit ints, maxedges will be larger than - * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` - * for tests on large graphs. - */ - igraph_integer_t no_of_nodes = n; - igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + + long int no_of_nodes = n; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; igraph_vector_t s = IGRAPH_VECTOR_NULL; - igraph_integer_t vsize; + int retval = 0; + long int vsize; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (p < 0.0 || p > 1.0) { - IGRAPH_ERROR("Invalid probability given.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid probability given", IGRAPH_EINVAL); } if (p == 0.0 || no_of_nodes == 0) { - IGRAPH_CHECK(igraph_empty(graph, n, directed)); + IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); } else if (p == 1.0) { - IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); + IGRAPH_CHECK(retval = igraph_full(graph, n, directed, loops)); } else { - igraph_integer_t i; - igraph_real_t maxedges = n, last; - igraph_integer_t maxedges_int; - + long int i; + double maxedges = n, last; if (directed && loops) { maxedges *= n; } else if (directed && !loops) { @@ -79,12 +71,8 @@ igraph_error_t igraph_erdos_renyi_game_gnp( maxedges *= (n - 1) / 2.0; } - if (maxedges > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); - } IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &maxedges_int)); - IGRAPH_CHECK(igraph_vector_reserve(&s, maxedges_int)); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); RNG_BEGIN(); @@ -97,82 +85,77 @@ igraph_error_t igraph_erdos_renyi_game_gnp( RNG_END(); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_size(&s) * 2)); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); - igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); - igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + 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_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); } } else if (!directed && loops) { for (i = 0; i < vsize; i++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); - igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); - igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n, directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return retval; } -igraph_error_t igraph_erdos_renyi_game_gnm( - igraph_t *graph, igraph_integer_t n, igraph_integer_t m, +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 ) { - /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. - * This is because on a system with 32-bit ints, maxedges will be larger than - * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` - * for tests on large graphs. This is also why we need a 'real' version of random_sample. - */ igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_edges = m; - igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + 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); + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } - if (m < 0 || m > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); + if (m < 0) { + IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); } if (m == 0.0 || no_of_nodes == 0) { - IGRAPH_CHECK(igraph_empty(graph, n, directed)); + IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); } else { - igraph_integer_t i; - igraph_real_t maxedges = n; + long int i; + double maxedges = n; if (directed && loops) { maxedges *= n; } else if (directed && !loops) { @@ -184,64 +167,65 @@ igraph_error_t igraph_erdos_renyi_game_gnm( } if (no_of_edges > maxedges) { - IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); } if (maxedges == no_of_edges) { - IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); + retval = igraph_full(graph, n, directed, loops); } else { - igraph_integer_t slen; + long int slen; IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_random_sample_real(&s, 0, maxedges - 1, no_of_edges)); + IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1, + (igraph_integer_t) no_of_edges)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_size(&s) * 2)); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); - igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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++) { - igraph_integer_t from = floor(VECTOR(s)[i] / (no_of_nodes_real - 1)); - igraph_integer_t to = VECTOR(s)[i] - from * (no_of_nodes_real - 1); + 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_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); } } else if (!directed && loops) { for (i = 0; i < slen; i++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); - igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); - igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + 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(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + retval = igraph_create(graph, &edges, n, directed); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } } - return IGRAPH_SUCCESS; + return retval; } /** @@ -284,15 +268,18 @@ igraph_error_t igraph_erdos_renyi_game_gnm( * * \example examples/simple/igraph_erdos_renyi_game.c */ -igraph_error_t igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type, +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) { - return igraph_erdos_renyi_game_gnp(graph, n, p_or_m, directed, loops); + retval = igraph_erdos_renyi_game_gnp(graph, n, p_or_m, directed, loops); } else if (type == IGRAPH_ERDOS_RENYI_GNM) { - return igraph_erdos_renyi_game_gnm(graph, n, (igraph_integer_t) p_or_m, directed, loops); + 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/vendor/cigraph/src/games/establishment.c b/src/vendor/cigraph/src/games/establishment.c index 9f8cf4ea8e7..552858c7909 100644 --- a/src/vendor/cigraph/src/games/establishment.c +++ b/src/vendor/cigraph/src/games/establishment.c @@ -56,21 +56,21 @@ * Time complexity: O(|V|*k*log(|V|)), |V| is the number of vertices * and k is the \p k parameter. */ -igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, +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_int_t *node_type_vec) { - igraph_integer_t i, j; - igraph_vector_int_t edges; + igraph_vector_t *node_type_vec) { + long int i, j; + igraph_vector_t edges; igraph_vector_t cumdist; - igraph_vector_int_t potneis; + igraph_vector_t potneis; igraph_real_t maxcum; - igraph_vector_int_t *nodetypes; + igraph_vector_t *nodetypes; /* Argument contracts */ - if (nodes < 0) { + if(nodes < 0){ IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } @@ -90,7 +90,7 @@ igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (isnan(lo)) { + if (igraph_is_nan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -101,12 +101,12 @@ igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ + 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 (isnan(lo) || isnan(hi)) { + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -115,9 +115,9 @@ igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&potneis, k); + IGRAPH_VECTOR_INIT_FINALLY(&potneis, k); if (type_dist) { VECTOR(cumdist)[0] = 0; @@ -137,33 +137,33 @@ igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes if (node_type_vec) { nodetypes = node_type_vec; - IGRAPH_CHECK(igraph_vector_int_resize(nodetypes, nodes)); + IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); if (! nodetypes) { - IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); } RNG_BEGIN(); for (i = 0; i < nodes; i++) { igraph_real_t uni = RNG_UNIF(0, maxcum); - igraph_integer_t type; + long int type; igraph_vector_binsearch(&cumdist, uni, &type); VECTOR(*nodetypes)[i] = type - 1; } for (i = k; i < nodes; i++) { - igraph_integer_t type1 = VECTOR(*nodetypes)[i]; + long int type1 = (long int) VECTOR(*nodetypes)[i]; igraph_random_sample(&potneis, 0, i - 1, k); for (j = 0; j < k; j++) { - igraph_integer_t type2 = VECTOR(*nodetypes)[VECTOR(potneis)[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_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, VECTOR(potneis)[j])); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, VECTOR(potneis)[j])); } } } @@ -171,15 +171,15 @@ igraph_error_t igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes RNG_END(); if (! node_type_vec) { - igraph_vector_int_destroy(nodetypes); + igraph_vector_destroy(nodetypes); IGRAPH_FREE(nodetypes); IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_int_destroy(&potneis); + igraph_vector_destroy(&potneis); igraph_vector_destroy(&cumdist); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/forestfire.c b/src/vendor/cigraph/src/games/forestfire.c index bb9c12bee75..a4d69bfad11 100644 --- a/src/vendor/cigraph/src/games/forestfire.c +++ b/src/vendor/cigraph/src/games/forestfire.c @@ -22,28 +22,27 @@ */ #include "igraph_games.h" - -#include "igraph_constructors.h" -#include "igraph_dqueue.h" -#include "igraph_interface.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_int_t *inneis; - igraph_vector_int_t *outneis; - igraph_integer_t no_of_nodes; + 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) { - igraph_integer_t i; + long int i; for (i = 0; i < data->no_of_nodes; i++) { - igraph_vector_int_destroy(data->inneis + i); - igraph_vector_int_destroy(data->outneis + i); + igraph_vector_destroy(data->inneis + i); + igraph_vector_destroy(data->outneis + i); } } @@ -79,14 +78,12 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * \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 @@ -98,7 +95,7 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * \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. + 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. @@ -106,17 +103,17 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * Time complexity: TODO. */ -igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, +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_int_t visited; - igraph_integer_t no_of_nodes = nodes, actnode, i; - igraph_vector_int_t edges; - igraph_vector_int_t *inneis, *outneis; + 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_int_t neiq; - igraph_integer_t ambs = pambs; + 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; @@ -135,19 +132,19 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, if (ambs == 0) { IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); + inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); if (!inneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, inneis); - outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); + outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); if (!outneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, outneis); data.inneis = inneis; @@ -155,24 +152,24 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, 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_int_init(inneis + i, 0)); - IGRAPH_CHECK(igraph_vector_int_init(outneis + i, 0)); + IGRAPH_CHECK(igraph_vector_init(inneis + i, 0)); + IGRAPH_CHECK(igraph_vector_init(outneis + i, 0)); } - IGRAPH_CHECK(igraph_vector_int_init(&visited, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &visited); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&neiq, 10); + 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_int_push(&neiq, (nei))); \ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, actnode)); \ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (nei))); \ - IGRAPH_CHECK(igraph_vector_int_push_back(outneis+actnode, (nei))); \ - IGRAPH_CHECK(igraph_vector_int_push_back(inneis+(nei), actnode)); \ + 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); @@ -188,29 +185,29 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, /* Choose ambassador(s) */ for (i = 0; i < ambs; i++) { - igraph_integer_t a = RNG_INTEGER(0, actnode - 1); + long int a = RNG_INTEGER(0, actnode - 1); ADD_EDGE_TO(a); } - while (!igraph_dqueue_int_empty(&neiq)) { - igraph_integer_t actamb = igraph_dqueue_int_pop(&neiq); - igraph_vector_int_t *outv = outneis + actamb; - igraph_vector_int_t *inv = inneis + actamb; - igraph_integer_t no_in = igraph_vector_int_size(inv); - igraph_integer_t no_out = igraph_vector_int_size(outv); - igraph_integer_t neis_out = RNG_GEOM(param_geom_out); - igraph_integer_t neis_in = RNG_GEOM(param_geom_in); + 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++) { - igraph_integer_t nei = VECTOR(*outv)[i]; + long int nei = (long int) VECTOR(*outv)[i]; ADD_EDGE_TO(nei); } } else { - igraph_integer_t oleft = no_out; + long int oleft = no_out; for (i = 0; i < neis_out && oleft > 0; ) { - igraph_integer_t which = RNG_INTEGER(0, oleft - 1); - igraph_integer_t nei = VECTOR(*outv)[which]; + 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) { @@ -223,14 +220,14 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, /* incoming neighbors */ if (neis_in >= no_in) { for (i = 0; i < no_in; i++) { - igraph_integer_t nei = VECTOR(*inv)[i]; + long int nei = (long int) VECTOR(*inv)[i]; ADD_EDGE_TO(nei); } } else { - igraph_integer_t ileft = no_in; + long int ileft = no_in; for (i = 0; i < neis_in && ileft > 0; ) { - igraph_integer_t which = RNG_INTEGER(0, ileft - 1); - igraph_integer_t nei = VECTOR(*inv)[which]; + 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) { @@ -251,15 +248,15 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL); - igraph_dqueue_int_destroy(&neiq); - igraph_vector_int_destroy(&visited); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/grg.c b/src/vendor/cigraph/src/games/grg.c index dbd6348a566..922a9b254ca 100644 --- a/src/vendor/cigraph/src/games/grg.c +++ b/src/vendor/cigraph/src/games/grg.c @@ -55,22 +55,18 @@ * * \example examples/simple/igraph_grg_game.c */ -igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, +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) { - igraph_integer_t i; - igraph_vector_t myx, myy, *xx = &myx, *yy = &myy; - igraph_vector_int_t edges; + long int i; + igraph_vector_t myx, myy, *xx = &myx, *yy = &myy, edges; igraph_real_t r2; if (nodes < 0) { IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes)); - /* since we only connect nodes strictly closer than radius, * radius < 0 is equivalent to radius == 0 */ if (radius < 0) { @@ -78,6 +74,9 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, } 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)); @@ -106,7 +105,7 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, for (i = 0; i < nodes; i++) { igraph_real_t xx1 = VECTOR(*xx)[i]; igraph_real_t yy1 = VECTOR(*yy)[i]; - igraph_integer_t j = i + 1; + long int j = i + 1; igraph_real_t dx, dy; IGRAPH_ALLOW_INTERRUPTION(); @@ -115,8 +114,8 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, while ( j < nodes && (dx = VECTOR(*xx)[j] - xx1) < radius) { dy = VECTOR(*yy)[j] - yy1; if (dx * dx + dy * dy < r2) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } j++; } @@ -125,7 +124,7 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, for (i = 0; i < nodes; i++) { igraph_real_t xx1 = VECTOR(*xx)[i]; igraph_real_t yy1 = VECTOR(*yy)[i]; - igraph_integer_t j = i + 1; + long int j = i + 1; igraph_real_t dx, dy; IGRAPH_ALLOW_INTERRUPTION(); @@ -140,8 +139,8 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, dy = 1 - dy; } if (dx * dx + dy * dy < r2) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } j++; } @@ -154,8 +153,8 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, dy = 1 - dy; } if (dx * dx + dy * dy < r2) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } j++; } @@ -173,7 +172,7 @@ igraph_error_t igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, IGRAPH_UNDIRECTED)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/growing_random.c b/src/vendor/cigraph/src/games/growing_random.c index cdbfe7fff68..68fdefc72d2 100644 --- a/src/vendor/cigraph/src/games/growing_random.c +++ b/src/vendor/cigraph/src/games/growing_random.c @@ -26,24 +26,22 @@ #include "igraph_constructors.h" #include "igraph_random.h" -#include "math/safe_intop.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 + * \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: @@ -54,49 +52,41 @@ * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges. */ -igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, +int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation) { - igraph_integer_t no_of_nodes = n; - igraph_integer_t no_of_neighbors = m; - igraph_integer_t no_of_edges; - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + long int no_of_nodes = n; + long int no_of_neighbors = m; + long int no_of_edges; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t resp = 0; + long int resp = 0; - igraph_integer_t i, j; + long int i, j; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (m < 0) { - IGRAPH_ERROR("Invalid number of edges per step (m).", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of edges per step (m)", IGRAPH_EINVAL); } - if (no_of_nodes == 0) { - no_of_edges = 0; - } else { - IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Number of edges overflows.", IGRAPH_EOVERFLOW); - } - } + no_of_edges = no_of_nodes > 0 ? (no_of_nodes - 1) * no_of_neighbors : 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + 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) { - igraph_integer_t to = RNG_INTEGER(0, i - 1); + long int to = RNG_INTEGER(0, i - 1); VECTOR(edges)[resp++] = i; VECTOR(edges)[resp++] = to; } else { - igraph_integer_t from = RNG_INTEGER(0, i); - igraph_integer_t to = RNG_INTEGER(1, i); + long int from = RNG_INTEGER(0, i); + long int to = RNG_INTEGER(1, i); VECTOR(edges)[resp++] = from; VECTOR(edges)[resp++] = to; } @@ -106,8 +96,8 @@ igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, RNG_END(); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/games/islands.c b/src/vendor/cigraph/src/games/islands.c index edececa2be1..e9f5ace1466 100644 --- a/src/vendor/cigraph/src/games/islands.c +++ b/src/vendor/cigraph/src/games/islands.c @@ -26,9 +26,6 @@ #include "igraph_constructors.h" #include "igraph_random.h" -#include "math/safe_intop.h" -#include "random/random_internal.h" - /** * \ingroup generators * \function igraph_simple_interconnected_islands_game @@ -36,16 +33,15 @@ * * All islands are of the same size. Within an island, each edge is generated * with the same probability. A fixed number of additional edges are then - * generated for each unordered pair of islands to connect them. The generated - * graph is guaranteed to be simple. + * generated for each unordered pair of islands to connect them. Note that + * the islands themselves will \em not contain multiple edges, but there might + * be multiple edges between the same pair of vertices between islands. * * \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 within islands. - * \param n_inter The number of edges to create between two islands. It may be - * larger than \p islands_size squared, but in this case it is assumed - * to be \p islands_size squared. + * \param n_inter The number of edges to create between two islands. * * \return Error code: * \c IGRAPH_EINVAL: invalid parameter @@ -55,24 +51,26 @@ * number of vertices plus the number of edges in the graph. * */ -igraph_error_t igraph_simple_interconnected_islands_game( +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_int_t edges = IGRAPH_VECTOR_NULL; + + igraph_vector_t edges = IGRAPH_VECTOR_NULL; igraph_vector_t s = IGRAPH_VECTOR_NULL; - igraph_integer_t number_of_nodes; - igraph_real_t max_possible_edges_per_island; - igraph_real_t avg_edges_per_island; - igraph_integer_t number_of_inter_island_edges; - igraph_integer_t start_index_of_island, start_index_of_other_island; - igraph_integer_t i, j, is, from, to; - igraph_real_t last; - igraph_integer_t island_ecount; - igraph_real_t nr_edges_reserved; + 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); @@ -87,89 +85,78 @@ igraph_error_t igraph_simple_interconnected_islands_game( IGRAPH_ERRORF("Number of inter-island links cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n_inter); } - number_of_inter_island_edges = islands_size * islands_size; - if (n_inter > number_of_inter_island_edges) { - IGRAPH_ERRORF( - "Too many edges requested between islands, maximum possible " - "is %" IGRAPH_PRId ", got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, number_of_inter_island_edges, n_inter - ); - } - /* how much memory ? */ - number_of_nodes = islands_n * islands_size; - max_possible_edges_per_island = ((igraph_real_t)islands_size * ((igraph_real_t)islands_size - 1.0)) / 2.0; - avg_edges_per_island = islands_pin * max_possible_edges_per_island; - number_of_inter_island_edges = n_inter * (islands_n * (islands_n - 1)) / 2; - - nr_edges_reserved = 1.1 * avg_edges_per_island * islands_n + number_of_inter_island_edges; - /* The cast of ECOUNT_MAX to double could change its value, which means in theory the size of - the edges vector could still overflow, but only for very rare cases. */ - if (nr_edges_reserved > (double) (IGRAPH_ECOUNT_MAX ) || nr_edges_reserved > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); - } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nr_edges_reserved * 2)); + 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; - IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_vector_reserve(&s, 1.1 * avg_edges_per_island)); + /* 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, both inclusive */ - start_index_of_island = islands_size * is; - igraph_vector_clear(&s); + /* 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 < max_possible_edges_per_island) { /* avg_edges_per_island */ + while (last < maxpossibleedgesPerIsland) { /* maxedgesPerIsland */ IGRAPH_CHECK(igraph_vector_push_back(&s, last)); - last += RNG_GEOM(islands_pin); + myrand = RNG_GEOM(islands_pin); + last += myrand; /* RNG_GEOM(islands_pin); */ last += 1; } - island_ecount = igraph_vector_size(&s); - for (i = 0; i < island_ecount; i++) { - to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2.0); - from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2.0; - to += start_index_of_island; - from += start_index_of_island; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + + /* 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 */ - island_ecount = islands_size * islands_size; - number_of_inter_island_edges = n_inter; for (i = is + 1; i < islands_n; i++) { /* for each other island (not the previous ones) */ - IGRAPH_CHECK(igraph_random_sample_real(&s, 0, island_ecount - 1, n_inter)); - start_index_of_other_island = i * islands_size; for (j = 0; j < n_inter; j++) { /* for each link between islands */ - from = VECTOR(s)[j] / islands_size; - to = VECTOR(s)[j] - from * islands_size; - from += start_index_of_island; - to += start_index_of_other_island; + long int from = RNG_INTEGER(startIsland, endIsland); + long int to = RNG_INTEGER(i * islands_size, (i + 1) * islands_size - 1); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); } + } } - igraph_vector_destroy(&s); - IGRAPH_FINALLY_CLEAN(1); - RNG_END(); /* actually fill the graph object */ - IGRAPH_CHECK(igraph_create(graph, &edges, number_of_nodes, 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, nbNodes, 0)); /* clean remaining things */ - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/k_regular.c b/src/vendor/cigraph/src/games/k_regular.c index 0cebc213ad2..ff989ecbcfa 100644 --- a/src/vendor/cigraph/src/games/k_regular.c +++ b/src/vendor/cigraph/src/games/k_regular.c @@ -23,6 +23,8 @@ #include "igraph_games.h" +#include "igraph_interface.h" + /** * \ingroup generators * \function igraph_k_regular_game @@ -34,11 +36,10 @@ * * * Currently, this game simply uses \ref igraph_degree_sequence_game with - * the \c IGRAPH_DEGSEQ_CONFIGURATION or the \c IGRAPH_DEGSEQ_FAST_SIMPLE - * 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. + * 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. @@ -56,29 +57,28 @@ * * Time complexity: O(|V|+|E|) if \c multiple is true, otherwise not known. */ -igraph_error_t igraph_k_regular_game(igraph_t *graph, +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_int_t degseq; - igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_CONFIGURATION : IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE; + 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, - * and it produces only connected graphs. - * Actually, that's why we have added FAST_HEUR_SIMPLE. */ + * 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); + IGRAPH_ERROR("number of nodes must be non-negative", IGRAPH_EINVAL); } if (k < 0) { - IGRAPH_ERROR("Degree must be non-negative.", IGRAPH_EINVAL); + IGRAPH_ERROR("degree must be non-negative", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(°seq, no_of_nodes); - igraph_vector_int_fill(°seq, k); + 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_int_destroy(°seq); + igraph_vector_destroy(°seq); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/preference.c b/src/vendor/cigraph/src/games/preference.c index 1af55299c9f..2148550a4fc 100644 --- a/src/vendor/cigraph/src/games/preference.c +++ b/src/vendor/cigraph/src/games/preference.c @@ -26,12 +26,22 @@ #include "igraph_constructors.h" #include "igraph_memory.h" #include "igraph_random.h" -#include "igraph_vector_list.h" #include "core/interruption.h" -#include "math/safe_intop.h" -#include /* for sqrt and floor */ +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 @@ -82,23 +92,22 @@ * \ref igraph_establishment_game(), \ref igraph_callaway_traits_game() */ -igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, +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_int_t *node_type_vec, + igraph_vector_t *node_type_vec, igraph_bool_t directed, igraph_bool_t loops) { - igraph_integer_t i, j, no_reserved_edges; - igraph_vector_int_t edges; - igraph_vector_t s; - igraph_vector_int_t* nodetypes; - igraph_vector_int_list_t vids_by_type; + 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) { + if(nodes < 0){ IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } @@ -118,7 +127,7 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (isnan(lo)) { + if (igraph_is_nan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -129,12 +138,12 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ + 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 (isnan(lo) || isnan(hi)) { + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -150,18 +159,28 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, } if (node_type_vec) { - IGRAPH_CHECK(igraph_vector_int_resize(node_type_vec, nodes)); + IGRAPH_CHECK(igraph_vector_resize(node_type_vec, nodes)); nodetypes = node_type_vec; } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); if (nodetypes == 0) { - IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); } - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_type, types); + 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(); @@ -183,66 +202,65 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, maxcum = igraph_vector_tail(&cumdist); for (i = 0; i < nodes; i++) { - igraph_integer_t type1; + 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_int_push_back( - igraph_vector_int_list_get_ptr(&vids_by_type, type1 - 1), i - )); + 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 { - igraph_integer_t an = 0; + long int an = 0; if (type_dist) { for (i = 0; i < types; i++) { - igraph_integer_t no = VECTOR(*type_dist)[i]; - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, 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_int_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_push_back(v, an)); an++; } } } else { - igraph_integer_t size_of_one_group = nodes / types; + igraph_integer_t size_of_one_group = (igraph_integer_t) floor( (double)nodes / types); igraph_integer_t num_groups_with_one_extra_node = nodes - size_of_one_group * types; for (i = 0; i < types; i++) { - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, i); + igraph_vector_t *v = VECTOR(vids_by_type)[i]; for (j = 0; j < size_of_one_group; j++) { VECTOR(*nodetypes)[an] = i; - IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_push_back(v, an)); an++; } if (i < num_groups_with_one_extra_node) { VECTOR(*nodetypes)[an] = i; - IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_push_back(v, an)); an++; } } } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + 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 */ - igraph_integer_t k, l, l_x2; + long int k, l; igraph_real_t p, last; - igraph_vector_int_t *v1, *v2; - igraph_integer_t v1_size, v2_size; + igraph_vector_t *v1, *v2; + long int v1_size, v2_size; IGRAPH_ALLOW_INTERRUPTION(); - v1 = igraph_vector_int_list_get_ptr(&vids_by_type, i); - v2 = igraph_vector_int_list_get_ptr(&vids_by_type, j); - v1_size = igraph_vector_int_size(v1); - v2_size = igraph_vector_int_size(v2); + 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); @@ -251,25 +269,20 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, if (i > j && !directed) { continue; } - maxedges = ((igraph_real_t) v1_size) * v2_size; + maxedges = v1_size * v2_size; } else { if (directed && loops) { - maxedges = ((igraph_real_t) v1_size) * v1_size; + maxedges = v1_size * v1_size; } else if (directed && !loops) { - maxedges = ((igraph_real_t) v1_size) * (v1_size - 1); + maxedges = v1_size * (v1_size - 1); } else if (!directed && loops) { - maxedges = ((igraph_real_t) v1_size) * (v1_size + 1) / 2; + maxedges = v1_size * (v1_size + 1) / 2; } else { - maxedges = ((igraph_real_t) v1_size) * (v1_size - 1) / 2; + maxedges = v1_size * (v1_size - 1) / 2; } } - if (maxedges > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); - } - - IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); - IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); last = RNG_GEOM(p); while (last < maxedges) { @@ -279,50 +292,48 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, } l = igraph_vector_size(&s); - IGRAPH_SAFE_MULT(l, 2, &l_x2); - IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); - igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); - igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + 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++) { - igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); - igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + 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_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + 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++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) - 1) / 2); - igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to + 1)) / 2); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + 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++) { - igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) + 1) / 2); - igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to - 1)) / 2); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); + 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]); } } } @@ -332,17 +343,17 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, RNG_END(); igraph_vector_destroy(&s); - igraph_vector_int_list_destroy(&vids_by_type); + igraph_i_preference_game_free_vids_by_type(&vids_by_type); IGRAPH_FINALLY_CLEAN(2); if (node_type_vec == 0) { - igraph_vector_int_destroy(nodetypes); + igraph_vector_destroy(nodetypes); IGRAPH_FREE(nodetypes); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -386,26 +397,23 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, * \sa \ref igraph_preference_game() */ -igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, +int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t no_out_types, igraph_integer_t no_in_types, const igraph_matrix_t *type_dist_matrix, const igraph_matrix_t *pref_matrix, - igraph_vector_int_t *node_type_out_vec, - igraph_vector_int_t *node_type_in_vec, + igraph_vector_t *node_type_out_vec, + igraph_vector_t *node_type_in_vec, igraph_bool_t loops) { - igraph_integer_t i, j, k, no_reserved_edges; - igraph_vector_int_t edges; - igraph_vector_t s; - igraph_vector_t cumdist; - igraph_vector_int_t intersect; - igraph_vector_int_t *nodetypes_in; - igraph_vector_int_t *nodetypes_out; - igraph_vector_int_list_t vids_by_intype, vids_by_outtype; + 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) { + if(nodes < 0){ IGRAPH_ERROR("The number of vertices must not be negative.", IGRAPH_EINVAL); } @@ -429,7 +437,7 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer if (lo < 0) { IGRAPH_ERROR("The type distribution matrix must not contain negative values.", IGRAPH_EINVAL); } - if (isnan(lo)) { + if (igraph_is_nan(lo)) { IGRAPH_ERROR("The type distribution matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -441,12 +449,12 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ + 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 (isnan(lo) || isnan(hi)) { + if (igraph_is_nan(lo) || igraph_is_nan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -455,26 +463,47 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer if (node_type_in_vec) { nodetypes_in = node_type_in_vec; - IGRAPH_CHECK(igraph_vector_int_resize(nodetypes_in, nodes)); + IGRAPH_CHECK(igraph_vector_resize(nodetypes_in, nodes)); } else { - nodetypes_in = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(nodetypes_in, "Insufficient memory for asymmetric preference game."); - IGRAPH_FINALLY(igraph_free, &nodetypes_in); - IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_in, nodes); + 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_int_resize(nodetypes_out, nodes)); + IGRAPH_CHECK(igraph_vector_resize(nodetypes_out, nodes)); } else { - nodetypes_out = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(nodetypes_out, "Insufficient memory for asymmetric preference game."); - IGRAPH_FINALLY(igraph_free, &nodetypes_out); - IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_out, nodes); + 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_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_intype, no_in_types); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_outtype, no_out_types); + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, no_in_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype); + IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, no_out_types)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype); + for (i = 0; i < no_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 < no_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; k = 0; @@ -495,59 +524,49 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer RNG_BEGIN(); for (i = 0; i < nodes; i++) { - igraph_integer_t in_type, out_type; + long int in_type, out_type; igraph_real_t uni1 = RNG_UNIF(0, maxcum); igraph_vector_binsearch(&cumdist, uni1, &in_type); - out_type = (in_type - 1) % no_out_types; - in_type = (in_type - 1) / no_out_types; + out_type = (in_type - 1) % (long int) no_out_types; + in_type = (in_type - 1) / (long int) no_out_types; VECTOR(*nodetypes_in)[i] = in_type; VECTOR(*nodetypes_out)[i] = out_type; - IGRAPH_CHECK(igraph_vector_int_push_back( - igraph_vector_int_list_get_ptr(&vids_by_intype, in_type), i - )); - IGRAPH_CHECK(igraph_vector_int_push_back( - igraph_vector_int_list_get_ptr(&vids_by_outtype, out_type), i - )); + IGRAPH_CHECK(igraph_vector_push_back( + (igraph_vector_t*)VECTOR(vids_by_intype)[in_type], i)); + IGRAPH_CHECK(igraph_vector_push_back( + (igraph_vector_t*)VECTOR(vids_by_outtype)[out_type], i)); } igraph_vector_destroy(&cumdist); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&intersect, 0); + IGRAPH_VECTOR_INIT_FINALLY(&intersect, 0); for (i = 0; i < no_out_types; i++) { for (j = 0; j < no_in_types; j++) { - igraph_integer_t kk, l, l_x2; - igraph_integer_t c = 0; + long int kk, l, c = 0; igraph_real_t p, last; - igraph_vector_int_t *v1, *v2; - igraph_integer_t v1_size, v2_size; + igraph_vector_t *v1, *v2; + long int v1_size, v2_size; IGRAPH_ALLOW_INTERRUPTION(); - v1 = igraph_vector_int_list_get_ptr(&vids_by_outtype, i); - v2 = igraph_vector_int_list_get_ptr(&vids_by_intype, j); - v1_size = igraph_vector_int_size(v1); - v2_size = igraph_vector_int_size(v2); - - maxedges = ((igraph_real_t) v1_size) * v2_size; - - if (maxedges > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); - } + 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_int_intersect_sorted(v1, v2, &intersect)); - c = igraph_vector_int_size(&intersect); + 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_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); - IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); last = RNG_GEOM(p); while (last < maxedges) { @@ -557,19 +576,16 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer } l = igraph_vector_size(&s); - IGRAPH_SAFE_MULT(l, 2, &l_x2); - IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); - + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2)); if (!loops && c > 0) { for (kk = 0; kk < l; kk++) { - igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); - igraph_integer_t from = (VECTOR(s)[kk] - ((igraph_real_t) to) * v1_size); + 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_int_binsearch(&intersect, VECTOR(*v1)[from], &c); + igraph_vector_binsearch(&intersect, VECTOR(*v1)[from], &c); from = v1_size - 1; if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) { from--; @@ -581,15 +597,15 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer } } } - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + igraph_vector_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_push_back(&edges, VECTOR(*v2)[to]); } } else { for (kk = 0; kk < l; kk++) { - igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); - igraph_integer_t from = (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size); - igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); + 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]); } } } @@ -598,25 +614,25 @@ igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer RNG_END(); igraph_vector_destroy(&s); - igraph_vector_int_destroy(&intersect); - igraph_vector_int_list_destroy(&vids_by_intype); - igraph_vector_int_list_destroy(&vids_by_outtype); + 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_int_destroy(nodetypes_out); + igraph_vector_destroy(nodetypes_out); IGRAPH_FREE(nodetypes_out); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); } if (node_type_in_vec == 0) { - igraph_vector_int_destroy(nodetypes_in); + igraph_vector_destroy(nodetypes_in); IGRAPH_FREE(nodetypes_in); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, 1)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/recent_degree.c b/src/vendor/cigraph/src/games/recent_degree.c index 5564fcaaf0f..efd896c796e 100644 --- a/src/vendor/cigraph/src/games/recent_degree.c +++ b/src/vendor/cigraph/src/games/recent_degree.c @@ -29,8 +29,6 @@ #include "igraph_random.h" #include "igraph_interface.h" -#include "math/safe_intop.h" - /** * \function igraph_recent_degree_game * \brief Stochastic graph generator based on the number of incident edges a node has gained recently. @@ -63,32 +61,32 @@ * vertices, |E| is the number of edges in the graph. * */ -igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, +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_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t zero_appeal, igraph_bool_t directed) { - igraph_integer_t no_of_nodes = nodes; - igraph_integer_t no_of_neighbors = 0; - igraph_integer_t no_of_edges; - igraph_vector_int_t edges; - igraph_integer_t i, j; + 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; - igraph_integer_t edgeptr = 0; + long int edgeptr = 0; igraph_vector_t degree; - igraph_dqueue_int_t history; - igraph_bool_t have_outseq = outseq && igraph_vector_int_size(outseq) > 0; + 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 %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + IGRAPH_ERRORF("Number of vertices cannot be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); } - if (have_outseq && igraph_vector_int_size(outseq) != no_of_nodes) { - IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(outseq), 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 ".", @@ -108,40 +106,39 @@ igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes if (!have_outseq) { no_of_neighbors = m; - IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; } else { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); - no_of_edges -= VECTOR(*outseq)[0]; - } - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + 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_INT_INIT_FINALLY(&edges, no_of_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); - IGRAPH_CHECK(igraph_dqueue_int_init(&history, + IGRAPH_CHECK(igraph_dqueue_init(&history, 1.5 * time_window * no_of_edges / no_of_nodes + 10)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); + IGRAPH_FINALLY(igraph_dqueue_destroy, &history); RNG_BEGIN(); /* first node */ IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); - IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + igraph_dqueue_push(&history, -1); /* and the rest */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - igraph_integer_t to; + long int to; if (have_outseq) { - no_of_neighbors = VECTOR(*outseq)[i]; + no_of_neighbors = (long int) VECTOR(*outseq)[i]; } if (i >= time_window) { - while ((j = igraph_dqueue_int_pop(&history)) != -1) { + 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)); } @@ -159,13 +156,13 @@ igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes VECTOR(degree)[to]++; VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = to; - IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); + igraph_dqueue_push(&history, to); } - IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + igraph_dqueue_push(&history, -1); /* update probabilities */ for (j = 0; j < no_of_neighbors; j++) { - igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; + 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) { @@ -178,16 +175,16 @@ igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes RNG_END(); - igraph_dqueue_int_destroy(&history); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -230,10 +227,10 @@ igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes * Time complexity: O((|V|+|V|/aging_bins)*log(|V|)+|E|). |V| is the number * of vertices, |E| the number of edges. */ -igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, +int igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_int_t *outseq, + const igraph_vector_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -242,28 +239,28 @@ igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, igraph_real_t zero_appeal, igraph_bool_t directed) { - igraph_integer_t no_of_nodes = nodes; - igraph_integer_t no_of_neighbors; - igraph_integer_t binwidth; - igraph_integer_t no_of_edges; - igraph_vector_int_t edges; - igraph_integer_t i, j, k; + 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; - igraph_integer_t edgeptr = 0; + long int edgeptr = 0; igraph_vector_t degree; - igraph_dqueue_int_t history; - igraph_bool_t have_outseq = outseq && igraph_vector_int_size(outseq) > 0; + 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 %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_nodes); + IGRAPH_ERRORF("Number of nodes should not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); } - if (have_outseq && igraph_vector_int_size(outseq) != no_of_nodes) { - IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(outseq), 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); @@ -280,44 +277,43 @@ igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, if (!have_outseq) { no_of_neighbors = m; - IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + no_of_edges = (no_of_nodes - 1) * no_of_neighbors; } else { - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); - no_of_edges -= VECTOR(*outseq)[0]; - } - /* To ensure the size of the edges vector will not overflow. */ - if (no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + 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_INT_INIT_FINALLY(&edges, no_of_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); - IGRAPH_CHECK(igraph_dqueue_int_init(&history, - 1.5 * time_window * no_of_edges / no_of_nodes + 10)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); + 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_CHECK(igraph_dqueue_int_push(&history, -1)); + igraph_dqueue_push(&history, -1); /* and the rest */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - igraph_integer_t to; + long int to; if (have_outseq) { - no_of_neighbors = VECTOR(*outseq)[i]; + no_of_neighbors = (long int) VECTOR(*outseq)[i]; } if (i >= time_window) { - while ((j = igraph_dqueue_int_pop(&history)) != -1) { - igraph_integer_t age = (i - j) / binwidth; + 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, @@ -338,14 +334,14 @@ igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, VECTOR(degree)[to]++; VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = to; - IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); + igraph_dqueue_push(&history, to); } - IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); + igraph_dqueue_push(&history, -1); /* update probabilities */ for (j = 0; j < no_of_neighbors; j++) { - igraph_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; - igraph_integer_t age = (i - n) / binwidth; + 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) @@ -363,9 +359,9 @@ igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, /* aging */ for (k = 1; binwidth * k <= i; k++) { - igraph_integer_t shnode = i - binwidth * k; - igraph_integer_t deg = VECTOR(degree)[shnode]; - igraph_integer_t age = (i - shnode) / binwidth; + 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) @@ -375,14 +371,14 @@ igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, RNG_END(); - igraph_dqueue_int_destroy(&history); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/games/sbm.c b/src/vendor/cigraph/src/games/sbm.c index 25e27dad3bf..b2d75e88b4a 100644 --- a/src/vendor/cigraph/src/games/sbm.c +++ b/src/vendor/cigraph/src/games/sbm.c @@ -22,18 +22,17 @@ */ -#include "igraph_games.h" - -#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_random.h" -#include "igraph_vector.h" +#include "igraph_constructors.h" +#include "igraph_games.h" #include "core/interruption.h" -#include "math/safe_intop.h" #include /* for DBL_EPSILON */ -#include /* for sqrt and floor */ +#include /* for sqrt */ /** * \function igraph_sbm_game @@ -47,7 +46,7 @@ * Interpretation and evaluation. Social Networks, 14, 5-–61. * * - * The order of the vertex IDs in the generated graph corresponds to + * 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 @@ -72,20 +71,15 @@ * */ -igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, +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) { -#define IGRAPH_CHECK_MAXEDGES() \ - do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ - }} while (0) - - igraph_integer_t no_blocks = igraph_matrix_nrow(pref_matrix); - igraph_integer_t from, to, fromoff = 0; + long int no_blocks = igraph_matrix_nrow(pref_matrix); + long int from, to, fromoff = 0; igraph_real_t minp, maxp; - igraph_vector_int_t edges; + igraph_vector_t edges; /* ------------------------------------------------------------ */ /* Check arguments */ @@ -109,8 +103,8 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, } if (igraph_vector_int_size(block_sizes) != no_blocks) { - IGRAPH_ERRORF("Block size vector length (%" IGRAPH_PRId ") does not agree with " - "preference matrix size (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + 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); } @@ -131,14 +125,14 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * guaranteed to be non-negative. This shouldn't be checked separately. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); RNG_BEGIN(); for (from = 0; from < no_blocks; from++) { - igraph_integer_t fromsize = VECTOR(*block_sizes)[from]; - igraph_integer_t start = directed ? 0 : from; - igraph_integer_t i, tooff = 0; + double fromsize = VECTOR(*block_sizes)[from]; + long int start = directed ? 0 : from; + long int i, tooff = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -146,89 +140,79 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, tooff += VECTOR(*block_sizes)[i]; } for (to = start; to < no_blocks; to++) { - igraph_integer_t tosize = VECTOR(*block_sizes)[to]; + double tosize = VECTOR(*block_sizes)[to]; igraph_real_t prob = MATRIX(*pref_matrix, from, to); - igraph_real_t maxedges; - igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ - igraph_integer_t vfrom, vto; - + double maxedges, last = RNG_GEOM(prob); if (directed && loops) { - maxedges = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * tosize; while (last < maxedges) { - vto = floor(last / fromsize); - vfrom = last - ((igraph_real_t) vto) * fromsize; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * tosize; while (last < maxedges) { - vto = floor(last / fromsize); - vfrom = last - ((igraph_real_t) vto) * fromsize; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * (fromsize - 1.0); - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * (fromsize - 1); while (last < maxedges) { - vto = floor(last / fromsize); - vfrom = last - ((igraph_real_t) vto) * fromsize; + long int vto = floor(last / fromsize); + long int vfrom = last - (igraph_real_t)vto * fromsize; if (vfrom == vto) { vto = fromsize - 1; } - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * tosize; while (last < maxedges) { - vto = floor(last / fromsize); - vfrom = last - ((igraph_real_t) vto) * fromsize; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * (fromsize + 1.0) / 2.0; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * (fromsize + 1) / 2.0; while (last < maxedges) { - vto = floor((sqrt(8 * last + 1) - 1) / 2); - vfrom = last - (((igraph_real_t) vto) * (vto + 1.0)) / 2.0; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * tosize; while (last < maxedges) { - vto = floor(last / fromsize); - vfrom = last - ((igraph_real_t) vto) * fromsize; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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 = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * (fromsize - 1) / 2.0; while (last < maxedges) { - vto = floor((sqrt(8 * last + 1) + 1) / 2); - vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; - igraph_vector_int_push_back(&edges, fromoff + vfrom); - igraph_vector_int_push_back(&edges, tooff + vto); + 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; } @@ -241,13 +225,11 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - - igraph_vector_int_destroy(&edges); + igraph_create(graph, &edges, n, directed); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; -#undef IGRAPH_CHECK_MAXEDGES } /** @@ -265,7 +247,7 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * 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 - * \p rho vector. + * \code{rho} vector. * \param p The Bernoulli rate of connections between * vertices in different blocks. * \return Error code. @@ -274,20 +256,16 @@ igraph_error_t igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * \ref igraph_hsbm_list_game() for a more general version. */ -igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, +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) { -#define IGRAPH_CHECK_MAXEDGES() \ - do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ - }} while (0) - igraph_integer_t b, i, k = igraph_vector_size(rho); + int b, i, k = igraph_vector_size(rho); igraph_vector_t csizes; igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); - igraph_integer_t no_blocks = n / m; - igraph_vector_int_t edges; - igraph_integer_t offset = 0; + 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); @@ -295,7 +273,7 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, if (m < 1) { IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL); } - if (n % m) { + 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)) { @@ -330,45 +308,43 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); RNG_BEGIN(); /* Block models first */ for (b = 0; b < no_blocks; b++) { - igraph_integer_t from, to, fromoff = 0; + int from, to, fromoff = 0; for (from = 0; from < k; from++) { - igraph_integer_t fromsize = VECTOR(csizes)[from]; - igraph_integer_t i, tooff = 0; + 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++) { - igraph_integer_t tosize = VECTOR(csizes)[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); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + igraph_real_t last = RNG_GEOM(prob); if (from != to) { - maxedges = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * tosize; while (last < maxedges) { - igraph_integer_t vto = floor(last / fromsize); - igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + 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 = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; - IGRAPH_CHECK_MAXEDGES(); + maxedges = fromsize * (fromsize - 1) / 2.0; while (last < maxedges) { - igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); - igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + 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; } @@ -385,33 +361,32 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, /* And now the rest, if not a special case */ if (p == 1) { - igraph_integer_t fromoff = 0, tooff = m; + int fromoff = 0, tooff = m; for (b = 0; b < no_blocks; b++) { - igraph_integer_t fromsize = m; - igraph_integer_t tosize = n - tooff; - igraph_integer_t from, to; + 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_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); + igraph_vector_push_back(&edges, fromoff + from); + igraph_vector_push_back(&edges, tooff + to); } } fromoff += m; tooff += m; } } else if (p > 0) { - igraph_integer_t fromoff = 0, tooff = m; + int fromoff = 0, tooff = m; for (b = 0; b < no_blocks; b++) { - igraph_integer_t fromsize = m; - igraph_integer_t tosize = n - tooff; - igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; - IGRAPH_CHECK_MAXEDGES(); - igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + 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) { - igraph_integer_t vto = floor(last / fromsize); - igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + vto)); + 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; } @@ -423,14 +398,13 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - IGRAPH_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); + igraph_create(graph, &edges, n, /*directed=*/ 0); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&csizes); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; -#undef IGRAPH_CHECK_MAXEDGES + return 0; } /** @@ -446,7 +420,7 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, * \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, specifying the Bernoulli rates of connections + * 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. @@ -456,132 +430,130 @@ igraph_error_t igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, * \ref igraph_hsbm_game() for a simpler general version. */ -igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, +int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *mlist, - const igraph_vector_list_t *rholist, - const igraph_matrix_list_t *Clist, + const igraph_vector_ptr_t *rholist, + const igraph_vector_ptr_t *Clist, igraph_real_t p) { - igraph_integer_t i, no_blocks = igraph_vector_list_size(rholist); + int i, no_blocks = igraph_vector_ptr_size(rholist); igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); - igraph_vector_int_t edges; - igraph_vector_t csizes; - igraph_integer_t b, offset = 0; + igraph_vector_t csizes, edges; + int b, offset = 0; if (n < 1) { - IGRAPH_ERROR("`n' must be positive for HSBM.", IGRAPH_EINVAL); + IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL); } if (no_blocks == 0) { - IGRAPH_ERROR("`rholist' empty for HSBM.", IGRAPH_EINVAL); + IGRAPH_ERROR("`rholist' empty for HSBM", IGRAPH_EINVAL); } - if (igraph_matrix_list_size(Clist) != no_blocks && + 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); + "for HSBM", IGRAPH_EINVAL); } if (p < 0 || p > 1) { - IGRAPH_ERROR("`p' must be a probability for HSBM.", IGRAPH_EINVAL); + 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); + 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); + 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 = igraph_vector_list_get_ptr(rholist, 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_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); + 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 = igraph_matrix_list_get_ptr(Clist, i); + const igraph_matrix_t *C = VECTOR(*Clist)[i]; if (igraph_matrix_min(C) < 0 || igraph_matrix_max(C) > 1) { - IGRAPH_ERROR("Bernoulli rates must be between zero and one for HSBM.", + IGRAPH_ERROR("`C' must be between zero and one for HSBM", IGRAPH_EINVAL); } if (!igraph_matrix_is_symmetric(C)) { - IGRAPH_ERROR("Bernoulli rate matrices must be symmetric.", IGRAPH_EINVAL); + 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 = igraph_vector_list_get_ptr(rholist, i); - const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, i); - igraph_integer_t k = igraph_vector_size(rho); + 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("All Bernoulli rate matrix dimensions must match `rho' " - "dimensions in HSBM.", + 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 = igraph_vector_list_get_ptr(rholist, i); + const igraph_vector_t *rho = VECTOR(*rholist)[i]; igraph_real_t m = VECTOR(*mlist)[i]; - igraph_integer_t j, k = igraph_vector_size(rho); + 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_ERROR("`rho' * `m' is not integer in HSBM", IGRAPH_EINVAL); } } } IGRAPH_VECTOR_INIT_FINALLY(&csizes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); RNG_BEGIN(); /* Block models first */ for (b = 0; b < no_blocks; b++) { - igraph_integer_t from, to, fromoff = 0; - const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, b); - const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, b); - igraph_integer_t m = VECTOR(*mlist)[b]; - igraph_integer_t k = igraph_vector_size(rho); + 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_CHECK(igraph_vector_resize(&csizes, k)); + 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++) { - igraph_integer_t fromsize = VECTOR(csizes)[from]; - igraph_integer_t i, tooff = 0; + 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++) { - igraph_integer_t tosize = VECTOR(csizes)[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); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + igraph_real_t last = RNG_GEOM(prob); if (from != to) { - maxedges = ((igraph_real_t) fromsize) * tosize; + maxedges = fromsize * tosize; while (last < maxedges) { - igraph_integer_t vto = floor(last / fromsize); - igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + 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 = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + maxedges = fromsize * (fromsize - 1) / 2.0; while (last < maxedges) { - igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); - igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); + 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; } @@ -598,15 +570,15 @@ igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, /* And now the rest, if not a special case */ if (p == 1) { - igraph_integer_t fromoff = 0, tooff = VECTOR(*mlist)[0]; + int fromoff = 0, tooff = VECTOR(*mlist)[0]; for (b = 0; b < no_blocks; b++) { - igraph_integer_t fromsize = VECTOR(*mlist)[b]; - igraph_integer_t tosize = n - tooff; - igraph_integer_t from, to; + 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_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); + igraph_vector_push_back(&edges, fromoff + from); + igraph_vector_push_back(&edges, tooff + to); } } fromoff += fromsize; @@ -615,17 +587,17 @@ igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, } } } else if (p > 0) { - igraph_integer_t fromoff = 0, tooff = VECTOR(*mlist)[0]; + int fromoff = 0, tooff = VECTOR(*mlist)[0]; for (b = 0; b < no_blocks; b++) { - igraph_integer_t fromsize = VECTOR(*mlist)[b]; - igraph_integer_t tosize = n - tooff; - igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; - igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + 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) { - igraph_integer_t vto = floor(last / fromsize); - igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + vto)); + 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; } @@ -639,11 +611,11 @@ igraph_error_t igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - IGRAPH_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); + igraph_create(graph, &edges, n, /*directed=*/ 0); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&csizes); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/games/static_fitness.c b/src/vendor/cigraph/src/games/static_fitness.c index 3df0aa6d799..8a7d4c05fe2 100644 --- a/src/vendor/cigraph/src/games/static_fitness.c +++ b/src/vendor/cigraph/src/games/static_fitness.c @@ -31,7 +31,6 @@ #include "igraph_random.h" #include "core/interruption.h" -#include "core/math.h" /* M_SQRT2 */ /** * \ingroup generators @@ -68,12 +67,8 @@ * 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 - * https://doi.org/10.1103/PhysRevLett.87.278701. + * 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. @@ -93,10 +88,10 @@ * * Time complexity: O(|V| + |E| log |E|). */ -igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, +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_int_t edges = IGRAPH_VECTOR_NULL; + 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; @@ -104,17 +99,19 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o igraph_real_t x, max_in, max_out; igraph_real_t max_no_of_edges; igraph_bool_t is_directed = (fitness_in != 0); - igraph_real_t num_steps; + float num_steps; igraph_integer_t step_counter = 0; - igraph_integer_t i, from, to, pos; + long int i, from, to, pos; - IGRAPH_ASSERT(fitness_out != NULL); + 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_vector_size(fitness_out); + 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; @@ -184,8 +181,8 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o if (multiple) { /* Generating when multiple edges are allowed */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * no_of_edges)); + 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 */ @@ -204,8 +201,8 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o continue; } - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); no_of_edges--; } @@ -214,7 +211,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed)); /* Clear the edge list */ - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } else { /* Multiple edges are disallowed */ @@ -340,7 +337,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o * 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 Erdős-Rényi random network. + * 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 @@ -358,7 +355,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o * * Time complexity: O(|V| + |E| log |E|). */ -igraph_error_t igraph_static_power_law_game(igraph_t *graph, +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, @@ -366,7 +363,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, igraph_vector_t fitness_out, fitness_in; igraph_real_t alpha_out = 0.0, alpha_in = 0.0; - igraph_integer_t i; + long int i; igraph_real_t j; if (no_of_nodes < 0) { @@ -376,7 +373,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, /* Calculate alpha_out */ if (exponent_out < 2) { IGRAPH_ERRORF("Out-degree exponent must be >= 2, got %g.", IGRAPH_EINVAL, exponent_out); - } else if (isfinite(exponent_out)) { + } else if (igraph_finite(exponent_out)) { alpha_out = -1.0 / (exponent_out - 1); } else { alpha_out = 0.0; @@ -388,7 +385,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, 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 * M_SQRT2 * (1 + alpha_out), -1.0 / alpha_out) - 1; + pow(10 * sqrt(2) * (1 + alpha_out), -1.0 / alpha_out) - 1; } if (j < no_of_nodes) { j = no_of_nodes; @@ -401,7 +398,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, if (exponent_in < 2) { IGRAPH_ERRORF("For directed graphs the in-degree exponent must be >= 2, got %g.", IGRAPH_EINVAL, exponent_in); - } else if (isfinite(exponent_in)) { + } else if (igraph_finite(exponent_in)) { alpha_in = -1.0 / (exponent_in - 1); } else { alpha_in = 0.0; @@ -412,7 +409,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, 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 * M_SQRT2 * (1 + alpha_in), -1.0 / alpha_in) - 1; + pow(10 * sqrt(2) * (1 + alpha_in), -1.0 / alpha_in) - 1; } if (j < no_of_nodes) { j = no_of_nodes; diff --git a/src/vendor/cigraph/src/games/tree.c b/src/vendor/cigraph/src/games/tree.c index 1f2f422a17e..805ad5313b4 100644 --- a/src/vendor/cigraph/src/games/tree.c +++ b/src/vendor/cigraph/src/games/tree.c @@ -27,17 +27,15 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "math/safe_intop.h" - /* Uniform sampling of labelled trees (igraph_tree_game) */ /* The following implementation uniformly samples Prufer trees and converts * them to trees. */ -static igraph_error_t igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { +static int igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { igraph_vector_int_t prufer; - igraph_integer_t i; + long i; if (directed) { IGRAPH_ERROR("The Prufer method for random tree generation does not support directed trees", IGRAPH_EINVAL); @@ -76,22 +74,19 @@ static igraph_error_t igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_ VECTOR(vec)[j] = temp; \ } -static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { - igraph_vector_int_t edges; +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; - igraph_integer_t i, j, k; - igraph_integer_t no_edges; - - IGRAPH_SAFE_MULT(n - 1, 2, &no_edges); + long i, j, k; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges); + 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_range(&vertices, 0, n)); + IGRAPH_CHECK(igraph_vector_int_init_seq(&vertices, 0, n - 1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &vertices); RNG_BEGIN(); @@ -143,7 +138,7 @@ static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph igraph_vector_int_destroy(&vertices); igraph_vector_bool_destroy(&visited); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -184,7 +179,7 @@ static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph * */ -igraph_error_t igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method) { +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; diff --git a/src/vendor/cigraph/src/games/watts_strogatz.c b/src/vendor/cigraph/src/games/watts_strogatz.c index 184286b9df2..1a6e730fe86 100644 --- a/src/vendor/cigraph/src/games/watts_strogatz.c +++ b/src/vendor/cigraph/src/games/watts_strogatz.c @@ -43,7 +43,8 @@ * \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(). + * 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. @@ -51,7 +52,7 @@ * generated graph. * \return Error code. * - * \sa \ref igraph_square_lattice(), \ref igraph_connect_neighborhood() and + * \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. * @@ -59,13 +60,13 @@ * vertices and edges, d is the average degree, o is the \p nei * argument. */ -igraph_error_t igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, +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_int_t dimvector; - igraph_vector_bool_t periodic; + igraph_vector_t dimvector; + long int i; if (dim < 1) { IGRAPH_ERROR("WS game: dimension should be at least one", IGRAPH_EINVAL); @@ -81,18 +82,15 @@ igraph_error_t igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, /* Create the lattice first */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&dimvector, dim); - igraph_vector_int_fill(&dimvector, size); - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, dim); - igraph_vector_bool_fill(&periodic, true); - - IGRAPH_CHECK(igraph_square_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED, - /* mutual */ false, &periodic)); + IGRAPH_VECTOR_INIT_FINALLY(&dimvector, dim); + for (i = 0; i < dim; i++) { + VECTOR(dimvector)[i] = size; + } - igraph_vector_bool_destroy(&periodic); - igraph_vector_int_destroy(&dimvector); - IGRAPH_FINALLY_CLEAN(2); + 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 */ @@ -100,5 +98,5 @@ igraph_error_t igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, IGRAPH_CHECK(igraph_rewire_edges(graph, p, loops, multiple)); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/graph/adjlist.c b/src/vendor/cigraph/src/graph/adjlist.c index c1ad5a278aa..e3da88d09fd 100644 --- a/src/vendor/cigraph/src/graph/adjlist.c +++ b/src/vendor/cigraph/src/graph/adjlist.c @@ -33,34 +33,17 @@ /** * Helper function that simplifies a sorted adjacency vector by removing * duplicate elements and optionally self-loops. - * - * has_loops and has_multiple are pointers to booleans that will be updated - * to \c true if the function \em finds a loop or a multiple edge. These values will - * \em never be set back to zero by this function. The usage pattern for these - * arguments is that the caller should set them to zero, followed by one or - * multiple calls to this function; at the end of such a sequence the booleans - * will contain whether the function found at least one loop or multiple edge - * in the set of vertices that were investigated. - * - * Note the usage of the word "found" -- it might be the case that the - * function is not interested in loop or multiple edges due to how it is - * parameterized; in this case, we don't spend extra time in investigating the - * existence of loop or multiple edges, so the values of the has_loops and - * has_multiple arguments will stay as is. Therefore, upon exiting the - * function, finding \c false in one of these variables does \em not mean that - * there is no loop or multiple edge, only that the function hasn't found one. */ -static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( +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, igraph_bool_t *has_loops, - igraph_bool_t *has_multiple + 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 igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( +static int igraph_i_remove_loops_from_incidence_vector_in_place( igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops ); @@ -116,121 +99,77 @@ static igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( * adjacency list contains the state of the graph at the time of its * initialization. * - * - * This function returns each neighbor list in sorted order, just - * like \ref igraph_neighbors(). - * - * - * As of igraph 0.10, there is a small performance cost to setting \p loops - * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a - * different value from \c IGRAPH_MULTIPLE. - * * \param graph The input graph. * \param al Pointer to an uninitialized igraph_adjlist_t object. - * \param mode Constant specifying whether to include only outgoing - * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), - * or both (\c IGRAPH_ALL) types of neighbors - * in the adjacency list. It is ignored for undirected graphs. - * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS - * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE + * \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. \c IGRAPH_LOOPS_TWICE makes loop edges + * 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 \p mode is set to - * \c IGRAPH_ALL. + * but only if the graph is undirected or mode is set to + * IGRAPH_ALL. * \param multiple Specifies how to treat multiple (parallel) edges. - * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; - * \c IGRAPH_MULTIPLE keeps the multiplicities of 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. * - * \sa \ref igraph_neighbors() for getting the neighbor lists of individual - * vertices. - * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ -igraph_error_t igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, +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 no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degrees; + 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_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; } - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - /* igraph_degree() is fast when loops=true */ - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ true)); - - al->length = no_of_nodes; + al->length = igraph_vcount(graph); al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); - IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating adjacency list view."); - IGRAPH_FINALLY(igraph_adjlist_destroy, al); - - /* if we already know there are no multi-edges, they don't need to be removed */ - if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { - multiple = IGRAPH_MULTIPLE; + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create adjacency list view", IGRAPH_ENOMEM); } - /* if we already know there are no loops, they don't need to be removed */ - if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { - if (mode == IGRAPH_ALL) { - loops = IGRAPH_LOOPS_TWICE; - } else { - loops = IGRAPH_LOOPS_ONCE; - } - } + IGRAPH_FINALLY(igraph_adjlist_destroy, al); - igraph_bool_t has_loops = false; - igraph_bool_t has_multiple = false; - for (igraph_integer_t i = 0; i < al->length; i++) { + for (i = 0; i < al->length; i++) { IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], VECTOR(degrees)[i])); - IGRAPH_CHECK(igraph_neighbors(graph, &al->adjs[i], i, mode)); + 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]; + } - /* Attention: This function will only set values for has_loops and has_multiple - * if it finds loops/multi-edges. Otherwise they are left at their original value. */ IGRAPH_CHECK(igraph_i_simplify_sorted_int_adjacency_vector_in_place( - &al->adjs[i], i, mode, loops, multiple, &has_loops, &has_multiple + &al->adjs[i], i, mode, loops, multiple )); } - if (has_loops) { - /* If we have found at least one loop above, set the cache to true */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, true); - } else if (loops == IGRAPH_NO_LOOPS) { - /* If we explicitly _checked_ for loops (to remove them) and haven't - * found one, set the cache to false. This is the only case when a - * definite "no" from has_loops really means that there are no loops at - * all */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, false); - } - if (has_multiple) { - /* If we have found at least one multiedge above, set the cache to true */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, true); - } else if (multiple == IGRAPH_NO_MULTIPLE) { - /* If we explicitly _checked_ for multi-edges (to remove them) and - * haven't found one, set the cache to false. This is the only case - * when a definite "no" from has_multiple really means that there are - * no multi-edges at all all */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, false); - } - igraph_vector_int_destroy(°rees); - IGRAPH_FINALLY_CLEAN(2); /* + igraph_adjlist_destroy */ + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -240,27 +179,28 @@ igraph_error_t igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, * 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. */ -igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) { +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); - IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating adjlist."); - IGRAPH_FINALLY(igraph_adjlist_destroy, al); + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create adjlist view", IGRAPH_ENOMEM); + } - for (igraph_integer_t i = 0; i < al->length; i++) { + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -271,34 +211,28 @@ igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t * 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. - * - * - * This function returns each neighbor list in sorted order. - * * \param graph The input graph. * \param al Pointer to a not yet initialized adjacency list. * \param mode Constant specifying whether outgoing - * (\c IGRAPH_OUT), incoming (\c IGRAPH_IN), - * or both (\c IGRAPH_ALL) types of neighbors (in the + * (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. * - * \sa \ref igraph_adjlist_init(), \ref igraph_complementer() - * * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices. */ -igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, +int igraph_adjlist_init_complementer(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_bool_t loops) { - - igraph_vector_bool_t seen; - igraph_vector_int_t neis; + 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("Invalid neighbor mode specified for complementer adjlist view.", IGRAPH_EINVMODE); + IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { @@ -307,114 +241,48 @@ igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, al->length = igraph_vcount(graph); al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); - IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating complementer adjlist view."); + if (al->adjs == 0) { + IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_adjlist_destroy, al); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen, al->length); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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); - for (igraph_integer_t i = 0; i < al->length; i++) { - /* For each vertex, we mark neighbors within the 'seen' bool vector. - * Then we iterate over 'seen' and record non-marked vertices in - * the adjacency list. */ + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + for (i = 0; i < al->length; i++) { IGRAPH_ALLOW_INTERRUPTION(); - - /* Reset neighbor counter and 'seen' vector. */ - igraph_vector_bool_null(&seen); - igraph_integer_t n = al->length; - - IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); - + igraph_neighbors(graph, &vec, i, mode); + memset(seen, 0, sizeof(igraph_bool_t) * (unsigned) al->length); + n = al->length; if (!loops) { - VECTOR(seen)[i] = true; + seen[i] = 1; n--; } - - igraph_integer_t neis_size = igraph_vector_int_size(&neis); - for (igraph_integer_t j = 0; j < neis_size; j++) { - if (! VECTOR(seen)[ VECTOR(neis)[j] ] ) { + for (j = 0; j < igraph_vector_size(&vec); j++) { + if (! seen [ (long int) VECTOR(vec)[j] ] ) { n--; - VECTOR(seen)[ VECTOR(neis)[j] ] = true; + seen[ (long int) VECTOR(vec)[j] ] = 1; } } - - /* Produce "non-neighbor" list in sorted order. */ IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); - for (igraph_integer_t j = 0, k = 0; k < n; j++) { - if (!VECTOR(seen)[j]) { + for (j = 0, k = 0; k < n; j++) { + if (!seen[j]) { VECTOR(al->adjs[i])[k++] = j; } } } - igraph_vector_bool_destroy(&seen); - igraph_vector_int_destroy(&neis); - IGRAPH_FINALLY_CLEAN(3); /* +1 for the adjlist itself */ - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_adjlist_init_from_inclist - * \brief Constructs an adjacency list of vertices from an incidence list. - * - * In some algorithms it is useful to have an adjacency list \em and an incidence - * list representation of the same graph, and in many cases it is the most useful - * if they are consistent with each other, i.e. if can be guaranteed that the - * vertex ID in the i-th entry of the adjacency list of vertex v is the - * \em other endpoint of the edge in the i-th entry of the incidence list of the - * same vertex. This function creates such an adjacency list from the corresponding - * incidence list by looking up the endpoints of each edge in the incidence - * list and constructing the corresponding adjacenecy vectors. - * - * - * The adjacency list is independent of the graph or the incidence list after - * creation; in other words, modifications that are made to the graph or the - * incidence list are not reflected in the adjacency list. - * - * \param graph The input graph. - * \param al Pointer to an uninitialized igraph_adjlist_t object. - * \param il Pointer to an \em initialized igraph_inclist_t object - * that will be converted into an adjacency list. - * \return Error code. - * - * Time complexity: O(|V|+|E|), linear in the number of vertices and - * edges. - */ - -igraph_error_t igraph_adjlist_init_from_inclist( - const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j, num_neis; - - igraph_vector_int_t *neis; - igraph_vector_int_t *incs; - - if (igraph_inclist_size(il) != no_of_nodes) { - IGRAPH_ERRORF( - "Incidence list has %" IGRAPH_PRId " entries but the graph has %" IGRAPH_PRId " vertices.", - IGRAPH_EINVAL, - igraph_inclist_size(il), - no_of_nodes - ); - } - - IGRAPH_CHECK(igraph_adjlist_init_empty(al, no_of_nodes)); - for (i = 0; i < no_of_nodes; i++) { - neis = igraph_adjlist_get(al, i); - incs = igraph_inclist_get(il, i); - - num_neis = igraph_vector_int_size(incs); - IGRAPH_CHECK(igraph_vector_int_resize(neis, num_neis)); - - for (j = 0; j < num_neis; j++) { - VECTOR(*neis)[j] = IGRAPH_OTHER(graph, VECTOR(*incs)[j], i); - } - } - - return IGRAPH_SUCCESS; + IGRAPH_FREE(seen); + igraph_vector_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + return 0; } /** @@ -427,10 +295,8 @@ igraph_error_t igraph_adjlist_init_from_inclist( * Time complexity: depends on memory management. */ void igraph_adjlist_destroy(igraph_adjlist_t *al) { - igraph_integer_t i; + long int i; for (i = 0; i < al->length; i++) { - /* This works if some igraph_vector_int_t's contain NULL, - because igraph_vector_int_destroy can handle this. */ igraph_vector_int_destroy(&al->adjs[i]); } IGRAPH_FREE(al->adjs); @@ -445,7 +311,7 @@ void igraph_adjlist_destroy(igraph_adjlist_t *al) { * the total number of elements in the adjacency list. */ void igraph_adjlist_clear(igraph_adjlist_t *al) { - igraph_integer_t i; + long int i; for (i = 0; i < al->length; i++) { igraph_vector_int_clear(&al->adjs[i]); } @@ -468,19 +334,14 @@ igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) { * \function igraph_adjlist_sort * \brief Sorts each vector in an adjacency list. * - * Sorts every vector of the adjacency list. Note that - * \ref igraph_adjlist_init() already produces sorted neighbor lists. - * This function is useful when the adjacency list is produced in - * a different manner, or is modified in a way that does not preserve - * the sorted order. - * + * 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) { - igraph_integer_t i; + long int i; for (i = 0; i < al->length; i++) { igraph_vector_int_sort(&al->adjs[i]); } @@ -492,28 +353,24 @@ void igraph_adjlist_sort(igraph_adjlist_t *al) { * * Simplifies an adjacency list, i.e. removes loop and multiple edges. * - * - * When the adjacency list is created with \ref igraph_adjlist_init(), - * use the \c loops and \c multiple parameters of that function instead. - * * \param al The adjacency list. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of edges and * vertices. */ -igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al) { - igraph_integer_t i; - igraph_integer_t n = al->length; +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_FINALLY(&mark, n); + 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]; - igraph_integer_t j, l = igraph_vector_int_size(v); + long int j, l = igraph_vector_int_size(v); VECTOR(mark)[i] = i + 1; for (j = 0; j < l; /* nothing */) { - igraph_integer_t e = VECTOR(*v)[j]; + long int e = (long int) VECTOR(*v)[j]; if (VECTOR(mark)[e] != i + 1) { VECTOR(mark)[e] = i + 1; j++; @@ -527,40 +384,74 @@ igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al) { igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + 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 -igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al) { - igraph_integer_t i; - igraph_integer_t n = al->length; +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 IGRAPH_SUCCESS; + return 0; } #endif -igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { - igraph_integer_t i; - igraph_integer_t n = al->length; +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 IGRAPH_SUCCESS; + return 0; } #define ADJLIST_CANON_EDGE(from, to, directed) \ do { \ igraph_integer_t temp; \ - if ((!directed) && from < to) { \ + if((!directed) && from < to) { \ temp = to; \ to = from; \ from = temp; \ } \ - } while (0); + } 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; @@ -570,52 +461,40 @@ igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t fro } -igraph_error_t igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t oldto, igraph_integer_t newto, igraph_bool_t directed) { +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; - igraph_bool_t found_old, found_new; - igraph_integer_t oldpos, newpos; + 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. */ - found_old = igraph_vector_int_binsearch(oldfromvec, oldto, &oldpos); - if (! found_old) { - IGRAPH_ERROR("Edge to replace does not exist.", IGRAPH_EINVAL); - } - found_new = igraph_vector_int_binsearch(newfromvec, newto, &newpos); - if (found_new) { - IGRAPH_ERROR("New edge already exists.", IGRAPH_EINVAL); + if ((!err1) || err2) { + return 1; } - if (oldfromvec != newfromvec) { - /* grow the new vector first and then remove the item from the old one - * to ensure that we don't end up in a situation where the removal - * succeeds but the addition does not */ - IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); - igraph_vector_int_remove(oldfromvec, oldpos); - } else { - /* moving item within the same vector; here we can safely remove first - * and insert afterwards because there is no need to re-allocate memory */ - igraph_vector_int_remove(oldfromvec, oldpos); - if (oldpos < newpos) { - --newpos; - } - IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); + igraph_vector_int_remove(oldfromvec, oldpos); + if (oldfromvec == newfromvec && oldpos < newpos) { + --newpos; } + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( +static int igraph_i_remove_loops_from_incidence_vector_in_place( igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops ) { - igraph_integer_t i, length, eid, write_ptr; + 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 @@ -682,26 +561,49 @@ static igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( 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 -igraph_error_t igraph_inclist_print(const igraph_inclist_t *al) { - igraph_integer_t i; - igraph_integer_t n = al->length; +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 IGRAPH_SUCCESS; + return 0; } #endif -igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { - igraph_integer_t i; - igraph_integer_t n = al->length; +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 IGRAPH_SUCCESS; + return 0; } /** @@ -723,10 +625,6 @@ igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) * argument to control whether this will be the case (\c IGRAPH_LOOPS_TWICE ) * or not (\c IGRAPH_LOOPS_ONCE or \c IGRAPH_NO_LOOPS). * - * - * As of igraph 0.10, there is a small performance cost to setting \p loops - * to a different value than \c IGRAPH_LOOPS_TWICE. - * * \param graph The input graph. * \param il Pointer to an uninitialized incidence list. * \param mode Constant specifying whether incoming edges @@ -745,37 +643,44 @@ igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ -igraph_error_t igraph_inclist_init(const igraph_t *graph, +int igraph_inclist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degrees; + 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_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; } - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - /* igraph_degrees() is fast when loops=true */ - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ 1)); - - il->length = no_of_nodes; + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_inclist_destroy, il); - for (igraph_integer_t i = 0; i < il->length; i++) { + for (i = 0; i < il->length; i++) { + int j, n; + IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], VECTOR(degrees)[i])); - IGRAPH_CHECK(igraph_incident(graph, &il->incs[i], i, mode)); + 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( @@ -784,10 +689,9 @@ igraph_error_t igraph_inclist_init(const igraph_t *graph, } } - igraph_vector_int_destroy(°rees); - IGRAPH_FINALLY_CLEAN(2); /* + igraph_inclist_destroy */ - - return IGRAPH_SUCCESS; + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + return 0; } /** @@ -805,13 +709,13 @@ igraph_error_t igraph_inclist_init(const igraph_t *graph, * Time complexity: O(|V|), linear in the number of vertices. */ -igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { - igraph_integer_t i; +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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_inclist_destroy, il); @@ -820,7 +724,7 @@ igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t } IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -833,10 +737,10 @@ igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t */ void igraph_inclist_destroy(igraph_inclist_t *il) { - igraph_integer_t i; + long int i; for (i = 0; i < il->length; i++) { - /* This works if some igraph_vector_int_t's contain NULL, - because igraph_vector_int_destroy can handle this. */ + /* 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); @@ -852,7 +756,7 @@ void igraph_inclist_destroy(igraph_inclist_t *il) { * the total number of elements in the incidence list. */ void igraph_inclist_clear(igraph_inclist_t *il) { - igraph_integer_t i; + long int i; for (i = 0; i < il->length; i++) { igraph_vector_int_clear(&il->incs[i]); } @@ -871,22 +775,12 @@ igraph_integer_t igraph_inclist_size(const igraph_inclist_t *il) { return il->length; } -/* See the prototype above for a description of this function. */ -static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( +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, igraph_bool_t *has_loops, - igraph_bool_t *has_multiple - + igraph_loops_t loops, igraph_multiple_t multiple ) { - igraph_bool_t dummy1, dummy2; - if (has_loops == NULL) { - has_loops = &dummy1; - } - if (has_multiple == NULL) { - has_multiple = &dummy2; - } - igraph_integer_t i, p = 0; - igraph_integer_t n = igraph_vector_int_size(v); + long int i, p = 0; + long int n = igraph_vector_int_size(v); if ( multiple == IGRAPH_MULTIPLE && @@ -907,12 +801,6 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i])) { VECTOR(*v)[p] = VECTOR(*v)[i]; p++; - } else { - if (VECTOR(*v)[i] == index) { - *has_loops = true; - } else if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { - *has_multiple = true; - } } } } else { @@ -921,8 +809,6 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( if (VECTOR(*v)[i] != index) { VECTOR(*v)[p] = VECTOR(*v)[i]; p++; - } else { - *has_loops = true; } } } @@ -930,25 +816,11 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( 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 if ( - /* If this is not a loop then we have a multigraph. - Else we have at least two loops. - The v vector comes from a call to igraph_neighbors. - This will count loops twice if mode == IGRAPH_ALL. - So if mode != IGRAPH_ALL, - then we have a multigraph. - If mode == IGRAPH_ALL and we have three loops - then we also have a multigraph - */ - (VECTOR(*v)[i] != index) || - (mode != IGRAPH_ALL) || - (mode == IGRAPH_ALL && i < n - 2 && VECTOR(*v)[i + 2] == VECTOR(*v)[i]) - ){ - *has_multiple = true; } } } else { @@ -959,7 +831,6 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( for (i = 0; i < n; i++) { VECTOR(*v)[p] = VECTOR(*v)[i]; if (VECTOR(*v)[i] == index) { - *has_loops = true; /* 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) { @@ -972,12 +843,12 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( } 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 { - *has_multiple = 1; /* 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, @@ -1011,76 +882,41 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( /** * \function igraph_lazy_adjlist_init - * \brief Initializes a lazy adjacency list. + * \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. Neighbor lists will be returned - * in sorted order. - * - * - * As of igraph 0.10, there is a small performance cost to setting \p loops - * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a - * different value from \c IGRAPH_MULTIPLE. - * + * 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 specifying whether to include only outgoing - * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), - * or both (\c IGRAPH_ALL) types of neighbors - * in the adjacency list. It is ignored for undirected graphs. - * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS - * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE - * makes each loop edge appear only once in the adjacency list of the - * corresponding vertex. \c 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 \p mode is set to - * \c IGRAPH_ALL. - * \param multiple Specifies how to treat multiple (parallel) edges. - * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; - * \c 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. + * \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. * - * \sa \ref igraph_neighbors() for getting the neighbor lists of individual - * vertices. - * * Time complexity: O(|V|), the number of vertices, possibly, but * depends on the underlying memory management too. */ -igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, +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("Cannot create lazy adjacency list view.", IGRAPH_EINVMODE); + IGRAPH_ERROR("Cannor create lazy adjacency list view", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; } - /* if we already know there are no multi-edges, they don't need to be removed */ - if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { - multiple = IGRAPH_MULTIPLE; - } - - /* if we already know there are no loops, they don't need to be removed */ - if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { - if (mode == IGRAPH_ALL) { - loops = IGRAPH_LOOPS_TWICE; - } else { - loops = IGRAPH_LOOPS_ONCE; - } - } - al->mode = mode; al->loops = loops; al->multiple = multiple; @@ -1088,9 +924,17 @@ igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, al->length = igraph_vcount(graph); al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t*); - IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating lazy adjacency list view."); - return IGRAPH_SUCCESS; + 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; } /** @@ -1105,6 +949,7 @@ igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { igraph_lazy_adjlist_clear(al); + igraph_vector_destroy(&al->dummy); IGRAPH_FREE(al->adjs); } @@ -1117,7 +962,7 @@ void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { * the total number of elements in the adjacency list. */ void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al) { - igraph_integer_t i, n = al->length; + long int i, n = al->length; for (i = 0; i < n; i++) { if (al->adjs[i] != 0) { igraph_vector_int_destroy(al->adjs[i]); @@ -1139,36 +984,46 @@ 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 no) { - igraph_error_t ret; +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; + } - if (al->adjs[no] == NULL) { al->adjs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); - if (al->adjs[no] == NULL) { - return NULL; + if (al->adjs[no] == 0) { + igraph_error("Lazy adjlist failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; } - ret = igraph_vector_int_init(al->adjs[no], 0); - if (ret != IGRAPH_SUCCESS) { + n = igraph_vector_size(&al->dummy); + ret = igraph_vector_int_init(al->adjs[no], n); + if (ret != 0) { IGRAPH_FREE(al->adjs[no]); - return NULL; + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; } - ret = igraph_neighbors(al->graph, al->adjs[no], no, al->mode); - if (ret != IGRAPH_SUCCESS) { - igraph_vector_int_destroy(al->adjs[no]); - IGRAPH_FREE(al->adjs[no]); - return NULL; + 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, NULL, - NULL + al->adjs[no], no, al->mode, al->loops, al->multiple ); - if (ret != IGRAPH_SUCCESS) { + if (ret != 0) { igraph_vector_int_destroy(al->adjs[no]); IGRAPH_FREE(al->adjs[no]); - return NULL; + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; } } @@ -1191,10 +1046,6 @@ igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, i * 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. * - * - * As of igraph 0.10, there is a small performance cost to setting \p loops - * to a different value than \c IGRAPH_LOOPS_TWICE. - * * \param graph The input graph. * \param al Pointer to an uninitialized incidence list. * \param mode Constant, it gives whether incoming edges @@ -1215,7 +1066,7 @@ igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, i * also depends on the underlying memory management. */ -igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, +int igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops) { @@ -1235,10 +1086,15 @@ igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, 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); /* LCOV_EXCL_LINE */ + + IGRAPH_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM); } + IGRAPH_FINALLY(igraph_free, il->incs); - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_vector_init(&il->dummy, 0)); + IGRAPH_FINALLY_CLEAN(1); + + return 0; } @@ -1254,6 +1110,7 @@ igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { igraph_lazy_inclist_clear(il); + igraph_vector_destroy(&il->dummy); IGRAPH_FREE(il->incs); } @@ -1267,7 +1124,7 @@ void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { * the total number of elements in the incidence list. */ void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il) { - igraph_integer_t i, n = il->length; + long int i, n = il->length; for (i = 0; i < n; i++) { if (il->incs[i] != 0) { igraph_vector_int_destroy(il->incs[i]); @@ -1289,34 +1146,44 @@ 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 no) { - igraph_error_t ret; +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; + } - if (il->incs[no] == NULL) { il->incs[no] = IGRAPH_CALLOC(1, igraph_vector_int_t); - if (il->incs[no] == NULL) { - return NULL; + if (il->incs[no] == 0) { + igraph_error("Lazy incidence list query failed", IGRAPH_FILE_BASENAME, __LINE__, + IGRAPH_ENOMEM); + return 0; } - ret = igraph_vector_int_init(il->incs[no], 0); - if (ret != IGRAPH_SUCCESS) { + n = igraph_vector_size(&il->dummy); + ret = igraph_vector_int_init(il->incs[no], n); + if (ret != 0) { IGRAPH_FREE(il->incs[no]); - return NULL; + igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); + return 0; } - ret = igraph_incident(il->graph, il->incs[no], no, il->mode); - if (ret != IGRAPH_SUCCESS) { - igraph_vector_int_destroy(il->incs[no]); - IGRAPH_FREE(il->incs[no]); - return NULL; + 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 != IGRAPH_SUCCESS) { + if (ret != 0) { igraph_vector_int_destroy(il->incs[no]); IGRAPH_FREE(il->incs[no]); - return NULL; + return 0; } } } diff --git a/src/vendor/cigraph/src/graph/attributes.c b/src/vendor/cigraph/src/graph/attributes.c index 8c1226454bc..afb05208c17 100644 --- a/src/vendor/cigraph/src/graph/attributes.c +++ b/src/vendor/cigraph/src/graph/attributes.c @@ -25,21 +25,22 @@ #include "igraph_memory.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strdup */ + +#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 and #include "config.h". */ + * IGRAPH_THREAD_LOCAL to the following declaration */ igraph_attribute_table_t *igraph_i_attribute_table = 0; -igraph_error_t igraph_i_attribute_init(igraph_t *graph, void *attr) { +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 IGRAPH_SUCCESS; + return 0; } } @@ -49,105 +50,92 @@ void igraph_i_attribute_destroy(igraph_t *graph) { } } -igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph, +int igraph_i_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_t *idx) { - /* graph and newgraph may be the same, in which case we need to support - * in-place operations. If they are _not_ the same, it is assumed that the - * new graph has no vertex attributes yet */ + const igraph_vector_t *idx) { + if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx); } else { - return IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, +int igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { - /* It is assumed that the two graphs are not the same and that the new - * graph has no vertex attributes yet. We cannot assert the latter but we - * can assert the former */ - IGRAPH_ASSERT(graph != newgraph); if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_vertices(graph, newgraph, merges, comb); } else { - return IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_add_edges(igraph_t *graph, - const igraph_vector_int_t *edges, void *attr) { +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph, +int igraph_i_attribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_t *idx) { - /* graph and newgraph may be the same, in which case we need to support - * in-place operations. If they are _not_ the same, it is assumed that the - * new graph has no edge attributes yet */ + const igraph_vector_t *idx) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_edges(graph, newgraph, idx); } else { - return IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, +int igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb) { - /* It is assumed that the two graphs are not the same and that the new - * graph has no eedge attributes yet. We cannot assert the latter but we - * can assert the former */ - IGRAPH_ASSERT(graph != newgraph); if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_edges(graph, newgraph, merges, comb); } else { - return IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, +int igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_int_t *gtypes, + igraph_vector_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_int_t *vtypes, + igraph_vector_t *vtypes, igraph_strvector_t *enames, - igraph_vector_int_t *etypes) { + 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 IGRAPH_SUCCESS; + return 0; } } @@ -157,115 +145,115 @@ igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, if (igraph_i_attribute_table) { return igraph_i_attribute_table->has_attr(graph, type, name); } else { - return IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_gettype(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } -igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, +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 IGRAPH_SUCCESS; + return 0; } } @@ -278,12 +266,6 @@ igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, * igraph is compiled in thread-local mode. In the vast majority of cases, * this is not a significant restriction. * - * - * Attribute handlers are normally attached on program startup, and are - * left active for the program's lifetime. This is because a graph object - * created with a given attribute handler must not be manipulated while - * a different attribute handler is active. - * * \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. @@ -306,7 +288,7 @@ igraph_i_set_attribute_table(const igraph_attribute_table_t * table) { } igraph_bool_t igraph_has_attribute_table(void) { - return igraph_i_attribute_table != NULL; + return igraph_i_attribute_table != 0; } @@ -319,7 +301,7 @@ igraph_bool_t igraph_has_attribute_table(void) { * * Time complexity: O(1) */ -igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { +int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0)); return IGRAPH_SUCCESS; } @@ -334,7 +316,7 @@ igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t attribute combination list. */ void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) { - igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list); + 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) { @@ -357,23 +339,17 @@ void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) * \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. This function is called - * by the concrete attribute handler attached to igraph, and its - * calling signature depends completely on the attribute handler. - * For instance, if you are using attributes from C and you have - * attached the C attribute handler, you need to follow the - * documentation of the C attribute handler - * for more details. + * \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. * \return Error code. * * Time complexity: O(n), where n is the number of current attribute * combinations. */ -igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb, +int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t type, igraph_function_pointer_t func) { - igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list); + long int i, n = igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i = 0; i < n; i++) { @@ -391,26 +367,20 @@ igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t * /* 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); /* LCOV_EXCL_LINE */ + + if (!rec) { + IGRAPH_ERROR("Cannot create attribute combination data", + IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, rec); - if (! name) { + if (!name) { rec->name = NULL; } else { rec->name = strdup(name); - if (! rec->name) { - IGRAPH_ERROR("Cannot create attribute combination data.", - IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } } - IGRAPH_FINALLY(igraph_free, (char *) rec->name); /* free() is safe on NULL */ rec->type = type; rec->func = func; IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); - IGRAPH_FINALLY_CLEAN(2); /* ownership of 'rec' transferred to 'comb->list' */ } @@ -431,9 +401,9 @@ igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t * * Time complexity: O(n), where n is the number of records in the attribute combination list. */ -igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, +int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name) { - igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list); + long int i, n = igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i = 0; i < n; i++) { @@ -459,11 +429,11 @@ igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_ return IGRAPH_SUCCESS; } -igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, +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) { - igraph_integer_t i, def = -1, len = igraph_vector_ptr_size(&comb->list); + 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]; @@ -472,7 +442,7 @@ igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combina (name && n && !strcmp(n, name)) ) { *type = rec->type; *func = rec->func; - return IGRAPH_SUCCESS; + return 0; } if (!n) { def = i; @@ -489,41 +459,18 @@ igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combina *func = rec->func; } - return IGRAPH_SUCCESS; + return 0; } -/** - * \function igraph_attribute_combination - * \brief Initialize attribute combination list and add records. - * - * \param comb The uninitialized attribute combination list. - * \param ... A list of 'name, type[, func]', where: - * \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. - * The list is closed by setting the name to \c IGRAPH_NO_MORE_ATTRIBUTES. - * \return Error code. - * - * Time complexity: O(n^2), where n is the number attribute - * combinations records to add. - * - * \example examples/simple/igraph_attribute_combination.c - */ -igraph_error_t igraph_attribute_combination( - igraph_attribute_combination_t *comb, ...) { +int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { va_list ap; IGRAPH_CHECK(igraph_attribute_combination_init(comb)); va_start(ap, comb); - while (true) { - igraph_function_pointer_t func = NULL; + while (1) { + igraph_function_pointer_t func = 0; igraph_attribute_combination_type_t type; const char *name; @@ -533,7 +480,7 @@ igraph_error_t igraph_attribute_combination( break; } - type = (igraph_attribute_combination_type_t) va_arg(ap, int); + type = (igraph_attribute_combination_type_t)va_arg(ap, int); if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) { func = va_arg(ap, igraph_function_pointer_t); } @@ -542,14 +489,10 @@ igraph_error_t igraph_attribute_combination( name = 0; } - igraph_error_t ret = igraph_attribute_combination_add(comb, name, type, func); - if (ret != IGRAPH_SUCCESS) { - va_end(ap); - return ret; - } + IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func)); } va_end(ap); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/graph/attributes.h b/src/vendor/cigraph/src/graph/attributes.h index ef5838bae81..d0c8426f8f7 100644 --- a/src/vendor/cigraph/src/graph/attributes.h +++ b/src/vendor/cigraph/src/graph/attributes.h @@ -20,97 +20,93 @@ #define IGRAPH_GRAPH_ATTRIBUTES_H #include "igraph_attributes.h" -#include "igraph_decls.h" #include "igraph_strvector.h" #include "igraph_types.h" - -__BEGIN_DECLS +#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 { \ - igraph_error_t igraph_i_ret2=IGRAPH_SUCCESS; \ + 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 = NULL; \ + (to)->attr = 0; \ } \ - if (igraph_i_ret2 != IGRAPH_SUCCESS) { \ + if (igraph_i_ret2 != 0) { \ IGRAPH_ERROR("", igraph_i_ret2); \ } \ } while(0) -igraph_error_t igraph_i_attribute_init(igraph_t *graph, void *attr); +int igraph_i_attribute_init(igraph_t *graph, void *attr); void igraph_i_attribute_destroy(igraph_t *graph); -igraph_error_t igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, +int igraph_i_attribute_copy(igraph_t *to, const igraph_t *from, igraph_bool_t ga, igraph_bool_t va, igraph_bool_t ea); -igraph_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); -igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph, +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_int_t *idx); -igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, + const igraph_vector_t *idx); +int igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); -igraph_error_t igraph_i_attribute_add_edges(igraph_t *graph, - const igraph_vector_int_t *edges, void *attr); -igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph, +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_int_t *idx); -igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, + const igraph_vector_t *idx); +int igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, const igraph_attribute_combination_t *comb); -igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, +int igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_int_t *gtypes, + igraph_vector_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_int_t *vtypes, + igraph_vector_t *vtypes, igraph_strvector_t *enames, - igraph_vector_int_t *etypes); + igraph_vector_t *etypes); igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); -igraph_error_t igraph_i_attribute_gettype(const igraph_t *graph, +int igraph_i_attribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name); -igraph_error_t igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, +int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, const char *name, igraph_vector_t *value); -igraph_error_t igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, +int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_t *value); -igraph_error_t igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, +int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_t *value); -igraph_error_t igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, +int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, const char *name, igraph_strvector_t *value); -igraph_error_t igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, +int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_strvector_t *value); -igraph_error_t igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, +int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_strvector_t *value); -igraph_error_t igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, +int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, const char *name, igraph_vector_bool_t *value); -igraph_error_t igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, +int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, const char *name, igraph_vs_t vs, igraph_vector_bool_t *value); -igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, +int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value); -__END_DECLS - #endif /* IGRAPH_GRAPH_ATTRIBUTES_H */ diff --git a/src/vendor/cigraph/src/graph/basic_query.c b/src/vendor/cigraph/src/graph/basic_query.c index 3681131eb07..34c4ef48ae5 100644 --- a/src/vendor/cigraph/src/graph/basic_query.c +++ b/src/vendor/cigraph/src/graph/basic_query.c @@ -29,33 +29,34 @@ /** * \ingroup structural * \function igraph_are_connected - * \brief Decides whether two vertices are connected. - * - * This function is of course symmetric for undirected graphs. + * \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. + * \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. */ -igraph_error_t igraph_are_connected(const igraph_t *graph, +int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res) { - igraph_integer_t nov = igraph_vcount(graph); + long int nov = igraph_vcount(graph); igraph_integer_t eid = -1; if (v1 < 0 || v2 < 0 || v1 > nov - 1 || v2 > nov - 1) { - IGRAPH_ERROR("Invalid vertex ID when checking if two vertices are connected.", IGRAPH_EINVVID); + IGRAPH_ERROR("are connected", IGRAPH_EINVVID); } - igraph_get_eid(graph, &eid, v1, v2, IGRAPH_DIRECTED, /*error=*/ false); + igraph_get_eid(graph, &eid, v1, v2, /*directed=*/1, /*error=*/ 0); *res = (eid >= 0); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/graph/caching.c b/src/vendor/cigraph/src/graph/caching.c deleted file mode 100644 index fb0f52d387a..00000000000 --- a/src/vendor/cigraph/src/graph/caching.c +++ /dev/null @@ -1,185 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_interface.h" - -#include "graph/caching.h" - -#include - -/****** Strictly internal functions ******/ - -/** - * \brief Initializes a property cache, ensuring that all values are unknown. - */ -igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache) { - IGRAPH_STATIC_ASSERT(IGRAPH_PROP_I_SIZE <= 32); - - memset(cache->value, 0, sizeof(cache->value) / sizeof(cache->value[0])); - cache->known = 0; - return IGRAPH_SUCCESS; -} - -/** - * \brief Copies a property cache. - */ -igraph_error_t igraph_i_property_cache_copy( - igraph_i_property_cache_t *cache, - const igraph_i_property_cache_t *other_cache) { - *cache = *other_cache; - return IGRAPH_SUCCESS; -} - -/** - * \brief Destroys a property cache. - */ -void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache) { - IGRAPH_UNUSED(cache); - /* Nothing to do */ -} - -/***** Developer fuctions, exposed *****/ - -/** - * \brief Returns the value of a cached boolean property. - * - * This function provides valid results only when the property is already - * cached. Use \ref igraph_i_property_cache_has() to retrieve whether the - * property is cached. - * - * \param graph the graph whose cache is to be checked - * \param prop the property to retrieve from the cache - * \return the cached value of the property if the value is in the cache, or - * an undefined value otherwise - */ -igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop) { - IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); - assert(graph->cache != NULL); - return graph->cache->value[prop]; -} - -/** - * \brief Returns whether the cache contains a value for the given cached property. - * - * \param graph the graph whose cache is to be checked - * \param prop the property to check in the cache - */ -igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop) { - IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); - assert(graph->cache != NULL); - return graph->cache->known & (1 << prop); -} - -/** - * \brief Stores a property value in the cache. - * - * \param graph the graph whose cache is to be modified - * \param prop the property to update in the cache - * \param value the value of the property to add to the cache - */ -void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) { - IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); - assert(graph->cache != NULL); - /* Even though graph is const, updating the cache is not considered modification. - * Functions that merely compute graph properties, and thus leave the graph structure - * intact, will often update the cache. */ - graph->cache->value[prop] = value; - graph->cache->known |= (1 << prop); -} - -/** - * \brief Invalidates the cached value of a property in a graph. - * - * \param graph the graph whose cache is to be modified - * \param prop the property to invalidate in the cache - */ -void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop) { - IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); - assert(graph->cache != NULL); - graph->cache->known &= ~(1 << prop); -} - -/** - * \brief Invalidates all cached properties of the graph. - * - * This function is typically called after the graph is modified. - * - * \param graph the graph whose cache is to be invalidated - */ -void igraph_i_property_cache_invalidate_all(const igraph_t *graph) { - assert(graph->cache != NULL); - graph->cache->known = 0; -} - -/** - * \brief Invalidates all but a few cached properties of the graph, subject to specific conditions. - * - * This function is typically called after the graph is modified if we know that - * the modification does not affect certain cached properties in certain cases. - * For instance, adding more vertices does not make a connected graph disconnected, - * so we can keep the cached properties related to graph connectivity if they - * were already cached as true, but we need to invalidate them if they were - * cached as false. - * - * - * Use 1 << IGRAPH_PROP_SOMETHING to encode an individual property - * in the bits of the bitmask used in the arguments of this function. - * - * \param graph the graph whose cache is to be invalidated - * \param keep_always bitmask where the i-th bit corresponds to cached property \em i - * and it should be set to 1 if the property should be \em kept , - * irrespectively of its current cached value. - */ -void igraph_i_property_cache_invalidate_conditionally( - const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, - uint32_t keep_when_true -) { - uint32_t invalidate = ~keep_always; - uint32_t mask; - uint32_t maybe_keep; - igraph_bool_t cached_value; - - assert(graph->cache != NULL); - - /* The bits of maybe_keep are set to 1 for those properties that are: - * - * - currently cached - * - should _probably_ be invalidated - * - _but_ the current cached value of the property may change the decision - */ - maybe_keep = graph->cache->known & invalidate & (keep_when_false | keep_when_true); - - if (maybe_keep) { - for (igraph_cached_property_t prop = (igraph_cached_property_t ) 0; prop < IGRAPH_PROP_I_SIZE; ++prop) { - mask = 1 << prop; - if (maybe_keep & mask) { - /* if we get here, we know that the property is cached; we have - * masked maybe_keep with graph->cache->known */ - cached_value = igraph_i_property_cache_get_bool(graph, prop); - if ( - ((keep_when_false & mask) && !cached_value) || - ((keep_when_true & mask) && cached_value) - ) { - invalidate &= ~mask; - } - } - } - } - - graph->cache->known &= ~invalidate; -} diff --git a/src/vendor/cigraph/src/graph/caching.h b/src/vendor/cigraph/src/graph/caching.h deleted file mode 100644 index 3a3e1491ffa..00000000000 --- a/src/vendor/cigraph/src/graph/caching.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - 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, see . -*/ - -#ifndef IGRAPH_CACHING_H -#define IGRAPH_CACHING_H - -#include "igraph_datatype.h" -#include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_types.h" - -#include "internal/hacks.h" - -#include /* memset */ - -__BEGIN_DECLS - -struct igraph_i_property_cache_t { - igraph_bool_t value[IGRAPH_PROP_I_SIZE]; - - /** Bit field that stores which of the properties are cached at the moment */ - uint32_t known; -}; - -igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache); -igraph_error_t igraph_i_property_cache_copy( - igraph_i_property_cache_t *cache, - const igraph_i_property_cache_t *other_cache); -void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache); - -void igraph_i_property_cache_invalidate_conditionally( - const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, uint32_t keep_when_true -); - -__END_DECLS - -#endif /* IGRAPH_CACHING_H */ diff --git a/src/vendor/cigraph/src/graph/cattributes.c b/src/vendor/cigraph/src/graph/cattributes.c index 1a341a39c30..1a458c864d1 100644 --- a/src/vendor/cigraph/src/graph/cattributes.c +++ b/src/vendor/cigraph/src/graph/cattributes.c @@ -23,24 +23,24 @@ #include "igraph_attributes.h" #include "igraph_memory.h" +#include "core/math.h" #include "igraph_interface.h" #include "igraph_random.h" -#include "internal/hacks.h" /* strdup */ - #include -/* An attribute is either a numeric vector (vector_t), a boolean vector - * (vector_bool_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 igraph_i_cattribute_t, which contains three vector_ptr_t's, each - * holding pointers to igraph_attribute_record_t objects. */ +/* 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, igraph_integer_t *idx) { - igraph_integer_t i, n = igraph_vector_ptr_size(ptrvec); - igraph_bool_t l = false; + 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); @@ -51,140 +51,84 @@ static igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec, return l; } -/* - * Restores attribute vector lengths to their original size after a failure. - * This function assumes that none of the attribute vectors are shorter than origlen. - * Some may be longer due to a partially completed size extension: these will be - * shrunk to their original size. - */ -static void igraph_i_cattribute_revert_attribute_vector_sizes( - igraph_vector_ptr_t *attrlist, igraph_integer_t origlen) { - - igraph_integer_t no_of_attrs = igraph_vector_ptr_size(attrlist); - for (igraph_integer_t i = 0; i < no_of_attrs; i++) { - igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; - if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_vector_t *nvec = (igraph_vector_t *) rec->value; - IGRAPH_ASSERT(igraph_vector_capacity(nvec) >= origlen); - igraph_vector_resize(nvec, origlen); /* shrinks */ - } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { - igraph_vector_bool_t *bvec = (igraph_vector_bool_t *) rec->value; - IGRAPH_ASSERT(igraph_vector_bool_capacity(bvec) >= origlen); - igraph_vector_bool_resize(bvec, origlen); /* shrinks */ - } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *svec = (igraph_strvector_t *) rec->value; - IGRAPH_ASSERT(igraph_strvector_capacity(svec) >= origlen); - igraph_strvector_resize(svec, origlen); /* shrinks */ - } else { - /* Must never reach here */ - IGRAPH_FATAL("Unknown attribute type encountered."); - } - } -} - 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 igraph_error_t igraph_i_cattributes_copy_attribute_record(igraph_attribute_record_t **newrec, +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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); - IGRAPH_CHECK(igraph_vector_init_copy(newnum, num)); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); - IGRAPH_CHECK(igraph_strvector_init_copy(newstr, str)); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newlog); - IGRAPH_CHECK(igraph_vector_bool_init_copy(newlog, log)); + IGRAPH_CHECK(igraph_vector_bool_copy(newlog, log)); IGRAPH_FINALLY(igraph_vector_bool_destroy, newlog); (*newrec)->value = newlog; } IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } -static void igraph_i_attribute_list_destroy(igraph_vector_ptr_t *attrlist) { - igraph_integer_t i; - igraph_integer_t n = igraph_vector_ptr_size(attrlist); - for (i = 0; i < n; i++) { - igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; - if (rec) { - if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_vector_t *num = (igraph_vector_t *) rec->value; - igraph_vector_destroy(num); - IGRAPH_FREE(num); - } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *str = (igraph_strvector_t *) rec->value; - igraph_strvector_destroy(str); - IGRAPH_FREE(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(boolvec); - } - IGRAPH_FREE(rec->name); - IGRAPH_FREE(rec); - } - } - igraph_vector_ptr_destroy(attrlist); -} -static igraph_error_t igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { +static int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { igraph_attribute_record_t *attr_rec; - igraph_integer_t i, n; + 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); /* LCOV_EXCL_LINE */ + 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_i_attribute_list_destroy, &nattr->gal); + 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(igraph_vector_ptr_destroy, &nattr->eal); + IGRAPH_FINALLY_CLEAN(3); for (i = 0; i < n; i++) { IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record( @@ -193,30 +137,58 @@ static igraph_error_t igraph_i_cattribute_init(igraph_t *graph, igraph_vector_pt } graph->attr = nattr; - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + 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 }; - for (size_t a = 0; a < 3; a++) { - igraph_i_attribute_list_destroy(als[a]); + 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_FREE(graph->attr); /* sets to NULL */ + 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 }; - igraph_integer_t i, n; + long int i, n, a; igraph_vector_t *num; igraph_strvector_t *str; igraph_vector_bool_t *boolvec; igraph_attribute_record_t *rec; - for (size_t a = 0; a < 3; a++) { + for (a = 0; a < 3; a++) { n = igraph_vector_ptr_size(als[a]); for (i = 0; i < n; i++) { rec = VECTOR(*als[a])[i]; @@ -226,18 +198,18 @@ static void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { num = (igraph_vector_t*)rec->value; igraph_vector_destroy(num); - IGRAPH_FREE(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); + 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(str); } - IGRAPH_FREE(rec->name); - IGRAPH_FREE(rec); + igraph_free((char*)rec->name); + igraph_free(rec); } } } @@ -245,17 +217,17 @@ static void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { /* No reference counting here. If you use attributes in C you should know what you're doing. */ -static igraph_error_t igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from, +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 }; - igraph_integer_t i, n; + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, attrto); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0); @@ -265,7 +237,7 @@ static igraph_error_t igraph_i_cattribute_copy(igraph_t *to, const igraph_t *fro IGRAPH_FINALLY(igraph_i_cattribute_copy_free, attrto); alto[0] = &attrto->gal; alto[1] = &attrto->val; alto[2] = &attrto->eal; - for (size_t a = 0; a < 3; a++) { + 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)); @@ -280,31 +252,31 @@ static igraph_error_t igraph_i_cattribute_copy(igraph_t *to, const igraph_t *fro } IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, igraph_integer_t nv, - igraph_vector_ptr_t *nattr) { +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; - igraph_integer_t length = igraph_vector_ptr_size(val); - igraph_integer_t nattrno = nattr == NULL ? 0 : igraph_vector_ptr_size(nattr); - igraph_integer_t origlen = igraph_vcount(graph) - nv; - igraph_integer_t newattrs = 0, i; - igraph_vector_int_t news; + 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_INT_INIT_FINALLY(&news, 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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, nname, &j); if (!l) { newattrs++; - IGRAPH_CHECK(igraph_vector_int_push_back(&news, i)); + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != @@ -317,23 +289,23 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig /* 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)[VECTOR(news)[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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); @@ -342,7 +314,7 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig } 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); @@ -350,12 +322,13 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig } 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newbool); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); + IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); newrec->value = newbool; - igraph_vector_bool_fill(newbool, false); + igraph_vector_bool_fill(newbool, 0); } IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec)); IGRAPH_FINALLY_CLEAN(4); @@ -368,8 +341,8 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig igraph_attribute_record_t *oldrec = VECTOR(*val)[i]; igraph_attribute_record_t *newrec = 0; const char *name = oldrec->name; - igraph_integer_t j = -1; - igraph_bool_t l = false; + long int j = -1; + igraph_bool_t l = 0; if (nattr) { l = igraph_i_cattribute_find(nattr, name, &j); } @@ -386,29 +359,29 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig 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); + 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_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_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_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); break; default: - IGRAPH_WARNING("Invalid attribute type."); + IGRAPH_WARNING("Invalid attribute type"); break; } } else { @@ -439,42 +412,14 @@ static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, ig } } - igraph_vector_int_destroy(&news); + igraph_vector_destroy(&news); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_add_vertices(igraph_t *graph, igraph_integer_t nv, - igraph_vector_ptr_t *nattr) { - /* Record information needed to restore attribute vector sizes */ - igraph_i_cattributes_t *attr = graph->attr; - igraph_vector_ptr_t *val = &attr->val; - igraph_integer_t origlen = igraph_vcount(graph) - nv; - - /* Attempt adding attributes */ - igraph_error_t err = igraph_i_cattribute_add_vertices_inner(graph, nv, nattr); - if (err != IGRAPH_SUCCESS) { - /* If unsuccessful, revert attribute vector sizes. - * The following function assumes that all attributes vectors that - * are present have a length at least as great as origlen. - * This is true at the moment because any new attributes that are - * added to the graph are created directly at 'origlen' instead of - * being created at smaller sizes and resized later. - * - * NOTE: While this ensures that all attribute vector lengths are - * correct, it does not ensure that no extra attributes have - * been added to the graph. However, the presence of extra - * attributes does not make the attribute table inconsistent - * like the incorrect attribute vector lengths would. - */ - igraph_i_cattribute_revert_attribute_vector_sizes(val, origlen); - } - return err; -} - -static void igraph_i_cattribute_clear_attribute_container(igraph_vector_ptr_t *v) { - igraph_integer_t i, n = igraph_vector_ptr_size(v); +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); @@ -496,360 +441,180 @@ static void igraph_i_cattribute_clear_attribute_container(igraph_vector_ptr_t *v igraph_vector_ptr_clear(v); } -typedef struct { - igraph_vector_t *numeric; - igraph_vector_bool_t *boolean; - igraph_vector_ptr_t *strings; - igraph_integer_t length; -} igraph_i_attribute_permutation_work_area_t; - -static igraph_error_t igraph_i_attribute_permutation_work_area_init( - igraph_i_attribute_permutation_work_area_t *work_area, igraph_integer_t length -) { - work_area->length = length; - work_area->numeric = NULL; - work_area->boolean = NULL; - work_area->strings = NULL; - return IGRAPH_SUCCESS; -} - -static void igraph_i_attribute_permutation_work_area_release_stored_strvectors( - igraph_i_attribute_permutation_work_area_t *work_area -) { - if (work_area->strings != NULL) { - igraph_vector_ptr_destroy_all(work_area->strings); - igraph_Free(work_area->strings); - work_area->strings = NULL; - } -} - -static void igraph_i_attribute_permutation_work_area_destroy( - igraph_i_attribute_permutation_work_area_t *work_area -) { - igraph_i_attribute_permutation_work_area_release_stored_strvectors(work_area); - if (work_area->numeric != NULL) { - igraph_vector_destroy(work_area->numeric); - igraph_Free(work_area->numeric); - work_area->numeric = NULL; - } - if (work_area->boolean != NULL) { - igraph_vector_bool_destroy(work_area->boolean); - igraph_Free(work_area->boolean); - work_area->boolean = NULL; - } -} - -static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_numeric( - igraph_i_attribute_permutation_work_area_t *work_area -) { - igraph_vector_t* vec = work_area->numeric; - - if (vec == NULL) { - vec = IGRAPH_CALLOC(1, igraph_vector_t); - IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_init(vec, work_area->length)); - work_area->numeric = vec; - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_boolean( - igraph_i_attribute_permutation_work_area_t *work_area -) { - igraph_vector_bool_t* vec = work_area->boolean; - - if (vec == NULL) { - vec = IGRAPH_CALLOC(1, igraph_vector_bool_t); - IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_bool_init(vec, work_area->length)); - work_area->boolean = vec; - IGRAPH_FINALLY_CLEAN(1); - } +static int igraph_i_cattribute_permute_vertices(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_t *idx) { - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_strings( - igraph_i_attribute_permutation_work_area_t *work_area -) { - igraph_vector_ptr_t* vec = work_area->strings; - - if (vec == NULL) { - vec = IGRAPH_CALLOC(1, igraph_vector_ptr_t); - IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_ptr_init(vec, 0)); - IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(vec, igraph_strvector_destroy); - work_area->strings = vec; - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_attribute_permutation_work_area_permute_and_store_strvector( - igraph_i_attribute_permutation_work_area_t *work_area, - const igraph_strvector_t *vec, - const igraph_vector_int_t *idx -) { - igraph_strvector_t *new_vec; - - new_vec = IGRAPH_CALLOC(1, igraph_strvector_t); - IGRAPH_CHECK_OOM(new_vec, "Cannot permute attributes"); - IGRAPH_FINALLY(igraph_free, new_vec); - IGRAPH_CHECK(igraph_strvector_init(new_vec, 0)); - IGRAPH_FINALLY(igraph_strvector_destroy, new_vec); - IGRAPH_CHECK(igraph_vector_ptr_push_back(work_area->strings, new_vec)); - IGRAPH_FINALLY_CLEAN(2); - - IGRAPH_CHECK(igraph_strvector_index(vec, new_vec, idx)); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_cattribute_permute_vertices_in_place( - igraph_t *graph, const igraph_vector_int_t *idx -) { - igraph_i_cattributes_t *attr = graph->attr; - igraph_vector_ptr_t *val = &attr->val; - igraph_integer_t valno = igraph_vector_ptr_size(val); - igraph_integer_t i, j; - igraph_attribute_record_t *oldrec; - igraph_vector_t *num, *num_work; - igraph_strvector_t *str, str_work; - igraph_vector_bool_t *oldbool, *bool_work; - igraph_i_attribute_permutation_work_area_t work_area; - igraph_integer_t idx_size = igraph_vector_int_size(idx); - - /* shortcut: don't allocate anything if there are no attributes */ - if (valno == 0) { - return IGRAPH_SUCCESS; - } - - /* do all the allocations that can potentially fail before we actually - * start to permute the vertices to ensure that we will not ever need to - * back out from a permutation once we've started it */ - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); - IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); - for (i = 0; i < valno; i++) { - oldrec = VECTOR(*val)[i]; - switch (oldrec->type) { - case IGRAPH_ATTRIBUTE_NUMERIC: - num = (igraph_vector_t*) oldrec->value; - IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); - break; - - case IGRAPH_ATTRIBUTE_BOOLEAN: - oldbool = (igraph_vector_bool_t*) oldrec->value; - IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); - break; - - case IGRAPH_ATTRIBUTE_STRING: - str = (igraph_strvector_t*) oldrec->value; - IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); - break; - - default: - IGRAPH_WARNING("Unknown vertex attribute ignored"); - } - } - - /* let's do string attributes first because these might need extra - * allocations that can fail. The strategy is to build new igraph_strvector_t - * instances for the permuted attributes and store them in an - * igraph_vector_ptr_t until we are done with all of them. If any of the - * allocations fail, we can destroy the igraph_vector_ptr_t safely */ - for (i = 0; i < valno; i++) { - oldrec = VECTOR(*val)[i]; - if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { - continue; - } - - str = (igraph_strvector_t*) oldrec->value; - IGRAPH_CHECK( - igraph_i_attribute_permutation_work_area_permute_and_store_strvector( - &work_area, str, idx - ) - ); - } + if (graph == newgraph) { - /* strings are done, and now all vectors involved in the process are - * as large as they should be (or larger) so the operations below are not - * supposed to fail. We can safely replace the original string attribute - * vectors with the permuted ones, and then proceed to the remaining - * attributes */ - for (i = 0, j = 0; i < valno; i++) { - oldrec = VECTOR(*val)[i]; - if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { - continue; + 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"); + } } - str = (igraph_strvector_t*) oldrec->value; - str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); - *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; - *str = str_work; - j++; - } - igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); + } 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; - for (i = 0; i < valno; i++) { - oldrec = VECTOR(*val)[i]; - switch (oldrec->type) { - case IGRAPH_ATTRIBUTE_NUMERIC: - num = (igraph_vector_t*) oldrec->value; - num_work = work_area.numeric; - IGRAPH_ASSERT(num_work != NULL); - IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); - work_area.numeric = num; - oldrec->value = num_work; - break; - case IGRAPH_ATTRIBUTE_BOOLEAN: - oldbool = (igraph_vector_bool_t*) oldrec->value; - bool_work = work_area.boolean; - IGRAPH_ASSERT(bool_work != NULL); - IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); - work_area.boolean = oldbool; - oldrec->value = bool_work; - break; - case IGRAPH_ATTRIBUTE_STRING: - /* nothing to do */ - break; - default: - /* already warned */ - break; + /* 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_i_attribute_permutation_work_area_destroy(&work_area); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_cattribute_permute_vertices( - const igraph_t *graph, igraph_t *newgraph, const igraph_vector_int_t *idx -) { - igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; - igraph_vector_ptr_t *val = &attr->val, *new_val = &new_attr->val; - igraph_integer_t i, valno; - - IGRAPH_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_val)); + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); - /* Handle in-place permutation separately */ - if (graph == newgraph) { - return igraph_i_cattribute_permute_vertices_in_place(newgraph, idx); - } + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); - /* New vertex attributes */ - valno = igraph_vector_ptr_size(val); - IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); - IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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; - 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); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, new_rec); - new_rec->name = strdup(oldrec->name); - if (! new_rec->name) { - IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); - new_rec->type = oldrec->type; - - /* 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); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, newnum); - IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); - IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); - new_rec->value = newnum; - 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); /* LCOV_EXCL_LINE */ + /* 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); } - IGRAPH_FINALLY(igraph_free, newbool); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); - IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); - new_rec->value = newbool; - 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); /* LCOV_EXCL_LINE */ + 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(igraph_free, newstr); - IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); - IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); - new_rec->value = newstr; - break; - default: - IGRAPH_WARNING("Unknown vertex attribute ignored"); } - - VECTOR(*new_val)[i] = new_rec; - IGRAPH_FINALLY_CLEAN(4); } IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } -typedef igraph_error_t igraph_cattributes_combine_num_t(const igraph_vector_t *input, +typedef int igraph_cattributes_combine_num_t(const igraph_vector_t *input, igraph_real_t *output); -typedef igraph_error_t igraph_cattributes_combine_str_t(const igraph_strvector_t *input, +typedef int igraph_cattributes_combine_str_t(const igraph_strvector_t *input, char **output); -typedef igraph_error_t igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input, +typedef int igraph_cattributes_combine_bool_t(const igraph_vector_bool_t *input, igraph_bool_t *output); -static igraph_error_t igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + long int newlen = igraph_vector_ptr_size(merges); + long int i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); for (j = 0; j < n; j++) { - igraph_integer_t x = VECTOR(*idx)[j]; + long int x = (long int) VECTOR(*idx)[j]; s += VECTOR(*oldv)[x]; } VECTOR(*newv)[i] = s; @@ -858,29 +623,29 @@ static igraph_error_t igraph_i_cattributes_cn_sum(const igraph_attribute_record_ IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + long int newlen = igraph_vector_ptr_size(merges); + long int i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); for (j = 0; j < n; j++) { - igraph_integer_t x = VECTOR(*idx)[j]; + long int x = (long int) VECTOR(*idx)[j]; s *= VECTOR(*oldv)[x]; } VECTOR(*newv)[i] = s; @@ -889,30 +654,30 @@ static igraph_error_t igraph_i_cattributes_cn_prod(const igraph_attribute_record IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); - igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : nan; + 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++) { - igraph_integer_t x = VECTOR(*idx)[j]; + long int x = (long int) VECTOR(*idx)[j]; igraph_real_t val = VECTOR(*oldv)[x]; if (val < m) { m = val; @@ -924,30 +689,30 @@ static igraph_error_t igraph_i_cattributes_cn_min(const igraph_attribute_record_ IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); - igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : nan; + 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++) { - igraph_integer_t x = VECTOR(*idx)[j]; + long int x = (long int) VECTOR(*idx)[j]; igraph_real_t val = VECTOR(*oldv)[x]; if (val > m) { m = val; @@ -959,21 +724,21 @@ static igraph_error_t igraph_i_cattributes_cn_max(const igraph_attribute_record_ IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); @@ -981,15 +746,15 @@ static igraph_error_t igraph_i_cattributes_cn_random(const igraph_attribute_reco RNG_BEGIN(); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; } else { - igraph_integer_t r = RNG_INTEGER(0, n - 1); - VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; + long int r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; } } @@ -998,94 +763,94 @@ static igraph_error_t igraph_i_cattributes_cn_random(const igraph_attribute_reco IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[n - 1] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_vector_t *oldv = oldrec->value; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); + 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++) { - igraph_integer_t x = VECTOR(*idx)[j]; + long int x = (long int) VECTOR(*idx)[j]; s += VECTOR(*oldv)[x]; } if (n > 0) { @@ -1097,36 +862,37 @@ static igraph_error_t igraph_i_cattributes_cn_mean(const igraph_attribute_record IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, igraph_cattributes_combine_num_t *func) { const igraph_vector_t *oldv = oldrec->value; - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - + long int newlen = igraph_vector_ptr_size(merges); + long int i; igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); - IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); + 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_t values; IGRAPH_VECTOR_INIT_FINALLY(&values, 0); - for (igraph_integer_t i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - - igraph_integer_t n = igraph_vector_int_size(idx); + 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 (igraph_integer_t j = 0; j < n; j++) { - igraph_integer_t x = VECTOR(*idx)[j]; + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; VECTOR(values)[j] = VECTOR(*oldv)[x]; } - - igraph_real_t res; IGRAPH_CHECK(func(&values, &res)); VECTOR(*newv)[i] = res; } @@ -1135,36 +901,37 @@ static igraph_error_t igraph_i_cattributes_cn_func(const igraph_attribute_record IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + long int newlen = igraph_vector_ptr_size(merges); + long int i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; } else { - igraph_integer_t r = RNG_INTEGER(0, n - 1); - VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; + long int r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; } } @@ -1173,92 +940,95 @@ static igraph_error_t igraph_i_cattributes_cb_random(const igraph_attribute_reco IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + long int newlen = igraph_vector_ptr_size(merges); + long int i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + long int newlen = igraph_vector_ptr_size(merges); + long int i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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)[ VECTOR(*idx)[n - 1] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i, j, n, x; + long int newlen = igraph_vector_ptr_size(merges); + long int i, j, n, x; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - n = igraph_vector_int_size(idx); + igraph_vector_t *idx = VECTOR(*merges)[i]; + n = igraph_vector_size(idx); VECTOR(*newv)[i] = 1; for (j = 0; j < n; j++) { - x = VECTOR(*idx)[j]; + x = (long int) VECTOR(*idx)[j]; if (!VECTOR(*oldv)[x]) { VECTOR(*newv)[i] = 0; break; @@ -1269,30 +1039,31 @@ static igraph_error_t igraph_i_cattributes_cb_all_is_true(const igraph_attribute IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i, j, n, x; + long int newlen = igraph_vector_ptr_size(merges); + long int i, j, n, x; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - n = igraph_vector_int_size(idx); + igraph_vector_t *idx = VECTOR(*merges)[i]; + n = igraph_vector_size(idx); VECTOR(*newv)[i] = 0; for (j = 0; j < n; j++) { - x = VECTOR(*idx)[j]; + x = (long int) VECTOR(*idx)[j]; if (VECTOR(*oldv)[x]) { VECTOR(*newv)[i] = 1; break; @@ -1303,34 +1074,35 @@ static igraph_error_t igraph_i_cattributes_cb_any_is_true(const igraph_attribute IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t * newrec, - const igraph_vector_int_list_t *merges) { + 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); - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i, j, n, x, num_trues; + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + 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_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_vector_t *idx = VECTOR(*merges)[i]; - n = igraph_vector_int_size(idx); + n = igraph_vector_size(idx); num_trues = 0; for (j = 0; j < n; j++) { - x = VECTOR(*idx)[j]; + x = (long int) VECTOR(*idx)[j]; if (VECTOR(*oldv)[x]) { num_trues++; } @@ -1352,36 +1124,41 @@ static igraph_error_t igraph_i_cattributes_cb_majority(const igraph_attribute_re IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, igraph_cattributes_combine_bool_t *func) { const igraph_vector_bool_t *oldv = oldrec->value; - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - + long int newlen = igraph_vector_ptr_size(merges); + long int i; igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); - IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); + igraph_vector_bool_t values; + + if (!newv) { + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); - igraph_vector_bool_t values; - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, 0); + IGRAPH_CHECK(igraph_vector_bool_init(&values, 0)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); - for (igraph_integer_t i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + 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_integer_t n = igraph_vector_int_size(idx); IGRAPH_CHECK(igraph_vector_bool_resize(&values, n)); - for (igraph_integer_t j = 0; j < n; j++) { - igraph_integer_t x = VECTOR(*idx)[j]; + for (j = 0; j < n; j++) { + long int x = (long int) VECTOR(*idx)[j]; VECTOR(values)[j] = VECTOR(*oldv)[x]; } - igraph_bool_t res; IGRAPH_CHECK(func(&values, &res)); VECTOR(*newv)[i] = res; } @@ -1390,38 +1167,39 @@ static igraph_error_t igraph_i_cattributes_cb_func(const igraph_attribute_record IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - igraph_integer_t i; + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); RNG_BEGIN(); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); - const char *tmp; + 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) { - tmp = igraph_strvector_get(oldv, 0); + igraph_strvector_get(oldv, 0, &tmp); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } else { - igraph_integer_t r = RNG_INTEGER(0, n - 1); - tmp = igraph_strvector_get(oldv, r); + long int r = RNG_INTEGER(0, n - 1); + igraph_strvector_get(oldv, r, &tmp); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1431,30 +1209,32 @@ static igraph_error_t igraph_i_cattributes_sn_random(const igraph_attribute_reco IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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 { - const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[0]); + char *tmp; + igraph_strvector_get(oldv, (long int) VECTOR(*idx)[0], &tmp); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1462,30 +1242,32 @@ static igraph_error_t igraph_i_cattributes_sn_first(const igraph_attribute_recor IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t n = igraph_vector_int_size(idx); + 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 { - const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[n - 1]); + char *tmp; + igraph_strvector_get(oldv, (long int) VECTOR(*idx)[n - 1], &tmp); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1493,41 +1275,41 @@ static igraph_error_t igraph_i_cattributes_sn_last(const igraph_attribute_record IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges) { + const igraph_vector_ptr_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); + IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); + IGRAPH_FINALLY(igraph_strvector_destroy, newv); for (i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_vector_t *idx = VECTOR(*merges)[i]; + long int j, n = igraph_vector_size(idx); size_t len = 0; - const char *tmp; - char *tmp2; + char *tmp, *tmp2; for (j = 0; j < n; j++) { - tmp = igraph_strvector_get(oldv, 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, tmp2); len = 0; for (j = 0; j < n; j++) { - tmp = igraph_strvector_get(oldv, j); + igraph_strvector_get(oldv, j, &tmp); strcpy(tmp2 + len, tmp); len += strlen(tmp); } @@ -1540,160 +1322,131 @@ static igraph_error_t igraph_i_cattributes_sn_concat(const igraph_attribute_reco IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, +static int igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_int_list_t *merges, + const igraph_vector_ptr_t *merges, igraph_cattributes_combine_str_t *func) { const igraph_strvector_t *oldv = oldrec->value; - igraph_integer_t newlen = igraph_vector_int_list_size(merges); - + long int newlen = igraph_vector_ptr_size(merges); + long int i; igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); - IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); - IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); - igraph_strvector_t values; - IGRAPH_STRVECTOR_INIT_FINALLY(&values, 0); - for (igraph_integer_t i = 0; i < newlen; i++) { - igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + 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); - igraph_integer_t n = igraph_vector_int_size(idx); + 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 (igraph_integer_t j = 0; j < n; j++) { - igraph_integer_t x = VECTOR(*idx)[j]; - const char *elem = igraph_strvector_get(oldv, x); + 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)); } - - char *res; IGRAPH_CHECK(func(&values, &res)); IGRAPH_FINALLY(igraph_free, res); - IGRAPH_CHECK(igraph_strvector_set(newv, i, res)); - - IGRAPH_FREE(res); IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FREE(res); } igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return IGRAPH_SUCCESS; + return 0; } -/** - * \section c_attribute_combination_functions - * - * - * The C attribute handler supports combining the attributes of multiple - * vertices of edges into a single attribute during a vertex or edge contraction - * operation via a user-defined function. This is achieved by setting the - * type of the attribute combination to \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION - * and passing in a pointer to the custom combination function when specifying - * attribute combinations in \ref igraph_attribute_combination() or - * \ref igraph_attribute_combination_add() . For the C attribute handler, the - * signature of the function depends on the type of the underlying attribute. - * For numeric attributes, use: - * \verbatim igraph_error_t function(const igraph_vector_t *input, igraph_real_t *output); \endverbatim - * where \p input will receive a vector containing the value of the attribute - * for all the vertices or edges being combined, and \p output must be filled - * by the function to the combined value. Similarly, for Boolean attributes, the - * function takes a boolean vector in \p input and must return the combined Boolean - * value in \p output: - * \verbatim igraph_error_t function(const igraph_vector_bool_t *input, igraph_bool_t *output); \endverbatim - * For string attributes, the signature is slightly different: - * \verbatim igraph_error_t function(const igraph_strvector_t *input, char **output); \endverbatim - * In case of strings, all strings in the input vector are \em owned by igraph - * and must not be modified or freed in the combination handler. The string - * returned to the caller in \p output remains owned by the caller; igraph will - * make a copy it and store the copy in the appropriate part of the data - * structure holding the vertex or edge attributes. - * - */ -typedef struct { - igraph_attribute_combination_type_t type; - union { - igraph_function_pointer_t as_void; - igraph_cattributes_combine_num_t *as_num; - igraph_cattributes_combine_str_t *as_str; - igraph_cattributes_combine_bool_t *as_bool; - } func; -} igraph_attribute_combination_todo_item_t; - -static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph, +static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + 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; - igraph_integer_t valno = igraph_vector_ptr_size(val); - igraph_integer_t i, j, keepno = 0; - igraph_attribute_combination_todo_item_t *todo_items; + long int valno = igraph_vector_ptr_size(val); + long int i, j, keepno = 0; + int *TODO; + igraph_function_pointer_t *funcs; - IGRAPH_ASSERT(graph != newgraph); - IGRAPH_ASSERT(igraph_vector_ptr_empty(new_val)); - - todo_items = IGRAPH_CALLOC(valno, igraph_attribute_combination_todo_item_t); - if (!todo_items) { + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, todo_items); + 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 type; + igraph_attribute_combination_type_t todo; igraph_function_pointer_t voidfunc; - IGRAPH_CHECK(igraph_attribute_combination_query(comb, name, &type, &voidfunc)); - todo_items[i].type = type; - todo_items[i].func.as_void = voidfunc; - if (type != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + 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_clear_attribute_container, new_val); + 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_todo_item_t todo_item = todo_items[i]; - igraph_attribute_type_t attr_type = oldrec->type; - - if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || - todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine vertex attributes", + IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, newrec); newrec->name = strdup(name); - if (!newrec->name) { - IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, (char *) newrec->name); - newrec->type = attr_type; + newrec->type = type; + VECTOR(*new_val)[j] = newrec; - if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { - switch (todo_item.type) { + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, - todo_item.func.as_num)); + numfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); @@ -1732,11 +1485,11 @@ static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph IGRAPH_UNIMPLEMENTED); break; } - } else if (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - switch (todo_item.type) { + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, - todo_item.func.as_bool)); + boolfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: case IGRAPH_ATTRIBUTE_COMBINE_MAX: @@ -1768,11 +1521,11 @@ static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph IGRAPH_UNIMPLEMENTED); break; } - } else if (attr_type == IGRAPH_ATTRIBUTE_STRING) { - switch (todo_item.type) { + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, - todo_item.func.as_str)); + strfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); @@ -1818,41 +1571,103 @@ static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph IGRAPH_UNIMPLEMENTED); } - VECTOR(*new_val)[j] = newrec; - IGRAPH_FINALLY_CLEAN(2); /* newrec->name and newrec */ - j++; } - IGRAPH_FREE(todo_items); - IGRAPH_FINALLY_CLEAN(2); + igraph_free(funcs); + igraph_free(TODO); + igraph_i_cattribute_permute_free(new_val); + IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const igraph_vector_int_t *edges, - igraph_vector_ptr_t *nattr) { +/* 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; - igraph_integer_t ealno = igraph_vector_ptr_size(eal); - igraph_integer_t ne = igraph_vector_int_size(edges) / 2; - igraph_integer_t origlen = igraph_ecount(graph) - ne; - igraph_integer_t nattrno = nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); - igraph_vector_int_t news; - igraph_integer_t newattrs, i; + 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_INT_INIT_FINALLY(&news, 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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, nname, &j); if (!l) { newattrs++; - IGRAPH_CHECK(igraph_vector_int_push_back(&news, i)); + IGRAPH_CHECK(igraph_vector_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != @@ -1862,26 +1677,26 @@ static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const } } - /* Add NaN/false/"" for the existing vertices for numeric, boolean and string attributes. */ + /* 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)[ VECTOR(news)[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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); @@ -1890,16 +1705,17 @@ static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const } 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newbool); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); + IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); + IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); newrec->value = newbool; - igraph_vector_bool_fill(newbool, false); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); @@ -1914,10 +1730,10 @@ static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const /* Now append the new values */ for (i = 0; i < ealno; i++) { igraph_attribute_record_t *oldrec = VECTOR(*eal)[i]; - igraph_attribute_record_t *newrec = NULL; + igraph_attribute_record_t *newrec = 0; const char *name = oldrec->name; - igraph_integer_t j = -1; - igraph_bool_t l = false; + long int j = -1; + igraph_bool_t l = 0; if (nattr) { l = igraph_i_cattribute_find(nattr, name, &j); } @@ -1934,33 +1750,33 @@ static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const 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); + 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_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_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_ERROR("Invalid Boolean attribute length", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); break; default: - IGRAPH_WARNING("Invalid attribute type."); + IGRAPH_WARNING("Invalid attribute type"); break; } } else { - /* No such attribute, append NaN/false/"". */ + /* 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; @@ -1987,316 +1803,261 @@ static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const } } - igraph_vector_int_destroy(&news); + igraph_vector_destroy(&news); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, - igraph_vector_ptr_t *nattr) { - /* Record information needed to restore attribute vector sizes */ - igraph_i_cattributes_t *attr = graph->attr; - igraph_vector_ptr_t *eal = &attr->eal; - igraph_integer_t ne = igraph_vector_int_size(edges) / 2; - igraph_integer_t origlen = igraph_ecount(graph) - ne; - - /* Attempt adding attributes */ - igraph_error_t err = igraph_i_cattribute_add_edges_inner(graph, edges, nattr); - if (err != IGRAPH_SUCCESS) { - /* If unsuccessful, revert attribute vector sizes. - * The following function assumes that all attributes vectors that - * are present have a length at least as great as origlen. - * This is true at the moment because any new attributes that are - * added to the graph are created directly at 'origlen' instead of - * being created at smaller sizes and resized later. - * - * NOTE: While this ensures that all attribute vector lengths are - * correct, it does not ensure that no extra attributes have - * been added to the graph. However, the presence of extra - * attributes does not make the attribute table inconsistent - * like the incorrect attribute vector lengths would. - */ - igraph_i_cattribute_revert_attribute_vector_sizes(eal, origlen); - } - return err; + return 0; } -static igraph_error_t igraph_i_cattribute_permute_edges_in_place( - igraph_t *graph, const igraph_vector_int_t *idx -) { - igraph_i_cattributes_t *attr = graph->attr; - igraph_vector_ptr_t *eal = &attr->eal; - igraph_integer_t ealno = igraph_vector_ptr_size(eal); - igraph_integer_t i, j; - igraph_attribute_record_t *oldrec; - igraph_vector_t *num, *num_work; - igraph_strvector_t *str, str_work; - igraph_vector_bool_t *oldbool, *bool_work; - igraph_i_attribute_permutation_work_area_t work_area; - igraph_integer_t idx_size = igraph_vector_int_size(idx); - - /* shortcut: don't allocate anything if there are no attributes */ - if (ealno == 0) { - return IGRAPH_SUCCESS; - } - - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); - IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); - for (i = 0; i < ealno; i++) { - oldrec = VECTOR(*eal)[i]; - switch (oldrec->type) { - case IGRAPH_ATTRIBUTE_NUMERIC: - num = (igraph_vector_t*) oldrec->value; - IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); - break; - - case IGRAPH_ATTRIBUTE_BOOLEAN: - oldbool = (igraph_vector_bool_t*) oldrec->value; - IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); - break; - - case IGRAPH_ATTRIBUTE_STRING: - str = (igraph_strvector_t*) oldrec->value; - IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); - IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); - break; - - default: - IGRAPH_WARNING("Unknown edge attribute ignored"); - } - } - - /* let's do string attributes first because these might need extra - * allocations that can fail. The strategy is to build new igraph_strvector_t - * instances for the permuted attributes and store them in an - * igraph_vector_ptr_t until we are done with all of them. If any of the - * allocations fail, we can destroy the igraph_vector_ptr_t safely */ - for (i = 0; i < ealno; i++) { - oldrec = VECTOR(*eal)[i]; - if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { - continue; - } - - str = (igraph_strvector_t*) oldrec->value; - IGRAPH_CHECK( - igraph_i_attribute_permutation_work_area_permute_and_store_strvector( - &work_area, str, idx - ) - ); - } - - /* strings are done, and now all vectors involved in the process are - * as large as they should be (or larger) so the operations below are not - * supposed to fail. We can safely replace the original string attribute - * vectors with the permuted ones, and then proceed to the remaining - * attributes */ - for (i = 0, j = 0; i < ealno; i++) { - oldrec = VECTOR(*eal)[i]; - if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { - continue; - } +/* 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) { - str = (igraph_strvector_t*) oldrec->value; - str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); - *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; - *str = str_work; - j++; - } - igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); + if (graph == newgraph) { - /* now all vectors involved in the process are as large as they should be - * (or larger) so the operations below are not supposed to fail -- except - * for string operations that still do some extra allocations and we are - * not prepared for the failures of those. This must still be fixed. */ - for (i = 0; i < ealno; i++) { - oldrec = VECTOR(*eal)[i]; - switch (oldrec->type) { - case IGRAPH_ATTRIBUTE_NUMERIC: - num = (igraph_vector_t*) oldrec->value; - num_work = work_area.numeric; - IGRAPH_ASSERT(num_work != NULL); - IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); - work_area.numeric = num; - oldrec->value = num_work; - break; - case IGRAPH_ATTRIBUTE_BOOLEAN: - oldbool = (igraph_vector_bool_t*) oldrec->value; - bool_work = work_area.boolean; - IGRAPH_ASSERT(bool_work != NULL); - IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); - work_area.boolean = oldbool; - oldrec->value = bool_work; - break; - case IGRAPH_ATTRIBUTE_STRING: - /* nothing to do */ - break; - default: - /* already warned */ - break; + 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"); + } } - } - - igraph_i_attribute_permutation_work_area_destroy(&work_area); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} + } else { -static igraph_error_t igraph_i_cattribute_permute_edges(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_int_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; - igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; - igraph_vector_ptr_t *eal = &attr->eal, *new_eal = &new_attr->eal; - igraph_integer_t i, ealno; + /* 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_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_eal)); + IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); - if (graph == newgraph) { - return igraph_i_cattribute_permute_edges_in_place(newgraph, idx); - } - - /* New edge attributes */ - ealno = igraph_vector_ptr_size(eal); - IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); - IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); - IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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; - 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); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, new_rec); - new_rec->name = strdup(oldrec->name); - if (! new_rec->name) { - IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); - new_rec->type = oldrec->type; - - 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); /* LCOV_EXCL_LINE */ + /* 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); } - IGRAPH_FINALLY(igraph_free, newnum); - IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); - IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); - new_rec->value = newnum; - 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); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, newstr); - IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); - IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); - new_rec->value = newstr; - 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); /* LCOV_EXCL_LINE */ + 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(igraph_free, newbool); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); - IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); - new_rec->value = newbool; - break; - default: - IGRAPH_WARNING("Unknown edge attribute ignored"); } - VECTOR(*new_eal)[i] = new_rec; - IGRAPH_FINALLY_CLEAN(4); + IGRAPH_FINALLY_CLEAN(1); } - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, +static int igraph_i_cattribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_int_list_t *merges, + 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; - igraph_integer_t ealno = igraph_vector_ptr_size(eal); - igraph_integer_t i, j, keepno = 0; - igraph_attribute_combination_todo_item_t *todo_items; - - IGRAPH_ASSERT(graph != newgraph); - IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); + long int ealno = igraph_vector_ptr_size(eal); + long int i, j, keepno = 0; + int *TODO; + igraph_function_pointer_t *funcs; - todo_items = IGRAPH_CALLOC(ealno, igraph_attribute_combination_todo_item_t); - if (!todo_items) { - IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + TODO = IGRAPH_CALLOC(ealno, int); + if (!TODO) { + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, todo_items); + 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_CHECK(igraph_attribute_combination_query(comb, name, &todo, &voidfunc)); - todo_items[i].type = todo; - todo_items[i].func.as_void = 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_clear_attribute_container, new_eal); + 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_todo_item_t todo_item = todo_items[i]; - igraph_attribute_type_t attr_type = oldrec->type; - - if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || - todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot combine edge attributes", + IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, newrec); newrec->name = strdup(name); - if (! newrec->name) { - IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, (char *) newrec->name); - newrec->type = attr_type; + newrec->type = type; + VECTOR(*new_eal)[j] = newrec; - if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { - switch (todo_item.type) { + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, - todo_item.func.as_num)); + numfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); @@ -2335,11 +2096,11 @@ static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - switch (todo_item.type) { + } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, - todo_item.func.as_bool)); + boolfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: case IGRAPH_ATTRIBUTE_COMBINE_MAX: @@ -2371,11 +2132,11 @@ static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (attr_type == IGRAPH_ATTRIBUTE_STRING) { - switch (todo_item.type) { + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, - todo_item.func.as_str)); + strfunc)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); @@ -2421,43 +2182,41 @@ static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); } - VECTOR(*new_eal)[j] = newrec; - IGRAPH_FINALLY_CLEAN(2); /* newrec and newrc->name */ - j++; } - IGRAPH_FREE(todo_items); - IGRAPH_FINALLY_CLEAN(2); + igraph_free(funcs); + igraph_free(TODO); + IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_info(const igraph_t *graph, +static int igraph_i_cattribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_int_t *gtypes, + igraph_vector_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_int_t *vtypes, + igraph_vector_t *vtypes, igraph_strvector_t *enames, - igraph_vector_int_t *etypes) { + igraph_vector_t *etypes) { igraph_strvector_t *names[3] = { gnames, vnames, enames }; - igraph_vector_int_t *types[3] = { gtypes, vtypes, etypes }; + 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 }; - igraph_integer_t i, j; + long int i, j; for (i = 0; i < 3; i++) { igraph_strvector_t *n = names[i]; - igraph_vector_int_t *t = types[i]; + igraph_vector_t *t = types[i]; igraph_vector_ptr_t *al = attr[i]; - igraph_integer_t len = igraph_vector_ptr_size(al); + long int len = igraph_vector_ptr_size(al); if (n) { IGRAPH_CHECK(igraph_strvector_resize(n, len)); } if (t) { - IGRAPH_CHECK(igraph_vector_int_resize(t, len)); + IGRAPH_CHECK(igraph_vector_resize(t, len)); } for (j = 0; j < len; j++) { @@ -2473,7 +2232,7 @@ static igraph_error_t igraph_i_cattribute_get_info(const igraph_t *graph, } } - return IGRAPH_SUCCESS; + return 0; } static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, @@ -2481,7 +2240,7 @@ static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, const char *name) { igraph_i_cattributes_t *at = graph->attr; igraph_vector_ptr_t *attr[3] = { &at->gal, &at->val, &at->eal }; - igraph_integer_t attrnum; + long int attrnum; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -2501,17 +2260,17 @@ static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, return igraph_i_cattribute_find(attr[attrnum], name, 0); } -static igraph_error_t igraph_i_cattribute_gettype(const igraph_t *graph, +static int igraph_i_cattribute_gettype(const igraph_t *graph, igraph_attribute_type_t *type, igraph_attribute_elemtype_t elemtype, const char *name) { - igraph_integer_t attrnum; + 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; - igraph_integer_t j; - igraph_bool_t l = false; + long int j; + igraph_bool_t l = 0; switch (elemtype) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -2536,21 +2295,21 @@ static igraph_error_t igraph_i_cattribute_gettype(const igraph_t *graph, rec = VECTOR(*al)[j]; *type = rec->type; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*gal)[j]; @@ -2561,21 +2320,21 @@ static igraph_error_t igraph_i_cattribute_get_numeric_graph_attr(const igraph_t IGRAPH_CHECK(igraph_vector_resize(value, 1)); VECTOR(*value)[0] = VECTOR(*num)[0]; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*gal)[j]; @@ -2586,21 +2345,21 @@ static igraph_error_t igraph_i_cattribute_get_bool_graph_attr(const igraph_t *gr IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); VECTOR(*value)[0] = VECTOR(*log)[0]; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*gal)[j]; @@ -2611,22 +2370,22 @@ static igraph_error_t igraph_i_cattribute_get_string_graph_attr(const igraph_t * IGRAPH_CHECK(igraph_strvector_resize(value, 1)); IGRAPH_CHECK(igraph_strvector_set(value, 0, STR(*str, 0))); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*val)[j]; @@ -2639,35 +2398,35 @@ static igraph_error_t igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_vit_t it; - igraph_integer_t i = 0; + 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++) { - igraph_integer_t v = IGRAPH_VIT_GET(it); + long int v = IGRAPH_VIT_GET(it); VECTOR(*value)[i] = VECTOR(*num)[v]; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph, +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; - igraph_integer_t i, j, v; + 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_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*val)[j]; @@ -2690,22 +2449,22 @@ static igraph_error_t igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *g IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*val)[j]; @@ -2714,39 +2473,40 @@ static igraph_error_t igraph_i_cattribute_get_string_vertex_attr(const igraph_t } str = (igraph_strvector_t*)rec->value; if (igraph_vs_is_all(&vs)) { - igraph_strvector_clear(value); + igraph_strvector_resize(value, 0); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_vit_t it; - igraph_integer_t i = 0; + 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++) { - igraph_integer_t v = IGRAPH_VIT_GET(it); - const char *s = igraph_strvector_get(str, v); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*eal)[j]; @@ -2759,34 +2519,34 @@ static igraph_error_t igraph_i_cattribute_get_numeric_edge_attr(const igraph_t * IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_eit_t it; - igraph_integer_t i = 0; + 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++) { - igraph_integer_t e = IGRAPH_EIT_GET(it); + long int e = IGRAPH_EIT_GET(it); VECTOR(*value)[i] = VECTOR(*num)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*eal)[j]; @@ -2795,39 +2555,40 @@ static igraph_error_t igraph_i_cattribute_get_string_edge_attr(const igraph_t *g } str = (igraph_strvector_t*)rec->value; if (igraph_es_is_all(&es)) { - igraph_strvector_clear(value); + igraph_strvector_resize(value, 0); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_eit_t it; - igraph_integer_t i = 0; + 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++) { - igraph_integer_t e = IGRAPH_EIT_GET(it); - const char *s = igraph_strvector_get(str, e); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph, +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; - igraph_integer_t j; + 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_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); + IGRAPH_ERROR("Unknown attribute", IGRAPH_EINVAL); } rec = VECTOR(*eal)[j]; @@ -2840,19 +2601,19 @@ static igraph_error_t igraph_i_cattribute_get_bool_edge_attr(const igraph_t *gra IGRAPH_CHECK(igraph_vector_bool_append(value, log)); } else { igraph_eit_t it; - igraph_integer_t i = 0; + 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++) { - igraph_integer_t e = IGRAPH_EIT_GET(it); + long int e = IGRAPH_EIT_GET(it); VECTOR(*value)[i] = VECTOR(*log)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /* -------------------------------------- */ @@ -2863,8 +2624,7 @@ const igraph_attribute_table_t igraph_cattribute_table = { &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_combine_vertices, &igraph_i_cattribute_add_edges, &igraph_i_cattribute_permute_edges, &igraph_i_cattribute_combine_edges, &igraph_i_cattribute_get_info, @@ -2925,12 +2685,10 @@ const igraph_attribute_table_t igraph_cattribute_table = { /** * \function igraph_cattribute_GAN - * \brief Query a numeric graph attribute. + * Query a numeric graph attribute. * * Returns the value of the given numeric graph attribute. - * If the attribute does not exist, a warning is issued - * and NaN is returned. - * + * 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. @@ -2943,14 +2701,14 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Graph attribute '%s' does not exist, returning default numeric attribute value.", name); - return IGRAPH_NAN; + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; } rec = VECTOR(*gal)[j]; @@ -2960,12 +2718,10 @@ igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_GAB - * \brief Query a boolean graph attribute. - * - * Returns the value of the given boolean graph attribute. - * If the attribute does not exist, a warning is issued - * and false is returned. + * 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. @@ -2978,14 +2734,14 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Graph attribute '%s' does not exist, returning default boolean attribute value.", name); - return false; + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; } rec = VECTOR(*gal)[j]; @@ -2995,13 +2751,11 @@ igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_GAS - * \brief Query a string graph attribute. + * Query a string graph attribute. * * Returns a const pointer to the string graph attribute - * specified in \p name. The value must not be modified. - * If the attribute does not exist, a warning is issued and - * an empty string is returned. - * + * 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. @@ -3010,18 +2764,18 @@ igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { * * Time complexity: O(Ag), the number of graph attributes. */ -const char *igraph_cattribute_GAS(const igraph_t *graph, const char *name) { +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; - igraph_integer_t j; + 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_WARNINGF("Graph attribute '%s' does not exist, returning default string attribute value.", name); - return ""; + igraph_error("Unknown attribute", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_EINVAL); + return 0; } rec = VECTOR(*gal)[j]; @@ -3031,12 +2785,9 @@ const char *igraph_cattribute_GAS(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_VAN - * \brief Query a numeric vertex attribute. - * - * If the attribute does not exist, a warning is issued and - * NaN is returned. See \ref igraph_cattribute_VANV() for - * an error-checked version. + * 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. @@ -3050,29 +2801,26 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Vertex attribute '%s' does not exist, returning default numeric attribute value.", name); - return IGRAPH_NAN; + 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)[vid]; + return VECTOR(*num)[(long int)vid]; } /** * \function igraph_cattribute_VAB - * \brief Query a boolean vertex attribute. - * - * If the vertex attribute does not exist, a warning is issued - * and false is returned. See \ref igraph_cattribute_VABV() for - * an error-checked version. + * 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. @@ -3086,31 +2834,26 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Vertex attribute '%s' does not exist, returning default boolean attribute value.", name); - return false; + 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)[vid]; + return VECTOR(*log)[(long int)vid]; } /** * \function igraph_cattribute_VAS - * \brief Query a string vertex attribute. - * - * Returns a const pointer to the string vertex attribute - * specified in \p name. The value must not be modified. - * If the vertex attribute does not exist, a warning is issued and - * an empty string is returned. See \ref igraph_cattribute_VASV() - * for an error-checked version. + * 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. @@ -3120,33 +2863,30 @@ igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, * * Time complexity: O(Av), the number of vertex attributes. */ -const char *igraph_cattribute_VAS(const igraph_t *graph, const char *name, +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; - igraph_integer_t j; + 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_WARNINGF("Vertex attribute '%s' does not exist, returning default string attribute value.", name); - return ""; + 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, vid); + return STR(*str, (long int)vid); } /** * \function igraph_cattribute_EAN - * \brief Query a numeric edge attribute. - * - * If the attribute does not exist, a warning is issued and - * NaN is returned. See \ref igraph_cattribute_EANV() for - * an error-checked version. + * 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. @@ -3160,29 +2900,26 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Edge attribute '%s' does not exist, returning default numeric attribute value.", name); - return IGRAPH_NAN; + 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)[eid]; + return VECTOR(*num)[(long int)eid]; } /** * \function igraph_cattribute_EAB - * \brief Query a boolean edge attribute. - * - * If the edge attribute does not exist, a warning is issued and - * false is returned. See \ref igraph_cattribute_EABV() for - * an error-checked version. + * 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. @@ -3196,31 +2933,26 @@ 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; - igraph_integer_t j; + 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_WARNINGF("Edge attribute '%s' does not exist, returning default boolean attribute value.", name); - return false; + 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)[eid]; + return VECTOR(*log)[(long int)eid]; } /** * \function igraph_cattribute_EAS - * \brief Query a string edge attribute. - * - * Returns a const pointer to the string edge attribute - * specified in \p name. The value must not be modified. - * If the edge attribute does not exist, a warning is issued and - * an empty string is returned. See \ref igraph_cattribute_EASV() for - * an error-checked version. + * 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. @@ -3230,28 +2962,28 @@ igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, * * Time complexity: O(Ae), the number of edge attributes. */ -const char *igraph_cattribute_EAS(const igraph_t *graph, const char *name, +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; - igraph_integer_t j; + 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_WARNINGF("Edge attribute '%s' does not exist, returning default string attribute value.", name); - return ""; + 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, eid); + return STR(*str, (long int)eid); } /** * \function igraph_cattribute_VANV - * \brief Query a numeric vertex attribute for many vertices. + * Query a numeric vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. @@ -3263,7 +2995,7 @@ const char *igraph_cattribute_EAS(const igraph_t *graph, const char *name, * Time complexity: O(v), where v is the number of vertices in 'vids'. */ -igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, +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, @@ -3272,7 +3004,7 @@ igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_VABV - * \brief Query a boolean vertex attribute for many vertices. + * Query a boolean vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. @@ -3284,7 +3016,7 @@ igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, * Time complexity: O(v), where v is the number of vertices in 'vids'. */ -igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, +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, @@ -3293,7 +3025,7 @@ igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EANV - * \brief Query a numeric edge attribute for many edges. + * Query a numeric edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. @@ -3305,7 +3037,7 @@ igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, * Time complexity: O(e), where e is the number of edges in 'eids'. */ -igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, +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, @@ -3314,7 +3046,7 @@ igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EABV - * \brief Query a boolean edge attribute for many edges. + * Query a boolean edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. @@ -3326,7 +3058,7 @@ igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, * Time complexity: O(e), where e is the number of edges in 'eids'. */ -igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, +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, @@ -3335,7 +3067,7 @@ igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_VASV - * \brief Query a string vertex attribute for many vertices. + * Query a string vertex attribute for many vertices * * \param graph The input graph. * \param name The name of the attribute. @@ -3348,7 +3080,7 @@ igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, * (We assume that the string attributes have a bounded length.) */ -igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, +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, @@ -3357,7 +3089,7 @@ igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EASV - * \brief Query a string edge attribute for many edges. + * Query a string edge attribute for many edges * * \param graph The input graph. * \param name The name of the attribute. @@ -3370,7 +3102,7 @@ igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, * 'eids'. (We assume that the string attributes have a bounded length.) */ -igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, +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, @@ -3379,7 +3111,7 @@ igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_list - * \brief List all attributes. + * List all attributes * * See \ref igraph_attribute_type_t for the various attribute types. * \param graph The input graph. @@ -3397,23 +3129,23 @@ igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, * * Time complexity: O(Ag+Av+Ae), the number of all attributes. */ -igraph_error_t igraph_cattribute_list(const igraph_t *graph, - igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, - igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, - igraph_strvector_t *enames, igraph_vector_int_t *etypes) { +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 - * \brief Checks whether a (graph, vertex or edge) attribute exists. + * 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, \c true if the attribute exists, \c false otherwise. + * \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. @@ -3426,7 +3158,7 @@ igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, /** * \function igraph_cattribute_GAN_set - * \brief Set a numeric graph attribute. + * Set a numeric graph attribute * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3438,12 +3170,12 @@ igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, * * Time complexity: O(1). */ -igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3458,18 +3190,18 @@ igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); IGRAPH_VECTOR_INIT_FINALLY(num, 1); @@ -3479,12 +3211,12 @@ igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_GAB_set - * \brief Set a boolean graph attribute. + * Set a boolean graph attribute * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3496,12 +3228,12 @@ igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, * * Time complexity: O(1). */ -igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3516,33 +3248,34 @@ igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, 1); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_GAS_set - * \brief Set a string graph attribute. + * Set a string graph attribute. * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3555,12 +3288,12 @@ igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, * * Time complexity: O(1). */ -igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3575,18 +3308,18 @@ igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, 1); @@ -3596,12 +3329,12 @@ igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAN_set - * \brief Set a numeric vertex attribute. + * 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 @@ -3617,12 +3350,12 @@ igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices if the attribute is * new, O(|vid|) otherwise. */ -igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3631,40 +3364,40 @@ igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num = (igraph_vector_t*)rec->value; - VECTOR(*num)[vid] = 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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)[vid] = value; + VECTOR(*num)[(long int)vid] = value; rec->value = num; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAB_set - * \brief Set a boolean vertex attribute. + * 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 @@ -3680,12 +3413,12 @@ igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices if the attribute is * new, O(|vid|) otherwise. */ -igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3694,40 +3427,41 @@ igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value; - VECTOR(*log)[vid] = 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_vcount(graph)); - igraph_vector_bool_fill(log, false); - VECTOR(*log)[vid] = value; + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAS_set - * \brief Set a string vertex attribute. + * 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 @@ -3744,12 +3478,12 @@ igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, * length of the string to set. If the attribute if not new then only * O(|vid|*l). */ -igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3764,18 +3498,18 @@ igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_vcount(graph)); @@ -3785,12 +3519,12 @@ igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAN_set - * \brief Set a numeric edge attribute. + * 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 @@ -3806,12 +3540,12 @@ igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, * Time complexity: O(e), the number of edges if the attribute is * new, O(|eid|) otherwise. */ -igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3820,40 +3554,40 @@ igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num = (igraph_vector_t*)rec->value; - VECTOR(*num)[eid] = 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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)[eid] = value; + VECTOR(*num)[(long int)eid] = value; rec->value = num; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAB_set - * \brief Set a boolean edge attribute. + * 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 @@ -3869,12 +3603,12 @@ igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, * Time complexity: O(e), the number of edges if the attribute is * new, O(|eid|) otherwise. */ -igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3883,40 +3617,41 @@ igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_bool_t *log = (igraph_vector_bool_t*)rec->value; - VECTOR(*log)[eid] = 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_ecount(graph)); - igraph_vector_bool_fill(log, false); - VECTOR(*log)[eid] = value; + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAS_set - * \brief Set a string edge attribute. + * 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 @@ -3933,12 +3668,12 @@ igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, * length of the string to set. If the attribute if not new then only * O(|eid|*l). */ -igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3953,18 +3688,18 @@ igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_ecount(graph)); @@ -3974,12 +3709,12 @@ igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAN_setv - * \brief Set a numeric vertex attribute for all vertices. + * Set a numeric vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3993,11 +3728,11 @@ igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices. */ -igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -4019,32 +3754,32 @@ igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); rec->value = num; - IGRAPH_CHECK(igraph_vector_init_copy(num, v)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAB_setv - * \brief Set a boolean vertex attribute for all vertices. + * Set a boolean vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4058,11 +3793,11 @@ igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices. */ -igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -4084,33 +3819,33 @@ igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); rec->value = log; - IGRAPH_CHECK(igraph_vector_bool_init_copy(log, v)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_VAS_setv - * \brief Set a string vertex attribute for all vertices. + * Set a string vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4124,12 +3859,12 @@ igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, * Time complexity: O(n+l), n is the number of vertices, l is the * total length of the strings. */ -igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -4151,33 +3886,33 @@ igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); rec->value = str; - IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAN_setv - * \brief Set a numeric edge attribute for all edges. + * Set a numeric edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4190,12 +3925,12 @@ igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, * * Time complexity: O(e), the number of edges. */ -igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -4217,33 +3952,33 @@ igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, num); rec->value = num; - IGRAPH_CHECK(igraph_vector_init_copy(num, v)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAB_setv - * \brief Set a boolean edge attribute for all edges. + * Set a boolean edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4256,12 +3991,12 @@ igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, * * Time complexity: O(e), the number of edges. */ -igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -4283,33 +4018,33 @@ igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, log); rec->value = log; - IGRAPH_CHECK(igraph_vector_bool_init_copy(log, v)); + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_cattribute_EAS_setv - * \brief Set a string edge attribute for all edges. + * Set a string edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4323,12 +4058,12 @@ igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, * Time complexity: O(e+l), e is the number of edges, l is the * total length of the strings. */ -igraph_error_t igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, +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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -4350,28 +4085,28 @@ igraph_error_t igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, str); rec->value = str; - IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); + 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 IGRAPH_SUCCESS; + return 0; } static void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { @@ -4393,7 +4128,7 @@ static void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { /** * \function igraph_cattribute_remove_g - * \brief Remove a graph attribute. + * Remove a graph attribute * * \param graph The graph object. * \param name Name of the graph attribute to remove. @@ -4405,7 +4140,7 @@ 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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -4418,7 +4153,7 @@ void igraph_cattribute_remove_g(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_v - * \brief Remove a vertex attribute. + * Remove a vertex attribute * * \param graph The graph object. * \param name Name of the vertex attribute to remove. @@ -4430,7 +4165,7 @@ 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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -4443,7 +4178,7 @@ void igraph_cattribute_remove_v(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_e - * \brief Remove an edge attribute. + * Remove an edge attribute * * \param graph The graph object. * \param name Name of the edge attribute to remove. @@ -4455,7 +4190,7 @@ 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; - igraph_integer_t j; + long int j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -4468,7 +4203,7 @@ void igraph_cattribute_remove_e(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_all - * \brief Remove all graph/vertex/edge attributes. + * Remove all graph/vertex/edge attributes * * \param graph The graph object. * \param g Boolean, whether to remove graph attributes. @@ -4485,7 +4220,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, if (g) { igraph_vector_ptr_t *gal = &attr->gal; - igraph_integer_t i, n = igraph_vector_ptr_size(gal); + long int i, n = igraph_vector_ptr_size(gal); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*gal)[i]); } @@ -4493,7 +4228,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, } if (v) { igraph_vector_ptr_t *val = &attr->val; - igraph_integer_t i, n = igraph_vector_ptr_size(val); + long int i, n = igraph_vector_ptr_size(val); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*val)[i]); } @@ -4501,7 +4236,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, } if (e) { igraph_vector_ptr_t *eal = &attr->eal; - igraph_integer_t i, n = igraph_vector_ptr_size(eal); + long int i, n = igraph_vector_ptr_size(eal); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*eal)[i]); } diff --git a/src/vendor/cigraph/src/graph/graph_list.c b/src/vendor/cigraph/src/graph/graph_list.c deleted file mode 100644 index 9d82ec2901a..00000000000 --- a/src/vendor/cigraph/src/graph/graph_list.c +++ /dev/null @@ -1,61 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2022 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 - -*/ - -#include "igraph_graph_list.h" - -#include "igraph_error.h" -#include "igraph_interface.h" -#include "igraph_types.h" - -#define GRAPH_LIST -#define BASE_GRAPH -#define CUSTOM_INIT_DESTROY -#include "igraph_pmt.h" -#include "../core/typed_list.pmt" -#include "igraph_pmt_off.h" -#undef CUSTOM_INIT_DESTROY -#undef BASE_GRAPH -#undef GRAPH_LIST - -void igraph_graph_list_set_directed( - igraph_graph_list_t* list, igraph_bool_t directed -) { - IGRAPH_ASSERT(list != 0); - list->directed = directed; -} - -static igraph_error_t igraph_i_graph_list_init_item( - const igraph_graph_list_t* list, igraph_t* item -) { - return igraph_empty(item, 0, list->directed); -} - -static igraph_error_t igraph_i_graph_list_copy_item( - igraph_t* dest, const igraph_t* source -) { - return igraph_copy(dest, source); -} - -static void igraph_i_graph_list_destroy_item(igraph_t* item) { - igraph_destroy(item); -} diff --git a/src/vendor/cigraph/src/graph/iterators.c b/src/vendor/cigraph/src/graph/iterators.c index 566e1c19cd0..cdf39888af7 100644 --- a/src/vendor/cigraph/src/graph/iterators.c +++ b/src/vendor/cigraph/src/graph/iterators.c @@ -21,12 +21,9 @@ */ -#include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_iterators.h" #include "igraph_memory.h" #include "igraph_interface.h" -#include "igraph_types.h" #include #include @@ -94,9 +91,9 @@ * Time complexity: O(1). */ -igraph_error_t igraph_vs_all(igraph_vs_t *vs) { +int igraph_vs_all(igraph_vs_t *vs) { vs->type = IGRAPH_VS_ALL; - return IGRAPH_SUCCESS; + return 0; } /** @@ -151,12 +148,12 @@ igraph_vs_t igraph_vss_all(void) { * Time complexity: O(1). */ -igraph_error_t igraph_vs_adj(igraph_vs_t *vs, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -196,12 +193,12 @@ igraph_error_t igraph_vs_adj(igraph_vs_t *vs, * \example examples/simple/igraph_vs_nonadj.c */ -igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -217,9 +214,9 @@ igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, * Time complexity: O(1). */ -igraph_error_t igraph_vs_none(igraph_vs_t *vs) { +int igraph_vs_none(igraph_vs_t *vs) { vs->type = IGRAPH_VS_NONE; - return IGRAPH_SUCCESS; + return 0; } /** @@ -254,10 +251,10 @@ igraph_vs_t igraph_vss_none(void) { * Time complexity: O(1). */ -igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { +int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { vs->type = IGRAPH_VS_1; vs->data.vid = vid; - return IGRAPH_SUCCESS; + return 0; } /** @@ -284,7 +281,7 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * \function igraph_vs_vector * \brief Vertex set based on a vector. * - * This function makes it possible to handle an \type igraph_vector_int_t + * This function makes it possible to handle an \type igraph_vector_t * temporarily as a vertex selector. The vertex selector should be * thought of as a \em view into the vector. If you make changes to * the vector that also affects the vertex selector. Destroying the @@ -295,7 +292,7 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * the vector are valid. * * \param vs Pointer to an uninitialized vertex selector. - * \param v Pointer to a \type igraph_vector_int_t object. + * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_vss_vector(), \ref igraph_vs_destroy() * @@ -304,11 +301,11 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * \example examples/simple/igraph_vs_vector.c */ -igraph_error_t igraph_vs_vector(igraph_vs_t *vs, - const igraph_vector_int_t *v) { +int igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_t *v) { vs->type = IGRAPH_VS_VECTORPTR; vs->data.vecptr = v; - return IGRAPH_SUCCESS; + return 0; } /** @@ -317,7 +314,7 @@ igraph_error_t igraph_vs_vector(igraph_vs_t *vs, * * This is the immediate version of \ref igraph_vs_vector. * - * \param v Pointer to a \type igraph_vector_int_t object. + * \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() @@ -325,7 +322,7 @@ igraph_error_t igraph_vs_vector(igraph_vs_t *vs, * Time complexity: O(1). */ -igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v) { +igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { igraph_vs_t vecvs; vecvs.type = IGRAPH_VS_VECTORPTR; vecvs.data.vecptr = v; @@ -337,7 +334,7 @@ igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v) { * \brief Create a vertex set by giving its elements. * * This function can be used to create a vertex selector with a few - * of vertices. Do not forget to include a -1 after the + * 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. * @@ -347,23 +344,24 @@ igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v) { * large for \type int) vertex IDs here. * * \param vs Pointer to an uninitialized vertex selector object. - * \param ... Additional parameters, these will be the vertex IDs to + * \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. + * Time complexity: O(n), the number of vertex ids supplied. */ -igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...) { +int igraph_vs_vector_small(igraph_vs_t *vs, ...) { va_list ap; - igraph_integer_t i, n = 0; - igraph_vector_int_t* vec; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); - IGRAPH_FINALLY(igraph_free, vec); + 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) { @@ -375,27 +373,23 @@ igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...) { } va_end(ap); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vs->data.vecptr, n); va_start(ap, vs); for (i = 0; i < n; i++) { - VECTOR(*vec)[i] = va_arg(ap, int); + VECTOR(*vs->data.vecptr)[i] = (igraph_real_t) va_arg(ap, int); } va_end(ap); IGRAPH_FINALLY_CLEAN(2); - - vs->type = IGRAPH_VS_VECTOR; - vs->data.vecptr = vec; - - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vs_vector_copy * \brief Vertex set based on a vector, with copying. * - * This function makes it possible to handle an \type igraph_vector_int_t + * This function makes it possible to handle an \type igraph_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 @@ -405,119 +399,77 @@ igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...) { * the vertex IDs in the vector are valid. * * \param vs Pointer to an uninitialized vertex selector. - * \param v Pointer to a \type igraph_vector_int_t object. + * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_vs_destroy() * * Time complexity: O(1). */ -igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_int_t *v) { - igraph_vector_int_t* vec; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); - IGRAPH_FINALLY_CLEAN(1); - +int igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_t *v) { vs->type = IGRAPH_VS_VECTOR; - vs->data.vecptr = vec; - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_vs_range - * \brief Vertex set, an interval of vertices. - * - * Creates a vertex selector containing all vertices with vertex ID - * equal to or bigger than \p from and smaller than \p to. Note that the - * interval is closed from the left and open from the right, following C - * conventions. - * - * \param vs Pointer to an uninitialized vertex selector object. - * \param start The first vertex ID to be included in the vertex selector. - * \param end The first vertex ID \em not to be included in the vertex selector. - * \return Error code. - * \sa \ref igraph_vss_range(), \ref igraph_vs_destroy() - * - * Time complexity: O(1). - * - * \example examples/simple/igraph_vs_seq.c - */ - -igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end) { - *vs = igraph_vss_range(start, end); - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_vss_range - * \brief An interval of vertices (immediate version). - * - * The immediate version of \ref igraph_vs_range(). - * - * \param start The first vertex ID to be included in the vertex selector. - * \param end The first vertex ID \em not to be included in the vertex selector. - * \return Error code. - * \sa \ref igraph_vs_range() - * - * Time complexity: O(1). - */ - -igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end) { - igraph_vs_t vs; - vs.type = IGRAPH_VS_RANGE; - vs.data.range.start = start; - vs.data.range.end = end; - return vs; + 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 with inclusive endpoints (deprecated). + * \brief Vertex set, an interval of vertices. * * Creates a vertex selector containing all vertices with vertex ID - * equal to or bigger than \p from and equal to or smaller than \p to. - * Note that both endpoints are inclusive, contrary to C conventions. - * - * \deprecated-by igraph_vs_range 0.10.0 + * equal to or bigger than \p from and equal to or smaller than \p + * 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. + * \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_range(), \ref igraph_vss_seq(), \ref igraph_vs_destroy() + * \sa \ref igraph_vss_seq(), \ref igraph_vs_destroy() * * Time complexity: O(1). * * \example examples/simple/igraph_vs_seq.c */ -igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to) { - *vs = igraph_vss_range(from, to + 1); - return IGRAPH_SUCCESS; +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; + return 0; } /** * \function igraph_vss_seq - * \brief An interval of vertices with inclusive endpoints (immediate version, deprecated). + * \brief An interval of vertices (immediate version). * * The immediate version of \ref igraph_vs_seq(). * - * \deprecated-by igraph_vss_range 0.10.0 - * - * \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. + * \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_range(), \ref igraph_vs_seq() + * \sa \ref igraph_vs_seq() * * Time complexity: O(1). */ igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { - return igraph_vss_range(from, to + 1); + igraph_vs_t vs; + vs.type = IGRAPH_VS_SEQ; + vs.data.seq.from = from; + vs.data.seq.to = to; + return vs; } /** @@ -542,11 +494,11 @@ void igraph_vs_destroy(igraph_vs_t *vs) { case IGRAPH_VS_NONE: case IGRAPH_VS_1: case IGRAPH_VS_VECTORPTR: - case IGRAPH_VS_RANGE: + case IGRAPH_VS_SEQ: case IGRAPH_VS_NONADJ: break; case IGRAPH_VS_VECTOR: - igraph_vector_int_destroy((igraph_vector_int_t*) vs->data.vecptr); + igraph_vector_destroy((igraph_vector_t*)vs->data.vecptr); IGRAPH_FREE(vs->data.vecptr); break; default: @@ -562,11 +514,11 @@ void igraph_vs_destroy(igraph_vs_t *vs) { * 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 \c false. + * value will be \c FALSE. * * \param vs Pointer to a vertex selector object. - * \return \c true if the vertex selector contains all vertices and - * \c false otherwise. + * \return \c TRUE (1) if the vertex selector contains all vertices and + * \c FALSE (0) otherwise. * * Time complexity: O(1). */ @@ -575,8 +527,8 @@ igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs) { return vs->type == IGRAPH_VS_ALL; } -igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, - igraph_vector_int_t *v) { +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)); @@ -585,7 +537,7 @@ igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -595,32 +547,26 @@ igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, * \param src The selector being copied. * \param dest An uninitialized selector that will contain the copy. */ -igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) { - igraph_vector_int_t *vec; - +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: - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot copy vertex selector."); - IGRAPH_FINALLY(igraph_free, &vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); - dest->data.vecptr = vec; - IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ - break; - default: + 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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_vs_type * \brief Returns the type of the vertex selector. */ -igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs) { +int igraph_vs_type(const igraph_vs_t *vs) { return vs->type; } @@ -634,62 +580,62 @@ igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs) { * \param graph The graph over which we will iterate. * \param result The result will be returned here. */ -igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, +int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result) { - igraph_vector_int_t vec; + igraph_vector_t vec; igraph_bool_t *seen; - igraph_integer_t i; - igraph_integer_t vec_len; + long i; switch (vs->type) { case IGRAPH_VS_NONE: - *result = 0; return IGRAPH_SUCCESS; + *result = 0; return 0; case IGRAPH_VS_1: *result = 0; if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) { *result = 1; } - return IGRAPH_SUCCESS; + return 0; - case IGRAPH_VS_RANGE: - *result = vs->data.range.end - vs->data.range.start; - return IGRAPH_SUCCESS; + case IGRAPH_VS_SEQ: + *result = vs->data.seq.to - vs->data.seq.from + 1; + return 0; case IGRAPH_VS_ALL: - *result = igraph_vcount(graph); return IGRAPH_SUCCESS; + *result = igraph_vcount(graph); return 0; case IGRAPH_VS_ADJ: - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); - *result = igraph_vector_int_size(&vec); - igraph_vector_int_destroy(&vec); + *result = (igraph_integer_t) igraph_vector_size(&vec); + igraph_vector_destroy(&vec); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_VS_NONADJ: - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); - vec_len = igraph_vector_int_size(&vec); *result = igraph_vcount(graph); seen = IGRAPH_CALLOC(*result, igraph_bool_t); - IGRAPH_CHECK_OOM(seen, "Cannot calculate vertex selector length."); + if (seen == 0) { + IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, seen); - for (i = 0; i < vec_len; i++) { - if (!seen[ VECTOR(vec)[i] ]) { + for (i = 0; i < igraph_vector_size(&vec); i++) { + if (!seen[(long int)VECTOR(vec)[i]]) { (*result)--; - seen[ VECTOR(vec)[i] ] = 1; + seen[(long int)VECTOR(vec)[i]] = 1; } } igraph_free(seen); - igraph_vector_int_destroy(&vec); + igraph_vector_destroy(&vec); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_VS_VECTOR: case IGRAPH_VS_VECTORPTR: - *result = igraph_vector_int_size(vs->data.vecptr); - return IGRAPH_SUCCESS; + *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)vs->data.vecptr); + return 0; } IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", @@ -703,12 +649,12 @@ igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, * \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 + * 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 + * this contains the actual vertex ids in the graph supplied as a * parameter. * * @@ -724,92 +670,85 @@ igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, * 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_range(), \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 + * 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. */ -igraph_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit) { - igraph_vector_int_t vec; - igraph_vector_int_t *vec_int; +int igraph_vit_create(const igraph_t *graph, + igraph_vs_t vs, igraph_vit_t *vit) { + igraph_vector_t vec; igraph_bool_t *seen; - igraph_integer_t i, j, n; - igraph_integer_t vec_len; + long int i, j, n; switch (vs.type) { case IGRAPH_VS_ALL: - vit->type = IGRAPH_VIT_RANGE; + vit->type = IGRAPH_VIT_SEQ; vit->pos = 0; vit->start = 0; vit->end = igraph_vcount(graph); break; case IGRAPH_VS_ADJ: - vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); - IGRAPH_FINALLY(igraph_free, vec_int); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); - n = igraph_vector_int_size(&vec); - IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); - for (i = 0; i < n; i++) { - VECTOR(*vec_int)[i] = VECTOR(vec)[i]; - } - - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(3); - vit->type = IGRAPH_VIT_VECTOR; vit->pos = 0; vit->start = 0; - vit->vec = vec_int; - vit->end = n; - + 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: - vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); - IGRAPH_FINALLY(igraph_free, vec_int); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); - vec_len = igraph_vector_int_size(&vec); + 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); - IGRAPH_CHECK_OOM(seen, "Cannot create vertex iterator."); + if (seen == 0) { + IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); + } IGRAPH_FINALLY(igraph_free, seen); - for (i = 0; i < vec_len; i++) { - if (! seen [ VECTOR(vec)[i] ] ) { + for (i = 0; i < igraph_vector_size(&vec); i++) { + if (! seen [ (long int) VECTOR(vec)[i] ] ) { n--; - seen[ VECTOR(vec)[i] ] = 1; + seen[ (long int) VECTOR(vec)[i] ] = 1; } } - IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); + IGRAPH_CHECK(igraph_vector_resize((igraph_vector_t*)vit->vec, n)); for (i = 0, j = 0; j < n; i++) { if (!seen[i]) { - VECTOR(*vec_int)[j++] = i; + VECTOR(*vit->vec)[j++] = i; } } IGRAPH_FREE(seen); - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(4); - - vit->type = IGRAPH_VIT_VECTOR; - vit->pos = 0; - vit->start = 0; - vit->vec = vec_int; + igraph_vector_destroy(&vec); vit->end = n; + IGRAPH_FINALLY_CLEAN(4); break; case IGRAPH_VS_NONE: - vit->type = IGRAPH_VIT_RANGE; + vit->type = IGRAPH_VIT_SEQ; vit->pos = 0; vit->start = 0; vit->end = 0; break; case IGRAPH_VS_1: - vit->type = IGRAPH_VIT_RANGE; + vit->type = IGRAPH_VIT_SEQ; vit->pos = vs.data.vid; vit->start = vs.data.vid; vit->end = vs.data.vid + 1; @@ -823,33 +762,28 @@ igraph_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_v vit->pos = 0; vit->start = 0; vit->vec = vs.data.vecptr; - vit->end = igraph_vector_int_size(vit->vec); - if (!igraph_vector_int_isininterval(vit->vec, 0, igraph_vcount(graph) - 1)) { + 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_RANGE: - { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - if (vs.data.range.start < 0 || - vs.data.range.start > no_of_nodes || - (no_of_nodes > 0 && vs.data.range.start == no_of_nodes)) { - IGRAPH_ERROR("Cannot create range iterator, starting vertex ID out of range.", IGRAPH_EINVAL); - } - if (vs.data.range.end < 0 || vs.data.range.end > no_of_nodes) { - IGRAPH_ERROR("Cannot create range iterator, ending vertex ID out of range.", IGRAPH_EINVAL); - } + case IGRAPH_VS_SEQ: + if (vs.data.seq.from < 0 || vs.data.seq.from >= igraph_vcount(graph)) { + IGRAPH_ERROR("Cannot create sequence iterator, starting vertex ID out of range.", IGRAPH_EINVAL); + } + if (vs.data.seq.to < 0 || vs.data.seq.to >= igraph_vcount(graph)) { + IGRAPH_ERROR("Cannot create sequence iterator, ending vertex ID out of range.", IGRAPH_EINVAL); } - vit->type = IGRAPH_VIT_RANGE; - vit->pos = vs.data.range.start; - vit->start = vs.data.range.start; - vit->end = vs.data.range.end; + vit->type = IGRAPH_VIT_SEQ; + vit->pos = vs.data.seq.from; + vit->start = vs.data.seq.from; + vit->end = vs.data.seq.to + 1; break; default: - IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL); break; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -867,12 +801,12 @@ igraph_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_v void igraph_vit_destroy(const igraph_vit_t *vit) { switch (vit->type) { - case IGRAPH_VIT_RANGE: + case IGRAPH_VIT_SEQ: case IGRAPH_VIT_VECTORPTR: break; case IGRAPH_VIT_VECTOR: - igraph_vector_int_destroy((igraph_vector_int_t*) vit->vec); - igraph_free((igraph_vector_int_t*) vit->vec); + 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); */ @@ -880,13 +814,14 @@ void igraph_vit_destroy(const igraph_vit_t *vit) { } } -igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v) { - igraph_integer_t i; +int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { + + long int i; - IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_VIT_SIZE(*vit))); + IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_VIT_SIZE(*vit))); switch (vit->type) { - case IGRAPH_VIT_RANGE: + case IGRAPH_VIT_SEQ: for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) { VECTOR(*v)[i] = vit->start + i; } @@ -903,7 +838,7 @@ igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t break; } - return IGRAPH_SUCCESS; + return 0; } /*******************************************************/ @@ -916,7 +851,7 @@ igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t * \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 + * \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 @@ -929,7 +864,7 @@ igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t * Time complexity: O(1). */ -igraph_error_t igraph_es_all(igraph_es_t *es, +int igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order) { switch (order) { case IGRAPH_EDGEORDER_ID: @@ -945,12 +880,12 @@ igraph_error_t igraph_es_all(igraph_es_t *es, IGRAPH_ERROR("Invalid edge order, cannot create selector.", IGRAPH_EINVAL); break; } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_ess_all - * \brief Edge set, all edges (immediate version). + * \brief Edge set, all edges (immediate version) * * The immediate version of the all-edges selector. * @@ -986,12 +921,12 @@ igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order) { * Time complexity: O(1). */ -igraph_error_t igraph_es_incident(igraph_es_t *es, +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 IGRAPH_SUCCESS; + return 0; } /** @@ -1006,9 +941,9 @@ igraph_error_t igraph_es_incident(igraph_es_t *es, * Time complexity: O(1). */ -igraph_error_t igraph_es_none(igraph_es_t *es) { +int igraph_es_none(igraph_es_t *es) { es->type = IGRAPH_ES_NONE; - return IGRAPH_SUCCESS; + return 0; } /** @@ -1042,10 +977,10 @@ igraph_es_t igraph_ess_none(void) { * Time complexity: O(1). */ -igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { +int igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { es->type = IGRAPH_ES_1; es->data.eid = eid; - return IGRAPH_SUCCESS; + return 0; } /** @@ -1084,17 +1019,18 @@ igraph_es_t igraph_ess_1(igraph_integer_t eid) { * Time complexity: O(1). */ -igraph_error_t igraph_es_vector(igraph_es_t *es, const igraph_vector_int_t *v) { +int igraph_es_vector(igraph_es_t *es, + const igraph_vector_t *v) { es->type = IGRAPH_ES_VECTORPTR; es->data.vecptr = v; - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_es_vector_copy * \brief Edge set, based on a vector, with copying. * - * This function makes it possible to handle an \type igraph_vector_int_t + * This function makes it possible to handle an \type igraph_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 @@ -1104,43 +1040,40 @@ igraph_error_t igraph_es_vector(igraph_es_t *es, const igraph_vector_int_t *v) { * whether the edge IDs in the vector are valid. * * \param es Pointer to an uninitialized edge selector. - * \param v Pointer to a \type igraph_vector_int_t object. + * \param v Pointer to a \type igraph_vector_t object. * \return Error code. * \sa \ref igraph_es_destroy() * * Time complexity: O(1). */ -igraph_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v) { - igraph_vector_int_t* vec; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); - IGRAPH_FINALLY_CLEAN(1); - +int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v) { es->type = IGRAPH_ES_VECTOR; - es->data.vecptr = vec; - - return IGRAPH_SUCCESS; + 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 + * This is the immediate version of the vector of edge ids edge * selector. * - * \param v The vector of edge IDs. + * \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_int_t *v) { +igraph_es_t igraph_ess_vector(const igraph_vector_t *v) { igraph_es_t es; es.type = IGRAPH_ES_VECTORPTR; es.data.vecptr = v; @@ -1148,57 +1081,37 @@ igraph_es_t igraph_ess_vector(const igraph_vector_int_t *v) { } /** - * \function igraph_es_range - * \brief Edge selector, a sequence of edge IDs. + * \function igraph_es_fromto + * \brief Edge selector, all edges between two vertex sets. * - * Creates an edge selector containing all edges with edge ID - * equal to or bigger than \p from and smaller than \p to. Note that the - * interval is closed from the left and open from the right, following C - * conventions. + * This function is not implemented yet. * - * \param vs Pointer to an uninitialized edge selector object. - * \param start The first edge ID to be included in the edge selector. - * \param end The first edge ID \em not to be included in the edge selector. + * \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_ess_range(), \ref igraph_es_destroy() + * \sa \ref igraph_es_destroy() * * Time complexity: O(1). */ -igraph_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t start, igraph_integer_t end) { - *es = igraph_ess_range(start, end); - return IGRAPH_SUCCESS; -} +int igraph_es_fromto(igraph_es_t *es, + igraph_vs_t from, igraph_vs_t to) { -/** - * \function igraph_ess_range - * \brief Immediate version of the sequence edge selector. - * - * \param start The first edge ID to be included in the edge selector. - * \param end The first edge ID \em not to be included in the edge selector. - * \return The initialized edge selector. - * \sa \ref igraph_es_range() - * - * Time complexity: O(1). - */ - -igraph_es_t igraph_ess_range(igraph_integer_t start, igraph_integer_t end) { - igraph_es_t es; - es.type = IGRAPH_ES_RANGE; - es.data.range.start = start; - es.data.range.end = end; - return es; + 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, with inclusive endpoints (deprecated). + * \brief Edge selector, a sequence of edge ids. * - * All edge IDs between \p from and \p to (inclusive) will be + * All edge ids between \p from and \p to (inclusive) will be * included in the edge selection. * - * \deprecated-by igraph_es_range 0.10.0 - * * \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. @@ -1208,16 +1121,17 @@ igraph_es_t igraph_ess_range(igraph_integer_t start, igraph_integer_t end) { * Time complexity: O(1). */ -igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to) { - *es = igraph_ess_range(from, to + 1); - return IGRAPH_SUCCESS; +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, with inclusive endpoints. - * - * \deprecated-by igraph_ess_range 0.10.0 + * \brief Immediate version of the sequence edge selector. * * \param from The first edge ID to include. * \param to The last edge ID to include. @@ -1228,7 +1142,11 @@ igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_inte */ igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) { - return igraph_ess_range(from, to + 1); + igraph_es_t es; + es.type = IGRAPH_ES_SEQ; + es.data.seq.from = from; + es.data.seq.to = to; + return es; } /** @@ -1253,21 +1171,20 @@ igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) { * \example examples/simple/igraph_es_pairs.c */ -igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, +int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed) { - igraph_vector_int_t* vec; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); - IGRAPH_FINALLY_CLEAN(1); - es->type = IGRAPH_ES_PAIRS; es->data.path.mode = directed; - es->data.path.ptr = vec; + 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)); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(1); + return 0; } /** @@ -1281,64 +1198,66 @@ igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, * 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. * - * - * 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 es Pointer to an uninitialized edge selector object. * \param directed Whether the graph is directed or not. - * \param ... The additional arguments give the edges to be included in the - * selector, as pairs of vertex IDs. The last argument must be -1. - * The \p first parameter is present for technical reasons and represents - * the first variadic argument. * \return Error code. * \sa \ref igraph_es_pairs(), \ref igraph_es_destroy() * * Time complexity: O(n), the number of edges being selected. */ -igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { +int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...) { va_list ap; - igraph_integer_t i, n = 0; - igraph_vector_int_t *vec; - int num; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); - IGRAPH_FINALLY(igraph_free, vec); + 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, first); - num = first; - while (num != -1) { + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } n++; - num = va_arg(ap, int); } va_end(ap); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); - if (n > 0) { - va_start(ap, first); - VECTOR(*vec)[0] = first; - for (i = 1; i < n; i++) { - VECTOR(*vec)[i] = va_arg(ap, int); - } - va_end(ap); + 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; +} - es->type = IGRAPH_ES_PAIRS; +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 = vec; + 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); - return IGRAPH_SUCCESS; + 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. + * \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 @@ -1354,87 +1273,53 @@ igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, in * * Time complexity: O(n), the number of vertices. */ -igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, +int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, igraph_bool_t directed) { - igraph_vector_int_t *vec; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); - IGRAPH_FINALLY_CLEAN(1); - es->type = IGRAPH_ES_PATH; es->data.path.mode = directed; - es->data.path.ptr = vec; + 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)); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(1); + return 0; } -igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { +int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...) { va_list ap; - igraph_integer_t i, n = 0; - igraph_vector_int_t *vec; - int num; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); - IGRAPH_FINALLY(igraph_free, vec); + 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, first); - num = first; - while (num != -1) { + va_start(ap, directed); + while (1) { + int num = va_arg(ap, int); + if (num == -1) { + break; + } n++; - num = va_arg(ap, int); } va_end(ap); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); + IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); - if (n > 0) { - va_start(ap, first); - VECTOR(*vec)[0] = first; - for (i = 1; i < n; i++) { - VECTOR(*vec)[i] = va_arg(ap, int); - } - va_end(ap); + 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); - - es->type = IGRAPH_ES_PATH; - es->data.path.mode = directed; - es->data.path.ptr = vec; - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_es_all_between - * \brief Edge selector, all edge IDs between a pair of vertices. - * - * This function takes a pair of vertices and creates a selector that matches - * all edges between those vertices. - * - * \param es Pointer to an uninitialized edge selector object. - * \param from The ID of the source vertex. - * \param to The ID of the target vertex. - * \param direectd 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(1). - */ -IGRAPH_EXPORT igraph_error_t igraph_es_all_between( - igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, - igraph_bool_t directed -) { - es->type = IGRAPH_ES_ALL_BETWEEN; - es->data.between.from = from; - es->data.between.to = to; - es->data.between.directed = directed; - return IGRAPH_SUCCESS; + return 0; } /** @@ -1459,16 +1344,16 @@ void igraph_es_destroy(igraph_es_t *es) { case IGRAPH_ES_NONE: case IGRAPH_ES_1: case IGRAPH_ES_VECTORPTR: - case IGRAPH_ES_RANGE: - case IGRAPH_ES_ALL_BETWEEN: + case IGRAPH_ES_SEQ: break; case IGRAPH_ES_VECTOR: - igraph_vector_int_destroy((igraph_vector_int_t*)es->data.vecptr); + igraph_vector_destroy((igraph_vector_t*)es->data.vecptr); IGRAPH_FREE(es->data.vecptr); break; case IGRAPH_ES_PAIRS: case IGRAPH_ES_PATH: - igraph_vector_int_destroy((igraph_vector_int_t*)es->data.path.ptr); + case IGRAPH_ES_MULTIPAIRS: + igraph_vector_destroy((igraph_vector_t*)es->data.path.ptr); IGRAPH_FREE(es->data.path.ptr); break; default: @@ -1481,8 +1366,8 @@ void igraph_es_destroy(igraph_es_t *es) { * \brief Check whether an edge selector includes all edges. * * \param es Pointer to an edge selector object. - * \return \c true if \p es was created with \ref - * igraph_es_all() or \ref igraph_ess_all(), and \c false otherwise. + * \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). */ @@ -1498,32 +1383,29 @@ igraph_bool_t igraph_es_is_all(const igraph_es_t *es) { * \param dest An uninitialized selector that will contain the copy. * \sa \ref igraph_es_destroy() */ -igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { - igraph_vector_int_t *vec; - +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: - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); - IGRAPH_FINALLY(igraph_free, &vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); - dest->data.vecptr = vec; - IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + 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: - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); - IGRAPH_FINALLY(igraph_free, &vec); - IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.path.ptr)); - dest->data.path.ptr = vec; - IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ - break; - default: + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -1541,8 +1423,8 @@ igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { * * Time complexity: O(n), the number of edges in the selector. */ -igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, - igraph_vector_int_t *v) { +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)); @@ -1551,23 +1433,23 @@ igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_es_type * \brief Returns the type of the edge selector. */ -igraph_es_type_t igraph_es_type(const igraph_es_t *es) { +int igraph_es_type(const igraph_es_t *es) { return es->type; } -static igraph_error_t igraph_i_es_pairs_size(const igraph_t *graph, +static int igraph_i_es_pairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); -static igraph_error_t igraph_i_es_path_size(const igraph_t *graph, - const igraph_es_t *es, igraph_integer_t *result); -static igraph_error_t igraph_i_es_all_between_size(const igraph_t *graph, +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 @@ -1579,35 +1461,35 @@ static igraph_error_t igraph_i_es_all_between_size(const igraph_t *graph, * \param graph The graph over which we will iterate. * \param result The result will be returned here. */ -igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, +int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { - igraph_vector_int_t v; + igraph_vector_t v; switch (es->type) { case IGRAPH_ES_ALL: *result = igraph_ecount(graph); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_ALLFROM: *result = igraph_ecount(graph); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_ALLTO: *result = igraph_ecount(graph); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_INCIDENT: - IGRAPH_VECTOR_INT_INIT_FINALLY(&v, 0); + IGRAPH_VECTOR_INIT_FINALLY(&v, 0); IGRAPH_CHECK(igraph_incident(graph, &v, es->data.incident.vid, es->data.incident.mode)); - *result = igraph_vector_int_size(&v); - igraph_vector_int_destroy(&v); + *result = (igraph_integer_t) igraph_vector_size(&v); + igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_NONE: *result = 0; - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_1: if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) { @@ -1615,28 +1497,28 @@ igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, } else { *result = 0; } - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_VECTOR: case IGRAPH_ES_VECTORPTR: - *result = igraph_vector_int_size(es->data.vecptr); - return IGRAPH_SUCCESS; + *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)es->data.vecptr); + return 0; - case IGRAPH_ES_RANGE: - *result = es->data.range.end - es->data.range.start; - return IGRAPH_SUCCESS; + case IGRAPH_ES_SEQ: + *result = es->data.seq.to - es->data.seq.from + 1; + return 0; case IGRAPH_ES_PAIRS: IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result)); - return IGRAPH_SUCCESS; + return 0; case IGRAPH_ES_PATH: IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result)); - return IGRAPH_SUCCESS; + return 0; - case IGRAPH_ES_ALL_BETWEEN: - IGRAPH_CHECK(igraph_i_es_all_between_size(graph, es, result)); - return IGRAPH_SUCCESS; + 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.", @@ -1644,133 +1526,126 @@ igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, } } -static igraph_error_t igraph_i_es_pairs_size(const igraph_t *graph, +static int igraph_i_es_pairs_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { - igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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_int_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { IGRAPH_ERROR("Cannot calculate edge selector length.", IGRAPH_EINVVID); } - *result = n / 2; + *result = (igraph_integer_t) (n / 2); /* Check for the existence of all edges */ for (i = 0; i < *result; i++) { - igraph_integer_t from = VECTOR(*es->data.path.ptr)[2 * i]; - igraph_integer_t to = VECTOR(*es->data.path.ptr)[2 * i + 1]; + 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, from, to, es->data.path.mode, + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es->data.path.mode, /*error=*/ 1)); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_es_path_size(const igraph_t *graph, +static int igraph_i_es_path_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { - igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int n = igraph_vector_size(es->data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i; - if (!igraph_vector_int_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + 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 = n - 1; + *result = (igraph_integer_t) (n - 1); } for (i = 0; i < *result; i++) { - igraph_integer_t from = VECTOR(*es->data.path.ptr)[i]; - igraph_integer_t to = VECTOR(*es->data.path.ptr)[i + 1]; + 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, from, to, es->data.path.mode, + IGRAPH_CHECK(igraph_get_eid(graph, &eid, (igraph_integer_t) from, + (igraph_integer_t) to, es->data.path.mode, /*error=*/ 1)); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_es_all_between_size(const igraph_t *graph, - const igraph_es_t *es, igraph_integer_t *result) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t from = es->data.between.from; - igraph_integer_t to = es->data.between.to; - igraph_bool_t directed = es->data.between.directed; - igraph_vector_int_t vec; - - if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { - IGRAPH_ERROR("Cannot calculate selector length.", IGRAPH_EINVVID); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - IGRAPH_CHECK(igraph_get_all_eids_between(graph, &vec, from, to, directed)); - *result = igraph_vector_int_size(&vec); - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; +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 igraph_error_t igraph_i_eit_create_allfromto(const igraph_t *graph, +static int igraph_i_eit_create_allfromto(const igraph_t *graph, igraph_eit_t *eit, igraph_neimode_t mode); -static igraph_error_t igraph_i_eit_create_incident(const igraph_t* graph, +static int igraph_i_eit_pairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); -static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, - igraph_es_t es, igraph_eit_t *eit); -static igraph_error_t igraph_i_eit_path(const igraph_t *graph, +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 igraph_error_t igraph_i_eit_create_allfromto(const igraph_t *graph, +static int igraph_i_eit_create_allfromto(const igraph_t *graph, igraph_eit_t *eit, igraph_neimode_t mode) { - igraph_vector_int_t *vec; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, length; - - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + 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_INT_INIT_FINALLY(vec, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(vec, no_of_edges)); + IGRAPH_VECTOR_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_vector_reserve(vec, no_of_edges)); if (igraph_is_directed(graph)) { - igraph_vector_int_t adj; - IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + igraph_vector_t adj; + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); for (i = 0; i < no_of_nodes; i++) { - IGRAPH_CHECK(igraph_incident(graph, &adj, i, mode)); - igraph_vector_int_append(vec, &adj); /* reserved */ + igraph_incident(graph, &adj, (igraph_integer_t) i, mode); + igraph_vector_append(vec, &adj); } - igraph_vector_int_destroy(&adj); + igraph_vector_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); } else { - igraph_vector_int_t adj; + igraph_vector_t adj; igraph_bool_t *added; - IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + long int j; + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); added = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); - IGRAPH_CHECK_OOM(added, "Cannot create edge iterator."); + 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_CHECK(igraph_incident(graph, &adj, i, IGRAPH_ALL)); - length = igraph_vector_int_size(&adj); - for (j = 0; j < length; j++) { - if (!added[ VECTOR(adj)[j] ]) { - igraph_vector_int_push_back(vec, VECTOR(adj)[j]); /* reserved */ - added[ VECTOR(adj)[j] ] += 1; + 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_int_destroy(&adj); + igraph_vector_destroy(&adj); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(2); } @@ -1779,92 +1654,90 @@ static igraph_error_t igraph_i_eit_create_allfromto(const igraph_t *graph, eit->pos = 0; eit->start = 0; eit->vec = vec; - eit->end = igraph_vector_int_size(eit->vec); + eit->end = igraph_vector_size(eit->vec); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eit_create_incident(const igraph_t* graph, +static int igraph_i_eit_pairs(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { - igraph_vector_int_t vec; - igraph_vector_int_t* vec_int; - igraph_integer_t i, n; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - IGRAPH_CHECK(igraph_incident(graph, &vec, es.data.incident.vid, es.data.incident.mode)); + long int n = igraph_vector_size(es.data.path.ptr); + long int no_of_nodes = igraph_vcount(graph); + long int i; - vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec_int, "Cannot create edge iterator."); - IGRAPH_FINALLY(igraph_free, vec_int); - - n = igraph_vector_int_size(&vec); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, n); - - for (i = 0; i < n; i++) { - VECTOR(*vec_int)[i] = VECTOR(vec)[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); } - - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(3); eit->type = IGRAPH_EIT_VECTOR; eit->pos = 0; eit->start = 0; - eit->vec = vec_int; - eit->end = igraph_vector_int_size(vec_int); + 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; + } - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(2); + return 0; } -static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, - igraph_es_t es, igraph_eit_t *eit) { - igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; - igraph_vector_int_t* vec; +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_int_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); } - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n / 2); - - for (i = 0; i < n / 2; i++) { - igraph_integer_t from = VECTOR(*es.data.path.ptr)[2 * i]; - igraph_integer_t to = VECTOR(*es.data.path.ptr)[2 * i + 1]; - igraph_integer_t eid; - IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, - /*error=*/ 1)); - VECTOR(*vec)[i] = eid; - } - - IGRAPH_FINALLY_CLEAN(2); - eit->type = IGRAPH_EIT_VECTOR; eit->pos = 0; eit->start = 0; eit->end = n / 2; - eit->vec = vec; + 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)); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(2); + return 0; } -static igraph_error_t igraph_i_eit_path(const igraph_t *graph, +static int igraph_i_eit_path(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { - igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, len; - igraph_vector_int_t* vec; + 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_int_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); } @@ -1874,59 +1747,30 @@ static igraph_error_t igraph_i_eit_path(const igraph_t *graph, len = n - 1; } - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); - IGRAPH_FINALLY(igraph_free, vec); - - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, len); - - for (i = 0; i < len; i++) { - igraph_integer_t from = VECTOR(*es.data.path.ptr)[i]; - igraph_integer_t to = VECTOR(*es.data.path.ptr)[i + 1]; - igraph_integer_t eid; - IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, - /*error=*/ 1)); - VECTOR(*vec)[i] = eid; - } - - IGRAPH_FINALLY_CLEAN(2); - eit->type = IGRAPH_EIT_VECTOR; eit->pos = 0; eit->start = 0; eit->end = len; - eit->vec = vec; + 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); - return IGRAPH_SUCCESS; -} + IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *)eit->vec, len); -static igraph_error_t igraph_i_eit_all_between( - const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit -) { - igraph_integer_t from = es.data.between.from; - igraph_integer_t to = es.data.between.to; - igraph_bool_t directed = es.data.between.directed; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t* vec; - - if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { - IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); + 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; } - vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_VECTOR_INT_INIT_FINALLY(vec, 0); - IGRAPH_CHECK(igraph_get_all_eids_between(graph, vec, from, to, directed)); IGRAPH_FINALLY_CLEAN(2); - - eit->type = IGRAPH_EIT_VECTOR; - eit->pos = 0; - eit->start = 0; - eit->end = igraph_vector_int_size(vec); - eit->vec = vec; - - return IGRAPH_SUCCESS; + return 0; } /** @@ -1950,15 +1794,16 @@ static igraph_error_t igraph_i_eit_all_between( * * 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(), \ref igraph_es_vector(), \ref igraph_es_seq() it is + * \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. */ -igraph_error_t igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit) { +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_RANGE; + eit->type = IGRAPH_EIT_SEQ; eit->pos = 0; eit->start = 0; eit->end = igraph_ecount(graph); @@ -1970,16 +1815,28 @@ igraph_error_t igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_e IGRAPH_CHECK(igraph_i_eit_create_allfromto(graph, eit, IGRAPH_IN)); break; case IGRAPH_ES_INCIDENT: - IGRAPH_CHECK(igraph_i_eit_create_incident(graph, es, eit)); + 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_RANGE; + eit->type = IGRAPH_EIT_SEQ; eit->pos = 0; eit->start = 0; eit->end = 0; break; case IGRAPH_ES_1: - eit->type = IGRAPH_EIT_RANGE; + eit->type = IGRAPH_EIT_SEQ; eit->pos = es.data.eid; eit->start = es.data.eid; eit->end = es.data.eid + 1; @@ -1993,42 +1850,37 @@ igraph_error_t igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_e eit->pos = 0; eit->start = 0; eit->vec = es.data.vecptr; - eit->end = igraph_vector_int_size(eit->vec); - if (!igraph_vector_int_isininterval(eit->vec, 0, igraph_ecount(graph) - 1)) { + 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_RANGE: - { - igraph_integer_t no_of_edges = igraph_ecount(graph); - if (es.data.range.start < 0 || - es.data.range.start > no_of_edges || - (no_of_edges > 0 && es.data.range.start == no_of_edges)) { - IGRAPH_ERROR("Cannot create range iterator, starting edge ID out of range.", IGRAPH_EINVAL); - } - if (es.data.range.end < 0 || es.data.range.end > no_of_edges) { - IGRAPH_ERROR("Cannot create range iterator, ending edge ID out of range.", IGRAPH_EINVAL); - } + case IGRAPH_ES_SEQ: + if (es.data.seq.from < 0 || es.data.seq.from >= igraph_ecount(graph)) { + IGRAPH_ERROR("Cannot create sequence iterator, starting edge ID out of range.", IGRAPH_EINVAL); } - eit->type = IGRAPH_EIT_RANGE; - eit->pos = es.data.range.start; - eit->start = es.data.range.start; - eit->end = es.data.range.end; + if (es.data.seq.to < 0 || es.data.seq.to >= igraph_ecount(graph)) { + IGRAPH_ERROR("Cannot create sequence iterator, ending edge ID out of range.", IGRAPH_EINVAL); + } + eit->type = IGRAPH_EIT_SEQ; + eit->pos = es.data.seq.from; + eit->start = es.data.seq.from; + eit->end = es.data.seq.to + 1; 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; - case IGRAPH_ES_ALL_BETWEEN: - IGRAPH_CHECK(igraph_i_eit_all_between(graph, es, eit)); - break; default: IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); break; } - return IGRAPH_SUCCESS; + return 0; } /** @@ -2043,12 +1895,12 @@ igraph_error_t igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_e void igraph_eit_destroy(const igraph_eit_t *eit) { switch (eit->type) { - case IGRAPH_EIT_RANGE: + case IGRAPH_EIT_SEQ: case IGRAPH_EIT_VECTORPTR: break; case IGRAPH_EIT_VECTOR: - igraph_vector_int_destroy((igraph_vector_int_t*)eit->vec); - igraph_free((igraph_vector_int_t*)eit->vec); + 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); */ @@ -2056,14 +1908,14 @@ void igraph_eit_destroy(const igraph_eit_t *eit) { } } -igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v) { +int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) { - igraph_integer_t i; + long int i; - IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_EIT_SIZE(*eit))); + IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_EIT_SIZE(*eit))); switch (eit->type) { - case IGRAPH_EIT_RANGE: + case IGRAPH_EIT_SEQ: for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) { VECTOR(*v)[i] = eit->start + i; } @@ -2080,5 +1932,5 @@ igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t break; } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/graph/internal.h b/src/vendor/cigraph/src/graph/neighbors.h similarity index 55% rename from src/vendor/cigraph/src/graph/internal.h rename to src/vendor/cigraph/src/graph/neighbors.h index 1e5d2620c1b..3ff36476795 100644 --- a/src/vendor/cigraph/src/graph/internal.h +++ b/src/vendor/cigraph/src/graph/neighbors.h @@ -16,27 +16,26 @@ along with this program. If not, see . */ -#ifndef IGRAPH_GRAPH_INTERNAL_H -#define IGRAPH_GRAPH_INTERNAL_H +#ifndef IGRAPH_NEIGHBORS_H +#define IGRAPH_NEIGHBORS_H -#include "igraph_datatype.h" -#include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_error.h" -#include "igraph_vector.h" __BEGIN_DECLS -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_neighbors( - const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t pnode, - igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple); +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 igraph_error_t igraph_i_incident( - const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, - igraph_neimode_t mode, igraph_loops_t loops); - -igraph_error_t igraph_i_reverse(igraph_t *graph); +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); __END_DECLS -#endif /* IGRAPH_GRAPH_INTERNAL_H */ +#endif /* IGRAPH_NEIGHBORS_H */ diff --git a/src/vendor/cigraph/src/graph/type_common.c b/src/vendor/cigraph/src/graph/type_common.c deleted file mode 100644 index da01c834c2e..00000000000 --- a/src/vendor/cigraph/src/graph/type_common.c +++ /dev/null @@ -1,207 +0,0 @@ -/* -*- 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_datatype.h" -#include "igraph_interface.h" - -/* Internal functions */ - -/* The functions in this file are sensible "default" implementations for some - * of the core API functions that simply call other core API functions. If - * you are implementing your own data type, chances are that you can use these - * as is. */ - -/** - * \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/creation.c - */ -igraph_error_t 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_delete_vertices - * \brief Removes some 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 - */ -igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) { - return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0, /* invidx= */ 0); -} - -/** - * \function igraph_edge - * \brief Returns 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 (source) of - * the edge will be placed here. - * \param to Pointer to an \type igraph_integer_t. The head (target) of the - * edge will be placed here. - * \return Error code. - * - * \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). - */ -igraph_error_t igraph_edge( - const igraph_t *graph, igraph_integer_t eid, - igraph_integer_t *from, igraph_integer_t *to -) { - - if (eid < 0 || eid >= igraph_ecount(graph)) { - IGRAPH_ERROR("Invalid edge ID when retrieving edge endpoints.", IGRAPH_EINVAL); - } - - 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() 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. - */ -igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, igraph_vector_int_t *edges) { - igraph_eit_t eit; - igraph_integer_t 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_int_resize(edges, n * 2)); - - if (igraph_is_directed(graph)) { - for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t 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)) { - igraph_integer_t 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; -} - -/** - * \function igraph_invalidate_cache - * \brief Invalidates the internal cache of an igraph graph. - * - * - * igraph graphs cache some basic properties about themselves in an internal - * data structure. This function invalidates the contents of the cache and - * forces a recalculation of the cached properties the next time they are - * needed. - * - * - * You should not need to call this function during normal usage; however, we - * might ask you to call this function explicitly if we suspect that you are - * running into a bug in igraph's cache handling. A tell-tale sign of an invalid - * cache entry is that the result of a cached igraph function (such as - * \ref igraph_is_dag() or \ref igraph_is_simple()) is different before and - * after a cache invalidation. - * - * \param graph The graph whose cache is to be invalidated. - * - * Time complexity: O(1). - */ -void igraph_invalidate_cache(const igraph_t* graph) { - igraph_i_property_cache_invalidate_all(graph); -} diff --git a/src/vendor/cigraph/src/graph/type_indexededgelist.c b/src/vendor/cigraph/src/graph/type_indexededgelist.c index 04689c9430d..b2ef1408ce0 100644 --- a/src/vendor/cigraph/src/graph/type_indexededgelist.c +++ b/src/vendor/cigraph/src/graph/type_indexededgelist.c @@ -1,7 +1,7 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2005-2021 The igraph development team + 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 @@ -26,15 +26,13 @@ #include "igraph_memory.h" #include "graph/attributes.h" -#include "graph/caching.h" -#include "graph/internal.h" -#include "math/safe_intop.h" +#include "graph/neighbors.h" /* Internal functions */ -static igraph_error_t igraph_i_create_start_vectors( - igraph_vector_int_t *res, igraph_vector_int_t *el, - igraph_vector_int_t *index, igraph_integer_t nodes); +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 @@ -46,32 +44,55 @@ static igraph_error_t igraph_i_create_start_vectors( * 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. * - * This section lists all the functions and macros that are considered - * as part of the core API from the point of view of the \em users - * of igraph. Some of these functions and macros have sensible default - * implementations that simply call some other core function (e.g., - * \ref igraph_empty() calls \ref igraph_empty_attrs() with a null attribute - * table pointer). If you wish to experiment with implementing an alternative - * data type, the actual number of functions that you need to replace is lower - * as you can rely on the same default implementations in most cases. + * 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(). - * - * - * This function does not set any vertex attributes. To create a graph which has - * vertex attributes, call this function specifying 0 vertices, then use - * \ref igraph_add_vertices() to add vertices and their attributes. - * * \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. @@ -83,19 +104,14 @@ static igraph_error_t igraph_i_create_start_vectors( * \cli IGRAPH_UNDIRECTED * Create an \em undirected graph. * \endclist - * \param attr The graph attributes. Supply \c NULL if not graph attributes - * are to be set. + * \param attr The attributes. * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * - * \sa \ref igraph_empty() to create an empty graph without attributes; - * \ref igraph_add_vertices() and \ref igraph_add_edges() to add vertices - * and edges, possibly with associated attributes. - * * Time complexity: O(|V|) for a graph with * |V| vertices (and no edges). */ -igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr) { +int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void* attr) { if (n < 0) { IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); @@ -103,19 +119,12 @@ igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bo graph->n = 0; graph->directed = directed; - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->from, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->to, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->oi, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->ii, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->os, 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->is, 1); - - /* init cache */ - graph->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); - IGRAPH_CHECK_OOM(graph->cache, "Cannot create graph."); - IGRAPH_FINALLY(igraph_free, graph->cache); - IGRAPH_CHECK(igraph_i_property_cache_init(graph->cache)); - IGRAPH_FINALLY(igraph_i_property_cache_destroy, graph->cache); + 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; @@ -127,8 +136,8 @@ igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bo /* add the vertices */ IGRAPH_CHECK(igraph_add_vertices(graph, n, 0)); - IGRAPH_FINALLY_CLEAN(8); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(6); + return 0; } /** @@ -151,15 +160,12 @@ void igraph_destroy(igraph_t *graph) { IGRAPH_I_ATTRIBUTE_DESTROY(graph); - igraph_i_property_cache_destroy(graph->cache); - IGRAPH_FREE(graph->cache); - - igraph_vector_int_destroy(&graph->from); - igraph_vector_int_destroy(&graph->to); - igraph_vector_int_destroy(&graph->oi); - igraph_vector_int_destroy(&graph->ii); - igraph_vector_int_destroy(&graph->os); - igraph_vector_int_destroy(&graph->is); + 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); } /** @@ -188,32 +194,26 @@ void igraph_destroy(igraph_t *graph) { * \example examples/simple/igraph_copy.c */ -igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from) { +int igraph_copy(igraph_t *to, const igraph_t *from) { to->n = from->n; to->directed = from->directed; - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->from, &from->from)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->from); - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->to, &from->to)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->to); - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->oi, &from->oi)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->oi); - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->ii, &from->ii)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->ii); - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->os, &from->os)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->os); - IGRAPH_CHECK(igraph_vector_int_init_copy(&to->is, &from->is)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &to->is); - - to->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); - IGRAPH_CHECK_OOM(to->cache, "Cannot copy graph."); - IGRAPH_FINALLY(igraph_free, to->cache); - IGRAPH_CHECK(igraph_i_property_cache_copy(to->cache, from->cache)); - IGRAPH_FINALLY(igraph_i_property_cache_destroy, to->cache); - - IGRAPH_I_ATTRIBUTE_COPY(to, from, true, true, true); /* does IGRAPH_CHECK */ - - IGRAPH_FINALLY_CLEAN(8); - return IGRAPH_SUCCESS; + 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; } /** @@ -228,134 +228,107 @@ igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from) { * 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 \ref igraph_add_vertices() first. + * 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. You can supply a null pointer - * here if you do not need edge attributes. + * \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. + * \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. + * 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/creation.c + * \example examples/simple/igraph_add_edges.c */ -igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, +int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, void *attr) { - igraph_integer_t no_of_edges = igraph_vector_int_size(&graph->from); - igraph_integer_t edges_to_add = igraph_vector_int_size(edges) / 2; - igraph_integer_t new_no_of_edges; - igraph_integer_t i = 0; - igraph_vector_int_t newoi, newii; + 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_int_size(edges) % 2 != 0) { - IGRAPH_ERROR("Invalid (odd) length of edges vector.", IGRAPH_EINVEVECTOR); + if (igraph_vector_size(edges) % 2 != 0) { + IGRAPH_ERROR("invalid (odd) length of edges vector", IGRAPH_EINVEVECTOR); } - if (!igraph_vector_int_isininterval(edges, 0, igraph_vcount(graph) - 1)) { - IGRAPH_ERROR("Out-of-range vertex IDs when adding edges.", IGRAPH_EINVVID); + if (!igraph_vector_isininterval(edges, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("cannot add edges", IGRAPH_EINVVID); } /* from & to */ - IGRAPH_SAFE_ADD(no_of_edges, edges_to_add, &new_no_of_edges); - if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { - IGRAPH_ERRORF("Maximum edge count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, - IGRAPH_ECOUNT_MAX); - } - IGRAPH_CHECK(igraph_vector_int_reserve(&graph->from, no_of_edges + edges_to_add)); - IGRAPH_CHECK(igraph_vector_int_reserve(&graph->to, no_of_edges + edges_to_add)); + 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_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ - igraph_vector_int_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ } else { - igraph_vector_int_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ - igraph_vector_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ } } - /* If an error occurs while the edges are being added, we make the necessary fixup - * to ensure that the graph is still in a consistent state when this function returns. - * The graph may already be on the finally stack when calling this function. We use - * a separate finally stack level to avoid its destructor from being called on error, - * so that the fixup can succeed. - */ - -#define CHECK_ERR(expr) \ - do { \ - igraph_error_t err = (expr); \ - if (err != IGRAPH_SUCCESS) { \ - igraph_vector_int_resize(&graph->from, no_of_edges); /* gets smaller, error safe */ \ - igraph_vector_int_resize(&graph->to, no_of_edges); /* gets smaller, error safe */ \ - IGRAPH_FINALLY_EXIT(); \ - IGRAPH_ERROR("Cannot add edges.", err); \ - } \ - } while (0) + /* disable the error handler temporarily */ + oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); /* oi & ii */ - IGRAPH_FINALLY_ENTER(); - { - CHECK_ERR(igraph_vector_int_init(&newoi, no_of_edges)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &newoi); - CHECK_ERR(igraph_vector_int_init(&newii, no_of_edges)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &newii); - CHECK_ERR(igraph_vector_int_pair_order(&graph->from, &graph->to, &newoi, graph->n)); - CHECK_ERR(igraph_vector_int_pair_order(&graph->to, &graph->from, &newii, graph->n)); - - /* Attributes */ - if (graph->attr) { - /* TODO: Does this keep the attribute table in a consistent state upon failure? */ - CHECK_ERR(igraph_i_attribute_add_edges(graph, edges, attr)); - } - - /* os & is, its length does not change, error safe */ - igraph_i_create_start_vectors(&graph->os, &graph->from, &newoi, graph->n); - igraph_i_create_start_vectors(&graph->is, &graph->to, &newii, graph->n); - - /* everything went fine */ - igraph_vector_int_destroy(&graph->oi); - igraph_vector_int_destroy(&graph->ii); - IGRAPH_FINALLY_CLEAN(2); + 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)); + } - graph->oi = newoi; - graph->ii = newii; + /* 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); + } } - IGRAPH_FINALLY_EXIT(); -#undef CHECK_ERR + /* 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); - /* modification successful, clear the cached properties of the graph. - * - * Adding one or more edges cannot make a strongly or weakly connected - * graph disconnected, so we keep those flags if they are cached as true. - * - * Adding one or more edges may turn a DAG into a non-DAG or a forest into - * a non-forest, so we can keep those flags only if they are cached as - * false. - * - * Also, adding one or more edges does not change HAS_LOOP, HAS_MULTI and - * HAS_MUTUAL if they were already true. - */ - igraph_i_property_cache_invalidate_conditionally( - graph, - /* keep_always = */ 0, - /* keep_when_false = */ - (1 << IGRAPH_PROP_IS_DAG) | (1 << IGRAPH_PROP_IS_FOREST), - /* keep_when_true = */ - (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) | - (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | - (1 << IGRAPH_PROP_HAS_LOOP) | - (1 << IGRAPH_PROP_HAS_MULTI) | - (1 << IGRAPH_PROP_HAS_MUTUAL) - ); + /* 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 IGRAPH_SUCCESS; + return 0; } /** @@ -367,101 +340,45 @@ igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edge * This function invalidates all iterators. * * \param graph The graph object to extend. - * \param nv Non-negative integer specifying the number of vertices to add. - * \param attr The attributes of the new vertices. You can supply a null pointer - * here if you do not need vertex attributes. + * \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. + * \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. + * Time complexity: O(|V|) where + * |V| is + * the number of vertices in the \em new, extended graph. * - * \example examples/simple/creation.c + * \example examples/simple/igraph_add_vertices.c */ -igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { - igraph_integer_t ec = igraph_ecount(graph); - igraph_integer_t vc = igraph_vcount(graph); - igraph_integer_t new_vc; - igraph_integer_t i; +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_ERROR("cannot add negative number of vertices", IGRAPH_EINVAL); } - IGRAPH_SAFE_ADD(graph->n, nv, &new_vc); - if (new_vc > IGRAPH_VCOUNT_MAX) { - IGRAPH_ERRORF("Maximum vertex count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, - IGRAPH_VCOUNT_MAX); - } - IGRAPH_CHECK(igraph_vector_int_reserve(&graph->os, new_vc + 1)); - IGRAPH_CHECK(igraph_vector_int_reserve(&graph->is, new_vc + 1)); + IGRAPH_CHECK(igraph_vector_reserve(&graph->os, graph->n + nv + 1)); + IGRAPH_CHECK(igraph_vector_reserve(&graph->is, graph->n + nv + 1)); - igraph_vector_int_resize(&graph->os, new_vc + 1); /* reserved */ - igraph_vector_int_resize(&graph->is, new_vc + 1); /* reserved */ - for (i = graph->n + 1; i < new_vc + 1; i++) { + 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; - /* Add attributes if necessary. This section is protected with - * FINALLY_ENTER/EXIT so that the graph would not be accidentally - * free upon error until it could be restored to a consistant state. */ - if (graph->attr) { - igraph_error_t err; - IGRAPH_FINALLY_ENTER(); - err = igraph_i_attribute_add_vertices(graph, nv, attr); - if (err != IGRAPH_SUCCESS) { - /* Restore original vertex count on failure */ - graph->n = vc; - igraph_vector_int_resize(&graph->os, vc + 1); /* shrinks */ - igraph_vector_int_resize(&graph->is, vc + 1); /* shrinks */ - } - IGRAPH_FINALLY_EXIT(); - if (err != IGRAPH_SUCCESS) { - IGRAPH_ERROR("Cannot add vertices.", err); - } + IGRAPH_CHECK(igraph_i_attribute_add_vertices(graph, nv, attr)); } - /* modification successful, clear the cached properties of the graph. - * - * Adding one or more nodes does not change the following cached properties: - * - * - IGRAPH_PROP_HAS_LOOP - * - IGRAPH_PROP_HAS_MULTI - * - IGRAPH_PROP_HAS_MUTUAL - * - IGRAPH_PROP_IS_DAG (adding a node does not create/destroy cycles) - * - IGRAPH_PROP_IS_FOREST (same) - * - * Adding one or more nodes without any edges incident on them is sure to - * make the graph disconnected (weakly or strongly), so we can keep the - * connectivity-related properties if they are currently cached as false. - * (Actually, even if they weren't cached as false, we could still set them - * to false, but we don't have that functionality yet). The only exception - * is when the graph had zero vertices and gained only one vertex, because - * it then becomes connected. That's why we have the condition below in the - * keep_when_false section. - */ - igraph_i_property_cache_invalidate_conditionally( - graph, - /* keep_always = */ - (1 << IGRAPH_PROP_HAS_LOOP) | - (1 << IGRAPH_PROP_HAS_MULTI) | - (1 << IGRAPH_PROP_HAS_MUTUAL) | - (1 << IGRAPH_PROP_IS_DAG) | - (1 << IGRAPH_PROP_IS_FOREST), - /* keep_when_false = */ - igraph_vcount(graph) >= 2 ? ( - (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | - (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) - ) : 0, - /* keep_when_true = */ - 0 - ); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -470,11 +387,11 @@ igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *a * \brief Removes edges from a graph. * * - * The edges to remove are specified as an edge selector. + * The edges to remove are given as an edge selector. * * - * This function cannot remove vertices; vertices will be kept even if they lose - * all their edges. + * This function cannot remove vertices, they will be kept, even if + * they lose all their edges. * * * This function invalidates all iterators. @@ -482,36 +399,39 @@ igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *a * \param edges The edges to remove. * \return Error code. * - * Time complexity: O(|V|+|E|) where |V| and |E| are the number of vertices + * 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 */ -igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t edges_to_remove = 0; - igraph_integer_t remaining_edges; +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_int_t newfrom, newto; - igraph_vector_int_t newoi, newii; + igraph_vector_t newfrom, newto, newoi; - igraph_bool_t *mark; - igraph_integer_t i, j; + int *mark; + long int i, j; - mark = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); - IGRAPH_CHECK_OOM(mark, "Cannot delete edges."); + 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)) { - igraph_integer_t e = IGRAPH_EIT_GET(eit); - if (! mark[e]) { + long int e = IGRAPH_EIT_GET(eit); + if (mark[e] == 0) { edges_to_remove++; - mark[e] = true; + mark[e]++; } } remaining_edges = no_of_edges - edges_to_remove; @@ -520,12 +440,12 @@ igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newfrom, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newto, remaining_edges); + 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]) { + if (mark[i] == 0) { VECTOR(newfrom)[j] = VECTOR(graph->from)[i]; VECTOR(newto)[j] = VECTOR(graph->to)[i]; j++; @@ -533,138 +453,108 @@ igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { } /* Create index, this might require additional memory */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&newoi, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newii, remaining_edges); - IGRAPH_CHECK(igraph_vector_int_pair_order(&newfrom, &newto, &newoi, no_of_nodes)); - IGRAPH_CHECK(igraph_vector_int_pair_order(&newto, &newfrom, &newii, no_of_nodes)); + 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 + /* Edge attributes, we need an index that gives the ids of the original edges for every new edge. */ if (graph->attr) { - igraph_vector_int_t idx; - IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, remaining_edges); + igraph_vector_t idx; + IGRAPH_VECTOR_INIT_FINALLY(&idx, remaining_edges); for (i = 0, j = 0; i < no_of_edges; i++) { - if (! mark[i]) { + if (mark[i] == 0) { VECTOR(idx)[j++] = i; } } IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, graph, &idx)); - igraph_vector_int_destroy(&idx); + igraph_vector_destroy(&idx); IGRAPH_FINALLY_CLEAN(1); } /* Ok, we've all memory needed, free the old structure */ - igraph_vector_int_destroy(&graph->from); - igraph_vector_int_destroy(&graph->to); - igraph_vector_int_destroy(&graph->oi); - igraph_vector_int_destroy(&graph->ii); + igraph_vector_destroy(&graph->from); + igraph_vector_destroy(&graph->to); + igraph_vector_destroy(&graph->oi); graph->from = newfrom; graph->to = newto; graph->oi = newoi; - graph->ii = newii; - IGRAPH_FINALLY_CLEAN(4); + IGRAPH_FINALLY_CLEAN(3); IGRAPH_FREE(mark); IGRAPH_FINALLY_CLEAN(1); /* Create start vectors, no memory is needed for this */ - igraph_i_create_start_vectors(&graph->os, &graph->from, &graph->oi, no_of_nodes); - igraph_i_create_start_vectors(&graph->is, &graph->to, &graph->ii, no_of_nodes); - - /* modification successful, clear the cached properties of the graph. - * - * Deleting one or more edges cannot make a directed acyclic graph cyclic, - * or an undirected forest into a cyclic graph, so we keep those flags if - * they are cached as true. - * - * Similarly, deleting one or more edges cannot make a disconnected graph - * connected, so we keep the connectivity flags if they are cached as false. - * - * Also, if the graph had no loop edges before the deletion, it will have - * no loop edges after the deletion either. The same applies to reciprocal - * edges or multiple edges as well. - */ - igraph_i_property_cache_invalidate_conditionally( - graph, - /* keep_always = */ 0, - /* keep_when_false = */ - (1 << IGRAPH_PROP_HAS_LOOP) | - (1 << IGRAPH_PROP_HAS_MULTI) | - (1 << IGRAPH_PROP_HAS_MUTUAL) | - (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | - (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED), - /* keep_when_true = */ - (1 << IGRAPH_PROP_IS_DAG) | - (1 << IGRAPH_PROP_IS_FOREST) - ); + 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 IGRAPH_SUCCESS; + return 0; } /** * \ingroup interface - * \function igraph_delete_vertices_idx - * \brief Removes some vertices (with all their edges) from the graph. + * \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). You can use the - * \c idx argument to obtain the mapping from old vertex IDs to the new ones, - * and the \c newidx argument to obtain the reverse mapping. + * 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. - * \param idx An optional pointer to a vector that provides the mapping from - * the vertex IDs \em before the removal to the vertex IDs \em after - * the removal, \em plus one. Zero is used to represent vertices that were - * removed during the operation. You can supply \c NULL here if you are not - * interested. - * \param invidx An optional pointer to a vector that provides the mapping from - * the vertex IDs \em after the removal to the vertex IDs \em before - * the removal. You can supply \c NULL here if you are not interested. + * \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. + * \c IGRAPH_EINVVID: invalid vertex id. * - * Time complexity: O(|V|+|E|), |V| and |E| are the number of vertices and + * 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 */ -igraph_error_t igraph_delete_vertices_idx( - igraph_t *graph, const igraph_vs_t vertices, igraph_vector_int_t *idx, - igraph_vector_int_t *invidx -) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t edge_recoding, vertex_recoding; - igraph_vector_int_t *my_vertex_recoding = &vertex_recoding; +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; - igraph_integer_t i, j; - igraph_integer_t remaining_vertices, remaining_edges; + long int i, j; + long int remaining_vertices, remaining_edges; if (idx) { my_vertex_recoding = idx; - IGRAPH_CHECK(igraph_vector_int_resize(idx, no_of_nodes)); - igraph_vector_int_null(idx); + IGRAPH_CHECK(igraph_vector_resize(idx, no_of_nodes)); + igraph_vector_null(idx); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_recoding, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_recoding, no_of_nodes); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_recoding, no_of_edges); + 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) ) { - igraph_integer_t vertex = IGRAPH_VIT_GET(vit); + long int vertex = IGRAPH_VIT_GET(vit); if (vertex < 0 || vertex >= no_of_nodes) { IGRAPH_ERROR("Cannot delete vertices", IGRAPH_EINVVID); } @@ -681,8 +571,8 @@ igraph_error_t igraph_delete_vertices_idx( } /* create edge recoding vector */ for (remaining_edges = 0, i = 0; i < no_of_edges; i++) { - igraph_integer_t from = VECTOR(graph->from)[i]; - igraph_integer_t to = VECTOR(graph->to)[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; @@ -691,59 +581,51 @@ igraph_error_t igraph_delete_vertices_idx( } /* start creating the graph */ - newgraph.n = remaining_vertices; + newgraph.n = (igraph_integer_t) remaining_vertices; newgraph.directed = graph->directed; /* allocate vectors */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.from, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.to, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.oi, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.ii, remaining_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.os, remaining_vertices + 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.is, remaining_vertices + 1); + 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) { - igraph_integer_t from = VECTOR(graph->from)[i]; - igraph_integer_t to = VECTOR(graph->to )[i]; + 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_int_pair_order(&newgraph.from, &newgraph.to, &newgraph.oi, - remaining_vertices)); - IGRAPH_CHECK(igraph_vector_int_pair_order(&newgraph.to, &newgraph.from, &newgraph.ii, - remaining_vertices)); - - IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.os, &newgraph.from, - &newgraph.oi, remaining_vertices)); - IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.is, &newgraph.to, - &newgraph.ii, remaining_vertices)); - - newgraph.cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); - IGRAPH_CHECK_OOM(newgraph.cache, "Cannot delete vertices."); - IGRAPH_FINALLY(igraph_free, newgraph.cache); - IGRAPH_CHECK(igraph_i_property_cache_init(newgraph.cache)); - IGRAPH_FINALLY(igraph_i_property_cache_destroy, newgraph.cache); + 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); - - /* at this point igraph_destroy can take over the responsibility of - * deallocating the graph */ - IGRAPH_FINALLY_CLEAN(8); /* 2 for the property cache, 6 for the vectors */ + IGRAPH_FINALLY_CLEAN(6); IGRAPH_FINALLY(igraph_destroy, &newgraph); if (newgraph.attr) { - igraph_vector_int_t iidx; - IGRAPH_VECTOR_INT_INIT_FINALLY(&iidx, remaining_vertices); + igraph_vector_t iidx; + IGRAPH_VECTOR_INIT_FINALLY(&iidx, remaining_vertices); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t jj = VECTOR(*my_vertex_recoding)[i]; + long int jj = (long int) VECTOR(*my_vertex_recoding)[i]; if (jj != 0) { VECTOR(iidx)[ jj - 1 ] = i; } @@ -751,20 +633,20 @@ igraph_error_t igraph_delete_vertices_idx( IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, &newgraph, &iidx)); - IGRAPH_CHECK(igraph_vector_int_resize(&iidx, remaining_edges)); + IGRAPH_CHECK(igraph_vector_resize(&iidx, remaining_edges)); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t jj = VECTOR(edge_recoding)[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_int_destroy(&iidx); + igraph_vector_destroy(&iidx); IGRAPH_FINALLY_CLEAN(1); } igraph_vit_destroy(&vit); - igraph_vector_int_destroy(&edge_recoding); + igraph_vector_destroy(&edge_recoding); igraph_destroy(graph); *graph = newgraph; @@ -772,9 +654,9 @@ igraph_error_t igraph_delete_vertices_idx( /* TODO: this is duplicate */ if (invidx) { - IGRAPH_CHECK(igraph_vector_int_resize(invidx, remaining_vertices)); + IGRAPH_CHECK(igraph_vector_resize(invidx, remaining_vertices)); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t newid = VECTOR(*my_vertex_recoding)[i]; + long int newid = (long int) VECTOR(*my_vertex_recoding)[i]; if (newid != 0) { VECTOR(*invidx)[newid - 1] = i; } @@ -782,33 +664,11 @@ igraph_error_t igraph_delete_vertices_idx( } if (!idx) { - igraph_vector_int_destroy(my_vertex_recoding); + igraph_vector_destroy(my_vertex_recoding); IGRAPH_FINALLY_CLEAN(1); } - /* modification successful, clear the cached properties of the graph. - * - * Deleting one or more vertices cannot make a directed acyclic graph cyclic, - * or an undirected forest into a cyclic graph, so we keep those flags if - * they are cached as true. - * - * Also, if the graph had no loop edges before the deletion, it will have - * no loop edges after the deletion either. The same applies to reciprocal - * edges or multiple edges as well. - */ - igraph_i_property_cache_invalidate_conditionally( - graph, - /* keep_always = */ 0, - /* keep_when_false = */ - (1 << IGRAPH_PROP_HAS_LOOP) | - (1 << IGRAPH_PROP_HAS_MULTI) | - (1 << IGRAPH_PROP_HAS_MUTUAL), - /* keep_when_true = */ - (1 << IGRAPH_PROP_IS_DAG) | - (1 << IGRAPH_PROP_IS_FOREST) - ); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -836,7 +696,7 @@ igraph_integer_t igraph_vcount(const igraph_t *graph) { * Time complexity: O(1) */ igraph_integer_t igraph_ecount(const igraph_t *graph) { - return igraph_vector_int_size(&graph->from); + return (igraph_integer_t) igraph_vector_size(&graph->from); } /** @@ -847,9 +707,8 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * \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. If one neighbor is connected with multiple - * edges, the neighbor will be returned multiple times. + * 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 @@ -862,7 +721,7 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * searched. * This parameter is ignored for undirected graphs. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * \c IGRAPH_ENOMEM: not enough memory. * @@ -872,7 +731,7 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * * \example examples/simple/igraph_neighbors.c */ -igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t pnode, +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); @@ -881,7 +740,7 @@ igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_t *neis } } -igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t pnode, +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) { \ @@ -902,11 +761,11 @@ igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *ne } \ } - igraph_integer_t length = 0, idx = 0; - igraph_integer_t i, j; + long int length = 0, idx = 0; + long int i, j; - igraph_integer_t node = pnode; - igraph_integer_t last_added = -1; + long int node = pnode; + igraph_real_t last_added = -1; igraph_bool_t should_filter_duplicates; if (node < 0 || node > igraph_vcount(graph) - 1) { @@ -935,7 +794,7 @@ igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *ne length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); } - IGRAPH_CHECK(igraph_vector_int_resize(neis, length)); + 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. @@ -957,18 +816,18 @@ igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *ne (igraph_is_directed(graph) && loops != IGRAPH_NO_LOOPS))); if (mode & IGRAPH_OUT) { - j = VECTOR(graph->os)[node + 1]; - for (i = VECTOR(graph->os)[node]; i < j; i++) { - igraph_integer_t to = VECTOR(graph->to)[ VECTOR(graph->oi)[i] ]; + 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 = VECTOR(graph->is)[node + 1]; - for (i = VECTOR(graph->is)[node]; i < j; i++) { - igraph_integer_t from = VECTOR(graph->from)[ VECTOR(graph->ii)[i] ]; + 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; } @@ -977,21 +836,21 @@ igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *ne /* Both in- and out- neighbors in a directed graph, we need to merge the two 'vectors' so the result is correctly ordered. */ - igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; - igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; - igraph_integer_t i1 = VECTOR(graph->os)[node]; - igraph_integer_t i2 = VECTOR(graph->is)[node]; - igraph_integer_t eid1, eid2; - igraph_integer_t n1, n2; + 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 = VECTOR(graph->oi)[i1]; - eid2 = VECTOR(graph->ii)[i2]; - n1 = VECTOR(graph->to)[eid1]; - n2 = VECTOR(graph->from)[eid2]; + 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); @@ -1018,64 +877,64 @@ igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_t *ne } while (i1 < j1) { - eid1 = VECTOR(graph->oi)[i1++]; - igraph_integer_t to = VECTOR(graph->to)[eid1]; + 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 = VECTOR(graph->ii)[i2++]; - igraph_integer_t from = VECTOR(graph->from)[eid2]; + 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_int_resize(neis, length)); + IGRAPH_CHECK(igraph_vector_resize(neis, length)); return IGRAPH_SUCCESS; #undef DEDUPLICATE_IF_NEEDED } - /** * \ingroup internal + * */ -static igraph_error_t igraph_i_create_start_vectors( - igraph_vector_int_t *res, igraph_vector_int_t *el, - igraph_vector_int_t *iindex, igraph_integer_t nodes) { +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)[ VECTOR(*iindex)[(i)] ]) +# define EDGE(i) (VECTOR(*el)[ (long int) VECTOR(*iindex)[(i)] ]) - igraph_integer_t no_of_nodes; - igraph_integer_t no_of_edges; - igraph_integer_t i, j, idx; + long int no_of_nodes; + long int no_of_edges; + long int i, j, idx; no_of_nodes = nodes; - no_of_edges = igraph_vector_int_size(el); + no_of_edges = igraph_vector_size(el); /* result */ - IGRAPH_CHECK(igraph_vector_int_resize(res, nodes + 1)); + IGRAPH_CHECK(igraph_vector_resize(res, nodes + 1)); /* create the index */ - if (no_of_edges == 0) { + if (igraph_vector_size(el) == 0) { /* empty graph */ - igraph_vector_int_null(res); + 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++) { - igraph_integer_t n = EDGE(i) - EDGE(VECTOR(*res)[idx]); + 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 = EDGE(VECTOR(*res)[idx]); + 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; } @@ -1084,7 +943,7 @@ static igraph_error_t igraph_i_create_start_vectors( /* clean */ # undef EDGE - return IGRAPH_SUCCESS; + return 0; } /** @@ -1093,8 +952,8 @@ static igraph_error_t igraph_i_create_start_vectors( * \brief Is this a directed graph? * * \param graph The graph. - * \return Logical value, \c true if the graph is directed, - * \c false otherwise. + * \return Logical value, TRUE if the graph is directed, + * FALSE otherwise. * * Time complexity: O(1) * @@ -1105,67 +964,6 @@ igraph_bool_t igraph_is_directed(const igraph_t *graph) { return graph->directed; } -/** - * \ingroup interface - * \function igraph_degree_1 - * \brief The degree of of a single vertex in the graph. - * - * This function calculates the in-, out- or total degree of a single vertex. - * For a single vertex, it is more efficient than calling \ref igraph_degree(). - * - * \param graph The graph. - * \param deg Pointer to the integer where the computed degree will be stored. - * \param vid The vertex for which the degree will be calculated. - * \param mode Defines the type of the degree for directed graphs. 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. - * - * \sa \ref igraph_degree() to compute the degree of several vertices at once. - * - * Time complexity: O(1) if \p loops is \c true, and - * O(d) otherwise, where d is the degree. - */ -igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, - igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops) { - - if (!igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - *deg = 0; - if (mode & IGRAPH_OUT) { - *deg += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); - } - if (mode & IGRAPH_IN) { - *deg += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); - } - if (! loops) { - /* When loops should not be counted, we remove their contribution from the - * previously computed degree. */ - if (mode & IGRAPH_OUT) { - for (igraph_integer_t i = VECTOR(graph->os)[vid]; i < VECTOR(graph->os)[vid + 1]; i++) { - if (VECTOR(graph->to)[ VECTOR(graph->oi)[i] ] == vid) { - *deg -= 1; - } - } - } - if (mode & IGRAPH_IN) { - for (igraph_integer_t i = VECTOR(graph->is)[vid]; i < VECTOR(graph->is)[vid + 1]; i++) { - if (VECTOR(graph->from)[ VECTOR(graph->ii)[i] ] == vid) { - *deg -= 1; - } - } - } - } - - return IGRAPH_SUCCESS; -} - /** * \ingroup interface * \function igraph_degree @@ -1174,18 +972,12 @@ igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, * * This function calculates the in-, out- or total degree of the * specified vertices. - * - * - * This function returns the result as a vector of \c igraph_integer_t - * values. In applications where \c igraph_real_t is desired, use - * \ref igraph_strength() with \c NULL weights. - * * \param graph The graph. - * \param res Integer vector, this will contain the result. It should be + * \param res Vector, this will contain the result. It should be * initialized and will be resized to be the appropriate size. - * \param vids Vertex selector, giving the vertex IDs of which the degree will + * \param vids Vector, giving the vertex ids of which the degree will * be calculated. - * \param mode Defines the type of the degree for directed graphs. Valid modes are: + * \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 @@ -1194,33 +986,35 @@ igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * - * Time complexity: O(v) if \p loops is \c true, and - * O(v*d) otherwise. v is the number of + * 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; \ref igraph_degree_1() to efficiently compute the - * degree of a single vertex. + * edge weights. * * \example examples/simple/igraph_degree.c */ -igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, +int igraph_degree(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops) { - igraph_integer_t nodes_to_calc; - igraph_integer_t i, j; + 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("Invalid mode for degree calculation.", IGRAPH_EINVMODE); + IGRAPH_ERROR("degree calculation failed", IGRAPH_EINVMODE); } nodes_to_calc = IGRAPH_VIT_SIZE(vit); @@ -1228,15 +1022,15 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, mode = IGRAPH_ALL; } - IGRAPH_CHECK(igraph_vector_int_resize(res, nodes_to_calc)); - igraph_vector_int_null(res); + 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++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); + long int vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); } } @@ -1244,7 +1038,7 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); + long int vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); } } @@ -1253,11 +1047,11 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); + long int vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); - for (j = VECTOR(graph->os)[vid]; + for (j = (long int) VECTOR(graph->os)[vid]; j < VECTOR(graph->os)[vid + 1]; j++) { - if (VECTOR(graph->to)[ VECTOR(graph->oi)[j] ] == vid) { + if (VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[j] ] == vid) { VECTOR(*res)[i] -= 1; } } @@ -1267,11 +1061,11 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); + long int vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); - for (j = VECTOR(graph->is)[vid]; + for (j = (long int) VECTOR(graph->is)[vid]; j < VECTOR(graph->is)[vid + 1]; j++) { - if (VECTOR(graph->from)[ VECTOR(graph->ii)[j] ] == vid) { + if (VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[j] ] == vid) { VECTOR(*res)[i] -= 1; } } @@ -1282,111 +1076,172 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, 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; } -/* These are unsafe macros. Only supply variable names, i.e. no - expressions as parameters, otherwise nasty things can happen. - - BINSEARCH is an inline binary search in the 'edgelist' vector, which is - assumed to be sorted in the order of indices stored in the 'iindex' vector. - (So, [edgelist[iindex[x]] for x in 0..] is assumed to be sorted). 'N' must - be the same as 'end' when invoking the macro but it must be a separate - variable as we want to modify 'end' independently of 'N'. Upon exiting the - macro, 'result' is the index of the _leftmost_ item in the sorted 'edgelist' - (i.e. indexed by 'iindex') where the value was found, if it was found; - otherwise 'pos' is left intact. - - FIND_DIRECTED_EDGE looks for an edge from 'xfrom' to 'xto' in the graph, and - stores the ID of the edge in 'eid' if it is found; otherwise 'eid' is left - intact. - - FIND_UNDIRECTED_EDGE looks for an edge between 'xfrom' and 'xto' in an - undirected graph, swapping them if necessary. It stores the ID of the edge - in 'eid' if it is found; otherwise 'eid' is left intact. - */ - -#define BINSEARCH(start,end,value,iindex,edgelist,N,result,result_pos) \ - do { \ - while ((start) < (end)) { \ - igraph_integer_t mid =(start)+((end)-(start))/2; \ - igraph_integer_t e = VECTOR((iindex))[mid]; \ - if (VECTOR((edgelist))[e] < (value)) { \ - (start) = mid+1; \ - } else { \ - (end) = mid; \ - } \ - } \ - if ((start) < (N)) { \ - igraph_integer_t e = VECTOR((iindex))[(start)]; \ - if (VECTOR((edgelist))[e] == (value)) { \ - *(result) = e; \ - if (result_pos != 0) { *(result_pos) = start; } \ - } \ - } \ - } while (0) +/** + * \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; -#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid) \ - do { \ - igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ - igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ - igraph_integer_t N = end; \ - igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ - igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ - igraph_integer_t N2 = end2; \ - igraph_integer_t *nullpointer = NULL; \ - if (end-start < end2-start2) { \ - BINSEARCH(start, end, xto, graph->oi, graph->to, N, eid, nullpointer); \ - } else { \ - BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, eid, nullpointer); \ - } \ + 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 { \ - igraph_integer_t xfrom1 = from > to ? from : to; \ - igraph_integer_t xto1 = from > to ? to : from; \ - FIND_DIRECTED_EDGE(graph, xfrom1, xto1, eid); \ +#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. + * \brief Get the edge id from the end points of an edge. * - * For undirected graphs \c from and \c to are exchangeable. + * 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 from The starting point of the edge. - * \param to The end point of the edge. + * \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. - * Note that invalid vertex IDs in input arguments (\p from or \p to) - * always return an error code. * \return Error code. - * \sa \ref igraph_edge() for the opposite operation, \ref igraph_get_all_eids_between() - * to retrieve all edge IDs between a pair of vertices. + * \sa \ref igraph_edge() for the opposite operation. * * Time complexity: O(log (d)), where d is smaller of the out-degree - * of \c from and in-degree of \c to if \p directed is true. If \p directed + * 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 to and the in-degree of \c from. + * 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. */ -igraph_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, - igraph_integer_t from, igraph_integer_t to, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int from = pfrom, to = pto; + long int nov = igraph_vcount(graph); - if (from < 0 || to < 0 || from >= no_of_nodes || to >= no_of_nodes) { - IGRAPH_ERROR("Cannot get edge ID.", IGRAPH_EINVVID); + if (from < 0 || to < 0 || from > nov - 1 || to > nov - 1) { + IGRAPH_ERROR("cannot get edge id", IGRAPH_EINVVID); } *eid = -1; @@ -1407,42 +1262,161 @@ igraph_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, if (*eid < 0) { if (error) { - IGRAPH_ERROR("Cannot get edge ID, no such edge", IGRAPH_EINVAL); + 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. + * 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. * - * The pairs of vertex IDs for which the edges are looked up are taken - * consecutively from the \c pairs vector, i.e. VECTOR(pairs)[0] - * and VECTOR(pairs)[1] specify 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 you have a sequence of vertex IDs that describe a \em path on the graph, - * use \ref igraph_expand_path_to_pairs() to convert them to a list of vertex - * pairs along the path. + * 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 specify pairs - * of vertices that are not connected. Otherwise -1 is reported for vertex pairs - * without at least one edge between them. + * 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, igraph always returns the same edge ID, - * even if the pair appears multiple times in \c pairs. + * 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 to fetch the edges for. + * \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 @@ -1453,167 +1427,304 @@ igraph_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, * 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. + * \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 */ -igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, - const igraph_vector_int_t *pairs, - igraph_bool_t directed, igraph_bool_t error) { - igraph_integer_t n = pairs ? igraph_vector_int_size(pairs) : 0; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; - igraph_integer_t eid = -1; +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 (n == 0) { - igraph_vector_int_clear(eids); - return IGRAPH_SUCCESS; + 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_ERROR("Cannot get edge ids, invalid length of edge ids", IGRAPH_EINVAL); } - - if (!igraph_vector_int_isininterval(pairs, 0, no_of_nodes - 1)) { - IGRAPH_ERROR("Cannot get edge IDs, invalid vertex ID", IGRAPH_EINVVID); + 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_int_resize(eids, n / 2)); + 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++) { - igraph_integer_t from = VECTOR(*pairs)[2 * i]; - igraph_integer_t to = VECTOR(*pairs)[2 * i + 1]; + 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); + FIND_DIRECTED_EDGE(graph, from, to, &eid, seen); if (!directed && eid < 0) { - FIND_DIRECTED_EDGE(graph, to, from, &eid); + FIND_DIRECTED_EDGE(graph, to, from, &eid, seen); } VECTOR(*eids)[i] = eid; - if (eid < 0 && error) { - IGRAPH_ERROR("Cannot get edge ID, no such edge", IGRAPH_EINVAL); + 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++) { - igraph_integer_t from = VECTOR(*pairs)[2 * i]; - igraph_integer_t to = VECTOR(*pairs)[2 * i + 1]; + 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); + FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen); VECTOR(*eids)[i] = eid; - if (eid < 0 && error) { - IGRAPH_ERROR("Cannot get edge ID, no such edge", IGRAPH_EINVAL); + if (eid >= 0) { + seen[(long int)(eid)] = 1; + } else if (error) { + IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); } } } - return IGRAPH_SUCCESS; + IGRAPH_FREE(seen); + IGRAPH_FINALLY_CLEAN(1); + return 0; } -#undef FIND_DIRECTED_EDGE -#undef FIND_UNDIRECTED_EDGE +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) { -#define FIND_ALL_DIRECTED_EDGES(graph,xfrom,xto,eidvec) \ - do { \ - igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ - igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ - igraph_integer_t N = end; \ - igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ - igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ - igraph_integer_t N2 = end2; \ - igraph_integer_t eid = -1; \ - igraph_integer_t pos = -1; \ - if (end-start < end2-start2) { \ - BINSEARCH(start, end, xto, graph->oi, graph->to, N, &eid,&pos); \ - while (pos >= 0 && pos < N) { \ - eid = VECTOR(graph->oi)[pos++]; \ - if (VECTOR(graph->to)[eid] != xto) { break; } \ - IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ - } \ - } else { \ - BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, &eid, &pos); \ - while (pos >= 0 && pos < N2) { \ - eid = VECTOR(graph->ii)[pos++]; \ - if (VECTOR(graph->from)[eid] != xfrom) { break; } \ - IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ - } \ - } \ - } while (0) + 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; -#define FIND_ALL_UNDIRECTED_EDGES(graph,from,to,eidvec) \ - do { \ - igraph_integer_t xfrom1 = from > to ? from : to; \ - igraph_integer_t xto1 = from > to ? to : from; \ - FIND_ALL_DIRECTED_EDGES(graph, xfrom1, xto1, eidvec); \ - } while (0) + 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_all_eids_between - * \brief Returns all edge IDs between a pair of vertices. + * \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. * * - * For undirected graphs \c source and \c target are exchangeable. + * 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 source The ID of the source vertex - * \param target The ID of the target vertex + * \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: TODO + * 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. + * \sa \ref igraph_get_eid() for a single edge, \ref + * igraph_get_eids() for a faster version that does not handle + * multiple edges. */ -igraph_error_t igraph_get_all_eids_between( - const igraph_t *graph, igraph_vector_int_t *eids, - igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - if (source < 0 || source >= no_of_nodes) { - IGRAPH_ERROR("Cannot get edge IDs, invalid source vertex ID", IGRAPH_EINVVID); +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); } - - if (target < 0 || target >= no_of_nodes) { - IGRAPH_ERROR("Cannot get edge IDs, invalid target vertex ID", IGRAPH_EINVVID); - } - - igraph_vector_int_clear(eids); - - if (igraph_is_directed(graph)) { - /* look in the specified direction first */ - FIND_ALL_DIRECTED_EDGES(graph, source, target, eids); - if (!directed) { - /* look in the reverse direction as well */ - FIND_ALL_DIRECTED_EDGES(graph, target, source, eids); - } - } else { - FIND_ALL_UNDIRECTED_EDGES(graph, source, target, eids); - } - - return IGRAPH_SUCCESS; } -#undef FIND_DIRECTED_EDGE -#undef FIND_UNDIRECTED_EDGE -#undef BINSEARCH - /** * \function igraph_incident * \brief Gives the incident edges of a vertex. * * \param graph The graph object. - * \param eids An initialized vector. It will be resized + * \param eids An initialized \type vector_t object. It will be resized * to hold the result. - * \param pnode A vertex ID. + * \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 @@ -1626,7 +1737,7 @@ igraph_error_t igraph_get_all_eids_between( * Time complexity: O(d), the number of incident edges to \p pnode. */ -igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t 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); @@ -1635,11 +1746,11 @@ igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_t *eids, } } -igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, +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_integer_t length = 0, idx = 0; - igraph_integer_t i, j; - igraph_integer_t node = pnode; + long int length = 0, idx = 0; + long int i, j; + long int node = pnode; igraph_bool_t directed = igraph_is_directed(graph); if (node < 0 || node > igraph_vcount(graph) - 1) { @@ -1669,7 +1780,7 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); } - IGRAPH_CHECK(igraph_vector_int_resize(eids, length)); + 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. @@ -1682,10 +1793,10 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid /* We did not ask for both directions; this is the easy case */ if (mode & IGRAPH_OUT) { - j = VECTOR(graph->os)[node + 1]; - for (i = VECTOR(graph->os)[node]; i < j; i++) { - igraph_integer_t edge = VECTOR(graph->oi)[i]; - igraph_integer_t other = VECTOR(graph->to)[edge]; + 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]; if (loops == IGRAPH_NO_LOOPS && other == pnode) { length--; } else { @@ -1695,10 +1806,10 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid } if (mode & IGRAPH_IN) { - j = VECTOR(graph->is)[node + 1]; - for (i = VECTOR(graph->is)[node]; i < j; i++) { - igraph_integer_t edge = VECTOR(graph->ii)[i]; - igraph_integer_t other = VECTOR(graph->from)[edge]; + 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]; if ((loops == IGRAPH_NO_LOOPS || (loops == IGRAPH_LOOPS_ONCE && !directed)) && other == pnode) { length--; } else { @@ -1709,19 +1820,19 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid } else { /* both in- and out- neighbors in a directed graph, we need to merge the two 'vectors' */ - igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; - igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; - igraph_integer_t i1 = VECTOR(graph->os)[node]; - igraph_integer_t i2 = VECTOR(graph->is)[node]; - igraph_integer_t eid1, eid2; - igraph_integer_t n1, n2; + 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; igraph_bool_t seen_loop_edge = 0; while (i1 < j1 && i2 < j2) { - eid1 = VECTOR(graph->oi)[i1]; - eid2 = VECTOR(graph->ii)[i2]; - n1 = VECTOR(graph->to)[eid1]; - n2 = VECTOR(graph->from)[eid2]; + 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++; VECTOR(*eids)[idx++] = eid1; @@ -1765,7 +1876,7 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid VECTOR(*eids)[idx++] = eid2; } } - IGRAPH_CHECK(igraph_vector_int_resize(eids, length)); + IGRAPH_CHECK(igraph_vector_resize(eids, length)); return IGRAPH_SUCCESS; #undef DEDUPLICATE_IF_NEEDED } @@ -1800,15 +1911,15 @@ igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eid * * Time complexity: O(E), the number of edges in the graphs. * - * \sa \ref igraph_isomorphic() to test if two graphs are isomorphic. + * \sa igraph_isomorphic() to test if two graphs are isomorphic. */ -igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res) { - igraph_integer_t nv1 = igraph_vcount(graph1); - igraph_integer_t nv2 = igraph_vcount(graph2); - igraph_integer_t ne1 = igraph_ecount(graph1); - igraph_integer_t ne2 = igraph_ecount(graph2); - igraph_integer_t i, eid1, eid2; +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 */ @@ -1834,8 +1945,8 @@ igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *grap * "target". */ for (i = 0; i < ne1; i++) { - eid1 = VECTOR(graph1->ii)[i]; - eid2 = VECTOR(graph2->ii)[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)) { @@ -1851,22 +1962,3 @@ igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *grap *res = 1; /* No difference was found, graphs are the same */ return IGRAPH_SUCCESS; } - - -/* Reverses the direction of all edges in a directed graph. - * The graph is modified in-place. - * Attributes are preserved. - */ -igraph_error_t igraph_i_reverse(igraph_t *graph) { - - /* Nothing to do for undirected graphs. */ - if (! igraph_is_directed(graph)) { - return IGRAPH_SUCCESS; - } - - igraph_vector_int_swap(&graph->to, &graph->from); - igraph_vector_int_swap(&graph->oi, &graph->ii); - igraph_vector_int_swap(&graph->os, &graph->is); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/graph/visitors.c b/src/vendor/cigraph/src/graph/visitors.c index 749e1b956ef..82997c74f88 100644 --- a/src/vendor/cigraph/src/graph/visitors.c +++ b/src/vendor/cigraph/src/graph/visitors.c @@ -40,7 +40,7 @@ * * 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. + * vertex ids. * * * Consider using \ref igraph_bfs_simple instead if you set most of the output @@ -65,34 +65,24 @@ * 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 + * 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 + * \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 parents If not a null pointer, then the id of the parent of - * each vertex is stored here. When a vertex was not visited - * during the traversal, -2 will be stored as the ID of its parent. - * When a vertex was visited during the traversal and it was one of - * the roots of the search trees, -1 will be stored as the ID of - * its parent. + * \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 as the predecessor of the vertex. - * If the vertex was not visited at all, then -2 is stored for - * the predecessor of the vertex. + * 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 as the successor of the vertex. - * If the vertex was not visited at all, then -2 is stored for - * the successor of the vertex. + * 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 for each vertex. If a - * vertex was not reached during the traversal, its distance will - * be -1 in this vector. + * 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. @@ -105,54 +95,62 @@ * \example examples/simple/igraph_bfs.c * \example examples/simple/igraph_bfs_callback.c */ -igraph_error_t igraph_bfs(const igraph_t *graph, - igraph_integer_t root, const igraph_vector_int_t *roots, +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_int_t *restricted, - igraph_vector_int_t *order, igraph_vector_int_t *rank, - igraph_vector_int_t *parents, - igraph_vector_int_t *pred, igraph_vector_int_t *succ, - igraph_vector_int_t *dist, igraph_bfshandler_t *callback, + 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_error_t ret; - - igraph_dqueue_int_t Q; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t actroot = 0; - igraph_vector_bool_t added; + 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; - igraph_integer_t act_rank = 0; - igraph_integer_t pred_vec = -1; + long int act_rank = 0; + long int pred_vec = -1; - igraph_integer_t rootpos = 0; - igraph_integer_t noroots = roots ? igraph_vector_int_size(roots) : 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_EINVVID); + IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); } - if (roots && !igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { - IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID); + 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_int_isininterval(restricted, 0, no_of_nodes-1)) { - IGRAPH_ERROR("Invalid vertex ID in restricted set.", IGRAPH_EINVVID); + 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); + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; } - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&added, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + 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); @@ -161,28 +159,26 @@ igraph_error_t igraph_bfs(const igraph_t *graph, 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) { - igraph_integer_t i, n = igraph_vector_int_size(restricted); - igraph_vector_bool_fill(&added, true); + long int i, n = igraph_vector_size(restricted); + igraph_vector_char_fill(&added, 1); for (i = 0; i < n; i++) { - igraph_integer_t v = VECTOR(*restricted)[i]; - VECTOR(added)[v] = false; + long int v = (long int) VECTOR(*restricted)[i]; + VECTOR(added)[v] = 0; } } /* Resize result vectors, and fill them with IGRAPH_NAN */ -# define VINIT(v, initial) \ - if (v) { \ - IGRAPH_CHECK(igraph_vector_int_resize((v), no_of_nodes)); \ - igraph_vector_int_fill((v), initial); \ - } +# define VINIT(v) if (v) { \ + igraph_vector_resize((v), no_of_nodes); \ + igraph_vector_fill((v), IGRAPH_NAN); } - VINIT(order, -1); - VINIT(rank, -1); - VINIT(parents, -2); - VINIT(pred, -2); - VINIT(succ, -2); - VINIT(dist, -1); + VINIT(order); + VINIT(rank); + VINIT(father); + VINIT(pred); + VINIT(succ); + VINIT(dist); # undef VINIT while (1) { @@ -191,7 +187,7 @@ igraph_error_t igraph_bfs(const igraph_t *graph, if (roots && rootpos < noroots) { /* We are still going through the 'roots' vector */ - actroot = VECTOR(*roots)[rootpos++]; + actroot = (long int) VECTOR(*roots)[rootpos++]; } else if (!roots && rootpos == 0) { /* We have a single root vertex given, and start now */ actroot = root; @@ -213,30 +209,28 @@ igraph_error_t igraph_bfs(const igraph_t *graph, if (VECTOR(added)[actroot]) { continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actroot)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); - VECTOR(added)[actroot] = true; - if (parents) { - VECTOR(*parents)[actroot] = -1; + 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_int_empty(&Q)) { - igraph_integer_t actvect = igraph_dqueue_int_pop(&Q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); - igraph_integer_t succ_vec; - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); - igraph_integer_t i, n; - - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - n = igraph_vector_int_size(neis); + 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; + VECTOR(*rank) [actvect] = act_rank; } if (order) { VECTOR(*order)[act_rank++] = actvect; @@ -246,27 +240,31 @@ igraph_error_t igraph_bfs(const igraph_t *graph, } for (i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; + long int nei = (long int) VECTOR(*neis)[i]; if (! VECTOR(added)[nei]) { - VECTOR(added)[nei] = true; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); - if (parents) { - VECTOR(*parents)[nei] = actvect; + 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_int_empty(&Q) ? -1L : - igraph_dqueue_int_head(&Q); + succ_vec = igraph_dqueue_empty(&Q) ? -1L : + (long int) igraph_dqueue_head(&Q); if (callback) { - IGRAPH_CHECK_CALLBACK( - callback(graph, actvect, pred_vec, succ_vec, act_rank - 1, actdist, extra), - &ret - ); - - if (ret == IGRAPH_STOP) { - goto cleanup; + 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; } } @@ -279,14 +277,12 @@ igraph_error_t igraph_bfs(const igraph_t *graph, } /* for actroot < no_of_nodes */ -cleanup: - igraph_lazy_adjlist_destroy(&adjlist); - igraph_dqueue_int_destroy(&Q); - igraph_vector_bool_destroy(&added); + igraph_dqueue_destroy(&Q); + igraph_vector_char_destroy(&added); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -301,26 +297,26 @@ igraph_error_t igraph_bfs(const igraph_t *graph, * be ignored. * * \param graph The input graph. - * \param root The id of the root vertex. + * \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 order If not a null pointer, then an initialized vector must be passed - * here. The IDs of the vertices visited during the traversal will be + * \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 order where the vertices that are at distance i from the root + * 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 order + * 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 set to -2 for - * vertices that were \em not visited, and -1 for the root vertex. + * 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 @@ -328,19 +324,17 @@ igraph_error_t igraph_bfs(const igraph_t *graph, * * \example examples/simple/igraph_bfs_simple.c */ -igraph_error_t igraph_bfs_simple( - const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, - igraph_vector_int_t *order, igraph_vector_int_t *layers, - igraph_vector_int_t *parents -) { - - igraph_dqueue_int_t q; - igraph_integer_t num_visited = 0; - igraph_vector_int_t neis; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; - bool *added; - igraph_integer_t lastlayer = -1; +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; @@ -352,79 +346,77 @@ igraph_error_t igraph_bfs_simple( } /* temporary storage */ - added = IGRAPH_CALLOC(no_of_nodes, bool); + added = IGRAPH_CALLOC(no_of_nodes, char); if (added == 0) { - IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); /* results */ - if (order) { - igraph_vector_int_clear(order); + if (vids) { + igraph_vector_clear(vids); } if (layers) { - igraph_vector_int_clear(layers); + igraph_vector_clear(layers); } if (parents) { - IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); - igraph_vector_int_fill(parents, -2); + IGRAPH_CHECK(igraph_vector_resize(parents, no_of_nodes)); } - /* ok start with root */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, root)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + /* ok start with vid */ + IGRAPH_CHECK(igraph_dqueue_push(&q, vid)); + IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); if (layers) { - IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); } - if (order) { - IGRAPH_CHECK(igraph_vector_int_push_back(order, root)); + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, vid)); } if (parents) { - VECTOR(*parents)[root] = -1; + VECTOR(*parents)[(long int)vid] = vid; } num_visited++; - added[root] = true; + added[(long int)vid] = 1; - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actvect = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actvect, + 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)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); - for (i = 0; i < nei_count; i++) { - igraph_integer_t neighbor = VECTOR(neis)[i]; - if (! added[neighbor]) { - added[neighbor] = true; + 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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); if (layers && lastlayer != actdist + 1) { - IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); } - if (order) { - IGRAPH_CHECK(igraph_vector_int_push_back(order, neighbor)); + if (vids) { + IGRAPH_CHECK(igraph_vector_push_back(vids, neighbor)); } num_visited++; lastlayer = actdist + 1; } } /* for i in neis */ - } /* while ! dqueue_int_empty */ + } /* while ! dqueue_empty */ if (layers) { - IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); } - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&q); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -440,7 +432,7 @@ igraph_error_t igraph_bfs_simple( * * 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. + * vertex ids. * * \param graph The input graph. * \param root The id of the root vertex. @@ -453,22 +445,15 @@ igraph_error_t igraph_bfs_simple( * 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. The tail of - * the vector will be padded with -1 to ensure that the length of the - * vector is the same as the number of vertices, even if some vertices - * were not visited during the traversal. - * \param order_out If not a null pointer, then the vertex IDs of the + * \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. The tail of the vector will be padded with -1 to ensure - * that the length of the vector is the same as the number of vertices, - * even if some vertices were not visited during the traversal. - * \param parents If not a null pointer, then the id of the parent of - * each vertex is stored here. -1 will be stored for the root of the - * search tree; -2 will be stored for vertices that were not visited. + * 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. -1 will be stored for vertices - * that were not visited. + * 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. @@ -482,24 +467,23 @@ igraph_error_t igraph_bfs_simple( * edges. */ -igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, +int igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, igraph_bool_t unreachable, - igraph_vector_int_t *order, - igraph_vector_int_t *order_out, igraph_vector_int_t *parents, - igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, + 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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_lazy_adjlist_t adjlist; - igraph_stack_int_t stack; + igraph_stack_t stack; igraph_vector_char_t added; - igraph_vector_int_t nptr; - igraph_error_t ret; - igraph_integer_t actroot; - igraph_integer_t act_rank = 0; - igraph_integer_t rank_out = 0; - igraph_integer_t act_dist = 0; + 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); @@ -516,56 +500,56 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, IGRAPH_CHECK(igraph_vector_char_init(&added, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_char_destroy, &added); - IGRAPH_CHECK(igraph_stack_int_init(&stack, 100)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + 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_int_init(&nptr, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &nptr); + IGRAPH_CHECK(igraph_vector_long_init(&nptr, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &nptr); # define FREE_ALL() do { \ - igraph_vector_int_destroy(&nptr); \ + igraph_vector_long_destroy(&nptr); \ igraph_lazy_adjlist_destroy(&adjlist); \ - igraph_stack_int_destroy(&stack); \ + igraph_stack_destroy(&stack); \ igraph_vector_char_destroy(&added); \ IGRAPH_FINALLY_CLEAN(4); } while (0) - /* Resize result vectors and fill them with the initial value */ + /* Resize result vectors and fill them with IGRAPH_NAN */ -# define VINIT(v, initial) if (v) { \ - IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes)); \ - igraph_vector_int_fill(v, initial); } +# define VINIT(v) if (v) { \ + igraph_vector_resize(v, no_of_nodes); \ + igraph_vector_fill(v, IGRAPH_NAN); } - VINIT(order, -1); - VINIT(order_out, -1); - VINIT(parents, -2); - VINIT(dist, -1); + VINIT(order); + VINIT(order_out); + VINIT(father); + VINIT(dist); # undef VINIT - IGRAPH_CHECK(igraph_stack_int_push(&stack, root)); - VECTOR(added)[root] = 1; - if (parents) { - VECTOR(*parents)[root] = -1; + 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)[root] = 0; + VECTOR(*dist)[(long int)root] = 0; } if (in_callback) { - IGRAPH_CHECK_CALLBACK(in_callback(graph, root, 0, extra), &ret); - if (ret == IGRAPH_STOP) { + igraph_bool_t terminate = in_callback(graph, root, 0, extra); + if (terminate) { FREE_ALL(); - return IGRAPH_SUCCESS; + return 0; } } for (actroot = 0; actroot < no_of_nodes; ) { /* 'root' first, then all other vertices */ - if (igraph_stack_int_empty(&stack)) { + if (igraph_stack_empty(&stack)) { if (!unreachable) { break; } @@ -573,10 +557,10 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, actroot++; continue; } - IGRAPH_CHECK(igraph_stack_int_push(&stack, actroot)); + IGRAPH_CHECK(igraph_stack_push(&stack, actroot)); VECTOR(added)[actroot] = 1; - if (parents) { - VECTOR(*parents)[actroot] = -1; + if (father) { + VECTOR(*father)[actroot] = -1; } if (order) { VECTOR(*order)[act_rank++] = actroot; @@ -586,38 +570,37 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, } if (in_callback) { - IGRAPH_CHECK_CALLBACK(in_callback(graph, actroot, 0, extra), &ret); - if (ret == IGRAPH_STOP) { + igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) actroot, + 0, extra); + if (terminate) { FREE_ALL(); - return IGRAPH_SUCCESS; + return 0; } } - actroot++; } - while (!igraph_stack_int_empty(&stack)) { - igraph_integer_t actvect = igraph_stack_int_top(&stack); - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); - igraph_integer_t n = igraph_vector_int_size(neis); - igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, actvect); - - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + 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 = false; - igraph_integer_t nei = 0; + igraph_bool_t any = 0; + long int nei = 0; while (!any && (*ptr) < n) { - nei = VECTOR(*neis)[(*ptr)]; + nei = (long int) VECTOR(*neis)[(*ptr)]; any = !VECTOR(added)[nei]; (*ptr) ++; } if (any) { /* There is such a neighbor, add it */ - IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_push(&stack, nei)); VECTOR(added)[nei] = 1; - if (parents) { - VECTOR(*parents)[ nei ] = actvect; + if (father) { + VECTOR(*father)[ nei ] = actvect; } if (order) { VECTOR(*order)[act_rank++] = nei; @@ -628,33 +611,30 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, } if (in_callback) { - IGRAPH_CHECK_CALLBACK( - in_callback(graph, nei, act_dist, extra), - &ret - ); - if (ret == IGRAPH_STOP) { + igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) nei, + (igraph_integer_t) act_dist, + extra); + if (terminate) { FREE_ALL(); - return IGRAPH_SUCCESS; + return 0; } } } else { /* There is no such neighbor, finished with the subtree */ - igraph_stack_int_pop(&stack); + igraph_stack_pop(&stack); if (order_out) { VECTOR(*order_out)[rank_out++] = actvect; } act_dist--; if (out_callback) { - IGRAPH_CHECK_CALLBACK( - out_callback(graph, actvect, act_dist, extra), - &ret - ); - - if (ret == IGRAPH_STOP) { + igraph_bool_t terminate = out_callback(graph, (igraph_integer_t) + actvect, (igraph_integer_t) + act_dist, extra); + if (terminate) { FREE_ALL(); - return IGRAPH_SUCCESS; + return 0; } } } @@ -664,5 +644,5 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, FREE_ALL(); # undef FREE_ALL - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/hrg/dendro.h b/src/vendor/cigraph/src/hrg/dendro.h index 113968a5ffd..a2d092bc93f 100644 --- a/src/vendor/cigraph/src/hrg/dendro.h +++ b/src/vendor/cigraph/src/hrg/dendro.h @@ -252,7 +252,7 @@ class dendro { int computeEdgeCount(const int, const short int, const int, const short int); // (consensus tree) counts children - size_t countChildren(const std::string); + 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 @@ -290,7 +290,7 @@ class dendro { // make single MCMC move bool monteCarloMove(double&, bool&, const double); // record consensus tree from splithist - void recordConsensusTree(igraph_vector_int_t *parents, + void recordConsensusTree(igraph_vector_t *parents, igraph_vector_t *weights); // record D structure void recordDendrogramStructure(igraph_hrg_t *hrg); @@ -303,9 +303,9 @@ class dendro { // reset the dendrograph structures void resetDendrograph(); // sample dendrogram's splits and update the split histogram - bool sampleSplitLikelihoods(igraph_integer_t&); + bool sampleSplitLikelihoods(int&); // reset splits histogram - //void resetAllSplits(); + void resetAllSplits(); }; } // namespace fitHRG diff --git a/src/vendor/cigraph/src/hrg/graph.h b/src/vendor/cigraph/src/hrg/graph.h index 43e501bee63..f990f9391ba 100644 --- a/src/vendor/cigraph/src/hrg/graph.h +++ b/src/vendor/cigraph/src/hrg/graph.h @@ -61,7 +61,6 @@ #ifndef IGRAPH_HRG_GRAPH #define IGRAPH_HRG_GRAPH -#include #include "hrg/rbtree.h" #include @@ -145,7 +144,7 @@ class graph { // clear all links from graph void resetLinks(); // allocate edge histograms - void setAdjacencyHistograms(const igraph_integer_t); + void setAdjacencyHistograms(const int); // set name of vertex i bool setName(const int, const std::string); diff --git a/src/vendor/cigraph/src/hrg/hrg.cc b/src/vendor/cigraph/src/hrg/hrg.cc index 605bf150730..58642376e59 100644 --- a/src/vendor/cigraph/src/hrg/hrg.cc +++ b/src/vendor/cigraph/src/hrg/hrg.cc @@ -21,19 +21,15 @@ */ -#include "hrg/dendro.h" -#include "hrg/graph.h" -#include "hrg/graph_simp.h" - #include "igraph_interface.h" +#include "igraph_memory.h" #include "igraph_attributes.h" #include "igraph_hrg.h" #include "igraph_random.h" -#include "igraph_structural.h" - -#include "core/exceptions.h" -#include +#include "hrg/dendro.h" +#include "hrg/graph.h" +#include "hrg/graph_simp.h" using namespace fitHRG; @@ -83,7 +79,7 @@ struct pblock { }; } -static igraph_error_t markovChainMonteCarlo(dendro *d, igraph_integer_t period, +static int markovChainMonteCarlo(dendro *d, unsigned int period, igraph_hrg_t *hrg) { igraph_real_t bestL = d->getLikelihood(); @@ -102,12 +98,10 @@ static igraph_error_t markovChainMonteCarlo(dendro *d, igraph_integer_t period, // model averaging sense), you'll need to code that yourself. // do 'period' MCMC moves before doing anything else - for (igraph_integer_t i = 0; i < period; i++) { + for (unsigned int i = 0; i < period; i++) { // make a MCMC move - if (!d->monteCarloMove(dL, flag_taken, 1.0)) { - return IGRAPH_FAILURE; - } + IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); // get likelihood of this D given G igraph_real_t cl = d->getLikelihood(); @@ -121,14 +115,13 @@ static igraph_error_t markovChainMonteCarlo(dendro *d, igraph_integer_t period, // corrects floating-point errors O(n) d->refreshLikelihood(); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t markovChainMonteCarlo2(dendro *d, igraph_integer_t num_samples) { +static int markovChainMonteCarlo2(dendro *d, int num_samples) { bool flag_taken; double dL, ptest = 1.0 / (50.0 * (double)(d->g->numNodes())); - igraph_integer_t sample_num = 0; - int t = 1, thresh = 200 * 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 @@ -151,10 +144,10 @@ static igraph_error_t markovChainMonteCarlo2(dendro *d, igraph_integer_t num_sam d->refreshLikelihood(); // TODO: less frequently } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { +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 @@ -172,9 +165,7 @@ static igraph_error_t MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { oldMeanL = newMeanL; newMeanL = 0.0; for (int i = 0; i < 65536; i++) { - if (!d->monteCarloMove(dL, flag_taken, 1.0)) { - return IGRAPH_FAILURE; - } + IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); Likeli = d->getLikelihood(); newMeanL += Likeli; } @@ -190,33 +181,27 @@ static igraph_error_t MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { d->recordDendrogramStructure(hrg); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_hrg_getgraph(const igraph_t *igraph, +static int igraph_i_hrg_getgraph(const igraph_t *igraph, dendro *d) { - igraph_integer_t no_of_nodes = igraph_vcount(igraph); - igraph_integer_t no_of_edges = igraph_ecount(igraph); - igraph_integer_t i; - - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph too large for the HRG module.", IGRAPH_EOVERFLOW); - } + int no_of_nodes = igraph_vcount(igraph); + int no_of_edges = igraph_ecount(igraph); + int i; - // TODO: Can this be relaxed? buildDendrogram() creates a tree with n-2 internal edges, - // i.e. zero internal edges for a 2-vertex graph. This is not handled at the moment. if (no_of_nodes < 3) { IGRAPH_ERROR("Graph must have at least 3 vertices for HRG, got only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL); } // Create graph - d->g = new graph((int) no_of_nodes); + d->g = new graph(no_of_nodes); // Add edges for (i = 0; i < no_of_edges; i++) { - int from = (int) IGRAPH_FROM(igraph, i); - int to = (int) IGRAPH_TO(igraph, i); + int from = IGRAPH_FROM(igraph, i); + int to = IGRAPH_TO(igraph, i); if (from == to) { continue; } @@ -230,34 +215,29 @@ static igraph_error_t igraph_i_hrg_getgraph(const igraph_t *igraph, d->buildDendrogram(); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_hrg_getsimplegraph(const igraph_t *igraph, +static int igraph_i_hrg_getsimplegraph(const igraph_t *igraph, dendro *d, simpleGraph **sg, - igraph_integer_t num_bins) { - - igraph_integer_t no_of_nodes = igraph_vcount(igraph); - igraph_integer_t no_of_edges = igraph_ecount(igraph); - igraph_integer_t i; + int num_bins) { - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph too large for the HRG module.", IGRAPH_EOVERFLOW); - } + int no_of_nodes = igraph_vcount(igraph); + int no_of_edges = igraph_ecount(igraph); + int i; - // TODO: See analogous TODO item in igraph_i_hrg_getgraph() if (no_of_nodes < 3) { IGRAPH_ERROR("Graph must have at least 3 vertices for HRG, got only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL); } // Create graphs - d->g = new graph((int) no_of_nodes, true); + d->g = new graph(no_of_nodes, true); d->g->setAdjacencyHistograms(num_bins); - (*sg) = new simpleGraph((int) no_of_nodes); + (*sg) = new simpleGraph(no_of_nodes); for (i = 0; i < no_of_edges; i++) { - int from = (int) IGRAPH_FROM(igraph, i); - int to = (int) IGRAPH_TO(igraph, i); + int from = IGRAPH_FROM(igraph, i); + int to = IGRAPH_TO(igraph, i); if (from == to) { continue; } @@ -277,16 +257,27 @@ static igraph_error_t igraph_i_hrg_getsimplegraph(const igraph_t *igraph, d->buildDendrogram(); - return IGRAPH_SUCCESS; + return 0; +} + +static void igraph_i_delete_dendrogram(dendro* d) { + delete d; +} + +static void igraph_i_delete_simple_graph(simpleGraph* g) { + delete g; +} + +static void igraph_i_clear_pblock_array(pblock* arr) { + delete [] arr; } /** * \function igraph_hrg_init - * \brief Allocate memory for a HRG. + * 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. @@ -295,45 +286,38 @@ static igraph_error_t igraph_i_hrg_getsimplegraph(const igraph_t *igraph, * Time complexity: O(n), the number of vertices in the graph. */ -igraph_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n) { - if (n < 0) { - IGRAPH_ERRORF("Number of vertices should not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); - } - if (n == 0) { - n = 1; - } - IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->left, n - 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->right, n - 1); - IGRAPH_VECTOR_INIT_FINALLY (&hrg->prob, n - 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->edges, n - 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->vertices, n - 1); +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 IGRAPH_SUCCESS; + return 0; } /** * \function igraph_hrg_destroy - * \brief Deallocate memory for an HRG. + * 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_int_destroy(&hrg->left); - igraph_vector_int_destroy(&hrg->right); + igraph_vector_destroy(&hrg->left); + igraph_vector_destroy(&hrg->right); igraph_vector_destroy(&hrg->prob); - igraph_vector_int_destroy(&hrg->edges); - igraph_vector_int_destroy(&hrg->vertices); + igraph_vector_destroy(&hrg->edges); + igraph_vector_destroy(&hrg->vertices); } /** * \function igraph_hrg_size - * \brief Returns the size of the HRG, the number of leaf nodes. + * 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. @@ -341,13 +325,13 @@ void igraph_hrg_destroy(igraph_hrg_t *hrg) { * Time complexity: O(1). */ -igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg) { - return igraph_vector_int_size(&hrg->left) + 1; +int igraph_hrg_size(const igraph_hrg_t *hrg) { + return igraph_vector_size(&hrg->left) + 1; } /** * \function igraph_hrg_resize - * \brief Resize a HRG. + * Resize a HRG. * * \param hrg Pointer to an initialized (see \ref igraph_hrg_init) * HRG. @@ -357,43 +341,35 @@ igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg) { * Time complexity: O(n), n is the new size. */ -igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize) { - igraph_integer_t origsize = igraph_hrg_size(hrg); - - /* The data structure must be left in a consistent state if resizing fails. */ - -#define CHECK_ERR(expr) \ - do { \ - igraph_error_t err = (expr); \ - if (err != IGRAPH_SUCCESS) { \ - igraph_vector_int_resize(&hrg->left, origsize); \ - igraph_vector_int_resize(&hrg->right, origsize); \ - igraph_vector_resize(&hrg->prob, origsize); \ - igraph_vector_int_resize(&hrg->edges, origsize); \ - igraph_vector_int_resize(&hrg->vertices, origsize); \ - IGRAPH_FINALLY_EXIT(); \ - IGRAPH_ERROR("Cannot resize HRG.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ - } \ - } while (0) - - IGRAPH_FINALLY_ENTER(); - { - CHECK_ERR(igraph_vector_int_resize(&hrg->left, newsize - 1)); - CHECK_ERR(igraph_vector_int_resize(&hrg->right, newsize - 1)); - CHECK_ERR(igraph_vector_resize(&hrg->prob, newsize - 1)); - CHECK_ERR(igraph_vector_int_resize(&hrg->edges, newsize - 1)); - CHECK_ERR(igraph_vector_int_resize(&hrg->vertices, newsize - 1)); +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); } - IGRAPH_FINALLY_EXIT(); -#undef CHECK_ERR - - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_hrg_fit - * \brief Fit a hierarchical random graph model to a network. + * 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. @@ -402,7 +378,7 @@ igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize) { * 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 model. + * 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. @@ -411,14 +387,12 @@ igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize) { * Time complexity: TODO. */ -igraph_error_t igraph_hrg_fit(const igraph_t *graph, +int igraph_hrg_fit(const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t steps) { - - IGRAPH_HANDLE_EXCEPTIONS_BEGIN + int steps) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + int no_of_nodes = igraph_vcount(graph); RNG_BEGIN(); @@ -427,7 +401,7 @@ igraph_error_t igraph_hrg_fit(const igraph_t *graph, // If we want to start from HRG if (start) { if (igraph_hrg_size(hrg) != no_of_nodes) { - IGRAPH_ERROR("Invalid HRG to start from.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid HRG to start from", IGRAPH_EINVAL); } // Convert the igraph graph IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, &d)); @@ -448,125 +422,164 @@ igraph_error_t igraph_hrg_fit(const igraph_t *graph, RNG_END(); - return IGRAPH_SUCCESS; + return 0; - IGRAPH_HANDLE_EXCEPTIONS_END } /** * \function igraph_hrg_sample - * \brief Sample from a hierarchical random graph model. + * Sample from a hierarchical random graph model * - * This function draws a single sample from a hierarchical random graph model. - * - * \param hrg A HRG model to sample from - * \param sample Pointer to an uninitialized graph; the sample is stored here. + * 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. */ -igraph_error_t igraph_hrg_sample(const igraph_hrg_t *hrg, igraph_t *sample) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN - dendro d; - - // TODO: error handling +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) { - RNG_BEGIN(); + int i; + dendro *d; - d.clearDendrograph(); - d.importDendrogramStructure(hrg); - d.makeRandomGraph(); - d.recordGraphStructure(sample); + if (no_samples < 0) { + IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); + } - RNG_END(); + if (!sample && !samples) { + IGRAPH_ERROR("Give at least one of `sample' and `samples'", + IGRAPH_EINVAL); + } - return IGRAPH_SUCCESS; - IGRAPH_HANDLE_EXCEPTIONS_END -} + if (no_samples != 1 && sample) { + IGRAPH_ERROR("Number of samples should be one if `sample' is given", + IGRAPH_EINVAL); + } -/** - * \function igraph_hrg_sample_many - * \brief Draw multiple samples from a hierarchical random graph model. - * - * This function draws multiple samples from a hierarchical random graph - * ensemble. The ensemble can be given as a graph (\c input_graph), or as an - * 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 new graph. - * - * \param hrg A HRG model to sample from - * \param samples An initialized graph list that will contain the sampled - * graphs. Note that existing graphs in the graph list are \em not removed - * so make sure you supply an empty list if you do not need the old contents - * of the list. - * \param num_samples The number of samples to generate. - * \return Error code. - * - * Time complexity: TODO. - */ + if (no_samples > 1 && !samples) { + IGRAPH_ERROR("`samples' must be non-null if number of samples " + "is larger than 1", IGRAPH_EINVAL); + } -igraph_error_t igraph_hrg_sample_many( - const igraph_hrg_t *hrg, igraph_graph_list_t *samples, - igraph_integer_t num_samples -) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN - igraph_t g; - dendro d; + if (!start && !input_graph) { + IGRAPH_ERROR("Input graph must be given if initial HRG is not used", + IGRAPH_EINVAL); + } - if (num_samples < 0) { - IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL); + if (!start) { + IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(input_graph))); } - if (num_samples == 0) { - return IGRAPH_SUCCESS; + 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.clearDendrograph(); - d.importDendrogramStructure(hrg); - while (num_samples > 0) { - d.makeRandomGraph(); - d.recordGraphStructure(&g); - IGRAPH_FINALLY(igraph_destroy, &g); - IGRAPH_CHECK(igraph_graph_list_push_back(samples, &g)); - IGRAPH_FINALLY_CLEAN(1); + d = new dendro; + IGRAPH_FINALLY(igraph_i_delete_dendrogram, d); + + // 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; + IGRAPH_FINALLY_CLEAN(1); + RNG_END(); - return IGRAPH_SUCCESS; - IGRAPH_HANDLE_EXCEPTIONS_END + return 0; } /** * \function igraph_hrg_game - * \brief Generate a hierarchical random graph. + * 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. - * + * 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. + * \param hrg The hierarchical random graph model to sample from. It + * is modified during the MCMC process. * \return Error code. * * Time complexity: TODO. */ -igraph_error_t igraph_hrg_game(igraph_t *graph, +int igraph_hrg_game(igraph_t *graph, const igraph_hrg_t *hrg) { - return igraph_hrg_sample(hrg, graph); + 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 - * \brief Create a dendrogram from a hierarchical random graph. + * 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. @@ -575,15 +588,14 @@ igraph_error_t igraph_hrg_game(igraph_t *graph, * Time complexity: O(n), the number of vertices in the graph. */ -igraph_error_t igraph_hrg_dendrogram( - igraph_t *graph, const igraph_hrg_t *hrg -) { +int igraph_hrg_dendrogram(igraph_t *graph, + const igraph_hrg_t *hrg) { - igraph_integer_t orig_nodes = igraph_hrg_size(hrg); - igraph_integer_t no_of_nodes = orig_nodes * 2 - 1; - igraph_integer_t no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; - igraph_vector_int_t edges; - igraph_integer_t i, idx = 0; + 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", @@ -600,14 +612,14 @@ igraph_error_t igraph_hrg_dendrogram( VECTOR(prob)[orig_nodes + i] = VECTOR(hrg->prob)[i]; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + 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++) { - igraph_integer_t left = VECTOR(hrg->left)[i]; - igraph_integer_t right = VECTOR(hrg->right)[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; @@ -618,19 +630,19 @@ igraph_error_t igraph_hrg_dendrogram( 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, NULL)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); igraph_vector_ptr_destroy(&vattrs); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&prob); IGRAPH_FINALLY_CLEAN(4); // + 1 for graph - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_hrg_consensus - * \brief Calculate a consensus tree for a HRG. + * 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. @@ -639,8 +651,8 @@ igraph_error_t igraph_hrg_dendrogram( * \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. + * 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. @@ -656,23 +668,23 @@ igraph_error_t igraph_hrg_dendrogram( * Time complexity: TODO. */ -igraph_error_t igraph_hrg_consensus(const igraph_t *graph, - igraph_vector_int_t *parents, +int igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t num_samples) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN + int num_samples) { dendro *d; if (start && !hrg) { - IGRAPH_ERROR("`hrg' must be given if `start' is true.", IGRAPH_EINVAL); + IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); } RNG_BEGIN(); d = new dendro; + IGRAPH_FINALLY(igraph_i_delete_dendrogram, d); if (start) { IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); @@ -681,7 +693,7 @@ igraph_error_t igraph_hrg_consensus(const igraph_t *graph, } else { IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); if (hrg) { - igraph_hrg_resize(hrg, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(graph))); } IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } @@ -691,15 +703,14 @@ igraph_error_t igraph_hrg_consensus(const igraph_t *graph, d->recordConsensusTree(parents, weights); delete d; + IGRAPH_FINALLY_CLEAN(1); RNG_END(); - return IGRAPH_SUCCESS; - - IGRAPH_HANDLE_EXCEPTIONS_END + return 0; } -static igraph_error_t MCMCEquilibrium_Sample(dendro *d, igraph_integer_t num_samples) { +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 @@ -714,8 +725,8 @@ static igraph_error_t MCMCEquilibrium_Sample(dendro *d, igraph_integer_t num_sam double dL; bool flag_taken; - igraph_integer_t sample_num = 0; - igraph_integer_t t = 1, thresh = 100 * d->g->numNodes(); + 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) { @@ -728,10 +739,10 @@ static igraph_error_t MCMCEquilibrium_Sample(dendro *d, igraph_integer_t num_sam t++; } - return IGRAPH_SUCCESS; + return 0; } -static int QsortPartition (pblock* array, igraph_integer_t left, igraph_integer_t right, igraph_integer_t index) { +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; @@ -748,8 +759,8 @@ static int QsortPartition (pblock* array, igraph_integer_t left, igraph_integer_ array[index].i = temp.i; array[index].j = temp.j; - igraph_integer_t stored = left; - for (igraph_integer_t i = left; i < right; i++) { + 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; @@ -778,17 +789,17 @@ static int QsortPartition (pblock* array, igraph_integer_t left, igraph_integer_ return stored; } -static void QsortMain (pblock* array, igraph_integer_t left, igraph_integer_t right) { +static void QsortMain (pblock* array, int left, int right) { if (right > left) { - igraph_integer_t pivot = left; - igraph_integer_t part = QsortPartition(array, left, right, pivot); + int pivot = left; + int part = QsortPartition(array, left, right, pivot); QsortMain(array, left, part - 1); QsortMain(array, part + 1, right ); } return; } -static igraph_error_t rankCandidatesByProbability(simpleGraph *sg, dendro *d, +static int rankCandidatesByProbability(simpleGraph *sg, dendro *d, pblock *br_list, int mk) { int mkk = 0; int n = sg->getNumNodes(); @@ -807,13 +818,13 @@ static igraph_error_t rankCandidatesByProbability(simpleGraph *sg, dendro *d, // Sort the candidates by their average probability QsortMain(br_list, 0, mk - 1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t recordPredictions(pblock *br_list, igraph_vector_int_t *edges, +static int recordPredictions(pblock *br_list, igraph_vector_t *edges, igraph_vector_t *prob, int mk) { - IGRAPH_CHECK(igraph_vector_int_resize(edges, mk * 2)); + 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--) { @@ -822,16 +833,15 @@ static igraph_error_t recordPredictions(pblock *br_list, igraph_vector_int_t *ed VECTOR(*prob)[idx2++] = br_list[i].L; } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_hrg_predict - * \brief Predict missing edges in a graph, based on HRG models. + * 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, @@ -849,29 +859,28 @@ static igraph_error_t recordPredictions(pblock *br_list, igraph_vector_int_t *ed * Time complexity: TODO. */ -igraph_error_t igraph_hrg_predict(const igraph_t *graph, - igraph_vector_int_t *edges, +int igraph_hrg_predict(const igraph_t *graph, + igraph_vector_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, - igraph_integer_t num_samples, - igraph_integer_t num_bins) { - IGRAPH_HANDLE_EXCEPTIONS_BEGIN + int num_samples, + int num_bins) { - dendro *d; pblock *br_list; int mk; simpleGraph *sg; if (start && !hrg) { - IGRAPH_ERROR("`hrg' must be given if `start' is true.", IGRAPH_EINVAL); + IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); } RNG_BEGIN(); - d = new dendro; + dendro d; - IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, d, &sg, num_bins)); + IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, &d, &sg, num_bins)); + IGRAPH_FINALLY(igraph_i_delete_simple_graph, sg); mk = sg->getNumNodes() * (sg->getNumNodes() - 1) / 2 - sg->getNumLinks() / 2; br_list = new pblock[mk]; @@ -880,35 +889,34 @@ igraph_error_t igraph_hrg_predict(const igraph_t *graph, br_list[i].i = -1; br_list[i].j = -1; } + IGRAPH_FINALLY(igraph_i_clear_pblock_array, br_list); if (start) { - d->clearDendrograph(); - d->importDendrogramStructure(hrg); + d.clearDendrograph(); + d.importDendrogramStructure(hrg); } else { if (hrg) { - igraph_hrg_resize(hrg, igraph_vcount(graph)); + IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(graph))); } - IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + IGRAPH_CHECK(MCMCEquilibrium_Find(&d, hrg)); } - IGRAPH_CHECK(MCMCEquilibrium_Sample(d, num_samples)); - IGRAPH_CHECK(rankCandidatesByProbability(sg, d, br_list, mk)); + 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; + IGRAPH_FINALLY_CLEAN(2); RNG_END(); - return IGRAPH_SUCCESS; - - IGRAPH_HANDLE_EXCEPTIONS_END + return 0; } /** * \function igraph_hrg_create - * \brief Create a HRG from an igraph graph. + * Create a HRG from an igraph graph. * * \param hrg Pointer to an initialized \ref igraph_hrg_t. The result * is stored here. @@ -916,25 +924,25 @@ igraph_error_t igraph_hrg_predict(const igraph_t *graph, * 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. + * 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. */ -igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, +int igraph_hrg_create(igraph_hrg_t *hrg, const igraph_t *graph, const igraph_vector_t *prob) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_internal = no_of_nodes > 0 ? (no_of_nodes - 1) / 2 : 0; - igraph_vector_int_t deg, idx; - igraph_integer_t root = 0; - igraph_integer_t d0 = 0, d1 = 0, d2 = 0; - igraph_integer_t ii = 0, il = 0; - igraph_vector_int_t neis; + 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; - igraph_bool_t simple; // -------------------------------------------------------- // CHECKS @@ -942,57 +950,50 @@ igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, // At least three vertices are required if (no_of_nodes < 3) { - IGRAPH_ERROR("HRG tree must have at least three vertices.", + 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_ERROR("Probability vector must be given for HRG", IGRAPH_EINVAL); } // Length of prob vector - if (igraph_vector_size(prob) != no_of_nodes / 2) { - IGRAPH_ERRORF("HRG probability vector size (%" IGRAPH_PRId ") should be equal " - "to the number of internal nodes (%" IGRAPH_PRId ").", IGRAPH_EINVAL, - igraph_vector_size(prob), no_of_nodes / 2); + 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); + 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_ERROR("Complete HRG graph must have odd number of vertices", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_is_simple(graph, &simple)); - if (!simple) { - IGRAPH_ERROR("HRG graph must be a simple graph.", IGRAPH_EINVAL); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(°, 0); + 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 (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t d = VECTOR(deg)[i]; + 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); + "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); + "root vertex", IGRAPH_EINVAL); } // Every internal vertex must have out-degree two, @@ -1001,13 +1002,13 @@ igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); for (int i = 0; i < no_of_nodes; i++) { - igraph_integer_t d = VECTOR(deg)[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); + "degree 0 (leaves)", IGRAPH_EINVAL); } } @@ -1024,10 +1025,10 @@ igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, // Create an index, that maps the root node as first, then // the internal nodes, then the leaf nodes - IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&idx, no_of_nodes); VECTOR(idx)[root] = - (ii++) - 1; - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t d = VECTOR(deg)[i]; + for (int i = 0; i < no_of_nodes; i++) { + int d = VECTOR(deg)[i]; if (i == root) { continue; } @@ -1040,27 +1041,27 @@ igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, } IGRAPH_CHECK(igraph_hrg_resize(hrg, no_of_internal + 1)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t ri = VECTOR(idx)[i]; + 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)[ VECTOR(neis)[0] ]; - VECTOR(hrg->right)[-ri - 1] = VECTOR(idx)[ VECTOR(neis)[1] ]; + 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_int_null(&hrg->edges); - igraph_vector_int_null(&hrg->vertices); + 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)) { - igraph_integer_t ri = igraph_vector_tail(&path); - igraph_integer_t lc = VECTOR(hrg->left)[-ri - 1]; - igraph_integer_t rc = VECTOR(hrg->right)[-ri - 1]; + 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)); @@ -1080,10 +1081,10 @@ igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, } igraph_vector_destroy(&path); - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&idx); - igraph_vector_int_destroy(°); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&idx); + igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/hrg/hrg_types.cc b/src/vendor/cigraph/src/hrg/hrg_types.cc index d8ba2ac8f19..f2673b6d48f 100644 --- a/src/vendor/cigraph/src/hrg/hrg_types.cc +++ b/src/vendor/cigraph/src/hrg/hrg_types.cc @@ -41,9 +41,6 @@ #include "igraph_constructors.h" #include "igraph_random.h" -#include -#include - using namespace std; using namespace fitHRG; @@ -1315,10 +1312,10 @@ int dendro::computeEdgeCount(const int a, const short int atype, // *********************************************************************** -size_t dendro::countChildren(const string s) { - size_t len = s.size(); - size_t numC = 0; - for (size_t i = 0; i < len; i++) { +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++; } @@ -1440,13 +1437,7 @@ double dendro::getSplitTotalWeight() { // *********************************************************************** bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) { - igraph_integer_t size = igraph_hrg_size(hrg); - - if (size > INT_MAX) { - throw std::range_error("Hierarchical random graph too large for the HRG module"); - } - - n = (int) size; + n = igraph_hrg_size(hrg); // allocate memory for G, O(n) leaf = new elementd[n]; @@ -1979,7 +1970,7 @@ int dendro::QsortPartition (block* array, int left, int right, int index) { return stored; } -void dendro::recordConsensusTree(igraph_vector_int_t *parents, +void dendro::recordConsensusTree(igraph_vector_t *parents, igraph_vector_t *weights) { keyValuePairSplit *curr, *prev; @@ -2078,7 +2069,7 @@ void dendro::recordConsensusTree(igraph_vector_int_t *parents, } // Return the consensus tree - igraph_vector_int_resize(parents, ii + orig_nodes); + igraph_vector_resize(parents, ii + orig_nodes); if (weights) { igraph_vector_resize(weights, ii); } @@ -2107,6 +2098,8 @@ void dendro::recordConsensusTree(igraph_vector_int_t *parents, VECTOR(*parents)[i] = -1; } } + + } // ********************************************************************** @@ -2124,13 +2117,13 @@ void dendro::recordDendrogramStructure(igraph_hrg_t *hrg) { } void dendro::recordGraphStructure(igraph_t *graph) { - igraph_vector_int_t edges; + igraph_vector_t edges; int no_of_nodes = g->numNodes(); int no_of_edges = g->numLinks() / 2; int idx = 0; - igraph_vector_int_init(&edges, no_of_edges * 2); - IGRAPH_FINALLY(igraph_vector_int_destroy, &edges); + 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); @@ -2145,7 +2138,7 @@ void dendro::recordGraphStructure(igraph_t *graph) { igraph_create(graph, &edges, no_of_nodes, /* directed= */ 0); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } @@ -2176,7 +2169,7 @@ list* dendro::reversePathToRoot(const int leafIndex) { // *********************************************************************** -bool dendro::sampleSplitLikelihoods(igraph_integer_t &sample_num) { +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 @@ -2622,7 +2615,7 @@ void graph::resetLinks() { // ********************************************************************** -void graph::setAdjacencyHistograms(const igraph_integer_t bin_count) { +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); @@ -3126,7 +3119,7 @@ int splittree::returnNodecount() { keyValuePairSplit* splittree::returnTheseSplits(const int target) { keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair; - size_t count, len; + int count, len; head = returnTreeAsList(); prev = newhead = newtail = newpair = NULL; diff --git a/src/vendor/cigraph/src/internal/glpk_support.c b/src/vendor/cigraph/src/internal/glpk_support.c index 7cd89db97b2..9b0fcae0f7f 100644 --- a/src/vendor/cigraph/src/internal/glpk_support.c +++ b/src/vendor/cigraph/src/internal/glpk_support.c @@ -118,19 +118,18 @@ void igraph_i_glp_delete_prob(glp_prob *p) { } } -igraph_error_t igraph_i_glpk_check(int retval, const char* message) { - const char *code = "none"; +int igraph_i_glpk_check(int retval, const char* message) { + char* code = "none"; char message_and_code[4096]; - igraph_error_t ret; - if (retval == 0) { + if (retval == IGRAPH_SUCCESS) { return IGRAPH_SUCCESS; } /* handle errors */ -#define HANDLE_CODE(c) case c: code = #c; ret = IGRAPH_##c; break; -#define HANDLE_CODE2(c) case c: code = #c; ret = IGRAPH_FAILURE; break; -#define HANDLE_CODE3(c) case c: code = #c; ret = IGRAPH_INTERRUPTED; break; +#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); @@ -150,7 +149,7 @@ igraph_error_t igraph_i_glpk_check(int retval, const char* message) { HANDLE_CODE2(GLP_EITLIM); default: - IGRAPH_ERROR("Unknown GLPK error.", IGRAPH_FAILURE); + IGRAPH_ERROR("Unknown GLPK error", IGRAPH_FAILURE); } #undef HANDLE_CODE #undef HANDLE_CODE2 @@ -158,7 +157,7 @@ igraph_error_t igraph_i_glpk_check(int retval, const char* message) { snprintf(message_and_code, sizeof(message_and_code) / sizeof(message_and_code[0]), "%s (%s)", message, code); - IGRAPH_ERROR(message_and_code, ret); + IGRAPH_ERROR(message_and_code, retval); } #else diff --git a/src/vendor/cigraph/src/internal/glpk_support.h b/src/vendor/cigraph/src/internal/glpk_support.h index b8adb4b7599..9d0ae73b41b 100644 --- a/src/vendor/cigraph/src/internal/glpk_support.h +++ b/src/vendor/cigraph/src/internal/glpk_support.h @@ -33,14 +33,9 @@ #ifdef HAVE_GLPK -#include "igraph_decls.h" -#include "igraph_error.h" - #include #include -__BEGIN_DECLS - 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 */ @@ -51,15 +46,15 @@ typedef struct igraph_i_glpk_error_info_s { extern IGRAPH_THREAD_LOCAL igraph_i_glpk_error_info_t igraph_i_glpk_error_info; -igraph_error_t igraph_i_glpk_check(int retval, const char* message); +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 { \ - igraph_error_t igraph_i_ret = igraph_i_glpk_check(func, message); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ + int igraph_i_ret = igraph_i_glpk_check(func, message); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) { \ return igraph_i_ret; \ } } while (0) @@ -141,8 +136,6 @@ void igraph_i_glp_delete_prob(glp_prob *p); } \ } while (0) -__END_DECLS - -#endif /* HAVE_GLPK */ +#endif -#endif /* IGRAPH_GLPK_SUPPORT_H */ +#endif diff --git a/src/vendor/cigraph/src/internal/hacks.c b/src/vendor/cigraph/src/internal/hacks.c index 09f42206b62..d6653e05e84 100644 --- a/src/vendor/cigraph/src/internal/hacks.c +++ b/src/vendor/cigraph/src/internal/hacks.c @@ -26,15 +26,17 @@ #include #include -/* These are implementations of common C functions that may be missing from some compilers. */ +/* 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) { +char* igraph_i_strdup(const char *s) { size_t n = strlen(s) + 1; - char *result = malloc(sizeof(char) * n); + char* result = (char*)malloc(sizeof(char) * n); if (result) { memcpy(result, s, n); } @@ -42,21 +44,10 @@ char *igraph_i_strdup(const char *s) { } /** - * Drop-in replacement for strndup. - * Used only in compilers that do not have strndup or _strndup + * Drop-in replacement for stpcpy. + * Used only in compilers that do not have stpcpy */ -char *igraph_i_strndup(const char *s1, size_t n) { - size_t i; - /* We need to check if the string is shorter than n characters. - * We could use strlen, but that would do more work for long s1 and small n. - * TODO: Maybe memchr would be nicer here. - */ - for (i = 0; s1[i] != '\0' && i < n; i++) {} - n = i; - char *result = malloc(sizeof(char) * (n + 1)); - if (result) { - memcpy(result, s1, n); - result[n] = '\0'; - } - return result; +char* igraph_i_stpcpy(char* s1, const char* s2) { + char* result = strcpy(s1, s2); + return result + strlen(s1); } diff --git a/src/vendor/cigraph/src/internal/hacks.h b/src/vendor/cigraph/src/internal/hacks.h index 6e9810f5d1a..559ca40f3e3 100644 --- a/src/vendor/cigraph/src/internal/hacks.h +++ b/src/vendor/cigraph/src/internal/hacks.h @@ -24,11 +24,17 @@ #ifndef IGRAPH_HACKS_INTERNAL_H #define IGRAPH_HACKS_INTERNAL_H -#include "igraph_decls.h" - #include "config.h" -#include +#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 @@ -37,9 +43,9 @@ __BEGIN_DECLS char* igraph_i_strdup(const char *s); #endif -#ifndef HAVE_STRNDUP - #define strndup igraph_i_strndup - char* igraph_i_strndup(const char *s, size_t n); +#ifndef HAVE_STPCPY + #define stpcpy igraph_i_stpcpy + char* igraph_i_stpcpy(char* s1, const char* s2); #endif #ifndef HAVE_STRCASECMP @@ -50,19 +56,6 @@ __BEGIN_DECLS #endif #endif -#ifndef HAVE_STRNCASECMP - #ifdef HAVE__STRNICMP - #define strncasecmp _strnicmp - #else - #error "igraph needs strncasecmp() or _strnicmp()" - #endif -#endif - -/* Magic macro to fail the build if certain condition does not hold. See: - * https://stackoverflow.com/questions/4079243/how-can-i-use-sizeof-in-a-preprocessor-macro - */ -#define IGRAPH_STATIC_ASSERT(condition) ((void)sizeof(char[1 - 2*!(condition)])) - __END_DECLS #endif diff --git a/src/vendor/cigraph/src/internal/lsap.c b/src/vendor/cigraph/src/internal/lsap.c index cc38f6ae0ce..bdbe96ccd6c 100644 --- a/src/vendor/cigraph/src/internal/lsap.c +++ b/src/vendor/cigraph/src/internal/lsap.c @@ -24,30 +24,30 @@ #define NOREDUCE 0 typedef struct { - igraph_integer_t n; /* order of problem */ - double **C; /* cost matrix */ - double **c; /* reduced cost matrix */ - igraph_integer_t *s; /* assignment */ - igraph_integer_t *f; /* column i is assigned to f[i] */ - igraph_integer_t na; /* number of assigned items; */ - igraph_integer_t runs; /* number of iterations */ - double cost; /* minimum cost */ - time_t rtime; /* time */ + 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, igraph_integer_t n); +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 igraph_integer_t ap_assignment(AP *p, igraph_integer_t *res); +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 igraph_error_t ap_hungarian(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); */ @@ -60,16 +60,15 @@ static igraph_error_t ap_hungarian(AP *p); /* private functions */ static void preprocess(AP *p); static void preassign(AP *p); -static int cover(AP *p, igraph_integer_t *ri, igraph_integer_t *ci); -static void reduce(AP *p, igraph_integer_t *ri, igraph_integer_t *ci); +static int cover(AP *p, int *ri, int *ci); +static void reduce(AP *p, int *ri, int *ci); -igraph_error_t ap_hungarian(AP *p) { - igraph_integer_t n; /* size of problem */ - igraph_integer_t *ri; /* covered rows */ - igraph_integer_t *ci; /* covered columns */ +int ap_hungarian(AP *p) { + int n; /* size of problem */ + int *ri; /* covered rows */ + int *ci; /* covered columns */ time_t start, end; /* timer */ - igraph_integer_t i, j; - igraph_integer_t ok; + int i, j, ok; start = time(0); @@ -77,14 +76,14 @@ igraph_error_t ap_hungarian(AP *p) { p->runs = 0; /* allocate memory */ - p->s = calloc(1 + n, sizeof(igraph_integer_t)); - p->f = calloc(1 + n, sizeof(igraph_integer_t)); + p->s = calloc(1 + n, sizeof(int)); + p->f = calloc(1 + n, sizeof(int)); - ri = calloc(1 + n, sizeof(igraph_integer_t)); - ci = calloc(1 + n, sizeof(igraph_integer_t)); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("ap_hungarian: could not allocate memory", IGRAPH_ENOMEM); } preprocess(p); @@ -129,12 +128,12 @@ igraph_error_t ap_hungarian(AP *p) { free(ri); free(ci); - return IGRAPH_SUCCESS; + return 0; } /* abbreviated interface */ -igraph_integer_t ap_assignment(AP *p, igraph_integer_t *res) { - igraph_integer_t i; +int ap_assignment(AP *p, int *res) { + int i; if (p->s == NULL) { ap_hungarian(p); @@ -283,8 +282,8 @@ AP *ap_create_problem_from_matrix(double **t, int n) { #endif /* read data from vector */ -AP *ap_create_problem(double *t, igraph_integer_t n) { - igraph_integer_t i, j; +AP *ap_create_problem(double *t, int n) { + int i, j; AP *p; p = (AP*) malloc(sizeof(AP)); @@ -322,7 +321,7 @@ AP *ap_create_problem(double *t, igraph_integer_t n) { /* destructor */ void ap_free(AP *p) { - igraph_integer_t i; + int i; free(p->s); free(p->f); @@ -342,7 +341,7 @@ void ap_free(AP *p) { /* void ap_show_data(AP *p) { - igraph_integer_t i, j; + int i, j; for(i = 1; i <= p->n; i++){ for(j = 1; j <= p->n; j++) @@ -359,7 +358,7 @@ double ap_mincost(AP *p) { return p->cost; } -igraph_integer_t ap_size(AP *p) { +int ap_size(AP *p) { return p->n; } @@ -373,7 +372,7 @@ int ap_iterations(AP *p) { void ap_print_solution(AP *p) { - igraph_integer_t i; + int i; printf("%d itertations, %d secs.\n",p->runs, (int)p->rtime); printf("Min Cost: %10.4f\n",p->cost); @@ -384,7 +383,7 @@ void ap_print_solution(AP *p) } int ap_costmatrix(AP *p, double **m) { - igraph_integer_t i, j; + int i, j; for (i = 0; i < p->n; i++) for (j = 0; j < p->n; j++) { @@ -395,7 +394,7 @@ int ap_costmatrix(AP *p, double **m) { } int ap_datamatrix(AP *p, double **m) { - igraph_integer_t i, j; + int i, j; for (i = 0; i < p->n; i++) for (j = 0; j < p->n; j++) { @@ -421,12 +420,12 @@ void ap_error(char *message) /* by ap_hungarian */ /*************************************************************/ -int cover(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { - igraph_integer_t *mr, i, r; - igraph_integer_t n; +int cover(AP *p, int *ri, int *ci) { + int *mr, i, r; + int n; n = p->n; - mr = calloc(1 + p->n, sizeof(igraph_integer_t)); + mr = calloc(1 + p->n, sizeof(int)); /* reset cover indices */ for (i = 1; i <= n; i++) { @@ -476,8 +475,8 @@ int cover(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { return REDUCE; } -void reduce(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { - igraph_integer_t i, j, n; +void reduce(AP *p, int *ri, int *ci) { + int i, j, n; double min; n = p->n; @@ -506,19 +505,19 @@ void reduce(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { } void preassign(AP *p) { - igraph_integer_t i, j, min, r, c, n, count; - igraph_integer_t *ri, *ci, *rz, *cz; + 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(igraph_integer_t)); - ci = calloc(1 + n, sizeof(igraph_integer_t)); + ri = calloc(1 + n, sizeof(int)); + ci = calloc(1 + n, sizeof(int)); /* row and column counts of zeroes */ - rz = calloc(1 + n, sizeof(igraph_integer_t)); - cz = calloc(1 + n, sizeof(igraph_integer_t)); + rz = calloc(1 + n, sizeof(int)); + cz = calloc(1 + n, sizeof(int)); for (i = 1; i <= n; i++) { count = 0; @@ -540,7 +539,7 @@ void preassign(AP *p) { while (TRUE) { /* find unassigned row with least number of zeroes > 0 */ - min = IGRAPH_INTEGER_MAX; + min = INT_MAX; r = 0; for (i = 1; i <= n; i++) if (rz[i] > 0 && rz[i] < min && ri[i] == UNASSIGNED) { @@ -554,7 +553,7 @@ void preassign(AP *p) { /* find unassigned column in row r with least number of zeroes */ c = 0; - min = IGRAPH_INTEGER_MAX; + min = INT_MAX; for (i = 1; i <= n; i++) if (p->c[r][i] == 0 && cz[i] < min && ci[i] == UNASSIGNED) { min = cz[i]; @@ -586,7 +585,7 @@ void preassign(AP *p) { } void preprocess(AP *p) { - igraph_integer_t i, j, n; + int i, j, n; double min; n = p->n; @@ -620,7 +619,7 @@ void preprocess(AP *p) { * \function igraph_solve_lsap * \brief Solve a balanced linear assignment problem. * - * This functions solves a linear assignment problem using the Hungarian + * 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 @@ -632,7 +631,7 @@ void preprocess(AP *p) { * * * To solve an unbalanced assignment problem, where the number of agents - * is greater than the number of tasks, extra tasks with zero costs + * 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 @@ -647,18 +646,18 @@ void preprocess(AP *p) { * * Time complexity: O(n^3), where n is the number of agents. */ -igraph_error_t igraph_solve_lsap(const igraph_matrix_t *c, igraph_integer_t n, +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)) { + if(n != igraph_matrix_nrow(c)) { IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " - "not equal to number of agents (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + "not equal to number of agents (%ld).", IGRAPH_EINVAL, n, igraph_matrix_nrow(c)); } - if (n != igraph_matrix_ncol(c)) { + if(n != igraph_matrix_ncol(c)) { IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " - "not equal to number of tasks (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + "not equal to number of tasks (%ld).", IGRAPH_EINVAL, n, igraph_matrix_ncol(c)); } IGRAPH_CHECK(igraph_vector_int_resize(p, n)); diff --git a/src/vendor/cigraph/src/internal/pstdint.h b/src/vendor/cigraph/src/internal/pstdint.h new file mode 100644 index 00000000000..bd5697f4bc5 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/internal/qsort.c b/src/vendor/cigraph/src/internal/qsort.c index 48f3003cd7c..f877beb73fd 100644 --- a/src/vendor/cigraph/src/internal/qsort.c +++ b/src/vendor/cigraph/src/internal/qsort.c @@ -45,11 +45,6 @@ #define __unused #endif -#if defined(_MSC_VER) && _MSC_VER < 1927 - /* MSVC does not understand restrict before version 19.27 */ - #define restrict __restrict -#endif - #ifndef __unused #define __unused __attribute__ ((unused)) #endif @@ -76,7 +71,7 @@ static inline char *med3(char *, char *, char *, cmp_t *, void *); */ static inline void -swapfunc(char * restrict a, char * restrict b, size_t es) +swapfunc(char *a, char *b, size_t es) { char t; diff --git a/src/vendor/cigraph/src/internal/utils.c b/src/vendor/cigraph/src/internal/utils.c deleted file mode 100644 index 0698e47dfc8..00000000000 --- a/src/vendor/cigraph/src/internal/utils.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - IGraph library. - Copyright (C) 2023 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_interface.h" - -#include "internal/utils.h" - -/** - * \function igraph_i_matrix_subset_vertices - * \brief Subsets a matrix whose rows/columns correspond to graph vertices. - * - * This is a convenience function to subset a matrix computed from a graph. - * It takes a matrix whose rows and columns correspond to the vertices - * of a graph, and subsets it in-place to retain only some of the vertices. - * - * \param m A square matrix with the same number of rows/columns as the vertex - * count of \p graph. It will be modified in-place, deleting rows \em not present - * in \p from and columns \em not present in \p to. - * \param graph The corresponding graph. m[u,v] is assumed to contain - * a value associated with vertices \c u and \c v of \p graph, e.g. the graph - * distance between them, their similarity, etc. - * \param from Vertex set, these rows of the matrix will be retained. - * \param to Vertex set, these columns of the matrix will be retained. - * \return Error code. - * - * Time complexity: - * O(1) when taking all vertices, - * O(|from|*|to|) otherwise where |from| and |to| denote the size - * of the source and target vertex sets. - */ -igraph_error_t igraph_i_matrix_subset_vertices( - igraph_matrix_t *m, - const igraph_t *graph, - igraph_vs_t from, - igraph_vs_t to) { - - /* Assertion: the size of 'm' agrees with 'graph': */ - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t ncol = igraph_matrix_ncol(m); - igraph_integer_t nrow = igraph_matrix_nrow(m); - - IGRAPH_ASSERT(nrow == no_of_nodes && nrow == ncol); - - /* When taking all vertices, nothing needs to be done: */ - - if (igraph_vs_is_all(&from) && igraph_vs_is_all(&to)) { - return IGRAPH_SUCCESS; - } - - /* Otherwise, allocate a temporary matrix to copy the data into: */ - - igraph_vit_t fromvit, tovit; - igraph_matrix_t tmp; - - IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); - IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); - - IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); - IGRAPH_FINALLY(igraph_vit_destroy, &tovit); - - IGRAPH_MATRIX_INIT_FINALLY(&tmp, IGRAPH_VIT_SIZE(fromvit), IGRAPH_VIT_SIZE(tovit)); - - for (igraph_integer_t j=0; ! IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { - igraph_integer_t i; - for (IGRAPH_VIT_RESET(fromvit), i=0; ! IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { - MATRIX(tmp, i, j) = MATRIX(*m, IGRAPH_VIT_GET(fromvit), IGRAPH_VIT_GET(tovit)); - } - } - - /* This is O(1) time */ - IGRAPH_CHECK(igraph_matrix_swap(m, &tmp)); - - igraph_matrix_destroy(&tmp); - igraph_vit_destroy(&tovit); - igraph_vit_destroy(&fromvit); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/internal/utils.h b/src/vendor/cigraph/src/internal/utils.h deleted file mode 100644 index 94be451fc3a..00000000000 --- a/src/vendor/cigraph/src/internal/utils.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - 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 . -*/ - -#ifndef IGRAPH_INTERNAL_UTILS_H -#define IGRAPH_INTERNAL_UTILS_H - -#include "igraph_datatype.h" -#include "igraph_iterators.h" -#include "igraph_matrix.h" - -igraph_error_t igraph_i_matrix_subset_vertices( - igraph_matrix_t *m, - const igraph_t *graph, - igraph_vs_t from, - igraph_vs_t to); - -#endif /* IGRAPH_INTERNAL_UTILS_H */ diff --git a/src/vendor/cigraph/src/internal/zeroin.c b/src/vendor/cigraph/src/internal/zeroin.c index 89ade546255..3d1d8d0abd2 100644 --- a/src/vendor/cigraph/src/internal/zeroin.c +++ b/src/vendor/cigraph/src/internal/zeroin.c @@ -89,16 +89,16 @@ #define EPSILON DBL_EPSILON -igraph_error_t igraph_zeroin( /* An estimate of the root */ +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 */ + 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 */ + 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) */ + fa, fb, fc; /* f(a), f(b), f(c) */ igraph_real_t tol; int maxit; @@ -111,27 +111,30 @@ igraph_error_t igraph_zeroin( /* An estimate of the root */ *Tol = 0.0; *Maxit = 0; *res = a; - return IGRAPH_SUCCESS; + return 0; } if (fb == 0.0) { *Tol = 0.0; *Maxit = 0; *res = b; - return IGRAPH_SUCCESS; + 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 calculated in the form p/q; */ - igraph_real_t q; /* division operations are delayed until the last moment */ - igraph_real_t new_step; /* Step at this iteration */ + 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 best approximation */ - a = b; b = c; c = a; + /* 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; @@ -141,18 +144,18 @@ igraph_error_t igraph_zeroin( /* An estimate of the root */ *Maxit -= maxit; *Tol = fabs(c - b); *res = b; - return IGRAPH_SUCCESS; /* Acceptable approx. is found */ + 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 */ + * 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 */ + /* points linear interpolation */ t1 = fb / fa; /* can only be applied */ p = cb * t1; q = 1.0 - t1; diff --git a/src/vendor/cigraph/src/io/dimacs.c b/src/vendor/cigraph/src/io/dimacs.c index dcbc3717759..c615b269604 100644 --- a/src/vendor/cigraph/src/io/dimacs.c +++ b/src/vendor/cigraph/src/io/dimacs.c @@ -30,83 +30,42 @@ #include -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ -#define IGRAPH_DIMACS_MAX_VERTEX_COUNT (1 << 20) -#define IGRAPH_DIMACS_MAX_EDGE_COUNT (1 << 20) -#else -#define IGRAPH_DIMACS_MAX_VERTEX_COUNT INT32_MAX -#define IGRAPH_DIMACS_MAX_EDGE_COUNT INT32_MAX -#endif - /** * \function igraph_read_graph_dimacs - * \brief Read a graph in DIMACS format (deprecated alias). - * - * \deprecated-by igraph_read_graph_dimacs_flow 0.10.0 - */ -igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, - igraph_strvector_t *problem, - igraph_vector_int_t *label, - igraph_integer_t *source, - igraph_integer_t *target, - igraph_vector_t *capacity, - igraph_bool_t directed) { - return igraph_read_graph_dimacs_flow( - graph, instream, problem, label, source, target, capacity, directed - ); -} - -#define EXPECT(actual, expected) \ - do { \ - if ((actual) != (expected)) { \ - IGRAPH_ERROR("Reading DIMACS flow problem file failed.", IGRAPH_PARSEERROR); \ - } \ - } while (0) - -/** - * \function igraph_read_graph_dimacs_flow * \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 - * http://archive.dimacs.rutgers.edu/pub/netflow/general-info/ + * 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 c the line is a comment line and it is - * ignored. There is one problem line (\c p in the file), it + * 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 - * (\c max or \c edge), the number of vertices, - * and number of edges in the graph. In MAX problems, - * exactly two node identification lines are expected - * (\c n), one for the source, and one for the target vertex. - * These have two fields: the ID of the vertex and the type of the - * vertex, either \c s ( = source) or \c t ( = target). - * Arc lines start with \c a and have three fields: the source vertex, - * the target vertex and the edge capacity. In EDGE problems, - * there may be a node line (\c n) for each node. It specifies the - * node index and an integer node label. Nodes for which no explicit - * label was specified will use their index as label. In EDGE problems, - * each edge is specified as an edge line (\c e). + * (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. * * - * Within DIMACS files, vertex IDs are numbered from 1. - * + * Vertex ids are numbered from 1. * \param graph Pointer to an uninitialized graph object. * \param instream The file to read from. - * \param problem If not \c NULL, it will contain the problem type. - * \param label If not \c NULL, node labels will be stored here for \c edge - * problems. Ignored for \c max problems. - * \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 \c NULL. - * \param target Pointer to an integer, the (igraph) ID of the target - * node will be stored here. It is ignored if \c NULL. + * \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. + * the edges will be stored here if not NULL. * \param directed Boolean, whether to create a directed graph. * \return Error code. * @@ -115,36 +74,34 @@ igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, * * \sa \ref igraph_write_graph_dimacs() */ -igraph_error_t igraph_read_graph_dimacs_flow( - igraph_t *graph, FILE *instream, - igraph_strvector_t *problem, - igraph_vector_int_t *label, - igraph_integer_t *source, - igraph_integer_t *target, - igraph_vector_t *capacity, - igraph_bool_t directed) { - - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes = -1; - igraph_integer_t no_of_edges = -1; - igraph_integer_t tsource = -1; - igraph_integer_t ttarget = -1; +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; - enum { - PROBLEM_NONE, - PROBLEM_EDGE, - PROBLEM_MAX - } problem_type = PROBLEM_NONE; + int problem_type = 0; + +#define PROBLEM_EDGE 1 +#define PROBLEM_MAX 2 - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (capacity) { igraph_vector_clear(capacity); } while (!feof(instream)) { int read; - char str[2]; + char str[3]; IGRAPH_ALLOW_INTERRUPTION(); @@ -152,10 +109,12 @@ igraph_error_t igraph_read_graph_dimacs_flow( if (feof(instream)) { break; } - EXPECT(read, 1); + if (read != 1) { + IGRAPH_ERROR("parsing dimacs file failed", IGRAPH_PARSEERROR); + } switch (str[0]) { - igraph_integer_t tmp, tmp2; - igraph_integer_t from, to; + long int tmp, tmp2; + long int from, to; igraph_real_t cap; case 'c': @@ -164,29 +123,23 @@ igraph_error_t igraph_read_graph_dimacs_flow( case 'p': if (no_of_nodes != -1) { - IGRAPH_ERROR("Reading DIMACS file failed, double 'p' line.", + IGRAPH_ERROR("reading dimacs file failed, double 'p' line", IGRAPH_PARSEERROR); } - read = fscanf(instream, "%20s %" IGRAPH_PRId " %" IGRAPH_PRId "", prob, + read = fscanf(instream, "%20s %li %li", prob, &no_of_nodes, &no_of_edges); - EXPECT(read, 3); - if (no_of_nodes > IGRAPH_DIMACS_MAX_VERTEX_COUNT) { - IGRAPH_ERROR("Vertex count too large in DIMACS file.", IGRAPH_PARSEERROR); - } - if (no_of_nodes < 0) { - IGRAPH_ERROR("Invalid (negative) vertex count in DIMACS file.", IGRAPH_PARSEERROR); - } - if (no_of_edges > IGRAPH_DIMACS_MAX_EDGE_COUNT) { - IGRAPH_ERROR("Edge count too large in DIMACS file.", IGRAPH_PARSEERROR); - } - if (no_of_edges < 0) { - IGRAPH_ERROR("Invalid (negative) edge count in DIMACS file.", IGRAPH_PARSEERROR); + if (read != 3) { + IGRAPH_ERROR("reading dimacs file failed", IGRAPH_PARSEERROR); } if (!strcmp(prob, "edge")) { /* edge list */ problem_type = PROBLEM_EDGE; if (label) { - IGRAPH_CHECK(igraph_vector_int_range(label, 1, no_of_nodes+1)); + 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 */ @@ -195,14 +148,14 @@ igraph_error_t igraph_read_graph_dimacs_flow( IGRAPH_CHECK(igraph_vector_reserve(capacity, no_of_edges)); } } else { - IGRAPH_ERROR("Unknown problem type, should be 'edge' or 'max'.", + IGRAPH_ERROR("Unknown problem type, should be 'edge' or 'max'", IGRAPH_PARSEERROR); } if (problem) { igraph_strvector_clear(problem); - IGRAPH_CHECK(igraph_strvector_push_back(problem, prob)); + IGRAPH_CHECK(igraph_strvector_add(problem, prob)); } - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); break; case 'n': @@ -210,35 +163,28 @@ igraph_error_t igraph_read_graph_dimacs_flow( for EDGE this is a vertex label */ if (problem_type == PROBLEM_MAX) { str[0] = 'x'; - read = fscanf(instream, "%" IGRAPH_PRId " %1s", &tmp, str); - EXPECT(read, 2); + read = fscanf(instream, "%li %1s", &tmp, str); if (str[0] == 's') { if (tsource != -1) { - IGRAPH_ERROR("Reading DIMACS file: multiple source vertex line.", + IGRAPH_ERROR("reading dimacsfile: multiple source vertex line", IGRAPH_PARSEERROR); } else { tsource = tmp; } } else if (str[0] == 't') { if (ttarget != -1) { - IGRAPH_ERROR("Reading DIMACS file: multiple target vertex line.", + IGRAPH_ERROR("reading dimacsfile: multiple target vertex line", IGRAPH_PARSEERROR); } else { ttarget = tmp; } } else { - IGRAPH_ERROR("Invalid node descriptor line in DIMACS file.", + IGRAPH_ERROR("invalid node descriptor line in dimacs file", IGRAPH_PARSEERROR); } - } else { /* PROBLEM_EDGE */ - read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &tmp, &tmp2); - EXPECT(read, 1); + } else { + read = fscanf(instream, "%li %li", &tmp, &tmp2); if (label) { - if (tmp < 0 || tmp >= no_of_nodes) { - IGRAPH_ERRORF("Invalid node index %" IGRAPH_PRId " in DIMACS file. " - "Number of nodes was given as %" IGRAPH_PRId".", - IGRAPH_PARSEERROR, tmp, no_of_nodes); - } VECTOR(*label)[tmp] = tmp2; } } @@ -248,13 +194,15 @@ igraph_error_t igraph_read_graph_dimacs_flow( 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_ERROR("'a' lines are allowed only in MAX problem files", IGRAPH_PARSEERROR); } - read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %lf", &from, &to, &cap); - EXPECT(read, 3); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to - 1)); + 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)); } @@ -263,17 +211,19 @@ igraph_error_t igraph_read_graph_dimacs_flow( case 'e': /* Edge line, only in EDGE */ if (problem_type != PROBLEM_EDGE) { - IGRAPH_ERROR("'e' lines are allowed only in EDGE problem files.", + IGRAPH_ERROR("'e' lines are allowed only in EDGE problem files", IGRAPH_PARSEERROR); } - read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &from, &to); - EXPECT(read, 2); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to - 1)); + 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); + IGRAPH_ERROR("unknown line type in dimacs file", IGRAPH_PARSEERROR); } /* Go to next line */ @@ -281,34 +231,23 @@ igraph_error_t igraph_read_graph_dimacs_flow( } if (source) { - *source = tsource - 1; + *source = (igraph_integer_t) tsource - 1; } if (target) { - *target = ttarget - 1; + *target = (igraph_integer_t) ttarget - 1; } - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, + directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_write_graph_dimacs - * \brief Write a graph in DIMACS format (deprecated alias). - * - * \deprecated-by igraph_write_graph_dimacs_flow 0.10.0 - */ -igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, - igraph_integer_t source, igraph_integer_t target, - const igraph_vector_t *capacity) { - return igraph_write_graph_dimacs_flow(graph, outstream, source, target, capacity); -} - -/** - * \function igraph_write_graph_dimacs_flow * \brief Write a graph in DIMACS format. * * This function writes a graph to an output stream in DIMACS format, @@ -317,7 +256,7 @@ igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, * * * This file format is discussed in the documentation of \ref - * igraph_read_graph_dimacs_flow(), see that for more information. + * igraph_read_graph_dimacs(), see that for more information. * * \param graph The graph to write to the stream. * \param outstream The stream. @@ -330,16 +269,16 @@ igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, * * Time complexity: O(|E|), the number of edges in the graph. * - * \sa \ref igraph_read_graph_dimacs_flow() + * \sa igraph_read_graph_dimacs() */ -igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, - igraph_integer_t source, igraph_integer_t target, +int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + long int source, long int target, const igraph_vector_t *capacity) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_eit_t it; - igraph_integer_t i = 0; + long int i = 0; int ret, ret1, ret2, ret3; if (igraph_vector_size(capacity) != no_of_edges) { @@ -351,7 +290,7 @@ igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outst IGRAPH_FINALLY(igraph_eit_destroy, &it); ret = fprintf(outstream, - "c created by igraph\np max %" IGRAPH_PRId " %" IGRAPH_PRId "\nn %" IGRAPH_PRId " s\nn %" IGRAPH_PRId " t\n", + "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); @@ -363,8 +302,8 @@ igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outst igraph_real_t cap; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); cap = VECTOR(*capacity)[i++]; - ret1 = fprintf(outstream, "a %" IGRAPH_PRId " %" IGRAPH_PRId " ", - from + 1, to + 1); + 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) { @@ -375,5 +314,5 @@ igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outst igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/dl-header.h b/src/vendor/cigraph/src/io/dl-header.h index b34910957aa..fe4d340e57e 100644 --- a/src/vendor/cigraph/src/io/dl-header.h +++ b/src/vendor/cigraph/src/io/dl-header.h @@ -25,14 +25,6 @@ #include "core/trie.h" -/* TODO: Find out maximum supported vertex count. */ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ -#define IGRAPH_DL_MAX_VERTEX_COUNT (1 << 20) -#else -#define IGRAPH_DL_MAX_VERTEX_COUNT INT32_MAX -#endif - typedef enum { IGRAPH_DL_MATRIX, IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 } igraph_i_dl_type_t; @@ -40,14 +32,13 @@ typedef enum { IGRAPH_DL_MATRIX, typedef struct { void *scanner; int eof; - char errmsg[300]; - igraph_error_t igraph_errno; int mode; - igraph_integer_t n; - igraph_integer_t from, to; - igraph_vector_int_t edges; + 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/vendor/cigraph/src/io/dl-lexer.l b/src/vendor/cigraph/src/io/dl-lexer.l index e4a96c4b993..a1f69740e9a 100644 --- a/src/vendor/cigraph/src/io/dl-lexer.l +++ b/src/vendor/cigraph/src/io/dl-lexer.l @@ -44,6 +44,7 @@ */ +#include "config.h" #include #include @@ -70,8 +71,6 @@ %option reentrant %option bison-bridge %option bison-locations -%option yylineno -%option caseless digit [0-9] whitespace [ \t\v\f] @@ -80,13 +79,14 @@ whitespace [ \t\v\f] %% -<*>\n\r|\r\n|\r|\n { return NEWLINE; } +<*>\n\r|\r\n|\r|\n { return NEWLINE; } -dl{whitespace}+ { return DL; } -n{whitespace}*[=]{whitespace}* { return NEQ; } -{digit}+ { return NUM; } +[dD][lL]{whitespace}+ { return DL; } +[nN]{whitespace}*[=]{whitespace}* { + return NEQ; } +{digit}+ { return NUM; } -data: { +[dD][aA][tT][aA][:] { switch (yyextra->mode) { case 0: BEGIN(FULLMATRIX); break; @@ -97,29 +97,29 @@ n{whitespace}*[=]{whitespace}* { return NEQ; } } return DATA; } -labels: { BEGIN(LABELM); return LABELS; } -labels{whitespace}+embedded:?{whitespace}* { +[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; } -format{whitespace}*[=]{whitespace}*fullmatrix{whitespace}* { +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[fF][uU][lL][lL][mM][aA][tT][rR][iI][xX]{whitespace}* { yyextra->mode=0; return FORMATFULLMATRIX; } -format{whitespace}*[=]{whitespace}*edgelist1{whitespace}* { +[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[eE][dD][gG][eE][lL][iI][sS][tT][1]{whitespace}* { yyextra->mode=1; return FORMATEDGELIST1; } -format{whitespace}*[=]{whitespace}*nodelist1{whitespace}* { +[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\0]+{whitespace}* { return LABEL; } +[^, \t\n\r\f\v]+{whitespace}* { return LABEL; } {digit}{whitespace}* { return DIGIT; } -[^ \t\n\r\v\f\0,]+ { return LABEL; } +[^ \t\n\r\v\f,]+ { return LABEL; } {whitespace} { } -(\+|\-)?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } -[^ \t\n\r\v\f\0,]+ { return LABEL; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[^ \t\n\r\v\f,]+ { return LABEL; } {whitespace}* { } {digit}+ { return NUM; } -[^ \t\r\n\v\f\0,]+ { return LABEL; } +[^ \t\r\n\v\f,]+ { return LABEL; } {whitespace}* { } {whitespace}+ { /* eaten up */ } diff --git a/src/vendor/cigraph/src/io/dl-parser.y b/src/vendor/cigraph/src/io/dl-parser.y index ce58f28cfc7..f62d16230f4 100644 --- a/src/vendor/cigraph/src/io/dl-parser.y +++ b/src/vendor/cigraph/src/io/dl-parser.y @@ -44,22 +44,27 @@ */ +#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 "io/parse_utils.h" + +#include int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s); -static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t length, +int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context); -static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t to, +int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context); -static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t to, +int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context); -static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid); + +extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); #define scanner context->scanner @@ -76,40 +81,32 @@ static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid); %lex-param { void* scanner } %union { - igraph_integer_t integer; + long int integer; igraph_real_t real; }; %type integer elabel; %type weight; -%token NUM "number" -%token NEWLINE "end of line" -%token DL "DL" -%token NEQ "n=vertexcount" -%token DATA "data:" -%token LABELS "labels:" -%token LABELSEMBEDDED "labels embedded:" +%token NUM +%token NEWLINE +%token DL +%token NEQ +%token DATA +%token LABELS +%token LABELSEMBEDDED %token FORMATFULLMATRIX %token FORMATEDGELIST1 %token FORMATNODELIST1 -%token DIGIT "binary digit" -%token LABEL "label" +%token DIGIT +%token LABEL %token EOFF %token END 0 "end of file" /* friendly name for $end */ %token ERROR %% -input: DL NEQ integer NEWLINE rest trail eof { - context->n=$3; - if (context->n < 0) { - IGRAPH_YY_ERRORF("Invalid vertex count in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); - } - if (context->n > IGRAPH_DL_MAX_VERTEX_COUNT) { - IGRAPH_YY_ERRORF("Vertex count too large in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); - } -}; +input: DL NEQ integer NEWLINE rest trail eof { context->n=$3; }; trail: | trail newline; @@ -131,9 +128,9 @@ fullmatrix: DATA newline fullmatrixdata { } labels: {} /* nothing, empty matrix */ | labels newline LABEL { - IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - context)); } + igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); } ; fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { @@ -144,16 +141,11 @@ fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { zerooneseq: | zerooneseq zeroone { } ; zeroone: DIGIT { - /* TODO: What if the digit is neither 0 or 1? Are multigraphs allowed? */ - char c = igraph_dl_yyget_text(scanner)[0]; - if (c == '1') { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + if (igraph_dl_yyget_text(scanner)[0]=='1') { + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->from)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, context->to)); - } else if (c != '0') { - IGRAPH_YY_ERRORF("Unexpected digit '%c' in adjacency matrix in DL file.", - IGRAPH_EINVAL, c); } context->to += 1; } ; @@ -164,9 +156,9 @@ reallabeledfullmatrixdata: labelseq NEWLINE labeledmatrixlines {} ; labelseq: | labelseq newline label ; -label: LABEL { IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - context)); }; +label: LABEL { igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context); }; labeledmatrixlines: labeledmatrixline { context->from += 1; @@ -195,58 +187,39 @@ edgelist1data: {} /* nothing, empty graph */ ; edgelist1dataline: integer integer weight NEWLINE { - igraph_integer_t from = $1, to = $2; - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); - IGRAPH_YY_CHECK(igraph_i_dl_add_edge_w(from-1, to-1, $3, context)); } + igraph_i_dl_add_edge_w($1-1, $2-1, $3, context); } | integer integer NEWLINE { - igraph_integer_t from = $1, to = $2; - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); - IGRAPH_YY_CHECK(igraph_i_dl_add_edge(from-1, to-1, context)); + igraph_i_dl_add_edge($1-1, $2-1, context); } ; -integer: NUM { - igraph_integer_t val; - IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - &val)); - $$=val; -}; +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_YY_CHECK(igraph_i_dl_add_edge_w($1, $2, $3, context)); } + igraph_i_dl_add_edge_w($1, $2, $3, context); } | elabel elabel NEWLINE { - IGRAPH_YY_CHECK(igraph_i_dl_add_edge($1, $2, context)); + igraph_i_dl_add_edge($1, $2, context); }; -weight: NUM { - igraph_real_t val; - IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - &val)); - $$=val; -}; +weight: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); }; elabel: LABEL { - igraph_integer_t trie_id; - /* Copy label list to trie, if needed */ if (igraph_strvector_size(&context->labels) != 0) { - igraph_integer_t i, id, n=igraph_strvector_size(&context->labels); + long int i, id, n=igraph_strvector_size(&context->labels); for (i=0; itrie, STR(context->labels, i), &id)); + igraph_trie_get(&context->trie, + STR(context->labels, i), &id); } igraph_strvector_clear(&context->labels); } - IGRAPH_YY_CHECK(igraph_trie_get_len(&context->trie, igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), &trie_id)); - IGRAPH_ASSERT(0 <= trie_id && trie_id < IGRAPH_DL_MAX_VERTEX_COUNT); - $$ = trie_id; + igraph_trie_get2(&context->trie, igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), &$$); }; /*-----------------------------------------------------------*/ @@ -266,19 +239,13 @@ nodelist1data: {} /* nothing, empty graph */ nodelist1dataline: from tolist NEWLINE {} ; -from: NUM { - IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - &context->from)); - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(context->from)); -} ; +from: NUM { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner)); } ; tolist: {} | tolist integer { - igraph_integer_t to = $2; - IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, - context->from-1)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, to-1)); + 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 */ @@ -292,63 +259,49 @@ fromelabel: elabel { }; labeltolist: | labeltolist elabel { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, - context->from)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, $2)); + 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, +int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, const char *s) { - snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, - "Parse error in DL file, line %i (%s)", - locp->first_line, s); + snprintf(context->errmsg, + sizeof(context->errmsg)/sizeof(char)-1, + "%s in line %i", s, locp->first_line); return 0; } -static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t length, +int igraph_i_dl_add_str(char *newstr, int length, igraph_i_dl_parsedata_t *context) { - IGRAPH_CHECK(igraph_strvector_push_back_len(&context->labels, newstr, length)); - return IGRAPH_SUCCESS; + int tmp=newstr[length]; + newstr[length]='\0'; + IGRAPH_CHECK(igraph_strvector_add(&context->labels, newstr)); + newstr[length]=tmp; + return 0; } -static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t to, +int igraph_i_dl_add_edge(long int from, long int to, igraph_i_dl_parsedata_t *context) { - //IGRAPH_CHECK(igraph_i_dl_check_vid(from+1)); - //IGRAPH_CHECK(igraph_i_dl_check_vid(to+1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, to)); - return IGRAPH_SUCCESS; + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&context->edges, to)); + return 0; } -static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t to, +int igraph_i_dl_add_edge_w(long int from, long int to, igraph_real_t weight, igraph_i_dl_parsedata_t *context) { - igraph_integer_t n=igraph_vector_size(&context->weights); - igraph_integer_t n2=igraph_vector_int_size(&context->edges)/2; + long int n=igraph_vector_size(&context->weights); + long int n2=igraph_vector_size(&context->edges)/2; if (n != n2) { - IGRAPH_CHECK(igraph_vector_resize(&context->weights, 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 IGRAPH_SUCCESS; -} - -/* Raise an error if the vertex index is invalid in the DL file. - * DL files use 1-based vertex indices. */ -static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid) { - if (dl_vid < 1) { - IGRAPH_ERRORF("Invalid vertex index in DL file: %" IGRAPH_PRId ".", - IGRAPH_EINVAL, dl_vid); - } - if (dl_vid > IGRAPH_DL_MAX_VERTEX_COUNT) { - IGRAPH_ERRORF("Vertex index too large in DL file: %" IGRAPH_PRId ".", - IGRAPH_EINVAL, dl_vid); - } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/dl.c b/src/vendor/cigraph/src/io/dl.c index 3f484b744e5..8eb868fbce6 100644 --- a/src/vendor/cigraph/src/io/dl.c +++ b/src/vendor/cigraph/src/io/dl.c @@ -26,7 +26,6 @@ #include "igraph_interface.h" #include "io/dl-header.h" -#include "io/parsers/dl-parser.h" int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined, void* scanner); @@ -41,7 +40,7 @@ void igraph_dl_yylex_destroy_wrapper (void *scanner ) { /** * \function igraph_read_graph_dl - * \brief Reads a file in the DL format of UCINET. + * \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 @@ -64,10 +63,11 @@ void igraph_dl_yylex_destroy_wrapper (void *scanner ) { * \example examples/simple/igraph_read_graph_dl.c */ -igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, +int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed) { - igraph_integer_t n, n2; + 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; @@ -80,10 +80,8 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, context.n = -1; context.from = 0; context.to = 0; - context.errmsg[0] = '\0'; - context.igraph_errno = IGRAPH_SUCCESS; - IGRAPH_VECTOR_INT_INIT_FINALLY(&context.edges, 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); @@ -94,39 +92,20 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_dl_yyset_in(instream, context.scanner); - /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ - IGRAPH_FINALLY_ENTER(); - int err = igraph_dl_yyparse(&context); - IGRAPH_FINALLY_EXIT(); - switch (err) { - case 0: /* success */ - break; - case 1: /* parse error */ + i = igraph_dl_yyparse(&context); + if (i != 0) { if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); - } else if (context.igraph_errno != IGRAPH_SUCCESS) { - IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read DL file.", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read DL file", IGRAPH_PARSEERROR); } - break; - case 2: /* out of memory */ - IGRAPH_ERROR("Cannot read DL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - default: /* must never reach here */ - /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison - * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being - * returned in place of a Bison error code. - * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? - */ - IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading DL file.", err); } /* Extend the weight vector, if needed */ n = igraph_vector_size(&context.weights); - n2 = igraph_vector_int_size(&context.edges) / 2; + n2 = igraph_vector_size(&context.edges) / 2; if (n != 0) { - IGRAPH_CHECK(igraph_vector_resize(&context.weights, n2)); + igraph_vector_resize(&context.weights, n2); for (; n < n2; n++) { VECTOR(context.weights)[n] = IGRAPH_NAN; } @@ -134,7 +113,7 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, /* Check number of vertices */ if (n2 > 0) { - n = igraph_vector_int_max(&context.edges); + n = (long int) igraph_vector_max(&context.edges); } else { n = 0; } @@ -143,13 +122,15 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, context.n = n; } - /* Prepare attributes */ + /* 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) { - namevec = igraph_i_trie_borrow_keys(&context.trie); + igraph_trie_getkeys(&context.trie, &namevec); } if (namevec) { IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); @@ -172,10 +153,7 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - IGRAPH_FINALLY(igraph_destroy, graph); - IGRAPH_CHECK(igraph_add_vertices(graph, context.n, pname)); + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) context.n, pname)); IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight)); if (pweight) { @@ -193,10 +171,10 @@ igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_trie_destroy(&context.trie); igraph_strvector_destroy(&context.labels); - igraph_vector_int_destroy(&context.edges); + igraph_vector_destroy(&context.edges); igraph_vector_destroy(&context.weights); igraph_dl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/dot.c b/src/vendor/cigraph/src/io/dot.c index e12619bb596..1c99a061a7d 100644 --- a/src/vendor/cigraph/src/io/dot.c +++ b/src/vendor/cigraph/src/io/dot.c @@ -29,24 +29,24 @@ #include "igraph_version.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp & strdup */ +#include "internal/hacks.h" /* strcasecmp */ #include #include -#define CHECK(cmd) do { int ret=cmd; if (ret<0) IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_EFILE); } while (0) +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write DOT format failed.", IGRAPH_EFILE); } while (0) -static igraph_error_t igraph_i_dot_escape(const char *orig, char **result) { +static int igraph_i_dot_escape(const char *orig, char **result) { /* do we have to escape the string at all? */ - igraph_integer_t i, j, len = strlen(orig), newlen = 0; - igraph_bool_t need_quote = false, is_number = true; + 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, or empty */ if (!strcasecmp(orig, "graph") || !strcasecmp(orig, "digraph") || !strcasecmp(orig, "node") || !strcasecmp(orig, "edge") || !strcasecmp(orig, "strict") || !strcasecmp(orig, "subgraph") || len == 0) { - need_quote = true; - is_number = false; + need_quote = 1; + is_number = 0; } /* next, check whether we need to escape the string for any other reason. @@ -83,12 +83,12 @@ static igraph_error_t igraph_i_dot_escape(const char *orig, char **result) { if (is_number || !need_quote) { *result = strdup(orig); if (!*result) { - IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); } (*result)[0] = '"'; (*result)[newlen + 1] = '"'; @@ -116,7 +116,7 @@ static igraph_error_t igraph_i_dot_escape(const char *orig, char **result) { /** * \function igraph_write_graph_dot - * \brief Write the graph to a stream in DOT format. + * \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 @@ -135,13 +135,14 @@ static igraph_error_t igraph_i_dot_escape(const char *orig, char **result) { * * \example examples/simple/dot.c */ -igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { - igraph_integer_t i, j; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); +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_int_t gtypes, vtypes, etypes; + igraph_vector_t gtypes, vtypes, etypes; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; @@ -149,9 +150,9 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(>ypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 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, @@ -173,28 +174,26 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } /* Write the graph attributes */ - if (igraph_vector_int_size(>ypes) > 0) { + if (igraph_vector_size(>ypes) > 0) { CHECK(fprintf(outstream, " graph [\n")); - for (i = 0; i < igraph_vector_int_size(>ypes); i++) { - const char *name; - char *newname; - name = igraph_strvector_get(&gnames, i); + 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] == (igraph_integer_t)VECTOR(numv)[0]) { - CHECK(fprintf(outstream, " %s=%" IGRAPH_PRId "\n", newname, (igraph_integer_t)VECTOR(numv)[0])); + 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) { - const char *s; - char *news; + char *s, *news; IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); - s = igraph_strvector_get(&strv, 0); + 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); @@ -212,13 +211,12 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } /* Write the vertices */ - if (igraph_vector_int_size(&vtypes) > 0) { + if (igraph_vector_size(&vtypes) > 0) { for (i = 0; i < no_of_nodes; i++) { - CHECK(fprintf(outstream, " %" IGRAPH_PRId " [\n", i)); - for (j = 0; j < igraph_vector_int_size(&vtypes); j++) { - const char *name; - char *newname; - name = igraph_strvector_get(&vnames, j); + 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) { @@ -232,15 +230,14 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { CHECK(fputc('\n', outstream)); } } else if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - char *news; - IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(i), &strv)); - s = igraph_strvector_get(&strv, 0); + 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(i), &boolv)); + 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 { @@ -253,21 +250,20 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } } else { for (i = 0; i < no_of_nodes; i++) { - CHECK(fprintf(outstream, " %" IGRAPH_PRId ";\n", i)); + CHECK(fprintf(outstream, " %ld;\n", i)); } } CHECK(fprintf(outstream, "\n")); /* Write the edges */ - if (igraph_vector_int_size(&etypes) > 0) { + if (igraph_vector_size(&etypes) > 0) { for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId " [\n", from, edgeop, to)); - for (j = 0; j < igraph_vector_int_size(&etypes); j++) { - const char *name; - char *newname; - name = igraph_strvector_get(&enames, j); + 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) { @@ -281,17 +277,16 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { CHECK(fputc('\n', outstream)); } } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - char *news; + char *s, *news; IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, - name, igraph_ess_1(i), &strv)); - s = igraph_strvector_get(&strv, 0); + 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(i), &boolv)); + 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 { @@ -304,9 +299,9 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } } else { for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId ";\n", from, edgeop, to)); + 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")); @@ -314,9 +309,9 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { igraph_vector_bool_destroy(&boolv); igraph_strvector_destroy(&strv); igraph_vector_destroy(&numv); - igraph_vector_int_destroy(&etypes); - igraph_vector_int_destroy(&vtypes); - igraph_vector_int_destroy(>ypes); + igraph_vector_destroy(&etypes); + igraph_vector_destroy(&vtypes); + igraph_vector_destroy(>ypes); igraph_strvector_destroy(&enames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&gnames); diff --git a/src/vendor/cigraph/src/io/edgelist.c b/src/vendor/cigraph/src/io/edgelist.c index 90797a5daf1..1dea9d62fdc 100644 --- a/src/vendor/cigraph/src/io/edgelist.c +++ b/src/vendor/cigraph/src/io/edgelist.c @@ -27,7 +27,8 @@ #include "igraph_iterators.h" #include "core/interruption.h" -#include "io/parse_utils.h" + +#include /** * \section about_loadsave @@ -36,8 +37,7 @@ * from a file. * * They assume that the current locale uses a decimal point and not - * a decimal comma. See \ref igraph_enter_safelocale() and - * \ref igraph_exit_safelocale() for more information. + * a decimal comma. * * Note that as \a igraph uses the traditional C streams, it is * possible to read/write files from/to memory, at least on GNU @@ -54,7 +54,6 @@ * 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 @@ -69,44 +68,52 @@ * * 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. + * reading an integer requires O(1) + * time. */ -igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, +int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, igraph_integer_t n, igraph_bool_t directed) { - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t from, to; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; + long int from, to; + int c; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 100)); - - for (;;) { - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 100)); - IGRAPH_CHECK(igraph_i_fskip_whitespace(instream)); + /* skip all whitespace */ + do { + c = getc (instream); + } while (isspace (c)); + ungetc (c, instream); - if (feof(instream)) break; + while (!feof(instream)) { + int read; - IGRAPH_CHECK(igraph_i_fget_integer(instream, &from)); - IGRAPH_CHECK(igraph_i_fget_integer(instream, &to)); + IGRAPH_ALLOW_INTERRUPTION(); -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* Protect from very large memory allocations when fuzzing. */ -#define IGRAPH_EDGELIST_MAX_VERTEX_COUNT (1L << 20) - if (from > IGRAPH_EDGELIST_MAX_VERTEX_COUNT || to > IGRAPH_EDGELIST_MAX_VERTEX_COUNT) { - IGRAPH_ERROR("Vertex count too large in edgelist file.", IGRAPH_EINVAL); + read = fscanf(instream, "%li", &from); + if (read != 1) { + IGRAPH_ERROR("parsing edgelist file failed", IGRAPH_PARSEERROR); } -#endif - - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -115,10 +122,8 @@ igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, * \brief Writes the edge list of a graph to a file. * * - * Edges are represented as pairs of 0-based vertex indices. * 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: @@ -130,7 +135,7 @@ igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, * integer to the file requires O(1) * time. */ -igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { +int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { igraph_eit_t it; @@ -142,16 +147,16 @@ igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstrea igraph_integer_t from, to; int ret; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); - ret = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", - from, - to); + ret = fprintf(outstream, "%li %li\n", + (long int) from, + (long int) to); if (ret < 0) { - IGRAPH_ERROR("Failed writing edgelist.", IGRAPH_EFILE); + IGRAPH_ERROR("Write error", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/gml-header.h b/src/vendor/cigraph/src/io/gml-header.h index b8d590d8068..618db87ea77 100644 --- a/src/vendor/cigraph/src/io/gml-header.h +++ b/src/vendor/cigraph/src/io/gml-header.h @@ -1,6 +1,7 @@ /* IGraph library. - Copyright (C) 2011-2021 The igraph development team + 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 @@ -14,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #include "igraph_error.h" @@ -24,16 +26,16 @@ typedef struct { void *scanner; - char errmsg[300]; - igraph_error_t igraph_errno; + int eof; int depth; + char errmsg[300]; igraph_gml_tree_t *tree; } igraph_i_gml_parsedata_t; /** * Initializes a GML parser context. */ -igraph_error_t igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* 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 diff --git a/src/vendor/cigraph/src/io/gml-lexer.l b/src/vendor/cigraph/src/io/gml-lexer.l index 265fb7e3bf4..0acd8fa5eee 100644 --- a/src/vendor/cigraph/src/io/gml-lexer.l +++ b/src/vendor/cigraph/src/io/gml-lexer.l @@ -1,6 +1,7 @@ /* IGraph library. - Copyright (C) 2007-2021 The igraph development team + 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 @@ -14,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ %{ @@ -42,6 +44,7 @@ */ +#include "config.h" #include #include "io/gml-header.h" @@ -67,25 +70,18 @@ %option reentrant %option bison-bridge %option bison-locations -%option yylineno digit [0-9] -whitespace [ \r\n\t\v\f] - -/* Use to parse inf/nan as number only when expecting a value, i.e. after a keyword. - * Otherwise they are parsed as a keyword. */ -%s VALUE +whitespace [ \r\n\t] %% -^#[^\0\n\r]* { /* comments ignored */ } +^#[^\n\r]*[\n]|[\r] { /* comments ignored */ } -\"[^\0\"]*\" { BEGIN(INITIAL); return STRING; } -(\+|\-)?((?i:nan)|(?i:inf)) { BEGIN(INITIAL); return NUM; } -(\+|\-)?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { BEGIN(INITIAL); return NUM; } -[a-zA-Z_][a-zA-Z_0-9]* { BEGIN(VALUE); return KEYWORD; } +\"[^\x00\"]*\" { return STRING; } +\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[a-zA-Z_][a-zA-Z_0-9]* { return KEYWORD; } \[ { - BEGIN(INITIAL); yyextra->depth++; if (yyextra->depth >= 32) { return ERROR; @@ -95,9 +91,22 @@ whitespace [ \r\n\t\v\f] } \] { yyextra->depth--; - return LISTCLOSE; + 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/vendor/cigraph/src/io/gml-parser.y b/src/vendor/cigraph/src/io/gml-parser.y index c52aea0b5d4..040d0f635fb 100644 --- a/src/vendor/cigraph/src/io/gml-parser.y +++ b/src/vendor/cigraph/src/io/gml-parser.y @@ -1,6 +1,7 @@ /* IGraph library. - Copyright (C) 2009-2022 The igraph development team + 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 @@ -14,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ %{ @@ -42,38 +44,34 @@ */ +#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 "io/parse_utils.h" -#include "internal/hacks.h" /* strcasecmp & strndup */ - -#include -#include -#include +#include "internal/hacks.h" /* strcasecmp */ int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, const char *s); -static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res); -static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res); -static igraph_error_t igraph_i_gml_make_numeric(const char *name, - int line, - igraph_real_t value, - igraph_gml_tree_t **tree); -static igraph_error_t igraph_i_gml_make_string(const char *name, - int line, - char *value, - igraph_gml_tree_t **tree); -static igraph_error_t igraph_i_gml_make_list(const char *name, - int line, - igraph_gml_tree_t *list, - igraph_gml_tree_t **tree); -static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree); -static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); +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)*/ @@ -87,14 +85,16 @@ static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_ %defines %locations %error-verbose -%expect 2 /* from list rule */ %parse-param { igraph_i_gml_parsedata_t* context } %lex-param { void *scanner } %union { - char *str; - igraph_gml_tree_t *tree; - igraph_real_t real; + struct { + char *s; + int len; + } str; + void *tree; + double real; } %type list; @@ -103,50 +103,46 @@ static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_ %type num; %type string; -%token STRING "string" -%token NUM "number" -%token KEYWORD "keyword" -%token LISTOPEN "[" -%token LISTCLOSE "]" -/* The following ensures that the special $end token is shown with a friendly name - * even in older Bison versions. - * See https://www.gnu.org/software/bison/manual/bison.html#Token-I18n for more details. */ +%token STRING +%token NUM +%token KEYWORD +%token LISTOPEN +%token LISTCLOSE +%token EOFF %token END 0 "end of file" /* friendly name for $end */ %token ERROR -%destructor { free($$); } string key; +%destructor { IGRAPH_FREE($$.s); } string key KEYWORD; %destructor { igraph_gml_tree_destroy($$); } list keyvalue; %% -input: list { context->tree=$1; }; +input: list { context->tree=$1; } + | list EOFF { context->tree=$1; } +; -list: /* empty */ { IGRAPH_YY_CHECK(igraph_i_gml_make_empty(&$$)); } - | keyvalue { $$=$1; } /* redundant and causes shift/reduce conflict, but increases performance */ - | list keyvalue { IGRAPH_YY_CHECK(igraph_i_gml_merge($1, $2)); $$ = $1; }; +list: keyvalue { $$=$1; } + | list keyvalue { $$=igraph_i_gml_merge($1, $2); }; keyvalue: key num - { IGRAPH_YY_CHECK(igraph_i_gml_make_numeric($1, @1.first_line, $2, &$$)); } + { $$=igraph_i_gml_make_numeric($1.s, $1.len, $2); } | key string - { IGRAPH_YY_CHECK(igraph_i_gml_make_string($1, @1.first_line, $2, &$$)); } + { $$=igraph_i_gml_make_string($1.s, $1.len, $2.s, $2.len); } | key LISTOPEN list LISTCLOSE - { IGRAPH_YY_CHECK(igraph_i_gml_make_list($1, @1.first_line, $3, &$$)); } + { $$=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_YY_CHECK(igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), - igraph_gml_yyget_leng(scanner), - &$$)); USE($1); }; -num : NUM { - igraph_real_t val; - IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_gml_yyget_text(scanner), +key: KEYWORD { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), - &val)); - $$=val; -}; + &$$); USE($1); }; +num : NUM { $$=igraph_i_gml_get_real(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner)); }; -string: STRING { IGRAPH_YY_CHECK(igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), +string: STRING { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), - &$$)); }; + &$$); }; %% @@ -158,112 +154,145 @@ int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, return 0; } -static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res) { - *res = strndup(s, len); - if (! *res) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ +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); } - return IGRAPH_SUCCESS; + memcpy(p->s, s, sizeof(char)*len); + p->s[len]='\0'; + p->len=len; } -static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res) { - *res = strndup(s+1, len-2); - if (! *res) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ +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); } - return IGRAPH_SUCCESS; + memcpy(p->s, s+1, sizeof(char)*(len-2)); + p->s[len-2]='\0'; + p->len=len-2; } -static igraph_error_t igraph_i_gml_make_numeric(const char *name, - int line, - igraph_real_t value, - igraph_gml_tree_t **tree) { +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_ENOMEM); /* LCOV_EXCL_LINE */ + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, t); - - /* The GML spec only requires support for 32-bit signed integers. - * We treat anything out of that range as real. These values end - * up as igraph_real_t anyway, as igraph does not currently support - * integer-typed attributes. */ - if (floor(value) == value && value >= INT32_MIN && value <= INT32_MAX) { - IGRAPH_CHECK(igraph_gml_tree_init_integer(t, name, line, value)); + + if (floor(value)==value) { + if (igraph_gml_tree_init_integer(t, s, len, value)) { + free(t); + return 0; + } } else { - IGRAPH_CHECK(igraph_gml_tree_init_real(t, name, line, value)); + if (igraph_gml_tree_init_real(t, s, len, value)) { + free(t); + return 0; + } } - *tree = t; - IGRAPH_FINALLY_CLEAN(1); /* t */ - - return IGRAPH_SUCCESS; + return t; } -static igraph_error_t igraph_i_gml_make_string(const char *name, - int line, - char *value, - igraph_gml_tree_t **tree) { - +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_ENOMEM); /* LCOV_EXCL_LINE */ + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, t); - /* 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 */ - IGRAPH_FINALLY(igraph_free, value); - IGRAPH_CHECK(igraph_gml_tree_init_string(t, name, line, value)); + 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; + } - IGRAPH_FINALLY_CLEAN(1); /* value */ + v[vlen]=tmp; + free(v); - *tree = t; - IGRAPH_FINALLY_CLEAN(1); /* t */ + if (t == 0) { + /* no new tree node was created so s has no owner any more */ + free(s); + } - return IGRAPH_SUCCESS; + return t; } -static igraph_error_t igraph_i_gml_make_list(const char *name, - int line, - igraph_gml_tree_t *list, - igraph_gml_tree_t **tree) { - +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_ENOMEM); /* LCOV_EXCL_LINE */ + igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, t); - IGRAPH_CHECK(igraph_gml_tree_init_tree(t, name, line, list)); - - *tree = t; - IGRAPH_FINALLY_CLEAN(1); /* t */ + /* 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 IGRAPH_SUCCESS; + return t; } -static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree) { - igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); - if (!t) { - IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, t); +igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, + igraph_gml_tree_t *list) { - IGRAPH_CHECK(igraph_gml_tree_init_empty(t)); + igraph_gml_tree_t *t=IGRAPH_CALLOC(1, igraph_gml_tree_t); - *tree = t; - IGRAPH_FINALLY_CLEAN(1); /* 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 IGRAPH_SUCCESS; + return t; } -static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { +igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { - IGRAPH_CHECK(igraph_gml_tree_mergedest(t1, t2)); + igraph_gml_tree_mergedest(t1, t2); IGRAPH_FREE(t2); - return IGRAPH_SUCCESS; + return t1; } diff --git a/src/vendor/cigraph/src/io/gml-tree.c b/src/vendor/cigraph/src/io/gml-tree.c index 29dbccf561e..8d0fe076a8c 100644 --- a/src/vendor/cigraph/src/io/gml-tree.c +++ b/src/vendor/cigraph/src/io/gml-tree.c @@ -1,6 +1,7 @@ /* IGraph library. - Copyright (C) 2007-2022 The igraph development team + 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 @@ -14,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #include "igraph_memory.h" @@ -25,23 +27,21 @@ #include -igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_integer_t value) { +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_VECTOR_CHAR_INIT_FINALLY(&t->types, 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ - VECTOR(t->names)[0] = (void*) name; - - /* line number */ - VECTOR(t->lines)[0] = line; + VECTOR(t->names)[0] = (void*)name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER; @@ -49,89 +49,84 @@ igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, /* children */ p = IGRAPH_CALLOC(1, igraph_integer_t); if (!p) { - IGRAPH_ERROR("Cannot create integer GML tree node.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot create integer GML tree node", IGRAPH_ENOMEM); } *p = value; VECTOR(t->children)[0] = p; - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(3); + return 0; } -igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_real_t value) { +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_VECTOR_CHAR_INIT_FINALLY(&t->types, 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ VECTOR(t->names)[0] = (void*) name; - /* line number */ - VECTOR(t->lines)[0] = line; - /* 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot create real GML tree node", IGRAPH_ENOMEM); } *p = value; VECTOR(t->children)[0] = p; - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(3); + return 0; } -igraph_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - const char *value) { +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_VECTOR_CHAR_INIT_FINALLY(&t->types, 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ VECTOR(t->names)[0] = (void*) name; - /* line number */ - VECTOR(t->lines)[0] = line; - /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING; /* children */ - VECTOR(t->children)[0] = (void*) value; + VECTOR(t->children)[0] = (void*)value; - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(3); + return 0; } -igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_gml_tree_t *value) { +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_VECTOR_CHAR_INIT_FINALLY(&t->types, 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ - VECTOR(t->names)[0] = (void*) name; - - /* line number */ - VECTOR(t->lines)[0] = line; + VECTOR(t->names)[0] = (void*)name; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE; @@ -139,44 +134,32 @@ igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, /* children */ VECTOR(t->children)[0] = value; - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(3); + return 0; } -igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *t) { - IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 0); - IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 0); - IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 0); - IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; -} - /* merge is destructive, the _second_ tree is destroyed */ -igraph_error_t igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) { - igraph_integer_t i, n = igraph_vector_ptr_size(&t2->children); - +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_CHECK(igraph_vector_int_push_back(&t1->lines, VECTOR(t2->lines)[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); - igraph_vector_int_destroy(&t2->lines); - - return IGRAPH_SUCCESS; + return 0; } void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { - igraph_integer_t i, n = igraph_vector_ptr_size(&t->children); + long int i, n = igraph_vector_ptr_size(&t->children); for (i = 0; i < n; i++) { - igraph_i_gml_tree_type_t type = (igraph_i_gml_tree_type_t) VECTOR(t->types)[i]; + int type = VECTOR(t->types)[i]; switch (type) { case IGRAPH_I_GML_TREE_TREE: igraph_gml_tree_destroy(VECTOR(t->children)[i]); @@ -201,18 +184,17 @@ void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { igraph_vector_ptr_destroy(&t->names); igraph_vector_char_destroy(&t->types); igraph_vector_ptr_destroy(&t->children); - igraph_vector_int_destroy(&t->lines); IGRAPH_FREE(t); } -igraph_integer_t igraph_gml_tree_length(const igraph_gml_tree_t *t) { +long int igraph_gml_tree_length(const igraph_gml_tree_t *t) { return igraph_vector_ptr_size(&t->names); } -igraph_integer_t igraph_gml_tree_find( - const igraph_gml_tree_t *t, const char *name, igraph_integer_t from -) { - igraph_integer_t size = 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++; @@ -224,9 +206,8 @@ igraph_integer_t igraph_gml_tree_find( return from; } -igraph_integer_t igraph_gml_tree_findback( - const igraph_gml_tree_t *t, const char *name, igraph_integer_t 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--; @@ -235,43 +216,39 @@ igraph_integer_t igraph_gml_tree_findback( return from; } -igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos) { - return (igraph_i_gml_tree_type_t) VECTOR(t->types)[pos]; +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, igraph_integer_t 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_line(const igraph_gml_tree_t *t, igraph_integer_t pos) { - return VECTOR(t->lines)[pos]; -} - igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, - igraph_integer_t pos) { + 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, - igraph_integer_t pos) { + 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, - igraph_integer_t pos) { + 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, - igraph_integer_t pos) { + long int pos) { igraph_gml_tree_t *tree = VECTOR(t->children)[pos]; return tree; } -void igraph_gml_tree_delete(igraph_gml_tree_t *t, igraph_integer_t pos) { +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]); } diff --git a/src/vendor/cigraph/src/io/gml-tree.h b/src/vendor/cigraph/src/io/gml-tree.h index 71aacfeeb6e..074721c724a 100644 --- a/src/vendor/cigraph/src/io/gml-tree.h +++ b/src/vendor/cigraph/src/io/gml-tree.h @@ -1,6 +1,7 @@ /* IGraph library. - Copyright (C) 2007-2022 The igraph development team + 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 @@ -14,8 +15,9 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ #ifndef REST_GML_TREE_H @@ -39,48 +41,41 @@ typedef struct igraph_gml_tree_t { igraph_vector_ptr_t names; igraph_vector_char_t types; igraph_vector_ptr_t children; - igraph_vector_int_t lines; /* line numbers where names appear */ } igraph_gml_tree_t; -igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_integer_t value); -igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_real_t value); -igraph_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - const char *value); -igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, - const char *name, - igraph_integer_t line, - igraph_gml_tree_t *value); -igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *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, igraph_integer_t pos); -igraph_error_t igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2); +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); -igraph_integer_t igraph_gml_tree_length(const igraph_gml_tree_t *t); -igraph_integer_t igraph_gml_tree_find(const igraph_gml_tree_t *t, - const char *name, igraph_integer_t from); -igraph_integer_t igraph_gml_tree_findback(const igraph_gml_tree_t *t, - const char *name, igraph_integer_t from); -igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos); -const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos); -igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos); +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, - igraph_integer_t pos); + long int pos); igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, - igraph_integer_t pos); + long int pos); const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, - igraph_integer_t pos); + long int pos); igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, - igraph_integer_t pos); + long int pos); __END_DECLS diff --git a/src/vendor/cigraph/src/io/gml.c b/src/vendor/cigraph/src/io/gml.c index 5ed255b60e2..77c7f054150 100644 --- a/src/vendor/cigraph/src/io/gml.c +++ b/src/vendor/cigraph/src/io/gml.c @@ -1,7 +1,7 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2005-2022 The igraph development team + 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 @@ -17,6 +17,7 @@ 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" @@ -28,144 +29,23 @@ #include "core/trie.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strdup, strncasecmp */ - #include "io/gml-header.h" -#include "io/parsers/gml-parser.h" #include #include #include -int igraph_gml_yylex_init_extra(igraph_i_gml_parsedata_t *user_defined, void *scanner); -int 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); - -/* Checks if a null-terminated string needs encoding or decoding. - * - * Encoding is needed when an " or & character is present. - * - * Decoding is needed when an &xyz; style entity is present, so it's sufficient to look - * for & characters. " characters are never present in the raw strings returned by the - * GML parser, so we can use the same function to detect the need for either encoding - * or decoding. - */ -static igraph_bool_t needs_coding(const char *str) { - while (*str) { - if (*str == '&' || *str == '"') { - return true; - } - str++; - } - return false; -} - -/* Encode & and " character in 'src' to & and " - * '*dest' must be deallocated by the caller. - */ -static igraph_error_t entity_encode(const char *src, char **dest, igraph_bool_t only_quot) { - igraph_integer_t destlen = 0; - const char *s; - char *d; - - for (s = src; *s != '\0'; s++, destlen++) { - switch (*s) { - case '&': /* & */ - if (! only_quot) { - destlen += 4; - } - break; - case '"': /* " */ - destlen += 5; break; - } - } - *dest = IGRAPH_CALLOC(destlen + 1, char); - IGRAPH_CHECK_OOM(dest, "Not enough memory to encode string for GML export."); - for (s = src, d = *dest; *s != '\0'; s++, d++) { - switch (*s) { - case '&': - if (! only_quot) { - strcpy(d, "&"); - d += 4; - } else { - *d = *s; - } - break; - case '"': - strcpy(d, """); d += 5; break; - default: - *d = *s; - } - } - *d = '\0'; - return IGRAPH_SUCCESS; -} - -/* Decode the five standard predefined XML entities. Unknown entities or stray & characters - * will be passed through unchanged. '*dest' must be deallocated by the caller. - * If '*warned' is false, warnings will be issued for unsupported entities and - * '*warned' will be set to true. This is to prevent a flood of warnings in some files. - */ -static igraph_error_t entity_decode(const char *src, char **dest, igraph_bool_t *warned) { - const char *entity_names[] = { - """, "&", "'", "<", ">" - }; - - const char entity_values[] = { - '"', '&', '\'', '<', '>' - }; - - const int entity_count = sizeof entity_values / sizeof entity_values[0]; - - const char *s; - char *d; - size_t len = strlen(src); - *dest = IGRAPH_CALLOC(len+1, char); /* at most as much storage needed as for 'src' */ - IGRAPH_CHECK_OOM(dest, "Not enough memory to decode string during GML import."); - - for (s = src, d = *dest; *s != '\0';) { - if (*s == '&') { - int i; - for (i=0; i < entity_count; i++) { - size_t entity_len = strlen(entity_names[i]); - if (!strncasecmp(s, entity_names[i], entity_len)) { - *d++ = entity_values[i]; - s += entity_len; - break; - } - } - /* None of the known entities match, report warning and pass through unchanged. */ - if (i == entity_count) { - if (! *warned) { - const int max_entity_name_length = 34; - int j = 0; - while (s[j] != '\0' && s[j] != ';' && j < max_entity_name_length) { - j++; - } - if (s[j] == '\0' || j == max_entity_name_length) { - IGRAPH_WARNING("Unterminated entity or stray & character found, will be returned verbatim."); - } else { - IGRAPH_WARNINGF("One or more unknown entities will be returned verbatim (%.*s).", j+1, s); - } - *warned = true; /* warn only once */ - } - *d++ = *s++; - } - } else { - *d++ = *s++; - } - } - *d = '\0'; - - return IGRAPH_SUCCESS; -} +int igraph_gml_yylex_init_extra (igraph_i_gml_parsedata_t* user_defined, + void* scanner); +int 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) { - igraph_integer_t i; + long int i; igraph_vector_ptr_t *vec; for (i = 0; i < 3; i++) { - igraph_integer_t j; + long int j; vec = ptr[i]; for (j = 0; j < igraph_vector_ptr_size(vec); j++) { igraph_attribute_record_t *atrec = VECTOR(*vec)[j]; @@ -175,14 +55,12 @@ static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { igraph_vector_destroy(value); IGRAPH_FREE(value); } - } else if (atrec->type == IGRAPH_ATTRIBUTE_STRING) { + } else { igraph_strvector_t *value = (igraph_strvector_t*)atrec->value; if (value != 0) { igraph_strvector_destroy(value); IGRAPH_FREE(value); } - } else { - /* Some empty attribute records may have been created for composite attributes */ } IGRAPH_FREE(atrec->name); IGRAPH_FREE(atrec); @@ -191,34 +69,39 @@ static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { } } -static igraph_real_t igraph_i_gml_toreal(igraph_gml_tree_t *node, igraph_integer_t pos) { - igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); +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: - return igraph_gml_tree_get_integer(node, pos); + value = igraph_gml_tree_get_integer(node, pos); + break; case IGRAPH_I_GML_TREE_REAL: - return igraph_gml_tree_get_real(node, pos); - case IGRAPH_I_GML_TREE_TREE: - return IGRAPH_NAN; /* default value of NaN when composite is ignored */ + value = igraph_gml_tree_get_real(node, pos); + break; default: - /* Must never reach here, regardless of the contents of the GML file. */ - IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", - igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ + 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, igraph_integer_t pos) { - igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); - static char tmp[100]; +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; - igraph_integer_t i; + 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), "%" IGRAPH_PRId, i); + snprintf(tmp, sizeof(tmp) / sizeof(char), "%li", i); break; case IGRAPH_I_GML_TREE_REAL: d = igraph_gml_tree_get_real(node, pos); @@ -227,29 +110,24 @@ static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, igraph_integer case IGRAPH_I_GML_TREE_STRING: p = igraph_gml_tree_get_string(node, pos); break; - case IGRAPH_I_GML_TREE_TREE: - tmp[0] = '\0'; /* default value of "" when composite is ignored */ - break; default: - /* Must never reach here, regardless of the contents of the GML file. */ - IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", - igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ + break; } return p; } -igraph_error_t igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t *context) { +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'; - context->igraph_errno = IGRAPH_SUCCESS; + context->errmsg[0] = 0; return IGRAPH_SUCCESS; } -void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t *context) { +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; @@ -261,152 +139,37 @@ void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t *context) { } } -/* Takes a vector of attribute records and removes those elements - * whose type is unspecified, i.e. IGRAPH_ATTRIBUTE_UNSPECIFIED. */ -static void prune_unknown_attributes(igraph_vector_ptr_t *attrs) { - igraph_integer_t i, j; - for (i = 0, j = 0; i < igraph_vector_ptr_size(attrs); i++) { - igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; - if (atrec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - IGRAPH_FREE(atrec->name); - IGRAPH_FREE(atrec); - } else { - VECTOR(*attrs)[j++] = VECTOR(*attrs)[i]; - } - } - igraph_vector_ptr_resize(attrs, j); /* shrinks */ -} - -/* Converts an integer id to an optionally prefixed string id. */ -static const char *strid(igraph_integer_t id, const char *prefix) { - static char name[100]; - snprintf(name, sizeof(name) / sizeof(char) - 1, "%s%" IGRAPH_PRId, prefix, id); - return name; -} - -/* Creates an empty attribute record or if it exists, updates its type as needed. - * 'name' is the attribute name. 'type' is the current type in the GML tree, - * which will determine the igraph attribute type to use. */ -static igraph_error_t create_or_update_attribute(const char *name, - igraph_i_gml_tree_type_t type, - igraph_trie_t *attrnames, - igraph_vector_ptr_t *attrs) { - - igraph_integer_t trieid, triesize = igraph_trie_size(attrnames); - IGRAPH_CHECK(igraph_trie_get(attrnames, name, &trieid)); - if (trieid == triesize) { - /* new attribute */ - igraph_attribute_record_t *atrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); - if (!atrec) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, atrec); - atrec->name = strdup(name); - if (! atrec->name) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, (char *) atrec->name); - if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { - atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; - } else if (type == IGRAPH_I_GML_TREE_STRING) { - atrec->type = IGRAPH_ATTRIBUTE_STRING; - } else { - atrec->type = IGRAPH_ATTRIBUTE_UNSPECIFIED; - } - IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, atrec)); - IGRAPH_FINALLY_CLEAN(2); - } else { - /* already seen, should we update type? */ - igraph_attribute_record_t *atrec = VECTOR(*attrs)[trieid]; - igraph_attribute_type_t type1 = atrec->type; - if (type == IGRAPH_I_GML_TREE_STRING) { - atrec->type = IGRAPH_ATTRIBUTE_STRING; - } else if (type1 == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { - atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; - } - } - } - - return IGRAPH_SUCCESS; -} - -/* Allocates the contents of attribute records stored in 'attrs'. - * 'no_of_items' is the length of attribute vectors, i.e. no_of_nodes, - * no_of_edges, or 1 for vertex, edge and graph attributes. - * The 'kind' parameter can be "vertex", "edge" or "graph", and - * is used solely for showing better warning messages. */ -static igraph_error_t allocate_attributes(igraph_vector_ptr_t *attrs, - igraph_integer_t no_of_items, - const char *kind) { - - igraph_integer_t i, n = igraph_vector_ptr_size(attrs); - for (i = 0; i < n; i++) { - igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; - igraph_attribute_type_t type = atrec->type; - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); - if (! p) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, p); - IGRAPH_CHECK(igraph_vector_init(p, no_of_items)); - igraph_vector_fill(p, IGRAPH_NAN); /* use NaN as default */ - atrec->value = p; - IGRAPH_FINALLY_CLEAN(1); - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *p = IGRAPH_CALLOC(1, igraph_strvector_t); - if (! p) { - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - IGRAPH_FINALLY(igraph_free, p); - IGRAPH_CHECK(igraph_strvector_init(p, no_of_items)); - atrec->value = p; - IGRAPH_FINALLY_CLEAN(1); - } else if (type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - IGRAPH_WARNINGF("Composite %s attribute '%s' ignored in GML file.", kind, atrec->name); - } else { - /* Must never reach here. */ - IGRAPH_FATAL("Unexpected attribute type."); - } - } - return IGRAPH_SUCCESS; -} - /** * \function igraph_read_graph_gml * \brief Read a graph in GML format. * * GML is a simple textual format, see - * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 - * for details. + * 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 + * we implement only a subset of this format, some attributes might be * ignored. Here is a list of all the differences: * \olist - * \oli Only attributes with a simple type are used: integer, real or - * string. If an attribute is composite, i.e. an array or a record, - * then it is ignored. When some values of the attribute are simple and - * some compound, the composite ones are replaced with a default value - * (NaN for numeric, "" for string). - * \oli comment fields are not ignored. They are treated as any - * other field and converted to attributes. + * \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 There is no maximum line length or maximum keyword length. - * \oli Only the \c quot, \c amp, \c apos, \c lt and \c gt character entities - * are supported. Any other entity is passed through unchanged by the reader - * after issuing a warning, and is expected to be decoded by the user. - * \oli We allow inf, -inf and nan + * \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 equivalent. + * 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. @@ -418,27 +181,23 @@ static igraph_error_t allocate_attributes(igraph_vector_ptr_t *attrs, * * \example examples/simple/gml.c */ -igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream) { +int igraph_read_graph_gml(igraph_t *graph, FILE *instream) { - igraph_integer_t i; - igraph_integer_t no_of_nodes = 0, no_of_edges = 0; - igraph_integer_t node_no; + long int i, p; + long int no_of_nodes = 0, no_of_edges = 0; igraph_trie_t trie; - igraph_vector_int_t edges; + igraph_vector_t edges; igraph_bool_t directed = IGRAPH_UNDIRECTED; - igraph_bool_t has_directed = false; igraph_gml_tree_t *gtree; - igraph_integer_t gidx; + 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; + vattrs = IGRAPH_VECTOR_PTR_NULL, eattrs = IGRAPH_VECTOR_PTR_NULL; igraph_vector_ptr_t *attrs[3]; - igraph_integer_t edgeptr = 0; + long int edgeptr = 0; igraph_i_gml_parsedata_t context; - igraph_bool_t entity_warned = false; /* used to warn at most once about unsupported entities */ attrs[0] = &gattrs; attrs[1] = &vattrs; attrs[2] = &eattrs; @@ -449,383 +208,309 @@ igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream) { igraph_gml_yyset_in(instream, context.scanner); - /* Protect 'context' from being destroyed before returning from yyparse() */ - IGRAPH_FINALLY_ENTER(); - int err = igraph_gml_yyparse(&context); - IGRAPH_FINALLY_EXIT(); - switch (err) { - case 0: /* success */ - break; - case 1: /* parse error */ + i = igraph_gml_yyparse(&context); + if (i != 0) { if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); - } else if (context.igraph_errno != IGRAPH_SUCCESS) { - IGRAPH_ERROR("", context.igraph_errno); } else { IGRAPH_ERROR("Cannot read GML file.", IGRAPH_PARSEERROR); } - break; - case 2: /* out of memory */ - IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - default: /* must never reach here */ - /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison - * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being - * returned in place of a Bison error code. - * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? - */ - IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading GML file.", err); /* LCOV_EXCL_LINE */ } + 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_WARNINGF("Unknown GML version: %" IGRAPH_PRId ". " - "Parsing will continue assuming GML version 1, but may fail.", - igraph_gml_tree_get_integer(context.tree, i)); + IGRAPH_ERROR("Unknown GML version.", IGRAPH_UNIMPLEMENTED); + /* RETURN HERE!!!! */ } - /* Get the graph */ + /* 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_ERRORF("Invalid type for 'graph' object in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(context.tree, gidx)); + 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_CHECK(igraph_vector_ptr_init(&gattrs, 0)); - IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); - IGRAPH_CHECK(igraph_vector_ptr_init(&eattrs, 0)); + 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); - /* Now we go over all objects in the graph to - * - collect the attribute names and types - * - collect node IDs - * - set directedness - * - do some checks which the following code relies on - * - * The 'id' fields of 'node' objects are converted into strings, so that they - * can be inserted into a trie and re-encoded as consecutive integers starting - * at 0. The GML spec allows isolated nodes with no 'id' field. These get a - * generated string id of the form "n123" consisting of "n" and their count - * (i.e. ordinal position) within the GML file. - * - * We use an attribute type value of IGRAPH_ATTRIBUTE_UNSPECIFIED to mark attribute - * records which correspond to composite GML values and must therefore be removed - * before creating the graph. - */ - node_no = 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; - node_no++; no_of_nodes++; if (igraph_gml_tree_type(gtree, i) != IGRAPH_I_GML_TREE_TREE) { - IGRAPH_ERRORF("'node' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(gtree, i)); + IGRAPH_ERROR("'node' is not a list in GML file.", IGRAPH_PARSEERROR); } node = igraph_gml_tree_get_tree(gtree, i); hasid = 0; - for (igraph_integer_t j = 0; j < igraph_gml_tree_length(node); j++) { + for (j = 0; j < igraph_gml_tree_length(node); j++) { const char *name = igraph_gml_tree_name(node, j); - igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, j); - IGRAPH_CHECK(create_or_update_attribute(name, type, &vattrnames, &vattrs)); - /* check id */ - if (!strcmp(name, "id")) { - igraph_integer_t id, trie_id; - igraph_integer_t trie_size = igraph_trie_size(&trie); - if (hasid) { - /* A 'node' must not have more than one 'id' field. - * This error cannot be relaxed into a warning because all ids we find are - * added to the trie, and eventually converted to igraph vertex ids. */ - IGRAPH_ERRORF("Node has multiple 'id' fields in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, - igraph_gml_tree_line(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); } - if (type != IGRAPH_I_GML_TREE_INTEGER) { - IGRAPH_ERRORF("Non-integer node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(node, j)); + 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; } - id = igraph_gml_tree_get_integer(node, j); - IGRAPH_CHECK(igraph_trie_get(&trie, strid(id, ""), &trie_id)); - if (trie_id != trie_size) { - /* This id has already been seen in a previous node. */ - IGRAPH_ERRORF("Duplicate node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(node, j)); + } 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) { - /* Isolated nodes are allowed not to have an id. - * We generate an "n"-prefixed string id to be used in the trie. */ - igraph_integer_t trie_id; - IGRAPH_CHECK(igraph_trie_get(&trie, strid(node_no, "n"), &trie_id)); + 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 = false, has_target = false; + 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_ERRORF("'edge' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(gtree, i)); + IGRAPH_ERROR("'edge' is not a list in GML file.", IGRAPH_PARSEERROR); } edge = igraph_gml_tree_get_tree(gtree, i); - for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { + has_source = has_target = 0; + for (j = 0; j < igraph_gml_tree_length(edge); j++) { const char *name = igraph_gml_tree_name(edge, j); - igraph_i_gml_tree_type_t type = igraph_gml_tree_type(edge, j); if (!strcmp(name, "source")) { - if (has_source) { - /* An edge must not have more than one 'source' field. - * This could be relaxed to a warning, but we keep it as an error - * for consistency with the handling of duplicate node 'id' field, - * and because it indicates a serious corruption in the GML file. */ - IGRAPH_ERRORF("Duplicate 'source' in an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, - igraph_gml_tree_line(edge, j)); - } - has_source = true; - if (type != IGRAPH_I_GML_TREE_INTEGER) { - IGRAPH_ERRORF("Non-integer 'source' for an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, - igraph_gml_tree_line(edge, j)); + 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")) { - if (has_target) { - /* An edge must not have more than one 'target' field. */ - IGRAPH_ERRORF("Duplicate 'target' in an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, - igraph_gml_tree_line(edge, j)); - } - has_target = true; - if (type != IGRAPH_I_GML_TREE_INTEGER) { - IGRAPH_ERRORF("Non-integer 'target' for an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, - igraph_gml_tree_line(edge, j)); + 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 { - IGRAPH_CHECK(create_or_update_attribute(name, type, &eattrnames, &eattrs)); + 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_ERRORF("No 'source' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(gtree, i)); + IGRAPH_ERROR("No 'source' for edge in GML file.", IGRAPH_PARSEERROR); } if (!has_target) { - IGRAPH_ERRORF("No 'target' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, - igraph_gml_tree_line(gtree, i)); - } - } else if (! strcmp(name, "directed")) { - /* Set directedness of graph. */ - if (has_directed) { - /* Be tolerant of duplicate entries, but do show a warning. */ - IGRAPH_WARNINGF("Duplicate 'directed' field in 'graph', line %" IGRAPH_PRId ". " - "Ignoring previous 'directed' fields.", - igraph_gml_tree_line(gtree, i)); - } - if (igraph_gml_tree_type(gtree, i) == IGRAPH_I_GML_TREE_INTEGER) { - igraph_integer_t dir = igraph_gml_tree_get_integer(gtree, i); - if (dir != 0 && dir != 1) { - IGRAPH_WARNINGF( - "Invalid value %" IGRAPH_PRId " for 'directed' attribute on line %" IGRAPH_PRId ", should be 0 or 1.", - dir, igraph_gml_tree_line(gtree, i)); - } - if (dir) { - directed = IGRAPH_DIRECTED; - } - has_directed = true; - } else { - IGRAPH_WARNINGF("Invalid type for 'directed' attribute on line %" IGRAPH_PRId ", assuming undirected.", - igraph_gml_tree_line(gtree, i)); + IGRAPH_ERROR("No 'target' for edge in GML file.", IGRAPH_PARSEERROR); } } else { - /* Add the rest of items as graph attributes. */ - igraph_i_gml_tree_type_t type = igraph_gml_tree_type(gtree, i); - IGRAPH_CHECK(create_or_update_attribute(name, type, &gattrnames, &gattrs)); + /* 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."); } } - /* At this point, all nodes must have an id (from the file or generated) stored - * in the trie. Any condition that violates this should have been caught during - * the preceding checks. */ - IGRAPH_ASSERT(igraph_trie_size(&trie) == no_of_nodes); + 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."); + } + } - /* Now we allocate the vectors and strvectors for the attributes */ - IGRAPH_CHECK(allocate_attributes(&vattrs, no_of_nodes, "vertex")); - IGRAPH_CHECK(allocate_attributes(&eattrs, no_of_edges, "edge")); - IGRAPH_CHECK(allocate_attributes(&gattrs, 1, "graph")); + /* 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; + } - /* Add edges, edge attributes and vertex attributes */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); - node_no = 0; + /* and add vertex attributes */ for (i = 0; i < igraph_gml_tree_length(gtree); i++) { - const char *name; - name = igraph_gml_tree_name(gtree, i); - if (!strcmp(name, "node")) { + 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); - igraph_integer_t iidx = igraph_gml_tree_find(node, "id", 0); - igraph_integer_t trie_id; - const char *sid; - node_no++; - if (iidx < 0) { - /* Isolated node with no id field, use n-prefixed generated id */ - sid = strid(node_no, "n"); - } else { - sid = strid(igraph_gml_tree_get_integer(node, iidx), ""); - } - IGRAPH_CHECK(igraph_trie_get(&trie, sid, &trie_id)); - for (igraph_integer_t j = 0; j < igraph_gml_tree_length(node); j++) { + 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; - igraph_attribute_type_t type; - igraph_integer_t ai; - IGRAPH_CHECK(igraph_trie_get(&vattrnames, aname, &ai)); - atrec = VECTOR(vattrs)[ai]; + 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; - VECTOR(*v)[trie_id] = igraph_i_gml_toreal(node, j); + 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; + igraph_strvector_t *v = (igraph_strvector_t *)atrec->value; const char *value = igraph_i_gml_tostring(node, j); - if (needs_coding(value)) { - char *value_decoded; - IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); - IGRAPH_FINALLY(igraph_free, value_decoded); - IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value_decoded)); - IGRAPH_FREE(value_decoded); - IGRAPH_FINALLY_CLEAN(1); - } else { - IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value)); - } - } else { - /* Ignored composite attribute */ + IGRAPH_CHECK(igraph_strvector_set(v, id, value)); } } - } else if (!strcmp(name, "edge")) { - igraph_gml_tree_t *edge; - igraph_integer_t from, to, fromidx = 0, toidx = 0; - edge = igraph_gml_tree_get_tree(gtree, i); - for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { - const char *aname = igraph_gml_tree_name(edge, j); - if (!strcmp(aname, "source")) { - fromidx = igraph_gml_tree_find(edge, "source", 0); - } else if (!strcmp(aname, "target")) { - toidx = igraph_gml_tree_find(edge, "target", 0); - } else { - igraph_integer_t edgeid = edgeptr / 2; - igraph_integer_t ai; - igraph_attribute_record_t *atrec; - igraph_attribute_type_t type; - IGRAPH_CHECK(igraph_trie_get(&eattrnames, aname, &ai)); - atrec = VECTOR(eattrs)[ai]; - type = atrec->type; - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_vector_t *v = (igraph_vector_t *) atrec->value; - VECTOR(*v)[edgeid] = igraph_i_gml_toreal(edge, j); - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; - const char *value = igraph_i_gml_tostring(edge, j); - if (needs_coding(value)) { - char *value_decoded; - IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); - IGRAPH_FINALLY(igraph_free, value_decoded); - IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value_decoded)); - IGRAPH_FREE(value_decoded); - IGRAPH_FINALLY_CLEAN(1); - } else { - IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value)); - } - } else { - /* Ignored composite attribute */ - } - } - } - from = igraph_gml_tree_get_integer(edge, fromidx); - to = igraph_gml_tree_get_integer(edge, toidx); - IGRAPH_CHECK(igraph_trie_check(&trie, strid(from, ""), &from)); - if (from < 0) { - IGRAPH_ERRORF("Unknown source node id found in an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, fromidx)); - } - IGRAPH_CHECK(igraph_trie_check(&trie, strid(to, ""), &to)); - if (to < 0) { - IGRAPH_ERRORF("Unknown target node id found in an edge in GML file, line %" IGRAPH_PRId ".", - IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, toidx)); - } - VECTOR(edges)[edgeptr++] = from; - VECTOR(edges)[edgeptr++] = to; - } else if (! strcmp(name, "directed")) { - /* Nothing to do for 'directed' field, already handled earlier. */ - } else { - /* Set the rest as graph attributes */ - igraph_integer_t ai; - igraph_attribute_record_t *atrec; - igraph_attribute_type_t type; - IGRAPH_CHECK(igraph_trie_get(&gattrnames, name, &ai)); - atrec = VECTOR(gattrs)[ai]; - type = atrec->type; - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_vector_t *v = (igraph_vector_t *) atrec->value; - VECTOR(*v)[0] = igraph_i_gml_toreal(gtree, i); - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; - const char *value = igraph_i_gml_tostring(gtree, i); - if (needs_coding(value)) { - char *value_decoded; - IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); - IGRAPH_FINALLY(igraph_free, value_decoded); - IGRAPH_CHECK(igraph_strvector_set(v, 0, value_decoded)); - IGRAPH_FREE(value_decoded); - IGRAPH_FINALLY_CLEAN(1); - } else { - IGRAPH_CHECK(igraph_strvector_set(v, 0, value)); - } - } else { - /* Ignored composite attribute */ - } } } - /* Remove composite attributes */ - prune_unknown_attributes(&vattrs); - prune_unknown_attributes(&eattrs); - prune_unknown_attributes(&gattrs); - 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, &gattrs)); - IGRAPH_FINALLY(igraph_destroy, graph); - IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs)); + 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_FINALLY_CLEAN(1); /* do not destroy 'graph', just pop it from the stack */ - igraph_vector_int_destroy(&edges); igraph_i_gml_destroy_attrs(attrs); + igraph_vector_destroy(&edges); igraph_i_gml_parsedata_destroy(&context); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_gml_convert_to_key(const char *orig, char **key) { +static int igraph_i_gml_convert_to_key(const char *orig, char **key) { char strno[50]; size_t i, len = strlen(orig), newlen = 0, plen = 0; @@ -841,7 +526,7 @@ static igraph_error_t igraph_i_gml_convert_to_key(const char *orig, char **key) } *key = IGRAPH_CALLOC(newlen + 1, char); if (! *key) { - IGRAPH_ERROR("Writing GML format failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Writing GML format failed.", IGRAPH_ENOMEM); } memcpy(*key, strno, plen * sizeof(char)); for (i = 0; i < len; i++) { @@ -854,58 +539,19 @@ static igraph_error_t igraph_i_gml_convert_to_key(const char *orig, char **key) return IGRAPH_SUCCESS; } -/* Checks if a vector is free of duplicates. Since NaN == NaN is false, duplicate NaN values - * will not be detected. */ -static igraph_error_t igraph_i_vector_is_duplicate_free(const igraph_vector_t *v, igraph_bool_t *res) { - igraph_vector_t u; - igraph_integer_t n = igraph_vector_size(v); - - if (n < 2) { - *res = true; - return IGRAPH_SUCCESS; - } - - IGRAPH_CHECK(igraph_vector_init_copy(&u, v)); - IGRAPH_FINALLY(igraph_vector_destroy, &u); - igraph_vector_sort(&u); - - *res = true; - for (igraph_integer_t i=1; i < n; i++) { - if (VECTOR(u)[i-1] == VECTOR(u)[i]) { - *res = false; - break; - } - } - - igraph_vector_destroy(&u); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -#define CHECK(cmd) do { int ret=cmd; if (ret<0) IGRAPH_ERROR("Writing GML format failed.", IGRAPH_EFILE); } while (0) +#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. + * \brief Write the graph to a stream in GML format * * GML is a quite general textual format, see - * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 - * for details. + * 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. Boolean attributes are converted - * to numeric, with 0 and 1 used for false and true, respectively. - * NaN values of numeric attributes are skipped, as NaN is not part of the GML - * specification and other software may not be able to read files containing them. - * This is consistent with \ref igraph_read_graph_gml(), which produces NaN - * when an attribute value is missing. In contrast with NaN, infinite values - * are retained. Ensure that none of the numeric attributes values are infinite - * to produce a conformant GML file that can be read by other software. + * 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 + * 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 @@ -914,46 +560,26 @@ static igraph_error_t igraph_i_vector_is_duplicate_free(const igraph_vector_t *v * 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 \c NULL 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 \c NULL and there is a - * numeric id vertex attribute, it will be used instead. If ids - * are not specified in either way then the regular igraph vertex IDs are used. - * If some of the supplied id values are invalid (non-integer or NaN), all supplied - * id are ignored and igraph vertex IDs are used instead. - * - * - * Note that whichever way vertex IDs are specified, their uniqueness is not checked. + * 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. * - * - * If the graph has edge attributes that become source - * or target after encoding, or the graph has an attribute that becomes - * directed, they will be ignored with a warning. GML uses these attributes - * to specify the edge endpoints, and the graph directedness, so we cannot write them - * to the file. Rename them before calling this function if you want to preserve them. + * 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 options Set of |-combinable boolean flags for writing the GML file. - * \clist - * \cli 0 - * All options turned off. - * \cli IGRAPH_WRITE_GML_DEFAULT_SW - * Default options, currently equivalent to 0. May change in future versions. - * \cli IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW - * Do not encode any other characters than " as entities. Specifically, this - * option prevents the encoding of &. Useful when re-exporting a graph - * that was read from a GML file in which igraph could not interpret all entities, - * and thus passed them through without decoding. - * \endclist - * \param id Either NULL or a numeric vector with the vertex IDs. + * \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 \c NULL, the igraph version with the current date and time is added. - * If "", the creator line is omitted. Otherwise, the - * supplied string is used verbatim. + * 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 @@ -965,73 +591,39 @@ static igraph_error_t igraph_i_vector_is_duplicate_free(const igraph_vector_t *v * \example examples/simple/gml.c */ -igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, - igraph_write_gml_sw_t options, - const igraph_vector_t *id, const char *creator) { - igraph_strvector_t gnames, vnames, enames; /* attribute names */ - igraph_vector_int_t gtypes, vtypes, etypes; /* attribute types */ - igraph_integer_t gattr_no, vattr_no, eattr_no; /* attribute counts */ +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; - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - - /* Each element is a bit field used to prevent showing more - * than one warning for each vertex or edge attribute. */ - igraph_vector_int_t warning_shown; + 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; - /* Creator line */ - if (creator == NULL) { - 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\"\n", - IGRAPH_VERSION, timestr)); - } else if (creator[0] == '\0') { - /* creator == "", omit Creator line */ - } else { - if (needs_coding(creator)) { - char *d; - IGRAPH_CHECK(entity_encode(creator, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); - IGRAPH_FINALLY(igraph_free, d); - CHECK(fprintf(outstream, - "Creator \"%s\"\n", - creator)); - IGRAPH_FREE(d); - IGRAPH_FINALLY_CLEAN(1); - } else { - CHECK(fprintf(outstream, - "Creator \"%s\"\n", - creator)); - } - } + time_t curtime = time(0); + char *timestr = ctime(&curtime); + timestr[strlen(timestr) - 1] = '\0'; /* nicely remove \n */ - /* Version line */ - CHECK(fprintf(outstream, "Version 1\n")); - - /* The graph */ - CHECK(fprintf(outstream, "graph\n[\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_INT_INIT_FINALLY(>ypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 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)); - gattr_no = igraph_vector_int_size(>ypes); - vattr_no = igraph_vector_int_size(&vtypes); - eattr_no = igraph_vector_int_size(&etypes); IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); @@ -1039,11 +631,12 @@ igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, /* Check whether there is an 'id' node attribute if the supplied is 0 */ if (!id) { - igraph_bool_t found = false; - for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { - const char *n = igraph_strvector_get(&vnames, i); + 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 = true; break; + found = 1; break; } } if (found) { @@ -1055,162 +648,71 @@ igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, } } - /* Scan id vector for invalid values. If any are found, all ids are ignored. - * Invalid values may occur as a result of reading a GML file in which some - * nodes did not have an id, or by adding new vertices to a graph with an "id" - * attribute. In this case, the "id" attribute will contain NaN values. - */ - if (myid) { - if (igraph_vector_size(myid) != no_of_nodes) { - IGRAPH_ERROR("Size of id vector must match vertex count.", IGRAPH_EINVAL); - } - for (i = 0; i < no_of_nodes; ++i) { - igraph_real_t val = VECTOR(*myid)[i]; - if (val != (igraph_integer_t) val) { - IGRAPH_WARNINGF("%g is not a valid integer id for GML files, ignoring all supplied ids.", val); - myid = NULL; - break; - } - } - } - - if (myid) { - igraph_bool_t duplicate_free; - IGRAPH_CHECK(igraph_i_vector_is_duplicate_free(myid, &duplicate_free)); - if (! duplicate_free) { - IGRAPH_WARNING("Duplicate id values found, ignoring supplies ids."); - myid = NULL; - } - } - /* directedness */ CHECK(fprintf(outstream, " directed %i\n", igraph_is_directed(graph) ? 1 : 0)); /* Graph attributes first */ - for (i = 0; i < gattr_no; i++) { - const char *name; - char *newname; - name = igraph_strvector_get(&gnames, i); + 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 (!strcmp(newname, "directed")) { - IGRAPH_WARNINGF("The graph attribute '%s' was ignored while writing GML format.", name); + 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 { - if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); - /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ - if (! isnan(VECTOR(numv)[0])) { - if (! isfinite(VECTOR(numv)[0])) { - IGRAPH_WARNINGF("Infinite value in numeric graph attribute '%s'. " - "Produced GML file will not be conformant.", name); - } - 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) { - const char *s; - IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); - s = igraph_strvector_get(&strv, 0); - if (needs_coding(s)) { - char *d; - IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); - IGRAPH_FINALLY(igraph_free, d); - CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); - IGRAPH_FREE(d); - IGRAPH_FINALLY_CLEAN(1); - } else { - 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_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored"); } IGRAPH_FREE(newname); IGRAPH_FINALLY_CLEAN(1); } - /* Macros used to work with the bit fiels in 'warning_shown', - * and avoid showing warnings more than once for each attribute. */ -#define GETBIT(k, i) ((k) & (1 << i)) -#define SETBIT(k, i) ((k) |= (1 << i)) -#define WARN_ONCE(attrno, bit, warn) \ - do { \ - igraph_integer_t *p = &VECTOR(warning_shown)[attrno]; \ - if (! GETBIT(*p, bit)) { \ - warn; \ - SETBIT(*p, bit); \ - } \ - } while (0) - - /* Now come the vertices */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&warning_shown, vattr_no); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t j; + long int j; CHECK(fprintf(outstream, " node\n [\n")); /* id */ - CHECK(fprintf(outstream, " id %" IGRAPH_PRId "\n", myid ? (igraph_integer_t)VECTOR(*myid)[i] : i)); + CHECK(fprintf(outstream, " id %li\n", myid ? (long int)VECTOR(*myid)[i] : i)); /* other attributes */ - for (j = 0; j < vattr_no; j++) { - igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(vtypes)[j]; - const char *name; - char *newname; - name = igraph_strvector_get(&vnames, j); + 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")) { - /* No warning, the presence of this attribute is expected, and is handled specially. */ continue; } IGRAPH_CHECK(igraph_i_gml_convert_to_key(name, &newname)); IGRAPH_FINALLY(igraph_free, newname); - if (!strcmp(newname, "id")) { - /* In case an attribute name would conflict with 'id' only after encoding. */ - WARN_ONCE(j, 0, - IGRAPH_WARNINGF("The vertex attribute '%s' was ignored while writing GML format.", name)); + 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 { - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, - igraph_vss_1(i), &numv)); - /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ - if (! isnan(VECTOR(numv)[0])) { - if (! isfinite(VECTOR(numv)[0])) { - WARN_ONCE(j, 3, - IGRAPH_WARNINGF("Infinite value in numeric vertex attribute '%s'. " - "Produced GML file will not be conformant.", name)); - } - CHECK(fprintf(outstream, " %s ", newname)); - CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); - CHECK(fputc('\n', outstream)); - } - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, - igraph_vss_1(i), &strv)); - s = igraph_strvector_get(&strv, 0); - if (needs_coding(s)) { - char *d; - IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); - IGRAPH_FINALLY(igraph_free, d); - CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); - IGRAPH_FREE(d); - IGRAPH_FINALLY_CLEAN(1); - } else { - 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(i), &boolv)); - CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); - WARN_ONCE(j, 1, - IGRAPH_WARNINGF("The boolean vertex attribute '%s' was converted to numeric.", name)); - } else { - WARN_ONCE(j, 2, - IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean vertex attribute '%s' was ignored.", name)); - } + IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); } IGRAPH_FREE(newname); IGRAPH_FINALLY_CLEAN(1); @@ -1219,70 +721,46 @@ igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, } /* The edges too */ - IGRAPH_CHECK(igraph_vector_int_resize(&warning_shown, eattr_no)); - igraph_vector_int_fill(&warning_shown, 0); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - igraph_integer_t j; + 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 %" IGRAPH_PRId "\n", - myid ? (igraph_integer_t)VECTOR(*myid)[from] : from)); - CHECK(fprintf(outstream, " target %" IGRAPH_PRId "\n", - myid ? (igraph_integer_t)VECTOR(*myid)[to] : to)); + 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 < eattr_no; j++) { - igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(etypes)[j]; - const char *name; - char *newname; - name = igraph_strvector_get(&enames, j); + 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 (!strcmp(newname, "source") || !strcmp(newname, "target")) { - WARN_ONCE(j, 0, - IGRAPH_WARNINGF("The edge attribute '%s' was ignored while writing GML format.", name)); + 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 { - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, - igraph_ess_1(i), &numv)); - /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ - if (! isnan(VECTOR(numv)[0])) { - if (! isfinite(VECTOR(numv)[0])) { - WARN_ONCE(j, 3, - IGRAPH_WARNINGF("Infinite value in numeric edge attribute '%s'. " - "Produced GML file will not be conformant.", name)); - } - CHECK(fprintf(outstream, " %s ", newname)); - CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); - CHECK(fputc('\n', outstream)); - } - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, - igraph_ess_1(i), &strv)); - s = igraph_strvector_get(&strv, 0); - if (needs_coding(s)) { - char *d; - IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); - IGRAPH_FINALLY(igraph_free, d); - CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); - IGRAPH_FREE(d); - IGRAPH_FINALLY_CLEAN(1); - } else { - 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(i), &boolv)); - CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); - WARN_ONCE(j, 1, - IGRAPH_WARNINGF("The boolean edge attribute '%s' was converted to numeric.", name)); - } else { - WARN_ONCE(j, 2, - IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean edge attribute '%s' was ignored.", name)); - } + IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); } IGRAPH_FREE(newname); IGRAPH_FINALLY_CLEAN(1); @@ -1292,26 +770,21 @@ igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, CHECK(fprintf(outstream, "]\n")); -#undef GETBIT -#undef SETBIT -#undef WARN_ONCE - if (&v_myid == myid) { igraph_vector_destroy(&v_myid); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&warning_shown); igraph_vector_bool_destroy(&boolv); igraph_strvector_destroy(&strv); igraph_vector_destroy(&numv); - igraph_vector_int_destroy(&etypes); - igraph_vector_int_destroy(&vtypes); - igraph_vector_int_destroy(>ypes); + 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(10); + IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/graphdb.c b/src/vendor/cigraph/src/io/graphdb.c index 1920e39397a..21e1d4b95fa 100644 --- a/src/vendor/cigraph/src/io/graphdb.c +++ b/src/vendor/cigraph/src/io/graphdb.c @@ -23,16 +23,15 @@ #include "igraph_foreign.h" #include "igraph_constructors.h" -#include "core/interruption.h" -/* Read a little-endian encoded 16-bit unsigned word. - * Returns negative value on failure. */ +#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 && b2 != EOF) { + if (b1 != EOF) { c1 = (unsigned char) b1; c2 = (unsigned char) b2; return c1 | (c2 << 8); } else { @@ -40,26 +39,13 @@ static int igraph_i_read_graph_graphdb_getword(FILE *instream) { } } -/* Determine whether the read failed due to an input error or end-of-file condition. - * Must only be called after a read failure, always returns a non-success error code. */ -static igraph_error_t handle_input_error(FILE *instream) { - if (feof(instream)) { - IGRAPH_ERROR("Unexpected end of file, truncated graphdb file.", IGRAPH_PARSEERROR); - } else { - IGRAPH_ERROR("Cannot read from file.", IGRAPH_EFILE); - } -} - /** * \function igraph_read_graph_graphdb * \brief Read a graph in the binary graph database format. * - * This is a binary format, used in the ARG Graph Database - * for isomorphism testing. For more information, see - * https://mivia.unisa.it/datasets/graph-database/arg-database/ - * - * - * From the graph database homepage: + * This is a binary format, used in the graph database + * for isomorphism testing. From the (now defunct) graph database + * homepage: * * * \blockquote @@ -76,11 +62,9 @@ static igraph_error_t handle_input_error(FILE *instream) { * first node of the graph has index 0. \endblockquote * * - * As of igraph 0.10, only unlabelled graphs are implemented. - * + * Only unlabelled graphs are implemented. * \param graph Pointer to an uninitialized graph object. - * \param instream The stream to read from. It should be opened - * in binary mode. + * \param instream The stream to read from. * \param directed Logical scalar, whether to create a directed graph. * \return Error code. * @@ -90,41 +74,45 @@ static igraph_error_t handle_input_error(FILE *instream) { * \example examples/simple/igraph_read_graph_graphdb.c */ -igraph_error_t igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, +int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, igraph_bool_t directed) { - const igraph_integer_t nodes = igraph_i_read_graph_graphdb_getword(instream); - if (nodes < 0) { - IGRAPH_CHECK(handle_input_error(instream)); - } + igraph_vector_t edges; + long int nodes; + long int i, j; + igraph_bool_t end = 0; - igraph_vector_int_t edges; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); - igraph_vector_int_clear(&edges); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - for (igraph_integer_t i = 0; i < nodes; i++) { - igraph_integer_t len = igraph_i_read_graph_graphdb_getword(instream); + 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) { - IGRAPH_CHECK(handle_input_error(instream)); + end = 1; + break; } - for (igraph_integer_t j = 0; j < len; j++) { - igraph_integer_t to = igraph_i_read_graph_graphdb_getword(instream); + for (j = 0; ! end && j < len; j++) { + long int to = igraph_i_read_graph_graphdb_getword(instream); if (to < 0) { - IGRAPH_CHECK(handle_input_error(instream)); + end = 1; + break; } - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); } } - if (fgetc(instream) != EOF) { - IGRAPH_ERROR("Extra bytes at end of graphdb file.", IGRAPH_PARSEERROR); + + if (end) { + IGRAPH_ERROR("Truncated graphdb file", IGRAPH_EFILE); } - IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) nodes, + directed)); + igraph_vector_destroy(&edges); - igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/graphml.c b/src/vendor/cigraph/src/io/graphml.c index b0e587747db..e16683e3e52 100644 --- a/src/vendor/cigraph/src/io/graphml.c +++ b/src/vendor/cigraph/src/io/graphml.c @@ -22,20 +22,17 @@ */ #include "igraph_foreign.h" - #include "igraph_attributes.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/interruption.h" +#include "core/math.h" #include "core/trie.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp & strdup */ -#include "io/parse_utils.h" +#include "internal/hacks.h" /* strcasecmp */ #include "config.h" -#include #include /* isnan */ #include #include /* va_start & co */ @@ -43,7 +40,7 @@ #define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" #if HAVE_LIBXML == 1 -#include +#include #include xmlEntity blankEntityStruct = { @@ -72,20 +69,23 @@ xmlEntity blankEntityStruct = { xmlEntityPtr blankEntity = &blankEntityStruct; -#define toXmlChar(a) (BAD_CAST(a)) -#define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */ - -#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ - do { \ - if (state->successful) { \ - igraph_i_graphml_sax_handler_error(state, msg); \ - } \ - } while (0) -#define RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ - do { \ - GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code); \ - return; \ +#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 */ @@ -96,22 +96,19 @@ typedef struct igraph_i_graphml_attribute_record_t { union { igraph_real_t as_numeric; igraph_bool_t as_boolean; - char *as_string; + char* as_string; } default_value; /* Default value of the attribute, if any */ igraph_attribute_record_t record; } igraph_i_graphml_attribute_record_t; -typedef enum { - START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE, - INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR -} igraph_i_graphml_parser_state_index_t; - struct igraph_i_graphml_parser_state { - igraph_i_graphml_parser_state_index_t st; + 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_int_t edgelist; + igraph_vector_t edgelist; igraph_vector_int_t prev_state_stack; unsigned int unknown_depth; int index; @@ -128,85 +125,53 @@ struct igraph_i_graphml_parser_state { igraph_attribute_elemtype_t data_type; char *error_message; char *data_char; - igraph_integer_t act_node; + 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, target); + "attribute specifications", file, line, 0, target); } -static igraph_error_t igraph_i_graphml_parse_numeric( - const char* char_data, igraph_real_t* result, igraph_real_t default_value -) { - const char* trimmed; - size_t trimmed_length; +static igraph_real_t igraph_i_graphml_parse_numeric(const char* char_data, + igraph_real_t default_value) { + double result; if (char_data == 0) { - *result = default_value; - return IGRAPH_SUCCESS; + return default_value; } - igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); - if (trimmed_length > 0) { - IGRAPH_CHECK(igraph_i_parse_real(trimmed, trimmed_length, result)); - } else { - *result = default_value; + if (sscanf(char_data, "%lf", &result) == 0) { + return default_value; } - return IGRAPH_SUCCESS; + return result; } -static igraph_error_t igraph_i_graphml_parse_boolean( - const char* char_data, igraph_bool_t* result, igraph_bool_t default_value -) { - igraph_integer_t value; - const char* trimmed; - size_t trimmed_length; - +static igraph_bool_t igraph_i_graphml_parse_boolean(const char* char_data, + igraph_bool_t default_value) { + int value; if (char_data == 0) { - *result = default_value; - return IGRAPH_SUCCESS; + return default_value; } - - igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); - - if (trimmed_length == 4 && !strncasecmp(trimmed, "true", trimmed_length)) { - *result = 1; - return IGRAPH_SUCCESS; + if (!strcasecmp("true", char_data)) { + return 1; } - - if (trimmed_length == 3 && !strncasecmp(trimmed, "yes", trimmed_length)) { - *result = 1; - return IGRAPH_SUCCESS; + if (!strcasecmp("yes", char_data)) { + return 1; } - - if (trimmed_length == 5 && !strncasecmp(trimmed, "false", trimmed_length)) { - *result = 0; - return IGRAPH_SUCCESS; + if (!strcasecmp("false", char_data)) { + return 0; } - - if (trimmed_length == 2 && !strncasecmp(trimmed, "no", trimmed_length)) { - *result = 0; - return IGRAPH_SUCCESS; + if (!strcasecmp("no", char_data)) { + return 0; } - - if (trimmed_length > 0) { - if (isdigit(trimmed[0])) { - IGRAPH_CHECK(igraph_i_parse_integer(trimmed, trimmed_length, &value)); - } else { - IGRAPH_ERRORF("Cannot parse '%.*s' as Boolean value.", IGRAPH_PARSEERROR, - (int) trimmed_length, trimmed); - } - } else { - *result = default_value; - return IGRAPH_SUCCESS; + if (sscanf(char_data, "%d", &value) == 0) { + return default_value; } - - *result = value != 0; - return IGRAPH_SUCCESS; + return value != 0; } static void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute_record_t* rec) { @@ -228,19 +193,16 @@ static void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); IGRAPH_FREE(rec->record.value); } - } else if (rec->record.type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - /* no value was set */ } - if (rec->id != NULL) { - xmlFree((void *) rec->id); - rec->id = NULL; + if (rec->id != 0) { + IGRAPH_FREE(rec->id); } if (rec->record.name != 0) { IGRAPH_FREE(rec->record.name); } } -static igraph_error_t igraph_i_graphml_parser_state_init(struct igraph_i_graphml_parser_state* state, igraph_t* graph, int index) { +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; @@ -267,8 +229,8 @@ static igraph_error_t igraph_i_graphml_parser_state_init(struct igraph_i_graphml igraph_i_graphml_attribute_record_destroy); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &state->g_attrs); - IGRAPH_CHECK(igraph_vector_int_init(&state->edgelist, 0)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &state->edgelist); + 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); @@ -295,7 +257,7 @@ static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser igraph_trie_destroy(&state->v_names); igraph_trie_destroy(&state->e_names); igraph_trie_destroy(&state->g_names); - igraph_vector_int_destroy(&state->edgelist); + igraph_vector_destroy(&state->edgelist); igraph_vector_int_destroy(&state->prev_state_stack); igraph_vector_ptr_destroy_all(&state->v_attrs); @@ -303,25 +265,28 @@ static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser igraph_vector_ptr_destroy_all(&state->g_attrs); if (state->data_key) { - xmlFree((void *) state->data_key); + free(state->data_key); state->data_key = NULL; } if (state->data_char) { - IGRAPH_FREE(state->data_char); + free(state->data_char); + state->data_char = NULL; } if (state->error_message) { - IGRAPH_FREE(state->error_message); + free(state->error_message); + state->error_message = NULL; } } -static void igraph_i_graphml_parser_state_set_error_from_varargs( - struct igraph_i_graphml_parser_state *state, const char* msg, va_list args -) { +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; - state->successful = 0; - state->st = ERROR; + va_list ap; + + va_start(ap, msg); if (state->error_message == 0) { /* ownership of state->error_message passed on immediately to @@ -329,69 +294,32 @@ static void igraph_i_graphml_parser_state_set_error_from_varargs( state->error_message = IGRAPH_CALLOC(max_error_message_length, char); } - /* we need to guard against state->error_message == 0, which may happen - * if the memory allocation for the error message itself failed */ - if (state->error_message != 0) { - vsnprintf(state->error_message, max_error_message_length, msg, args); - } -} - -static void igraph_i_graphml_parser_state_set_error_from_xmlerror( - struct igraph_i_graphml_parser_state *state, const xmlErrorPtr error -) { - const size_t max_error_message_length = 4096; - state->successful = 0; state->st = ERROR; + vsnprintf(state->error_message, max_error_message_length, msg, ap); - 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); - } - - /* we need to guard against state->error_message == 0, which may happen - * if the memory allocation for the error message itself failed */ - if (state->error_message != 0) { - snprintf(state->error_message, max_error_message_length, "Line %d: %s", - error->line, error->message); - } -} - -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; - va_list args; - va_start(args, msg); - igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); - va_end(args); + va_end(ap); } static xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0, const xmlChar* name) { xmlEntityPtr predef = xmlGetPredefinedEntity(name); - const char* entityName; - IGRAPH_UNUSED(state0); if (predef != NULL) { return predef; } - - entityName = fromXmlChar(name); - IGRAPH_WARNINGF("Unknown XML entity found: '%s'.", entityName); - + IGRAPH_WARNING("unknown XML entity found\n"); return blankEntity; } -static igraph_error_t igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { +static void igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { if (state->st != UNKNOWN) { - IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + igraph_vector_int_push_back(&state->prev_state_stack, state->st); state->st = UNKNOWN; state->unknown_depth = 1; } else { state->unknown_depth++; } - return IGRAPH_SUCCESS; } static void igraph_i_graphml_sax_handler_start_document(void *state0) { @@ -407,22 +335,22 @@ static void igraph_i_graphml_sax_handler_start_document(void *state0) { state->ignore_namespaces = 0; } -static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { - igraph_integer_t i, l; +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 = false, already_has_edge_id = false; + igraph_bool_t already_has_vertex_id = 0, already_has_edge_id = 0; igraph_vector_ptr_t vattr, eattr, gattr; - igraph_integer_t esize; + 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)); /* +1 for 'id' */ + IGRAPH_CHECK(igraph_vector_ptr_init(&vattr, igraph_vector_ptr_size(&state->v_attrs) + 1)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); - igraph_vector_ptr_resize(&vattr, 0); /* will be filled with push_back() */ esize = igraph_vector_ptr_size(&state->e_attrs); if (igraph_strvector_size(&state->edgeids) != 0) { @@ -430,11 +358,9 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph } IGRAPH_CHECK(igraph_vector_ptr_init(&eattr, esize)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr); - igraph_vector_ptr_resize(&eattr, 0); /* will be filled with push_back() */ IGRAPH_CHECK(igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs))); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr); - igraph_vector_ptr_resize(&gattr, 0); /* will be filled with push_back() */ for (i = 0; i < igraph_vector_ptr_size(&state->v_attrs); i++) { igraph_i_graphml_attribute_record_t *graphmlrec = @@ -442,47 +368,46 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph igraph_attribute_record_t *rec = &graphmlrec->record; /* Check that the name of the vertex attribute is not 'id'. - * If it is then we cannot add the complementary 'id' attribute. */ + 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; - igraph_integer_t origsize = igraph_vector_size(vec); - igraph_integer_t nodes = igraph_trie_size(&state->node_trie); + 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; - igraph_integer_t origsize = igraph_strvector_size(strvec); - igraph_integer_t nodes = igraph_trie_size(&state->node_trie); + 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; - igraph_integer_t origsize = igraph_vector_bool_size(boolvec); - igraph_integer_t nodes = igraph_trie_size(&state->node_trie); + 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; } - } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - continue; /* skipped attribute */ } - igraph_vector_ptr_push_back(&vattr, rec); /* reserved */ + VECTOR(vattr)[i] = rec; } if (!already_has_vertex_id) { idrec.name = idstr; idrec.type = IGRAPH_ATTRIBUTE_STRING; - idrec.value = igraph_i_trie_borrow_keys(&state->node_trie); - igraph_vector_ptr_push_back(&vattr, &idrec); /* reserved */ + tmp = &idrec.value; + IGRAPH_CHECK(igraph_trie_getkeys(&state->node_trie, (const igraph_strvector_t **)tmp)); + VECTOR(vattr)[i] = &idrec; } else { - IGRAPH_WARNING("Could not add vertex ids, there is already an 'id' vertex attribute."); + igraph_vector_ptr_pop_back(&vattr); } for (i = 0; i < igraph_vector_ptr_size(&state->e_attrs); i++) { @@ -496,46 +421,46 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec = (igraph_vector_t*)rec->value; - igraph_integer_t origsize = igraph_vector_size(vec); - igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2; + 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; - igraph_integer_t origsize = igraph_strvector_size(strvec); - igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2; + 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; - igraph_integer_t origsize = igraph_vector_bool_size(boolvec); - igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2; + 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; } - } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - continue; /* skipped attribute */ } - igraph_vector_ptr_push_back(&eattr, rec); /* reserved */ + VECTOR(eattr)[i] = rec; } if (igraph_strvector_size(&state->edgeids) != 0) { if (!already_has_edge_id) { - igraph_integer_t origsize = igraph_strvector_size(&state->edgeids); + 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_int_size(&state->edgelist) / 2)); + 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; - igraph_vector_ptr_push_back(&eattr, &eidrec); /* reserved */ + VECTOR(eattr)[(long int)igraph_vector_ptr_size(&eattr) - 1] = &eidrec; } else { - IGRAPH_WARNING("Could not add edge ids, there is already an 'id' edge attribute."); + igraph_vector_ptr_pop_back(&eattr); + IGRAPH_WARNING("Could not add edge ids, " + "there is already an 'id' edge attribute"); } } @@ -545,36 +470,32 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph igraph_attribute_record_t *rec = &graphmlrec->record; if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { igraph_vector_t *vec = (igraph_vector_t*)rec->value; - igraph_integer_t origsize = igraph_vector_size(vec); + 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; - igraph_integer_t origsize = igraph_strvector_size(strvec); + 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; - igraph_integer_t origsize = igraph_vector_bool_size(boolvec); + 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; } - } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { - continue; /* skipped attribute */ } - igraph_vector_ptr_push_back(&gattr, rec); /* reserved */ + VECTOR(gattr)[i] = rec; } IGRAPH_CHECK(igraph_empty_attrs(state->g, 0, state->edges_directed, &gattr)); - IGRAPH_FINALLY(igraph_destroy, state->g); /* because the next two lines may fail as well */ - IGRAPH_CHECK(igraph_add_vertices(state->g, igraph_trie_size(&state->node_trie), &vattr)); + 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_FINALLY_CLEAN(1); /* graph construction completed successfully */ igraph_vector_ptr_destroy(&vattr); igraph_vector_ptr_destroy(&eattr); @@ -584,51 +505,36 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph 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), (int)((*(it+4))-(*(it+3))) -#define XML_ATTR_VALUE_PF(it) (int)((*(it+4))-(*(it+3))), *(it+3) /* for use in printf-style function with "%.*s" */ - -static igraph_error_t igraph_i_graphml_add_attribute_key( - igraph_i_graphml_attribute_record_t** record, - const xmlChar** attrs, int nb_attrs, - struct igraph_i_graphml_parser_state *state -) { - - /* This function must return in three possible ways: - * - * - a proper newly allocated attribute record is returned in 'record' and - * the function returns IGRAPH_SUCCESS; the parser will process the attribute - * - NULL is returned in 'record' and the function returns an igraph error - * code; the parser will handle the error - * - NULL is returned in 'record', but the function itself returns - * IGRAPH_SUCCESS; the parser will skip the attribute - * - * The caller should be prepared to handle all three cases. - */ +#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; - xmlChar *xmlStr; - char *str; igraph_trie_t *trie = NULL; igraph_vector_ptr_t *ptrvector = NULL; - igraph_integer_t id; - int i; - igraph_i_graphml_attribute_record_t *rec = NULL; - igraph_bool_t skip = false; + long int id; + unsigned short int skip = 0; + int i, ret; + igraph_i_graphml_attribute_record_t *rec; if (!state->successful) { - /* Parser is already in an error state */ - goto exit; + return 0; } rec = IGRAPH_CALLOC(1, igraph_i_graphml_attribute_record_t); - if (rec == NULL) { - IGRAPH_ERROR("Cannot allocate attribute record.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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); @@ -644,33 +550,18 @@ static igraph_error_t igraph_i_graphml_add_attribute_key( localname = XML_ATTR_LOCALNAME(it); if (xmlStrEqual(localname, toXmlChar("id"))) { - xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); - if (xmlStr == NULL) { - IGRAPH_ERROR("Cannot duplicate value of 'id' attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - rec->id = fromXmlChar(xmlStr); - xmlStr = NULL; + rec->id = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); } else if (xmlStrEqual(localname, toXmlChar("attr.name"))) { - xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); - if (xmlStr == NULL) { - IGRAPH_ERROR("Cannot duplicate value of 'attr.name' attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - rec->record.name = fromXmlChar(xmlStr); - xmlStr = NULL; + 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))) { - str = strdup(""); - if (!str) { - IGRAPH_ERROR("Cannot allocate new empty string.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } rec->type = I_GRAPHML_STRING; rec->record.type = IGRAPH_ATTRIBUTE_STRING; - rec->default_value.as_string = str; - str = NULL; + 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; @@ -688,8 +579,9 @@ static igraph_error_t igraph_i_graphml_add_attribute_key( rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; rec->default_value.as_numeric = IGRAPH_NAN; } else { - IGRAPH_ERRORF("Unknown attribute type '%.*s'.", IGRAPH_PARSEERROR, - XML_ATTR_VALUE_PF(it)); + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, unknown attribute type"); + return 0; } } else if (xmlStrEqual(*it, toXmlChar("for"))) { /* graph, vertex or edge attribute? */ @@ -719,64 +611,63 @@ static igraph_error_t igraph_i_graphml_add_attribute_key( igraph_i_report_unhandled_attribute_target("all", IGRAPH_FILE_BASENAME, __LINE__); skip = 1; } else { - IGRAPH_ERRORF("Unknown value '%.*s' in the 'for' attribute of a tag.", IGRAPH_PARSEERROR, - XML_ATTR_VALUE_PF(it)); + 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 == NULL) { - IGRAPH_ERROR("Found tag with no 'id' attribute.", IGRAPH_PARSEERROR); + /* 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 == NULL) { + if (rec->record.name == 0) { rec->record.name = strdup(rec->id); - IGRAPH_CHECK_OOM(rec->record.name, "Cannot duplicate attribute ID as name."); } - /* if the attribute type is missing, ignore the attribute with a warning */ + /* if the attribute type is missing, throw an error */ if (!skip && rec->type == I_GRAPHML_UNKNOWN_TYPE) { - IGRAPH_WARNINGF("Ignoring because of a missing 'attr.type' attribute.", rec->id); + 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) { - IGRAPH_ERROR("Missing 'for' attribute in a tag.", IGRAPH_PARSEERROR); + GRAPHML_PARSE_ERROR(state, + "Cannot parse GraphML file, missing 'for' attribute in a tag"); + return 0; } - /* If attribute is skipped, proceed according to the type of the associated graph element. */ + /* if the code above requested skipping the attribute, free everything and + * return */ if (skip) { - if (trie == 0) { - /* Attribute was skipped because it is not for a node, edge or the graph. - * Free everything and return. */ - if (rec) { - igraph_i_graphml_attribute_record_destroy(rec); - IGRAPH_FREE(rec); - } - IGRAPH_FINALLY_CLEAN(2); - goto exit; - } else { - /* If the skipped attribute was for a supported graph element, we add it - * as "UNSPECIFIED" so that we can avoid reporting "unknown attribute" warnings - * later. */ - rec->record.type = IGRAPH_ATTRIBUTE_UNSPECIFIED; - } + igraph_i_graphml_attribute_record_destroy(rec); + igraph_free(rec); + IGRAPH_FINALLY_CLEAN(2); + return 0; } - /* add to trie, attributes */ - IGRAPH_CHECK(igraph_trie_get(trie, rec->id, &id)); + /* add to trie, attribues */ + igraph_trie_get(trie, rec->id, &id); if (id != igraph_trie_size(trie) - 1) { - IGRAPH_ERRORF("Duplicate attribute found: '%s'.", IGRAPH_PARSEERROR, rec->id); + GRAPHML_PARSE_ERROR(state, "Cannot parse GraphML file, duplicate attribute"); + return 0; } - IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, rec)); + 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 are not responsible - * for destroying and freeing it any more */ - IGRAPH_FINALLY_CLEAN(2); + /* 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) { @@ -786,54 +677,45 @@ static igraph_error_t igraph_i_graphml_add_attribute_key( case IGRAPH_ATTRIBUTE_BOOLEAN: boolvec = IGRAPH_CALLOC(1, igraph_vector_bool_t); if (boolvec == 0) { - IGRAPH_ERROR("Cannot allocate value vector for Boolean attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, boolvec); - IGRAPH_CHECK(igraph_vector_bool_init(boolvec, 0)); rec->record.value = boolvec; - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_bool_init(boolvec, 0); break; case IGRAPH_ATTRIBUTE_NUMERIC: vec = IGRAPH_CALLOC(1, igraph_vector_t); if (vec == 0) { - IGRAPH_ERROR("Cannot allocate value vector for numeric attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_CHECK(igraph_vector_init(vec, 0)); rec->record.value = vec; - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_init(vec, 0); break; case IGRAPH_ATTRIBUTE_STRING: strvec = IGRAPH_CALLOC(1, igraph_strvector_t); if (strvec == 0) { - IGRAPH_ERROR("Cannot allocate value vector for string attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); + return 0; } - IGRAPH_FINALLY(igraph_free, strvec); - IGRAPH_CHECK(igraph_strvector_init(strvec, 0)); rec->record.value = strvec; - IGRAPH_FINALLY_CLEAN(1); - break; - case IGRAPH_ATTRIBUTE_UNSPECIFIED: - rec->record.value = NULL; + igraph_strvector_init(strvec, 0); break; - default: - IGRAPH_FATAL("Unexpected attribute type."); + default: break; } -exit: - *record = rec; - return IGRAPH_SUCCESS; + return rec; } -static igraph_error_t igraph_i_graphml_attribute_data_setup( - struct igraph_i_graphml_parser_state *state, const xmlChar **attrs, - int nb_attrs, igraph_attribute_elemtype_t type -) { +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 IGRAPH_SUCCESS; + return; } for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) { @@ -844,65 +726,52 @@ static igraph_error_t igraph_i_graphml_attribute_data_setup( if (xmlStrEqual(*it, toXmlChar("key"))) { if (state->data_key) { - xmlFree((void *) state->data_key); - state->data_key = NULL; + free(state->data_key); } state->data_key = xmlStrndup(XML_ATTR_VALUE(it)); - if (state->data_key == 0) { - return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ - } if (state->data_char) { - IGRAPH_FREE(state->data_char); + free(state->data_char); } + state->data_char = NULL; state->data_type = type; } else { /* ignore */ } } - - return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_graphml_append_to_data_char( - struct igraph_i_graphml_parser_state *state, const xmlChar *data, int len -) { - igraph_integer_t data_char_new_start = 0; - char* new_data_char; +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 IGRAPH_SUCCESS; + return; } if (state->data_char) { - data_char_new_start = strlen(state->data_char); - new_data_char = IGRAPH_REALLOC(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 { - new_data_char = IGRAPH_CALLOC((size_t) len + 1, char); + state->data_char = IGRAPH_CALLOC((size_t) len + 1, char); } - - if (new_data_char == 0) { - /* state->data_char is left untouched here so that's good */ - return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ + if (state->data_char == 0) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", IGRAPH_ENOMEM); } - - state->data_char = new_data_char; memcpy(state->data_char + data_char_new_start, data, (size_t) len * sizeof(xmlChar)); state->data_char[data_char_new_start + len] = '\0'; - - return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parser_state *state) { +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; - igraph_integer_t recid, id = 0; - igraph_error_t result = IGRAPH_SUCCESS; + long int recid, id = 0; + int ret; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -918,26 +787,30 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra case IGRAPH_ATTRIBUTE_EDGE: trie = &state->e_names; ptrvector = &state->e_attrs; - id = igraph_vector_int_size(&state->edgelist) / 2 - 1; /* hack */ + id = igraph_vector_size(&state->edgelist) / 2 - 1; /* hack */ break; default: - IGRAPH_FATAL("Unexpected attribute element type."); + /* impossible */ + break; } if (key == 0) { /* no key specified, issue a warning */ - IGRAPH_WARNING("Missing attribute key in a tag, ignoring attribute."); - goto exit; + IGRAPH_WARNING("missing attribute key in a tag, ignoring attribute"); + IGRAPH_FREE(state->data_char); + return; } - IGRAPH_CHECK(igraph_trie_check(trie, key, &recid)); + 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_warningf( + "unknown attribute key '%s' in a tag, ignoring attribute", + IGRAPH_FILE_BASENAME, __LINE__, 0, key ); - goto exit; + IGRAPH_FREE(state->data_char); + return; } graphmlrec = VECTOR(*ptrvector)[recid]; @@ -947,43 +820,49 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra igraph_vector_bool_t *boolvec; igraph_vector_t *vec; igraph_strvector_t *strvec; - igraph_integer_t s, i; + 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) { - IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, id + 1)); + 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; } } - IGRAPH_CHECK(igraph_i_graphml_parse_boolean( - state->data_char, VECTOR(*boolvec) + id, 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) { - IGRAPH_CHECK(igraph_vector_resize(vec, id + 1)); + 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; } } - IGRAPH_CHECK(igraph_i_graphml_parse_numeric( - state->data_char, VECTOR(*vec) + id, - 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) { - IGRAPH_CHECK(igraph_strvector_resize(strvec, id + 1)); + 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_CHECK(igraph_strvector_set(strvec, i, strvalue)); + igraph_strvector_set(strvec, i, strvalue); } } if (state->data_char) { @@ -991,85 +870,83 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra } else { strvalue = graphmlrec->default_value.as_string; } - IGRAPH_CHECK(igraph_strvector_set(strvec, id, strvalue)); - break; - case IGRAPH_ATTRIBUTE_UNSPECIFIED: + ret = igraph_strvector_set(strvec, id, strvalue); + if (ret) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file", ret); + } break; default: - IGRAPH_FATAL("Unexpected attribute type."); + break; } -exit: if (state->data_char) { IGRAPH_FREE(state->data_char); - state->data_char = NULL; } - - return result; } -static igraph_error_t igraph_i_graphml_attribute_default_value_finish(struct igraph_i_graphml_parser_state *state) { +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; - igraph_error_t result = IGRAPH_SUCCESS; - char* str = 0; - IGRAPH_ASSERT(state->current_attr_record != NULL); + 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 IGRAPH_SUCCESS; + return; } switch (graphmlrec->record.type) { case IGRAPH_ATTRIBUTE_BOOLEAN: - IGRAPH_CHECK(igraph_i_graphml_parse_boolean( - state->data_char, &graphmlrec->default_value.as_boolean, 0 - )); + graphmlrec->default_value.as_boolean = igraph_i_graphml_parse_boolean( + state->data_char, 0); break; case IGRAPH_ATTRIBUTE_NUMERIC: - IGRAPH_CHECK(igraph_i_graphml_parse_numeric( - state->data_char, &graphmlrec->default_value.as_numeric, IGRAPH_NAN - )); + graphmlrec->default_value.as_numeric = igraph_i_graphml_parse_numeric( + state->data_char, IGRAPH_NAN); break; case IGRAPH_ATTRIBUTE_STRING: - str = strdup(state->data_char); - if (str == NULL) { - IGRAPH_ERROR("Cannot allocate memory for string attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - - if (graphmlrec->default_value.as_string != 0) { - IGRAPH_FREE(graphmlrec->default_value.as_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); } - graphmlrec->default_value.as_string = str; - str = NULL; - break; - case IGRAPH_ATTRIBUTE_UNSPECIFIED: break; default: - IGRAPH_FATAL("Unexpected attribute type."); + break; } if (state->data_char) { IGRAPH_FREE(state->data_char); } - - return result; } -static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( - struct igraph_i_graphml_parser_state* state, const xmlChar* localname, const xmlChar* prefix, +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; - xmlChar* attr_value = 0; - igraph_integer_t id1, id2; + char* attr_value; + long int id1, id2; int i; - igraph_bool_t tag_is_unknown = false; + 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 @@ -1089,8 +966,8 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( } if (tag_is_unknown) { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); - goto exit; + igraph_i_graphml_handle_unknown_start_tag(state); + return; } switch (state->st) { @@ -1103,7 +980,7 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( } state->st = INSIDE_GRAPHML; } else { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + igraph_i_graphml_handle_unknown_start_tag(state); } break; @@ -1132,18 +1009,11 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( } state->index--; } else if (xmlStrEqual(localname, toXmlChar("key"))) { - IGRAPH_CHECK( - igraph_i_graphml_add_attribute_key( - &state->current_attr_record, - attributes, nb_attributes, state - ) - ); - /* NULL is okay here for state->current_attr_record -- we should have - * triggered an error in the parser already if we returned NULL, and - * the rest of the code is prepared to handle NULLs */ + state->current_attr_record = + igraph_i_graphml_add_attribute_key(attributes, nb_attributes, state); state->st = INSIDE_KEY; } else { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + igraph_i_graphml_handle_unknown_start_tag(state); } break; @@ -1153,13 +1023,13 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( if (state->current_attr_record != NULL && xmlStrEqual(localname, toXmlChar("default"))) { state->st = INSIDE_DEFAULT; } else { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + 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_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + igraph_i_graphml_handle_unknown_start_tag(state); break; case INSIDE_GRAPH: @@ -1173,54 +1043,31 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( continue; } if (xmlStrEqual(*it, toXmlChar("source"))) { - attr_value = xmlStrndup(XML_ATTR_VALUE(it)); - if (attr_value == 0) { - IGRAPH_ERROR("Cannot copy value of edge source attribute.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(xmlFree, attr_value); - - IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); - - xmlFree(attr_value); attr_value = NULL; - IGRAPH_FINALLY_CLEAN(1); + 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 = xmlStrndup(XML_ATTR_VALUE(it)); - if (attr_value == 0) { - IGRAPH_ERROR("Cannot copy value of edge target attribute.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(xmlFree, attr_value); - - IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id2)); - - xmlFree(attr_value); attr_value = NULL; - IGRAPH_FINALLY_CLEAN(1); + 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"))) { - igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2 + 1; - igraph_integer_t origsize = igraph_strvector_size(&state->edgeids); - - attr_value = xmlStrndup(XML_ATTR_VALUE(it)); - if (attr_value == 0) { - IGRAPH_ERROR("Cannot copy value of edge ID attribute.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(xmlFree, attr_value); - - IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, edges)); - + 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_CHECK(igraph_strvector_set(&state->edgeids, origsize, "")); + igraph_strvector_set(&state->edgeids, origsize, ""); } - - IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, edges - 1, fromXmlChar(attr_value))); - - xmlFree(attr_value); attr_value = NULL; - IGRAPH_FINALLY_CLEAN(1); + igraph_strvector_set(&state->edgeids, edges - 1, attr_value); + free(attr_value); } } if (id1 >= 0 && id2 >= 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id2)); + igraph_vector_push_back(&state->edgelist, id1); + igraph_vector_push_back(&state->edgelist, id2); } else { - IGRAPH_ERROR("Edge with missing source or target encountered.", IGRAPH_PARSEERROR); + 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"))) { @@ -1232,16 +1079,9 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( continue; } if (xmlStrEqual(XML_ATTR_LOCALNAME(it), toXmlChar("id"))) { - attr_value = xmlStrndup(XML_ATTR_VALUE(it)); - if (attr_value == 0) { - IGRAPH_ERROR("Cannot copy value of node ID attribute.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(xmlFree, attr_value); - - IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); - - xmlFree(attr_value); attr_value = NULL; - IGRAPH_FINALLY_CLEAN(1); + attr_value = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + igraph_trie_get(&state->node_trie, attr_value, &id1); + free(attr_value); break; } } @@ -1249,91 +1089,63 @@ static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( state->act_node = id1; } else { state->act_node = -1; - IGRAPH_ERROR("Node with missing ID encountered.", IGRAPH_PARSEERROR); + igraph_i_graphml_sax_handler_error(state, "Node with missing id encountered"); + return; } state->st = INSIDE_NODE; } else if (xmlStrEqual(localname, toXmlChar("data"))) { - IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( - state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_GRAPH - )); - IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + 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_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + igraph_i_graphml_handle_unknown_start_tag(state); } break; case INSIDE_NODE: if (xmlStrEqual(localname, toXmlChar("data"))) { - IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( - state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_VERTEX - )); - IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + 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; - } else { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } break; case INSIDE_EDGE: if (xmlStrEqual(localname, toXmlChar("data"))) { - IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( - state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_EDGE - )); - IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); + 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; - } else { - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } break; case INSIDE_DATA: /* We do not expect any new tags within a tag */ - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + igraph_i_graphml_handle_unknown_start_tag(state); break; case UNKNOWN: - IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); - break; - - case FINISH: + igraph_i_graphml_handle_unknown_start_tag(state); break; default: - IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); + break; } - -exit: - return IGRAPH_SUCCESS; } -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) { +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; - igraph_error_t result; if (!state->successful) { return; } - result = igraph_i_graphml_sax_handler_start_element_ns_inner( - state, localname, prefix, uri, nb_namespaces, namespaces, - nb_attributes, nb_defaulted, attributes - ); - - if (result != IGRAPH_SUCCESS) { - RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); - } -} - -static igraph_error_t igraph_i_graphml_sax_handler_end_element_ns_inner( - struct igraph_i_graphml_parser_state* state, - const xmlChar* localname, const xmlChar* prefix, - const xmlChar* uri -) { IGRAPH_UNUSED(localname); IGRAPH_UNUSED(prefix); IGRAPH_UNUSED(uri); @@ -1353,7 +1165,7 @@ static igraph_error_t igraph_i_graphml_sax_handler_end_element_ns_inner( break; case INSIDE_DEFAULT: - IGRAPH_CHECK(igraph_i_graphml_attribute_default_value_finish(state)); + igraph_i_graphml_attribute_default_value_finish(state); state->st = INSIDE_KEY; break; @@ -1366,54 +1178,25 @@ static igraph_error_t igraph_i_graphml_sax_handler_end_element_ns_inner( break; case INSIDE_DATA: - IGRAPH_CHECK(igraph_i_graphml_attribute_data_finish(state)); - IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); - state->st = (igraph_i_graphml_parser_state_index_t) igraph_vector_int_pop_back(&state->prev_state_stack); + 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) { - IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); - state->st = (igraph_i_graphml_parser_state_index_t) igraph_vector_int_pop_back(&state->prev_state_stack); + state->st = igraph_vector_int_pop_back(&state->prev_state_stack); } break; - case FINISH: - break; - default: - IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); - } - - return IGRAPH_SUCCESS; -} - -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; - igraph_error_t result; - - if (!state->successful) { - return; - } - - result = igraph_i_graphml_sax_handler_end_element_ns_inner( - state, localname, prefix, uri - ); - - if (result != IGRAPH_SUCCESS) { - RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + 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; - igraph_error_t result = IGRAPH_SUCCESS; if (!state->successful) { return; @@ -1425,17 +1208,13 @@ static void igraph_i_graphml_sax_handler_chars(void* state0, const xmlChar* ch, case INSIDE_DATA: case INSIDE_DEFAULT: - result = igraph_i_graphml_append_to_data_char(state, ch, len); + igraph_i_graphml_append_to_data_char(state, ch, len); break; default: /* just ignore it */ break; } - - if (result != IGRAPH_SUCCESS) { - RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); - } } static xmlSAXHandler igraph_i_graphml_sax_handler = { @@ -1477,10 +1256,9 @@ static xmlSAXHandler igraph_i_graphml_sax_handler = { #define IS_FORBIDDEN_CONTROL_CHAR(x) ((x) < ' ' && (x) != '\t' && (x) != '\r' && (x) != '\n') -static igraph_error_t igraph_i_xml_escape(const char* src, char** dest) { - igraph_integer_t destlen = 0; - const char *s; - char *d; +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++) { @@ -1496,12 +1274,15 @@ static igraph_error_t igraph_i_xml_escape(const char* src, char** dest) { } else if (ch == '\'') { destlen += 5; } else if (IS_FORBIDDEN_CONTROL_CHAR(ch)) { - IGRAPH_ERRORF("Forbidden control character 0x%02X found in igraph_i_xml_escape.", IGRAPH_EINVAL, 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); } for (s = src, d = *dest; *s; s++, d++) { ch = (unsigned char)(*s); @@ -1521,23 +1302,8 @@ static igraph_error_t igraph_i_xml_escape(const char* src, char** dest) { } } *d = 0; - return IGRAPH_SUCCESS; -} - -#if HAVE_LIBXML == 1 -static void igraph_i_libxml_generic_error_handler(void* ctx, const char* msg, ...) { - struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; - va_list args; - va_start(args, msg); - igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); - va_end(args); -} - -static void igraph_i_libxml_structured_error_handler(void* ctx, xmlErrorPtr error) { - struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; - igraph_i_graphml_parser_state_set_error_from_xmlerror(state, error); + return 0; } -#endif /** * \ingroup loadsave @@ -1549,8 +1315,7 @@ static void igraph_i_libxml_structured_error_handler(void* ctx, xmlErrorPtr erro * 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, see \ref igraph_set_attribute_table(). String attrribute values - * are returned in UTF-8 encoding. + * is attached, i.e. if you use igraph from R or Python. * * * Graph attribute names are taken from the attr.name attributes of the @@ -1574,14 +1339,10 @@ static void igraph_i_libxml_structured_error_handler(void* ctx, xmlErrorPtr erro * * \example examples/simple/graphml.c */ -igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph_integer_t index) { +int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { #if HAVE_LIBXML == 1 xmlParserCtxtPtr ctxt; - xmlGenericErrorFunc libxml_old_generic_error_handler; - void* libxml_old_generic_error_context; - xmlStructuredErrorFunc libxml_old_structured_error_handler; - void* libxml_old_structured_error_context; xmlDocPtr doc; struct igraph_i_graphml_parser_state state; @@ -1591,83 +1352,65 @@ igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph char* error_message; if (index < 0) { - IGRAPH_ERROR("Graph index must be non-negative.", IGRAPH_EINVAL); + 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 and use the first 4K to detect the - * encoding */ + /* Create a progressive parser context */ res = (int) fread(buffer, 1, sizeof(buffer), instream); - if (res < (int) sizeof buffer && !feof(instream)) { - IGRAPH_ERROR("IO error while reading GraphML data.", IGRAPH_EFILE); + 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_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); } - /* Retrieve the current libxml2 error handlers and temporarily replace them - * with ones that do not print anything to stdout/stderr */ - libxml_old_generic_error_handler = xmlGenericError; - libxml_old_generic_error_context = xmlGenericErrorContext; - libxml_old_structured_error_handler = xmlStructuredError; - libxml_old_structured_error_context = xmlStructuredErrorContext; - xmlSetGenericErrorFunc(&state, &igraph_i_libxml_generic_error_handler); - xmlSetStructuredErrorFunc(&state, &igraph_i_libxml_structured_error_handler); - /* 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 put a barrier on the FINALLY stack - * that prevents IGRAPH_ERROR() from freeing the parser state, and then we - * do this ourselves when needed */ - IGRAPH_FINALLY_ENTER(); - { - ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler, - &state, - buffer, - res, - NULL); - if (ctxt) { - if (xmlCtxtUseOptions(ctxt, - XML_PARSE_NOBLANKS | - XML_PARSE_NONET | XML_PARSE_NSCLEAN | - XML_PARSE_NOCDATA | XML_PARSE_HUGE - )) { - xmlFreeParserCtxt(ctxt); - ctxt = NULL; - } - } + * 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 */ - if (ctxt) { - while ((res = (int) fread(buffer, 1, sizeof buffer, instream)) > 0) { - xmlParseChunk(ctxt, buffer, res, 0); - if (!state.successful) { - break; - } - IGRAPH_ALLOW_INTERRUPTION(); - } - xmlParseChunk(ctxt, buffer, res, 1); + /* Do the parsing */ + while ((res = (int) fread(buffer, 1, sizeof(buffer), instream)) > 0) { + xmlParseChunk(ctxt, buffer, res, 0); + if (!state.successful) { + break; } } - IGRAPH_FINALLY_EXIT(); - - /* Restore error handlers */ - xmlSetGenericErrorFunc(libxml_old_generic_error_context, libxml_old_generic_error_handler); - xmlSetStructuredErrorFunc(libxml_old_structured_error_context, libxml_old_structured_error_handler); + xmlParseChunk(ctxt, buffer, res, 1); /* Free the context */ - if (ctxt) { - 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); - } - } else { - /* We could not create the context earlier so no parsing was done */ - IGRAPH_ERROR("Cannot create XML parser context.", IGRAPH_FAILURE); + 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 @@ -1676,28 +1419,28 @@ igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph 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(igraph_free, error_message); + IGRAPH_FINALLY(free, error_message); } /* Trigger the stored error if needed */ if (!parsing_successful) { if (error_message != NULL) { - size_t len = strlen(error_message); - if (error_message[len-1] == '\n') { - error_message[len-1] = '\0'; - } IGRAPH_ERROR(error_message, IGRAPH_PARSEERROR); } else { - IGRAPH_ERROR("Malformed GraphML file.", IGRAPH_PARSEERROR); + 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); + IGRAPH_ERROR("Graph index was too large", IGRAPH_EINVAL); } /* Okay, everything seems good. We can now take the parser state and @@ -1713,22 +1456,20 @@ igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph IGRAPH_UNUSED(instream); IGRAPH_UNUSED(index); - IGRAPH_ERROR("GraphML support is disabled.", IGRAPH_UNIMPLEMENTED); + 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. + * \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. * - * - * When a numerical attribute value is NaN, it will be omitted from the file. - * * \param graph The graph to write. * \param outstream The stream object to write to, it should be * writable. @@ -1745,14 +1486,14 @@ igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph * * \example examples/simple/graphml.c */ -igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, +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_int_t gtypes, vtypes, etypes; - igraph_integer_t i; + igraph_vector_t gtypes, vtypes, etypes; + long int i; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; @@ -1762,27 +1503,27 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n", GRAPHML_NAMESPACE_URI); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } /* dump the elements if any */ @@ -1794,81 +1535,81 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&enames, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(>ypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 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_int_size(>ypes); i++) { - const char *name; char *name_escaped; - name = igraph_strvector_get(&gnames, i); + 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); + 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); + 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_ERROR("Write failed", IGRAPH_EFILE); } } IGRAPH_FREE(name_escaped); } /* vertex attributes */ - for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { - const char *name; char *name_escaped; - name = igraph_strvector_get(&vnames, i); + 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); + 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); + 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_ERROR("Write failed", IGRAPH_EFILE); } } IGRAPH_FREE(name_escaped); } /* edge attributes */ - for (i = 0; i < igraph_vector_int_size(&etypes); i++) { - const char *name; char *name_escaped; - name = igraph_strvector_get(&enames, i); + 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); + 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); + 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_ERROR("Write failed", IGRAPH_EFILE); } } IGRAPH_FREE(name_escaped); @@ -1876,64 +1617,60 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream ret = fprintf(outstream, " \n", (igraph_is_directed(graph) ? "directed" : "undirected")); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } /* Write the graph atributes before anything else */ - for (i = 0; i < igraph_vector_int_size(>ypes); i++) { - const char *name; char *name_escaped; + for (i = 0; i < igraph_vector_size(>ypes); i++) { + char *name, *name_escaped; if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - name = igraph_strvector_get(&gnames, i); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - char *s_escaped; - name = igraph_strvector_get(&gnames, i); + 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); - if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); - } IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); - s = igraph_strvector_get(&strv, 0); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - name = igraph_strvector_get(&gnames, i); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } @@ -1941,16 +1678,16 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream /* Let's dump the nodes first */ vc = igraph_vcount(graph); for (l = 0; l < vc; l++) { - const char *name; char *name_escaped; - ret = fprintf(outstream, " \n", l); + char *name, *name_escaped; + ret = fprintf(outstream, " \n", (long)l); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } - for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + for (i = 0; i < igraph_vector_size(&vtypes); i++) { if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - name = igraph_strvector_get(&vnames, i); + 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])) { @@ -1958,43 +1695,39 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream ret = fprintf(outstream, " ", vprefix, name_escaped); IGRAPH_FREE(name_escaped); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - char *s_escaped; - name = igraph_strvector_get(&vnames, i); + 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); - if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); - } IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(l), &strv)); - s = igraph_strvector_get(&strv, 0); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - name = igraph_strvector_get(&vnames, i); + 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)); @@ -2002,93 +1735,89 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream vprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); IGRAPH_FREE(name_escaped); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } /* Now the edges */ - IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &it)); + 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; - const char *name; char *name_escaped; - igraph_integer_t edge = IGRAPH_EIT_GET(it); - igraph_edge(graph, edge, &from, &to); - ret = fprintf(outstream, " \n", - 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } - for (i = 0; i < igraph_vector_int_size(&etypes); i++) { + for (i = 0; i < igraph_vector_size(&etypes); i++) { if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - name = igraph_strvector_get(&enames, i); + igraph_strvector_get(&enames, i, &name); IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, - igraph_ess_1(edge), &numv)); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - const char *s; - char *s_escaped; - name = igraph_strvector_get(&enames, i); + 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); - if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); - } IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, - igraph_ess_1(edge), &strv)); - s = igraph_strvector_get(&strv, 0); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - name = igraph_strvector_get(&enames, i); + igraph_strvector_get(&enames, i, &name); IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, - igraph_ess_1(edge), &boolv)); + 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); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } } ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -2097,23 +1826,23 @@ igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } igraph_strvector_destroy(&gnames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&enames); - igraph_vector_int_destroy(>ypes); - igraph_vector_int_destroy(&vtypes); - igraph_vector_int_destroy(&etypes); + 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(9); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/leda.c b/src/vendor/cigraph/src/io/leda.c index 1191eaa163b..b33f2862a27 100644 --- a/src/vendor/cigraph/src/io/leda.c +++ b/src/vendor/cigraph/src/io/leda.c @@ -30,11 +30,7 @@ #include -#define CHECK(cmd) \ - do { \ - int ret=(cmd); \ - if (ret<0) IGRAPH_ERROR("Writing LEDA format failed.", IGRAPH_EFILE); \ - } while (0) +#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } while (0) /** * \function igraph_write_graph_leda @@ -51,68 +47,64 @@ * \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 should be stored. + * 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 should be stored. + * 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 */ -igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, - const char *vertex_attr_name, - const char *edge_attr_name) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); +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; - igraph_integer_t i = 0; - igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; - igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + 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_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)) { - IGRAPH_WARNINGF("The vertex attribute '%s' does not exist. No vertex values will be written.", - vertex_attr_name); - vertex_attr_name = NULL; + 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_type != IGRAPH_ATTRIBUTE_BOOLEAN) { - IGRAPH_WARNINGF("The vertex attribute '%s' is not numeric, string or boolean. " - "No vertex values will be written.", - vertex_attr_name); - vertex_attr_name = NULL; vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + 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)) { - IGRAPH_WARNINGF("The edge attribute '%s' does not exist. No edge values will be written.", - edge_attr_name); - edge_attr_name = NULL; + 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_type != IGRAPH_ATTRIBUTE_BOOLEAN) { - IGRAPH_WARNINGF("The edge attribute '%s' is not numeric, string or boolean. " - "No edge values will be written.", - edge_attr_name); - edge_attr_name = NULL; edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + 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"); } } @@ -121,28 +113,22 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, switch (vertex_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: - CHECK(fprintf(outstream, "double\n")); + CHECK(fprintf(outstream, "float\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; - case IGRAPH_ATTRIBUTE_BOOLEAN: - CHECK(fprintf(outstream, "bool\n")); - break; default: CHECK(fprintf(outstream, "void\n")); } switch (edge_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: - CHECK(fprintf(outstream, "double\n")); + CHECK(fprintf(outstream, "float\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; - case IGRAPH_ATTRIBUTE_BOOLEAN: - CHECK(fprintf(outstream, "bool\n")); - break; default: CHECK(fprintf(outstream, "void\n")); } @@ -151,7 +137,7 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, /* Start writing vertices */ CHECK(fprintf(outstream, "# Vertices\n")); - CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_nodes)); + CHECK(fprintf(outstream, "%ld\n", no_of_nodes)); if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Vertices with numeric attributes */ @@ -180,9 +166,9 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, graph, vertex_attr_name, igraph_vss_all(), &values)); for (i = 0; i < no_of_nodes; i++) { - const char *str = STR(values, i); + const char* str = STR(values, i); if (strchr(str, '\n') != 0) { - IGRAPH_ERROR("Vertex attribute values cannot contain newline characters.", + IGRAPH_ERROR("edge attribute values cannot contain newline characters", IGRAPH_EINVAL); } CHECK(fprintf(outstream, "|{%s}|\n", str)); @@ -190,20 +176,6 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); - } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - /* Vertices with boolean attributes */ - igraph_vector_bool_t values; - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_nodes); - IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr( - graph, vertex_attr_name, igraph_vss_all(), &values)); - - for (i = 0; i < no_of_nodes; i++) { - CHECK(fprintf(outstream, "|{%s|}\n", VECTOR(values)[i] ? "true" : "false")); - } - - igraph_vector_bool_destroy(&values); - IGRAPH_FINALLY_CLEAN(1); } else { /* Vertices with no attributes */ for (i = 0; i < no_of_nodes; i++) { @@ -212,7 +184,7 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, } CHECK(fprintf(outstream, "# Edges\n")); - CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_edges)); + CHECK(fprintf(outstream, "%ld\n", no_of_edges)); if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Edges with numeric attributes */ @@ -221,15 +193,15 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, 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)) { - igraph_integer_t eid = IGRAPH_EIT_GET(it); - igraph_edge(graph, eid, &from, &to); - igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + 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, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{", - from + 1, to + 1, - 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); @@ -244,59 +216,35 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, 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)) { - igraph_integer_t eid = IGRAPH_EIT_GET(it); - const char *str = STR(values, eid); - igraph_edge(graph, eid, &from, &to); - igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + 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_ERROR("edge attribute values cannot contain newline characters", IGRAPH_EINVAL); } - CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", - from + 1, to + 1, - rev + 1, str)); + 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 if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - /* Edges with boolean attributes */ - igraph_vector_bool_t values; - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_edges); - IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr( - graph, vertex_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); - - while (!IGRAPH_EIT_END(it)) { - igraph_integer_t eid = IGRAPH_EIT_GET(it); - igraph_edge(graph, eid, &from, &to); - igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); - if (rev == IGRAPH_EIT_GET(it)) { - rev = -1; - } - CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", - from + 1, to + 1, - rev + 1, - VECTOR(values)[eid] ? "true" : "false")); - IGRAPH_EIT_NEXT(it); - } - - igraph_vector_bool_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, IGRAPH_DIRECTED, false); + igraph_get_eid(graph, &rev, to, from, 1, 0); if (rev == IGRAPH_EIT_GET(it)) { rev = -1; } - CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{}|\n", - from + 1, to + 1, - 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); } } @@ -304,7 +252,7 @@ igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } #undef CHECK diff --git a/src/vendor/cigraph/src/io/lgl-header.h b/src/vendor/cigraph/src/io/lgl-header.h index 73361d3f152..fdb8f587f00 100644 --- a/src/vendor/cigraph/src/io/lgl-header.h +++ b/src/vendor/cigraph/src/io/lgl-header.h @@ -29,10 +29,9 @@ typedef struct { void *scanner; int eof; char errmsg[300]; - igraph_error_t igraph_errno; - igraph_bool_t has_weights; - igraph_vector_int_t *vector; + int has_weights; + igraph_vector_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; - igraph_integer_t actvertex; + int actvertex; } igraph_i_lgl_parsedata_t; diff --git a/src/vendor/cigraph/src/io/lgl-lexer.l b/src/vendor/cigraph/src/io/lgl-lexer.l index 7dfaa6cf9d2..e1ed9acc07e 100644 --- a/src/vendor/cigraph/src/io/lgl-lexer.l +++ b/src/vendor/cigraph/src/io/lgl-lexer.l @@ -44,6 +44,7 @@ */ +#include "config.h" #include #include "io/lgl-header.h" @@ -69,7 +70,6 @@ %option reentrant %option bison-bridge %option bison-locations -%option yylineno alnum [^ \t\r\n\0#] diff --git a/src/vendor/cigraph/src/io/lgl-parser.y b/src/vendor/cigraph/src/io/lgl-parser.y index c10c369a9ee..88dd4aa567e 100644 --- a/src/vendor/cigraph/src/io/lgl-parser.y +++ b/src/vendor/cigraph/src/io/lgl-parser.y @@ -44,21 +44,23 @@ */ +#include +#include + #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" +#include "config.h" +#include "core/math.h" #include "io/lgl-header.h" #include "io/parsers/lgl-parser.h" #include "io/parsers/lgl-lexer.h" -#include "io/parse_utils.h" #include "internal/hacks.h" -#include -#include - 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 %} @@ -74,17 +76,17 @@ int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, %lex-param { void *scanner } %union { - igraph_integer_t edgenum; - igraph_real_t weightnum; + long int edgenum; + double weightnum; } %type edgeid %type weight -%token ALNUM "alphanumeric" -%token NEWLINE "end of line" -%token HASH "#" -%token END 0 "end of file" /* friendly name for $end */ +%token ALNUM +%token NEWLINE +%token HASH "#" +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %% @@ -101,36 +103,26 @@ vertexdef : HASH edgeid NEWLINE { context->actvertex=$2; } ; edges : /* empty */ | edges edge ; edge : edgeid NEWLINE { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); - IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, 0)); + 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_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); - IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, $2)); + 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_integer_t trie_id; - IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, - igraph_lgl_yyget_text(scanner), - igraph_lgl_yyget_leng(scanner), - &trie_id - )); - $$ = trie_id; -}; - -weight : ALNUM { - igraph_real_t val; - IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_lgl_yyget_text(scanner), - igraph_lgl_yyget_leng(scanner), - &val)); - $$=val; -} ; +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)); } ; %% @@ -142,3 +134,13 @@ int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, 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/vendor/cigraph/src/io/lgl.c b/src/vendor/cigraph/src/io/lgl.c index ff39e9465ac..81f7dbee715 100644 --- a/src/vendor/cigraph/src/io/lgl.c +++ b/src/vendor/cigraph/src/io/lgl.c @@ -27,8 +27,7 @@ #include "graph/attributes.h" -#include "io/lgl-header.h" -#include "io/parsers/lgl-parser.h" +#include "lgl-header.h" int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined, void* scanner); @@ -44,8 +43,9 @@ void igraph_lgl_yylex_destroy_wrapper (void *scanner ) { /** * \ingroup loadsave * \function igraph_read_graph_lgl - * \brief Reads a graph from an .lgl file. + * \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 @@ -70,7 +70,7 @@ vertex3name [optionalWeight] \endverbatim * 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 \c true the symbolic names of the + * \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 @@ -101,13 +101,12 @@ vertex3name [optionalWeight] \endverbatim * * \example examples/simple/igraph_read_graph_lgl.c */ -igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, +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_int_t edges = IGRAPH_VECTOR_NULL; - igraph_vector_t ws = IGRAPH_VECTOR_NULL; + 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; @@ -116,7 +115,7 @@ igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_i_lgl_parsedata_t context; IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&trie, names); context.has_weights = 0; @@ -124,51 +123,32 @@ igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, context.weights = &ws; context.trie = ≜ context.eof = 0; - context.errmsg[0] = '\0'; - context.igraph_errno = IGRAPH_SUCCESS; igraph_lgl_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_lgl_yylex_destroy_wrapper, context.scanner); igraph_lgl_yyset_in(instream, context.scanner); - /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ - IGRAPH_FINALLY_ENTER(); - int err = igraph_lgl_yyparse(&context); - IGRAPH_FINALLY_EXIT(); - switch (err) { - case 0: /* success */ - break; - case 1: /* parse error */ + if (igraph_lgl_yyparse(&context)) { if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); - } else if (context.igraph_errno != IGRAPH_SUCCESS) { - IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR); } - break; - case 2: /* out of memory */ - IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - default: /* must never reach here */ - /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison - * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being - * returned in place of a Bison error code. - * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? - */ - IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading LGL file.", err); } - /* Prepare attributes, if needed */ + 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 = igraph_i_trie_borrow_keys(&trie); + namerec.value = namevec; VECTOR(name)[0] = &namerec; } @@ -183,10 +163,8 @@ igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - IGRAPH_FINALLY(igraph_destroy, graph); - IGRAPH_CHECK(igraph_add_vertices(graph, igraph_trie_size(&trie), pname)); + IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) + igraph_trie_size(&trie), pname)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight)); if (pweight) { @@ -198,19 +176,20 @@ igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, IGRAPH_FINALLY_CLEAN(1); } igraph_trie_destroy(&trie); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_vector_destroy(&ws); igraph_lgl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** * \ingroup loadsave * \function igraph_write_graph_lgl - * \brief Writes the graph to a file in .lgl format. + * \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. * @@ -228,25 +207,27 @@ igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, * \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 \c true isolated vertices are also written - * to the file. If \c false they will be omitted. + * \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 \c false, - * O(|V|+|E|) otherwise. All file operations are expected to have - * time complexity O(1). + * 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 */ -igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, +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; - igraph_integer_t actvertex = -1; + long int actvertex = -1; igraph_attribute_type_t nametype, weighttype; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), @@ -256,50 +237,51 @@ igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, /* Check if we have the names attribute */ if (names && !igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, names)) { - IGRAPH_WARNINGF("Names attribute '%s' does not exists.", names); - names = NULL; + 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_WARNINGF("Ignoring names attribute '%s', unknown attribute type.", names); - names = NULL; + 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)) { - IGRAPH_WARNINGF("Weights attribute '%s' does not exists.", weights); - weights = NULL; + 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_WARNINGF("Ignoring weights attribute '%s', unknown attribute type.", weights); - weights = NULL; + IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); + weights = 0; } } - if (names == NULL && weights == NULL) { + 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, "%" IGRAPH_PRId "\n", to); + ret = fprintf(outstream, "%li\n", (long int)to); } else { actvertex = from; - ret = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId "\n", from, to); + ret = fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to); } if (ret < 0) { - IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } - } else if (weights == NULL) { + } else if (weights == 0) { /* No weights but use names */ igraph_strvector_t nvec; IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); @@ -311,24 +293,24 @@ igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0; - const char *str1, *str2; + char *str1, *str2; igraph_edge(graph, edge, &from, &to); - str2 = igraph_strvector_get(&nvec, to); + igraph_strvector_get(&nvec, to, &str2); if (from == actvertex) { ret = fprintf(outstream, "%s\n", str2); } else { actvertex = from; - str1 = igraph_strvector_get(&nvec, from); + igraph_strvector_get(&nvec, from, &str1); ret = fprintf(outstream, "# %s\n%s\n", str1, str2); } if (ret < 0) { - IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } IGRAPH_FINALLY_CLEAN(1); - } else if (names == NULL) { + } else if (names == 0) { /* No names but weights */ igraph_vector_t wvec; IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); @@ -341,15 +323,15 @@ igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, int ret1, ret2, ret3; igraph_edge(graph, edge, &from, &to); if (from == actvertex) { - ret1 = fprintf(outstream, "%" IGRAPH_PRId " ", to); + ret1 = fprintf(outstream, "%li ", (long)to); } else { actvertex = from; - ret1 = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId " ", from, to); + ret1 = fprintf(outstream, "# %li\n%li ", (long)from, (long)to); } - ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + 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 LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -372,23 +354,23 @@ igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0, ret2; - const char *str1, *str2; + char *str1, *str2; igraph_edge(graph, edge, &from, &to); - str2 = igraph_strvector_get(&nvec, to); + igraph_strvector_get(&nvec, to, &str2); if (from == actvertex) { ret = fprintf(outstream, "%s ", str2); } else { actvertex = from; - str1 = igraph_strvector_get(&nvec, from); + igraph_strvector_get(&nvec, from, &str1); ret = fprintf(outstream, "# %s\n%s ", str1, str2); } if (ret < 0) { - IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } - ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); ret2 = fputc('\n', outstream); if (ret < 0 || ret2 == EOF) { - IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -398,36 +380,39 @@ igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, } if (isolates) { - igraph_integer_t nov = igraph_vcount(graph); - igraph_integer_t i; + long int nov = igraph_vcount(graph); + long int i; int ret = 0; - igraph_integer_t deg; + igraph_vector_t deg; igraph_strvector_t nvec; - const char *str; + 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_CHECK(igraph_degree_1(graph, °, i, IGRAPH_ALL, IGRAPH_LOOPS)); - if (deg == 0) { - if (names == NULL) { - ret = fprintf(outstream, "# %" IGRAPH_PRId "\n", 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(i), &nvec)); - str = igraph_strvector_get(&nvec, 0); + 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 LGL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } } igraph_strvector_destroy(&nvec); - IGRAPH_FINALLY_CLEAN(1); + igraph_vector_destroy(°); + IGRAPH_FINALLY_CLEAN(2); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/ncol-header.h b/src/vendor/cigraph/src/io/ncol-header.h index 31e6c03f5b7..2ed06f287b5 100644 --- a/src/vendor/cigraph/src/io/ncol-header.h +++ b/src/vendor/cigraph/src/io/ncol-header.h @@ -29,9 +29,8 @@ typedef struct { void *scanner; int eof; char errmsg[300]; - igraph_error_t igraph_errno; - igraph_bool_t has_weights; - igraph_vector_int_t *vector; + int has_weights; + igraph_vector_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; } igraph_i_ncol_parsedata_t; diff --git a/src/vendor/cigraph/src/io/ncol-lexer.l b/src/vendor/cigraph/src/io/ncol-lexer.l index fea437e3088..a2ed8d34f3f 100644 --- a/src/vendor/cigraph/src/io/ncol-lexer.l +++ b/src/vendor/cigraph/src/io/ncol-lexer.l @@ -44,6 +44,7 @@ */ +#include "config.h" #include #include "io/ncol-header.h" @@ -69,7 +70,6 @@ %option reentrant %option bison-bridge %option bison-locations -%option yylineno alnum [^ \t\n\r\0] diff --git a/src/vendor/cigraph/src/io/ncol-parser.y b/src/vendor/cigraph/src/io/ncol-parser.y index 4339bde5829..efb32b9be22 100644 --- a/src/vendor/cigraph/src/io/ncol-parser.y +++ b/src/vendor/cigraph/src/io/ncol-parser.y @@ -44,22 +44,24 @@ */ +#include +#include + #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" +#include "config.h" +#include "core/math.h" #include "io/ncol-header.h" #include "io/parsers/ncol-parser.h" #include "io/parsers/ncol-lexer.h" -#include "io/parse_utils.h" #include "internal/hacks.h" -#include -#include - 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 %} @@ -75,16 +77,16 @@ int igraph_ncol_yyerror(YYLTYPE* locp, %lex-param { void *scanner } %union { - igraph_integer_t edgenum; - igraph_real_t weightnum; + long int edgenum; + double weightnum; } %type edgeid %type weight -%token ALNUM "alphanumeric" -%token NEWLINE "end of line" -%token END 0 "end of file" /* friendly name for $end */ +%token ALNUM +%token NEWLINE +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %% @@ -95,35 +97,25 @@ input : /* empty */ ; edge : edgeid edgeid NEWLINE { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2)); - IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, 0.0)); + 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_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2)); - IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, $3)); + 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_integer_t trie_id; - IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, - igraph_ncol_yyget_text(scanner), - igraph_ncol_yyget_leng(scanner), - &trie_id - )); - $$ = trie_id; -}; - -weight : ALNUM { - igraph_real_t val; - IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_ncol_yyget_text(scanner), - igraph_ncol_yyget_leng(scanner), - &val)); - $$=val; -} ; +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)); } ; %% @@ -135,3 +127,14 @@ int igraph_ncol_yyerror(YYLTYPE* locp, 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/vendor/cigraph/src/io/ncol.c b/src/vendor/cigraph/src/io/ncol.c index 7bbe42a6f54..d0417c9febb 100644 --- a/src/vendor/cigraph/src/io/ncol.c +++ b/src/vendor/cigraph/src/io/ncol.c @@ -27,8 +27,7 @@ #include "graph/attributes.h" -#include "io/ncol-header.h" -#include "io/parsers/ncol-parser.h" +#include "ncol-header.h" int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined, void* scanner); @@ -69,12 +68,12 @@ void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { * \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 + * 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 \c true the symbolic names of the + * \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 @@ -103,38 +102,39 @@ void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { * * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() */ -igraph_error_t igraph_read_graph_ncol(igraph_t *graph, FILE *instream, +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_int_t edges; - igraph_vector_t ws; + igraph_vector_t edges, ws; igraph_trie_t trie = IGRAPH_TRIE_NULL; igraph_integer_t no_of_nodes; - igraph_integer_t no_predefined = 0; + long int no_predefined = 0; igraph_vector_ptr_t name, weight; - igraph_vector_ptr_t *pname = NULL, *pweight = NULL; + 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_VECTOR_INT_INIT_FINALLY(&edges, 0); + 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) { - igraph_integer_t i, id, n; - const char *key; + long int i, id, n; + char *key; n = no_predefined = igraph_strvector_size(predefnames); for (i = 0; i < n; i++) { - key = igraph_strvector_get(predefnames, i); - IGRAPH_CHECK(igraph_trie_get(&trie, key, &id)); + igraph_strvector_get(predefnames, i, &key); + igraph_trie_get(&trie, key, &id); if (id != i) { - IGRAPH_WARNING("Reading NCOL file, duplicate entry in predefined names."); + IGRAPH_WARNING("reading NCOL file, duplicate entry in predefnames"); no_predefined--; } } @@ -145,64 +145,39 @@ igraph_error_t igraph_read_graph_ncol(igraph_t *graph, FILE *instream, context.weights = &ws; context.trie = ≜ context.eof = 0; - context.errmsg[0] = '\0'; - context.igraph_errno = IGRAPH_SUCCESS; igraph_ncol_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_ncol_yylex_destroy_wrapper, context.scanner); igraph_ncol_yyset_in(instream, context.scanner); - /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ - IGRAPH_FINALLY_ENTER(); - int err = igraph_ncol_yyparse(&context); - IGRAPH_FINALLY_EXIT(); - switch (err) { - case 0: /* success */ - break; - case 1: /* parse error */ + if (igraph_ncol_yyparse(&context)) { if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); - } else if (context.igraph_errno != IGRAPH_SUCCESS) { - IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read NCOL file", IGRAPH_PARSEERROR); } - break; - case 2: /* out of memory */ - IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - default: /* must never reach here */ - /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison - * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being - * returned in place of a Bison error code. - * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? - */ - IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading NCOL file.", err); } if (predefnames != 0 && igraph_trie_size(&trie) != no_predefined) { - IGRAPH_WARNING("Unknown vertex/vertices found in NCOL file, predefined names extended."); + IGRAPH_WARNING("unknown vertex/vertices found, predefnames extended"); } - /* Prepare attributes, if needed */ - 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 = igraph_i_trie_borrow_keys(&trie); + 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; @@ -210,33 +185,28 @@ igraph_error_t igraph_read_graph_ncol(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - if (igraph_vector_int_empty(&edges)) { + if (igraph_vector_empty(&edges)) { no_of_nodes = 0; } else { - no_of_nodes = igraph_vector_int_max(&edges) + 1; + no_of_nodes = igraph_vector_max(&edges) + 1; } - /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - IGRAPH_FINALLY(igraph_destroy, graph); 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); - IGRAPH_FINALLY_CLEAN(1); } if (pweight) { igraph_vector_ptr_destroy(pweight); - IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&ws); igraph_trie_destroy(&trie); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_ncol_yylex_destroy(context.scanner); - IGRAPH_FINALLY_CLEAN(5); /* +1 for 'graph' */ + IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** @@ -275,43 +245,42 @@ igraph_error_t igraph_read_graph_ncol(igraph_t *graph, FILE *instream, * * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() */ -igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, +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_ID), + 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)) { - IGRAPH_WARNINGF("Names attribute '%s' does not exists.", names); - names = NULL; + 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_WARNINGF("Ignoring names attribute '%s', " - "attribute type is not a string.", names); - names = NULL; + 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)) { - IGRAPH_WARNINGF("Weights attribute '%s' does not exists.", weights); - weights = NULL; + 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_WARNINGF("Ignoring weights attribute '%s', " - "attribute type is not numeric.", weights); - weights = NULL; + IGRAPH_WARNING("ignoring weights attribute, unknown attribute type"); + weights = 0; } } @@ -321,9 +290,11 @@ igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t from, to; int ret; igraph_edge(graph, IGRAPH_EIT_GET(it), &from, &to); - ret = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", from, to); + ret = fprintf(outstream, "%li %li\n", + (long int) from, + (long int) to); if (ret < 0) { - IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -339,13 +310,13 @@ igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0; - const char *str1, *str2; + char *str1, *str2; igraph_edge(graph, edge, &from, &to); - str1 = igraph_strvector_get(&nvec, from); - str2 = igraph_strvector_get(&nvec, 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("Writing NCOL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -363,11 +334,12 @@ igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t from, to; int ret1, ret2, ret3; igraph_edge(graph, edge, &from, &to); - ret1 = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " ", from, to); - ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + 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("Writing NCOL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -390,18 +362,18 @@ igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0, ret2 = 0; - const char *str1, *str2; + char *str1, *str2; igraph_edge(graph, edge, &from, &to); - str1 = igraph_strvector_get(&nvec, from); - str2 = igraph_strvector_get(&nvec, 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("Writing NCOL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } - ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); ret2 = fputc('\n', outstream); if (ret < 0 || ret2 == EOF) { - IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -412,5 +384,5 @@ igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/pajek-header.h b/src/vendor/cigraph/src/io/pajek-header.h index 03b7765420a..92caae45dab 100644 --- a/src/vendor/cigraph/src/io/pajek-header.h +++ b/src/vendor/cigraph/src/io/pajek-header.h @@ -26,35 +26,21 @@ #include "core/trie.h" -/* According to Pajek's author, limits of the Pajek program as of 2022-1-1 are: - * "At the moment regular Pajek has limit one billion vertices, - * PajekXXL two billions, while Pajek 3XL ten billions." - * Hard-coding the limit INT32_MAX is safe when compiling wiht 32-bit integers, - * and likely sufficient for practical applications. - */ -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ -#define IGRAPH_PAJEK_MAX_VERTEX_COUNT (1 << 20) -#else -#define IGRAPH_PAJEK_MAX_VERTEX_COUNT INT32_MAX -#endif - typedef struct { void *scanner; int eof; char errmsg[300]; - igraph_error_t igraph_errno; - igraph_vector_int_t *vector; + igraph_vector_t *vector; igraph_bool_t directed; - igraph_integer_t vcount, vcount2; - igraph_integer_t actfrom; - igraph_integer_t actto; + 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; - igraph_integer_t vertexid; - igraph_integer_t actvertex; - igraph_integer_t actedge; + int vertexid; + int actvertex; + int actedge; } igraph_i_pajek_parsedata_t; diff --git a/src/vendor/cigraph/src/io/pajek-lexer.l b/src/vendor/cigraph/src/io/pajek-lexer.l index 7e533e58b88..4ba29dcf46b 100644 --- a/src/vendor/cigraph/src/io/pajek-lexer.l +++ b/src/vendor/cigraph/src/io/pajek-lexer.l @@ -44,6 +44,7 @@ */ +#include "config.h" #include #include "io/pajek-header.h" @@ -69,72 +70,66 @@ %option reentrant %option bison-bridge %option bison-locations -%option yylineno -%option caseless digit [0-9] -word [^ \t\v\f\r\n\0] -whitespace [ \t\v\f] - -%x netline +word [^ \t\r\n\0] %% -<*>{whitespace}+ { } -%[^\n\r\0]*[\n\r]* { } /* comments */ -\*net { BEGIN(netline); return NETWORKLINE; } -\*network { BEGIN(netline); return NETWORKLINE; } -{whitespace}({word}|{whitespace})* { - return NET_TITLE; } -\*vertices { return VERTICESLINE; } -\*arcs { return ARCSLINE; } -\*edges { return EDGESLINE; } -\*arcslist { return ARCSLISTLINE; } -\*edgeslist { return EDGESLISTLINE; } -\*matrix { return MATRIXLINE; } -<*>[\n\r]+ { BEGIN(INITIAL); yyextra->mode=0; return NEWLINE; } /* skip over multiple newlines */ -\"[^\"\0]*\" { return QSTR; } -\([^\)\0]*\) { return PSTR; } -(\+|\-)?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { +[ \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; } -x_fact/[ \t\n\r] { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } -y_fact/[ \t\n\r] { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } -ic/[ \t\n\r] { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } -bc/[ \t\n\r] { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } -bw/[ \t\n\r] { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } -phi/[ \t\n\r] { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } -r/[ \t\n\r] { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } -q/[ \t\n\r] { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } -font/[ \t\n\r] { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } -url/[ \t\n\r] { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } - -c/[ \t\n\r] { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } -p/[ \t\n\r] { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } -s/[ \t\n\r] { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } -a/[ \t\n\r] { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } -w/[ \t\n\r] { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } -h1/[ \t\n\r] { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } -h2/[ \t\n\r] { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } -a1/[ \t\n\r] { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } -a2/[ \t\n\r] { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } -k1/[ \t\n\r] { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } -k2/[ \t\n\r] { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } -ap/[ \t\n\r] { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } -l/[ \t\n\r] { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } -lp/[ \t\n\r] { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } - -lphi/[ \t\n\r] { if (yyextra->mode==1) { return VP_LPHI; } else +[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; } } -lc/[ \t\n\r] { if (yyextra->mode==1) { return VP_LC; } else +[Ll][Cc]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LC; } else if (yyextra->mode==2) { return EP_LC; } else { return ALNUM; } } -lr/[ \t\n\r] { if (yyextra->mode==1) { return VP_LR; } else +[Ll][Rr]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LR; } else if (yyextra->mode==2) { return EP_LR; } else { return ALNUM; } } -la/[ \t\n\r] { if (yyextra->mode==1) { return VP_LA; } else +[Ll][Aa]/[ \t\n\r] { if (yyextra->mode==1) { return VP_LA; } else if (yyextra->mode==2) { return EP_LA; } else { return ALNUM; } } -size/[ \t\n\r] { if (yyextra->mode==1) { return VP_SIZE; } else +[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; } } -fos/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else +[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; } @@ -147,6 +142,6 @@ fos/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else } } -<*>. { return ERROR; } +. { return ERROR; } %% diff --git a/src/vendor/cigraph/src/io/pajek-parser.y b/src/vendor/cigraph/src/io/pajek-parser.y index 78201314145..7069a77274e 100644 --- a/src/vendor/cigraph/src/io/pajek-parser.y +++ b/src/vendor/cigraph/src/io/pajek-parser.y @@ -44,55 +44,59 @@ */ -#include "igraph_attributes.h" -#include "igraph_error.h" -#include "igraph_memory.h" +#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 "io/pajek-header.h" #include "io/parsers/pajek-parser.h" /* it must come first because of YYSTYPE */ #include "io/parsers/pajek-lexer.h" -#include "io/parse_utils.h" -#include "internal/hacks.h" /* strdup */ - -#include -#include -#include +#include "internal/hacks.h" int igraph_pajek_yyerror(YYLTYPE* locp, igraph_i_pajek_parsedata_t *context, const char *s); -static igraph_error_t igraph_i_pajek_add_string_vertex_attribute(const char *name, +int igraph_i_pajek_add_string_vertex_attribute(const char *name, const char *value, - size_t len, + int len, igraph_i_pajek_parsedata_t *context); -static igraph_error_t igraph_i_pajek_add_string_edge_attribute(const char *name, +int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, - size_t len, + int len, igraph_i_pajek_parsedata_t *context); -static igraph_error_t igraph_i_pajek_add_numeric_vertex_attribute(const char *name, +int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); -static igraph_error_t igraph_i_pajek_add_numeric_edge_attribute(const char *name, +int igraph_i_pajek_add_numeric_edge_attribute(const char *name, igraph_real_t value, igraph_i_pajek_parsedata_t *context); -static igraph_error_t igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - igraph_integer_t count, + long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number); -static igraph_error_t igraph_i_pajek_add_string_attribute(igraph_trie_t *names, +int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - igraph_integer_t count, + long int count, const char *attrname, igraph_integer_t vid, - const char *str, - igraph_integer_t str_len); + 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); -static igraph_error_t igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); -static igraph_error_t 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 @@ -109,11 +113,11 @@ static igraph_error_t igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t %lex-param { void *scanner } %union { - igraph_integer_t intnum; - igraph_real_t realnum; + long int intnum; + double realnum; struct { char *str; - size_t len; + int len; } string; } @@ -128,20 +132,19 @@ static igraph_error_t igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t %type epwordpar; %type vertex; -%token NEWLINE "end of line" -%token NUM "number" +%token NEWLINE +%token NUM %token ALNUM %token QSTR %token PSTR -%token NETWORKLINE "*network line" -%token NET_TITLE -%token VERTICESLINE "*vertices line" -%token ARCSLINE "*arcs line" -%token EDGESLINE "*edges line" -%token ARCSLISTLINE "*arcslist line" -%token EDGESLISTLINE "*edgeslist line" -%token MATRIXLINE "*matrix line" -%token END 0 "end of file" /* friendly name for $end */ +%token NETWORKLINE +%token VERTICESLINE +%token ARCSLINE +%token EDGESLINE +%token ARCSLISTLINE +%token EDGESLISTLINE +%token MATRIXLINE +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %token VP_X_FACT @@ -188,73 +191,47 @@ input: nethead vertices edgeblock { if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } }; -nethead: /* empty */ | NETWORKLINE NEWLINE | NETWORKLINE NET_TITLE NEWLINE ; +nethead: /* empty */ | NETWORKLINE words NEWLINE; vertices: verticeshead NEWLINE vertdefs; verticeshead: VERTICESLINE longint { context->vcount=$2; context->vcount2=0; - if (context->vcount < 0) { - IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); - } - if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { - IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); - } } | VERTICESLINE longint longint { context->vcount=$2; context->vcount2=$3; - if (context->vcount < 0) { - IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); - } - if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { - IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); - } - if (context->vcount2 < 0) { - IGRAPH_YY_ERRORF("Invalid two-mode vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); - } - if (context->vcount2 > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { - IGRAPH_YY_ERRORF("2-mode vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); - } - IGRAPH_YY_CHECK(igraph_i_pajek_add_bipartite_type(context)); + igraph_i_pajek_add_bipartite_type(context); }; vertdefs: /* empty */ | vertdefs vertexline; vertexline: NEWLINE | vertex NEWLINE | - vertex { - context->actvertex=$1; - if (context->actvertex < 1 || context->actvertex > context->vcount) { - IGRAPH_YY_ERRORF( - "Invalid vertex id (%" IGRAPH_PRId ") in Pajek file. " - "The number of vertices is %" IGRAPH_PRId ".", - IGRAPH_EINVAL, context->actvertex, context->vcount); - } - } vertexid vertexcoords shape params NEWLINE { } + vertex { context->actvertex=$1; } vertexid vertexcoords shape params NEWLINE { } ; vertex: longint { $$=$1; context->mode=1; }; vertexid: word { - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("name", $1.str, $1.len, context)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context); + igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context); } | number number number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("z", $3, context)); + 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_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context)); + igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context); }; params: /* empty */ | params param; @@ -262,76 +239,76 @@ params: /* empty */ | params param; param: vpword | VP_X_FACT number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context); } | VP_Y_FACT number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context); } | VP_IC number number number { /* RGB color */ - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("color-blue", $4, context)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framecolor-blue", $4, context)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context); } | VP_LPHI number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context); } | VP_BW number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context); } | VP_FOS number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context); } | VP_PHI number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context); } | VP_R number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context); } | VP_Q number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context); } | VP_LA number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context); } | VP_SIZE number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context)); + igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context); } ; vpword: VP_FONT { context->mode=3; } vpwordpar { context->mode=1; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context)); + igraph_i_pajek_add_string_vertex_attribute("font", $3.str, $3.len, context); } | VP_URL { context->mode=3; } vpwordpar { context->mode=1; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context)); + igraph_i_pajek_add_string_vertex_attribute("url", $3.str, $3.len, context); } | VP_IC { context->mode=3; } vpwordpar { context->mode=1; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context)); + igraph_i_pajek_add_string_vertex_attribute("color", $3.str, $3.len, context); } | VP_BC { context->mode=3; } vpwordpar { context->mode=1; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("framecolor", - $3.str, $3.len, context)); + igraph_i_pajek_add_string_vertex_attribute("framecolor", + $3.str, $3.len, context); } | VP_LC { context->mode=3; } vpwordpar { context->mode=1; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("labelcolor", - $3.str, $3.len, context)); + igraph_i_pajek_add_string_vertex_attribute("labelcolor", + $3.str, $3.len, context); } ; @@ -347,14 +324,8 @@ arcsdefs: /* empty */ | arcsdefs arcsline; arcsline: NEWLINE | arcfrom arcto { context->actedge++; context->mode=2; } weight edgeparams NEWLINE { - if ($1 < 1) { - IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $1); - } - if ($2 < 1) { - IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $2); - } - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2-1)); } + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } ; arcfrom: longint; @@ -369,14 +340,8 @@ edgesdefs: /* empty */ | edgesdefs edgesline; edgesline: NEWLINE | edgefrom edgeto { context->actedge++; context->mode=2; } weight edgeparams NEWLINE { - if ($1 < 1) { - IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $1); - } - if ($2 < 1) { - IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $2); - } - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2-1)); } + igraph_vector_push_back(context->vector, $1-1); + igraph_vector_push_back(context->vector, $2-1); } ; edgefrom: longint; @@ -384,7 +349,7 @@ edgefrom: longint; edgeto: longint; weight: /* empty */ | number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); + igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); }; edgeparams: /* empty */ | edgeparams edgeparam; @@ -392,76 +357,76 @@ edgeparams: /* empty */ | edgeparams edgeparam; edgeparam: epword | EP_C number number number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context)); - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); } | EP_W number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context); } | EP_H1 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context); } | EP_H2 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context); } | EP_A1 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context); } | EP_A2 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context); } | EP_K1 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context); } | EP_K2 number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context); } | EP_AP number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context); } | EP_LP number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context); } | EP_LR number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context); } | EP_LPHI number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context); } | EP_LA number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context); } | EP_SIZE number { /* what is this??? */ - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); } | EP_FOS number { - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context)); + igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context); } ; epword: EP_A { context->mode=4; } epwordpar { context->mode=2; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context)); + igraph_i_pajek_add_string_edge_attribute("arrowtype", $3.str, $3.len, context); } | EP_P { context->mode=4; } epwordpar { context->mode=2; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context)); + igraph_i_pajek_add_string_edge_attribute("linepattern", $3.str, $3.len, context); } | EP_L { context->mode=4; } epwordpar { context->mode=2; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context)); + igraph_i_pajek_add_string_edge_attribute("label", $3.str, $3.len, context); } | EP_LC { context->mode=4; } epwordpar { context->mode=2; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context)); + igraph_i_pajek_add_string_edge_attribute("labelcolor", $3.str, $3.len, context); } | EP_C { context->mode=4; } epwordpar { context->mode=2; - IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context)); + igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context); } ; @@ -478,8 +443,8 @@ arctolist: /* empty */ | arctolist arclistto; arclistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; arclistto: longint { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, labs($1)-1)); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); }; edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; @@ -493,8 +458,8 @@ edgetolist: /* empty */ | edgetolist edgelistto; edgelistfrom: longint { context->mode=0; context->actfrom=labs($1)-1; }; edgelistto: longint { - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, labs($1)-1)); + igraph_vector_push_back(context->vector, context->actfrom); + igraph_vector_push_back(context->vector, labs($1)-1); }; /* -----------------------------------------------------*/ @@ -516,15 +481,15 @@ adjmatrixentry: number { if ($1 != 0) { if (context->vcount2==0) { context->actedge++; - IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actto)); + 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_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); - IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, - context->vcount2+context->actto)); + 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++; @@ -532,21 +497,13 @@ adjmatrixentry: number { /* -----------------------------------------------------*/ -longint: NUM { - igraph_integer_t val; - IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_pajek_yyget_text(scanner), - igraph_pajek_yyget_leng(scanner), - &val)); - $$=val; -}; +longint: NUM { $$=igraph_pajek_get_number(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner)); }; -number: NUM { - igraph_real_t val; - IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_pajek_yyget_text(scanner), - igraph_pajek_yyget_leng(scanner), - &val)); - $$=val; -}; +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); } @@ -566,197 +523,208 @@ int igraph_pajek_yyerror(YYLTYPE* locp, 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 */ -static igraph_error_t igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, +int igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - igraph_integer_t count, + long int count, const char *attrname, igraph_integer_t vid, igraph_real_t number) { - igraph_integer_t attrsize = igraph_trie_size(names); - igraph_integer_t id; + long int attrsize=igraph_trie_size(names); + long int id; igraph_vector_t *na; igraph_attribute_record_t *rec; - IGRAPH_CHECK(igraph_trie_get(names, attrname, &id)); + igraph_trie_get(names, attrname, &id); if (id == attrsize) { /* add a new attribute */ rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); - if (! rec) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, rec); na=IGRAPH_CALLOC(1, igraph_vector_t); - if (! na) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, na); - IGRAPH_VECTOR_INIT_FINALLY(na, count); + igraph_vector_init(na, count); rec->name=strdup(attrname); - if (! rec->name) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, (void *) rec->name); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; - IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); - IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ + 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) { - igraph_integer_t origsize=igraph_vector_size(na); - IGRAPH_CHECK(igraph_vector_resize(na, vid+1)); + long int origsize=igraph_vector_size(na); + IGRAPH_CHECK(igraph_vector_resize(na, (long int)vid+1)); for (;origsizename=strdup(attrname); - if (! rec->name) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, (char *) rec->name); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->value=na; - IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); - IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ + 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); - return igraph_i_pajek_add_string_attribute(context->vertex_attribute_names, - context->vertex_attributes, - context->vcount, - name, context->actvertex-1, - value, len); + IGRAPH_FREE(tmp); + IGRAPH_FINALLY_CLEAN(1); + + return ret; } -static igraph_error_t igraph_i_pajek_add_string_edge_attribute(const char *name, +int igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, - size_t len, + 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 igraph_i_pajek_add_string_attribute(context->edge_attribute_names, - context->edge_attributes, - context->actedge, - name, context->actedge-1, - value, len); + return ret; } -static igraph_error_t igraph_i_pajek_add_numeric_vertex_attribute(const char *name, +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); + return + igraph_i_pajek_add_numeric_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value); } -static igraph_error_t igraph_i_pajek_add_numeric_edge_attribute(const char *name, +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); + return + igraph_i_pajek_add_numeric_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value); } -static igraph_error_t igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context) { +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; - igraph_integer_t i, n=context->vcount, n1=context->vcount2; - igraph_integer_t attrid, attrsize = igraph_trie_size(names); + int i, n=context->vcount, n1=context->vcount2; + long int attrid, attrsize=igraph_trie_size(names); igraph_attribute_record_t *rec; - igraph_vector_bool_t *na; + igraph_vector_t *na; if (n1 > n) { - IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file.", + IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", IGRAPH_PARSEERROR); } - IGRAPH_CHECK(igraph_trie_get(names, attrname, &attrid)); - /* It should not be possible for the "type" attribute to be already - * present at this point. */ - IGRAPH_ASSERT(attrid == attrsize); + 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); - if (! rec) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, rec); - na=IGRAPH_CALLOC(1, igraph_vector_bool_t); - if (! na) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, na); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(na, n); + na=IGRAPH_CALLOC(1, igraph_vector_t); + igraph_vector_init(na, n); rec->name=strdup(attrname); - if (! rec->name) { - IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, (char *) rec->name); - rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; + rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; - IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); - IGRAPH_FINALLY_CLEAN(4); /* ownership of 'rec' transferred to 'attrs' */ + igraph_vector_ptr_push_back(attrs, rec); for (i=0; ivector; - igraph_integer_t i, n1=context->vcount2; - igraph_integer_t ne=igraph_vector_int_size(edges); +int igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context) { + const igraph_vector_t *edges=context->vector; + 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."); + IGRAPH_WARNING("Invalid edge in bipartite graph"); } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/pajek.c b/src/vendor/cigraph/src/io/pajek.c index 660676f614d..9667a63ba83 100644 --- a/src/vendor/cigraph/src/io/pajek.c +++ b/src/vendor/cigraph/src/io/pajek.c @@ -29,8 +29,7 @@ #include "graph/attributes.h" -#include "io/pajek-header.h" -#include "io/parsers/pajek-parser.h" +#include "pajek-header.h" #include #include @@ -46,35 +45,9 @@ void igraph_pajek_yylex_destroy_wrapper (void *scanner ) { (void) igraph_pajek_yylex_destroy(scanner); } -void igraph_i_pajek_destroy_attr_vector(igraph_vector_ptr_t *attrs) { - const igraph_integer_t attr_count = igraph_vector_ptr_size(attrs); - for (igraph_integer_t i = 0; i < attr_count; i++) { - igraph_attribute_record_t *rec = VECTOR(*attrs)[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_BOOLEAN) { - igraph_vector_bool_t *vec = (igraph_vector_bool_t*) rec->value; - igraph_vector_bool_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); - } else { - /* Must never reach here */ - IGRAPH_FATAL("Unknown attribute type encountered."); - } - IGRAPH_FREE(rec->name); - IGRAPH_FREE(rec); - } - igraph_vector_ptr_destroy(attrs); -} - /** * \function igraph_read_graph_pajek - * \brief Reads a file in Pajek format. + * \brief Reads a file in Pajek format * * \param graph Pointer to an uninitialized graph object. * \param file An already opened file handler. @@ -138,7 +111,7 @@ void igraph_i_pajek_destroy_attr_vector(igraph_vector_ptr_t *attrs) { * * * 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 + * 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 @@ -162,27 +135,23 @@ void igraph_i_pajek_destroy_attr_vector(igraph_vector_ptr_t *attrs) { * \example examples/simple/foreign.c */ -igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { +int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { - igraph_vector_int_t edges; + igraph_vector_t edges; igraph_trie_t vattrnames; igraph_vector_ptr_t vattrs; igraph_trie_t eattrnames; igraph_vector_ptr_t eattrs; - igraph_integer_t i, j; + long int i, j; igraph_i_pajek_parsedata_t context; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1); - IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); - IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &vattrs); - + IGRAPH_VECTOR_PTR_INIT_FINALLY(&vattrs, 0); IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1); - IGRAPH_CHECK(igraph_vector_ptr_init(&eattrs, 0)); - IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &eattrs); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&eattrs, 0); - context.directed = false; /* assume undirected until an element implying directedness is encountered */ context.vector = &edges; context.mode = 0; context.vcount = -1; @@ -193,87 +162,90 @@ igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { context.edge_attributes = &eattrs; context.actedge = 0; context.eof = 0; - context.errmsg[0] = '\0'; - context.igraph_errno = IGRAPH_SUCCESS; igraph_pajek_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_pajek_yylex_destroy_wrapper, context.scanner); igraph_pajek_yyset_in(instream, context.scanner); - /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ - IGRAPH_FINALLY_ENTER(); - int err = igraph_pajek_yyparse(&context); - IGRAPH_FINALLY_EXIT(); - switch (err) { - case 0: /* success */ - break; - case 1: /* parse error */ + if (igraph_pajek_yyparse(&context)) { if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); - } else if (context.igraph_errno != IGRAPH_SUCCESS) { - IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read Pajek file", IGRAPH_PARSEERROR); } - break; - case 2: /* out of memory */ - IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - default: /* must never reach here */ - /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison - * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being - * returned in place of a Bison error code. - * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? - */ - IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading Pajek file.", err); } - /* Prepare attributes */ - const igraph_integer_t eattr_count = igraph_vector_ptr_size(&eattrs); - for (i = 0; i < eattr_count; i++) { + 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; - igraph_integer_t origsize = igraph_vector_size(vec); - IGRAPH_CHECK(igraph_vector_resize(vec, context.actedge)); + 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_BOOLEAN) { - /* Boolean attributes are not currently added by the parser. - * This section is here for future-proofing. */ - igraph_vector_bool_t *vec = (igraph_vector_bool_t*)rec->value; - igraph_integer_t origsize = igraph_vector_bool_size(vec); - IGRAPH_CHECK(igraph_vector_bool_resize(vec, context.actedge)); - for (j = origsize; j < context.actedge; j++) { - VECTOR(*vec)[j] = 0; - } } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; - /* strvector_resize() adds empty strings */ - IGRAPH_CHECK(igraph_strvector_resize(strvec, context.actedge)); - } else { - /* Must never reach here */ - IGRAPH_FATAL("Unknown attribute type encountered."); + 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, ""); + } } } - /* Create graph */ 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)); - igraph_vector_int_destroy(&edges); - igraph_i_pajek_destroy_attr_vector(&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_i_pajek_destroy_attr_vector(&vattrs); + igraph_vector_ptr_destroy(&vattrs); igraph_trie_destroy(&vattrnames); igraph_pajek_yylex_destroy(context.scanner); - IGRAPH_FINALLY_CLEAN(7); /* +1 for 'graph' */ - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(7); + return 0; } /* Order matters here! */ @@ -334,13 +306,12 @@ igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { #define E_COLOR 22 #define E_LAST 23 -static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { - igraph_integer_t destlen = 0; - igraph_bool_t need_escape = false; +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 */ - const char *s; - char *d; + char *s, *d; for (s = src; *s; s++, destlen++) { if (*s == '\\') { need_escape = 1; @@ -362,7 +333,7 @@ static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { */ *dest = IGRAPH_CALLOC(destlen + 3, char); if (!*dest) { - IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); } d = *dest; @@ -374,7 +345,7 @@ static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { *dest = IGRAPH_CALLOC(destlen + 3, char); if (!*dest) { - IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); } d = *dest; @@ -447,12 +418,12 @@ static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { * \example examples/simple/igraph_write_graph_pajek.c */ -igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j; +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 = false; + igraph_bool_t write_vertex_attrs = 0; /* Same order as the #define's */ const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact", @@ -509,33 +480,35 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) igraph_vector_t numv; igraph_strvector_t strv; - igraph_vector_int_t ex_numa; - igraph_vector_int_t ex_stra; - igraph_vector_int_t vx_numa; - igraph_vector_int_t vx_stra; + igraph_vector_t ex_numa; + igraph_vector_t ex_stra; + igraph_vector_t vx_numa; + igraph_vector_t vx_stra; - const char *s; - char *escaped; + char *s, *escaped; - igraph_bool_t bipartite = false; + igraph_bool_t bipartite = 0; igraph_vector_int_t bip_index, bip_index2; igraph_vector_bool_t bvec; - igraph_integer_t notop = 0, nobottom = 0; + long int notop = 0, nobottom = 0; + + IGRAPH_UNUSED(notop); IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_numa, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_stra, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vx_numa, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vx_stra, 0); + 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_CHECK(igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, "type")); + igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, + "type"); if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - igraph_integer_t bptr = 0, tptr = 0; + 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 */ @@ -547,22 +520,22 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) 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(i), &bvec)); + "type", igraph_vss_1((igraph_integer_t) i), &bvec)); if (VECTOR(bvec)[0]) { notop++; } else { nobottom++; } } - for (i = 0, bptr = 0, tptr = nobottom; i < no_of_nodes; i++) { + 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(i), &bvec)); + "type", igraph_vss_1((igraph_integer_t) i), &bvec)); if (VECTOR(bvec)[0]) { - VECTOR(bip_index)[tptr] = i; + VECTOR(bip_index)[tptr] = (int) i; VECTOR(bip_index2)[i] = tptr; tptr++; } else { - VECTOR(bip_index)[bptr] = i; + VECTOR(bip_index)[bptr] = (int) i; VECTOR(bip_index2)[i] = bptr; bptr++; } @@ -574,12 +547,12 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) /* Write header */ if (bipartite) { - if (fprintf(outstream, "*Vertices %" IGRAPH_PRId " %" IGRAPH_PRId "%s", no_of_nodes, nobottom, + 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 %" IGRAPH_PRId "%s", no_of_nodes, newline) < 0) { + if (fprintf(outstream, "*Vertices %li%s", no_of_nodes, newline) < 0) { IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE); } } @@ -587,31 +560,34 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) /* 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_CHECK(igraph_i_attribute_gettype( - graph, &vtypes[i], IGRAPH_ATTRIBUTE_VERTEX, vnames[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 < (igraph_integer_t) (sizeof(vnumnames) / sizeof(vnumnames[0])); i++) { + 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_CHECK(igraph_i_attribute_gettype( - graph, &type, IGRAPH_ATTRIBUTE_VERTEX, vnumnames[i])); + 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_int_push_back(&vx_numa, i)); + IGRAPH_CHECK(igraph_vector_push_back(&vx_numa, i)); } } } - for (i = 0; i < (igraph_integer_t) (sizeof(vstrnames) / sizeof(vstrnames[0])); 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_CHECK(igraph_i_attribute_gettype( - graph, &type, IGRAPH_ATTRIBUTE_VERTEX, vstrnames[i])); + 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_int_push_back(&vx_stra, i)); + IGRAPH_CHECK(igraph_vector_push_back(&vx_stra, i)); } } } @@ -619,41 +595,41 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) /* Write vertices */ if (write_vertex_attrs) { for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t id = bipartite ? VECTOR(bip_index)[i] : i; + long int id = bipartite ? VECTOR(bip_index)[i] : i; - /* vertex ID */ - fprintf(outstream, "%" IGRAPH_PRId, i + 1); + /* vertex id */ + fprintf(outstream, "%li", i + 1); if (vtypes[V_ID] == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( - graph, vnames[V_ID], igraph_vss_1(id), &numv)); + 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_CHECK(igraph_i_attribute_get_string_vertex_attr( - graph, vnames[V_ID], igraph_vss_1(id), &strv)); - s = igraph_strvector_get(&strv, 0); + 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, " \"%" IGRAPH_PRId "\"", id + 1); + fprintf(outstream, " \"%li\"", id + 1); } /* coordinates */ if (vtypes[V_X] == IGRAPH_ATTRIBUTE_NUMERIC && vtypes[V_Y] == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( - graph, vnames[V_X], igraph_vss_1(id), &numv)); + 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_CHECK(igraph_i_attribute_get_numeric_vertex_attr( - graph, vnames[V_Y], igraph_vss_1(id), &numv)); + 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_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Z], - igraph_vss_1(id), &numv)); + 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]); } @@ -661,29 +637,29 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) /* shape */ if (vtypes[V_SHAPE] == IGRAPH_ATTRIBUTE_STRING) { - IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( - graph, vnames[V_SHAPE], igraph_vss_1(id), &strv)); - s = igraph_strvector_get(&strv, 0); + 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_int_size(&vx_numa); j++) { - igraph_integer_t idx = VECTOR(vx_numa)[j]; - IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( - graph, vnumnames[idx], igraph_vss_1(id), &numv)); + 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_int_size(&vx_stra); j++) { - igraph_integer_t idx = VECTOR(vx_stra)[j]; - IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( - graph, vstrnames[idx], igraph_vss_1(id), &strv)); - s = igraph_strvector_get(&strv, 0); + 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); @@ -708,67 +684,70 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) /* Check edge attributes */ for (i = 0; i < E_LAST; i++) { - if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, enames[i])) { - IGRAPH_CHECK(igraph_i_attribute_gettype( - graph, &etypes[i], IGRAPH_ATTRIBUTE_EDGE, enames[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 < (igraph_integer_t) (sizeof(enumnames) / sizeof(enumnames[0])); i++) { + 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_CHECK(igraph_i_attribute_gettype( - graph, &type, IGRAPH_ATTRIBUTE_EDGE, enumnames[i])); + 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_int_push_back(&ex_numa, i)); + IGRAPH_CHECK(igraph_vector_push_back(&ex_numa, i)); } } } - for (i = 0; i < (igraph_integer_t) (sizeof(estrnames) / sizeof(estrnames[0])); 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_CHECK(igraph_i_attribute_gettype( - graph, &type, IGRAPH_ATTRIBUTE_EDGE, estrnames[i])); + 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_int_push_back(&ex_stra, i)); + IGRAPH_CHECK(igraph_vector_push_back(&ex_stra, i)); } } } for (i = 0; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit), i++) { - igraph_integer_t edge = IGRAPH_EIT_GET(eit); + long int edge = IGRAPH_EIT_GET(eit); igraph_integer_t from, to; - igraph_edge(graph, edge, &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, "%" IGRAPH_PRId " %" IGRAPH_PRId , from + 1, to + 1); + fprintf(outstream, "%li %li", (long int) from + 1, (long int) to + 1); /* Weights */ if (etypes[E_WEIGHT] == IGRAPH_ATTRIBUTE_NUMERIC) { - IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( - graph, enames[E_WEIGHT], igraph_ess_1(edge), &numv)); + 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_int_size(&ex_numa); j++) { - igraph_integer_t idx = VECTOR(ex_numa)[j]; - IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( - graph, enumnames[idx], igraph_ess_1(edge), &numv)); + 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_int_size(&ex_stra); j++) { - igraph_integer_t idx = VECTOR(ex_stra)[j]; - IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr( - graph, estrnames[idx], igraph_ess_1(edge), &strv)); - s = igraph_strvector_get(&strv, 0); + 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); @@ -788,12 +767,12 @@ igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_int_destroy(&ex_numa); - igraph_vector_int_destroy(&ex_stra); - igraph_vector_int_destroy(&vx_numa); - igraph_vector_int_destroy(&vx_stra); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/io/parse_utils.c b/src/vendor/cigraph/src/io/parse_utils.c deleted file mode 100644 index 8fa17c84c41..00000000000 --- a/src/vendor/cigraph/src/io/parse_utils.c +++ /dev/null @@ -1,387 +0,0 @@ - -#include "parse_utils.h" - -#include "igraph_foreign.h" -#include "igraph_memory.h" - -#include "config.h" - -#include -#include -#include -#include - -#if defined(HAVE_XLOCALE) -/* On some systems, xlocale.h exists, but uselocale() is still in locale.h. - * Thus we include both. */ -#include -#include -#else -#include -#endif - -/* Trims whitespace from the beginning and the end of a string with a specified length. - * A pointer to the first character of the result substring, as well as its length, are returned. - * - * If you have a null-terminated string, call this function as - * - * igraph_i_trim_whitespace(str, strlen(str), &res, &len); - * - * This does not carry a performance penalty, as the end of the string would need to be - * determinted anyway. - */ -void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len) { - const char *beg = str, *end = str + str_len; - while (beg < end && isspace(beg[0]) ) beg++; - while (end > beg && isspace(end[-1])) end--; - *res = beg; - *res_len = end - beg; -} - - -/* TODO: Support for reporting line number where parse error occurred. */ - -/* Converts a string to an integer. Throws an error if the result is not representable. - * - * The input is a not-necesarily-null-terminated string that must contain only the number. - * Any additional characters at the end of the string, such as whitespace, will trigger - * a parsing error. - * - * An error is returned if the input is an empty string. - */ -igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value) { - char buffer[128]; - char *tmp, *end; - char last_char; - igraph_bool_t out_of_range, dynamic_alloc; - long long val; - - if (length == 0) { - IGRAPH_ERROR("Cannot parse integer from empty string.", IGRAPH_PARSEERROR); - } - - dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); - - if (dynamic_alloc) { - tmp = IGRAPH_CALLOC(length+1, char); - if (tmp == NULL) { - IGRAPH_ERROR("Failed to parse integer.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - } else { - tmp = buffer; - } - - strncpy(tmp, str, length); - tmp[length]='\0'; - - /* To avoid having to choose the appropriate strto?() function based on - * the definition of igraph_integer_t, we first use a long long variable - * which should be at least as large as igraph_integer_t on any platform. */ - errno = 0; - val = strtoll(tmp, &end, 10); - out_of_range = errno == ERANGE; - *value = (igraph_integer_t) val; - last_char = *end; - if (*value != val) { - out_of_range = 1; - } - - /* Free memory before raising any errors. */ - if (dynamic_alloc) { - IGRAPH_FREE(tmp); - } - - if (out_of_range) { - IGRAPH_ERROR("Failed to parse integer.", val > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); - } - - /* Did we parse to the end of the string? */ - if (last_char) { - IGRAPH_ERRORF("Unexpected character '%c' while parsing integer.", IGRAPH_PARSEERROR, last_char); - } - - return IGRAPH_SUCCESS; -} - - -/* Converts a string to a real number. Throws an error if the result is not representable. - * - * The input is a not-necesarily-null-terminated string that must contain only the number. - * Any additional characters at the end of the string, such as whitespace, will trigger - * a parsing error. - * - * NaN and Inf are supported. An error is returned if the input is an empty string. - */ -igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value) { - char buffer[128]; - char *tmp, *end; - char last_char; - igraph_bool_t out_of_range, dynamic_alloc; - - if (length == 0) { - IGRAPH_ERROR("Cannot parse real number from empty string.", IGRAPH_PARSEERROR); - } - - dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); - - if (dynamic_alloc) { - tmp = IGRAPH_CALLOC(length+1, char); - if (tmp == NULL) { - IGRAPH_ERROR("Failed to parse real number.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } - } else { - tmp = buffer; - } - - strncpy(tmp, str, length); - tmp[length]='\0'; - - errno = 0; - *value = strtod(tmp, &end); - out_of_range = errno == ERANGE; /* This does not trigger when reading +-Inf. */ - last_char = *end; - - /* Free memory before raising any errors. */ - if (dynamic_alloc) { - IGRAPH_FREE(tmp); - } - - if (out_of_range) { - IGRAPH_ERROR("Failed to parse real number.", *value > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); - } - - /* Did we parse to the end of the string? */ - if (last_char) { - IGRAPH_ERRORF("Unexpected character '%c' while parsing real number.", IGRAPH_PARSEERROR, last_char); - } - - return IGRAPH_SUCCESS; -} - - -/* Skips all whitespace in a file. */ -igraph_error_t igraph_i_fskip_whitespace(FILE *file) { - int ch; - - do { - ch = fgetc(file); - } while (isspace(ch)); - if (ferror(file)) { - IGRAPH_ERROR("Error reading file.", IGRAPH_EFILE); - } - ungetc(ch, file); - - return IGRAPH_SUCCESS; -} - - -/* Reads an integer from a file. Throws an error if the result is not representable. - * - * Any initial whitespace is skipped. If no number is found, an error is raised. - * - * This function assumes that the number is followed by whitespace or the end of the file. - * If this is not the case, an error will be raised. - */ -igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value) { - /* The value requiring the most characters on 64-bit is -2^63, i.e. "-9223372036854775808". - * This is 20 characters long, plus one for the null terminator, requiring a buffer of - * at least 21 characters. We use a slightly larger buffer to allow for leading zeros and - * clearer error messages. - * - * Note: The string held in this buffer is not null-terminated. - */ - char buf[32]; - int ch; - - IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); - - int i = 0; /* must be 'int' due to use in printf format specifier */ - while (1) { - ch = fgetc(file); - if (ch == EOF) break; - if (isspace(ch)) { - ungetc(ch, file); - break; - } - if (i == sizeof(buf)) { - /* Reached the end of the buffer. */ - IGRAPH_ERRORF("'%.*s' is not a valid integer value.", IGRAPH_PARSEERROR, i, buf); - } - buf[i++] = ch; - } - if (ferror(file)) { - IGRAPH_ERROR("Error while reading integer.", IGRAPH_EFILE); - } - - if (i == 0) { - IGRAPH_ERROR("Integer expected, reached end of file instead.", IGRAPH_PARSEERROR); - } - - IGRAPH_CHECK(igraph_i_parse_integer(buf, i, value)); - - return IGRAPH_SUCCESS; -} - - -/* Reads a real number from a file. Throws an error if the result is not representable. - * - * Any initial whitespace is skipped. If no number is found, an error is raised. - * - * This function assumes that the number is followed by whitespace or the end of the file. - * If this is not the case, an error will be raised. - */ -igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value) { - /* The value requiring the most characters with an IEEE-754 double is the smallest - * representable number, with signs added, "-2.2250738585072014e-308" - * - * This is 24 characters long, plus one for the null terminator, requiring a buffer of - * at least 25 characters. This is 17 mantissa digits for lossless representation, - * 3 exponent digits, "e", and up to two minus signs. We use a larger buffer as some - * files may have more digits specified than necessary for exact representation. - * - * Note: The string held in this buffer is not null-terminated. - */ - char buf[64]; - int ch; - - IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); - - int i = 0; /* must be 'int' due to use in printf format specifier */ - while (1) { - ch = fgetc(file); - if (ch == EOF) break; - if (isspace(ch)) { - ungetc(ch, file); - break; - } - if (i == sizeof(buf)) { - /* Reached the end of the buffer. */ - IGRAPH_ERRORF("'%.*s' is not a valid real value.", IGRAPH_PARSEERROR, i, buf); - } - buf[i++] = ch; - } - if (ferror(file)) { - IGRAPH_ERROR("Error while reading real number.", IGRAPH_EFILE); - } - - if (i == 0) { - IGRAPH_ERROR("Real number expected, reached end of file instead.", IGRAPH_PARSEERROR); - } - - IGRAPH_CHECK(igraph_i_parse_real(buf, i, value)); - - return IGRAPH_SUCCESS; -} - - -/* igraph_i_safelocale() and igraph_i_unsafelocale() will set the numeric locale to "C" - * and re-set it to its original value. This is to ensure that parsing and writing - * numbers uses a decimal point instead of a comma. - * - * These functions attempt to set the locale only for the current thread on a best-effort - * basis. On some platforms this is not possible, so the global locale will be changes. - * This is not safe to do in multi-threaded programs (not even if igraph runs only in - * a single thread). - */ - -struct igraph_safelocale_s { -#ifdef HAVE_USELOCALE - locale_t original_locale; - locale_t c_locale; -#else - char *original_locale; -# ifdef HAVE__CONFIGTHREADLOCALE - int per_thread_locale; -# endif -#endif -}; - -/** - * \function igraph_enter_safelocale - * \brief Temporarily set the C locale. - * - * \experimental - * - * igraph's foreign format readers and writers require a locale that uses a - * decimal point instead of a decimal comma. This is a convenience function - * that temporarily sets the C locale so that readers and writer would work - * correctly. It \em must be paired with a call to \ref igraph_exit_safelocale(), - * otherwise a memory leak will occur. - * - * - * This function tries to set the locale for the current thread only on a - * best-effort basis. Restricting the locale change to a single thread is not - * supported on all platforms. In these cases, this function falls back to using - * the standard setlocale() function, which affects the entire process - * and is not safe to use from concurrent threads. - * - * - * It is generally recommended to run igraph within a thread that has been - * permanently set to the C locale using system-specific means. This is a convenience - * function for situations when this is not easily possible because the programmer - * is not in control of the process, such as when developing plugins/extensions. - * Note that processes start up in the C locale by default, thus nothing needs to - * be done unless the locale has been changed away from the default. - * - * \param loc Pointer to a variable of type \c igraph_safelocale_t. The current - * locale will be stored here, so that it can be restored using - * \ref igraph_exit_safelocale(). - * \return Error code. - * - * \example examples/simple/safelocale.c - */ - -igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc) { - *loc = IGRAPH_CALLOC(1, struct igraph_safelocale_s); - IGRAPH_CHECK_OOM(loc, "Could not set C locale."); - igraph_safelocale_t l = *loc; -#ifdef HAVE_USELOCALE - l->c_locale = newlocale(LC_NUMERIC_MASK, "C", NULL); - if (! l->c_locale) { - IGRAPH_ERROR("Could not set C locale.", IGRAPH_FAILURE); - } - l->original_locale = uselocale(l->c_locale); -#else - l->original_locale = strdup(setlocale(LC_NUMERIC, NULL)); - if (! l->original_locale) { - IGRAPH_ERROR("Not enough memory.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } -# ifdef HAVE__CONFIGTHREADLOCALE - /* On Windows, we can enable per-thread locale */ - l->per_thread_locale = _configthreadlocale(0); - _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); -# endif - setlocale(LC_NUMERIC, "C"); -#endif - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_exit_safelocale - * \brief Temporarily set the C locale. - * - * \experimental - * - * Restores a locale saved by \ref igraph_enter_safelocale() and deallocates - * all associated data. This function \em must be paired with a call to - * \ref igraph_enter_safelocale(). - * - * \param loc A variable of type \c igraph_safelocale_t, originally set - * by \ref igraph_enter_safelocale(). - */ - -void igraph_exit_safelocale(igraph_safelocale_t *loc) { - igraph_safelocale_t l = *loc; -#ifdef HAVE_USELOCALE - uselocale(l->original_locale); - freelocale(l->c_locale); -#else - setlocale(LC_NUMERIC, l->original_locale); - IGRAPH_FREE(l->original_locale); -# ifdef HAVE__CONFIGTHREADLOCALE - /* Restore per-thread locale setting on Windows */ - _configthreadlocale(l->per_thread_locale); -# endif -#endif - IGRAPH_FREE(*loc); -} diff --git a/src/vendor/cigraph/src/io/parse_utils.h b/src/vendor/cigraph/src/io/parse_utils.h deleted file mode 100644 index 49a23d03a2a..00000000000 --- a/src/vendor/cigraph/src/io/parse_utils.h +++ /dev/null @@ -1,41 +0,0 @@ - -#ifndef IGRAPH_PARSE_UTILS_H -#define IGRAPH_PARSE_UTILS_H - -#include "igraph_error.h" -#include "igraph_types.h" - -/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ -#define IGRAPH_YY_CHECK(expr) \ - do { \ - igraph_error_t igraph_i_ret = (expr); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ - context->igraph_errno = igraph_i_ret; \ - yyerror(&yylloc, context, "failed"); \ - YYABORT; \ - } \ - } while (0) - -/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ -/* Note: - * Don't name macro argument 'igraph_errno' due to use of context->igraph_errno, - * or 'errno' due to use of #include in parse_utils.c. */ -#define IGRAPH_YY_ERRORF(reason, error_code, ...) \ - do { \ - igraph_errorf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ - error_code, __VA_ARGS__) ; \ - context->igraph_errno = error_code; \ - YYABORT; \ - } while (0) - -void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len); - -igraph_error_t igraph_i_fskip_whitespace(FILE *file); - -igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value); -igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value); - -igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value); -igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value); - -#endif /* IGRAPH_PARSE_UTILS_H */ diff --git a/src/vendor/cigraph/src/isomorphism/bliss.cc b/src/vendor/cigraph/src/isomorphism/bliss.cc index d304ff531e9..2439f629b84 100644 --- a/src/vendor/cigraph/src/isomorphism/bliss.cc +++ b/src/vendor/cigraph/src/isomorphism/bliss.cc @@ -25,12 +25,10 @@ #include "igraph_interrupt.h" #include "igraph_memory.h" #include "igraph_vector.h" +#include "igraph_vector_ptr.h" #include "core/exceptions.h" -#include -#include - using namespace bliss; using namespace std; @@ -76,28 +74,21 @@ using namespace std; namespace { // unnamed namespace inline AbstractGraph *bliss_from_igraph(const igraph_t *graph) { - igraph_integer_t nof_vertices = igraph_vcount(graph); - igraph_integer_t nof_edges = igraph_ecount(graph); - - if (nof_vertices > UINT_MAX || nof_edges > UINT_MAX) { - throw std::runtime_error("Graph too large for BLISS"); - } + 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(static_cast(nof_vertices)); + g = new Digraph(nof_vertices); } else { - g = new Graph(static_cast(nof_vertices)); + g = new Graph(nof_vertices); } /* g->set_verbose_level(0); */ - for (unsigned int i = 0; i < static_cast(nof_edges); i++) { - g->add_edge( - static_cast(IGRAPH_FROM(graph, i)), - static_cast(IGRAPH_TO(graph, i)) - ); + 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; @@ -109,7 +100,7 @@ void bliss_free_graph(AbstractGraph *g) { } -inline igraph_error_t bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { +inline int bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { if (directed) { Digraph::SplittingHeuristic gsh = Digraph::shs_fsm; switch (sh) { @@ -139,7 +130,7 @@ inline igraph_error_t bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool } -inline igraph_error_t bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { +inline int bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { if (colors == NULL) { return IGRAPH_SUCCESS; } @@ -148,17 +139,13 @@ inline igraph_error_t bliss_set_colors(AbstractGraph *g, const igraph_vector_int IGRAPH_ERROR("Invalid vertex color vector length.", IGRAPH_EINVAL); } for (int i = 0; i < n; ++i) { - igraph_integer_t color = VECTOR(*colors)[i]; - if (color < INT_MIN || color > INT_MAX) { - IGRAPH_ERRORF("Invalid vertex color index %" IGRAPH_PRId " for vertex %d.", IGRAPH_EOVERFLOW, color, i); - } - g->change_color(i, static_cast(color)); + g->change_color(i, VECTOR(*colors)[i]); } return IGRAPH_SUCCESS; } -inline igraph_error_t bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { +inline int bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { if (info) { size_t group_size_strlen; @@ -175,7 +162,7 @@ inline igraph_error_t bliss_info_to_igraph(igraph_bliss_info_t *info, const Stat 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); /* LCOV_EXCL_LINE */ + 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); @@ -203,24 +190,24 @@ struct AbortChecker { // This is the callback function used with AbstractGraph::find_automorphisms(). // It collects the automorphism group generators into a pointer vector. class AutCollector { - igraph_vector_int_list_t *generators; + igraph_vector_ptr_t *generators; public: - AutCollector(igraph_vector_int_list_t *generators_) : generators(generators_) { } + AutCollector(igraph_vector_ptr_t *generators_) : generators(generators_) { } void operator ()(unsigned int n, const unsigned int *aut) { - igraph_vector_int_t newvector; - igraph_error_t err; - - err = igraph_vector_int_init(&newvector, n); - if (err != IGRAPH_SUCCESS) { + int err; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); + if (! newvector) { throw bad_alloc(); } - - copy(aut, aut + n, VECTOR(newvector)); // takes care of unsigned int -> igraph_integer_t conversion - - err = igraph_vector_int_list_push_back(generators, &newvector); - if (err != IGRAPH_SUCCESS) { + 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(); } } @@ -254,12 +241,12 @@ class AutCollector { * when no longer needed, see \ref igraph_bliss_info_t. * \return Error code. * - * \sa \ref igraph_is_same_graph() + * \sa igraph_is_same_graph() * * Time complexity: exponential, in practice it is fast for many graphs. */ -igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_vector_int_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { +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); @@ -275,7 +262,7 @@ igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_ return IGRAPH_INTERRUPTED; } - IGRAPH_CHECK(igraph_vector_int_resize(labeling, N)); + IGRAPH_CHECK(igraph_vector_resize(labeling, N)); for (unsigned int i = 0; i < N; i++) { VECTOR(*labeling)[i] = cl[i]; } @@ -291,23 +278,14 @@ igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_ /** * \function igraph_automorphisms - * \brief Number of automorphisms using Bliss (deprecated alias). - * - * \deprecated-by igraph_count_automorphisms 0.10.5 - */ -igraph_error_t igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { - return igraph_count_automorphisms(graph, colors, sh, info); -} - -/** - * \function igraph_count_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. See also \ref igraph_bliss_info_t. + * 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. @@ -322,7 +300,7 @@ igraph_error_t igraph_automorphisms(const igraph_t *graph, const igraph_vector_i * * Time complexity: exponential, in practice it is fast for many graphs. */ -igraph_error_t igraph_count_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, +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); @@ -361,7 +339,7 @@ igraph_error_t igraph_count_automorphisms(const igraph_t *graph, const igraph_ve * \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_int_t objects + * 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. @@ -372,8 +350,8 @@ igraph_error_t igraph_count_automorphisms(const igraph_t *graph, const igraph_ve * * Time complexity: exponential, in practice it is fast for many graphs. */ -igraph_error_t igraph_automorphism_group( - const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_list_t *generators, +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); @@ -383,7 +361,7 @@ igraph_error_t igraph_automorphism_group( IGRAPH_CHECK(bliss_set_colors(g, colors)); Stats stats; - igraph_vector_int_list_clear(generators); + igraph_vector_ptr_resize(generators, 0); AutCollector collector(generators); AbortChecker checker; g->find_automorphisms(stats, collector, checker); @@ -463,20 +441,20 @@ igraph_error_t igraph_automorphism_group( * * Time complexity: exponential, but in practice it is quite fast. */ -igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, +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_int_t *map12, - igraph_vector_int_t *map21, igraph_bliss_sh_t sh, + 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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph1); - igraph_integer_t no_of_edges = igraph_ecount(graph1); - igraph_vector_int_t perm1, perm2; - igraph_vector_int_t vmap12, *mymap12 = &vmap12; - igraph_vector_int_t from, to, index; - igraph_vector_int_t from2, to2, index2; + 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; - igraph_integer_t i, j; + long int i, j; *iso = 0; if (info1) { @@ -503,80 +481,80 @@ igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *g if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { if (map12) { - igraph_vector_int_clear(map12); + igraph_vector_clear(map12); } if (map21) { - igraph_vector_int_clear(map21); + igraph_vector_clear(map21); } - return IGRAPH_SUCCESS; + return 0; } if (map12) { mymap12 = map12; } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(mymap12, 0); + IGRAPH_VECTOR_INIT_FINALLY(mymap12, 0); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&perm1, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&perm2, no_of_nodes); + 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_int_resize(mymap12, no_of_nodes)); + 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)[ VECTOR(perm2)[i] ] = 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)[ VECTOR(perm1)[i] ]; + VECTOR(perm2)[i] = VECTOR(*mymap12)[ (long int) VECTOR(perm1)[i] ]; } /* Copy it to mymap12 */ - IGRAPH_CHECK(igraph_vector_int_update(mymap12, &perm2)); + igraph_vector_update(mymap12, &perm2); - igraph_vector_int_destroy(&perm1); - igraph_vector_int_destroy(&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_INT_INIT_FINALLY(&from, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&from2, no_of_edges * 2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&to2, no_of_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&index2, no_of_edges); + 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)[ IGRAPH_FROM(graph1, i) ]; - VECTOR(to)[i] = VECTOR(*mymap12)[ IGRAPH_TO (graph1, 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_integer_t tmp = VECTOR(from)[i]; + igraph_real_t tmp = VECTOR(from)[i]; VECTOR(from)[i] = VECTOR(to)[i]; VECTOR(to)[i] = tmp; } } - igraph_vector_int_pair_order(&from, &to, &index, no_of_nodes); + 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_integer_t tmp = VECTOR(from2)[i]; + igraph_real_t tmp = VECTOR(from2)[i]; VECTOR(from2)[i] = VECTOR(to2)[i]; VECTOR(to2)[i] = tmp; } } - igraph_vector_int_resize(&from2, no_of_edges); - igraph_vector_int_pair_order(&from2, &to2, &index2, no_of_nodes); + 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++) { - igraph_integer_t i1 = VECTOR(index)[i]; - igraph_integer_t i2 = VECTOR(index2)[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; @@ -589,42 +567,42 @@ igraph_error_t igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *g if (*iso && colors1 != NULL) { for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(*colors1)[i] != VECTOR(*colors2)[ VECTOR(*mymap12)[i] ]) { + if (VECTOR(*colors1)[i] != VECTOR(*colors2)[(long int) VECTOR(*mymap12)[i] ]) { *iso = 0; break; } } } - igraph_vector_int_destroy(&index2); - igraph_vector_int_destroy(&to2); - igraph_vector_int_destroy(&from2); - igraph_vector_int_destroy(&index); - igraph_vector_int_destroy(&to); - igraph_vector_int_destroy(&from); + 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_int_resize(map21, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(map21, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - VECTOR(*map21)[ VECTOR(*mymap12)[i] ] = i; + VECTOR(*map21)[ (long int) VECTOR(*mymap12)[i] ] = i; } } } else { if (map12) { - igraph_vector_int_clear(map12); + igraph_vector_clear(map12); } if (map21) { - igraph_vector_int_clear(map21); + igraph_vector_clear(map21); } } if (!map12) { - igraph_vector_int_destroy(mymap12); + igraph_vector_destroy(mymap12); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/isomorphism/bliss/graph.hh b/src/vendor/cigraph/src/isomorphism/bliss/graph.hh index 5a60389628a..c4d30b3f66e 100644 --- a/src/vendor/cigraph/src/isomorphism/bliss/graph.hh +++ b/src/vendor/cigraph/src/isomorphism/bliss/graph.hh @@ -514,9 +514,7 @@ protected: unsigned int color; std::vector edges; - unsigned int nof_edges() const { - return static_cast(edges.size()); - } + unsigned int nof_edges() const {return edges.size(); } }; std::vector vertices; void sort_edges(); @@ -612,9 +610,7 @@ public: /** * Return the number of vertices in the graph. */ - unsigned int get_nof_vertices() const { - return static_cast(vertices.size()); - } + unsigned int get_nof_vertices() const {return vertices.size(); } /** * \copydoc AbstractGraph::permute(const unsigned int* const perm) const @@ -716,8 +712,8 @@ protected: unsigned int color; std::vector edges_out; std::vector edges_in; - unsigned int nof_edges_in() const { return static_cast(edges_in.size()); } - unsigned int nof_edges_out() const { return static_cast(edges_out.size()); } + 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(); @@ -823,7 +819,7 @@ public: /** * Return the number of vertices in the graph. */ - unsigned int get_nof_vertices() const { return static_cast(vertices.size()); } + unsigned int get_nof_vertices() const {return vertices.size(); } /** * Add a new vertex with color 'color' in the graph and return its index. diff --git a/src/vendor/cigraph/src/isomorphism/isoclasses.c b/src/vendor/cigraph/src/isomorphism/isoclasses.c index 27162e903f1..d2a4eca5040 100644 --- a/src/vendor/cigraph/src/isomorphism/isoclasses.c +++ b/src/vendor/cigraph/src/isomorphism/isoclasses.c @@ -2525,7 +2525,6 @@ const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, * (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. - * These values can also be retrieved using \ref igraph_graph_count(). * For more information, see https://oeis.org/A000273 and https://oeis.org/A000088. * * @@ -2548,10 +2547,10 @@ const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, * * Time complexity: O(|E|), the number of edges in the graph. */ -igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { - igraph_integer_t e; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(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; @@ -2626,8 +2625,8 @@ igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass * 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 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. @@ -2637,18 +2636,18 @@ igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass * Time complexity: O((d+n)*n), d is the average degree in the network, * and n is the number of vertices in \c vids. */ -igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, +int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, igraph_integer_t *isoclass) { - igraph_integer_t subgraph_size = igraph_vector_int_size(vids); - igraph_vector_int_t neis; + 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; - igraph_integer_t i, j, s; + long int i, j, s; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (igraph_is_directed(graph)) { switch (subgraph_size) { @@ -2695,20 +2694,20 @@ igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vect } for (i = 0; i < subgraph_size; i++) { - igraph_integer_t from = VECTOR(*vids)[i]; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); - s = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j], to; - if (igraph_vector_int_search(vids, 0, nei, &to)) { + 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 = arr_code[code]; - igraph_vector_int_destroy(&neis); + *isoclass = (igraph_integer_t) arr_code[code]; + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -2746,22 +2745,22 @@ igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vect * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the graph to create. */ -igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, +int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed) { - igraph_vector_int_t edges; + igraph_vector_t edges; const unsigned int *classedges; - igraph_integer_t graphcount; - igraph_integer_t pos; - unsigned int power; + long int graphcount; + long int power; + long int pos; unsigned int code; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); #define CHECK_ISOCLASS(number, directed, size, graphcount) \ IGRAPH_ERRORF( \ - "Isoclass %" IGRAPH_PRId " requested, but there are only %" \ - IGRAPH_PRId " %s graphs of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, \ - number, graphcount, directed ? "directed" : "undirected", size) + "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) { @@ -2773,7 +2772,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_3[number]; + code = igraph_i_isographs_3[ (long int) number]; power = 32; break; @@ -2786,7 +2785,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_4[number]; + code = igraph_i_isographs_4[ (long int) number]; power = 2048; break; @@ -2806,7 +2805,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_3u[number]; + code = igraph_i_isographs_3u[ (long int) number]; power = 4; break; @@ -2819,7 +2818,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_4u[number]; + code = igraph_i_isographs_4u[ (long int) number]; power = 32; break; @@ -2832,7 +2831,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_5u[number]; + code = igraph_i_isographs_5u[ (long int) number]; power = 512; break; @@ -2845,7 +2844,7 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_6u[number]; + code = igraph_i_isographs_6u[ (long int) number]; power = 16384; break; @@ -2861,8 +2860,8 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, pos = 0; while (code > 0) { if (code >= power) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, classedges[2 * pos])); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, classedges[2 * pos + 1])); + 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; @@ -2870,64 +2869,8 @@ igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, } IGRAPH_CHECK(igraph_create(graph, &edges, size, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } - -/* https://oeis.org/A000088 */ -static igraph_integer_t undirected_graph_counts[] = { - 1, 1, 2, 4, 11, 34, 156, 1044, 12346, 274668, 12005168, 1018997864, -#if IGRAPH_INTEGER_SIZE == 64 - 165091172592, 50502031367952, 29054155657235488 -#endif -}; - -/* https://oeis.org/A000273 */ -static igraph_integer_t directed_graph_counts[] = { - 1, 1, 3, 16, 218, 9608, 1540944, 882033440, -#if IGRAPH_INTEGER_SIZE == 64 - 1793359192848, 13027956824399552 -#endif -}; - -/** - * \function igraph_graph_count - * \brief The number of unlabelled graphs on the given number of vertices. - * - * Gives the number of unlabelled \em simple graphs on the specified number of vertices. - * The "isoclass" of a graph of this size is at most one less than this value. - * - * - * This function is meant to be used in conjunction with isoclass and motif finder - * functions. It will only work for small \p n values for which the result is - * represetable in an \type igraph_integer_t. For larger \p n values, an overflow - * error is raised. - * - * \param n The number of vertices. - * \param directed Boolean, whether to consider directed graphs. - * \param count Pointer to an integer, the result will be stored here. - * \return Error code. - * - * \sa \ref igraph_isoclass(), \ref igraph_motifs_randesu_callback(). - * - * Time complexity: O(1). - */ -igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count) { - if (n < 0) { - IGRAPH_ERROR("Graph size must not be negative.", IGRAPH_EINVAL); - } - if (directed) { - if (n >= (igraph_integer_t) (sizeof directed_graph_counts / sizeof directed_graph_counts[0])) { - IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); - } - *count = directed_graph_counts[n]; - } else { - if (n >= (igraph_integer_t) (sizeof undirected_graph_counts / sizeof undirected_graph_counts[0])) { - IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); - } - *count = undirected_graph_counts[n]; - } - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c b/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c index 2b8ff8e693c..235e5e73c31 100644 --- a/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c +++ b/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c @@ -52,24 +52,24 @@ * \sa \ref igraph_simplify(), \ref igraph_isomorphic_vf2(), \ref igraph_subisomorphic_vf2() * */ -igraph_error_t igraph_simplify_and_colorize( +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_int_t edges; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t pto = -1, pfrom = -1; - igraph_integer_t i; + 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_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + 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); @@ -79,9 +79,9 @@ igraph_error_t igraph_simplify_and_colorize( i = -1; for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); + 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]++; @@ -91,8 +91,8 @@ igraph_error_t igraph_simplify_and_colorize( if (to == pto && from == pfrom) { VECTOR(*edge_color)[i]++; } else { - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); i++; VECTOR(*edge_color)[i] = 1; } @@ -108,7 +108,7 @@ igraph_error_t igraph_simplify_and_colorize( IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/isomorphism/lad.c b/src/vendor/cigraph/src/isomorphism/lad.c index fbb9518db07..d6be6657f51 100644 --- a/src/vendor/cigraph/src/isomorphism/lad.c +++ b/src/vendor/cigraph/src/isomorphism/lad.c @@ -57,17 +57,23 @@ #include "core/interruption.h" -#include #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); /* LCOV_EXCL_LINE */ \ + IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, VAR); \ } @@ -77,7 +83,7 @@ #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); /* LCOV_EXCL_LINE */ \ + 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)); \ @@ -89,15 +95,15 @@ /* ---------------------------------------------------------*/ typedef struct { - igraph_integer_t nbVertices; /* Number of vertices */ - igraph_vector_int_t nbSucc; + long int nbVertices; /* Number of vertices */ + igraph_vector_t nbSucc; igraph_adjlist_t succ; igraph_matrix_char_t isEdge; } Tgraph; -static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { - igraph_integer_t i, j, n; - igraph_integer_t no_of_nodes = igraph_vcount(igraph); +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; @@ -105,7 +111,7 @@ static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* g IGRAPH_CHECK(igraph_adjlist_init(igraph, &graph->succ, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &graph->succ); - IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->nbSucc, no_of_nodes); + 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)); } @@ -117,7 +123,7 @@ static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* g neis = igraph_adjlist_get(&graph->succ, i); n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - igraph_integer_t v = VECTOR(*neis)[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); } @@ -133,7 +139,7 @@ static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* g static void igraph_i_lad_destroyGraph(Tgraph *graph) { igraph_matrix_char_destroy(&graph->isEdge); igraph_adjlist_destroy(&graph->succ); - igraph_vector_int_destroy(&graph->nbSucc); + igraph_vector_destroy(&graph->nbSucc); } @@ -151,17 +157,17 @@ typedef struct { /* 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] */ - igraph_integer_t valSize; /* size of val */ + 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) */ - igraph_integer_t nextOutToFilter; /* position in toFilter of the next pattern node whose + int nextOutToFilter; /* position in toFilter of the next pattern node whose domain should be filtered (-1 if no domain to filter) */ - igraph_integer_t lastInToFilter; /* position in toFilter of the last pattern node whose + 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 */ @@ -186,11 +192,11 @@ static void igraph_i_lad_resetToFilter(Tdomain *D) { } -static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t size) { +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 */ - igraph_integer_t u = VECTOR(D->toFilter)[D->nextOutToFilter]; + int u = VECTOR(D->toFilter)[D->nextOutToFilter]; VECTOR(D->markedToFilter)[u] = false; if (D->nextOutToFilter == D->lastInToFilter) { /* u was the last node in tofilter */ @@ -203,7 +209,7 @@ static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t s return u; } -static void igraph_i_lad_addToFilter(igraph_integer_t u, Tdomain* D, igraph_integer_t size) { +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; @@ -220,29 +226,29 @@ static void igraph_i_lad_addToFilter(igraph_integer_t u, Tdomain* D, igraph_inte VECTOR(D->toFilter)[D->lastInToFilter] = u; } -static bool igraph_i_lad_isInD(igraph_integer_t u, igraph_integer_t v, Tdomain* D) { +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 igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D, igraph_integer_t nbV, bool* result) { +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 */ - igraph_integer_t *fifo, *pred; + int *fifo, *pred; bool *marked; - igraph_integer_t nextIn = 0; - igraph_integer_t nextOut = 0; - igraph_integer_t i, v, v2, u2; + int nextIn = 0; + int nextOut = 0; + int i, v, v2, u2; *result = false; /* Allocate memory */ - ALLOC_ARRAY(fifo, nbV, igraph_integer_t); - ALLOC_ARRAY(pred, nbV, igraph_integer_t); + ALLOC_ARRAY(fifo, nbV, int); + ALLOC_ARRAY(pred, nbV, int); ALLOC_ARRAY(marked, nbV, bool); for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { @@ -291,21 +297,21 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D igraph_free(marked); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lad_removeAllValuesButOne(igraph_integer_t u, igraph_integer_t v, Tdomain* D, Tgraph* Gp, +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 */ - igraph_integer_t j, oldPos, newPos; + int j, oldPos, newPos; igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); - igraph_integer_t n = igraph_vector_int_size(uneis); + 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(VECTOR(*uneis)[j], D, - Gp->nbVertices); + 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); @@ -320,27 +326,27 @@ static igraph_error_t igraph_i_lad_removeAllValuesButOne(igraph_integer_t u, igr 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, Gt->nbVertices, result)); + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); } else { *result = true; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lad_removeValue(igraph_integer_t u, igraph_integer_t v, Tdomain* D, Tgraph* Gp, +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 */ - igraph_integer_t j; + int j; igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); - igraph_integer_t n = igraph_vector_int_size(uneis); - igraph_integer_t oldPos, newPos; + 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(VECTOR(*uneis)[j], D, - Gp->nbVertices); + igraph_i_lad_addToFilter((int) VECTOR(*uneis)[j], D, + (int) (Gp->nbVertices)); } /* remove v from D[u] */ oldPos = MATRIX(D->posInVal, u, v); @@ -355,17 +361,17 @@ static igraph_error_t igraph_i_lad_removeValue(igraph_integer_t u, igraph_intege if (VECTOR(D->globalMatchingP)[u] == v) { VECTOR(D->globalMatchingP)[u] = -1; VECTOR(D->globalMatchingT)[v] = -1; - IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, Gt->nbVertices, result)); + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, (int) (Gt->nbVertices), result)); } else { *result = true; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vector_int_t* toBeMatched, +static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, bool induced, Tdomain* D, Tgraph* Gp, - Tgraph* Gt, igraph_integer_t *invalid) { + 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 @@ -373,7 +379,7 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec 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_integer_t j, u, v, u2, oldNbVal; + int j, u, v, u2, oldNbVal; igraph_vector_int_t *vneis; bool result = false; @@ -388,8 +394,7 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec 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 IGRAPH_SUCCESS; + *invalid = 1 ; return 0; } } if (MATRIX(Gp->isEdge, u, u2)) { @@ -401,8 +406,7 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { - *invalid = 1; - return IGRAPH_SUCCESS; + *invalid = 1; return 0; } } } @@ -416,18 +420,16 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { - *invalid = 1; - return IGRAPH_SUCCESS; + *invalid = 1; return 0; } } } } else { for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) { - if (igraph_i_lad_isInD(u2, VECTOR(*vneis)[j], D)) { - IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(*vneis)[j], D, Gp, Gt, &result)); + 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 IGRAPH_SUCCESS; + *invalid = 1; return 0; } } } @@ -435,7 +437,7 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec } if (VECTOR(D->nbVal)[u2] == 0) { *invalid = 1; /* D[u2] is empty */ - return IGRAPH_SUCCESS; + return 0; } if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) { VECTOR(*toBeMatched)[nb++] = u2; @@ -444,13 +446,13 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec } } *invalid = 0; - return IGRAPH_SUCCESS; + return 0; } -static bool igraph_i_lad_matchVertex(igraph_integer_t u, bool induced, Tdomain* D, Tgraph* Gp, +static bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp, Tgraph *Gt) { - igraph_integer_t invalid; + 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) @@ -458,7 +460,8 @@ static bool igraph_i_lad_matchVertex(igraph_integer_t u, bool induced, Tdomain* return false if an inconsistency is detected by FC(Edges) or FC(diff); true otherwise; */ igraph_vector_int_t toBeMatched; - IGRAPH_VECTOR_INT_INIT_FINALLY(&toBeMatched, Gp->nbVertices); + 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)); @@ -471,22 +474,17 @@ static bool igraph_i_lad_matchVertex(igraph_integer_t u, bool induced, Tdomain* static int igraph_i_lad_qcompare (void const *a, void const *b) { /* function used by the qsort function */ - igraph_integer_t pa = ((*((igraph_integer_t*)a) - *((igraph_integer_t*)b))); - if (pa < 0) { - return -1; - } else if (pa > 0) { - return 1; - } - return 0; + int pa = *((int*)a) - *((int*)b); + return pa; } -static bool igraph_i_lad_compare(igraph_integer_t size_mu, igraph_integer_t* mu, igraph_integer_t size_mv, igraph_integer_t* mv) { +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 */ - igraph_integer_t i, j; - igraph_qsort(mu, (size_t) size_mu, sizeof(mu[0]), igraph_i_lad_qcompare); - igraph_qsort(mv, (size_t) size_mv, sizeof(mv[0]), igraph_i_lad_qcompare); + 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]) { @@ -497,22 +495,22 @@ static bool igraph_i_lad_compare(igraph_integer_t size_mu, igraph_integer_t* mu, return true; } -static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, - const igraph_vector_int_list_t *domains, Tdomain *D, - const Tgraph *Gp, const Tgraph *Gt, igraph_integer_t *empty) { +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 */ - igraph_integer_t *val; + int *val; bool *dom; - igraph_integer_t *mu, *mv; - igraph_integer_t matchingSize, u, v, i, j; - igraph_vector_int_t *vec; + int *mu, *mv; + int matchingSize, u, v, i, j; + igraph_vector_t *vec; - ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, int); ALLOC_ARRAY(dom, Gt->nbVertices, bool); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); @@ -546,11 +544,11 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, 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 = igraph_vector_int_list_get_ptr(domains, u); - i = igraph_vector_int_size(vec); + 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 = VECTOR(*vec)[j]; + v = (int) VECTOR(*vec)[j]; dom[v] = true; } } @@ -561,42 +559,42 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, 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) = VECTOR(D->firstVal)[u] + - Gt->nbVertices; + 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(VECTOR(Gp->nbSucc)[u], igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } - mv = IGRAPH_CALLOC(VECTOR(Gt->nbSucc)[v], igraph_integer_t); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("cannot allocate 'mv' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); } for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { - mu[i] = VECTOR(Gp->nbSucc)[VECTOR(*Gp_uneis)[i]]; + mu[i] = (int) VECTOR(Gp->nbSucc)[(long int) VECTOR(*Gp_uneis)[i]]; } for (i = 0; i < VECTOR(Gt->nbSucc)[v]; i++) { - mv[i] = VECTOR(Gt->nbSucc)[VECTOR(*Gt_vneis)[i]]; + mv[i] = (int) VECTOR(Gt->nbSucc)[(long int) VECTOR(*Gt_vneis)[i]]; } - if (igraph_i_lad_compare(VECTOR(Gp->nbSucc)[u], mu, - VECTOR(Gt->nbSucc)[v], mv) == 1) { + 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) = - VECTOR(D->firstVal)[u] + Gt->nbVertices; + (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) = - VECTOR(D->firstVal)[u] + Gt->nbVertices; + (int) (VECTOR(D->firstVal)[u] + Gt->nbVertices); } } } @@ -624,7 +622,7 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, igraph_vector_int_fill(&D->matching, -1); D->nextOutToFilter = 0; - D->lastInToFilter = Gp->nbVertices - 1; + D->lastInToFilter = (int) (Gp->nbVertices - 1); *empty = 0; @@ -661,19 +659,19 @@ static void igraph_i_lad_destroyDomains(Tdomain *D) { #define toBeDeleted 3 #define deleted 4 -static void igraph_i_lad_addToDelete(igraph_integer_t u, igraph_integer_t* list, igraph_integer_t* nb, igraph_integer_t* marked) { +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 igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igraph_integer_t sizeOfV, +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, - igraph_integer_t *invalid) { + int *invalid) { /* input: sizeOfU = number of vertices in U sizeOfV = number of vertices in V @@ -688,53 +686,53 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra 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 */ - igraph_integer_t *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */ - igraph_integer_t *nbPred; /* nbPred[i] = nb of predecessors of the ith + int *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */ + int *nbPred; /* nbPred[i] = nb of predecessors of the ith vertex of V in the DAG */ - igraph_integer_t *pred; /* pred[i][j] = jth predecessor the ith + int *pred; /* pred[i][j] = jth predecessor the ith vertex of V in the DAG */ - igraph_integer_t *nbSucc; /* nbSucc[i] = nb of successors of the ith + int *nbSucc; /* nbSucc[i] = nb of successors of the ith vertex of U in the DAG */ - igraph_integer_t *succ; /* succ[i][j] = jth successor of the ith + int *succ; /* succ[i][j] = jth successor of the ith vertex of U in the DAG */ - igraph_integer_t *listV, *listU, *listDV, *listDU; - igraph_integer_t nbV, nbU, nbDV, nbDU; - igraph_integer_t i, j, k, stop, u, v; - igraph_integer_t *markedV, *markedU; + 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 */ - igraph_integer_t nbUnmatched = 0; /* number of vertices of U that are not matched */ - igraph_integer_t *unmatched; /* vertices of U that are not matched */ - igraph_integer_t *posInUnmatched; /* unmatched[posInUnmatched[u]]=u */ + 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 IGRAPH_SUCCESS; + return 0; } - ALLOC_ARRAY(matchedWithV, sizeOfV, igraph_integer_t); - ALLOC_ARRAY(nbPred, sizeOfV, igraph_integer_t); - ALLOC_ARRAY(pred, sizeOfV * sizeOfU, igraph_integer_t); - ALLOC_ARRAY(nbSucc, sizeOfU, igraph_integer_t); - ALLOC_ARRAY(succ, sizeOfU * sizeOfV, igraph_integer_t); - ALLOC_ARRAY(listV, sizeOfV, igraph_integer_t); - ALLOC_ARRAY(listU, sizeOfU, igraph_integer_t); - ALLOC_ARRAY(listDV, sizeOfV, igraph_integer_t); - ALLOC_ARRAY(listDU, sizeOfU, igraph_integer_t); - ALLOC_ARRAY(markedV, sizeOfV, igraph_integer_t); - ALLOC_ARRAY(markedU, sizeOfU, igraph_integer_t); - ALLOC_ARRAY(unmatched, sizeOfU, igraph_integer_t); - ALLOC_ARRAY(posInUnmatched, sizeOfU, igraph_integer_t); + 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(matchedWithV[0])); + memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(int)); for (u = 0; u < sizeOfU; u++) { if (VECTOR(*matchedWithU)[u] >= 0) { matchedWithV[VECTOR(*matchedWithU)[u]] = u; @@ -764,10 +762,10 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra while (nbUnmatched > 0) { /* Try to increase the number of matched vertices */ /* step 1 : build the DAG */ - memset(markedU, white, (size_t) sizeOfU * sizeof(markedU[0])); - memset(nbSucc, 0, (size_t) sizeOfU * sizeof(nbSucc[0])); - memset(markedV, white, (size_t) sizeOfV * sizeof(markedV[0])); - memset(nbPred, 0, (size_t) sizeOfV * sizeof(nbPred[0])); + 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++) { @@ -871,6 +869,7 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra 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++) { } @@ -916,12 +915,12 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra igraph_free(nbPred); igraph_free(matchedWithV); IGRAPH_FINALLY_CLEAN(14); - return IGRAPH_SUCCESS; + return 0; } -static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t u, bool* marked, igraph_integer_t* nbSucc, - igraph_integer_t* succ, igraph_vector_int_t * matchedWithU, - igraph_integer_t* order, igraph_integer_t* nb) { +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 @@ -932,8 +931,8 @@ static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_ 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 */ - igraph_integer_t i; - igraph_integer_t v = VECTOR(*matchedWithU)[u]; /* the only one predecessor of v is u */ + 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++) { @@ -947,9 +946,9 @@ static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_ order[*nb] = u; (*nb)--; } -static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t* numV, igraph_integer_t* numU, - igraph_integer_t* nbSucc, igraph_integer_t* succ, - igraph_integer_t* nbPred, igraph_integer_t* pred, +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 @@ -962,15 +961,15 @@ static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nb Given a vertex v of Gt, nbSucc[v]=number of sucessors of v and succ[v]=list of successors of v */ - igraph_integer_t *order; + int *order; bool *marked; - igraph_integer_t *fifo; - igraph_integer_t u, v, i, j, k, nbSCC, nb; + int *fifo; + int u, v, i, j, k, nbSCC, nb; /* Allocate memory */ - ALLOC_ARRAY(order, nbU, igraph_integer_t); + ALLOC_ARRAY(order, nbU, int); ALLOC_ARRAY(marked, nbU, bool); - ALLOC_ARRAY(fifo, nbV, igraph_integer_t); + ALLOC_ARRAY(fifo, nbV, int); /* Order vertices of Gp wrt DFS */ nb = nbU - 1; @@ -983,8 +982,8 @@ static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nb /* traversal starting from order[0], then order[1], ... */ nbSCC = 0; - memset(numU, -1, (size_t) nbU * sizeof(numU[0])); - memset(numV, -1, (size_t) nbV * sizeof(numV[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]; @@ -1018,12 +1017,12 @@ static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nb igraph_free(order); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, - Tdomain* D, igraph_integer_t *invalid) { +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) @@ -1034,29 +1033,29 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg v=D->globalMatchingP[u])} U { (v, u) / v is a vertex of Gt which is in D(u) but is not matched to u} */ - igraph_integer_t *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ - igraph_integer_t *pred; /* pred[u][i] = ith + int *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ + int *pred; /* pred[u][i] = ith predecessor of u in Go */ - igraph_integer_t *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ - igraph_integer_t *succ; /* succ[v][i] = ith + int *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ + int *succ; /* succ[v][i] = ith successor of v in Go */ - igraph_integer_t u, v, i, w, oldNbVal, nbToMatch; - igraph_integer_t *numV, *numU; + int u, v, i, w, oldNbVal, nbToMatch; + int *numV, *numU; igraph_vector_int_t toMatch; bool *used; - igraph_integer_t *list; - igraph_integer_t nb = 0; + int *list; + int nb = 0; bool result; /* Allocate memory */ - ALLOC_ARRAY(nbPred, Gp->nbVertices, igraph_integer_t); - ALLOC_ARRAY(pred, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); - ALLOC_ARRAY(nbSucc, Gt->nbVertices, igraph_integer_t); - ALLOC_ARRAY(succ, Gt->nbVertices * Gp->nbVertices, igraph_integer_t); - ALLOC_ARRAY(numV, Gt->nbVertices, igraph_integer_t); - ALLOC_ARRAY(numU, Gp->nbVertices, igraph_integer_t); + 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, igraph_integer_t); + ALLOC_ARRAY(list, Gt->nbVertices, int); IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); @@ -1097,7 +1096,7 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg /* look for strongly connected components in Go */ IGRAPH_CHECK( - igraph_i_lad_SCC(Gp->nbVertices, Gt->nbVertices, numV, numU, + 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 @@ -1142,38 +1141,38 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg igraph_free(nbPred); IGRAPH_FINALLY_CLEAN(9); - return IGRAPH_SUCCESS; + return 0; } /* ---------------------------------------------------------*/ /* Coming from lad.c */ /* ---------------------------------------------------------*/ -static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, +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 */ - igraph_integer_t u2, v2, i, j; - igraph_integer_t nbMatched = 0; + int u2, v2, i, j; + int nbMatched = 0; igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u); - igraph_integer_t *num, *numInv; + int *num, *numInv; igraph_vector_int_t nbComp; igraph_vector_int_t firstComp; igraph_vector_int_t comp; - igraph_integer_t nbNum = 0; - igraph_integer_t posInComp = 0; + int nbNum = 0; + int posInComp = 0; igraph_vector_int_t matchedWithU; - igraph_integer_t invalid; + 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 = VECTOR(*Gp_uneis)[0]; /* u2 is the only node adjacent to u */ + 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 IGRAPH_SUCCESS; + return 0; } /* look for a support of edge (u, u2) for v */ for (i = VECTOR(D->firstVal)[u2]; @@ -1182,18 +1181,18 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = VECTOR(D->val)[i]; *result = true; - return IGRAPH_SUCCESS; + return 0; } } *result = false; - return IGRAPH_SUCCESS; + 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 = VECTOR(*Gp_uneis)[i]; + 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++; @@ -1201,32 +1200,32 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t } if (nbMatched == VECTOR(Gp->nbSucc)[u]) { *result = true; - return IGRAPH_SUCCESS; + return 0; } /* The matching still covers adj(u) */ /* Allocate memory */ - ALLOC_ARRAY(num, Gt->nbVertices, igraph_integer_t); - ALLOC_ARRAY(numInv, Gt->nbVertices, igraph_integer_t); + 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, VECTOR(Gp->nbSucc)[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, VECTOR(Gp->nbSucc)[u])); + 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, (VECTOR(Gp->nbSucc)[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, VECTOR(Gp->nbSucc)[u])); + 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(num[0])); + memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(int)); for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { - u2 = VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */ + 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; @@ -1246,7 +1245,7 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t } else { igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v); for (j = 0; j < VECTOR(Gt->nbSucc)[v]; j++) { - v2 = VECTOR(*Gt_vneis)[j]; /* v2 is a successor of v */ + 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; @@ -1271,7 +1270,7 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t } /* Call Hopcroft Karp to update the matching */ IGRAPH_CHECK( - igraph_i_lad_updateMatching(VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, + igraph_i_lad_updateMatching((int) VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, &firstComp, &comp, &matchedWithU, &invalid) ); if (invalid) { @@ -1293,24 +1292,24 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t igraph_vector_int_destroy(&nbComp); IGRAPH_FINALLY_CLEAN(6); - return IGRAPH_SUCCESS; + return 0; } /* ---------------------------------------------------------*/ /* Coming from main.c */ /* ---------------------------------------------------------*/ -static igraph_error_t igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, +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 */ - igraph_integer_t u, v, i, oldNbVal; - igraph_integer_t invalid; + 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, Gp->nbVertices); + 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]) { @@ -1324,37 +1323,36 @@ static igraph_error_t igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result2)); if (!result2) { *result = false; - return IGRAPH_SUCCESS; + return 0; } } } if ((VECTOR(D->nbVal)[u] == 1) && (oldNbVal > 1) && (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) { - *result = false; - return IGRAPH_SUCCESS; + *result = false; return 0; } if (VECTOR(D->nbVal)[u] == 0) { *result = false; - return IGRAPH_SUCCESS; + return 0; } } igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid); if (invalid) { *result = false; - return IGRAPH_SUCCESS; + return 0; } } *result = true; - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstSol, bool induced, +static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, - igraph_integer_t *invalid, igraph_bool_t *iso, - igraph_vector_int_t *vec, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, - igraph_integer_t *nbNodes, igraph_integer_t *nbFail, igraph_integer_t *nbSol, + 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; @@ -1362,11 +1360,12 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS return false if CPU time limit exceeded before the search is completed, return true otherwise */ - igraph_integer_t u, v, minDom, i; - igraph_integer_t* nbVal; - igraph_integer_t* globalMatching; + int u, v, minDom, i; + int* nbVal; + int* globalMatching; clock_t end = clock(); - igraph_integer_t* val; + igraph_vector_t *vec; + int* val; bool result; (*nbNodes)++; @@ -1377,8 +1376,8 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS } /* Allocate memory */ - ALLOC_ARRAY_IN_HISTORY(nbVal, Gp->nbVertices, igraph_integer_t, alloc_history); - ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, igraph_integer_t, alloc_history); + 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) { @@ -1407,18 +1406,25 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS *iso = 1; } (*nbSol)++; - if (map && igraph_vector_int_size(map) == 0) { - IGRAPH_CHECK(igraph_vector_int_resize(map, Gp->nbVertices)); + 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) { - IGRAPH_CHECK(igraph_vector_int_resize(vec, Gp->nbVertices)); + 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_int_list_push_back_copy(maps, vec)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(maps, vec)); + IGRAPH_FINALLY_CLEAN(2); } igraph_i_lad_resetToFilter(D); *invalid = 0; @@ -1426,7 +1432,7 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS } /* save the domain of minDom to iterate on its values */ - ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], igraph_integer_t, alloc_history); + 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 ]; } @@ -1442,7 +1448,7 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS igraph_i_lad_resetToFilter(D); } else { IGRAPH_CHECK(igraph_i_lad_solve(timeLimit, firstSol, induced, - D, Gp, Gt, invalid, iso, vec, map, maps, + D, Gp, Gt, invalid, iso, map, maps, nbNodes, nbFail, nbSol, begin, alloc_history)); } @@ -1465,7 +1471,7 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS igraph_free(nbVal); igraph_vector_ptr_pop_back(alloc_history); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1509,20 +1515,20 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS * \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 \ref igraph_vector_int_t + * 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 + * 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 \c true if a subgraph - * isomorphism is found, and to \c false otherwise. + * 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 to a list of integer vectors or a null pointer. If not - * a null pointer, then all subgraph isomorphisms are stored in the - * vector list, in \ref igraph_vector_int_t objects. + * (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 @@ -1537,34 +1543,32 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS * \example examples/simple/igraph_subisomorphic_lad.c */ -igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, - const igraph_vector_int_list_t *domains, - igraph_bool_t *iso, igraph_vector_int_t *map, - igraph_vector_int_list_t *maps, - igraph_bool_t induced, igraph_integer_t time_limit) { +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; - igraph_integer_t invalidDomain; - igraph_integer_t u, nbToMatch = 0; + int invalidDomain; + int u, nbToMatch = 0; igraph_vector_int_t toMatch; - /* Helper vector in which we build the current subisomorphism mapping */ - igraph_vector_int_t vec; /* Number of nodes in the search tree */ - igraph_integer_t nbNodes = 0; + int nbNodes = 0; /* number of failed nodes in the search tree */ - igraph_integer_t nbFail = 0; + int nbFail = 0; /* number of solutions found */ - igraph_integer_t nbSol = 0; + 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 specify at least one of `iso', `map' or `maps'", + IGRAPH_ERROR("Please give least one of `iso', `map' or `maps'", IGRAPH_EINVAL); } @@ -1573,17 +1577,17 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t "or vice versa", IGRAPH_EINVAL); } if (time_limit <= 0) { - time_limit = IGRAPH_INTEGER_MAX; + time_limit = INT_MAX; } if (iso) { *iso = (igraph_vcount(pattern) == 0); } if (map) { - igraph_vector_int_clear(map); + igraph_vector_clear(map); } if (maps) { - igraph_vector_int_list_clear(maps); + igraph_vector_ptr_clear(maps); } if (igraph_vcount(pattern) == 0) { @@ -1591,8 +1595,6 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); - IGRAPH_CHECK(igraph_i_lad_createGraph(pattern, &Gp)); IGRAPH_FINALLY(igraph_i_lad_destroyGraph, &Gp); @@ -1610,8 +1612,8 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t goto exit2; } - IGRAPH_CHECK(igraph_i_lad_updateMatching(Gp.nbVertices, - Gt.nbVertices, + IGRAPH_CHECK(igraph_i_lad_updateMatching((int) (Gp.nbVertices), + (int) (Gt.nbVertices), &D.nbVal, &D.firstVal, &D.val, &D.globalMatchingP, &invalidDomain)); @@ -1649,7 +1651,7 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t 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, &vec, map, maps, + &Gp, &Gt, &invalidDomain, iso, map, maps, &nbNodes, &nbFail, &nbSol, &begin, &alloc_history)); @@ -1666,8 +1668,7 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t igraph_i_lad_destroyGraph(&Gt); igraph_i_lad_destroyGraph(&Gp); - igraph_vector_int_destroy(&vec); - IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/isomorphism/queries.c b/src/vendor/cigraph/src/isomorphism/queries.c index 2528a9ae4d0..2f55cab7706 100644 --- a/src/vendor/cigraph/src/isomorphism/queries.c +++ b/src/vendor/cigraph/src/isomorphism/queries.c @@ -46,21 +46,18 @@ * Functions for the Bliss algorithm constitute the third set, * see \ref igraph_isomorphic_bliss(). * - * Finally, the isomorphism classes of all directed graphs with three and - * four vertices and all undirected graphs with 3-6 vertices are precomputed - * and stored in igraph, so for these small graphs there is a separate fast - * path in the code that does not use more complex, generic isomorphism - * algorithms. + * 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(). + * */ -static igraph_error_t igraph_i_isomorphic_small( - const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso -); - /** * \function igraph_isomorphic - * \brief Are two graphs 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 @@ -79,11 +76,9 @@ static igraph_error_t igraph_i_isomorphic_small( * 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 \ref igraph_isoclass() function supports both - * graphs (which is true for directed graphs with 3 and 4 vertices, and - * undirected graphs with 3-6 vertices), an O(1) algorithm is used with - * precomputed data. + * 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 * @@ -92,19 +87,19 @@ static igraph_error_t igraph_i_isomorphic_small( * * \param graph1 The first graph. * \param graph2 The second graph. - * \param iso Pointer to a logical variable, will be set to \c true - * if the two graphs are isomorphic, and \c false otherwise. + * \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. */ -igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, +int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso) { - igraph_integer_t nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2); - igraph_integer_t edges1 = igraph_ecount(graph1), edges2 = igraph_ecount(graph2); + 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; @@ -119,11 +114,11 @@ igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, IGRAPH_ERROR("Cannot compare directed and undirected graphs", IGRAPH_EINVAL); } else if (nodes1 != nodes2 || edges1 != edges2) { *iso = 0; - } else if (nodes1 >= 3 && nodes1 <= (dir1 ? 4 : 6)) { + } 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_i_isomorphic_small(graph1, graph2, iso)); + 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)); @@ -133,45 +128,16 @@ igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 0, 0)); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_isomorphic_34 - * \brief Graph isomorphism for 3-4 vertices (deprecated). - * - * \deprecated-by igraph_isomorphic 0.10.0 - * - * If you really care about performance and you \em know for sure that your - * input graphs are simple and have either 3 or 4 vertices for directed graphs, - * or 3-6 vertices for undirected graphs, you can compare their isomorphism - * classes obtained from \ref igraph_isoclass() directly instead of calling - * \ref igraph_isomorphic(); this saves the cost of checking whether the graphs - * do not contain multiple edges or self-loops. - * - * \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). - */ -igraph_error_t igraph_isomorphic_34( - const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso -) { - return igraph_i_isomorphic_small(graph1, graph2, iso); -} - -/** - * \function igraph_i_isomorphic_small - * \brief Graph isomorphism for small graphs. + * Graph isomorphism for 3-4 vertices * * This function uses precomputed indices to decide isomorphism - * problems for directed graphs with only 3 or 4 vertices, or for undirected - * graphs with 3, 4, 5 or 6 vertices. Multi-edges and self-loops are ignored by - * this function. - * + * 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. @@ -180,14 +146,14 @@ igraph_error_t igraph_isomorphic_34( * * Time complexity: O(1). */ -igraph_error_t igraph_i_isomorphic_small( - const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso -) { +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 IGRAPH_SUCCESS; + return 0; } /** @@ -211,7 +177,7 @@ igraph_error_t igraph_i_isomorphic_small( * * Time complexity: exponential. */ -igraph_error_t igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, +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/vendor/cigraph/src/isomorphism/vf2.c b/src/vendor/cigraph/src/isomorphism/vf2.c index 50f991ef383..ade96f50f8b 100644 --- a/src/vendor/cigraph/src/isomorphism/vf2.c +++ b/src/vendor/cigraph/src/isomorphism/vf2.c @@ -25,8 +25,8 @@ #include "igraph_adjlist.h" #include "igraph_interface.h" +#include "igraph_memory.h" #include "igraph_stack.h" -#include "igraph_structural.h" #include "core/interruption.h" @@ -53,35 +53,8 @@ * */ -static igraph_error_t igraph_i_perform_vf2_pre_checks( - const igraph_t* graph1, const igraph_t* graph2 -) { - igraph_bool_t has_loops; - - if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { - IGRAPH_ERROR("Cannot compare directed and undirected graphs", - IGRAPH_EINVAL); - } - - IGRAPH_CHECK(igraph_has_loop(graph1, &has_loops)); - if (!has_loops) { - IGRAPH_CHECK(igraph_has_loop(graph2, &has_loops)); - } - - if (has_loops) { - IGRAPH_ERROR("The VF2 algorithm does not support graphs with loop edges.", - IGRAPH_EINVAL); - } - - /* TODO: VF2 does not support graphs with multiple edges either, but we - * don't check for this as the check would be complex, comparable to - * the runtime of the algorithm itself */ - - return IGRAPH_SUCCESS; -} - /** - * \function igraph_get_isomorphisms_vf2_callback + * \function igraph_isomorphic_function_vf2 * The generic VF2 interface * * @@ -94,11 +67,8 @@ static igraph_error_t igraph_i_perform_vf2_pre_checks( * \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 \c IGRAPH_SUCCESS, then the search is continued, - * otherwise it stops. \c IGRAPH_STOP as a return value can be used to - * indicate normal premature termination; any other return value will be - * treated as an igraph error code, making the caller function return the - * same error code as well. The callback function must not destroy 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. @@ -135,31 +105,37 @@ static igraph_error_t igraph_i_perform_vf2_pre_checks( * Time complexity: exponential. */ -igraph_error_t igraph_get_isomorphisms_vf2_callback( - 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph1); - igraph_integer_t no_of_edges = igraph_ecount(graph1); - igraph_vector_int_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; - igraph_vector_int_t in_1, in_2, out_1, out_2; - igraph_integer_t in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0; +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; - igraph_integer_t matched_nodes = 0; - igraph_integer_t depth; - igraph_integer_t cand1, cand2; - igraph_integer_t last1, last2; - igraph_stack_int_t path; + 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_int_t indeg1, indeg2, outdeg1, outdeg2; - igraph_integer_t vsize; + igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; + long int vsize; - IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); + 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"); @@ -173,7 +149,7 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { - return IGRAPH_SUCCESS; + return 0; } if (vertex_color1) { @@ -192,11 +168,11 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( /* Check color distribution */ if (vertex_color1) { - igraph_bool_t ret = false; + int ret = 0; igraph_vector_int_t tmp1, tmp2; - IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, vertex_color1)); + IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, vertex_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); - IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp2, vertex_color2)); + 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); @@ -205,17 +181,17 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { - return IGRAPH_SUCCESS; + return 0; } } /* Check edge color distribution */ if (edge_color1) { - igraph_bool_t ret = false; + int ret = 0; igraph_vector_int_t tmp1, tmp2; - IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, edge_color1)); + IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, edge_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); - IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp2, edge_color2)); + 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); @@ -224,32 +200,32 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { - return IGRAPH_SUCCESS; + return 0; } } if (map12) { core_1 = map12; - IGRAPH_CHECK(igraph_vector_int_resize(core_1, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes)); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes); } - igraph_vector_int_fill(core_1, -1); + igraph_vector_fill(core_1, -1); if (map21) { core_2 = map21; - IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes)); - igraph_vector_int_null(core_2); + IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes)); + igraph_vector_null(core_2); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(core_2, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes); } - igraph_vector_int_fill(core_2, -1); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes); - IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &path); + 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)); @@ -258,12 +234,12 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( 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_INT_INIT_FINALLY(&indeg1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); + 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_int_reserve(&path, no_of_nodes * 2)); + 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(), @@ -275,7 +251,7 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( depth = 0; last1 = -1; last2 = -1; while (depth >= 0) { - igraph_integer_t i; + long int i; IGRAPH_ALLOW_INTERRUPTION(); @@ -357,8 +333,8 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( /**************************************************************/ /* dead end, step back, if possible. Otherwise we'll terminate */ if (depth >= 1) { - last2 = igraph_stack_int_pop(&path); - last1 = igraph_stack_int_pop(&path); + 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; @@ -376,48 +352,37 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( out_2_size += 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, last1); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_1)[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, last1); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1); vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[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, last2); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2); vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[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, last2); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2); vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[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; @@ -431,50 +396,48 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( } else { /**************************************************************/ /* step forward if worth, check if worth first */ - igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; - igraph_bool_t end = false; - - inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); - outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); - inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); - outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + 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 = true; + end = 1; } if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { - end = true; + end = 1; } - if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { - end = true; + 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++) { - igraph_integer_t node = VECTOR(*inneis_1)[i]; + long int node = (long int) VECTOR(*inneis_1)[i]; if (VECTOR(*core_1)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_1)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, node, cand1, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, node2, cand2, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -488,25 +451,27 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( } vsize = igraph_vector_int_size(outneis_1); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[i]; + long int node = (long int) VECTOR(*outneis_1)[i]; if (VECTOR(*core_1)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_1)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, cand1, node, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, cand2, node2, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -520,25 +485,27 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( } vsize = igraph_vector_int_size(inneis_2); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[i]; + long int node = (long int) VECTOR(*inneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_2)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -552,25 +519,27 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( } vsize = igraph_vector_int_size(outneis_2); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[i]; + long int node = (long int) VECTOR(*outneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_2)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -586,8 +555,8 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( if (!end && (xin1 == xin2 && xout1 == xout2)) { /* Ok, we add the (cand1, cand2) pair to the mapping */ depth += 1; - IGRAPH_CHECK(igraph_stack_int_push(&path, cand1)); - IGRAPH_CHECK(igraph_stack_int_push(&path, cand2)); + 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; @@ -606,54 +575,42 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( out_2_size -= 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_1)[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, cand1); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[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, cand2); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[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, cand2); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[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; @@ -663,58 +620,36 @@ igraph_error_t igraph_get_isomorphisms_vf2_callback( } if (matched_nodes == no_of_nodes && isohandler_fn) { - igraph_error_t ret; - IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); - if (ret == IGRAPH_STOP) { + if (!isohandler_fn(core_1, core_2, arg)) { break; } } } - igraph_vector_int_destroy(&outdeg2); - igraph_vector_int_destroy(&outdeg1); - igraph_vector_int_destroy(&indeg2); - igraph_vector_int_destroy(&indeg1); + 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_int_destroy(&path); - igraph_vector_int_destroy(&out_2); - igraph_vector_int_destroy(&out_1); - igraph_vector_int_destroy(&in_2); - igraph_vector_int_destroy(&in_1); + 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_int_destroy(core_2); + igraph_vector_destroy(core_2); IGRAPH_FINALLY_CLEAN(1); } if (!map12) { - igraph_vector_int_destroy(core_1); + igraph_vector_destroy(core_1); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_isomorphic_function_vf2 - * \brief The generic VF2 interface (deprecated alias). - * - * \deprecated-by igraph_get_isomorphisms_vf2_callback 0.10.0 - */ -igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -) { - return igraph_get_isomorphisms_vf2_callback( - graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, - map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg - ); + return 0; } typedef struct { @@ -742,24 +677,23 @@ static igraph_bool_t igraph_i_isocompat_edge_cb( return data->edge_compat_fn(graph1, graph2, g1_num, g2_num, data->carg); } -static igraph_error_t igraph_i_isomorphic_vf2_cb( - const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, - void *arg -) { +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 IGRAPH_STOP; + return 0; /* don't need to continue */ } /** * \function igraph_isomorphic_vf2 - * \brief Isomorphism via VF2. + * \brief Isomorphism via VF2 * * * This function performs the VF2 algorithm via calling \ref - * igraph_get_isomorphisms_vf2_callback(). + * igraph_isomorphic_function_vf2(). * * Note that this function cannot be used for * deciding subgraph isomorphism, use \ref igraph_subisomorphic_vf2() @@ -808,13 +742,13 @@ static igraph_error_t igraph_i_isomorphic_vf2_cb( * \example examples/simple/igraph_isomorphic_vf2.c */ -igraph_error_t igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +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_int_t *map12, - igraph_vector_int_t *map21, + 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) { @@ -823,42 +757,42 @@ igraph_error_t igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *gra 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_get_isomorphisms_vf2_callback(graph1, graph2, + 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_cb, + (igraph_isohandler_t*) + igraph_i_isomorphic_vf2, ncb, ecb, &data)); if (! *iso) { if (map12) { - igraph_vector_int_clear(map12); + igraph_vector_clear(map12); } if (map21) { - igraph_vector_int_clear(map21); + igraph_vector_clear(map21); } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_count_isomorphisms_vf2_cb( - const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, - void *arg -) { +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 IGRAPH_SUCCESS; + return 1; /* always continue */ } /** * \function igraph_count_isomorphisms_vf2 - * \brief Number of isomorphisms via VF2. + * Number of isomorphisms via VF2 * * This function counts the number of isomorphic mappings between two - * graphs. It uses the generic \ref igraph_get_isomorphisms_vf2_callback() + * 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. @@ -885,12 +819,10 @@ static igraph_error_t igraph_i_count_isomorphisms_vf2_cb( * \p edge_compat_fn. * \return Error code. * - * \sa igraph_count_automorphisms() - * * Time complexity: exponential. */ -igraph_error_t igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +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, @@ -906,23 +838,50 @@ igraph_error_t igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igrap 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_get_isomorphisms_vf2_callback(graph1, graph2, + 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_cb, + (igraph_isohandler_t*) + igraph_i_count_isomorphisms_vf2, ncb, ecb, &data)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_store_mapping_vf2_cb( - const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, - void *arg -) { +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_int_list_t *ptrvector = data->arg; + igraph_vector_ptr_t *ptrvector = data->arg; + igraph_vector_t *newvector = IGRAPH_CALLOC(1, igraph_vector_t); IGRAPH_UNUSED(map12); - return igraph_vector_int_list_push_back_copy(ptrvector, map21); + 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; } /** @@ -930,7 +889,7 @@ static igraph_error_t igraph_i_store_mapping_vf2_cb( * \brief Collect all isomorphic mappings of two graphs. * * This function finds all the isomorphic mappings between two simple - * graphs. It uses the \ref igraph_get_isomorphisms_vf2_callback() + * 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. @@ -948,10 +907,15 @@ static igraph_error_t igraph_i_store_mapping_vf2_cb( * 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 to a list of integer vectors. On return it is empty if - * the input graphs are not isomorphic. Otherwise it contains pointers to - * \ref igraph_vector_int_t objects, each vector is an - * isomorphic mapping of \p graph2 to \p graph1. + * \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. @@ -965,13 +929,13 @@ static igraph_error_t igraph_i_store_mapping_vf2_cb( * Time complexity: exponential. */ -igraph_error_t igraph_get_isomorphisms_vf2(const igraph_t *graph1, +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_int_list_t *maps, + igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -980,21 +944,24 @@ igraph_error_t igraph_get_isomorphisms_vf2(const igraph_t *graph1, 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_int_list_clear(maps); - IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, + 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_store_mapping_vf2_cb, + (igraph_isohandler_t*) + igraph_i_get_isomorphisms_vf2, ncb, ecb, &data)); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** - * \function igraph_get_subisomorphisms_vf2_callback - * \brief Generic VF2 function for subgraph isomorphism problems. + * \function igraph_subisomorphic_function_vf2 + * Generic VF2 function for subgraph isomorphism problems * - * This function is the pair of \ref igraph_get_isomorphisms_vf2_callback(), + * 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. @@ -1024,10 +991,9 @@ igraph_error_t igraph_get_isomorphisms_vf2(const igraph_t *graph1, * 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 \c IGRAPH_SUCCESS, - * then the search is continued. If the function returns \c IGRAPH_STOP, - * the search is terminated normally. Any other value is treated as an - * igraph error code. + * 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. @@ -1041,36 +1007,44 @@ igraph_error_t igraph_get_isomorphisms_vf2(const igraph_t *graph1, * Time complexity: exponential. */ -igraph_error_t igraph_get_subisomorphisms_vf2_callback( - 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -) { - - igraph_integer_t no_of_nodes1 = igraph_vcount(graph1), +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); - igraph_integer_t no_of_edges1 = igraph_ecount(graph1), + long int no_of_edges1 = igraph_ecount(graph1), no_of_edges2 = igraph_ecount(graph2); - igraph_vector_int_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; - igraph_vector_int_t in_1, in_2, out_1, out_2; - igraph_integer_t in_1_size = 0, in_2_size = 0, out_1_size = 0, out_2_size = 0; + 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; - igraph_integer_t matched_nodes = 0; - igraph_integer_t depth; - igraph_integer_t cand1, cand2; - igraph_integer_t last1, last2; - igraph_stack_int_t path; + 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_int_t indeg1, indeg2, outdeg1, outdeg2; - igraph_integer_t vsize; + igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; + long int vsize; - IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); + 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 IGRAPH_SUCCESS; + if (no_of_nodes1 < no_of_nodes2 || + no_of_edges1 < no_of_edges2) { + return 0; } if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { @@ -1109,24 +1083,24 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( if (map12) { core_1 = map12; - IGRAPH_CHECK(igraph_vector_int_resize(core_1, no_of_nodes1)); + IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes1)); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes1); + IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes1); } - igraph_vector_int_fill(core_1, -1); + igraph_vector_fill(core_1, -1); if (map21) { core_2 = map21; - IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes2)); + IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes2)); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(core_2, no_of_nodes2); + IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes2); } - igraph_vector_int_fill(core_2, -1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes2); - IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &path); + 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)); @@ -1135,12 +1109,12 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( 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_INT_INIT_FINALLY(&indeg1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); + 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_int_reserve(&path, no_of_nodes2 * 2)); + 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(), @@ -1152,7 +1126,7 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( depth = 0; last1 = -1; last2 = -1; while (depth >= 0) { - igraph_integer_t i; + long int i; IGRAPH_ALLOW_INTERRUPTION(); @@ -1234,8 +1208,8 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( /**************************************************************/ /* dead end, step back, if possible. Otherwise we'll terminate */ if (depth >= 1) { - last2 = igraph_stack_int_pop(&path); - last1 = igraph_stack_int_pop(&path); + 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; @@ -1253,48 +1227,37 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( out_2_size += 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, last1); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_1)[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, last1); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) last1); vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[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, last2); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) last2); vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[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, last2); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) last2); vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[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; @@ -1308,32 +1271,28 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( } else { /**************************************************************/ /* step forward if worth, check if worth first */ - igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; - igraph_bool_t end = false; - - inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); - outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); - inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); - outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + 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 = true; + end = 1; } if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { - end = true; + end = 1; } - if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { - end = true; + 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++) { - igraph_integer_t node = VECTOR(*inneis_1)[i]; + long int node = (long int) VECTOR(*inneis_1)[i]; if (VECTOR(*core_1)[node] < 0) { if (VECTOR(in_1)[node] != 0) { xin1++; @@ -1345,7 +1304,7 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( } vsize = igraph_vector_int_size(outneis_1); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[i]; + long int node = (long int) VECTOR(*outneis_1)[i]; if (VECTOR(*core_1)[node] < 0) { if (VECTOR(in_1)[node] != 0) { xin1++; @@ -1357,25 +1316,27 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( } vsize = igraph_vector_int_size(inneis_2); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[i]; + long int node = (long int) VECTOR(*inneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_2)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -1389,25 +1350,27 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( } vsize = igraph_vector_int_size(outneis_2); for (i = 0; !end && i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[i]; + long int node = (long int) VECTOR(*outneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - igraph_integer_t node2 = VECTOR(*core_2)[node]; + 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 = true; + end = 1; } else if (edge_color1 || edge_compat_fn) { igraph_integer_t eid1, eid2; - igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, - /*error=*/ true); - igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, - /*error=*/ true); - if (edge_color1 && VECTOR(*edge_color1)[eid1] != - VECTOR(*edge_color2)[eid2]) { - end = true; + 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 = true; + end = 1; } } } else { @@ -1423,8 +1386,8 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( if (!end && (xin1 >= xin2 && xout1 >= xout2)) { /* Ok, we add the (cand1, cand2) pair to the mapping */ depth += 1; - IGRAPH_CHECK(igraph_stack_int_push(&path, cand1)); - IGRAPH_CHECK(igraph_stack_int_push(&path, cand2)); + 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; @@ -1443,54 +1406,42 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( out_2_size -= 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); - IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); - + inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_1)[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, cand1); - IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); - + outneis_1 = igraph_lazy_adjlist_get(&outadj1, (igraph_integer_t) cand1); vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_1)[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, cand2); - IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); - + inneis_2 = igraph_lazy_adjlist_get(&inadj2, (igraph_integer_t) cand2); vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*inneis_2)[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, cand2); - IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); - + outneis_2 = igraph_lazy_adjlist_get(&outadj2, (igraph_integer_t) cand2); vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - igraph_integer_t node = VECTOR(*outneis_2)[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; @@ -1500,69 +1451,47 @@ igraph_error_t igraph_get_subisomorphisms_vf2_callback( } if (matched_nodes == no_of_nodes2 && isohandler_fn) { - igraph_error_t ret; - IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); - if (ret == IGRAPH_STOP) { + if (!isohandler_fn(core_1, core_2, arg)) { break; } } } - igraph_vector_int_destroy(&outdeg2); - igraph_vector_int_destroy(&outdeg1); - igraph_vector_int_destroy(&indeg2); - igraph_vector_int_destroy(&indeg1); + 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_int_destroy(&path); - igraph_vector_int_destroy(&out_2); - igraph_vector_int_destroy(&out_1); - igraph_vector_int_destroy(&in_2); - igraph_vector_int_destroy(&in_1); + 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_int_destroy(core_2); + igraph_vector_destroy(core_2); IGRAPH_FINALLY_CLEAN(1); } if (!map12) { - igraph_vector_int_destroy(core_1); + igraph_vector_destroy(core_1); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_subisomorphic_vf2_cb( - const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, - void *arg -) { +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 IGRAPH_STOP; -} - -/** - * \function igraph_subisomorphic_function_vf2 - * \brief Generic VF2 function for subgraph isomorphism problems (deprecated alias). - * - * \deprecated-by igraph_get_subisomorphisms_vf2_callback 0.10.0 - */ -igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, - igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, - igraph_isocompat_t *edge_compat_fn, void *arg -) { - return igraph_get_subisomorphisms_vf2_callback( - graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, - map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg - ); + return 0; /* stop */ } /** @@ -1570,7 +1499,7 @@ igraph_error_t igraph_subisomorphic_function_vf2( * Decide subgraph isomorphism using VF2 * * Decides whether a subgraph of \p graph1 is isomorphic to \p - * graph2. It uses \ref igraph_get_subisomorphisms_vf2_callback(). + * 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 @@ -1608,13 +1537,13 @@ igraph_error_t igraph_subisomorphic_function_vf2( * Time complexity: exponential. */ -igraph_error_t igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +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_int_t *map12, - igraph_vector_int_t *map21, + 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) { @@ -1624,32 +1553,33 @@ igraph_error_t igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t * igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *iso = 0; - IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, + 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_cb, + (igraph_isohandler_t *) + igraph_i_subisomorphic_vf2, ncb, ecb, &data)); if (! *iso) { if (map12) { - igraph_vector_int_clear(map12); + igraph_vector_clear(map12); } if (map21) { - igraph_vector_int_clear(map21); + igraph_vector_clear(map21); } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_count_subisomorphisms_vf2_cb( - const igraph_vector_int_t *map12, const igraph_vector_int_t *map21, - void *arg -) { +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 IGRAPH_SUCCESS; + return 1; /* always continue */ } /** @@ -1657,7 +1587,8 @@ static igraph_error_t igraph_i_count_subisomorphisms_vf2_cb( * Number of subgraph isomorphisms using VF2 * * Count the number of isomorphisms between subgraphs of \p graph1 and - * \p graph2. This function uses \ref igraph_get_subisomorphisms_vf2_callback(). + * \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 @@ -1690,7 +1621,7 @@ static igraph_error_t igraph_i_count_subisomorphisms_vf2_cb( * Time complexity: exponential. */ -igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +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, @@ -1706,22 +1637,59 @@ igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const ig 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_get_subisomorphisms_vf2_callback(graph1, graph2, + 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_cb, + (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_get_subisomorphisms_vf2_callback() function. The graphs should be simple. + * 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 @@ -1739,9 +1707,14 @@ igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const ig * 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 to a list of integer vectors. On return it contains - * pointers to \ref igraph_vector_int_t objects, each vector is an isomorphic - * mapping of \p graph2 to a subgraph of \p graph1. + * \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. @@ -1755,13 +1728,13 @@ igraph_error_t igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const ig * Time complexity: exponential. */ -igraph_error_t igraph_get_subisomorphisms_vf2(const igraph_t *graph1, +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_int_list_t *maps, + igraph_vector_ptr_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -1770,13 +1743,15 @@ igraph_error_t igraph_get_subisomorphisms_vf2(const igraph_t *graph1, 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_int_list_clear(maps); - IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, + 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_store_mapping_vf2_cb, + (igraph_isohandler_t*) + igraph_i_get_subisomorphisms_vf2, ncb, ecb, &data)); - + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/circular.c b/src/vendor/cigraph/src/layout/circular.c index ed1bad318ca..dc2019a147b 100644 --- a/src/vendor/cigraph/src/layout/circular.c +++ b/src/vendor/cigraph/src/layout/circular.c @@ -31,24 +31,26 @@ /** * \ingroup layout * \function igraph_layout_circle - * \brief Places the vertices uniformly on a circle in arbitrary order. + * \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 to place vertices in the - * order of their vertex IDs. + * \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. + * Time complexity: O(|V|), the + * number of vertices. */ -igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, +int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t order) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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)); @@ -56,16 +58,16 @@ igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); igraph_matrix_null(res); - IGRAPH_CHECK(igraph_vit_create(graph, order, &vit)); - for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + 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; - igraph_integer_t idx = IGRAPH_VIT_GET(vit); + int idx = IGRAPH_VIT_GET(vit); MATRIX(*res, idx, 0) = cos(phi); MATRIX(*res, idx, 1) = sin(phi); } igraph_vit_destroy(&vit); - return IGRAPH_SUCCESS; + return 0; } /** @@ -81,22 +83,26 @@ igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, * minus 1. * \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. + * 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. */ -igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t center, const igraph_vector_int_t *order) { +int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_t *order) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int c = center; + long int i; + igraph_real_t step; + igraph_real_t phi; if (no_of_nodes > 0 && (center < 0 || center >= no_of_nodes)) { IGRAPH_ERROR("The given center is not a vertex of the graph.", IGRAPH_EINVAL); } - if (order && igraph_vector_int_size(order) != no_of_nodes) { + if (order && igraph_vector_size(order) != no_of_nodes) { IGRAPH_ERROR("Invalid order vector length.", IGRAPH_EINVAL); } @@ -105,40 +111,34 @@ igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, if (no_of_nodes == 1) { MATRIX(*res, 0, 0) = MATRIX(*res, 0, 1) = 0.0; } else if (no_of_nodes > 1) { - igraph_real_t phi = 0.0; - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t node = order ? VECTOR(*order)[i] : i; + 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 != center) { + if (node != c) { MATRIX(*res, node, 0) = cos(phi); MATRIX(*res, node, 1) = sin(phi); - phi += 2.0 * M_PI / (no_of_nodes - 1); + phi += step; } else { MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0; } } } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_layout_sphere * \brief Places vertices (more or less) uniformly on a sphere. * - * The vertices are placed with approximately equal spacing on a spiral - * wrapped around a sphere, in the order of their vertex IDs. Vertices - * with consecutive vertex IDs are placed near each other. - * * * 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. https://doi.org/10.1007/BF03024331 + * 5--11. * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will @@ -150,39 +150,39 @@ igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, * * Time complexity: O(|V|), the number of vertices in the graph. */ -igraph_error_t igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { +int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_real_t sqrt_no_of_nodes = sqrt(no_of_nodes); + 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)); - igraph_real_t phi = 0; - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_real_t r, z; - - /* The first and last point are handled separately to avoid - * division by zero or 1-z*z becoming slightly negative due - * to roundoff errors. */ - if (i == 0) { - z = -1; r = 0; - } else if (i == no_of_nodes-1) { - z = 1; r = 0; - } else { - z = -1.0 + 2.0 * i / (no_of_nodes - 1); - r = sqrt(1 - z*z); - phi += 3.6 / (sqrt_no_of_nodes*r); - } - - igraph_real_t x = r*cos(phi); - igraph_real_t y = r*sin(phi); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/davidson_harel.c b/src/vendor/cigraph/src/layout/davidson_harel.c index b0ac699cc8b..57b706f3bdc 100644 --- a/src/vendor/cigraph/src/layout/davidson_harel.c +++ b/src/vendor/cigraph/src/layout/davidson_harel.c @@ -26,45 +26,45 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "core/interruption.h" #include "core/math.h" +#include "core/interruption.h" #include "layout/layout_internal.h" #include /* not 'static', used in tests */ -igraph_bool_t igraph_i_layout_segments_intersect(igraph_real_t p0_x, igraph_real_t p0_y, - igraph_real_t p1_x, igraph_real_t p1_y, - igraph_real_t p2_x, igraph_real_t p2_y, - igraph_real_t p3_x, igraph_real_t p3_y) { - igraph_real_t s1_x = p1_x - p0_x; - igraph_real_t s1_y = p1_y - p0_y; - igraph_real_t s2_x = p3_x - p2_x; - igraph_real_t s2_y = p3_y - p2_y; - - igraph_real_t s1, s2, t1, t2, s, t; +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 false; + 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; + return s >= 0 && s <= 1 && t >= 0 && t <= 1 ? 1 : 0; } /* not 'static', used in tests */ -igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real_t v_y, - igraph_real_t u1_x, igraph_real_t u1_y, - igraph_real_t u2_x, igraph_real_t u2_y) { - - igraph_real_t dx = u2_x - u1_x; - igraph_real_t dy = u2_y - u1_y; - igraph_real_t l2 = dx * dx + dy * dy; - igraph_real_t t, p_x, p_y; +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); } @@ -81,13 +81,12 @@ igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real /** * \function igraph_layout_davidson_harel - * \brief Davidson-Harel layout algorithm. + * 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. - * https://doi.org/10.1145/234535.234538 * * * The algorithm uses simulated annealing and a sophisticated @@ -114,8 +113,7 @@ igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real * \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 \c n is the - * number of vertices. + * 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. @@ -140,7 +138,7 @@ igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real * */ -igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, +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, @@ -150,58 +148,62 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix igraph_integer_t no_nodes = igraph_vcount(graph); igraph_integer_t no_edges = igraph_ecount(graph); - igraph_real_t width = sqrt(no_nodes) * 10, height = width; + float width = sqrt(no_nodes) * 10, height = width; igraph_vector_int_t perm; - igraph_bool_t fine_tuning = false; - igraph_vector_t try_x, try_y; + 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; - igraph_real_t move_radius = width / 2; - igraph_real_t fine_tuning_factor = 0.01; - igraph_vector_int_t neis; - igraph_real_t min_x = width / 2, max_x = -width / 2, min_y = height / 2, max_y = -height / 2; + 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; - igraph_real_t w_node_dist = weight_node_dist ; /* 1.0 */ - igraph_real_t w_borderlines = weight_border; /* 0.0 */ - igraph_real_t w_edge_lengths = weight_edge_lengths; /* 0.0001; */ - igraph_real_t w_edge_crossings = weight_edge_crossings; /* 1.0 */ - igraph_real_t w_node_edge_dist = weight_node_edge_dist; /* 0.2 */ - + 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 not be negative for the Davidson-Harel layout.", IGRAPH_EINVAL); + 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 not be negative for the Davidson-Harel layout.", - IGRAPH_EINVAL); + 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) for the Davidson-Harel layout.", IGRAPH_EINVAL); - } - if (use_seed) { - if (igraph_matrix_nrow(res) != no_nodes || igraph_matrix_ncol(res) != 2) { - IGRAPH_ERROR("Invalid start position matrix size in Davidson-Harel layout.", IGRAPH_EINVAL); - } - } else { - IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); + IGRAPH_ERROR("Cooling factor must be in (0,1) in " + "Davidson-Harel layout", IGRAPH_EINVAL); } if (no_nodes == 0) { - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); + IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); - IGRAPH_VECTOR_INIT_FINALLY(&try_x, no_tries); - IGRAPH_VECTOR_INIT_FINALLY(&try_y, no_tries); - IGRAPH_CHECK(igraph_vector_int_init_range(&try_idx, 0, no_tries)); + 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_INT_INIT_FINALLY(&neis, 100); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 100); RNG_BEGIN(); if (!use_seed) { - for (igraph_integer_t i = 0; i < no_nodes; i++) { - igraph_real_t x, y; + 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) { @@ -218,9 +220,9 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } else { min_x = IGRAPH_INFINITY; max_x = IGRAPH_NEGINFINITY; min_y = IGRAPH_INFINITY; max_y = IGRAPH_NEGINFINITY; - for (igraph_integer_t i = 0; i < no_nodes; i++) { - igraph_real_t x = MATRIX(*res, i, 0); - igraph_real_t y = MATRIX(*res, i, 1); + 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) { @@ -234,37 +236,39 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } } - for (igraph_integer_t i = 0; i < no_tries; i++) { - double phi = 2 * M_PI / no_tries * i; + 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 (igraph_integer_t round = 0; round < maxiter + fineiter; round++) { - IGRAPH_ALLOW_INTERRUPTION(); - + 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) { - igraph_real_t fx = fine_tuning_factor * (max_x - min_x); - igraph_real_t fy = fine_tuning_factor * (max_y - min_y); + 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 (igraph_integer_t p = 0; p < no_nodes; p++) { + 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 (igraph_integer_t t = 0; t < no_tries; t++) { - igraph_real_t diff_energy = 0.0; - igraph_integer_t ti = VECTOR(try_idx)[t]; + for (t = 0; t < no_tries; t++) { + float diff_energy = 0.0; + int ti = VECTOR(try_idx)[t]; /* Try moving it */ - igraph_real_t old_x = MATRIX(*res, v, 0); - igraph_real_t old_y = MATRIX(*res, v, 1); - igraph_real_t new_x = old_x + move_radius * VECTOR(try_x)[ti]; - igraph_real_t new_y = old_y + move_radius * VECTOR(try_y)[ti]; + 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; @@ -280,8 +284,9 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } if (w_node_dist != 0) { - for (igraph_integer_t u = 0; u < no_nodes; u++) { - igraph_real_t odx, ody, odist2, dx, dy, dist2; + igraph_integer_t u; + for (u = 0; u < no_nodes; u++) { + float odx, ody, odist2, dx, dy, dist2; if (u == v) { continue; } @@ -296,10 +301,10 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } if (w_borderlines != 0) { - igraph_real_t odx1 = width / 2 - old_x, odx2 = old_x + width / 2; - igraph_real_t ody1 = height / 2 - old_y, ody2 = old_y + height / 2; - igraph_real_t dx1 = width / 2 - new_x, dx2 = new_x + width / 2; - igraph_real_t dy1 = height / 2 - new_y, dy2 = new_y + height / 2; + 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) { @@ -329,34 +334,34 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } if (w_edge_lengths != 0) { - IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - igraph_integer_t len = igraph_vector_int_size(&neis); - for (igraph_integer_t j = 0; j < len; j++) { + 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]; - igraph_real_t odx = old_x - MATRIX(*res, u, 0); - igraph_real_t ody = old_y - MATRIX(*res, u, 1); - igraph_real_t odist2 = odx * odx + ody * ody; - igraph_real_t dx = new_x - MATRIX(*res, u, 0); - igraph_real_t dy = new_y - MATRIX(*res, u, 1); - igraph_real_t dist2 = dx * dx + dy * dy; + 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 no = 0; - - IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - igraph_integer_t len = igraph_vector_int_size(&neis); - for (igraph_integer_t j = 0; j < len; j++) { + 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]; - igraph_real_t u_x = MATRIX(*res, u, 0); - igraph_real_t u_y = MATRIX(*res, u, 1); + 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); - igraph_real_t u1_x, u1_y, u2_x, u2_y; + float u1_x, u1_y, u2_x, u2_y; if (u1 == v || u2 == v || u1 == u || u2 == u) { continue; } @@ -374,11 +379,13 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix } if (w_node_edge_dist != 0 && fine_tuning) { + igraph_integer_t e, no; + /* All non-incident edges from the moved 'v' */ - for (igraph_integer_t e = 0; e < no_edges; e++) { + for (e = 0; e < no_edges; e++) { igraph_integer_t u1 = IGRAPH_FROM(graph, e); igraph_integer_t u2 = IGRAPH_TO(graph, e); - igraph_real_t u1_x, u1_y, u2_x, u2_y, d_ev; + float u1_x, u1_y, u2_x, u2_y, d_ev; if (u1 == v || u2 == v) { continue; } @@ -386,34 +393,35 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix 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); + 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); + 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_CHECK(igraph_incident(graph, &neis, v, IGRAPH_ALL)); - igraph_integer_t no = igraph_vector_int_size(&neis); - for (igraph_integer_t e = 0; e < no; e++) { + 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); - igraph_real_t u_x = MATRIX(*res, u, 0); - igraph_real_t u_y = MATRIX(*res, u, 1); - for (igraph_integer_t w = 0; w < no_nodes; w++) { - igraph_real_t w_x, w_y, d_ev; + 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); + 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); + 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; } } @@ -445,12 +453,12 @@ igraph_error_t igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix RNG_END(); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); igraph_vector_int_destroy(&try_idx); - igraph_vector_destroy(&try_x); - igraph_vector_destroy(&try_y); + igraph_vector_float_destroy(&try_x); + igraph_vector_float_destroy(&try_y); igraph_vector_int_destroy(&perm); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp b/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp index dd17d34aa74..1678db37b7a 100644 --- a/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp +++ b/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp @@ -35,10 +35,10 @@ #include "drl_Node.h" #include "DensityGrid.h" +#include "igraph_error.h" -#include #include -#include +#include using namespace std; @@ -65,9 +65,20 @@ DensityGrid::~DensityGrid () { void DensityGrid::Init() { - Density = new float[GRID_SIZE][GRID_SIZE]; - fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1]; - Bins = new deque[GRID_SIZE * GRID_SIZE]; + 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; @@ -177,7 +188,9 @@ void DensityGrid::Subtract(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - throw runtime_error("Exceeded density grid in DrL."); + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; #endif } @@ -219,7 +232,9 @@ void DensityGrid::Add(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - throw runtime_error("Exceeded density grid in DrL."); + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; #endif } diff --git a/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp b/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp index 19569cde1c1..0f02f1537a8 100644 --- a/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp @@ -35,10 +35,10 @@ #include "drl_Node_3d.h" #include "DensityGrid_3d.h" +#include "igraph_error.h" -#include #include -#include +#include using namespace std; @@ -65,9 +65,20 @@ DensityGrid::~DensityGrid () { void DensityGrid::Init() { - 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]; + 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; @@ -192,7 +203,9 @@ void DensityGrid::Subtract(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - throw runtime_error("Exceeded density grid in DrL."); + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; #endif } @@ -239,7 +252,9 @@ void DensityGrid::Add(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - throw runtime_error("Exceeded density grid in DrL."); + igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, + __LINE__, IGRAPH_EDRL); + return; #endif } diff --git a/src/vendor/cigraph/src/layout/drl/drl_Node.h b/src/vendor/cigraph/src/layout/drl/drl_Node.h index 387bbb293c8..bc894c23f7b 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_Node.h +++ b/src/vendor/cigraph/src/layout/drl/drl_Node.h @@ -33,8 +33,6 @@ #ifndef __NODE_H__ #define __NODE_H__ -#include - // The node class contains information about a given node for // use by the density server process. @@ -48,16 +46,16 @@ class Node { public: bool fixed; // if true do not change the - igraph_integer_t id; - // position of this node + int id; + float x, y; float sub_x, sub_y; float energy; public: - Node( igraph_integer_t node_id ) { + Node( int node_id ) { x = y = 0.0; fixed = false; id = node_id; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h b/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h index 250e934a366..e373d9a65e9 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h +++ b/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h @@ -33,8 +33,6 @@ #ifndef __NODE_H__ #define __NODE_H__ -#include - // The node class contains information about a given node for // use by the density server process. @@ -48,16 +46,16 @@ class Node { public: bool fixed; // if true do not change the - igraph_integer_t id; - // position of this node + int id; + float x, y, z; float sub_x, sub_y, sub_z; float energy; public: - Node( igraph_integer_t node_id ) { + Node( int node_id ) { x = y = z = 0.0; fixed = false; id = node_id; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph.cpp b/src/vendor/cigraph/src/layout/drl/drl_graph.cpp index f764409ec62..e96bb80331a 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_graph.cpp @@ -175,11 +175,11 @@ graph::graph(const igraph_t *igraph, // scan .int file for node info highest_sim = 1.0; num_nodes = igraph_vcount(igraph); - igraph_integer_t no_of_edges = igraph_ecount(igraph); - for (igraph_integer_t i = 0; i < num_nodes; i++) { + long int no_of_edges = igraph_ecount(igraph); + for (long int i = 0; i < num_nodes; i++) { id_catalog[i] = 1; } - map::iterator cat_iter; + 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; @@ -194,9 +194,9 @@ graph::graph(const igraph_t *igraph, } // read .int file for graph info - igraph_integer_t node_1, node_2; - igraph_real_t weight; - for (igraph_integer_t i = 0; i < no_of_edges; i++) { + 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 ; @@ -395,17 +395,17 @@ void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { cut_rate = ( cut_length_start - cut_length_end ) / 400.0; // finally set the number of iterations to leave .real coords fixed - igraph_integer_t full_comp_iters; + 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 = (igraph_integer_t)real_parm; + real_iterations = (int)real_parm; } else if ( real_parm == 1) { real_iterations = full_comp_iters + simmer.iterations + 100; } else { - real_iterations = (igraph_integer_t)(real_parm * full_comp_iters); + real_iterations = (int)(real_parm * full_comp_iters); } tot_iterations = 0; @@ -488,12 +488,13 @@ void graph::init_parms(const igraph_layout_drl_options_t *options) { // real_in.close(); // } -int graph::read_real(const igraph_matrix_t *real_mat) { - igraph_integer_t n = igraph_matrix_nrow(real_mat); - for (igraph_integer_t i = 0; i < n; i++) { +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 = false; + positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false; if ( real_iterations > 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); @@ -818,7 +819,7 @@ int graph::ReCompute( ) { void graph::update_nodes ( ) { - vector node_indices; // node list of nodes currently being updated + 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 @@ -831,9 +832,9 @@ void graph::update_nodes ( ) { // next we calculate the number of nodes there would be if the // num_nodes by num_procs schedule grid were perfectly square - igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); - for ( igraph_integer_t i = myid; i < square_num_nodes; i += num_procs ) { + for ( int i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); @@ -844,7 +845,7 @@ void graph::update_nodes ( ) { if ( i < num_nodes ) { // advance random sequence according to myid - for ( size_t j = 0; j < 2 * myid; j++ ) { + for ( int j = 0; j < 2 * myid; j++ ) { RNG_UNIF01(); } // rand(); @@ -855,7 +856,7 @@ void graph::update_nodes ( ) { } // advance random sequence for next iteration - for ( size_t j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { RNG_UNIF01(); } // rand(); @@ -863,7 +864,7 @@ void graph::update_nodes ( ) { } else { // advance random sequence according to use by // the other processors - for ( size_t j = 0; j < 2 * (node_indices.size()); j++ ) { + for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { RNG_UNIF01(); } //rand(); @@ -871,7 +872,7 @@ void graph::update_nodes ( ) { // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; - for ( size_t j = 0; j < node_indices.size (); j++ ) + for ( unsigned int j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { all_fixed = false; } @@ -898,7 +899,7 @@ void graph::update_nodes ( ) { */ // compute node list for next update - for ( size_t j = 0; j < node_indices.size(); j++ ) { + for ( unsigned int j = 0; j < node_indices.size(); j++ ) { node_indices [j] += num_procs; } @@ -919,11 +920,11 @@ void graph::update_nodes ( ) { // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. -void graph::get_positions ( vector &node_indices, +void graph::get_positions ( vector &node_indices, float return_positions[2 * MAX_PROCS] ) { // fill positions - for (size_t i = 0; i < node_indices.size(); i++) { + 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; } @@ -935,7 +936,7 @@ void graph::get_positions ( vector &node_indices, // of active processes at this level for use by the random number // generators. -void graph::update_node_pos ( igraph_integer_t node_ind, +void graph::update_node_pos ( int node_ind, float old_positions[2 * MAX_PROCS], float new_positions[2 * MAX_PROCS] ) { @@ -1007,13 +1008,13 @@ void graph::update_node_pos ( igraph_integer_t node_ind, // updates the positions by subtracting the old positions and adding the // new positions to the density grid. -void graph::update_density ( vector &node_indices, +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 ( size_t i = 0; i < node_indices.size(); i++ ) { + 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]], @@ -1033,13 +1034,13 @@ void graph::update_density ( vector &node_indices, * original code by B. Wylie. * *********************************************/ -float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { +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; + map ::iterator EI; float x_dis, y_dis; float energy_distance, weight; float node_energy = 0; @@ -1090,9 +1091,9 @@ float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { * originally written by B. Wylie * *********************************************/ -void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_y ) { +void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) { - map ::iterator EI; + map ::iterator EI; float total_weight = 0; float x_dis, y_dis, x_cen = 0, y_cen = 0; float x = 0, y = 0, dis; @@ -1133,7 +1134,7 @@ void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_ float num_connections = sqrt((double)neighbors[node_ind].size()); float maxLength = 0; - map::iterator maxIndex; + map::iterator maxIndex; // Go through nodes edges... cutting if necessary for (EI = maxIndex = neighbors[node_ind].begin(); @@ -1291,9 +1292,9 @@ int graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } - igraph_integer_t n = positions.size(); + long int n = positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); - for (size_t i = 0; i < n; i++) { + for (long int i = 0; i < n; i++) { MATRIX(*res, i, 0) = positions[i].x; MATRIX(*res, i, 1) = positions[i].y; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph.h b/src/vendor/cigraph/src/layout/drl/drl_graph.h index df1424f9432..1a2804dba59 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph.h +++ b/src/vendor/cigraph/src/layout/drl/drl_graph.h @@ -45,7 +45,7 @@ namespace drl { // layout schedule information struct layout_schedule { - igraph_integer_t iterations; + int iterations; float temperature; float attraction; float damping_mult; @@ -61,7 +61,8 @@ class graph { 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 ); + 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 ); @@ -82,13 +83,13 @@ class graph { // Methods int ReCompute ( ); void update_nodes ( ); - float Compute_Node_Energy ( igraph_integer_t node_ind ); - void Solve_Analytic ( igraph_integer_t 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 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 ( igraph_integer_t node_ind, + void update_node_pos ( int node_ind, float old_positions[2 * MAX_PROCS], float new_positions[2 * MAX_PROCS] ); @@ -96,18 +97,17 @@ class graph { int myid, num_procs; // graph decomposition information - igraph_integer_t num_nodes; // number of nodes in graph + 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. + 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; - igraph_integer_t iterations; + 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; @@ -123,9 +123,9 @@ class graph { time_t start_time, stop_time; // online clustering information - igraph_integer_t real_iterations; // number of iterations to hold .real input fixed - igraph_integer_t tot_iterations; - igraph_integer_t tot_expected_iterations; // for progress bar + int real_iterations; // number of iterations to hold .real input fixed + int tot_iterations; + int tot_expected_iterations; // for progress bar bool real_fixed; }; diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp index 27be46161e4..fe9d21a103d 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp @@ -98,11 +98,11 @@ graph::graph(const igraph_t *igraph, // scan .int file for node info highest_sim = 1.0; num_nodes = igraph_vcount(igraph); - igraph_integer_t no_of_edges = igraph_ecount(igraph); - for (igraph_integer_t i = 0; i < num_nodes; i++) { + long int no_of_edges = igraph_ecount(igraph); + for (long int i = 0; i < num_nodes; i++) { id_catalog[i] = 1; } - map< igraph_integer_t, igraph_integer_t>::iterator cat_iter; + 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; @@ -117,9 +117,9 @@ graph::graph(const igraph_t *igraph, } // read .int file for graph info - igraph_integer_t node_1, node_2; - igraph_real_t weight; - for (igraph_integer_t i = 0; i < no_of_edges; i++) { + 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 ; @@ -156,7 +156,7 @@ void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { cut_rate = ( cut_length_start - cut_length_end ) / 400.0; // finally set the number of iterations to leave .real coords fixed - igraph_integer_t full_comp_iters; + int full_comp_iters; full_comp_iters = liquid.iterations + expansion.iterations + cooldown.iterations + crunch.iterations + 3; @@ -200,13 +200,14 @@ void graph::init_parms(const igraph_layout_drl_options_t *options) { init_parms(rand_seed, options->edge_cut, real_in); } -int graph::read_real(const igraph_matrix_t *real_mat) { - igraph_integer_t n = igraph_matrix_nrow(real_mat); - for (igraph_integer_t i = 0; i < n; i++) { +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 = false; + positions[id_catalog[i]].fixed = fixed ? VECTOR(*fixed)[i] : false; if ( real_iterations > 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); @@ -469,7 +470,7 @@ int graph::ReCompute( ) { void graph::update_nodes ( ) { - vector node_indices; // node list of nodes currently being updated + 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 @@ -482,9 +483,9 @@ void graph::update_nodes ( ) { // next we calculate the number of nodes there would be if the // num_nodes by num_procs schedule grid were perfectly square - igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); + int square_num_nodes = (int)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); - for ( igraph_integer_t i = myid; i < square_num_nodes; i += num_procs ) { + for ( int i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); @@ -495,7 +496,7 @@ void graph::update_nodes ( ) { if ( i < num_nodes ) { // advance random sequence according to myid - for ( size_t j = 0; j < 2 * myid; j++ ) { + for ( int j = 0; j < 2 * myid; j++ ) { RNG_UNIF01(); } // rand(); @@ -506,7 +507,7 @@ void graph::update_nodes ( ) { } // advance random sequence for next iteration - for ( size_t j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { RNG_UNIF01(); } // rand(); @@ -514,7 +515,7 @@ void graph::update_nodes ( ) { } else { // advance random sequence according to use by // the other processors - for ( size_t j = 0; j < 2 * (node_indices.size()); j++ ) { + for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { RNG_UNIF01(); } //rand(); @@ -522,7 +523,7 @@ void graph::update_nodes ( ) { // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; - for ( size_t j = 0; j < node_indices.size (); j++ ) + for ( unsigned int j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { all_fixed = false; } @@ -542,14 +543,14 @@ void graph::update_nodes ( ) { if ( myid == 0 ) { // output node list (for debugging) - for ( size_t j = 0; j < node_indices.size(); j++ ) + for ( unsigned int j = 0; j < node_indices.size(); j++ ) cout << node_indices[j] << " "; cout << endl; } */ // compute node list for next update - for ( size_t j = 0; j < node_indices.size(); j++ ) { + for ( unsigned int j = 0; j < node_indices.size(); j++ ) { node_indices [j] += num_procs; } @@ -570,11 +571,11 @@ void graph::update_nodes ( ) { // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. -void graph::get_positions ( vector &node_indices, +void graph::get_positions ( vector &node_indices, float return_positions[3 * MAX_PROCS] ) { // fill positions - for (size_t i = 0; i < node_indices.size(); i++) { + 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; @@ -587,7 +588,7 @@ void graph::get_positions ( vector &node_indices, // of active processes at this level for use by the random number // generators. -void graph::update_node_pos ( igraph_integer_t node_ind, +void graph::update_node_pos ( int node_ind, float old_positions[3 * MAX_PROCS], float new_positions[3 * MAX_PROCS] ) { @@ -665,13 +666,13 @@ void graph::update_node_pos ( igraph_integer_t node_ind, // updates the positions by subtracting the old positions and adding the // new positions to the density grid. -void graph::update_density ( vector &node_indices, +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 ( size_t i = 0; i < node_indices.size(); i++ ) { + 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]; @@ -693,13 +694,13 @@ void graph::update_density ( vector &node_indices, * original code by B. Wylie. * *********************************************/ -float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { +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; + map ::iterator EI; float x_dis, y_dis, z_dis; float energy_distance, weight; float node_energy = 0; @@ -751,10 +752,10 @@ float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { * originally written by B. Wylie * *********************************************/ -void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_y, +void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y, float &pos_z) { - map ::iterator EI; + 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; @@ -795,7 +796,7 @@ void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_ float num_connections = (float)sqrt((float)neighbors[node_ind].size()); float maxLength = 0; - map::iterator maxIndex; + map::iterator maxIndex; // Go through nodes edges... cutting if necessary for (EI = maxIndex = neighbors[node_ind].begin(); @@ -857,9 +858,9 @@ int graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } - size_t n = positions.size(); + long int n = positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 3)); - for (size_t i = 0; i < n; i++) { + 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; diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h index b87ad86ce7d..c61612ca962 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h +++ b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h @@ -45,7 +45,7 @@ namespace drl3d { // layout schedule information struct layout_schedule { - igraph_integer_t iterations; + int iterations; float temperature; float attraction; float damping_mult; @@ -59,7 +59,8 @@ class graph { // 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 ); + 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 ( ); @@ -74,13 +75,13 @@ class graph { // Methods int ReCompute ( ); void update_nodes ( ); - float Compute_Node_Energy ( igraph_integer_t node_ind ); - void Solve_Analytic ( igraph_integer_t 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 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 ( igraph_integer_t node_ind, + void update_node_pos ( int node_ind, float old_positions[3 * MAX_PROCS], float new_positions[3 * MAX_PROCS] ); @@ -88,18 +89,17 @@ class graph { int myid, num_procs; // graph decomposition information - igraph_integer_t num_nodes; // number of nodes in graph + 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. + 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; - igraph_integer_t iterations; + 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; @@ -115,9 +115,9 @@ class graph { time_t start_time, stop_time; // online clustering information - igraph_integer_t real_iterations; // number of iterations to hold .real input fixed - igraph_integer_t tot_iterations; - igraph_integer_t tot_expected_iterations; // for progress bar + int real_iterations; // number of iterations to hold .real input fixed + int tot_iterations; + int tot_expected_iterations; // for progress bar bool real_fixed; }; diff --git a/src/vendor/cigraph/src/layout/drl/drl_layout.cpp b/src/vendor/cigraph/src/layout/drl/drl_layout.cpp index d31c71489ac..2ea5e862ffa 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_layout.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_layout.cpp @@ -248,7 +248,7 @@ namespace drl { * Time complexity: O(1). */ -igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, +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; @@ -438,15 +438,25 @@ igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *optio * \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: ???. */ -igraph_error_t igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, +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_t *weights, + const igraph_vector_bool_t *fixed) { + const char msg[] = "Damping multipliers cannot be negative, got %g."; if (options->init_damping_mult < 0) { @@ -485,7 +495,7 @@ igraph_error_t igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); - neighbors.read_real(res); + neighbors.read_real(res, fixed); } neighbors.draw_graph(res); diff --git a/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp b/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp index 6e953a8dae6..b5a6e2d5d60 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp @@ -86,6 +86,11 @@ using namespace drl3d; * \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: ???. @@ -93,10 +98,11 @@ using namespace drl3d; * \sa \ref igraph_layout_drl() for the standard 2d version. */ -igraph_error_t igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, +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_t *weights, + const igraph_vector_bool_t *fixed) { const char msg[] = "Damping multipliers cannot be negative, got %g."; @@ -136,7 +142,7 @@ igraph_error_t igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); - neighbors.read_real(res); + neighbors.read_real(res, fixed); } neighbors.draw_graph(res); diff --git a/src/vendor/cigraph/src/layout/fruchterman_reingold.c b/src/vendor/cigraph/src/layout/fruchterman_reingold.c index 69bcd967508..f7f14fc6062 100644 --- a/src/vendor/cigraph/src/layout/fruchterman_reingold.c +++ b/src/vendor/cigraph/src/layout/fruchterman_reingold.c @@ -31,7 +31,7 @@ #include "core/interruption.h" #include "layout/layout_internal.h" -static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, +static int igraph_layout_i_fr(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_integer_t niter, @@ -45,15 +45,15 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, igraph_integer_t no_nodes = igraph_vcount(graph); igraph_integer_t no_edges = igraph_ecount(graph); igraph_integer_t i; - igraph_vector_t dispx, dispy; + igraph_vector_float_t dispx, dispy; igraph_real_t temp = start_temp; igraph_real_t difftemp = start_temp / niter; - igraph_bool_t conn = true; - igraph_real_t C = 0; + igraph_bool_t conn = 1; + float C = 0; IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { - C = no_nodes * sqrt(no_nodes); + C = no_nodes * sqrtf(no_nodes); } RNG_BEGIN(); @@ -62,8 +62,10 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, igraph_i_layout_random_bounded(graph, res, minx, maxx, miny, maxy); } - IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + 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; @@ -72,14 +74,14 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, /* calculate repulsive forces, we have a special version for unconnected graphs */ - igraph_vector_null(&dispx); - igraph_vector_null(&dispy); + 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++) { - 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 dlen = dx * dx + dy * dy; + 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; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); @@ -96,9 +98,9 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, } else { for (v = 0; v < no_nodes; v++) { for (u = v + 1; u < no_nodes; u++) { - 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 dlen, rdlen; + 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; while (dlen == 0) { @@ -125,7 +127,7 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, 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; + 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); @@ -167,14 +169,14 @@ static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, RNG_END(); - igraph_vector_destroy(&dispx); - igraph_vector_destroy(&dispy); + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_layout_i_grid_fr( +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, @@ -184,14 +186,14 @@ static igraph_error_t igraph_layout_i_grid_fr( igraph_integer_t no_nodes = igraph_vcount(graph); igraph_integer_t no_edges = igraph_ecount(graph); - igraph_real_t width = sqrt(no_nodes), height = width; + float width = sqrtf(no_nodes), height = width; igraph_2dgrid_t grid; - igraph_vector_t dispx, dispy; + 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 igraph_real_t cellsize = 2.0; + const float cellsize = 2.0; RNG_BEGIN(); @@ -209,24 +211,26 @@ static igraph_error_t igraph_layout_i_grid_fr( igraph_2dgrid_add2(&grid, i); } - IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + 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_null(&dispx); - igraph_vector_null(&dispy); + 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) { - 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 dlen = dx * dx + dy * dy; + 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; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); dy = RNG_UNIF(-1e-9, 1e-9); @@ -248,7 +252,7 @@ static igraph_error_t igraph_layout_i_grid_fr( 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; + 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); @@ -287,11 +291,11 @@ static igraph_error_t igraph_layout_i_grid_fr( temp -= difftemp; } - igraph_vector_destroy(&dispx); - igraph_vector_destroy(&dispy); + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); igraph_2dgrid_destroy(&grid); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -343,7 +347,7 @@ static igraph_error_t igraph_layout_i_grid_fr( * IGRAPH_LAYOUT_AUTOGRID. The last one uses the grid based * version only for large graphs, currently the ones with * more than 1000 vertices. - * \param weights Pointer to a vector containing edge weights, + * \param weight Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * Weights must be positive. * It will be ignored if it is a null-pointer. @@ -364,13 +368,13 @@ static igraph_error_t igraph_layout_i_grid_fr( * vertices in the graph. */ -igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *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 *weights, + const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -390,10 +394,10 @@ igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, "Fruchterman-Reingold layout.", IGRAPH_EINVAL); } - if (weights && igraph_vector_size(weights) != no_edges) { + if (weight && igraph_vector_size(weight) != no_edges) { IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } - if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + if (weight && no_edges > 0 && igraph_vector_min(weight) <= 0) { IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", IGRAPH_EINVAL); } @@ -426,10 +430,10 @@ igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, if (grid == IGRAPH_LAYOUT_GRID) { return igraph_layout_i_grid_fr(graph, res, use_seed, niter, start_temp, - weights, minx, maxx, miny, maxy); + weight, minx, maxx, miny, maxy); } else { return igraph_layout_i_fr(graph, res, use_seed, niter, start_temp, - weights, minx, maxx, miny, maxy); + weight, minx, maxx, miny, maxy); } } @@ -452,7 +456,7 @@ igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, * of movement alloved along one axis, within one step, for a * vertex. Currently it is decreased linearly to zero during * the iteration. - * \param weights Pointer to a vector containing edge weights, + * \param weight Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * Weights must be positive. * It will be ignored if it is a null-pointer. @@ -481,12 +485,12 @@ igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, * */ -igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *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 *weights, + const igraph_vector_t *weight, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -497,11 +501,11 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, const igraph_integer_t no_nodes = igraph_vcount(graph); const igraph_integer_t no_edges = igraph_ecount(graph); igraph_integer_t i; - igraph_vector_t dispx, dispy, dispz; + igraph_vector_float_t dispx, dispy, dispz; igraph_real_t temp = start_temp; igraph_real_t difftemp = start_temp / niter; - igraph_bool_t conn = true; - igraph_real_t C = 0; + igraph_bool_t conn = 1; + float C = 0; if (niter < 0) { IGRAPH_ERROR("Number of iterations must be non-negative in " @@ -514,10 +518,10 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, "Fruchterman-Reingold layout", IGRAPH_EINVAL); } - if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { + if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } - if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { + if (weight && no_edges > 0 && igraph_vector_min(weight) <= 0) { IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", IGRAPH_EINVAL); } @@ -551,7 +555,7 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { - C = no_nodes * sqrt(no_nodes); + C = no_nodes * sqrtf(no_nodes); } RNG_BEGIN(); @@ -560,9 +564,12 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, igraph_i_layout_random_bounded_3d(graph, res, minx, maxx, miny, maxy, minz, maxz); } - IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&dispz, no_nodes); + 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; @@ -571,16 +578,16 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, /* calculate repulsive forces, we have a special version for unconnected graphs */ - igraph_vector_null(&dispx); - igraph_vector_null(&dispy); - igraph_vector_null(&dispz); + 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++) { - 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 dlen = dx * dx + dy * dy + dz * dz; + 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; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); @@ -600,10 +607,10 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, } else { for (v = 0; v < no_nodes; v++) { for (u = v + 1; u < no_nodes; u++) { - 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 dlen, rdlen; + 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; while (dlen == 0) { @@ -633,7 +640,7 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, 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 = weights ? VECTOR(*weights)[e] : 1.0; + 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); @@ -687,10 +694,10 @@ igraph_error_t igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, RNG_END(); - igraph_vector_destroy(&dispx); - igraph_vector_destroy(&dispy); - igraph_vector_destroy(&dispz); + igraph_vector_float_destroy(&dispx); + igraph_vector_float_destroy(&dispy); + igraph_vector_float_destroy(&dispz); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/gem.c b/src/vendor/cigraph/src/layout/gem.c index 991ea0d6f05..21f35eb91c4 100644 --- a/src/vendor/cigraph/src/layout/gem.c +++ b/src/vendor/cigraph/src/layout/gem.c @@ -25,15 +25,13 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "igraph_structural.h" -#include "core/interruption.h" #include "core/math.h" +#include "core/interruption.h" /** * \ingroup layout * \function igraph_layout_gem - * \brief Layout graph according to GEM algorithm. * * The GEM layout algorithm, as described in Arne Frick, Andreas Ludwig, * Heiko Mehldau: A Fast Adaptive Layout Algorithm for Undirected Graphs, @@ -66,76 +64,74 @@ * performed. */ -igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, +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_t impulse_x, impulse_y, temp, skew_gauge; + igraph_vector_float_t impulse_x, impulse_y, temp, skew_gauge; igraph_integer_t i; - igraph_real_t temp_global; + float temp_global; igraph_integer_t perm_pointer = 0; - igraph_real_t barycenter_x = 0, barycenter_y = 0; + float barycenter_x = 0, barycenter_y = 0; igraph_vector_t phi; - igraph_vector_int_t neis; - const igraph_real_t elen_des2 = 128 * 128; - const igraph_real_t gamma = 1 / 16.0; - const igraph_real_t alpha_o = M_PI; - const igraph_real_t alpha_r = M_PI / 3.0; - const igraph_real_t sigma_o = 1.0 / 3.0; - const igraph_real_t sigma_r = 1.0 / 2.0 / no_nodes; + 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_ERRORF("Number of iterations must be non-negative in GEM layout, " - "got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, maxiter); + IGRAPH_ERROR("Number of iterations must be non-negative in GEM layout", + IGRAPH_EINVAL); } - if (use_seed && igraph_matrix_nrow(res) != no_nodes) { - IGRAPH_ERRORF("In GEM layout, seed matrix number of rows should equal number of nodes (%" IGRAPH_PRId "), got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, no_nodes, igraph_matrix_nrow(res)); - } - if (use_seed && igraph_matrix_ncol(res) != 2) { - IGRAPH_ERRORF("In GEM layout, seed matrix number of columns should be 2, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, igraph_matrix_ncol(res)); + 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_ERRORF("Maximum temperature should be positive in GEM layout, got %g.", - IGRAPH_EINVAL, temp_max); + IGRAPH_ERROR("Maximum temperature should be positive in GEM layout", + IGRAPH_EINVAL); } if (temp_min <= 0) { - IGRAPH_ERRORF("Minimum temperature should be positive in GEM layout, got %g.", - IGRAPH_EINVAL, temp_min); + IGRAPH_ERROR("Minimum temperature should be positive in GEM layout", + IGRAPH_EINVAL); } if (temp_init <= 0) { - IGRAPH_ERRORF("Initial temperature should be positive in GEM layout, got %g.", - IGRAPH_EINVAL, temp_init); + IGRAPH_ERROR("Initial temperature should be positive in GEM layout", + IGRAPH_EINVAL); } if (temp_max < temp_init || temp_init < temp_min) { - IGRAPH_ERRORF("Minimum <= Initial <= Maximum temperature is required " - "in GEM layout, but %g is not larger than %g and smaller than %g.", IGRAPH_EINVAL, - temp_init, temp_min, temp_max); + IGRAPH_ERROR("Minimum <= Initial <= Maximum temperature is required " + "in GEM layout", IGRAPH_EINVAL); } if (no_nodes == 0) { - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INIT_FINALLY(&impulse_x, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&impulse_y, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&temp, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&skew_gauge, no_nodes); - IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); + 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_INT_INIT_FINALLY(&neis, 10); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 10); RNG_BEGIN(); /* Initialization */ - IGRAPH_CHECK(igraph_strength(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, /* weights = */ 0)); - + 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)); @@ -153,12 +149,12 @@ igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0); } } - igraph_vector_fill(&temp, temp_init); + 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; - igraph_real_t px, py, pvx, pvy; + float px, py, pvx, pvy; IGRAPH_ALLOW_INTERRUPTION(); @@ -176,7 +172,7 @@ igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, py += RNG_UNIF(-32.0, 32.0); for (u = 0; u < no_nodes; u++) { - igraph_real_t dx, dy, dist2; + float dx, dy, dist2; if (u == v) { continue; } @@ -190,19 +186,19 @@ igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, } IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - nlen = igraph_vector_int_size(&neis); + nlen = igraph_vector_size(&neis); for (j = 0; j < nlen; j++) { igraph_integer_t u = VECTOR(neis)[j]; - 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 dist2 = dx * dx + dy * dy; + 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) { - igraph_real_t plen = sqrt(px * px + py * py); + float plen = sqrtf(px * px + py * py); px *= VECTOR(temp)[v] / plen; py *= VECTOR(temp)[v] / plen; MATRIX(*res, v, 0) += px; @@ -213,19 +209,19 @@ igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, pvx = VECTOR(impulse_x)[v]; pvy = VECTOR(impulse_y)[v]; if (pvx != 0 || pvy != 0) { - igraph_real_t beta = atan2(pvy - py, pvx - px); - igraph_real_t sin_beta = sin(beta); - igraph_real_t sign_sin_beta = (sin_beta > 0) ? 1 : ((sin_beta < 0) ? -1 : 0); - igraph_real_t cos_beta = cos(beta); - igraph_real_t abs_cos_beta = fabs(cos_beta); - igraph_real_t old_temp = VECTOR(temp)[v]; + 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 >= cos(alpha_o / 2.0)) { + if (abs_cos_beta >= cosf(alpha_o / 2.0)) { VECTOR(temp)[v] *= sigma_o * cos_beta; } - VECTOR(temp)[v] *= (1 - fabs(VECTOR(skew_gauge)[v])); + VECTOR(temp)[v] *= (1 - fabsf(VECTOR(skew_gauge)[v])); if (VECTOR(temp)[v] > temp_max) { VECTOR(temp)[v] = temp_max; } @@ -241,14 +237,14 @@ igraph_error_t igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, RNG_END(); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); igraph_vector_destroy(&phi); igraph_vector_int_destroy(&perm); - igraph_vector_destroy(&skew_gauge); - igraph_vector_destroy(&temp); - igraph_vector_destroy(&impulse_y); - igraph_vector_destroy(&impulse_x); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/graphopt.c b/src/vendor/cigraph/src/layout/graphopt.c index 432cb641c0c..1668932fa05 100644 --- a/src/vendor/cigraph/src/layout/graphopt.c +++ b/src/vendor/cigraph/src/layout/graphopt.c @@ -33,43 +33,43 @@ static igraph_real_t igraph_i_distance_between( const igraph_matrix_t *c, - igraph_integer_t a, igraph_integer_t b); + long int a, long int b); -static void igraph_i_determine_electric_axal_forces( +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, - igraph_integer_t other_node, - igraph_integer_t this_node); + long int other_node, + long int this_node); -static void igraph_i_apply_electrical_force( +static int igraph_i_apply_electrical_force( const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, - igraph_integer_t other_node, igraph_integer_t this_node, + long int other_node, long int this_node, igraph_real_t node_charge, igraph_real_t distance); -static void igraph_i_determine_spring_axal_forces( +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, - igraph_integer_t other_node, - igraph_integer_t this_node); + long int other_node, + long int this_node); -static void igraph_i_apply_spring_force( +static int igraph_i_apply_spring_force( const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, - igraph_integer_t other_node, - igraph_integer_t this_node, igraph_real_t spring_length, + long int other_node, + long int this_node, igraph_real_t spring_length, igraph_real_t spring_constant); -static void igraph_i_move_nodes( +static int igraph_i_move_nodes( igraph_matrix_t *pos, const igraph_vector_t *pending_forces_x, const igraph_vector_t *pending_forces_y, @@ -78,19 +78,19 @@ static void igraph_i_move_nodes( static igraph_real_t igraph_i_distance_between( const igraph_matrix_t *c, - igraph_integer_t a, igraph_integer_t b) { + 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); + return sqrt( diffx * diffx + diffy * diffy ); } -static void igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos, +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, - igraph_integer_t other_node, - igraph_integer_t this_node) { + 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. @@ -133,13 +133,15 @@ static void igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos, if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) { *y = *y * -1; } + + return 0; } -static void igraph_i_apply_electrical_force( +static int igraph_i_apply_electrical_force( const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, - igraph_integer_t other_node, igraph_integer_t this_node, + long int other_node, long int this_node, igraph_real_t node_charge, igraph_real_t distance) { @@ -155,15 +157,17 @@ static void igraph_i_apply_electrical_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 void igraph_i_determine_spring_axal_forces( +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, - igraph_integer_t other_node, igraph_integer_t this_node) { + 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. @@ -192,14 +196,16 @@ static void igraph_i_determine_spring_axal_forces( *x = 0.5 * *x; *y = 0.5 * *y; } + + return 0; } -static void igraph_i_apply_spring_force( +static int igraph_i_apply_spring_force( const igraph_matrix_t *pos, igraph_vector_t *pending_forces_x, igraph_vector_t *pending_forces_y, - igraph_integer_t other_node, - igraph_integer_t this_node, igraph_real_t spring_length, + long int other_node, + long int this_node, igraph_real_t spring_length, igraph_real_t spring_constant) { // determined using Hooke's Law: @@ -216,7 +222,7 @@ static void igraph_i_apply_spring_force( // and when it does, electrical force will probably push one or both of them // one way or another anyway. if (distance == 0.0) { - return; + return 0; } displacement = distance - spring_length; @@ -237,9 +243,11 @@ static void igraph_i_apply_spring_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 void igraph_i_move_nodes( +static int igraph_i_move_nodes( igraph_matrix_t *pos, const igraph_vector_t *pending_forces_x, const igraph_vector_t *pending_forces_y, @@ -260,7 +268,7 @@ static void igraph_i_move_nodes( // velocity = force / mass // displacement = force / mass - igraph_integer_t this_node, no_of_nodes = igraph_vector_size(pending_forces_x); + long int this_node, no_of_nodes = igraph_vector_size(pending_forces_x); for (this_node = 0; this_node < no_of_nodes; this_node++) { @@ -284,16 +292,19 @@ static void igraph_i_move_nodes( 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, the support for - * layers was removed and the code was reorganized to avoid some - * unnecessary steps when the node charge (see below) is zero. + * 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 @@ -303,7 +314,6 @@ static void igraph_i_move_nodes( * * * 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 @@ -336,7 +346,7 @@ static void igraph_i_move_nodes( * |V| is the number of vertices, |E| the number * of edges. If \p node_charge is zero then it is only O(n|E|). */ -igraph_error_t igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, +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, @@ -344,16 +354,16 @@ igraph_error_t igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *re igraph_real_t max_sa_movement, igraph_bool_t use_seed) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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); - igraph_integer_t this_node, other_node, edge; + long int this_node, other_node, edge; igraph_real_t distance; - igraph_integer_t i; + long int i; IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes); @@ -412,8 +422,8 @@ igraph_error_t igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *re // Apply force from springs for (edge = 0; edge < no_of_edges; edge++) { - igraph_integer_t tthis_node = IGRAPH_FROM(graph, edge); - igraph_integer_t oother_node = IGRAPH_TO(graph, 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, diff --git a/src/vendor/cigraph/src/layout/kamada_kawai.c b/src/vendor/cigraph/src/layout/kamada_kawai.c index 59f52cb0bf5..e5f155c48a6 100644 --- a/src/vendor/cigraph/src/layout/kamada_kawai.c +++ b/src/vendor/cigraph/src/layout/kamada_kawai.c @@ -46,10 +46,6 @@ * far-apart veritces will have a smaller effect on the layout. * * - * Disconnected graphs are handled by assuming that the graph distance between - * vertices in different components is the same as the graph diameter. - * - * * This layout works particularly well for locally connected spatial networks * such as lattices. * @@ -103,7 +99,7 @@ * graph. */ -igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, +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, @@ -119,43 +115,43 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t igraph_integer_t i, j, m; if (maxiter < 0) { - IGRAPH_ERROR("Number of iterations must be non-negative in " - "Kamada-Kawai layout.", IGRAPH_EINVAL); + 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_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); + "Kamada-Kawai layout", IGRAPH_EINVAL); } if (weights && igraph_vector_size(weights) != no_edges) { - IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("Weights must be positive for Kamada-Kawai layout.", IGRAPH_EINVAL); } if (minx && igraph_vector_size(minx) != no_nodes) { - IGRAPH_ERROR("Invalid minx vector length.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_nodes) { - IGRAPH_ERROR("Invalid maxx vector length.", IGRAPH_EINVAL); + 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); + 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); + IGRAPH_ERROR("Invalid miny vector length", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_nodes) { - IGRAPH_ERROR("Invalid maxy vector length.", IGRAPH_EINVAL); + 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); + IGRAPH_ERROR("miny must not be greater than maxy", IGRAPH_EINVAL); } if (!use_seed) { @@ -172,21 +168,21 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t } if (no_nodes <= 1) { - return IGRAPH_SUCCESS; + 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_distances_dijkstra(graph, &dij, igraph_vss_all(), - igraph_vss_all(), weights, IGRAPH_ALL)); + IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, + IGRAPH_ALL)); - /* Find largest finite distance */ max_dij = 0.0; for (i = 0; i < no_nodes; i++) { for (j = i + 1; j < no_nodes; j++) { - if (!isfinite(MATRIX(dij, i, j))) { + if (!igraph_finite(MATRIX(dij, i, j))) { continue; } if (MATRIX(dij, i, j) > max_dij) { @@ -194,9 +190,6 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t } } } - - /* Replace infinite distances by the largest finite distance, - * effectively making the graph connected. */ for (i = 0; i < no_nodes; i++) { for (j = 0; j < no_nodes; j++) { if (MATRIX(dij, i, j) > max_dij) { @@ -229,7 +222,7 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t } 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); + 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); } @@ -269,7 +262,7 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t } dx = old_x - MATRIX(*res, i, 0); dy = old_y - MATRIX(*res, i, 1); - dist = sqrt(dx*dx + dy*dy); + 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; @@ -324,10 +317,10 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t } 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); + 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); + 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); @@ -356,7 +349,7 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t igraph_matrix_destroy(&dij); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** @@ -413,7 +406,7 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t * graph. */ -igraph_error_t igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, +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, @@ -491,19 +484,21 @@ igraph_error_t igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matri } if (no_nodes <= 1) { - return IGRAPH_SUCCESS; + 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_distances_dijkstra(graph, &dij, igraph_vss_all(), - igraph_vss_all(), weights, IGRAPH_ALL)); + + 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 (!isfinite(MATRIX(dij, i, j))) { + if (!igraph_finite(MATRIX(dij, i, j))) { continue; } if (MATRIX(dij, i, j) > max_dij) { @@ -698,5 +693,5 @@ igraph_error_t igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matri igraph_matrix_destroy(&dij); IGRAPH_FINALLY_CLEAN(6); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/large_graph.c b/src/vendor/cigraph/src/layout/large_graph.c index 0f10b690a0c..bd63dc1093d 100644 --- a/src/vendor/cigraph/src/layout/large_graph.c +++ b/src/vendor/cigraph/src/layout/large_graph.c @@ -34,7 +34,7 @@ #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); + igraph_real_t len = sqrt((*x) * (*x) + (*y) * (*y)); if (len != 0) { *x /= len; *y /= len; @@ -47,14 +47,14 @@ static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { * * * This is a layout generator similar to the Large Graph Layout - * algorithm and program (http://lgl.sourceforge.net/). But unlike LGL, this + * 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. It must be connnected; - * disconnected graphs are not handled by the algorithm. + * \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 @@ -88,24 +88,24 @@ static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { * in the same grid cell. */ -igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_t mst; - igraph_integer_t root; - igraph_integer_t no_of_layers, actlayer = 0; - igraph_vector_int_t vids; - igraph_vector_int_t layers; - igraph_vector_int_t parents; - igraph_vector_int_t edges; + 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_int_t eids; + igraph_vector_t eids; igraph_vector_t forcex; igraph_vector_t forcey; @@ -158,27 +158,22 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, } /* Assign the layers */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&layers, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&parents, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INIT_FINALLY(&layers, 0); + IGRAPH_VECTOR_INIT_FINALLY(&parents, 0); if (no_of_nodes > 0) { - IGRAPH_CHECK(igraph_bfs_simple(&mst, root, IGRAPH_ALL, &vids, &layers, &parents)); - } - no_of_layers = igraph_vector_int_size(&layers) - 1; - - /* Check whether we have reached all the nodes -- if not, the graph is - * disconnected */ - if (no_of_nodes > 0 && igraph_vector_int_min(&parents) <= -2) { - IGRAPH_WARNING("LGL layout does not support disconnected graphs yet."); + 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_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + 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); @@ -202,15 +197,14 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, for (actlayer = 1; actlayer < no_of_layers; actlayer++) { igraph_real_t c = 1; - igraph_integer_t i, j; + long int i, j; igraph_real_t massx, massy; igraph_real_t px, py; igraph_real_t sx, sy; - igraph_integer_t it = 0; + long int it = 0; igraph_real_t epsilon = 10e-6; igraph_real_t maxchange = epsilon + 1; - /* igraph_integer_t pairs; */ igraph_real_t sconst = sqrt(area / M_PI) / H_n; igraph_2dgrid_iterator_t vidit; @@ -222,18 +216,12 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, RNG_BEGIN(); - j = VECTOR(layers)[actlayer]; - for (i = VECTOR(layers)[actlayer - 1]; + j = (long int) VECTOR(layers)[actlayer]; + for (i = (long int) VECTOR(layers)[actlayer - 1]; i < VECTOR(layers)[actlayer]; i++) { - igraph_integer_t vid = VECTOR(vids)[i]; - igraph_integer_t par = VECTOR(parents)[vid]; - - if (par < 0) { - /* this is either the root vertex or an unreachable node */ - continue; - } - + 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); @@ -244,7 +232,8 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, sy = c * (massy + py) + MATRIX(*res, vid, 1); /* The neighbors of 'vid' */ - while (j < VECTOR(layers)[actlayer + 1] && VECTOR(parents)[VECTOR(vids)[j]] == 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); @@ -257,7 +246,7 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_i_norm2d(&rx, &ry); rx = rx / actlayer * sconst; ry = ry / actlayer * sconst; - igraph_2dgrid_add(&grid, VECTOR(vids)[j], sx + rx, sy + ry); + igraph_2dgrid_add(&grid, (long int) VECTOR(vids)[j], sx + rx, sy + ry); j++; } } @@ -268,18 +257,20 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, /* Step 2: add the edges of the next layer */ /*-----------------------------------------*/ - for (j = VECTOR(layers)[actlayer]; + for (j = (long int) VECTOR(layers)[actlayer]; j < VECTOR(layers)[actlayer + 1]; j++) { - igraph_integer_t vid = VECTOR(vids)[j]; - igraph_integer_t k; + long int vid = (long int) VECTOR(vids)[j]; + long int k; IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_incident(graph, &eids, vid, IGRAPH_ALL)); - for (k = 0; k < igraph_vector_int_size(&eids); k++) { - igraph_integer_t eid = VECTOR(eids)[k]; - igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); + 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_int_push_back(&edges, eid); + igraph_vector_push_back(&edges, eid); } } } @@ -290,12 +281,12 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, maxchange = epsilon + 1; while (it < maxit && maxchange > epsilon) { - igraph_integer_t jj; - igraph_real_t t = maxdelta * pow((maxit - it) / (igraph_real_t) maxit, coolexp); - igraph_integer_t vid, nei; + 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) + (it) / (maxit * (no_of_layers - 1.0))), + 100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + ((float)it) / (maxit * (no_of_layers - 1.0))), 0); /* init */ @@ -304,58 +295,58 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, maxchange = 0; /* attractive "forces" along the edges */ - for (jj = 0; jj < igraph_vector_int_size(&edges); jj++) { - igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(edges)[jj]); - igraph_integer_t to = IGRAPH_TO(graph, VECTOR(edges)[jj]); + for (jj = 0; jj < igraph_vector_size(&edges); jj++) { + igraph_integer_t from, to; igraph_real_t xd, yd, dist, force; IGRAPH_ALLOW_INTERRUPTION(); - xd = MATRIX(*res, from, 0) - MATRIX(*res, to, 0); - yd = MATRIX(*res, from, 1) - MATRIX(*res, to, 1); - dist = sqrt(xd*xd + yd*yd); + 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)[from] -= xd * force; - VECTOR(forcex)[to] += xd * force; - VECTOR(forcey)[from] -= yd * force; - VECTOR(forcey)[to] += yd * force; + 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, vid, 0) - MATRIX(*res, nei, 0); - igraph_real_t yd = MATRIX(*res, vid, 1) - MATRIX(*res, nei, 1); - igraph_real_t dist = sqrt(xd*xd + yd*yd); + 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)[vid] += xd * force; - VECTOR(forcex)[nei] -= xd * force; - VECTOR(forcey)[vid] += yd * force; - VECTOR(forcey)[nei] -= yd * force; + 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", */ - /* VECTOR(layers)[actlayer+1], pairs); */ + /* (long int) VECTOR(layers)[actlayer+1], pairs); */ /* apply the changes */ for (jj = 0; jj < VECTOR(layers)[actlayer + 1]; jj++) { - igraph_integer_t vvid = VECTOR(vids)[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); + igraph_real_t ded = sqrt(fx * fx + fy * fy); if (ded > t) { ded = t / ded; fx *= ded; fy *= ded; @@ -375,15 +366,15 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, IGRAPH_PROGRESS("Large graph layout", 100.0, 0); igraph_destroy(&mst); - igraph_vector_int_destroy(&vids); - igraph_vector_int_destroy(&layers); - igraph_vector_int_destroy(&parents); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&layers); + igraph_vector_destroy(&parents); + igraph_vector_destroy(&edges); igraph_2dgrid_destroy(&grid); - igraph_vector_int_destroy(&eids); + igraph_vector_destroy(&eids); igraph_vector_destroy(&forcex); igraph_vector_destroy(&forcey); IGRAPH_FINALLY_CLEAN(9); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/layout_bipartite.c b/src/vendor/cigraph/src/layout/layout_bipartite.c index 92d018b4cda..ebd4540ed0c 100644 --- a/src/vendor/cigraph/src/layout/layout_bipartite.c +++ b/src/vendor/cigraph/src/layout/layout_bipartite.c @@ -49,23 +49,23 @@ * * \sa \ref igraph_layout_sugiyama(). */ -igraph_error_t igraph_layout_bipartite(const igraph_t *graph, +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, igraph_integer_t maxiter) { + igraph_real_t vgap, long int maxiter) { - igraph_integer_t i, no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t layers; + 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 (%" IGRAPH_PRId ") should be equal to the number of nodes (%" IGRAPH_PRId ").", + 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 %g.", IGRAPH_EINVAL, hgap); + IGRAPH_ERRORF("The horizontal gap cannot be negative, got %f.", IGRAPH_EINVAL, hgap); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&layers, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes); for (i = 0; i < no_of_nodes; i++) { VECTOR(layers)[i] = VECTOR(*types)[i] ? 0 : 1; } @@ -74,7 +74,7 @@ igraph_error_t igraph_layout_bipartite(const igraph_t *graph, /*extd_to_orig_eids=*/ 0, &layers, hgap, vgap, maxiter, /*weights=*/ 0)); - igraph_vector_int_destroy(&layers); + igraph_vector_destroy(&layers); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/layout/layout_grid.c b/src/vendor/cigraph/src/layout/layout_grid.c index 16ec85b88fe..144fdb65de2 100644 --- a/src/vendor/cigraph/src/layout/layout_grid.c +++ b/src/vendor/cigraph/src/layout/layout_grid.c @@ -41,14 +41,14 @@ * * Time complexity: O(|V|), the number of vertices. */ -igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width) { - igraph_integer_t i, no_of_nodes = igraph_vcount(graph); +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 = ceil(sqrt(no_of_nodes)); + width = (long int) ceil(sqrt(no_of_nodes)); } x = y = 0; @@ -60,7 +60,7 @@ igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, i } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -81,19 +81,19 @@ igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, i * * Time complexity: O(|V|), the number of vertices. */ -igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t width, igraph_integer_t height) { - igraph_integer_t i, no_of_nodes = igraph_vcount(graph); +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 = ceil(pow(no_of_nodes, 1.0 / 3)); + width = height = (long int) ceil(pow(no_of_nodes, 1.0 / 3)); } else if (width <= 0) { - width = ceil(sqrt(no_of_nodes / (double)height)); + width = (long int) ceil(sqrt(no_of_nodes / (double)height)); } else if (height <= 0) { - height = ceil(sqrt(no_of_nodes / (double)width)); + height = (long int) ceil(sqrt(no_of_nodes / (double)width)); } x = y = z = 0; @@ -109,5 +109,5 @@ igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/layout/layout_internal.h b/src/vendor/cigraph/src/layout/layout_internal.h index f4af93e1cbd..b3dd8730005 100644 --- a/src/vendor/cigraph/src/layout/layout_internal.h +++ b/src/vendor/cigraph/src/layout/layout_internal.h @@ -25,45 +25,39 @@ #include "igraph_datatype.h" #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_matrix.h" #include "layout/merge_grid.h" __BEGIN_DECLS -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, - igraph_integer_t actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, +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 igraph_error_t igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, +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 igraph_error_t igraph_i_layout_sphere_3d(igraph_matrix_t *coords, igraph_real_t *x, +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 igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real_t v_y, - igraph_real_t u1_x, igraph_real_t u1_y, - igraph_real_t u2_x, igraph_real_t u2_y); +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(igraph_real_t p0_x, igraph_real_t p0_y, - igraph_real_t p1_x, igraph_real_t p1_y, - igraph_real_t p2_x, igraph_real_t p2_y, - igraph_real_t p3_x, igraph_real_t p3_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); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, - igraph_real_t *a_p, - igraph_real_t *b_p); - -igraph_error_t igraph_i_layout_random_bounded( +int igraph_i_layout_random_bounded( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy); -igraph_error_t igraph_i_layout_random_bounded_3d( +int igraph_i_layout_random_bounded_3d( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, diff --git a/src/vendor/cigraph/src/layout/layout_random.c b/src/vendor/cigraph/src/layout/layout_random.c index 31cad0008df..13120021050 100644 --- a/src/vendor/cigraph/src/layout/layout_random.c +++ b/src/vendor/cigraph/src/layout/layout_random.c @@ -42,10 +42,10 @@ * Time complexity: O(|V|), the * number of vertices. */ -igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { +int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; + long int no_of_nodes = igraph_vcount(graph); + long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); @@ -58,7 +58,7 @@ igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) RNG_END(); - return IGRAPH_SUCCESS; + return 0; } /** @@ -79,10 +79,10 @@ igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) * * Time complexity: O(|V|), the number of vertices. */ -igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { +int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i; + long int no_of_nodes = igraph_vcount(graph); + long int i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); @@ -103,7 +103,7 @@ igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *r /* The following functions generate suitable initial random layouts for * the Fruchterman-Reingold and Kamada-Kawai algorithms. */ -igraph_error_t igraph_i_layout_random_bounded( +int igraph_i_layout_random_bounded( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, @@ -160,16 +160,16 @@ igraph_error_t igraph_i_layout_random_bounded( igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : dmaxx; igraph_real_t y1 = miny ? VECTOR(*miny)[i] : dminy; igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; - if (!isfinite(x1)) { + if (!igraph_finite(x1)) { x1 = -width / 2; } - if (!isfinite(x2)) { + if (!igraph_finite(x2)) { x2 = width / 2; } - if (!isfinite(y1)) { + if (!igraph_finite(y1)) { y1 = -height / 2; } - if (!isfinite(y2)) { + if (!igraph_finite(y2)) { y2 = height / 2; } MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); @@ -179,7 +179,7 @@ igraph_error_t igraph_i_layout_random_bounded( return IGRAPH_SUCCESS; } -igraph_error_t igraph_i_layout_random_bounded_3d( +int igraph_i_layout_random_bounded_3d( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, @@ -257,22 +257,22 @@ igraph_error_t igraph_i_layout_random_bounded_3d( igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; igraph_real_t z1 = minz ? VECTOR(*minz)[i] : dminz; igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] : dmaxz; - if (!isfinite(x1)) { + if (!igraph_finite(x1)) { x1 = -width / 2; } - if (!isfinite(x2)) { + if (!igraph_finite(x2)) { x2 = width / 2; } - if (!isfinite(y1)) { + if (!igraph_finite(y1)) { y1 = -height / 2; } - if (!isfinite(y2)) { + if (!igraph_finite(y2)) { y2 = height / 2; } - if (!isfinite(z1)) { + if (!igraph_finite(z1)) { z1 = -depth / 2; } - if (!isfinite(z2)) { + if (!igraph_finite(z2)) { z2 = depth / 2; } MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); diff --git a/src/vendor/cigraph/src/layout/mds.c b/src/vendor/cigraph/src/layout/mds.c index 288e4f71c98..bf6e12dca79 100644 --- a/src/vendor/cigraph/src/layout/mds.c +++ b/src/vendor/cigraph/src/layout/mds.c @@ -33,43 +33,33 @@ #include "igraph_random.h" #include "igraph_structural.h" -#include - -static igraph_error_t igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, +static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, int n, void *extra); -static igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, - igraph_matrix_t *dist, igraph_integer_t dim); +static int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, long int dim); -static igraph_error_t igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, +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_CHECK(igraph_blas_dgemv_array(0, 1, matrix, from, 0, to)); - return IGRAPH_SUCCESS; + 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. */ -igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, - igraph_matrix_t *dist, igraph_integer_t dim) { +int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, long int dim) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t nev = 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; - igraph_integer_t i, j, k; + long int i, j, k; igraph_eigen_which_t which; - if (no_of_nodes > INT_MAX) { - IGRAPH_ERROR("Graph too large for eigenvector calculations", IGRAPH_EOVERFLOW); - } - - if (nev > INT_MAX) { - IGRAPH_ERROR("Dimensionality too large for eigenvector calculations", IGRAPH_EOVERFLOW); - } - /* Handle the trivial cases */ if (no_of_nodes == 1) { IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim)); @@ -100,7 +90,7 @@ igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t /* 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_CHECK(igraph_blas_dgemv(0, 1, dist, &values, 0, &row_means)); + 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++) { @@ -176,8 +166,8 @@ igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t * 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 undirected shortest path lengths between vertices - * will be used as distances. + * 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. @@ -188,9 +178,9 @@ igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t * Time complexity: usually around O(|V|^2 dim). */ -igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, - const igraph_matrix_t *dist, igraph_integer_t dim) { - igraph_integer_t i, no_of_nodes = igraph_vcount(graph); +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; @@ -212,10 +202,12 @@ igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, /* Copy or obtain the distance matrix */ if (dist == 0) { - IGRAPH_MATRIX_INIT_FINALLY(&m, no_of_nodes, no_of_nodes); - IGRAPH_CHECK(igraph_distances(graph, &m, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); + 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_init_copy(&m, dist)); + 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++) { @@ -230,27 +222,27 @@ igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, 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_matrix_list_t layouts; - igraph_vector_int_t vertex_order; - igraph_vector_int_t comp; + igraph_vector_ptr_t layouts; + igraph_vector_t comp, vertex_order; igraph_t subgraph; - igraph_matrix_t layout; + igraph_matrix_t *layout; igraph_matrix_t dist_submatrix; igraph_bool_t *seen_vertices; - igraph_integer_t j, n, processed_vertex_count = 0; + long int j, n, processed_vertex_count = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&comp, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&comp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes); - IGRAPH_MATRIX_LIST_INIT_FINALLY(&layouts, 0); - IGRAPH_MATRIX_INIT_FINALLY(&layout, 0, 0); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, seen_vertices); @@ -266,19 +258,29 @@ igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, 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)); + 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)); + IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim)); /* Store the layout */ - IGRAPH_CHECK(igraph_matrix_list_push_back_copy(&layouts, &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_int_size(&comp); + n = igraph_vector_size(&comp); for (j = 0; j < n; j++) { - seen_vertices[VECTOR(comp)[j]] = 1; - VECTOR(vertex_order)[VECTOR(comp)[j]] = processed_vertex_count++; + 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 */ @@ -288,11 +290,10 @@ igraph_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, igraph_free(seen_vertices); igraph_matrix_destroy(&dist_submatrix); - igraph_matrix_destroy(&layout); - igraph_matrix_list_destroy(&layouts); - igraph_vector_int_destroy(&vertex_order); - igraph_vector_int_destroy(&comp); - IGRAPH_FINALLY_CLEAN(6); + igraph_vector_ptr_destroy_all(&layouts); + igraph_vector_destroy(&vertex_order); + igraph_vector_destroy(&comp); + IGRAPH_FINALLY_CLEAN(5); } RNG_END(); diff --git a/src/vendor/cigraph/src/layout/merge_dla.c b/src/vendor/cigraph/src/layout/merge_dla.c index 7082540aacf..34e3cc46cf0 100644 --- a/src/vendor/cigraph/src/layout/merge_dla.c +++ b/src/vendor/cigraph/src/layout/merge_dla.c @@ -22,10 +22,10 @@ */ #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 "layout/merge_grid.h" @@ -33,62 +33,60 @@ /** * \function igraph_layout_merge_dla - * \brief Merges multiple layouts by using a DLA algorithm. - * - * \experimental + * \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 List of matrices with the 2D layouts of the graphs in \p thegraphs. + * \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. + * Added in version 0.2. This function is experimental. * * * Time complexity: TODO. */ -igraph_error_t igraph_layout_merge_dla( - const igraph_vector_ptr_t *thegraphs, const igraph_matrix_list_t *coords, - igraph_matrix_t *res -) { - igraph_integer_t coords_len = igraph_matrix_list_size(coords); +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; - igraph_integer_t allnodes = 0; - igraph_integer_t i, j; - igraph_integer_t actg; + long int allnodes = 0; + long int i, j; + long int actg; igraph_i_layout_mergegrid_t grid; - igraph_integer_t jpos = 0; + long int jpos = 0; igraph_real_t minx, maxx, miny, maxy; igraph_real_t area = 0; igraph_real_t maxr = 0; - igraph_integer_t respos; + long int respos; /* Graphs are currently not used, only the coordinates */ IGRAPH_UNUSED(thegraphs); - IGRAPH_VECTOR_INIT_FINALLY(&sizes, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&x, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&y, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&r, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&nx, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&ny, coords_len); - IGRAPH_VECTOR_INIT_FINALLY(&nr, coords_len); + 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 < coords_len; i++) { - igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); - igraph_integer_t size = igraph_matrix_nrow(mat); + 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", @@ -105,9 +103,10 @@ igraph_error_t igraph_layout_merge_dla( } igraph_i_layout_sphere_2d(mat, - igraph_vector_get_ptr(&nx, i), - igraph_vector_get_ptr(&ny, i), - igraph_vector_get_ptr(&nr, i)); + igraph_vector_e_ptr(&nx, i), + igraph_vector_e_ptr(&ny, i), + igraph_vector_e_ptr(&nr, i)); + } igraph_vector_order2(&sizes); /* largest first */ @@ -121,20 +120,20 @@ igraph_error_t igraph_layout_merge_dla( /* fprintf(stderr, "Ok, starting DLA\n"); */ /* 1. place the largest */ - actg = VECTOR(sizes)[jpos++]; + 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 < coords_len) { + while (jpos < graphs) { IGRAPH_ALLOW_INTERRUPTION(); /* fprintf(stderr, "comp: %li", jpos); */ - IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / coords_len, NULL); + IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / graphs, NULL); - actg = VECTOR(sizes)[jpos++]; + actg = (long int) VECTOR(sizes)[jpos++]; /* 2. random walk, TODO: tune parameters */ igraph_i_layout_merge_dla(&grid, actg, - igraph_vector_get_ptr(&x, actg), - igraph_vector_get_ptr(&y, actg), + igraph_vector_e_ptr(&x, actg), + igraph_vector_e_ptr(&y, actg), VECTOR(r)[actg], 0, 0, maxx, maxx + 5); @@ -147,12 +146,12 @@ igraph_error_t igraph_layout_merge_dla( /* Create the result */ IGRAPH_CHECK(igraph_matrix_resize(res, allnodes, 2)); respos = 0; - for (i = 0; i < coords_len; i++) { - igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); - igraph_integer_t size = igraph_matrix_nrow(mat); + 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; @@ -177,14 +176,14 @@ igraph_error_t igraph_layout_merge_dla( igraph_vector_destroy(&ny); igraph_vector_destroy(&nr); IGRAPH_FINALLY_CLEAN(8); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_i_layout_sphere_2d(igraph_matrix_t *coords, +int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, igraph_real_t *x, igraph_real_t *y, igraph_real_t *r) { - igraph_integer_t nodes = igraph_matrix_nrow(coords); - igraph_integer_t i; + long int nodes = igraph_matrix_nrow(coords); + long int i; igraph_real_t xmin, xmax, ymin, ymax; xmin = xmax = MATRIX(*coords, 0, 0); @@ -207,16 +206,16 @@ igraph_error_t igraph_i_layout_sphere_2d(igraph_matrix_t *coords, *x = (xmin + xmax) / 2; *y = (ymin + ymax) / 2; - *r = sqrt((xmax - xmin)*(xmax - xmin) + (ymax - ymin)*(ymax - ymin)) / 2; + *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) ) / 2; - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_i_layout_sphere_3d(igraph_matrix_t *coords, +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_integer_t nodes = igraph_matrix_nrow(coords); - igraph_integer_t i; + 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); @@ -250,16 +249,16 @@ igraph_error_t igraph_i_layout_sphere_3d(igraph_matrix_t *coords, *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) + (zmax - zmin) * (zmax - zmin) ) / 2; - return IGRAPH_SUCCESS; + return 0; } #define DIST(x,y) (sqrt(pow((x)-cx,2)+pow((y)-cy,2))) -igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, - igraph_integer_t actg, igraph_real_t *x, igraph_real_t *y, igraph_real_t r, +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_integer_t sp = -1; + long int sp = -1; igraph_real_t angle, len; /* The graph is not used, only its coordinates */ @@ -288,5 +287,6 @@ igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, } } - return IGRAPH_SUCCESS; + /* fprintf(stderr, "%li ", steps); */ + return 0; } diff --git a/src/vendor/cigraph/src/layout/merge_grid.c b/src/vendor/cigraph/src/layout/merge_grid.c index 38e36d54f1e..5b7d94be641 100644 --- a/src/vendor/cigraph/src/layout/merge_grid.c +++ b/src/vendor/cigraph/src/layout/merge_grid.c @@ -20,20 +20,19 @@ */ -#include "layout/merge_grid.h" - #include "igraph_memory.h" +#include "layout/merge_grid.h" -static igraph_error_t igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, +static int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, igraph_real_t xc, igraph_real_t yc, - igraph_integer_t *x, igraph_integer_t *y) { + long int *x, long int *y) { if (xc <= grid->minx) { *x = 0; } else if (xc >= grid->maxx) { *x = grid->stepsx - 1; } else { - *x = floor((xc - (grid->minx)) / (grid->deltax)); + *x = (long int) floor((xc - (grid->minx)) / (grid->deltax)); } if (yc <= grid->miny) { @@ -41,15 +40,15 @@ static igraph_error_t igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_ } else if (yc >= grid->maxy) { *y = grid->stepsy - 1; } else { - *y = floor((yc - (grid->miny)) / (grid->deltay)); + *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, - igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, - igraph_real_t miny, igraph_real_t maxy, igraph_integer_t stepsy) { +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; @@ -59,11 +58,11 @@ igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, grid->stepsy = stepsy; grid->deltay = (maxy - miny) / stepsy; - grid->data = IGRAPH_CALLOC(stepsx * stepsy, igraph_integer_t); + grid->data = IGRAPH_CALLOC(stepsx * stepsy, long int); if (grid->data == 0) { - IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); } - return IGRAPH_SUCCESS; + return 0; } void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { @@ -73,11 +72,11 @@ void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { #define MAT(i,j) (grid->data[(grid->stepsy)*(j)+(i)]) #define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) -igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, +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, - igraph_integer_t id) { - igraph_integer_t cx, cy; - igraph_integer_t i, j; + long int id) { + long int cx, cy; + long int i, j; igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); @@ -125,13 +124,13 @@ igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *g #undef DIST #undef DIST2 - return IGRAPH_SUCCESS; + return 0; } -igraph_integer_t igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, +long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y) { - igraph_integer_t cx, cy; - igraph_integer_t res; + long int cx, cy; + long int res; if (x <= grid->minx || x >= grid->maxx || y <= grid->miny || y >= grid->maxy) { @@ -146,11 +145,11 @@ igraph_integer_t igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid #define DIST2(x2,y2) (sqrt(pow(x-(x2),2)+pow(y-(y2), 2))) -igraph_integer_t igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid, +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) { - igraph_integer_t cx, cy; - igraph_integer_t i, j; - igraph_integer_t ret; + 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) { @@ -207,7 +206,7 @@ igraph_integer_t igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_ } /* int print_grid(igraph_i_layout_mergegrid_t *grid) { */ -/* igraph_integer_t i,j; */ +/* long int i,j; */ /* for (i=0; istepsx; i++) { */ /* for (j=0; jstepsy; j++) { */ diff --git a/src/vendor/cigraph/src/layout/merge_grid.h b/src/vendor/cigraph/src/layout/merge_grid.h index 82c65184a95..70fcfeed01c 100644 --- a/src/vendor/cigraph/src/layout/merge_grid.h +++ b/src/vendor/cigraph/src/layout/merge_grid.h @@ -24,7 +24,6 @@ #define IGRAPH_LAYOUT_MERGE_GRID_H #include "igraph_decls.h" -#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -32,26 +31,26 @@ __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 { - igraph_integer_t *data; - igraph_integer_t stepsx, stepsy; + 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 igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, - igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, - igraph_real_t miny, igraph_real_t maxy, igraph_integer_t stepsy); +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 igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, - igraph_real_t x, igraph_real_t y, igraph_real_t r, - igraph_integer_t id); +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); -igraph_integer_t igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, +long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, igraph_real_t x, igraph_real_t y); -igraph_integer_t igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *g, +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 diff --git a/src/vendor/cigraph/src/layout/reingold_tilford.c b/src/vendor/cigraph/src/layout/reingold_tilford.c index 607368e1522..7d101803e91 100644 --- a/src/vendor/cigraph/src/layout/reingold_tilford.c +++ b/src/vendor/cigraph/src/layout/reingold_tilford.c @@ -34,53 +34,53 @@ #include "core/math.h" -static igraph_error_t igraph_i_layout_reingold_tilford_unreachable( +static int igraph_i_layout_reingold_tilford_unreachable( const igraph_t *graph, igraph_neimode_t mode, - igraph_integer_t real_root, - igraph_integer_t no_of_nodes, - igraph_vector_int_t *pnewedges) { - - igraph_integer_t no_of_newedges; - igraph_vector_bool_t visited; - igraph_integer_t i, j, n; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + 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_int_clear(pnewedges); + igraph_vector_resize(pnewedges, 0); /* traverse from real_root and see what nodes you cannot reach */ no_of_newedges = 0; - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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_int_push(&q, real_root)); - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + 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] = true; + VECTOR(visited)[actnode] = 1; for (j = 0; j < n; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; - if (!VECTOR(visited)[neighbor]) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + 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 += VECTOR(visited)[j] ? 0 : 1; + 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_int_resize(pnewedges, no_of_newedges * 2); + igraph_vector_resize(pnewedges, no_of_newedges * 2); j = 0; for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(visited)[i]) { @@ -96,9 +96,9 @@ static igraph_error_t igraph_i_layout_reingold_tilford_unreachable( } } - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&allneis); - igraph_vector_bool_destroy(&visited); + igraph_vector_destroy(&visited); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -107,50 +107,50 @@ static igraph_error_t igraph_i_layout_reingold_tilford_unreachable( /* Internal structure for Reingold-Tilford layout */ struct igraph_i_reingold_tilford_vertex { - igraph_integer_t parent; /* Parent node index */ - igraph_integer_t level; /* Level of the node */ + long int parent; /* Parent node index */ + long int level; /* Level of the node */ igraph_real_t offset; /* X offset from parent node */ - igraph_integer_t left_contour; /* Next left node of the contour + long int left_contour; /* Next left node of the contour of the subtree rooted at this node */ - igraph_integer_t right_contour; /* Next right node of the contour + 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 */ - igraph_integer_t left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ - igraph_integer_t right_extreme; /* Rightmost node on the deepest layer of the subtree rooted at this node */ + 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 void igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, - igraph_integer_t node, igraph_integer_t vcount); -static void igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, - igraph_matrix_t *res, igraph_integer_t node, - igraph_integer_t vcount, igraph_real_t xpos); +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 igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, +static int igraph_i_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - igraph_integer_t root) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, n, j; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + 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_INT_INIT_FINALLY(&q, 100); + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, vdata); @@ -172,22 +172,22 @@ static igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, MATRIX(*res, root, 1) = 0; /* Step 1: assign Y coordinates based on BFS and setup parents vector */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, root)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + 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++) { - igraph_integer_t neighbor = VECTOR(*neis)[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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 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; } @@ -200,7 +200,7 @@ static igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, /* 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_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&allneis); igraph_free(vdata); IGRAPH_FINALLY_CLEAN(3); @@ -210,14 +210,14 @@ static igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, #ifdef LAYOUT_RT_DEBUG for (i = 0; i < no_of_nodes; i++) { printf( - "%3" IGRAPH_PRId ": offset = %.2f, contours = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], contour offsets = [%.2f, %.2f]\n", + "%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 = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], offsets to extrema = [%.2f, %.2f]\n", + " 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 ); @@ -225,16 +225,16 @@ static igraph_error_t igraph_i_layout_reingold_tilford(const igraph_t *graph, } #endif - return IGRAPH_SUCCESS; + return 0; } -static void igraph_i_layout_reingold_tilford_calc_coords( +static int igraph_i_layout_reingold_tilford_calc_coords( struct igraph_i_reingold_tilford_vertex *vdata, - igraph_matrix_t *res, igraph_integer_t node, - igraph_integer_t vcount, igraph_real_t xpos) { - + igraph_matrix_t *res, long int node, + long int vcount, igraph_real_t xpos) { + long int i; MATRIX(*res, node, 0) = xpos; - for (igraph_integer_t i = 0; i < vcount; i++) { + for (i = 0; i < vcount; i++) { if (i == node) { continue; } @@ -243,23 +243,23 @@ static void igraph_i_layout_reingold_tilford_calc_coords( xpos + vdata[i].offset); } } + return 0; } -static void igraph_i_layout_reingold_tilford_postorder( +static int igraph_i_layout_reingold_tilford_postorder( struct igraph_i_reingold_tilford_vertex *vdata, - igraph_integer_t node, igraph_integer_t vcount) { - - igraph_integer_t childcount, leftroot, leftrootidx; + 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 %" IGRAPH_PRId "\n", node); + printf("Starting visiting node %ld\n", node); #endif /* Check whether this node is a leaf node */ childcount = 0; - for (igraph_integer_t i = 0; i < vcount; i++) { + for (i = 0; i < vcount; i++) { if (i == node) { continue; } @@ -271,7 +271,7 @@ static void igraph_i_layout_reingold_tilford_postorder( } if (childcount == 0) { - return; + return 0; } /* Here we can assume that all of the subtrees have been placed and their @@ -285,9 +285,9 @@ static void igraph_i_layout_reingold_tilford_postorder( leftroot = leftrootidx = -1; avg = 0.0; #ifdef LAYOUT_RT_DEBUG - printf("Visited node %" IGRAPH_PRId " and arranged its subtrees\n", node); + printf("Visited node %ld and arranged its subtrees\n", node); #endif - for (igraph_integer_t i = 0, j = 0; i < vcount; i++) { + for (i = 0, j = 0; i < vcount; i++) { if (i == node) { continue; } @@ -295,11 +295,11 @@ static void igraph_i_layout_reingold_tilford_postorder( if (leftroot >= 0) { /* Now we will follow the right contour of leftroot and the * left contour of the subtree rooted at i */ - igraph_integer_t lnode, rnode, auxnode; + long lnode, rnode, auxnode; igraph_real_t loffset, roffset, rootsep, newoffset; #ifdef LAYOUT_RT_DEBUG - printf(" Placing child %" IGRAPH_PRId " on level %" IGRAPH_PRId ", to the right of %" IGRAPH_PRId "\n", i, vdata[i].level, leftroot); + 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; @@ -311,7 +311,7 @@ static void igraph_i_layout_reingold_tilford_postorder( vdata[node].offset_to_right_contour = rootsep; #ifdef LAYOUT_RT_DEBUG - printf(" Contour: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], offsets: [%lf, %lf], rootsep: %lf\n", + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep); #endif while ((lnode >= 0) && (rnode >= 0)) { @@ -341,8 +341,8 @@ static void igraph_i_layout_reingold_tilford_postorder( 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 %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, vdata[rnode].left_contour); - printf(" New contour following offset for node %" IGRAPH_PRId " is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); + 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 @@ -376,14 +376,14 @@ static void igraph_i_layout_reingold_tilford_postorder( * 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 %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, lnode); - printf(" New contour following offset for node %" IGRAPH_PRId " is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); + 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: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], offsets: [%lf, %lf], rootsep: %lf\n", + printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep); #endif @@ -399,7 +399,7 @@ static void igraph_i_layout_reingold_tilford_postorder( } #ifdef LAYOUT_RT_DEBUG - printf(" Offset of subtree with root node %" IGRAPH_PRId " will be %lf\n", i, rootsep); + 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; @@ -407,10 +407,10 @@ static void igraph_i_layout_reingold_tilford_postorder( 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. */ + /* 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 %" IGRAPH_PRId " on level %" IGRAPH_PRId " as first child\n", i, vdata[i].level); + printf(" Placing child %ld on level %ld as first child\n", i, vdata[i].level); #endif leftrootidx = j; leftroot = i; @@ -428,13 +428,13 @@ static void igraph_i_layout_reingold_tilford_postorder( } } #ifdef LAYOUT_RT_DEBUG - printf("Shifting node %" IGRAPH_PRId " to be centered above children. Shift amount: %lf\n", node, avg); + 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 (igraph_integer_t i = 0; i < vcount; i++) { + for (i = 0, j = 0; i < vcount; i++) { if (i == node) { continue; } @@ -442,17 +442,19 @@ static void igraph_i_layout_reingold_tilford_postorder( 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. */ -igraph_error_t igraph_i_layout_reingold_tilford_cluster_degrees_directed( +int igraph_i_layout_reingold_tilford_cluster_degrees_directed( const igraph_t *graph, - const igraph_vector_int_t *membership, + const igraph_vector_t *membership, igraph_integer_t no_comps, igraph_neimode_t mode, - igraph_vector_int_t *degrees) { + igraph_vector_t *degrees) { igraph_eit_t eit; @@ -460,8 +462,8 @@ igraph_error_t igraph_i_layout_reingold_tilford_cluster_degrees_directed( IGRAPH_ERROR("Directed graph expected.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_vector_int_resize(degrees, no_comps)); - igraph_vector_int_null(degrees); + 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); @@ -497,83 +499,33 @@ igraph_error_t igraph_i_layout_reingold_tilford_cluster_degrees_directed( * 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 eccentricity (if 'use_eccentricity' is true) or based on + * either lowest ecccentricity (if 'use_ecccentricity' is true) or based on * highest degree (out- or in-degree in directed mode). */ - -/** - * \function igraph_roots_for_tree_layout - * \brief Roots suitable for a nice tree layout. - * - * This function chooses a root, or a set of roots suitable for visualizing a tree, - * or a tree-like graph. It is typically used with \ref igraph_layout_reingold_tilford(). - * 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, vertices are prioritized based on the given \p heuristic. - * - * \param graph The graph, typically a tree, but any graph is accepted. - * \param mode Whether to interpret the input as undirected, a directed out-tree or in-tree. - * \param roots An initialized integer vector, the roots will be returned here. - * \param heuristic The heuristic to use for breaking ties when multiple root - * choices are possible. - * \clist - * \cli IGRAPH_ROOT_CHOICE_DEGREE - * Choose the vertices with the highest degree (out- or in-degree - * in directed mode). This simple heuristic is fast even in large graphs. - * \cli IGRAPH_ROOT_CHOICE_ECCENTRICITY - * Choose the vertices with the lowest eccentricity. This usually results - * in a "wide and shallow" tree layout. While this heuristic produces - * high-quality results, it is slow for large graphs: computing the - * eccentricities has quadratic complexity in the number of vertices. - * \endclist - * \return Error code. - * - * Time complexity: depends on the heuristic. - */ -igraph_error_t igraph_roots_for_tree_layout( +int igraph_i_layout_reingold_tilford_select_roots( const igraph_t *graph, igraph_neimode_t mode, - igraph_vector_int_t *roots, - igraph_root_choice_t heuristic) { + igraph_vector_t *roots, + igraph_bool_t use_eccentricity) { igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t order, membership; + igraph_vector_t order, membership; igraph_integer_t no_comps; - igraph_integer_t i, j; - igraph_bool_t use_eccentricity; - - switch (heuristic) { - case IGRAPH_ROOT_CHOICE_DEGREE: - use_eccentricity = false; break; - case IGRAPH_ROOT_CHOICE_ECCENTRICITY: - use_eccentricity = true; break; - default: - IGRAPH_ERROR("Invalid root choice heuristic given.", IGRAPH_EINVAL); - } + long int i, j; if (! igraph_is_directed(graph)) { mode = IGRAPH_ALL; } - if (no_of_nodes == 0) { - igraph_vector_int_clear(roots); - return IGRAPH_SUCCESS; - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); if (use_eccentricity) { - /* Sort vertices by decreasing 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, IGRAPH_ASCENDING)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&ecc, &order, /* descending= */ 0)); igraph_vector_destroy(&ecc); IGRAPH_FINALLY_CLEAN(1); @@ -584,14 +536,12 @@ igraph_error_t igraph_roots_for_tree_layout( igraph_vss_all(), mode, 0, IGRAPH_DESCENDING, 0)); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, no_of_nodes); - IGRAPH_CHECK(igraph_connected_components( - graph, &membership, /*csize=*/ NULL, - &no_comps, mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG - )); + 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_int_resize(roots, no_comps)); - igraph_vector_int_fill(roots, -1); /* -1 signifies a not-yet-determined root for a component */ + 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: @@ -601,9 +551,9 @@ igraph_error_t igraph_roots_for_tree_layout( * 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_int_t cluster_degrees; + igraph_vector_t cluster_degrees; - IGRAPH_VECTOR_INT_INIT_FINALLY(&cluster_degrees, no_of_nodes); + 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 */ @@ -613,14 +563,14 @@ igraph_error_t igraph_roots_for_tree_layout( * and record largest degree node in each strongly-connected component * which has no incoming (outgoing) edges. */ for (i = 0; i < no_of_nodes; ++i) { - igraph_integer_t v = VECTOR(order)[i]; - igraph_integer_t cl = VECTOR(membership)[v]; + 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_int_destroy(&cluster_degrees); + igraph_vector_destroy(&cluster_degrees); IGRAPH_FINALLY_CLEAN(1); /* Remove remaining -1 indices. These correspond to components that @@ -631,7 +581,7 @@ igraph_error_t igraph_roots_for_tree_layout( } VECTOR(*roots)[j++] = VECTOR(*roots)[i]; } - igraph_vector_int_resize(roots, j); + igraph_vector_resize(roots, j); } else { /* Undirected case: @@ -639,11 +589,11 @@ igraph_error_t igraph_roots_for_tree_layout( * Select the highest degree node from each component. */ - igraph_integer_t no_seen = 0; + long int no_seen = 0; for (i=0; i < no_of_nodes; ++i) { - igraph_integer_t v = VECTOR(order)[i]; - igraph_integer_t cl = VECTOR(membership)[v]; + long int v = VECTOR(order)[i]; + long int cl = VECTOR(membership)[v]; if (VECTOR(*roots)[cl] == -1) { no_seen += 1; VECTOR(*roots)[cl] = v; @@ -655,8 +605,8 @@ igraph_error_t igraph_roots_for_tree_layout( } } - igraph_vector_int_destroy(&membership); - igraph_vector_int_destroy(&order); + igraph_vector_destroy(&membership); + igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -664,7 +614,7 @@ igraph_error_t igraph_roots_for_tree_layout( /** * \function igraph_layout_reingold_tilford - * \brief Reingold-Tilford layout for tree graphs. + * \brief Reingold-Tilford layout for tree graphs * * * Arranges the nodes in a tree where the given node is used as the root. @@ -691,14 +641,13 @@ igraph_error_t igraph_roots_for_tree_layout( * 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 undirected case, one root should be given from each + * 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 eccentricity vertices in graphs with fewer than - * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. - * The root selection heuristic may change without notice. To ensure a consistent - * output, please specify the roots manually. The \ref igraph_roots_for_tree_layout() - * function gives more control over automatic root selection. + * 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 @@ -708,25 +657,26 @@ igraph_error_t igraph_roots_for_tree_layout( * * Added in version 0.2. * - * \sa \ref igraph_layout_reingold_tilford_circular(), \ref igraph_roots_for_tree_layout() + * \sa \ref igraph_layout_reingold_tilford_circular(). * * \example examples/simple/igraph_layout_reingold_tilford.c */ -igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, +int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - const igraph_vector_int_t *roots, - const igraph_vector_int_t *rootlevel) { + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel) { - const igraph_integer_t no_of_nodes_orig = igraph_vcount(graph); - igraph_integer_t no_of_nodes = no_of_nodes_orig; - igraph_integer_t real_root; + 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_int_t myroots; - const igraph_vector_int_t *proots = roots; - igraph_vector_int_t newedges; + 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 @@ -735,60 +685,61 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, */ /* at various steps it might be necessary to add edges to the graph */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; } - if ( (!roots || igraph_vector_int_size(roots) == 0) && - rootlevel && igraph_vector_int_size(rootlevel) != 0 ) { + 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_int_size(roots) == 0) { + if (!roots || igraph_vector_size(roots) == 0) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&myroots, 0); - igraph_roots_for_tree_layout(graph, mode, &myroots, - no_of_nodes < 500 ? IGRAPH_ROOT_CHOICE_DEGREE : IGRAPH_ROOT_CHOICE_ECCENTRICITY); + 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_int_size(rootlevel) > 0 && - igraph_vector_int_size(roots) > 1) { + } else if (rootlevel && igraph_vector_size(rootlevel) > 0 && + igraph_vector_size(roots) > 1) { /* ----------------------------------------------------------------------- */ /* Many roots were given to us, check 'rootlevel' */ - igraph_integer_t plus_levels = 0; + long int plus_levels = 0; + long int i; - if (igraph_vector_int_size(roots) != igraph_vector_int_size(rootlevel)) { + 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 (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { + 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) { - igraph_integer_t edgeptr = 0; + long int edgeptr = 0; pextended = &extended; IGRAPH_CHECK(igraph_copy(&extended, graph)); IGRAPH_FINALLY(igraph_destroy, &extended); - IGRAPH_CHECK(igraph_add_vertices(&extended, plus_levels, 0)); + IGRAPH_CHECK(igraph_add_vertices(&extended, + (igraph_integer_t) plus_levels, 0)); - igraph_vector_int_resize(&newedges, plus_levels * 2); + igraph_vector_resize(&newedges, plus_levels * 2); - for (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { - igraph_integer_t rl = VECTOR(*rootlevel)[i]; - igraph_integer_t rn = VECTOR(*roots)[i]; - igraph_integer_t j; + 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) { @@ -852,15 +803,15 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, But for now it's ok like this. */ /* if there is only one root, no need for real_root */ - if (igraph_vector_int_size(proots) == 1) { - real_root = VECTOR(*proots)[0]; + 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); + IGRAPH_ERROR("Invalid vertex id.", IGRAPH_EINVVID); } /* else, we need to make real_root */ } else { - igraph_integer_t no_of_newedges; + long int no_of_newedges; /* Make copy of the graph unless it exists already */ if (pextended == graph) { @@ -875,9 +826,9 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, no_of_nodes++; /* add edges from the roots to real_root */ - no_of_newedges = igraph_vector_int_size(proots); - igraph_vector_int_resize(&newedges, no_of_newedges * 2); - for (igraph_integer_t i = 0; i < no_of_newedges; i++) { + 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]; } @@ -888,7 +839,7 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, /* 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_int_size(&newedges) != 0) { + if (igraph_vector_size(&newedges) != 0) { /* Make copy of the graph unless it exists already */ if (pextended == graph) { pextended = &extended; @@ -898,7 +849,7 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0)); } - igraph_vector_int_destroy(&newedges); + igraph_vector_destroy(&newedges); IGRAPH_FINALLY_CLEAN(1); /* ----------------------------------------------------------------------- */ @@ -911,7 +862,7 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, IGRAPH_CHECK(igraph_matrix_remove_row(res, no_of_nodes_orig)); } else { igraph_matrix_t tmp; - igraph_integer_t i; + 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); @@ -930,17 +881,18 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, /* Remove the roots vector if it was created by us */ if (proots != roots) { - igraph_vector_int_destroy(&myroots); + igraph_vector_destroy(&myroots); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_layout_reingold_tilford_circular - * \brief Circular Reingold-Tilford layout for trees. + * \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. * @@ -955,12 +907,12 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, * 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 undirected case, one root should be given from each + * 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 eccentricity vertices in graphs with fewer than - * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. - * The root selection heuristic may change without notice. To ensure a consistent + * 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 @@ -971,13 +923,14 @@ igraph_error_t igraph_layout_reingold_tilford(const igraph_t *graph, * * \sa \ref igraph_layout_reingold_tilford(). */ -igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, +int igraph_layout_reingold_tilford_circular(const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - const igraph_vector_int_t *roots, - const igraph_vector_int_t *rootlevel) { + const igraph_vector_t *roots, + const igraph_vector_t *rootlevel) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int i; igraph_real_t ratio; igraph_real_t minx, maxx; @@ -990,7 +943,7 @@ igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, ratio = 2 * M_PI * (no_of_nodes - 1.0) / no_of_nodes; minx = maxx = MATRIX(*res, 0, 0); - for (igraph_integer_t i = 1; i < no_of_nodes; i++) { + for (i = 1; i < no_of_nodes; i++) { if (MATRIX(*res, i, 0) > maxx) { maxx = MATRIX(*res, i, 0); } @@ -1001,7 +954,7 @@ igraph_error_t igraph_layout_reingold_tilford_circular(const igraph_t *graph, if (maxx > minx) { ratio /= (maxx - minx); } - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + 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); diff --git a/src/vendor/cigraph/src/layout/sugiyama.c b/src/vendor/cigraph/src/layout/sugiyama.c index 37eaa740e33..a046e11c79e 100644 --- a/src/vendor/cigraph/src/layout/sugiyama.c +++ b/src/vendor/cigraph/src/layout/sugiyama.c @@ -23,7 +23,7 @@ */ #include "igraph_layout.h" - +#include "igraph_centrality.h" #include "igraph_components.h" #include "igraph_constants.h" #include "igraph_constructors.h" @@ -155,29 +155,38 @@ static void debug(const char* fmt, ...) { * Data structure to store a layering of the graph. */ typedef struct { - igraph_vector_int_list_t layers; + igraph_vector_ptr_t layers; } igraph_i_layering_t; /** * Initializes a layering. */ -static igraph_error_t igraph_i_layering_init(igraph_i_layering_t* layering, - const igraph_vector_int_t* membership) { - igraph_integer_t i, n, num_layers; +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_int_size(membership) == 0) { + if (igraph_vector_size(membership) == 0) { num_layers = 0; } else { - num_layers = igraph_vector_int_max(membership) + 1; + num_layers = (long int) igraph_vector_max(membership) + 1; } - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&layering->layers, num_layers); + 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_int_size(membership); + n = igraph_vector_size(membership); for (i = 0; i < n; i++) { - igraph_integer_t l = VECTOR(*membership)[i]; - igraph_vector_int_t* vec = igraph_vector_int_list_get_ptr(&layering->layers, l); - IGRAPH_CHECK(igraph_vector_int_push_back(vec, 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); @@ -189,22 +198,22 @@ static igraph_error_t igraph_i_layering_init(igraph_i_layering_t* layering, * Destroys a layering. */ static void igraph_i_layering_destroy(igraph_i_layering_t* layering) { - igraph_vector_int_list_destroy(&layering->layers); + igraph_vector_ptr_destroy_all(&layering->layers); } /** * Returns the number of layers in a layering. */ -static igraph_integer_t igraph_i_layering_num_layers(const igraph_i_layering_t* layering) { - return igraph_vector_int_list_size(&layering->layers); +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_int_t* igraph_i_layering_get(const igraph_i_layering_t* layering, - igraph_integer_t index) { - return igraph_vector_int_list_get_ptr(&layering->layers, index); +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]; } @@ -212,12 +221,12 @@ static igraph_vector_int_t* igraph_i_layering_get(const igraph_i_layering_t* lay * Forward declarations */ -static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, - const igraph_vector_t* weights, igraph_vector_int_t* membership); -static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, +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, - igraph_integer_t maxiter); -static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, + 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); @@ -298,56 +307,57 @@ static INLINE igraph_real_t igraph_i_median_4(igraph_real_t x1, * cycles; igraph will tend to reverse edges with smaller * weights when breaking the cycles. */ -igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, - igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, - const igraph_vector_int_t* layers, igraph_real_t hgap, igraph_real_t vgap, - igraph_integer_t maxiter, const igraph_vector_t *weights) { - igraph_integer_t i, j, k, l, m, nei; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t comp_idx; - igraph_integer_t next_extd_vertex_id = no_of_nodes; +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_int_t membership; /* components of the original graph */ - igraph_vector_int_t extd_edgelist; /* edge list of the extended graph */ - igraph_vector_int_t layers_own; /* layer indices after having eliminated empty layers */ + 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_int_size(layers) != no_of_nodes) { + 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_INT_INIT_FINALLY(&extd_edgelist, 0); + IGRAPH_VECTOR_INIT_FINALLY(&extd_edgelist, 0); if (extd_to_orig_eids != 0) { - igraph_vector_int_clear(extd_to_orig_eids); + igraph_vector_clear(extd_to_orig_eids); } } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + 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_INT_INIT_FINALLY(&layers_own, no_of_nodes); - IGRAPH_CHECK(igraph_i_layout_sugiyama_place_nodes_vertically(graph, weights, &layers_own)); + 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_int_init_copy(&layers_own, layers)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &layers_own); + 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_int_t inds; - IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 0); - IGRAPH_CHECK(igraph_vector_int_qsort_ind(&layers_own, &inds, IGRAPH_ASCENDING)); - j = -1; dx = VECTOR(layers_own)[VECTOR(inds)[0]] - 1; + 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 = VECTOR(inds)[i]; + k = (long int)VECTOR(inds)[i]; if (VECTOR(layers_own)[k] > dx) { /* New layer starts here */ dx = VECTOR(layers_own)[k]; @@ -356,37 +366,38 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re } VECTOR(layers_own)[k] = j; } - igraph_vector_int_destroy(&inds); + igraph_vector_destroy(&inds); IGRAPH_FINALLY_CLEAN(1); } /* 2. Find the connected components. */ - IGRAPH_CHECK(igraph_connected_components(graph, &membership, 0, &no_of_components, IGRAPH_WEAK)); + 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. */ - igraph_integer_t component_size, next_new_vertex_id; - igraph_vector_int_t old2new_vertex_ids; - igraph_vector_int_t new2old_vertex_ids; - igraph_vector_int_t new_layers; - igraph_vector_int_t edgelist; - igraph_vector_int_t neis; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layers, 0); - - igraph_vector_int_fill(&old2new_vertex_ids, -1); - - /* Construct a mapping from the old vertex IDs to the new ones */ + 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_int_push_back(&new_layers, VECTOR(layers_own)[i])); + 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++; @@ -403,10 +414,11 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re /* Okay, this vertex is in the component we are considering. * Add the neighbors of this vertex, excluding loops */ - IGRAPH_CHECK(igraph_incident(graph, &neis, i, IGRAPH_OUT)); - j = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, + IGRAPH_OUT)); + j = igraph_vector_size(&neis); for (k = 0; k < j; k++) { - igraph_integer_t eid = VECTOR(neis)[k]; + long int eid = (long int) VECTOR(neis)[k]; if (directed) { nei = IGRAPH_TO(graph, eid); } else { @@ -419,68 +431,68 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re /* 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_int_push_back(&extd_edgelist, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + 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_int_push_back(extd_to_orig_eids, eid)); + 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_int_push_back(&edgelist, + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[nei])); - for (l = VECTOR(layers_own)[nei] + 1; + for (l = (long int) VECTOR(layers_own)[nei] + 1; l < VECTOR(layers_own)[i]; l++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&new_layers, l)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); + 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_int_push_back(&edgelist, + 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_int_push_back(&extd_edgelist, i)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, i)); next_extd_vertex_id += VECTOR(layers_own)[i] - VECTOR(layers_own)[nei] - 1; - for (l = VECTOR(layers_own)[i] - 1, m = 1; + for (l = (long int) VECTOR(layers_own)[i] - 1, m = 1; l > VECTOR(layers_own)[nei]; l--, m++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id - m)); - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id - 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_int_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } } else { /* Edge goes downwards */ - IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, + IGRAPH_CHECK(igraph_vector_push_back(&edgelist, VECTOR(old2new_vertex_ids)[i])); - for (l = VECTOR(layers_own)[i] + 1; + for (l = (long int) VECTOR(layers_own)[i] + 1; l < VECTOR(layers_own)[nei]; l++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&new_layers, l)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); + 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_int_push_back(&edgelist, + 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_int_push_back(&extd_edgelist, i)); - for (l = VECTOR(layers_own)[i] + 1; + 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_int_push_back(&extd_edgelist, next_extd_vertex_id)); - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id++)); + 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_int_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } - IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); } } } @@ -496,12 +508,13 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re IGRAPH_CHECK(igraph_matrix_init(&layout, next_new_vertex_id, 2)); IGRAPH_FINALLY(igraph_matrix_destroy, &layout); - IGRAPH_CHECK(igraph_create(&subgraph, &edgelist, next_new_vertex_id, 1)); + IGRAPH_CHECK(igraph_create(&subgraph, &edgelist, (igraph_integer_t) + next_new_vertex_id, 1)); IGRAPH_FINALLY(igraph_destroy, &subgraph); /* - igraph_vector_int_print(&edgelist); - igraph_vector_int_print(&new_layers); + igraph_vector_print(&edgelist); + igraph_vector_print(&new_layers); */ /* Assign the vertical coordinates */ @@ -520,7 +533,7 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re /* 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, component_size)); + &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 */ @@ -529,16 +542,16 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re IGRAPH_CHECK(igraph_matrix_add_rows(res, j)); dx2 = dx; for (i = 0; i < component_size; i++) { - l = VECTOR(new2old_vertex_ids)[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)[(igraph_integer_t) MATRIX(layout, i, 1)]; + 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)[(igraph_integer_t) MATRIX(layout, i, 1)]; + MATRIX(*res, k, 1) = VECTOR(layer_to_y)[(long)MATRIX(layout, i, 1)]; if (dx2 < MATRIX(*res, k, 0)) { dx2 = MATRIX(*res, k, 0); } @@ -552,36 +565,37 @@ igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *re IGRAPH_FINALLY_CLEAN(3); } - igraph_vector_int_destroy(&new_layers); - igraph_vector_int_destroy(&old2new_vertex_ids); - igraph_vector_int_destroy(&new2old_vertex_ids); - igraph_vector_int_destroy(&edgelist); - igraph_vector_int_destroy(&neis); + 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_int_destroy(&layers_own); + igraph_vector_destroy(&layers_own); igraph_vector_destroy(&layer_to_y); - igraph_vector_int_destroy(&membership); + igraph_vector_destroy(&membership); IGRAPH_FINALLY_CLEAN(3); if (extd_graph != 0) { - IGRAPH_CHECK(igraph_create(extd_graph, &extd_edgelist, next_extd_vertex_id, igraph_is_directed(graph))); - igraph_vector_int_destroy(&extd_edgelist); + 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 igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, - const igraph_vector_t* weights, igraph_vector_int_t* membership) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); +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_int_fill(membership, 0); + igraph_vector_fill(membership, 0); return IGRAPH_SUCCESS; } @@ -589,37 +603,32 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igra if (igraph_is_directed(graph) && no_of_nodes <= 1000) { /* Network simplex algorithm of Gansner et al, using the original linear * programming formulation */ - igraph_integer_t i, j; - igraph_vector_t outdegs, indegs; - igraph_vector_int_t feedback_edges; + long int i, j; + igraph_vector_t outdegs, indegs, feedback_edges; glp_prob *ip; glp_smcp parm; - if (no_of_edges > INT_MAX) { - IGRAPH_ERROR("Number of edges in graph too large for GLPK.", IGRAPH_EOVERFLOW); - } - /* Allocate storage and create the problem */ ip = glp_create_prob(); IGRAPH_FINALLY(glp_delete_prob, ip); - IGRAPH_VECTOR_INT_INIT_FINALLY(&feedback_edges, 0); + 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_int_sort(&feedback_edges); + 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_int_size(&feedback_edges); + j = igraph_vector_size(&feedback_edges); for (i = 0; i < j; i++) { - igraph_integer_t eid = VECTOR(feedback_edges)[i]; - igraph_integer_t from = IGRAPH_FROM(graph, eid); - igraph_integer_t to = IGRAPH_TO(graph, eid); + 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; } @@ -645,13 +654,13 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igra /* Add constraints */ glp_add_rows(ip, (int) no_of_edges); - IGRAPH_CHECK(igraph_vector_int_push_back(&feedback_edges, -1)); + 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] = (int) IGRAPH_FROM(graph, i) + 1; - ind[2] = (int) IGRAPH_TO(graph, i) + 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) { @@ -681,7 +690,7 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igra } glp_delete_prob(ip); - igraph_vector_int_destroy(&feedback_edges); + 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)); @@ -699,35 +708,36 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igra return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, - const igraph_i_layering_t* layering, igraph_integer_t layer_index, +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) { - igraph_integer_t i, j, m, n; - igraph_vector_int_t* layer_members = igraph_i_layering_get(layering, layer_index); - igraph_vector_int_t neis; + long int i, j, m, n; + igraph_vector_t* layer_members = igraph_i_layering_get(layering, layer_index); + igraph_vector_t neis; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); - n = igraph_vector_int_size(layer_members); + 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, VECTOR(*layer_members)[i], direction)); - m = igraph_vector_int_size(&neis); + 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, (igraph_integer_t) VECTOR(neis)[j], 0); + VECTOR(*barycenters)[i] += MATRIX(*layout, (long)VECTOR(neis)[j], 0); } VECTOR(*barycenters)[i] /= m; } } - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -738,37 +748,33 @@ static igraph_error_t igraph_i_layout_sugiyama_calculate_barycenters(const igrap * exactly one layer, arranges the nodes in each layer horizontally in a way * that strives to minimize edge crossings. */ -static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, +static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* graph, igraph_matrix_t* layout, const igraph_i_layering_t* layering, - igraph_integer_t maxiter) { - igraph_integer_t i, n, nei; - igraph_integer_t no_of_vertices = igraph_vcount(graph); - igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); - igraph_integer_t iter, layer_index; - igraph_vector_int_t* layer_members; - igraph_vector_int_t new_layer_members; - igraph_vector_int_t neis; - igraph_vector_t barycenters; - igraph_vector_int_t sort_indices; + 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 */ { - igraph_integer_t *xs = IGRAPH_CALLOC(no_of_layers, igraph_integer_t); + long int *xs = IGRAPH_CALLOC(no_of_layers, long int); if (xs == 0) { - IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); } for (i = 0; i < no_of_vertices; i++) { - MATRIX(*layout, i, 0) = xs[(igraph_integer_t)MATRIX(*layout, i, 1)]++; + MATRIX(*layout, i, 0) = xs[(long int)MATRIX(*layout, i, 1)]++; } free(xs); } IGRAPH_VECTOR_INIT_FINALLY(&barycenters, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layer_members, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&sort_indices, 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; @@ -780,27 +786,27 @@ static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const ig /* 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_int_size(layer_members); - IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); + 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_int_print(layer_members); + printf("Vertices: "); igraph_vector_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif - IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, + &sort_indices, 0)); for (i = 0; i < n; i++) { - nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; - VECTOR(new_layer_members)[i] = nei; + nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; + VECTOR(barycenters)[i] = nei; MATRIX(*layout, nei, 0) = i; } - if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { - IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); + 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_int_print(layer_members); + printf("New vertex order: "); igraph_vector_print(layer_members); #endif changed = 1; } else { @@ -813,28 +819,28 @@ static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const ig /* 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_int_size(layer_members); - IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); + 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_int_print(layer_members); + printf("Vertices: "); igraph_vector_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif - IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, + &sort_indices, 0)); for (i = 0; i < n; i++) { - nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; - VECTOR(new_layer_members)[i] = nei; + nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; + VECTOR(barycenters)[i] = nei; MATRIX(*layout, nei, 0) = i; } - if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { - IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); + 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_int_print(layer_members); + printf("New vertex order: "); igraph_vector_print(layer_members); #endif changed = 1; } else { @@ -852,10 +858,9 @@ static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const ig } igraph_vector_destroy(&barycenters); - igraph_vector_int_destroy(&new_layer_members); - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&sort_indices); - IGRAPH_FINALLY_CLEAN(4); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&sort_indices); + IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } @@ -864,30 +869,30 @@ static igraph_error_t igraph_i_layout_sugiyama_order_nodes_horizontally(const ig #define IS_INNER_SEGMENT(u, v) (IS_DUMMY(u) && IS_DUMMY(v)) #define X_POS(v) (MATRIX(*layout, v, 0)) -static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, +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 igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, +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 igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t v, +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_int_t* sinks, igraph_vector_t* shifts, + igraph_vector_t* sinks, igraph_vector_t* shifts, igraph_real_t hgap, igraph_vector_t* xs); -static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* graph, +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) { - igraph_integer_t i, j, k, l, n; - igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t neis1, neis2; + 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; @@ -895,16 +900,16 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig /* { - igraph_vector_int_t edgelist; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); + igraph_vector_t edgelist; + IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0)); - igraph_vector_int_print(&edgelist); - igraph_vector_int_destroy(&edgelist); + igraph_vector_print(&edgelist); + igraph_vector_destroy(&edgelist); IGRAPH_FINALLY_CLEAN(1); for (i = 0; i < no_of_layers; i++) { - igraph_vector_int_t* layer = igraph_i_layering_get(layering, i); - igraph_vector_int_print(layer); + igraph_vector_t* layer = igraph_i_layering_get(layering, i); + igraph_vector_print(layer); } } */ @@ -913,8 +918,8 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig IGRAPH_FINALLY(igraph_vector_bool_destroy, &ignored_edges); IGRAPH_VECTOR_INIT_FINALLY(&vertex_to_the_left, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis2, 0); + 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 @@ -922,28 +927,29 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig * non-inner segment as we want to keep inner segments vertical. */ for (i = 0; i < no_of_layers - 1; i++) { - igraph_vector_int_t* vertices = igraph_i_layering_get(layering, i); - n = igraph_vector_int_size(vertices); + 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_int_clear(&neis1); + igraph_vector_clear(&neis1); for (j = 0; j < n; j++) { - IGRAPH_CHECK(igraph_neighbors(graph, &neis2, VECTOR(*vertices)[j], IGRAPH_OUT)); - IGRAPH_CHECK(igraph_vector_int_append(&neis1, &neis2)); + 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_int_size(&neis1); + n = igraph_vector_size(&neis1); for (j = 0; j < n; j++) { - igraph_integer_t u = IGRAPH_FROM(graph, j); - igraph_integer_t v = IGRAPH_TO(graph, 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++) { - igraph_integer_t w = IGRAPH_FROM(graph, k); - igraph_integer_t x = IGRAPH_TO(graph, k); + long int w = IGRAPH_FROM(graph, k); + long int x = IGRAPH_TO(graph, k); if (IS_INNER_SEGMENT(w, x) == j_inner) { continue; } @@ -967,8 +973,8 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig } } - igraph_vector_int_destroy(&neis1); - igraph_vector_int_destroy(&neis2); + igraph_vector_destroy(&neis1); + igraph_vector_destroy(&neis2); IGRAPH_FINALLY_CLEAN(2); /* @@ -977,16 +983,16 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig * vertex is the leftmost vertex in a layer. */ for (i = 0; i < no_of_layers; i++) { - igraph_vector_int_t* vertices = igraph_i_layering_get(layering, i); - n = igraph_vector_int_size(vertices); + igraph_vector_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_size(vertices); if (n == 0) { continue; } - k = l = VECTOR(*vertices)[0]; + k = l = (long int)VECTOR(*vertices)[0]; VECTOR(vertex_to_the_left)[k] = k; for (j = 1; j < n; j++) { - k = VECTOR(*vertices)[j]; + k = (long int)VECTOR(*vertices)[j]; VECTOR(vertex_to_the_left)[k] = l; l = k; } @@ -1004,7 +1010,7 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig for (i = 0; i < 4; i++) { IGRAPH_CHECK(igraph_i_layout_sugiyama_vertical_alignment(graph, layering, layout, &ignored_edges, - /* reverse = */ i / 2, /* align_right = */ i % 2, + /* 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])); @@ -1069,22 +1075,20 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, +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) { - igraph_integer_t i, j, k, n, di, dj, i_limit, j_limit, r; - igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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_int_t neis; - igraph_vector_t xs; - igraph_vector_int_t inds; + igraph_vector_t neis, xs, inds; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&xs, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 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)); @@ -1102,19 +1106,19 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t di = reverse ? -1 : 1; i_limit = reverse ? -1 : no_of_layers; for (; i != i_limit; i += di) { - igraph_vector_int_t *layer = igraph_i_layering_get(layering, i); + 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 ? IGRAPH_INTEGER_MAX : -1; + 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_int_size(layer) - 1) : 0; + j = align_right ? (igraph_vector_size(layer) - 1) : 0; dj = align_right ? -1 : 1; - j_limit = align_right ? -1 : igraph_vector_int_size(layer); + j_limit = align_right ? -1 : igraph_vector_size(layer); for (; j != j_limit; j += dj) { - igraph_integer_t medians[2]; - igraph_integer_t vertex = VECTOR(*layer)[j]; - igraph_integer_t pos; + 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, @@ -1124,9 +1128,10 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t } /* Find the neighbors of vertex j in layer i */ - IGRAPH_CHECK(igraph_neighbors(graph, &neis, vertex, neimode)); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) vertex, + neimode)); - n = igraph_vector_int_size(&neis); + n = igraph_vector_size(&neis); if (n == 0) /* No neighbors in this direction, continue */ { @@ -1134,30 +1139,30 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t } if (n == 1) { /* Just one neighbor; the median is trivial */ - medians[0] = VECTOR(neis)[0]; + 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(VECTOR(neis)[k]); + VECTOR(xs)[k] = X_POS((long int)VECTOR(neis)[k]); } - IGRAPH_CHECK(igraph_vector_qsort_ind(&xs, &inds, IGRAPH_ASCENDING)); + 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] = VECTOR(neis)[VECTOR(inds)[n / 2]]; + 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] = VECTOR(neis)[VECTOR(inds)[n / 2]]; - medians[1] = VECTOR(neis)[VECTOR(inds)[n / 2 - 1]]; + 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] = VECTOR(neis)[VECTOR(inds)[n / 2 - 1]]; - medians[1] = VECTOR(neis)[VECTOR(inds)[n / 2]]; + 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]]; } } } @@ -1174,12 +1179,13 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t } /* Is the edge between medians[k] and vertex ignored * because of a type 1 conflict? */ - IGRAPH_CHECK(igraph_get_eid(graph, &eid, vertex, medians[k], IGRAPH_UNDIRECTED, /* error= */ true)); - if (VECTOR(*ignored_edges)[eid]) { + 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 = X_POS(medians[k]); + 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]]; @@ -1190,8 +1196,8 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t } } - igraph_vector_int_destroy(&inds); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&inds); + igraph_vector_destroy(&neis); igraph_vector_destroy(&xs); IGRAPH_FINALLY_CLEAN(3); @@ -1208,27 +1214,27 @@ static igraph_error_t igraph_i_layout_sugiyama_vertical_alignment(const igraph_t * `graph` is the input graph, `layering` is the layering on which we operate. * `hgap` is the preferred horizontal gap between vertices. */ -static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, +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) { - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_t shifts, old_xs; - igraph_vector_int_t sinks; + long int i; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t sinks, shifts, old_xs; igraph_real_t shift; /* Initialization */ - IGRAPH_CHECK(igraph_vector_int_init_range(&sinks, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &sinks); - + IGRAPH_VECTOR_INIT_FINALLY(&sinks, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes); - igraph_vector_fill(&shifts, IGRAPH_INFINITY); - 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 @@ -1253,15 +1259,15 @@ static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igrap /* Calculate the absolute coordinates */ IGRAPH_CHECK(igraph_vector_update(&old_xs, xs)); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t root = VECTOR(*roots)[i]; + long int root = (long int) VECTOR(*roots)[i]; VECTOR(*xs)[i] = VECTOR(old_xs)[root]; - shift = VECTOR(shifts)[VECTOR(sinks)[root]]; + shift = VECTOR(shifts)[(long int)VECTOR(sinks)[root]]; if (shift < IGRAPH_INFINITY) { VECTOR(*xs)[i] += shift; } } - igraph_vector_int_destroy(&sinks); + igraph_vector_destroy(&sinks); igraph_vector_destroy(&shifts); igraph_vector_destroy(&old_xs); IGRAPH_FINALLY_CLEAN(3); @@ -1269,13 +1275,13 @@ static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction(const igrap return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t v, +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_int_t* sinks, igraph_vector_t* shifts, + igraph_vector_t* sinks, igraph_vector_t* shifts, igraph_real_t hgap, igraph_vector_t* xs) { - igraph_integer_t u, w; - igraph_integer_t u_sink, v_sink; + long int u, w; + long int u_sink, v_sink; if (VECTOR(*xs)[v] >= 0) { return IGRAPH_SUCCESS; @@ -1286,18 +1292,18 @@ static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block w = v; do { /* Check whether vertex w is the leftmost in its own layer */ - u = VECTOR(*vertex_to_the_left)[w]; + 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 = VECTOR(*roots)[u]; + 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 = VECTOR(*sinks)[u]; - v_sink = VECTOR(*sinks)[v]; + 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; @@ -1321,7 +1327,7 @@ static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block } /* Follow the alignment */ - w = VECTOR(*align)[w]; + w = (long int) VECTOR(*align)[w]; } while (w != v); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/layout/umap.c b/src/vendor/cigraph/src/layout/umap.c deleted file mode 100644 index d1f334313e0..00000000000 --- a/src/vendor/cigraph/src/layout/umap.c +++ /dev/null @@ -1,1260 +0,0 @@ -/* -*- 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_layout.h" - -#include "igraph_interface.h" -#include "igraph_lapack.h" -#include "igraph_matrix.h" -#include "igraph_nongraph.h" -#include "igraph_random.h" -#include "igraph_vector_list.h" - -#include "layout/layout_internal.h" -#include "core/interruption.h" - -#include - -/* This file contains the implementation of the UMAP algorithm. - * - * UMAP is typically used as a to reduce dimensionality of vectors, embedding them in - * 2D (or, less commonly, in 3D). Despite this geometric flair, UMAP heavily relies on - * graphs as intermediate data structures and is therefore a useful graph layout - * algorithm in its own right. Conceptually, there are three steps: - * - * 1. Compute a sparse graph with edges connecting similar vectors, e.g. a k-nearest - * neighbor graph. A vector of distances is associated with the graph edges. This - * file does *not* perform this part of the computation since there are many - * libraries out there that can compute knn or other sparse graphs efficiently - * starting from vector spaces (e.g. faiss). - * 2. Convert the distances into weights, which are weights between 0 and 1 - * that are larger for short-distance edges. This step is exposed via - * igraph_layout_umap_compute_weights. - * 3. Compute a layout for the graph, using its associated weights as edge - * weights. This step is exposed via igraph_layout_umap and its 3D counterpart. - * These two fuctions can also compute steps 2 and 3 in one go, since that's the - * most common use case: the argument "distances_are_weights" should be - * set to false. - * - * A few more details w/r/t steps 2 and 3, since they are computed in detail below. - * - * STEP 2 - * For each vertex, the distance to its closest neighbor, called rho, is "forfeited": - * that edge begets weight 1 (in principle, at least). Farther neighbors beget - * lower weights according to an exponential decay. The scale factor of this - * decay is called sigma and is computed from the graph itself. - * - * STEP 3 - * The layout is computed via stochastic gradient descent, i.e. applying stochastic - * forces along high-weight edges and, more rarely, low-weight edges. - * To compute the stochastic forces, one needs a smooth function that approximates - * weights but in the embedded space: - * Q(d) = ( 1 + a*d^2b )^-1 - * where d is the 2D/3D distance between the vertices and a and b are constants that - * are computed globally based on a user-chosen fudge parameter called min_dist. - * Smaller min_dist will give rise to slightly more compact embeddings. We find a - * and b via gradient descent, which is implemented de novo below. - * - * Repulsion is computed via negative sampling, typically a few nodes are picked - * at random as repulsive sources each time an attractive force is computed. - * - * During the stochastic gradient descent, the learning rate - a multiplicative factor - * on top of the stochastic forces themselves - is reduced linearly from 1 to 0. At - * the end, the stochastic forces can be strong but their effect is reduced to almost - * nothing by the small learning rate. Notice that UMAP does not formally converge: - * instead, we reduce the forces' impact steadily to a trickle and finally quench it - * altogether. - * - * FINAL COMMENTS - * This implementation uses a few more tricks to improve the result: - * - a few constants are defined to limit the force applied to vertices at each step - * and other geometric corrections - * - the layout is centered at the end of the computation. - * - a seed layout can be used. Notice that since UMAP runs for an essentially fixed - * time rather than until convergence, using a good/bad seed does not affect - * runtimes significantly. - * */ -#define UMAP_FORCE_LIMIT 4 -#define UMAP_MIN_DISTANCE_ATTRACTION 0.0001 -#define UMAP_CORRECT_DISTANCE_REPULSION 0.01 - -/* Find sigma for this vertex by binary search */ -static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, - const igraph_vector_int_t *eids, - igraph_real_t rho, igraph_real_t *sigma_p, igraph_real_t target) { - - igraph_real_t sigma = 1; - igraph_real_t sum; - igraph_real_t tol = 0.01; - igraph_integer_t maxiter = 100; - igraph_integer_t no_of_neis = igraph_vector_int_size(eids); - igraph_integer_t eid; - igraph_real_t step = sigma; - igraph_integer_t seen_max = 0; - - /* Binary search */ - for (igraph_integer_t iter = 0; iter < maxiter; iter++) { - sum = 0; - for (igraph_integer_t j = 0; j < no_of_neis; j++) { - eid = VECTOR(*eids)[j]; - sum += exp(-(VECTOR(*distances)[eid] - rho) / sigma); - } - -#ifdef UMAP_DEBUG - printf("SIGMA function (no_of_neis = %" IGRAPH_PRId ")- sum: %g, " - "target: %g, rho: %g, sigma: %g\n", no_of_neis, sum, target, rho, sigma); -#endif - - if (sum < target) { - /* going back up after having seen an upper bound */ - if (seen_max == 1) { - step /= 2; - /* we need to go up but have not seen an upper bound yet - * first iteration we want to increase by sigma, else we must come from - * below, so we are sitting at 2 * step, we want to move to 4 * step */ - } else if (iter > 0) { - step *= 2; - } - sigma += step; - /* overshooting, we have definitely seen the max */ - } else { - seen_max = 1; - step /= 2; - sigma -= step; - } - - /* Check for convergence */ - if (fabs(sum - target) < tol) { - break; - } - } - - *sigma_p = sigma; - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_layout_umap_compute_weights - * \brief Compute weights for a UMAP layout starting from distances. - * - * \experimental - * - * UMAP is used to embed high-dimensional vectors in a low-dimensional space - * (most commonly 2D). It uses a distance graph as an intermediate data structure, - * making it also a useful graph layout algorithm. See \ref igraph_layout_umap() - * for more information. - * - * - * - * An early step in UMAP is to compute exponentially decaying "weights" from the - * distance graph. Connectivities can also be viewed as edge weights that quantify - * similarity between two vertices. This function computes weights from the - * distance graph. To compute the layout from precomputed weights, call - * \ref igraph_layout_umap() with the \p distances_are_weights argument set to \c true. - * - * - * - * While the distance graph can be directed (e.g. in a k-nearest neighbors, it is - * clear *who* you are a neighbor of), the weights are usually undirected. Whenever two - * vertices are doubly connected in the distance graph, the resulting weight W is set as: - * - * W = W1 + W2 - W1 * W2 - * - * Because UMAP weights are interpreted as probabilities, this is just the probability - * that either edge is present, without double counting. It is called "fuzzy union" in - * the original UMAP implementation and is the default. One could also require that both - * edges are there, i.e. W = W1 * W2: this would represent the fuzzy intersection and is - * not implemented in igraph. As a consequence of this symmetrization, information is lost, - * i.e. one needs fewer weights than one had distances. To keep things efficient, here - * we set the weight for one of the two edges as above and the weight for its opposite edge - * as 0, so that it will be skipped in the UMAP gradient descent later on. - * - * - * - * Technical note: For each vertex, this function computes its scale factor (sigma), - * its connectivity correction (rho), and finally the weights themselves. - * - * - * References: - * - * - * Leland McInnes, John Healy, and James Melville. https://arxiv.org/abs/1802.03426 - * - * \param graph Pointer to the distance graph. This can be directed (e.g. connecting - * each vertex to its neighbors in a k-nearest neighbor) or undirected, but must - * have no loops nor parallel edges. The only exception is: if the graph is directed, - * having pairs of edges with opposite direction is accepted. - * \param distances Pointer to the vector with the vertex-to-vertex distance associated with - * each edge. This argument can be NULL, in which case all edges are assumed to have the - * same distance. - * \param weights Pointer to an initialized vector where the result will be stored. If the - * input graph is directed, the weights represent a symmetrized version which contains - * less information. Therefore, whenever two edges between the same vertices and opposite - * direction are present in the input graph, only one of the weights is set and the other - * is fixed to zero. That format is accepted by \ref igraph_layout_umap(), which skips - * all zero-weight edges from the layout optimization. - * - * \return Error code. - */ -igraph_error_t igraph_layout_umap_compute_weights( - const igraph_t *graph, - const igraph_vector_t *distances, - igraph_vector_t *weights) { - - igraph_integer_t no_of_vertices = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_neis, eid, i, j, k, l; - igraph_vector_int_t eids; - igraph_vector_int_list_t neighbors_seen; - igraph_vector_list_t weights_seen; - igraph_vector_int_t* neighbors_seen_elt; - igraph_vector_t* weights_seen_elt; - igraph_real_t rho, dist_max, dist, sigma, weight, weight_inv, sigma_target, dist_min; - - /* reserve memory for the weights */ - IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); - - /* UMAP is sometimes used on unweighted graphs, otherwise check distance vector. */ - if (distances != NULL) { - if (igraph_vector_size(distances) != no_of_edges) { - IGRAPH_ERROR("Distances must be the same number as the edges in the graph.", IGRAPH_EINVAL); - } - if (no_of_edges > 0) { - dist_min = igraph_vector_min(distances); - if (dist_min < 0) { - IGRAPH_ERROR("Distance values must not be negative.", IGRAPH_EINVAL); - } else if (isnan(dist_min)) { - IGRAPH_ERROR("Distance values must not be NaN.", IGRAPH_EINVAL); - } - } - } - - /* Initialize auxiliary vectors */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&neighbors_seen, no_of_vertices); - IGRAPH_VECTOR_LIST_INIT_FINALLY(&weights_seen, no_of_vertices); - - /* Iterate over vertices x, like in the paper */ - for (i = 0; i < no_of_vertices; i++) { - /* Edges out of this vertex, e.g. to its k-nearest neighbors */ - IGRAPH_CHECK(igraph_incident(graph, &eids, i, IGRAPH_OUT)); - no_of_neis = igraph_vector_int_size(&eids); - - /* Vertex has no neighbors */ - if (no_of_neis == 0) { - continue; - } - - /* Find rho for this vertex, i.e. the minimal non-self distance */ - if (distances != NULL) { - rho = VECTOR(*distances)[VECTOR(eids)[0]]; - dist_max = rho; - for (j = 1; j < no_of_neis; j++) { - eid = VECTOR(eids)[j]; - dist = VECTOR(*distances)[eid]; - rho = fmin(rho, dist); - dist_max = fmax(dist_max, dist); - } - } else { - rho = dist_max = 0; - } - - /* If the maximal distance is rho, all neighbors are identical to - * each other. This can happen e.g. if distances == NULL. */ - if (dist_max == rho) { - /* This is a special flag for later on */ - sigma = -1; - - /* Else, find sigma for this vertex, from its rho plus binary search */ - } else { - sigma_target = log2(no_of_neis); - IGRAPH_CHECK(igraph_i_umap_find_sigma(distances, - &eids, rho, &sigma, - sigma_target)); - } - - /* Convert to weights */ - for (j = 0; j < no_of_neis; j++) { - eid = VECTOR(eids)[j]; - - /* Basically, nodes closer than rho have probability 1, the rest is - * exponentially penalized keeping rough cardinality */ - weight = sigma < 0 ? 1 : exp(-(VECTOR(*distances)[eid] - rho) / sigma); - - #ifdef UMAP_DEBUG - if (distances != NULL) - printf("distance: %g\n", VECTOR(*distances)[eid]); - printf("weight: %g\n", weight); - #endif - - /* Store in vector lists for later symmetrization */ - k = IGRAPH_OTHER(graph, eid, i); - if (k == i) { - IGRAPH_ERROR("Input graph must contain no self-loops.", IGRAPH_EINVAL); - } - - neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); - IGRAPH_CHECK(igraph_vector_int_push_back(neighbors_seen_elt, k)); - - weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); - IGRAPH_CHECK(igraph_vector_push_back(weights_seen_elt, weight)); - } - - } - - /* Symmetrize the weights. UMAP weights are probabilities of that edge being a - * "real" connection. Unlike the distances, which can represent a directed graph, - * weights are usually symmetric. We symmetrize via fuzzy union. */ - for (eid=0; eid < no_of_edges; eid++) { - i = IGRAPH_FROM(graph, eid); - k = IGRAPH_TO(graph, eid); - - /* Direct weight, if found */ - /* NOTE: this and the subsequent loop could be faster if we sorted the vectors - * beforehand. Probably not such a big deal. */ - weight = 0; - neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); - weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); - no_of_neis = igraph_vector_int_size(neighbors_seen_elt); - for (l=0; l < no_of_neis; l++) { - if (VECTOR(*neighbors_seen_elt)[l] == k) { - weight = VECTOR(*weights_seen_elt)[l]; - /* Tag this weight so we can ignore it later on if the opposite - * directed edge is found. It's ok to retag */ - VECTOR(*weights_seen_elt)[l] = -1; - break; - } - } - - /* The opposite edge has already been union-ed, set this one to -1 */ - if (weight < 0) { - VECTOR(*weights)[eid] = 0; - continue; - } - - /* Weight of the opposite edge, if found */ - weight_inv = 0; - neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, k); - weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, k); - no_of_neis = igraph_vector_int_size(neighbors_seen_elt); - for (l=0; l < no_of_neis; l++) { - if (VECTOR(*neighbors_seen_elt)[l] == i) { - weight_inv = VECTOR(*weights_seen_elt)[l]; - /* Tag this weight so we can ignore it later on if the opposite - * directed edge is found. It's ok to retag */ - VECTOR(*weights_seen_elt)[l] = -1; - break; - } - } - - /* The opposite edge has already been union-ed, set this one to -1 */ - if (weight_inv < 0) { - VECTOR(*weights)[eid] = 0; - continue; - } - - /* First time this edge or its opposite are seen, set the W */ - VECTOR(*weights)[eid] = weight + weight_inv - weight * weight_inv; - } - - igraph_vector_list_destroy(&weights_seen); - igraph_vector_int_list_destroy(&neighbors_seen); - igraph_vector_int_destroy(&eids); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} - - -/* Helper function to compute a and b parameters (smoothing probability metric in embedding space) */ -static igraph_error_t igraph_i_umap_get_ab_residuals(igraph_vector_t *residuals, - igraph_real_t *squared_sum_res, igraph_integer_t nr_points, igraph_real_t a, - igraph_real_t b, igraph_vector_t *powb, const igraph_vector_t *x, igraph_real_t min_dist) -{ - igraph_real_t tmp; - - *squared_sum_res = 0; - for (igraph_integer_t i = 0; i < nr_points; i++) { - /* The ideal probability is: - * - * P(d) = d < min_dist ? 1 : e^{-(d - min_dist)} - * - * which is the same as the high-dimensional probability, except - * min_dist plays the role of rho and sigma is fixed at 1. However, - * this function has a kink at min_dist (first derivative is not - * continuous). So we smoothen it with: - * - * Q(d) = ( 1 + a*d^2b )^-1 - * - * which is quite similar throughout for appropriate a and b. Notice - * that we do not need to smoothen the high-dimensional probability - * function because the vertices are not moved in the high-dimensional - * space, so there is no need for differentiating that function. - * - * The residual is of course: - * - * Q(d) - P(d) = ( 1 + a*d^2b )^-1 - [ d < min_dist ? 1 : e^{-(d - min_dist)} ] - * - * This function also sets the auxiliary vector powb. - * */ - VECTOR(*powb)[i] = pow(VECTOR(*x)[i], 2 * b); - tmp = 1 / (1 + a * VECTOR(*powb)[i]); - tmp -= VECTOR(*x)[i] <= min_dist ? 1 : exp(-(VECTOR(*x)[i] - min_dist)); - VECTOR(*residuals)[i] = tmp; - *squared_sum_res += tmp * tmp; - } - return IGRAPH_SUCCESS; -} - -/* UMAP minimizes the cross-entropy between probability of being a true edge in - * high and low dimensions. For the low-dimensional computation, it uses a smooth - * function of the Euclidean distance between two vertices: - * - * P(d) = (1 + a*d^2b)^-1 - * - * where d is the distance and a and b are hyperparameters that basically determine - * the cutoff distance at which the probability starts to decrease. - * - * We fit these two parameters using nonlinear least squares (Gauss-Newton + line search) - * on a grid of artificial distances. There is only one user-chosen input argument that - * determines this fit, called min_dist, which is approximately the cutoff distance we - * are trying to achieve. - * - * ADVANCED NOTE: - * In a way, the whole UMAP layout is invariant upon scaling transformations, of course, - * so min_dist is basically meaningless. Another way to see this is that for any pair - * (a,b) that minimize the least squares for dist_min, we can easily find a solution for - * a new dist_min2 := alpha * dist_min: - * - * P(d, a, b) = (1 + a*d^2b)^-1 - * - * P(alpha * d, a', b') = (1 + a'*(alpha * d)^2b' )^-1 - * - * that is: - * - * a*d^2b = a'*alpha^2b'*d^2b' for each d >= 0. - * - * So for d = 1 -> a = a'*alpha^2b' - * and for d = sqrt(2) -> a*2^b = a'*alpha^2b'*2^b' - * - * which solves as: - * - * b' = b - * a' = a / alpha^2b - * - * For instance, if b = 1, a -> 0.01*a moves the fit a decade towards larger min_dist, - * and a -> 100*a moves the fit a decade towards smaller min_dist. - * */ -igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, igraph_real_t *a_p, igraph_real_t *b_p) -{ - /* Grid points */ - igraph_vector_t x; - /* Make a lattice from 0 to 3 * sigma with 300 points. This is what - * umap.umap_.fit_ab_params does, but sigma is fixed to 1.0 here since - * that's the default value used in scanpy and by virtually everyone */ - igraph_integer_t nr_points = 300; - igraph_real_t end_point = 3.0; - /* Initial values takes as reasonable assumptions from typical min_dist values */ - igraph_real_t b = 0.8; - igraph_real_t a = 1.8; - /* deltas */ - igraph_real_t da, db; - /* Residuals */ - igraph_vector_t residuals; - igraph_real_t squared_sum_res, squared_sum_res_old, squared_sum_res_tmp; - /* Needed for the Gauss-Newton search */ - igraph_matrix_t jacobian, jTj, jTr; - igraph_real_t tol = 0.001; - igraph_real_t maxiter = 100; - /* Auxiliary vars */ - igraph_real_t tmp; - igraph_vector_t powb; - int lapack_info; - - /* Distance lattice */ - IGRAPH_VECTOR_INIT_FINALLY(&x, nr_points); - /* Residuals */ - IGRAPH_VECTOR_INIT_FINALLY(&residuals, nr_points); - /* First derivatives, for the fitting (direction) */ - IGRAPH_MATRIX_INIT_FINALLY(&jacobian, nr_points, 2); - /* Composite matrices/vectors for linear least squares at each iteration */ - IGRAPH_MATRIX_INIT_FINALLY(&jTj, 2, 2); - IGRAPH_MATRIX_INIT_FINALLY(&jTr, 2, 1); - /* Auxiliary vars for convenience */ - IGRAPH_VECTOR_INIT_FINALLY(&powb, nr_points); - - /* Distance |x-y| (this is a lattice, there are no actual x and y) */ - for (igraph_integer_t i = 0; i < nr_points; i++) { - VECTOR(x)[i] = (end_point / nr_points) * i + 0.001; /* added a 0.001 to prevent NaNs */ - } - - /* Initialize squared_sum_res_old to a dummy value to prevent some compilers - * from complaining about uninitialized values */ - squared_sum_res_old = IGRAPH_INFINITY; - -#ifdef UMAP_DEBUG - printf("start fit_ab\n"); -#endif - for (igraph_integer_t iter = 0; iter < maxiter; iter++) { - IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a, b, - &powb, &x, min_dist)); - - /* break if good fit (conergence to truth) */ - if (squared_sum_res < tol * tol) { -#ifdef UMAP_DEBUG - printf("convergence to zero (wow!)\n"); -#endif - break; - } - /* break if no change (convergence) */ - if ((iter > 0) && fabs(sqrt(squared_sum_res_old) - sqrt(squared_sum_res)) < tol) { -#ifdef UMAP_DEBUG - printf("no-change absolute convergence\n"); -#endif - break; - } - - /* Jacobian (first derivatives) of squared residuals at (a, b) */ - for (igraph_integer_t i = 0; i < nr_points; i++) { - tmp = 1 + a * VECTOR(powb)[i]; - MATRIX(jacobian, i, 0) = - 2 * VECTOR(powb)[i] / tmp / tmp; - MATRIX(jacobian, i, 1) = MATRIX(jacobian, i, 0) * a * log(VECTOR(x)[i]) * 2; - } - - /* At each iteration, we want to minimize the linear approximation of the sum of squared - * residuals: - * - * sum_i (Ji @ d(a,b) -r_i)^2 - * - * Putting the first derivative to zero results in a linear system of 2 equations - * (for a and b): - * - * sum_i J_i^T @ J_i @ d(a,b) = sum_i J_i^T r_i - * * - * or more compactly: - * - * J^T @ J @ d(a,b) = J^T @ r - * - * where J_T is the transpose of the Jacobian. Defining A := J^T @ J, B = J^T @ r: - * - * A @ d(a,b) = B - * - * This can be solved for d(a,b) using LAPACK within igraph - * */ - /* Compute A and B, i.e. J^T @ J and J^T @ r */ - MATRIX(jTj, 0, 0) = MATRIX(jTj, 0, 1) = MATRIX(jTj, 1, 0) = MATRIX(jTj, 1, 1) = 0; - MATRIX(jTr, 0, 0) = MATRIX(jTr, 1, 0) = 0; - for (igraph_integer_t i = 0; i < nr_points; i++) { - for (igraph_integer_t j1 = 0; j1 < 2; j1++) { - for (igraph_integer_t j2 = 0; j2 < 2; j2++) { - MATRIX(jTj, j1, j2) += MATRIX(jacobian, i, j1) * MATRIX(jacobian, i, j2); - } - MATRIX(jTr, j1, 0) += MATRIX(jacobian, i, j1) * VECTOR(residuals)[i]; - } - } - /* LAPACK puts solution into jTr */ - IGRAPH_CHECK(igraph_lapack_dgesv(&jTj, 0, &jTr, &lapack_info)); - - /* This might go wrong, in which case we should fail graciously */ - if (lapack_info != 0) { - IGRAPH_ERROR("Singular matrix in the estimation of a and b for UMAP", IGRAPH_EINVAL); - } - - da = -MATRIX(jTr, 0, 0); - db = -MATRIX(jTr, 1, 0); - - /* Improvement over GN: rough exponential line search for best delta - * start from largest change, and keep shrinking as long as we are going down - * */ - squared_sum_res_old = squared_sum_res; - IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a + da, - b + db, &powb, &x, min_dist)); - -#ifdef UMAP_DEBUG - printf("start line search, SSR before delta: %g, current SSR:, %g\n", squared_sum_res_old, - squared_sum_res); -#endif - for (igraph_integer_t k = 0; k < 30; k++) { - /* Try new parameters */ - da /= 2.0; - db /= 2.0; - squared_sum_res_tmp = squared_sum_res; - IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, - a + da, b + db, &powb, &x, min_dist)); - - /* Compare and if we are going back uphill, undo last step and break */ -#ifdef UMAP_DEBUG - printf("during line search, k = %d, old SSR:, %g, new SSR (half a,b):, %g\n", k, - squared_sum_res_tmp, squared_sum_res); -#endif - if (squared_sum_res > squared_sum_res_tmp - tol) { - da *= 2; - db *= 2; - break; - } - } -#ifdef UMAP_DEBUG - printf("end of line search and iteration, squared_sum_res: %g \n\n", squared_sum_res_tmp); -#endif - - /* assign a, b*/ - a += da; - b += db; - - } - - /* Free memory and tidy up stack */ - igraph_vector_destroy(&x); - igraph_vector_destroy(&residuals); - igraph_matrix_destroy(&jacobian); - igraph_matrix_destroy(&jTj); - igraph_matrix_destroy(&jTr); - igraph_vector_destroy(&powb); - IGRAPH_FINALLY_CLEAN(6); - -#ifdef UMAP_DEBUG - printf("a, b: %g %g\n", a, b); -#endif - - *a_p = a; - *b_p = b; - - return IGRAPH_SUCCESS; - -} - -/* cross-entropy */ -#ifdef UMAP_DEBUG -static igraph_error_t igraph_i_umap_compute_cross_entropy(const igraph_t *graph, - const igraph_vector_t *umap_weights, const igraph_matrix_t *layout, igraph_real_t a, igraph_real_t b, - igraph_real_t *cross_entropy) { - - igraph_real_t mu, nu, xd, yd, sqd; - igraph_integer_t from, to; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_vertices = igraph_vcount(graph); - igraph_matrix_t edge_seen; - - IGRAPH_MATRIX_INIT_FINALLY(&edge_seen, no_of_vertices, no_of_vertices); - - /* Measure the (variable part of the) cross-entropy terms for debugging: - * 1. - sum_edge_e mu(e) * log(nu(e)) - * 2. - sum_edge_e (1 - mu(e)) * log(1 - nu(e)) - * NOTE: the sum goes over the whole adjacency matrix, i.e. all potential edges, - * not just the actual edges. That is because otherwise there's no benefit from - * repelling unconnected edges. - * */ - *cross_entropy = 0; - for (igraph_integer_t eid = 0; eid < no_of_edges; eid++) { - mu = VECTOR(*umap_weights)[eid]; - - /* Find vertices */ - from = IGRAPH_FROM(graph, eid); - to = IGRAPH_TO(graph, eid); - /* Find distance in layout space */ - xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); - yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); - sqd = xd * xd + yd * yd; - /* Find probability associated with distance using fitted Phi */ - nu = 1.0 / (1 + a * pow(sqd, b)); - - /* Term 1: entropy from the edges */ - if (mu > 0) - *cross_entropy -= mu * log(nu); - /* Term 2: entropy from the missing edges */ - if (mu < 1) - *cross_entropy -= (1 - mu) * log(1 - nu); - - MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; - } - /* Add the entropy from the missing edges */ - for (igraph_integer_t from = 0; from < no_of_vertices; from++) { - for (igraph_integer_t to = 0; to < from; to++) { - if (MATRIX(edge_seen, from, to) > 0) { - continue; - } - - /* Find distance in layout space */ - xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); - yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); - sqd = xd * xd + yd * yd; - - /* Find probability associated with distance using fitted Phi */ - nu = 1.0 / (1 + a * pow(sqd, b)); - - /* Term 2*/ - *cross_entropy -= log(1 - nu); - - MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; - } - } - - igraph_matrix_destroy(&edge_seen); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} -#endif /* UMAP_DEBUG */ - - -/* clip forces to avoid too rapid shifts */ -static igraph_real_t igraph_i_umap_clip_force(igraph_real_t force, igraph_real_t limit) { - return force > limit ? limit : (force < -limit ? -limit : force); -} - -static igraph_real_t igraph_i_umap_attract( - igraph_real_t dsq, - igraph_real_t a, - igraph_real_t b) -{ - return - (2 * a * b * pow(dsq, b - 1.)) / (1. + a * pow(dsq, b)); -} - -static igraph_real_t igraph_i_umap_repel( - igraph_real_t dsq, - igraph_real_t a, - igraph_real_t b) -{ - igraph_real_t dsq_min = UMAP_CORRECT_DISTANCE_REPULSION * UMAP_CORRECT_DISTANCE_REPULSION; - - return (2 * b) / (dsq_min + dsq) / (1. + a * pow(dsq, b)); -} - -static igraph_error_t igraph_i_umap_apply_forces( - const igraph_t *graph, - const igraph_vector_t *umap_weights, - igraph_matrix_t *layout, - igraph_real_t a, - igraph_real_t b, - igraph_real_t learning_rate, - igraph_bool_t avoid_neighbor_repulsion, - igraph_integer_t negative_sampling_rate, - igraph_integer_t epoch, - igraph_integer_t epochs, - igraph_vector_t *next_epoch_sample_per_edge) -{ - igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); - igraph_integer_t ndim = igraph_matrix_ncol(layout); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t from, to, nneis, eid; - igraph_vector_t from_emb, to_emb, delta; - igraph_real_t force = 0, dsq, force_d; - /* The following is only used for small graphs, to avoid repelling your neighbors - * For large sparse graphs, it's not necessary. For large dense graphs, you should - * not be doing UMAP. - * */ - igraph_vector_int_t neis, negative_vertices; - igraph_integer_t n_negative_vertices = (no_of_vertices - 1 < negative_sampling_rate) ? (no_of_vertices - 1) : negative_sampling_rate; - - /* Initialize vectors */ - IGRAPH_VECTOR_INIT_FINALLY(&from_emb, ndim); - IGRAPH_VECTOR_INIT_FINALLY(&to_emb, ndim); - IGRAPH_VECTOR_INIT_FINALLY(&delta, ndim); - - if (avoid_neighbor_repulsion) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - } - IGRAPH_VECTOR_INT_INIT_FINALLY(&negative_vertices, 0); - - /* Iterate over edges. Stronger edges are sampled more often */ - for (eid = 0; eid < no_of_edges; eid++) { - /* Zero-weight edges do not affect vertex positions. They can - * also emerge during the weight symmetrization. */ - if (VECTOR(*umap_weights)[eid] <= 0) { - continue; - } - - /* We sample all and only edges that are supposed to be moved at this time */ - if ((VECTOR(*next_epoch_sample_per_edge)[eid] - epoch) >= 1) { - continue; - } - - /* set next epoch at which this edge will be sampled */ - VECTOR(*next_epoch_sample_per_edge)[eid] += 1.0 / VECTOR(*umap_weights)[eid]; - - /* we move all vertices on one end of the edges, then we come back for - * the vertices on the other end. This way we don't move both ends at the - * same time, which is almost a wasted move since they attract each other */ - int swapflag = (int)(RNG_UNIF01() > 0.5); - int swapflag_end = swapflag + 2; - for (; swapflag < swapflag_end; swapflag++) { - - /* half the time, swap the from/to, otherwise some vertices are never moved. - * This has to do with the graph representation within igraph */ - if (swapflag % 2) { - from = IGRAPH_FROM(graph, eid); - to = IGRAPH_TO(graph, eid); - } else { - to = IGRAPH_FROM(graph, eid); - from = IGRAPH_TO(graph, eid); - } - - - /* Current coordinates of both vertices */ - dsq = 0; - for (igraph_integer_t d = 0; d != ndim; d++) { - VECTOR(from_emb)[d] = MATRIX(*layout, from, d); - VECTOR(to_emb)[d] = MATRIX(*layout, to, d); - VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); - dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; - } - - /* Apply attractive force since they are neighbors */ - /* NOTE: If they are already together, no force needed */ - if (dsq >= UMAP_MIN_DISTANCE_ATTRACTION * UMAP_MIN_DISTANCE_ATTRACTION) { - force = igraph_i_umap_attract(dsq, a, b); - for (igraph_integer_t d = 0; d != ndim; d++) { - force_d = force * VECTOR(delta)[d]; - /* clip force to avoid too rapid change */ - force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); - - #ifdef UMAP_DEBUG - fprintf(stderr, "force attractive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); - #endif - - MATRIX(*layout, from, d) += learning_rate * force_d; - } - } - - /* Random other nodes repel the focal vertex */ - IGRAPH_CHECK(igraph_random_sample(&negative_vertices, - 0, no_of_vertices - 2, n_negative_vertices)); - for (igraph_integer_t j = 0; j < n_negative_vertices; j++) { - - IGRAPH_ALLOW_INTERRUPTION(); - - /* Get random neighbor */ - to = VECTOR(negative_vertices)[j]; - /* obviously you cannot repel yourself */ - if (to >= from) { - to++; - } - - /* do not repel neighbors for small graphs, for big graphs this - * does not matter as long as the k in knn << number of vertices */ - if (avoid_neighbor_repulsion) { - /* NOTE: the efficiency of this step could be improved but it - * should be only used for small graphs anyway, so it's fine */ - igraph_bool_t skip = 0; - IGRAPH_CHECK(igraph_incident(graph, &neis, from, IGRAPH_ALL)); - nneis = igraph_vector_int_size(&neis); - for (igraph_integer_t k = 0; k < nneis; k++) { - igraph_integer_t eid2 = VECTOR(neis)[k]; - igraph_integer_t from2, to2; - from2 = IGRAPH_FROM(graph, eid2); - to2 = IGRAPH_TO(graph, eid2); - if (((from2 == from) && (to2 == to)) || ((from2 == to) && (from == to2))) { - skip = 1; - break; - } - } - if (skip == 1) { - continue; - } - } - - /* Get layout of random neighbor and gradient in embedding */ - dsq = 0; - for (igraph_integer_t d = 0; d != ndim; d++) { - VECTOR(to_emb)[d] = MATRIX(*layout, to, d); - VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); - dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; - } - - /* This repels the other vertex assuming it's a negative example - * that is no weight, no edge */ - force = igraph_i_umap_repel(dsq, a, b); - /* The repulsive force is already *away* from the other (non-neighbor) vertex */ - for (igraph_integer_t d = 0; d != ndim; d++) { - force_d = force * VECTOR(delta)[d]; - - /* clip force to avoid too rapid change */ - force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); - - #ifdef UMAP_DEBUG - fprintf(stderr, "force repulsive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); - #endif - - MATRIX(*layout, from, d) += learning_rate * force_d; - } - } - } - } - - /* Free vectors */ - igraph_vector_int_destroy(&negative_vertices); - igraph_vector_destroy(&from_emb); - igraph_vector_destroy(&to_emb); - igraph_vector_destroy(&delta); - IGRAPH_FINALLY_CLEAN(4); - - /* Free vector of neighbors if needed */ - if (avoid_neighbor_repulsion) { - igraph_vector_int_destroy(&neis); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} - -/* Edges with heavier weight/higher probability should be sampled more often. In - * other words, vertices at each end of those edges should be moved more often. If the - * edge weight is 1.0, which happens to each nearest neighbor due to the correction via - * rho, that vertices at the end of that edge are moved each single epoch. Conversely, - * vertices at the end of weak edges can be moved only once in a while. */ -static igraph_error_t igraph_i_umap_optimize_layout_stochastic_gradient( - const igraph_t *graph, - const igraph_vector_t *umap_weights, - igraph_real_t a, - igraph_real_t b, - igraph_matrix_t *layout, - igraph_integer_t epochs, - igraph_integer_t negative_sampling_rate) { - - igraph_real_t learning_rate = 1; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_t next_epoch_sample_per_edge; - -#ifdef UMAP_DEBUG - igraph_real_t cross_entropy, cross_entropy_old; -#endif - - IGRAPH_VECTOR_INIT_FINALLY(&next_epoch_sample_per_edge, no_of_edges); - - /* Explicit avoidance of neighbor repulsion, only useful in small graphs - * which are never very sparse. This is because negative sampling as implemented - * relies on an approximation that only works if the graph is sparse, which is never - * quite true for small graphs (i.e. |V| << |E| << |V|^2 is hard to judge if - * |V| is small) */ - igraph_bool_t avoid_neighbor_repulsion = 0; - if (igraph_vcount(graph) < 100) { - avoid_neighbor_repulsion = 1; - } - - /* Measure the (variable part of the) cross-entropy terms for debugging: - * 1. - sum_edge_e mu(e) * log(nu(e)) - * 2. + sum_edge_e (1 - mu(e)) * log(1 - nu(e)) - * The latter is approximated by negative sampling as: - * 2b. + sum_random_ij 1 * log(1 - nu_ij) - * whereby the mu = 0 because we assume there's no edge between i and j, and nu_ij - * is basically their distance in embedding space, lensed through the probability - * function Phi. - * */ -#ifdef UMAP_DEBUG - igraph_umap_compute_cross_entropy( - graph, umap_weights, layout, a, b, &cross_entropy); -#endif - - for (igraph_integer_t e = 0; e < epochs; e++) { - /* Apply (stochastic) forces */ - igraph_i_umap_apply_forces( - graph, - umap_weights, - layout, - a, b, - learning_rate, - avoid_neighbor_repulsion, - negative_sampling_rate, - e, - epochs, - &next_epoch_sample_per_edge); - -#ifdef UMAP_DEBUG - /* Recompute CE and check how it's going*/ - cross_entropy_old = cross_entropy; - igraph_umap_compute_cross_entropy( - graph, umap_weights, layout, a, b, &cross_entropy); - - printf("Cross-entropy before shift: %g, after shift: %g\n", cross_entropy_old, cross_entropy); -#endif - - /* Adjust learning rate */ - learning_rate = 1.0 - (igraph_real_t)(e + 1) / epochs; - } - - igraph_vector_destroy(&next_epoch_sample_per_edge); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/* Center layout around (0,0) at the end, just for convenience */ -static igraph_error_t igraph_i_umap_center_layout(igraph_matrix_t *layout) { - igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); - igraph_real_t xm = 0, ym = 0; - - /* Compute center */ - xm = 0; - ym = 0; - for (igraph_integer_t i = 0; i < no_of_vertices; i++) { - xm += MATRIX(*layout, i, 0); - ym += MATRIX(*layout, i, 1); - } - xm /= no_of_vertices; - ym /= no_of_vertices; - - /* Shift vertices */ - for (igraph_integer_t i = 0; i < no_of_vertices; i++) { - MATRIX(*layout, i, 0) -= xm; - MATRIX(*layout, i, 1) -= ym; - } - - return IGRAPH_SUCCESS; -} - - -/* This is the main function that works for any dimensionality of the embedding - * (currently hard-constrained to 2 or 3 ONLY in the initialization). */ -static igraph_error_t igraph_i_layout_umap( - const igraph_t *graph, - igraph_matrix_t *res, - igraph_bool_t use_seed, - const igraph_vector_t *distances, - igraph_real_t min_dist, - igraph_integer_t epochs, - igraph_integer_t ndim, - igraph_bool_t distances_are_weights) { - - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_vertices = igraph_vcount(graph); - /* probabilities of each edge being a real connection */ - igraph_vector_t weights; - igraph_vector_t *weightsp; - /* The smoothing parameters given min_dist */ - igraph_real_t a, b; - /* How many repulsions for each attraction */ - igraph_integer_t negative_sampling_rate = 5; - - /* Check input arguments */ - if (min_dist < 0) { - IGRAPH_ERRORF("Minimum distance must not be negative, got %g.", - IGRAPH_EINVAL, min_dist); - } - - if (epochs < 0) { - IGRAPH_ERRORF("Number of epochs must be non-negative, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, epochs); - } - - if ((ndim != 2) && (ndim != 3)) { - IGRAPH_ERRORF("Number of dimensions must be 2 or 3, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, ndim); - - } - - /* Compute weights (exponential weights) from distances if required. - * If the weights have already been computed, they are stored in - * the "distances" vector and we can recycle the pointer. */ - if (distances_are_weights) { - weightsp = (igraph_vector_t *) distances; - } else { - IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_edges); - IGRAPH_CHECK(igraph_layout_umap_compute_weights( - graph, distances, &weights)); - weightsp = &weights; - } - /* From now on everything lives in probability space, it does not matter whether - * the original graph was weighted/distanced or unweighted */ - - /* Compute initial layout if required. If a seed layout is used, then just - * check that the dimensions of the layout make sense. */ - if (use_seed) { - if ((igraph_matrix_nrow(res) != no_of_vertices) || (igraph_matrix_ncol(res) != ndim)) { - if (!distances_are_weights) { - igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(1); - } - IGRAPH_ERRORF("Seed layout should have %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions, got %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions.", - IGRAPH_EINVAL, no_of_vertices, ndim, - igraph_matrix_nrow(res), - igraph_matrix_ncol(res)); - } - - /* Trivial graphs (0 or 1 nodes) with seed - do nothing */ - if (no_of_vertices <= 1) { - if (!distances_are_weights) { - igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(1); - } - return IGRAPH_SUCCESS; - } - } else { - /* Trivial graphs (0 or 1 nodes) beget trivial - but valid - layouts */ - if (no_of_vertices <= 1) { - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vertices, ndim)); - igraph_matrix_null(res); - if (!distances_are_weights) { - igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(1); - } - return IGRAPH_SUCCESS; - } - - /* Skip spectral embedding for now (see #1971), initialize at random */ - if (ndim == 2) { - igraph_layout_random(graph, res); - } else { - igraph_layout_random_3d(graph, res); - } - } - - RNG_BEGIN(); - - /* Fit a and b parameter to find smooth approximation to - * probability distribution in embedding space */ - IGRAPH_CHECK(igraph_i_umap_fit_ab(min_dist, &a, &b)); - - /* Minimize cross-entropy between high-d and low-d probability - * distributions */ - IGRAPH_CHECK(igraph_i_umap_optimize_layout_stochastic_gradient( - graph, - weightsp, - a, b, - res, - epochs, - negative_sampling_rate)); - - if (!distances_are_weights) { - igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(1); - } - RNG_END(); - - /* Center layout */ - IGRAPH_CHECK(igraph_i_umap_center_layout(res)); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_layout_umap - * \brief Layout using Uniform Manifold Approximation and Projection (UMAP). - * - * \experimental - * - * UMAP is mostly used to embed high-dimensional vectors in a low-dimensional space - * (most commonly 2D). The algorithm is probabilistic and introduces nonlinearities, - * unlike e.g. PCA and similar to T-distributed Stochastic Neighbor Embedding (t-SNE). - * Nonlinearity helps "cluster" very similar vectors together without imposing a - * global geometry on the embedded space (e.g. a rigid rotation + compression in PCA). - * UMAP uses graphs as intermediate data structures, hence it can be used as a - * graph layout algorithm as well. - * - * - * - * The general UMAP workflow is to start from vectors, compute a sparse distance - * graph that only contains edges between simiar points (e.g. a k-nearest neighbors - * graph), and then convert these distances into exponentially decaying weights - * between 0 and 1 that are larger for points that are closest neighbors in the - * distance graph. If a graph without any distances associated to the edges is used, - * all weights will be set to 1. - * - * - * - * If you are trying to use this function to embed high-dimensional vectors, you should - * first compute a k-nearest neighbors graph between your vectors and compute the - * associated distances, and then call this function on that graph. If you already - * have a distance graph, or you have a graph with no distances, you can call this - * function directly. If you already have a graph with meaningful weights - * associated to each edge, you can also call this function, but set the argument - * distances_are_weights to true. To compute weights from distances - * without computing the layout, see \ref igraph_layout_umap_compute_weights(). - * - * - * References: - * - * - * Leland McInnes, John Healy, and James Melville. https://arxiv.org/abs/1802.03426 - - * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is - * typically a sparse graph with only edges for the shortest distances stored, e.g. - * a k-nearest neighbors graph. - * \param res Pointer to the n by 2 matrix where the layout coordinates will be stored. - * \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 distances Pointer to a vector of distances associated with the graph edges. - * If this argument is \c NULL, all weights will be set to 1. - * \param min_dist A fudge parameter that decides how close two unconnected vertices - * can be in the embedding before feeling a repulsive force. It must not be - * negative. Typical values are between 0 and 1. - * \param epochs Number of iterations of the main stochastic gradient descent loop on - * the cross-entropy. Typical values are between 30 and 500. - * \param distances_are_weights Whether to use precomputed weights. If - * true, the "distances" vector contains precomputed weights. If false (the - * typical use case), this function will compute weights from distances and - * then use them to compute the layout. - * \return Error code. - */ -igraph_error_t igraph_layout_umap(const igraph_t *graph, - igraph_matrix_t *res, - igraph_bool_t use_seed, - const igraph_vector_t *distances, - igraph_real_t min_dist, - igraph_integer_t epochs, - igraph_bool_t distances_are_weights) { - return igraph_i_layout_umap(graph, res, use_seed, - distances, min_dist, epochs, 2, distances_are_weights); -} - - -/** - * \function igraph_layout_umap_3d - * \brief 3D layout using UMAP. - * - * \experimental - * - * - * - * This is the 3D version of the UMAP algorithm - * (see \ref igraph_layout_umap() for the 2D version). - * - * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is - * typically a directed, sparse graph with only edges for the shortest distances - * stored, e.g. a k-nearest neighbors graph with the edges going from each focal - * vertex to its neighbors. However, it can also be an undirected graph. If the - * \p distances_are_weights is \c true, this is treated as an undirected graph. - * \param res Pointer to the n by 3 matrix where the layout coordinates will be stored. - * \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 distances Pointer to a vector of distances associated with the graph edges. - * If this argument is \c NULL, all edges are assumed to have the same distance. - * \param min_dist A fudge parameter that decides how close two unconnected vertices - * can be in the embedding before feeling a repulsive force. It must not be - * negative. Typical values are between 0 and 1. - * \param epochs Number of iterations of the main stochastic gradient descent loop on - * the cross-entropy. Typical values are between 30 and 500. - * \param distances_are_weights Whether to use precomputed weights. If \c false (the - * typical use case), this function will compute weights from distances and - * then use them to compute the layout. If \c true, the "distances" vector contains - * precomputed weights, including possibly some weights equal to zero that are - * inconsequential for the layout optimization. - * \return Error code. - */ -igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, - igraph_matrix_t *res, - igraph_bool_t use_seed, - const igraph_vector_t *distances, - igraph_real_t min_dist, - igraph_integer_t epochs, - igraph_bool_t distances_are_weights) { - return igraph_i_layout_umap(graph, res, use_seed, - distances, min_dist, epochs, 3, distances_are_weights); -} diff --git a/src/vendor/cigraph/src/linalg/arpack.c b/src/vendor/cigraph/src/linalg/arpack.c index 12d70f8dc49..1b9f3a57da5 100644 --- a/src/vendor/cigraph/src/linalg/arpack.c +++ b/src/vendor/cigraph/src/linalg/arpack.c @@ -23,21 +23,19 @@ */ #include "igraph_arpack.h" +#include "igraph_memory.h" +#include "igraph_random.h" #include "core/interruption.h" #include "linalg/arpack_internal.h" -#include "igraph_memory.h" -#include "igraph_random.h" - #include #include #include -#include /* The ARPACK example file dssimp.f is used as a template */ -static igraph_error_t igraph_i_arpack_err_dsaupd(int error) { +static int igraph_i_arpack_err_dsaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; @@ -59,7 +57,7 @@ static igraph_error_t igraph_i_arpack_err_dsaupd(int error) { } } -static igraph_error_t igraph_i_arpack_err_dseupd(int error) { +static int igraph_i_arpack_err_dseupd(int error) { switch (error) { case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; @@ -81,7 +79,7 @@ static igraph_error_t igraph_i_arpack_err_dseupd(int error) { } -static igraph_error_t igraph_i_arpack_err_dnaupd(int error) { +static int igraph_i_arpack_err_dnaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; @@ -102,7 +100,7 @@ static igraph_error_t igraph_i_arpack_err_dnaupd(int error) { } } -static igraph_error_t igraph_i_arpack_err_dneupd(int error) { +static int igraph_i_arpack_err_dneupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_REORDER; case -1: return IGRAPH_ARPACK_NPOS; @@ -123,50 +121,9 @@ static igraph_error_t igraph_i_arpack_err_dneupd(int error) { } } -/* Pristine ARPACK options object that is not exposed to the user; this is used - * as a template for \c igraph_i_arpack_options_default when the user requests - * a pointer to the default object */ -const static igraph_arpack_options_t igraph_i_arpack_options_pristine = { - /* .bmat = */ { 'I' }, - /* .n = */ 0, - /* .which = */ { 'X', 'X' }, - /* .nev = */ 1, - /* .tol = */ 0, - /* .ncv = */ 0, /* 0 means "automatic" */ - /* .ldv = */ 0, - /* .ishift = */ 1, - /* .mxiter = */ 3000, - /* .nb = */ 1, - /* .mode = */ 1, - /* .start = */ 0, - /* .lworl = */ 0, - /* .sigma = */ 0, - /* .sigmai = */ 0, - /* .info = */ 0, - /* .ierr = */ 0, - /* .noiter = */ 0, - /* .nconv = */ 0, - /* .numop = */ 0, - /* .numopb = */ 0, - /* .numreo = */ 0, - /* .iparam = */ { - /* same as ishift: */ 1, - 0, - /* same as mxiter: */ 3000, - /* same as nb: */ 1, - 0, - 0, - /* same as mode: */ 1 - /* the rest are all zeros */ - }, - /* .ipntr = */ { 0 /* the rest are all zeros */ } -}; - -static IGRAPH_THREAD_LOCAL igraph_arpack_options_t igraph_i_arpack_options_default; - /** * \function igraph_arpack_options_init - * \brief Initialize ARPACK options. + * Initialize ARPACK options * * Initializes ARPACK options, set them to default values. * You can always pass the initialized \ref igraph_arpack_options_t @@ -175,20 +132,16 @@ static IGRAPH_THREAD_LOCAL igraph_arpack_options_t igraph_i_arpack_options_defau * 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 = igraph_i_arpack_options_pristine; - o->bmat[0] = 'I'; o->n = 0; /* needs to be updated! */ o->which[0] = 'X'; o->which[1] = 'X'; @@ -211,29 +164,9 @@ void igraph_arpack_options_init(igraph_arpack_options_t *o) { o->iparam[8] = 0; o->iparam[9] = 0; o->iparam[10] = 0; } -/** - * \function igraph_arpack_options_get_default - * \brief Returns a pointer to a "default" ARPACK options object. - * - * This function is used by other igraph functions taking an \ref igraph_arpack_options_t - * object as an argument to get a reference to a pre-initialized "default" - * ARPACK options object when the user passes \c NULL instead of a real ARPACK - * options object. The object returned from this function is reset to a pristine - * state with every call to \c igraph_arpack_options_get_default(). - * - * - * The object returned from this function must \em not be destroyed. - * - * Time complexity: O(1). - */ -igraph_arpack_options_t* igraph_arpack_options_get_default(void) { - igraph_i_arpack_options_default = igraph_i_arpack_options_pristine; - return &igraph_i_arpack_options_default; -} - /** * \function igraph_arpack_storage_init - * \brief Initialize ARPACK storage. + * Initialize ARPACK storage * * You only need this function if you want to run multiple eigenvalue * calculations using ARPACK, and want to spare the memory @@ -242,10 +175,9 @@ igraph_arpack_options_t* igraph_arpack_options_get_default(void) { * 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. - * + * 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. @@ -258,28 +190,18 @@ igraph_arpack_options_t* igraph_arpack_options_get_default(void) { * Time complexity: O(maxncv*(maxldv+maxn)). */ -igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, - igraph_integer_t maxncv, igraph_integer_t maxldv, +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 */ - if (maxn > INT_MAX) { - IGRAPH_ERROR("Maximum order of matrices too large for ARPACK.", IGRAPH_EOVERFLOW); - } - if (maxncv > INT_MAX) { - IGRAPH_ERROR("Maximum NCV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); - } - if (maxldv > INT_MAX) { - IGRAPH_ERROR("Maximum LDV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); - } - 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); /* LCOV_EXCL_LINE */ \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -304,12 +226,12 @@ igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_int #undef CHECKMEM IGRAPH_FINALLY_CLEAN(7); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_arpack_storage_destroy - * \brief Deallocate ARPACK storage. + * Deallocate ARPACK storage * * \param s The \ref igraph_arpack_storage_t object for which the * memory will be deallocated. @@ -339,7 +261,7 @@ void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) { * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with * these. */ -static igraph_error_t igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extra, +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; @@ -372,7 +294,7 @@ static igraph_error_t igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, * "Solver" for 1x1 eigenvalue problems since ARPACK sometimes blows up with * these. */ -static igraph_error_t igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extra, +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; @@ -405,7 +327,7 @@ static igraph_error_t igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, * "Solver" for 2x2 nonsymmetric eigenvalue problems since ARPACK sometimes * blows up with these. */ -static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extra, +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]; @@ -413,8 +335,8 @@ static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, 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 = false; - igraph_bool_t complex_evals = false; + igraph_bool_t swap_evals = 0; + igraph_bool_t complex_evals = 0; int nev = options->nev; if (nev <= 0) { @@ -470,7 +392,7 @@ static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, if (options->which[0] == 'S') { if (options->which[1] == 'M') { /* eval1 must be the one with the smallest magnitude */ - swap_evals = (igraph_complex_abs(eval1) > igraph_complex_abs(eval2)); + 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)); @@ -483,7 +405,7 @@ static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, } else if (options->which[0] == 'L') { if (options->which[1] == 'M') { /* eval1 must be the one with the largest magnitude */ - swap_evals = (igraph_complex_abs(eval1) < igraph_complex_abs(eval2)); + 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)); @@ -553,7 +475,7 @@ static igraph_error_t igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, * "Solver" for symmetric 2x2 eigenvalue problems since ARPACK sometimes blows * up with these. */ -static igraph_error_t igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extra, +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]; @@ -638,9 +560,9 @@ static igraph_error_t igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, return IGRAPH_SUCCESS; } -igraph_error_t 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) { +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]; @@ -663,15 +585,9 @@ igraph_error_t igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *ve sort[0] = 'L'; sort[1] = 'M'; } else if (which('B', 'E')) { sort[0] = 'L'; sort[1] = 'A'; - } else { - /* None of the above, no sorting. These 'X' values are - * ignored by ARPACK, but we set them anyway in order to - * avoid an uninitialized 'sort' which would trigger - * checkers such as MemorySanitizer. */ - sort[0] = 'X'; sort[1] = 'X'; } - IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); + 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); @@ -723,10 +639,10 @@ igraph_error_t igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *ve igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, +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) { @@ -754,17 +670,11 @@ igraph_error_t igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *ve sort[0] = 'S'; sort[1] = 'I'; } else if (which('S', 'I')) { sort[0] = 'L'; sort[1] = 'I'; - } else { - /* None of the above, no sorting. These 'X' values are - * ignored by ARPACK, but we set them anyway in order to - * avoid an uninitialized 'sort' which would trigger - * checkers such as MemorySanitizer. */ - sort[0] = 'X'; sort[1] = 'X'; } #undef which - IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); + 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); @@ -846,7 +756,7 @@ igraph_error_t igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *ve } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -897,7 +807,7 @@ static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* /** * \function igraph_arpack_rssolve - * \brief ARPACK solver for symmetric matrices. + * \brief ARPACK solver for symmetric matrices * * This is the ARPACK solver for symmetric matrices. Please use * \ref igraph_arpack_rnsolve() for non-symmetric matrices. @@ -929,13 +839,13 @@ static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* * are found in O(n) time as well. */ -igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, +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 = false; + igraph_bool_t free_them = 0; int *select, i; int ido = 0; @@ -998,7 +908,7 @@ igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, #define CHECKMEM(x) \ if (!x) { \ - IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -1155,12 +1065,12 @@ igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, IGRAPH_FREE(v); IGRAPH_FINALLY_CLEAN(7); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_arpack_rnsolve - * \brief ARPACK solver for non-symmetric matrices. + * \brief ARPACK solver for non-symmetric matrices * * Please always consider calling \ref igraph_arpack_rssolve() if your * matrix is symmetric, it is much faster. @@ -1205,13 +1115,13 @@ igraph_error_t igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, * are found in O(n) time as well. */ -igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, +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 = false; + igraph_bool_t free_them = 0; int *select, i; int ido = 0; @@ -1277,7 +1187,7 @@ igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, #define CHECKMEM(x) \ if (!x) { \ - IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -1444,12 +1354,12 @@ igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, IGRAPH_FREE(v); IGRAPH_FINALLY_CLEAN(8); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_arpack_unpack_complex - * \brief Makes the result of the non-symmetric ARPACK solver more readable. + * \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 @@ -1480,21 +1390,22 @@ igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, * matrix. */ -igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, - igraph_integer_t nev) { +int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + long int nev) { - igraph_integer_t nodes = igraph_matrix_nrow(vectors); - igraph_integer_t no_evs = igraph_matrix_nrow(values); - igraph_integer_t i, j; - igraph_integer_t new_vector_pos, vector_pos; + 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); + 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_ERROR("`nev' too large, we don't have that many in `values'", IGRAPH_EINVAL); } @@ -1544,7 +1455,7 @@ igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_mat } } igraph_matrix_destroy(vectors); - IGRAPH_CHECK(igraph_matrix_init_copy(vectors, &new_vectors)); + IGRAPH_CHECK(igraph_matrix_copy(vectors, &new_vectors)); igraph_matrix_destroy(&new_vectors); IGRAPH_FINALLY_CLEAN(1); diff --git a/src/vendor/cigraph/src/linalg/arpack_internal.h b/src/vendor/cigraph/src/linalg/arpack_internal.h index 9be196c90c0..f318371e66b 100644 --- a/src/vendor/cigraph/src/linalg/arpack_internal.h +++ b/src/vendor/cigraph/src/linalg/arpack_internal.h @@ -28,12 +28,9 @@ include this header. */ -#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" -__BEGIN_DECLS - #ifndef INTERNAL_ARPACK #define igraphdsaupd_ dsaupd_ #define igraphdseupd_ dseupd_ @@ -229,6 +226,4 @@ int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal, #endif -__END_DECLS - #endif /* ARPACK_INTERNAL_H */ diff --git a/src/vendor/cigraph/src/linalg/blas.c b/src/vendor/cigraph/src/linalg/blas.c index 8835e6fb25d..8a4d017a030 100644 --- a/src/vendor/cigraph/src/linalg/blas.c +++ b/src/vendor/cigraph/src/linalg/blas.c @@ -22,10 +22,10 @@ */ +#include "igraph_error.h" #include "igraph_blas.h" -#include "linalg/blas_internal.h" -#include +#include "linalg/blas_internal.h" /** * \function igraph_blas_dgemv @@ -45,24 +45,18 @@ * * Time complexity: O(nk) if the matrix is of size n x k * - * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, - * \c IGRAPH_SUCCESS otherwise. * \sa \ref igraph_blas_dgemv_array if you have arrays instead of * vectors. * * \example examples/simple/blas.c */ -igraph_error_t 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) { +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; - if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { - IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); - } - m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); @@ -76,86 +70,6 @@ igraph_error_t igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, VECTOR(*x), &inc, &beta, VECTOR(*y), &inc); #endif - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_blas_dgemm - * \brief Matrix-matrix multiplication using BLAS. - * - * This function is a somewhat more user-friendly interface to - * the \c dgemm function in BLAS. \c dgemm calculates - * alpha*a*b + beta*c, where a, b and c are matrices, of which a and b - * can be transposed. - * - * \param transpose_a whether to transpose the matrix \p a - * \param transpose_b whether to transpose the matrix \p b - * \param alpha the constant \c alpha - * \param a the matrix \c a - * \param b the matrix \c b - * \param beta the constant \c beta - * \param c the matrix \c c. The result will also be stored here. - * If beta is zero, c will be resized to fit the result. - * - * Time complexity: O(n m k) where matrix a is of size n × k, and matrix b is of - * size k × m. - * - * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, - * \c IGRAPH_EINVAL if the matrices have incompatible sizes, - * \c IGRAPH_SUCCESS otherwise. - * - * \example examples/simple/blas_dgemm.c - */ -igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, igraph_bool_t transpose_b, - igraph_real_t alpha, const igraph_matrix_t *a, const igraph_matrix_t *b, - igraph_real_t beta, igraph_matrix_t *c) { - char trans_a = transpose_a ? 'T' : 'N'; - char trans_b = transpose_b ? 'T' : 'N'; - int m, n, k, lda, ldb, ldc; - igraph_integer_t nrow_oa = transpose_a ? igraph_matrix_ncol(a) : igraph_matrix_nrow(a); - igraph_integer_t ncol_oa = transpose_a ? igraph_matrix_nrow(a) : igraph_matrix_ncol(a); - igraph_integer_t nrow_ob = transpose_b ? igraph_matrix_ncol(b) : igraph_matrix_nrow(b); - igraph_integer_t ncol_ob = transpose_b ? igraph_matrix_nrow(b) : igraph_matrix_ncol(b); - - if (ncol_oa != nrow_ob) { - IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId - " matrices cannot be multiplied, incompatible dimensions.", IGRAPH_EINVAL, - nrow_oa, ncol_oa, nrow_ob, ncol_ob); - } - if (beta != 0 && (ncol_oa != igraph_matrix_ncol(c) || nrow_oa != igraph_matrix_nrow(c))) { - IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId - " matrices cannot be added, incompatible dimensions.", IGRAPH_EINVAL, - nrow_oa, ncol_ob, igraph_matrix_nrow(c), igraph_matrix_ncol(c)); - } - if (nrow_oa > INT_MAX || ncol_oa > INT_MAX) { - IGRAPH_ERROR("Matrix A too large for BLAS.", IGRAPH_EOVERFLOW); - } - if (ncol_ob > INT_MAX) { - IGRAPH_ERROR("Matrix B too large for BLAS.", IGRAPH_EOVERFLOW); - } - if (beta == 0) { - IGRAPH_CHECK(igraph_matrix_resize(c, nrow_oa, ncol_ob)); - } - - m = (int) nrow_oa; - k = (int) ncol_oa; - n = (int) ncol_ob; - lda = (int) igraph_matrix_nrow(a); - ldb = (int) igraph_matrix_nrow(b); - ldc = (int) igraph_matrix_nrow(c); - - -#ifdef HAVE_GFORTRAN - igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), - &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc, - /*trans_a_len*/ 1, /*trans_b_len*/ 1); -#else - igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), - &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc); -#endif - - return IGRAPH_SUCCESS; } /** @@ -177,23 +91,16 @@ igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, igraph_bool_t transp * * Time complexity: O(nk) if the matrix is of size n x k * - * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, - * \c IGRAPH_SUCCESS otherwise. - * * \sa \ref igraph_blas_dgemv if you have vectors instead of * arrays. */ -igraph_error_t igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, +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; - if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { - IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); - } - m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); @@ -204,8 +111,6 @@ igraph_error_t igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t al igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, (igraph_real_t*)x, &inc, &beta, y, &inc); #endif - - return IGRAPH_SUCCESS; } /** @@ -218,11 +123,7 @@ igraph_error_t igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t al * Time complexity: O(n) where n is the length of the vector. */ igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v) { - if (igraph_vector_size(v) > INT_MAX) { - IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); - } - - int n = (int) igraph_vector_size(v); + int n = igraph_vector_size(v); int one = 1; return igraphdnrm2_(&n, VECTOR(*v), &one); } @@ -239,14 +140,10 @@ igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v) { * * \example examples/simple/blas.c */ -igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, +int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t *res) { - if (igraph_vector_size(v1) > INT_MAX) { - IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); - } - - int n = (int) igraph_vector_size(v1); + int n = igraph_vector_size(v1); int one = 1; if (igraph_vector_size(v2) != n) { @@ -256,5 +153,5 @@ igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *res = igraphddot_(&n, VECTOR(*v1), &one, VECTOR(*v2), &one); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/linalg/blas_internal.h b/src/vendor/cigraph/src/linalg/blas_internal.h index 63f8d4434d0..71810b05699 100644 --- a/src/vendor/cigraph/src/linalg/blas_internal.h +++ b/src/vendor/cigraph/src/linalg/blas_internal.h @@ -29,12 +29,9 @@ include this header. */ -#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" -__BEGIN_DECLS - #ifndef INTERNAL_BLAS #define igraphdaxpy_ daxpy_ #define igraphdger_ dger_ @@ -90,6 +87,4 @@ double igraphdnrm2_(int *n, double *x, int *incx); double igraphddot_(int *n, double *dx, int *incx, double *dy, int *incy); -__END_DECLS - #endif diff --git a/src/vendor/cigraph/src/linalg/eigen.c b/src/vendor/cigraph/src/linalg/eigen.c index 52ee6c711bd..f42dc86cd52 100644 --- a/src/vendor/cigraph/src/linalg/eigen.c +++ b/src/vendor/cigraph/src/linalg/eigen.c @@ -28,12 +28,11 @@ #include "igraph_interface.h" #include "igraph_adjlist.h" -#include #include #include #include -static igraph_error_t igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, +static int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, int n, void *extra, igraph_matrix_t *res) { @@ -55,23 +54,18 @@ static igraph_error_t igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t * igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_matrix_t *A, +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; - int n; - - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - n = (int) igraph_matrix_nrow(A); IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); @@ -138,25 +132,20 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_mat igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A, +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 i, w = 0, n = (int) igraph_matrix_nrow(A); igraph_real_t small; int p1, p2, pr = 0; - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - n = (int) igraph_matrix_nrow(A); - IGRAPH_VECTOR_INIT_FINALLY(&val, 0); if (vectors) { @@ -219,18 +208,15 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_mat igraph_vector_destroy(&val); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A, +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? */ - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } int n = (int) igraph_matrix_nrow(A); int il = n - which->howmany + 1; @@ -239,10 +225,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_mat /*il=*/ il, /*iu=*/ n, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A, +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) { @@ -255,10 +241,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_mat /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, +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) { @@ -267,14 +253,9 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_mat igraph_matrix_t vec1, vec2; igraph_vector_t val1, val2; - int n; + int n = (int) igraph_matrix_nrow(A); int p1 = 0, p2 = which->howmany / 2, pr = 0; - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - n = (int) igraph_matrix_nrow(A); - IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); @@ -339,10 +320,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_mat igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, +static int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, igraph_vector_t *values, igraph_matrix_t *vectors) { @@ -352,10 +333,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_ma /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A, +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) { @@ -367,10 +348,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_mat /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A, +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) { @@ -381,10 +362,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_ma /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, +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, @@ -398,15 +379,9 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix /* First we need to create a dense square matrix */ if (A) { - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - n = (int) igraph_matrix_nrow(A); /* TODO: n isn't used after this assignment */ + n = (int) igraph_matrix_nrow(A); } else if (sA) { - if (igraph_sparsemat_nrow(sA) > INT_MAX) { - IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - n = (int) igraph_sparsemat_nrow(sA); /* TODO: n isn't used after this assignment */ + 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)); @@ -463,7 +438,7 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { @@ -471,7 +446,7 @@ typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { const igraph_sparsemat_t *sA; } igraph_i_eigen_matrix_sym_arpack_data_t; -static igraph_error_t igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, +static int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -479,8 +454,8 @@ static igraph_error_t igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, (igraph_i_eigen_matrix_sym_arpack_data_t *) extra; if (data->A) { - IGRAPH_CHECK(igraph_blas_dgemv_array(/*transpose=*/ 0, /*alpha=*/ 1.0, - data->A, from, /*beta=*/ 0.0, to)); + 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); @@ -488,10 +463,10 @@ static igraph_error_t igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, igraph_vector_null(&vto); igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A, +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, @@ -563,10 +538,10 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_mat igraph_vector_destroy(&tmpvalues); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A, +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, @@ -642,7 +617,7 @@ static igraph_error_t igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, values, vectors)); - return IGRAPH_SUCCESS; + return 0; } } @@ -674,8 +649,7 @@ typedef struct igraph_i_eml_cmp_t { 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -720,8 +694,7 @@ static int igraph_i_eigen_matrix_lapack_cmp_lm(void *extra, const void *a, 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -765,8 +738,7 @@ 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -805,8 +777,7 @@ 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -843,8 +814,7 @@ 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -882,8 +852,7 @@ 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; - igraph_integer_t *aa = (igraph_integer_t*) a; - igraph_integer_t *bb = (igraph_integer_t*) b; + 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]; @@ -928,7 +897,7 @@ static int igraph_i_eigen_matrix_lapack_cmp_si(void *extra, const void *a, } \ } while (0) -static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, +static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, const igraph_vector_t *imag, const igraph_matrix_t *compressed, const igraph_eigen_which_t *which, @@ -936,24 +905,22 @@ static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t igraph_matrix_complex_t *vectors) { igraph_vector_int_t idx; igraph_vector_t mag; - igraph_bool_t hasmag = false; - int nev; + igraph_bool_t hasmag = 0; + int nev = (int) igraph_vector_size(real); int howmany = 0, start = 0; - igraph_integer_t i; + int i; igraph_i_eigen_matrix_lapack_cmp_t cmpfunc = 0; igraph_i_eml_cmp_t vextra; void *extra; - if (igraph_vector_size(real) > INT_MAX) { - IGRAPH_ERROR("Number of eigenvalues too large for LAPACK.", IGRAPH_EOVERFLOW); - } - nev = (int) igraph_vector_size(real); - vextra.mag = &mag; vextra.real = real; vextra.imag = imag; extra = &vextra; + IGRAPH_CHECK(igraph_vector_int_init(&idx, nev)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &idx); + switch (which->pos) { case IGRAPH_EIGEN_LM: INITMAG(); @@ -999,8 +966,9 @@ static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t break; } - IGRAPH_CHECK(igraph_vector_int_init_range(&idx, 0, nev)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &idx); + for (i = 0; i < nev; i++) { + VECTOR(idx)[i] = i; + } igraph_qsort_r(VECTOR(idx), (size_t) nev, sizeof(VECTOR(idx)[0]), extra, cmpfunc); @@ -1013,17 +981,17 @@ static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t if (values) { IGRAPH_CHECK(igraph_vector_complex_resize(values, howmany)); for (i = 0; i < howmany; i++) { - igraph_integer_t x = VECTOR(idx)[start + i]; + int x = VECTOR(idx)[start + i]; VECTOR(*values)[i] = igraph_complex(VECTOR(*real)[x], VECTOR(*imag)[x]); } } if (vectors) { - igraph_integer_t n = igraph_matrix_nrow(compressed); + int n = (int) igraph_matrix_nrow(compressed); IGRAPH_CHECK(igraph_matrix_complex_resize(vectors, n, howmany)); for (i = 0; i < howmany; i++) { - igraph_integer_t j, x = VECTOR(idx)[start + i]; + int j, x = VECTOR(idx)[start + i]; if (VECTOR(*imag)[x] == 0) { /* real eigenvalue */ for (j = 0; j < n; j++) { @@ -1049,17 +1017,17 @@ static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t igraph_vector_int_destroy(&idx); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t *A, +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; - igraph_integer_t n = igraph_matrix_nrow(A); + int n = (int) igraph_matrix_nrow(A); int info = 1; IGRAPH_VECTOR_INIT_FINALLY(&valuesreal, n); @@ -1084,11 +1052,68 @@ static igraph_error_t igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t igraph_vector_destroy(&valuesreal); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + 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 igraph_error_t igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, +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, @@ -1102,14 +1127,8 @@ static igraph_error_t igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, /* We need to create a dense square matrix first */ if (A) { - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } n = (int) igraph_matrix_nrow(A); } else if (sA) { - if (igraph_sparsemat_nrow(sA) > INT_MAX) { - IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } n = (int) igraph_sparsemat_nrow(sA); IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &mA); @@ -1120,19 +1139,54 @@ static igraph_error_t igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, IGRAPH_FINALLY(igraph_matrix_destroy, &mA); } - IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_common(myA, which, - values, - vectors)); + 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 IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_checks(const igraph_matrix_t *A, +static int igraph_i_eigen_checks(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n) { @@ -1151,7 +1205,7 @@ static igraph_error_t igraph_i_eigen_checks(const igraph_matrix_t *A, } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1160,7 +1214,7 @@ static igraph_error_t igraph_i_eigen_checks(const igraph_matrix_t *A, * \example examples/simple/igraph_eigen_matrix_symmetric.c */ -igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, +int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -1184,31 +1238,35 @@ igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } - if (algorithm == IGRAPH_EIGEN_AUTO) { + switch (algorithm) { + case IGRAPH_EIGEN_AUTO: if (which->howmany == n || n < 100) { - algorithm = IGRAPH_EIGEN_LAPACK; + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, + extra, which, + values, vectors)); } else { - algorithm = IGRAPH_EIGEN_ARPACK; + IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, + extra, which, + options, storage, + values, vectors)); } - } - - switch (algorithm) { + break; case IGRAPH_EIGEN_LAPACK: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, extra, - which, values, vectors)); + which, values, + vectors)); break; case IGRAPH_EIGEN_ARPACK: - if (options == 0) { - options = igraph_arpack_options_get_default(); - } IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra, - which, options, storage, values, vectors)); + which, options, + storage, + values, vectors)); break; default: IGRAPH_ERROR("Unknown 'algorithm'", IGRAPH_EINVAL); } - return IGRAPH_SUCCESS; + return 0; } /** @@ -1216,7 +1274,7 @@ igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, * */ -igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, +int igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -1278,30 +1336,30 @@ igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to, +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; - igraph_integer_t i, j, nlen; + 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++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + int nei = VECTOR(*neis)[j]; to[i] += from[nei]; } } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, +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, @@ -1315,7 +1373,7 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, igraph_adjlist_t adjlist; void *extra = (void*) &adjlist; - igraph_integer_t n = igraph_vcount(graph); + int n = igraph_vcount(graph); if (!options) { IGRAPH_ERROR("`options' must be given for ARPACK algorithm", @@ -1338,9 +1396,6 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " "`ALL' eigenvalues", IGRAPH_UNIMPLEMENTED); } - if (n > INT_MAX) { - IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); - } switch (which->pos) { case IGRAPH_EIGEN_LM: @@ -1361,7 +1416,7 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, break; case IGRAPH_EIGEN_ALL: options->which[0] = 'L'; options->which[1] = 'M'; - options->nev = (int) n; + options->nev = n; break; case IGRAPH_EIGEN_BE: IGRAPH_ERROR("Eigenvectors from both ends with ARPACK", @@ -1383,8 +1438,8 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, break; } - options->n = (int) n; - options->ncv = 2 * options->nev < options->n ? 2 * options->nev : options->n; + 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); @@ -1395,7 +1450,7 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1403,7 +1458,7 @@ static igraph_error_t igraph_i_eigen_adjacency_arpack(const igraph_t *graph, * */ -igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, +int igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, @@ -1424,38 +1479,34 @@ igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } - if (algorithm == IGRAPH_EIGEN_AUTO) { - /* Select ARPACK unconditionally because nothing else is implemented yet */ - algorithm = IGRAPH_EIGEN_ARPACK; - } else if (algorithm == IGRAPH_EIGEN_COMP_AUTO) { - /* Select ARPACK unconditionally because nothing else is implemented yet */ - algorithm = IGRAPH_EIGEN_COMP_ARPACK; - } - 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: - if (options == 0) { - options = igraph_arpack_options_get_default(); - } 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: - if (options == 0) { - options = igraph_arpack_options_get_default(); - } IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ @@ -1464,7 +1515,8 @@ igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } - return IGRAPH_SUCCESS; + + return 0; } /** @@ -1472,7 +1524,7 @@ igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, * */ -igraph_error_t igraph_eigen_laplacian(const igraph_t *graph, +int igraph_eigen_laplacian(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, diff --git a/src/vendor/cigraph/src/linalg/lapack.c b/src/vendor/cigraph/src/linalg/lapack.c index c50fe55cd56..5652caf78e0 100644 --- a/src/vendor/cigraph/src/linalg/lapack.c +++ b/src/vendor/cigraph/src/linalg/lapack.c @@ -22,53 +22,8 @@ */ #include "igraph_lapack.h" -#include "linalg/lapack_internal.h" - -#include - -#define BASE_FORTRAN_INT -#include "igraph_pmt.h" -#include "igraph_vector_type.h" -#include "igraph_vector_pmt.h" -#include "../core/vector.pmt" -#include "igraph_pmt_off.h" -#undef BASE_FORTRAN_INT - -/* Converts a Fortran integer vector to an igraph vector */ -static igraph_error_t igraph_vector_int_update_from_fortran( - igraph_vector_int_t* vec, const igraph_vector_fortran_int_t* fortran_vec -) { - igraph_integer_t size = igraph_vector_fortran_int_size(fortran_vec); - IGRAPH_CHECK(igraph_vector_int_resize(vec, size)); - - for (igraph_integer_t i = 0; i < size; i++) { - VECTOR(*vec)[i] = VECTOR(*fortran_vec)[i]; - } - - return IGRAPH_SUCCESS; -} - -/* Allocates a Fortran integer vector from the contents of an igraph vector */ -static igraph_error_t igraph_vector_int_copy_to_fortran( - const igraph_vector_int_t* vec, igraph_vector_fortran_int_t* fortran_vec -) { - igraph_integer_t i, size = igraph_vector_int_size(vec); - - IGRAPH_CHECK(igraph_vector_fortran_int_resize(fortran_vec, size)); - - for (i = 0; i < size; i++) { - if (VECTOR(*vec)[i] > INT_MAX) { - IGRAPH_ERROR( - "Overflow error while copying an igraph integer vector to a " - "Fortran integer vector.", IGRAPH_EOVERFLOW - ); - } - VECTOR(*fortran_vec)[i] = (int) VECTOR(*vec)[i]; - } - - return IGRAPH_SUCCESS; -} +#include "linalg/lapack_internal.h" /** * \function igraph_lapack_dgetrf @@ -98,30 +53,22 @@ static igraph_error_t igraph_vector_int_copy_to_fortran( * Time complexity: TODO. */ -igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, int *info) { - int m; - int n; - size_t num_elts; - int lda; - igraph_vector_fortran_int_t vipiv; - - if (igraph_matrix_nrow(a) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - if (igraph_matrix_ncol(a) > INT_MAX) { - IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + 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)); } - m = (int) igraph_matrix_nrow(a); - n = (int) igraph_matrix_ncol(a); - num_elts = m < n ? m : n; - lda = m > 0 ? m : 1; - - IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, num_elts)); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); - - igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(vipiv), info); + igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(*myipiv), info); if (*info > 0) { IGRAPH_WARNING("LU: factor is exactly singular."); @@ -151,14 +98,12 @@ igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipi } } - if (ipiv) { - IGRAPH_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); + if (!ipiv) { + igraph_vector_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_fortran_int_destroy(&vipiv); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -175,8 +120,8 @@ igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipi * 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 + * \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. @@ -185,27 +130,14 @@ igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipi * Time complexity: TODO. */ -igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, +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 nrhs; - int lda; - int ldb; + 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; - igraph_vector_fortran_int_t vipiv; - - if (igraph_matrix_nrow(a) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - if (igraph_matrix_ncol(a) > INT_MAX) { - IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - - n = (int) igraph_matrix_nrow(a); - nrhs = (int) igraph_matrix_ncol(b); - lda = n > 0 ? n : 1; - ldb = n > 0 ? n : 1; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); @@ -213,23 +145,19 @@ igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix if (n != igraph_matrix_nrow(b)) { IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size.", IGRAPH_EINVAL); } - if (! igraph_vector_int_isininterval(ipiv, 1, n)) { + 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); } - - IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, igraph_vector_int_size(ipiv))); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); - IGRAPH_CHECK(igraph_vector_int_copy_to_fortran(ipiv, &vipiv)); - - igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(vipiv), + igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(*ipiv), VECTOR(b->data), &ldb, &info); - igraph_vector_fortran_int_destroy(&vipiv); - IGRAPH_FINALLY_CLEAN(1); - if (info < 0) { switch (info) { case -1: @@ -265,12 +193,12 @@ igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix } } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_lapack_dgesv - * \brief Solve system of linear equations with LU factorization. + * 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 @@ -282,7 +210,6 @@ igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix * 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. @@ -304,20 +231,14 @@ igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix * \example examples/simple/igraph_lapack_dgesv.c */ -igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, - igraph_matrix_t *b, int *info) { +int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + igraph_matrix_t *b, int *info) { - if (igraph_matrix_nrow(a) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } - if (igraph_matrix_ncol(a) > INT_MAX) { - IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } 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_fortran_int_t vipiv; + igraph_vector_int_t *myipiv = ipiv, vipiv; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); @@ -326,10 +247,13 @@ igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv IGRAPH_ERROR("Cannot LU solve matrix, RHS of wrong size.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, n)); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + 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(vipiv), + igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(*myipiv), VECTOR(b->data), &ldb, info); if (*info > 0) { @@ -366,28 +290,24 @@ igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv } } - if (ipiv) { - IGRAPH_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); + if (!ipiv) { + igraph_vector_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_fortran_int_destroy(&vipiv); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_lapack_dsyevr - * \brief Selected eigenvalues and optionally eigenvectors of a symmetric matrix. + * 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. - * + * 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. @@ -438,7 +358,7 @@ igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv * \example examples/simple/igraph_lapack_dsyevr.c */ -igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, +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, @@ -447,15 +367,12 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, igraph_matrix_t Acopy; char jobz = vectors ? 'V' : 'N', range, uplo = 'U'; - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } int n = (int) igraph_matrix_nrow(A), lda = n, ldz = n; int m, info; igraph_vector_t *myvalues = values, vvalues; - igraph_vector_fortran_int_t mysupport; + igraph_vector_int_t *mysupport = support, vsupport; igraph_vector_t work; - igraph_vector_fortran_int_t iwork; + igraph_vector_int_t iwork; int lwork = -1, liwork = -1; if (n != igraph_matrix_ncol(A)) { @@ -470,41 +387,43 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, IGRAPH_ERROR("Invalid 'il' and/or 'iu' values.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); - IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, 1)); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &iwork); + 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; } - - IGRAPH_CHECK(igraph_vector_fortran_int_init(&mysupport, 0)); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &mysupport); + 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_fortran_int_resize(&mysupport, 2 * n)); + 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_fortran_int_resize(&mysupport, 2 * vestimate)); + 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_fortran_int_resize(&mysupport, 2 * (iu - il + 1))); + IGRAPH_CHECK(igraph_vector_int_resize(mysupport, 2 * (iu - il + 1))); if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, iu - il + 1)); } @@ -513,7 +432,7 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, 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), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); if (info != 0) { @@ -523,11 +442,11 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, lwork = (int) VECTOR(work)[0]; liwork = VECTOR(iwork)[0]; IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); - IGRAPH_CHECK(igraph_vector_fortran_int_resize(&iwork, liwork)); + 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), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(*mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); if (info != 0) { @@ -541,29 +460,29 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, IGRAPH_CHECK(igraph_matrix_resize(vectors, n, m)); } if (support) { - IGRAPH_CHECK(igraph_vector_int_update_from_fortran(support, &mysupport)); IGRAPH_CHECK(igraph_vector_int_resize(support, m)); } - igraph_vector_fortran_int_destroy(&mysupport); - IGRAPH_FINALLY_CLEAN(1); - + if (!support) { + igraph_vector_int_destroy(&vsupport); + IGRAPH_FINALLY_CLEAN(1); + } if (!values) { igraph_vector_destroy(&vvalues); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_fortran_int_destroy(&iwork); + igraph_vector_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_lapack_dgeev - * \brief Eigenvalues and optionally eigenvectors of a non-symmetric matrix. + * 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 @@ -574,8 +493,8 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, * 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). + * 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 @@ -619,7 +538,7 @@ igraph_error_t igraph_lapack_dsyevr(const igraph_matrix_t *A, * \example examples/simple/igraph_lapack_dgeev.c */ -igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, +int igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, @@ -628,11 +547,6 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, char jobvl = vectorsleft ? 'V' : 'N'; char jobvr = vectorsright ? 'V' : 'N'; - igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ - - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } int n = (int) igraph_matrix_nrow(A); int lda = n, ldvl = n, ldvr = n, lwork = -1; igraph_vector_t work; @@ -644,7 +558,7 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_NONSQUARE); } - IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); @@ -670,8 +584,8 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda, VECTOR(*myreal), VECTOR(*myimag), - vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, - vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, VECTOR(work), &lwork, info); lwork = (int) VECTOR(work)[0]; @@ -679,8 +593,8 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, igraphdgeev_(&jobvl, &jobvr, &n, &MATRIX(Acopy, 0, 0), &lda, VECTOR(*myreal), VECTOR(*myimag), - vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, - vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, VECTOR(work), &lwork, info); if (*info < 0) { @@ -706,12 +620,12 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_lapack_dgeevx - * \brief Eigenvalues/vectors of nonsymmetric matrices, expert mode. + * 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. @@ -745,8 +659,7 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, * 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. Note that the eigenvectors obtained for the balanced - * matrix are backtransformed to those of \p A. + * Users' Guide. * * \param balance Scalar that indicated, whether the input matrix * should be balanced. Possible values: @@ -777,14 +690,12 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, * 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 the real and imaginary parts of the - * corresponding eigenvectors. + * 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 if not NULL, \p ilo and \p ihi point to integer values - * determined when A was + * \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 @@ -830,7 +741,7 @@ igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, * \example examples/simple/igraph_lapack_dgeevx.c */ -igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, +int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, @@ -846,27 +757,15 @@ igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, char jobvl = vectorsleft ? 'V' : 'N'; char jobvr = vectorsright ? 'V' : 'N'; char sense; - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } int n = (int) igraph_matrix_nrow(A); int lda = n, ldvl = n, ldvr = n, lwork = -1; igraph_vector_t work; - igraph_vector_fortran_int_t iwork; + 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; - igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ - int ilo_dummy; - int ihi_dummy; - if (ilo == NULL) { - ilo = &ilo_dummy; - } - if (ihi == NULL) { - ihi = &ihi_dummy; - } if (igraph_matrix_ncol(A) != n) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeevx).", IGRAPH_NONSQUARE); } @@ -899,12 +798,12 @@ igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, sense = 'B'; } - IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); - IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, n)); - IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &iwork); + IGRAPH_CHECK(igraph_vector_int_init(&iwork, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &iwork); if (!valuesreal) { IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); @@ -933,11 +832,11 @@ igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0), &lda, VECTOR(*myreal), VECTOR(*myimag), - vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, - vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, - rconde ? VECTOR(*rconde) : &dummy, - rcondv ? VECTOR(*rcondv) : &dummy, + rconde ? VECTOR(*rconde) : 0, + rcondv ? VECTOR(*rcondv) : 0, VECTOR(work), &lwork, VECTOR(iwork), info); lwork = (int) VECTOR(work)[0]; @@ -945,11 +844,11 @@ igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, igraphdgeevx_(&balanc, &jobvl, &jobvr, &sense, &n, &MATRIX(Acopy, 0, 0), &lda, VECTOR(*myreal), VECTOR(*myimag), - vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, - vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : 0, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : 0, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, - rconde ? VECTOR(*rconde) : &dummy, - rcondv ? VECTOR(*rcondv) : &dummy, + rconde ? VECTOR(*rconde) : 0, + rcondv ? VECTOR(*rcondv) : 0, VECTOR(work), &lwork, VECTOR(iwork), info); if (*info < 0) { @@ -977,21 +876,18 @@ igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_fortran_int_destroy(&iwork); + igraph_vector_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, +int igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result) { - if (igraph_matrix_nrow(A) > INT_MAX) { - IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); - } int n = (int) igraph_matrix_nrow(A); int lda = n; int lwork = -1; @@ -1012,10 +908,10 @@ igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, if (n <= 1) { IGRAPH_CHECK(igraph_matrix_update(result, A)); - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&tau, n - 1); @@ -1054,5 +950,5 @@ igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, } } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/linalg/lapack_internal.h b/src/vendor/cigraph/src/linalg/lapack_internal.h index 198faf32ad5..0ab117e96f8 100644 --- a/src/vendor/cigraph/src/linalg/lapack_internal.h +++ b/src/vendor/cigraph/src/linalg/lapack_internal.h @@ -29,12 +29,9 @@ include this header. */ -#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" -__BEGIN_DECLS - #ifndef INTERNAL_LAPACK #define igraphdgeevx_ dgeevx_ #define igraphdgeev_ dgeev_ @@ -183,6 +180,4 @@ int igraphdgehrd_(int *n, int *ilo, int *ihi, igraph_real_t *A, int *lda, igraph_real_t igraphddot_(int *n, igraph_real_t *dx, int *incx, igraph_real_t *dy, int *incy); -__END_DECLS - #endif diff --git a/src/vendor/cigraph/src/math/bfgs.c b/src/vendor/cigraph/src/math/bfgs.c new file mode 100644 index 00000000000..a272029849c --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/math/complex.c b/src/vendor/cigraph/src/math/complex.c index e895ab3059a..e47b1685d73 100644 --- a/src/vendor/cigraph/src/math/complex.c +++ b/src/vendor/cigraph/src/math/complex.c @@ -22,7 +22,7 @@ */ #include "igraph_complex.h" - +#include "core/math.h" #include /** @@ -43,10 +43,6 @@ igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) { return res; } -/** - * Deprecated in favour of igraph_complex_almost_equals(), which uses relative - * tolerances. Will be removed in 0.11. - */ igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, igraph_complex_t z2, igraph_real_t tol) { @@ -57,6 +53,12 @@ igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, 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); @@ -192,7 +194,6 @@ igraph_complex_t igraph_complex_inv(igraph_complex_t z) { } igraph_real_t igraph_complex_abs(igraph_complex_t z) { - /* hypot() avoids overflow at intermediate stages of the calculation */ return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z)); } diff --git a/src/vendor/cigraph/src/math/safe_intop.c b/src/vendor/cigraph/src/math/safe_intop.c deleted file mode 100644 index 0c8b168f55e..00000000000 --- a/src/vendor/cigraph/src/math/safe_intop.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - IGraph library. - Copyright (C) 2022 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 -*/ - -#include "math/safe_intop.h" - -/* Use IGRAPH_SAFE_ADD() instead unless there is a need to intercept errors. */ -igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { - IGRAPH_SAFE_ADD(a, b, res); - return IGRAPH_SUCCESS; -} - -/* Use IGRAPH_SAFE_MULT() instead unless there is a need to intercept errors. */ -igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { - IGRAPH_SAFE_MULT(a, b, res); - return IGRAPH_SUCCESS; -} - -/* Overflow-safe sum of integer vector elements. */ -igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res) { - igraph_integer_t i, n = igraph_vector_int_size(vec); - igraph_integer_t sum = 0; - for (i=0; i < n; ++i) { - IGRAPH_SAFE_ADD(sum, VECTOR(*vec)[i], &sum); - } - *res = sum; - return IGRAPH_SUCCESS; -} - -/* Overflow-safe product of integer vector elements. */ -igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res) { - igraph_integer_t i, n = igraph_vector_int_size(vec); - igraph_integer_t prod = 1; - for (i=0; i < n; ++i) { - IGRAPH_SAFE_MULT(prod, VECTOR(*vec)[i], &prod); - } - *res = prod; - return IGRAPH_SUCCESS; -} - -/** - * Rounds up an integer to the next power of 2, with overflow check. - * The result for 2, 3 and 4, respectively, would be 2, 4, and 4. - * This function must not be called with negative input. - * Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - */ -igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res) { - IGRAPH_ASSERT(k >= 0); - if (k == 0) { - *res = 0; - return IGRAPH_SUCCESS; - } - k--; - k |= k >> 1; - k |= k >> 2; - k |= k >> 4; - k |= k >> 8; - k |= k >> 16; -#if IGRAPH_INTEGER_SIZE == 32 - /* Nothing else to do. */ -#elif IGRAPH_INTEGER_SIZE == 64 - k |= k >> 32; -#else - /* If values other than 32 or 64 become allowed, - * this code will need to be updated. */ -# error "Unexpected IGRAPH_INTEGER_SIZE value." -#endif - if (k < IGRAPH_INTEGER_MAX) { - *res = k+1; - return IGRAPH_SUCCESS; - } else { - IGRAPH_ERRORF("Overflow when computing next power of 2 for %" IGRAPH_PRId ".", - IGRAPH_EOVERFLOW, k); - } -} - -/** - * Computes 2^k as an integer, with overflow check. - * This function must not be called with negative input. - */ -igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res) { - IGRAPH_ASSERT(k >= 0); - if (k > IGRAPH_INTEGER_SIZE-2) { - IGRAPH_ERRORF("Overflow when raising 2 to power %" IGRAPH_PRId ".", - IGRAPH_EOVERFLOW, k); - } - *res = (igraph_integer_t) 1 << k; - return IGRAPH_SUCCESS; -} - -/** - * Converts an igraph_real_t into an igraph_integer_t with range checks to - * protect from undefined behaviour. The input value is assumed to have no - * fractional part. - */ -static igraph_error_t igraph_i_safe_real_to_int(igraph_real_t value, igraph_integer_t *result) { - /* IGRAPH_INTEGER_MAX is one less than a power of 2, and may not be representable as - * a floating point number. Thus we cannot safely check that value <= IGRAPH_INTEGER_MAX, - * as this would convert IGRAPH_INTEGER_MAX to floating point, potentially chaning its value. - * Instead, we compute int_max_plus_1 = IGRAPH_INTEGER_MAX + 1, which is exactly representable - * since it is a power of 2, and check that value < int_max_plus_1. - * - * IGRAPH_INTEGER_MIN is a negative power of 2, so there is no such issue. - */ - const igraph_real_t int_max_plus_1 = 2.0 * (IGRAPH_INTEGER_MAX / 2 + 1); - const igraph_real_t int_min = (igraph_real_t) IGRAPH_INTEGER_MIN; - if (int_min <= value && value < int_max_plus_1) { - *result = (igraph_integer_t) value; - return IGRAPH_SUCCESS; - } else { - /* %.f ensures exact printing, %g would not */ - IGRAPH_ERRORF("Cannot convert %.f to integer, outside of representable range.", IGRAPH_EOVERFLOW, value); - } -} - -/** - * Converts an igraph_real_t into an igraph_integer_t with range checks to - * protect from undefined behaviour. The input value is converted into an - * integer with ceil(). - */ -igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t *result) { - return igraph_i_safe_real_to_int(ceil(value), result); -} - -/** - * Converts an igraph_real_t into an igraph_integer_t with range checks to - * protect from undefined behaviour. The input value is converted into an - * integer with floor(). - */ -igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t *result) { - return igraph_i_safe_real_to_int(floor(value), result); -} - -/** - * Converts an igraph_real_t into an igraph_integer_t with range checks to - * protect from undefined behaviour. The input value is converted into an - * integer with round(). - */ -igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result) { - return igraph_i_safe_real_to_int(round(value), result); -} diff --git a/src/vendor/cigraph/src/math/safe_intop.h b/src/vendor/cigraph/src/math/safe_intop.h deleted file mode 100644 index 94360c16de3..00000000000 --- a/src/vendor/cigraph/src/math/safe_intop.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - 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 _safe_a copy of the GNU General Public 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_SAFE_INTOP_H -#define IGRAPH_MATH_SAFE_INTOP_H - -#include "igraph_decls.h" -#include "igraph_error.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -#include "config.h" - -#include - -__BEGIN_DECLS - -/* Largest positive value for igraph_real_t that can safely represent integers. */ -#define IGRAPH_MAX_EXACT_REAL ((double)(1LL << DBL_MANT_DIG)) - -/* These macros raise an error if the operation would result in an overflow. - * They must only be used in functions that return an igraph_error_t. - * - * This code is based on the recommendation of - * https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard - */ - -#ifdef HAVE_BUILTIN_OVERFLOW - -#define IGRAPH_SAFE_ADD(a, b, res) \ - do { \ - igraph_integer_t _safe_a = (a), _safe_b = (b); \ - igraph_integer_t _safe_sum; \ - if (__builtin_add_overflow(_safe_a, _safe_b, &_safe_sum)) { \ - IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ - } \ - *(res) = _safe_sum; \ - } while (0) - -#define IGRAPH_SAFE_MULT(a, b, res) \ - do { \ - igraph_integer_t _safe_a = (a), _safe_b = (b); \ - igraph_integer_t _safe_prod; \ - if (__builtin_mul_overflow(_safe_a, _safe_b, &_safe_prod)) { \ - IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ - } \ - *(res) = _safe_prod; \ - } while (0) - -#else - -#define IGRAPH_SAFE_ADD(a, b, res) \ - do { \ - igraph_integer_t _safe_a = (a), _safe_b = (b); \ - igraph_integer_t _safe_sum; \ - if (((_safe_b > 0) && (_safe_a > (IGRAPH_INTEGER_MAX - _safe_b))) || \ - ((_safe_b < 0) && (_safe_a < (IGRAPH_INTEGER_MIN - _safe_b)))) { \ - IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ - } \ - _safe_sum = _safe_a+_safe_b; \ - *(res) = _safe_sum; \ - } while (0) - -#define IGRAPH_SAFE_MULT(a, b, res) \ - do { \ - igraph_integer_t _safe_a = (a), _safe_b = (b); \ - igraph_integer_t _safe_prod; \ - int err=0; \ - if (_safe_a > 0) { /* _safe_a is positive */ \ - if (_safe_b > 0) { /* _safe_a and _safe_b are positive */ \ - if (_safe_a > (IGRAPH_INTEGER_MAX / _safe_b)) { \ - err=1; \ - } \ - } else { /* _safe_a positive, _safe_b nonpositive */ \ - if (_safe_b < (IGRAPH_INTEGER_MIN / _safe_a)) { \ - err=1; \ - } \ - } /* _safe_a positive, _safe_b nonpositive */ \ - } else { /* _safe_a is nonpositive */ \ - if (_safe_b > 0) { /* _safe_a is nonpositive, _safe_b is positive */ \ - if (_safe_a < (IGRAPH_INTEGER_MIN / _safe_b)) { \ - err=1; \ - } \ - } else { /* _safe_a and _safe_b are nonpositive */ \ - if ( (_safe_a != 0) && (_safe_b < (IGRAPH_INTEGER_MAX / _safe_a))) { \ - err=1; \ - } \ - } /* End if _safe_a and _safe_b are nonpositive */ \ - } /* End if _safe_a is nonpositive */ \ - if (err) { \ - IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ - } \ - _safe_prod = _safe_a*_safe_b; \ - *(res) = _safe_prod; \ - } while (0) - -#endif /* HAVE_BUILTIN_OVERFLOW */ - -/* Overflow-safe calculation of "n choose 2" = n*(n-1) / 2, assuming that n >= 0. */ -#define IGRAPH_SAFE_N_CHOOSE_2(n, res) \ - do { \ - igraph_integer_t _safe_n = (n); \ - if (_safe_n % 2 == 0) IGRAPH_SAFE_MULT(_safe_n / 2, _safe_n - 1, res); \ - else IGRAPH_SAFE_MULT(_safe_n, (_safe_n - 1) / 2, res); \ - } while (0) - -igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t* result); -igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t* result); -igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result); - -igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res); -igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); -igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res); -igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res); - -__END_DECLS - -#endif /* IGRAPH_MATH_SAFE_INTOP_H */ diff --git a/src/vendor/cigraph/src/math/utils.c b/src/vendor/cigraph/src/math/utils.c index 71150359ecd..abf175c8326 100644 --- a/src/vendor/cigraph/src/math/utils.c +++ b/src/vendor/cigraph/src/math/utils.c @@ -21,17 +21,222 @@ */ -#include "igraph_complex.h" -#include "igraph_nongraph.h" #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 IGRAPH_THREAD_LOCAL int nlnrel = 0; + static IGRAPH_THREAD_LOCAL 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); } @@ -50,22 +255,22 @@ int igraph_is_neginf(double x) { /** * \function igraph_almost_equals - * \brief Compare two double-precision floats with a tolerance. + * 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 abs(a-b) / (abs(a) + abs(b)). The + * \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 True if the two floats are nearly equal to each other within - * the given level of tolerance, false otherwise. + * \return nonzero if the two floats are nearly equal to each other within + * the given level of tolerance, zero otherwise */ -igraph_bool_t igraph_almost_equals(double a, double b, double eps) { - return igraph_cmp_epsilon(a, b, eps) == 0; +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 @@ -86,20 +291,20 @@ igraph_bool_t igraph_almost_equals(double a, double b, double eps) { /** * \function igraph_cmp_epsilon - * \brief Compare two double-precision floats with a tolerance. + * 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 abs(a-b) / (abs(a) + abs(b)). The + * \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 + * \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. + * larger, negative number if the second float is larger */ int igraph_cmp_epsilon(double a, double b, double eps) { double diff; @@ -129,40 +334,6 @@ int igraph_cmp_epsilon(double a, double b, double eps) { } } -/** - * \function igraph_complex_almost_equals - * \brief Compare two complex numbers with a tolerance. - * - * Determines whether two complex numbers are "almost equal" - * to each other with a given level of tolerance on the relative error. - * - * \param a The first complex number. - * \param b The second complex number. - * \param eps The level of tolerance on the relative error. The relative - * error is defined as abs(a-b) / (abs(a) + abs(b)). The - * two numbers are considered equal if this is less than \c eps. - * - * \return True if the two complex numbers are nearly equal to each other within - * the given level of tolerance, false otherwise. - */ -igraph_bool_t igraph_complex_almost_equals(igraph_complex_t a, - igraph_complex_t b, - igraph_real_t eps) { - - igraph_real_t a_abs = igraph_complex_abs(a); - igraph_real_t b_abs = igraph_complex_abs(b); - igraph_real_t sum = a_abs + b_abs; - igraph_real_t abs_diff = igraph_complex_abs(igraph_complex_sub(a, b)); - - if (a_abs == 0 || b_abs == 0 || sum < DBL_MIN) { - return abs_diff < eps * DBL_MIN; - } else if (! isfinite(sum)) { - return abs_diff < (eps * a_abs + eps * b_abs); - } else { - return abs_diff/ sum < eps; - } -} - #ifdef __INTEL_COMPILER #pragma float_control(pop) #endif diff --git a/src/vendor/cigraph/src/misc/bipartite.c b/src/vendor/cigraph/src/misc/bipartite.c index 7c0a3f8a538..e26ceb489e0 100644 --- a/src/vendor/cigraph/src/misc/bipartite.c +++ b/src/vendor/cigraph/src/misc/bipartite.c @@ -31,7 +31,6 @@ #include "igraph_nongraph.h" #include "graph/attributes.h" -#include "math/safe_intop.h" /** * \section about_bipartite Bipartite networks in igraph @@ -68,17 +67,13 @@ * \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. May be \c NULL - * if not needed. + * 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. May be \c NULL - * if not needed. + * 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. May be \c NULL - * if not needed. + * 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. May be \c NULL - * if not needed. + * edges in the second projection is stored here. * \return Error code. * * \sa \ref igraph_bipartite_projection() to calculate the actual @@ -87,30 +82,33 @@ * 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 */ -igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0; + long int no_of_nodes = igraph_vcount(graph); + long int vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0; igraph_adjlist_t adjlist; - igraph_vector_int_t added; - igraph_integer_t i; + igraph_vector_long_t added; + long int i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&added, 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); for (i = 0; i < no_of_nodes; i++) { igraph_vector_int_t *neis1; - igraph_integer_t neilen1, j; - igraph_integer_t *ecptr; + long int neilen1, j; + long int *ecptr; if (VECTOR(*types)[i]) { vc2++; ecptr = &ec2; @@ -121,7 +119,7 @@ igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, neis1 = igraph_adjlist_get(&adjlist, i); neilen1 = igraph_vector_int_size(neis1); for (j = 0; j < neilen1; j++) { - igraph_integer_t k, neilen2, nei = VECTOR(*neis1)[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", @@ -129,7 +127,7 @@ igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, } neilen2 = igraph_vector_int_size(neis2); for (k = 0; k < neilen2; k++) { - igraph_integer_t nei2 = VECTOR(*neis2)[k]; + long int nei2 = (long int) VECTOR(*neis2)[k]; if (nei2 <= i) { continue; } @@ -142,57 +140,45 @@ igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, } } - if (vcount1) { - *vcount1 = vc1; - } - - if (ecount1) { - *ecount1 = ec1; - } - - if (vcount2) { - *vcount2 = vc2; - } - - if (ecount2) { - *ecount2 = ec2; - } + *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_int_destroy(&added); + igraph_vector_long_destroy(&added); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, +static int igraph_i_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj, int which, - igraph_vector_int_t *multiplicity) { + igraph_vector_t *multiplicity) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j, k; + long int no_of_nodes = igraph_vcount(graph); + long int i, j, k; igraph_integer_t remaining_nodes = 0; - igraph_vector_int_t vertex_perm, vertex_index; - igraph_vector_int_t edges; + igraph_vector_t vertex_perm, vertex_index; + igraph_vector_t edges; igraph_adjlist_t adjlist; igraph_vector_int_t *neis1, *neis2; - igraph_integer_t neilen1, neilen2; - igraph_vector_int_t added; + long int neilen1, neilen2; + igraph_vector_long_t added; igraph_vector_t mult; if (which < 0) { - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_perm, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&vertex_perm, no_of_nodes)); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_index, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&added, no_of_nodes); - + 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); @@ -200,24 +186,24 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, * 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_int_clear(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_int_push_back(&vertex_perm, i); + 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) { - igraph_integer_t new_i = VECTOR(vertex_index)[i]; - igraph_integer_t iedges = 0; + 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++) { - igraph_integer_t nei = VECTOR(*neis1)[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); @@ -225,7 +211,7 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, neis2 = igraph_adjlist_get(&adjlist, nei); neilen2 = igraph_vector_int_size(neis2); for (k = 0; k < neilen2; k++) { - igraph_integer_t nei2 = VECTOR(*neis2)[k], new_nei2; + long int nei2 = (long int) VECTOR(*neis2)[k], new_nei2; if (nei2 <= i) { continue; } @@ -241,28 +227,28 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, } iedges++; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, new_i)); + 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_int_push_back(&edges, nei2)); + old vertex ids here and rewrite it later */ + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei2)); } else { - new_nei2 = VECTOR(vertex_index)[nei2]; - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, new_nei2)); + 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 */ - igraph_integer_t now = igraph_vector_int_size(&edges); - igraph_integer_t from = now - iedges * 2; + long int now = igraph_vector_size(&edges); + long int from = now - iedges * 2; for (j = from; j < now; j += 2) { - igraph_integer_t nei2 = VECTOR(edges)[j + 1]; - igraph_integer_t new_nei2 = VECTOR(vertex_index)[nei2]; - igraph_integer_t m = VECTOR(mult)[nei2]; + 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_int_push_back(multiplicity, m)); + IGRAPH_CHECK(igraph_vector_push_back(multiplicity, m)); } } } /* if VECTOR(*type)[i] == which */ @@ -270,23 +256,23 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, igraph_vector_destroy(&mult); igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&added); - igraph_vector_int_destroy(&vertex_index); + 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_int_destroy(&edges); + 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_int_destroy(&vertex_perm); + igraph_vector_destroy(&vertex_perm); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -328,17 +314,19 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, * 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 */ -igraph_error_t igraph_bipartite_projection(const igraph_t *graph, +int igraph_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj1, igraph_t *proj2, - igraph_vector_int_t *multiplicity1, - igraph_vector_int_t *multiplicity2, + igraph_vector_t *multiplicity1, + igraph_vector_t *multiplicity2, igraph_integer_t probe1) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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 */ @@ -357,7 +345,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, } if (probe1 >= 0) { - t1 = VECTOR(*types)[probe1]; + t1 = VECTOR(*types)[(long int)probe1]; if (proj2) { t2 = 1 - t1; } else { @@ -373,7 +361,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2)); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } @@ -419,7 +407,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, * \sa \ref igraph_full() for non-bipartite full graphs. */ -igraph_error_t igraph_full_bipartite(igraph_t *graph, +int igraph_full_bipartite(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_bool_t directed, @@ -427,10 +415,10 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, igraph_integer_t nn1 = n1, nn2 = n2; igraph_integer_t no_of_nodes = nn1 + nn2; - igraph_vector_int_t edges; - igraph_integer_t no_of_edges; - igraph_integer_t ptr = 0; - igraph_integer_t i, j; + igraph_vector_t edges; + long int no_of_edges; + long int ptr = 0; + long int i, j; if (!directed) { no_of_edges = nn1 * nn2; @@ -440,7 +428,7 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, no_of_edges = nn1 * nn2 * 2; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); if (!directed || mode == IGRAPH_OUT) { @@ -473,7 +461,7 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, } IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, graph); @@ -487,7 +475,7 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -505,7 +493,7 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, * \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 + * 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. @@ -517,29 +505,34 @@ igraph_error_t igraph_full_bipartite(igraph_t *graph, * \example examples/simple/igraph_bipartite_create.c */ -igraph_error_t igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, - const igraph_vector_int_t *edges, +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_vector_bool_size(types); - igraph_integer_t no_of_edges = igraph_vector_int_size(edges); - igraph_integer_t i; + 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 (! igraph_vector_int_isininterval(edges, 0, no_of_nodes-1)) { - IGRAPH_ERROR("Invalid (negative or too large) vertex ID", IGRAPH_EINVVID); + 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) { - igraph_integer_t from = VECTOR(*edges)[i]; - igraph_integer_t to = VECTOR(*edges)[i + 1]; - igraph_bool_t t1 = VECTOR(*types)[from]; - igraph_bool_t t2 = VECTOR(*types)[to]; + 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); } @@ -550,53 +543,37 @@ igraph_error_t igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_incidence - * \brief Creates a bipartite graph from a bipartite adjacency matrix (deprecated alias). - * - * \deprecated-by igraph_biadjacency 0.10.5 - */ - -igraph_error_t 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 -) { - return igraph_biadjacency(graph, types, incidence, directed, mode, multiple); -} - -/** - * \function igraph_biadjacency - * \brief Creates a bipartite graph from a bipartite adjacency matrix. + * \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. A bipartite adjacency - * matrix is an \em n x \em m matrix, \em n and \em m are the number of vertices - * of the two types, respectively. Nonzero elements in the matrix denote + * 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 \c false, then a single edge is - * created for every non-zero element in the bipartite adjacency matrix. If \p - * multiple is \c true, then the matrix elements are rounded up + * \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. + * \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 input The bipartite adjacency matrix that serves as an input - * to this function. - * \param directed Specifies whether to create an undirected or a directed + * \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 @@ -604,39 +581,32 @@ igraph_error_t igraph_incidence( * 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 matrix elements. See details below. + * \param multiple How to interpret the incidence matrix elements. See + * details below. * \return Error code. * - * Time complexity: O(n*m), the size of the bipartite adjacency matrix. + * Time complexity: O(n*m), the size of the incidence matrix. */ -igraph_error_t igraph_biadjacency( - igraph_t *graph, igraph_vector_bool_t *types, - const igraph_matrix_t *input, igraph_bool_t directed, - igraph_neimode_t mode, igraph_bool_t multiple -) { +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_matrix_nrow(input); - igraph_integer_t n2 = igraph_matrix_ncol(input); + 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_int_t edges; - igraph_integer_t i, j, k; + igraph_vector_t edges; + long int i, j, k; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - - if (n1 > 0 && n2 > 0 && igraph_matrix_min(input) < 0) { - IGRAPH_ERRORF( - "Bipartite adjacencey matrix elements should be non-negative, found %g.", - IGRAPH_EINVAL, igraph_matrix_min(input) - ); - } + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); if (multiple) { for (i = 0; i < n1; i++) { for (j = 0; j < n2; j++) { - igraph_integer_t elem = ceil(MATRIX(*input, i, j)); - igraph_integer_t from, to; + long int elem = (long int) MATRIX(*incidence, i, j); + long int from, to; if (!elem) { continue; @@ -652,15 +622,15 @@ igraph_error_t igraph_biadjacency( if (mode != IGRAPH_ALL || !directed) { for (k = 0; k < elem; k++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + 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_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + 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)); } } } @@ -670,9 +640,9 @@ igraph_error_t igraph_biadjacency( for (i = 0; i < n1; i++) { for (j = 0; j < n2; j++) { - igraph_integer_t from, to; + long int from, to; - if (MATRIX(*input, i, j) != 0) { + if (MATRIX(*incidence, i, j) != 0) { if (mode == IGRAPH_IN) { from = n1 + j; to = i; @@ -681,13 +651,13 @@ igraph_error_t igraph_biadjacency( to = n1 + j; } if (mode != IGRAPH_ALL || !directed) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); } else { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + 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)); } } } @@ -696,7 +666,7 @@ igraph_error_t igraph_biadjacency( } IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, graph); @@ -709,27 +679,12 @@ igraph_error_t igraph_biadjacency( } IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_get_incidence - * \brief Convert a bipartite graph into a bipartite adjacency matrix (deprecated alias). - * - * \deprecated-by igraph_get_biadjacency 0.10.5 - */ - -igraph_error_t igraph_get_incidence(const igraph_t *graph, - const igraph_vector_bool_t *types, - igraph_matrix_t *res, - igraph_vector_int_t *row_ids, - igraph_vector_int_t *col_ids) { - return igraph_get_biadjacency(graph, types, res, row_ids, col_ids); -} - -/** - * \function igraph_get_biadjacency - * \brief Convert a bipartite graph into a bipartite adjacency matrix. + * \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 @@ -740,44 +695,44 @@ igraph_error_t igraph_get_incidence(const igraph_t *graph, * 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 + * 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 + * 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_biadjacency() for the opposite operation. + * \sa \ref igraph_incidence() for the opposite operation. */ -igraph_error_t igraph_get_biadjacency( - const igraph_t *graph, const igraph_vector_bool_t *types, - igraph_matrix_t *res, igraph_vector_int_t *row_ids, - igraph_vector_int_t *col_ids -) { +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t n1 = 0, n2 = 0, i; - igraph_vector_int_t perm; - igraph_integer_t p1, p2; - igraph_integer_t ignored_edges = 0; + 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 (%" IGRAPH_PRId ") not equal to number of vertices (%" IGRAPH_PRId ").", + 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] == false ? 1 : 0; + n1 += VECTOR(*types)[i] == 0 ? 1 : 0; } n2 = no_of_nodes - n1; - IGRAPH_VECTOR_INT_INIT_FINALLY(&perm, no_of_nodes); + 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++; @@ -786,10 +741,10 @@ igraph_error_t igraph_get_biadjacency( IGRAPH_CHECK(igraph_matrix_resize(res, n1, n2)); igraph_matrix_null(res); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - igraph_integer_t from2 = VECTOR(perm)[from]; - igraph_integer_t to2 = VECTOR(perm)[to]; + 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]) { @@ -799,34 +754,33 @@ igraph_error_t igraph_get_biadjacency( } } if (ignored_edges) { - IGRAPH_WARNINGF("%" IGRAPH_PRId " edges running within partitions were ignored.", ignored_edges); + IGRAPH_WARNINGF("%ld edges running within partitions were ignored.", ignored_edges); } if (row_ids) { - IGRAPH_CHECK(igraph_vector_int_resize(row_ids, n1)); + IGRAPH_CHECK(igraph_vector_resize(row_ids, n1)); } if (col_ids) { - IGRAPH_CHECK(igraph_vector_int_resize(col_ids, n2)); + 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) { - igraph_integer_t i2 = VECTOR(perm)[i]; + long int i2 = (long int) VECTOR(perm)[i]; VECTOR(*row_ids)[i2] = i; } } else { if (col_ids) { - igraph_integer_t i2 = VECTOR(perm)[i]; + long int i2 = (long int) VECTOR(perm)[i]; VECTOR(*col_ids)[i2 - n1] = i; } } } } - igraph_vector_int_destroy(&perm); + igraph_vector_destroy(&perm); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; } @@ -860,7 +814,7 @@ igraph_error_t igraph_get_biadjacency( * edges. */ -igraph_error_t igraph_is_bipartite(const igraph_t *graph, +int igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *types) { @@ -872,50 +826,52 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, 2 means type 2. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vector_char_t seen; - igraph_dqueue_int_t Q; - igraph_vector_int_t neis; - igraph_bool_t bi = true; + igraph_dqueue_t Q; + igraph_vector_t neis; + igraph_bool_t bi = 1; + long int i; - IGRAPH_VECTOR_CHAR_INIT_FINALLY(&seen, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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 (igraph_integer_t i = 0; bi && i < no_of_nodes; i++) { + for (i = 0; bi && i < no_of_nodes; i++) { if (VECTOR(seen)[i]) { continue; } - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, i)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, i)); VECTOR(seen)[i] = 1; - while (bi && !igraph_dqueue_int_empty(&Q)) { - igraph_integer_t n, j; - igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + 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_int_size(&neis); + n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (VECTOR(seen)[nei]) { - igraph_integer_t neitype = VECTOR(seen)[nei]; + long int neitype = VECTOR(seen)[nei]; if (neitype == acttype) { - bi = false; + bi = 0; break; } } else { VECTOR(seen)[nei] = 3 - acttype; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); } } } } - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&Q); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&Q); IGRAPH_FINALLY_CLEAN(2); if (res) { @@ -924,7 +880,7 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, if (types && bi) { IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (i = 0; i < no_of_nodes; i++) { VECTOR(*types)[i] = VECTOR(seen)[i] - 1; } } @@ -932,16 +888,17 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, igraph_vector_char_destroy(&seen); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, +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) { - igraph_vector_int_t edges, s; - igraph_integer_t i; + int retval = 0; + igraph_vector_t edges, s; + int i; if (p < 0.0 || p > 1.0) { IGRAPH_ERROR("Invalid connection probability", IGRAPH_EINVAL); @@ -956,99 +913,92 @@ igraph_error_t igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t * } if (p == 0 || n1 * n2 < 1) { - IGRAPH_CHECK(igraph_empty(graph, n1 + n2, directed)); + IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); } else if (p == 1.0) { - IGRAPH_CHECK(igraph_full_bipartite(graph, types, n1, n2, directed, + IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, directed, mode)); } else { - igraph_integer_t to, from, slen; - igraph_real_t n1_real = n1; /* for divisions below */ - igraph_real_t n2_real = n2; /* for divisions below */ - igraph_real_t maxedges, last; - igraph_integer_t maxedges_int; - + long int to, from, slen; + double maxedges, last; if (!directed || mode != IGRAPH_ALL) { - maxedges = n1_real * n2_real; + maxedges = (double) n1 * (double) n2; } else { - maxedges = 2.0 * n1_real * n2_real; + maxedges = 2.0 * (double) n1 * (double) n2; } - if (maxedges > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); - } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &maxedges_int)); - IGRAPH_CHECK(igraph_vector_int_reserve(&s, maxedges_int)); + 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_int_push_back(&s, last)); + IGRAPH_CHECK(igraph_vector_push_back(&s, last)); last += RNG_GEOM(p); last += 1; } RNG_END(); - slen = igraph_vector_int_size(&s); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, slen * 2)); + 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 = floor(VECTOR(s)[i] / n1_real); - from = VECTOR(s)[i] - to * n1_real; + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); to += n1; } else { - igraph_integer_t n1n2 = n1 * n2; + long int n1n2 = n1 * n2; if (VECTOR(s)[i] < n1n2) { - to = floor(VECTOR(s)[i] / n1_real); - from = VECTOR(s)[i] - to * n1_real; + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); to += n1; } else { - to = floor((VECTOR(s)[i] - n1n2) / n2_real); - from = VECTOR(s)[i] - n1n2 - to * n2_real; + 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_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); } else { - igraph_vector_int_push_back(&edges, to); - igraph_vector_int_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + igraph_vector_push_back(&edges, from); } } - igraph_vector_int_destroy(&s); + igraph_vector_destroy(&s); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return retval; } -igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, +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_int_t edges; - igraph_vector_int_t s; + igraph_vector_t edges; + igraph_vector_t s; + int retval = 0; if (n1 < 0 || n2 < 0) { - IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); } if (m < 0) { - IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); } if (types) { - igraph_integer_t i; + long int i; IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2)); igraph_vector_bool_null(types); for (i = n1; i < n1 + n2; i++) { @@ -1058,73 +1008,71 @@ igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t * if (m == 0 || n1 * n2 == 0) { if (m > 0) { - IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_empty(graph, n1 + n2, directed)); + IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); } else { - igraph_integer_t i; - igraph_real_t maxedges; + + long int i; + double maxedges; if (!directed || mode != IGRAPH_ALL) { - maxedges = (igraph_real_t) n1 * (igraph_real_t) n2; + maxedges = (double) n1 * (double) n2; } else { - maxedges = 2.0 * (igraph_real_t) n1 * (igraph_real_t) n2; + maxedges = 2.0 * (double) n1 * (double) n2; } if (m > maxedges) { - IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); } if (maxedges == m) { - IGRAPH_CHECK(igraph_full_bipartite(graph, types, n1, n2, + IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, directed, mode)); } else { - igraph_integer_t to, from; - igraph_real_t n1_real = (igraph_real_t) n1; /* for divisions below */ - igraph_real_t n2_real = (igraph_real_t) n2; /* for divisions below */ - - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&s, 0); + 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_int_reserve(&edges, igraph_vector_int_size(&s) * 2)); + IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); for (i = 0; i < m; i++) { if (!directed || mode != IGRAPH_ALL) { - to = floor(VECTOR(s)[i] / n1_real); - from = VECTOR(s)[i] - to * n1_real; + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); to += n1; } else { - igraph_integer_t n1n2 = n1 * n2; + long int n1n2 = n1 * n2; if (VECTOR(s)[i] < n1n2) { - to = floor(VECTOR(s)[i] / n1_real); - from = VECTOR(s)[i] - to * n1_real; + to = (long) floor(VECTOR(s)[i] / n1); + from = (long) (VECTOR(s)[i] - ((igraph_real_t) to) * n1); to += n1; } else { - to = floor((VECTOR(s)[i] - n1n2) / n2_real); - from = VECTOR(s)[i] - n1n2 - to * n2_real; + 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_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); } else { - igraph_vector_int_push_back(&edges, to); - igraph_vector_int_push_back(&edges, from); + igraph_vector_push_back(&edges, to); + igraph_vector_push_back(&edges, from); } } - igraph_vector_int_destroy(&s); + igraph_vector_destroy(&s); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, directed)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } } - return IGRAPH_SUCCESS; + return retval; } /** @@ -1134,7 +1082,7 @@ igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t * * 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 probability p, independently of the + * 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. * @@ -1157,7 +1105,7 @@ igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t * * probability p. * \endclist * \param n1 The number of bottom vertices. - * \param n2 The number of top 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 @@ -1180,7 +1128,7 @@ igraph_error_t igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t * * edges. */ -igraph_error_t igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, +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, diff --git a/src/vendor/cigraph/src/misc/chordality.c b/src/vendor/cigraph/src/misc/chordality.c index 7cbcffc2105..fe57ff12eaf 100644 --- a/src/vendor/cigraph/src/misc/chordality.c +++ b/src/vendor/cigraph/src/misc/chordality.c @@ -18,7 +18,7 @@ */ #include "igraph_structural.h" - +#include "igraph_error.h" #include "igraph_adjlist.h" #include "igraph_interface.h" @@ -65,41 +65,45 @@ * \sa \ref igraph_is_chordal(). */ -igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, - igraph_vector_int_t *alpha, - igraph_vector_int_t *alpham1) { +int igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_t *alpha, + igraph_vector_t *alpham1) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t size; - igraph_vector_int_t head, next, prev; /* doubly linked list with head */ - igraph_integer_t i; + 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; */ /***************/ - igraph_integer_t j, v; + long int j, v; if (no_of_nodes == 0) { - igraph_vector_int_clear(alpha); + igraph_vector_clear(alpha); if (alpham1) { - igraph_vector_int_clear(alpham1); + igraph_vector_clear(alpham1); } return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&size, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&head, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&next, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&prev, no_of_nodes); + 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_int_resize(alpha, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(alpha, no_of_nodes)); if (alpham1) { - IGRAPH_CHECK(igraph_vector_int_resize(alpham1, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(alpham1, no_of_nodes)); } /***********************************************/ @@ -131,7 +135,7 @@ igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, /**************/ while (i >= 1) { - igraph_integer_t x, k, len; + long int x, k, len; igraph_vector_int_t *neis; /********************************/ @@ -162,16 +166,16 @@ igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, neis = igraph_adjlist_get(&adjlist, v); len = igraph_vector_int_size(neis); for (k = 0; k < len; k++) { - igraph_integer_t w = VECTOR(*neis)[k]; - igraph_integer_t ws = VECTOR(size)[w]; + long int w = (long int) VECTOR(*neis)[k]; + long int ws = VECTOR(size)[w]; if (ws >= 0) { /******************************/ /* delete w from set(size(w)) */ /******************************/ - igraph_integer_t nw = VECTOR(next)[w]; - igraph_integer_t pw = VECTOR(prev)[w]; + long int nw = VECTOR(next)[w]; + long int pw = VECTOR(prev)[w]; if (nw != 0) { VECTOR(prev)[nw - 1] = pw; } @@ -222,10 +226,10 @@ igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, } igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&prev); - igraph_vector_int_destroy(&next); - igraph_vector_int_destroy(&head); - igraph_vector_int_destroy(&size); + 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; @@ -272,37 +276,37 @@ igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, * \sa \ref igraph_maximum_cardinality_search(). */ -igraph_error_t igraph_is_chordal(const igraph_t *graph, - const igraph_vector_int_t *alpha, - const igraph_vector_int_t *alpham1, +int igraph_is_chordal(const igraph_t *graph, + const igraph_vector_t *alpha, + const igraph_vector_t *alpham1, igraph_bool_t *chordal, - igraph_vector_int_t *fill_in, + igraph_vector_t *fill_in, igraph_t *newgraph) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_vector_int_t *my_alpha = alpha, *my_alpham1 = alpham1; - igraph_vector_int_t v_alpha, v_alpham1; - igraph_vector_int_t f, index; - igraph_integer_t i; + 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_int_t mark; + igraph_vector_long_t mark; igraph_bool_t calc_edges = fill_in || newgraph; - igraph_vector_int_t *my_fill_in = fill_in, v_fill_in; + igraph_vector_t *my_fill_in = fill_in, v_fill_in; /*****************/ /* local v, w, x */ /*****************/ - igraph_integer_t v, w, x; + long int v, w, x; - if (alpha && (igraph_vector_int_size(alpha) != no_of_nodes)) { - IGRAPH_ERRORF("Alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(alpha), no_of_nodes); + 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_int_size(alpham1) != no_of_nodes)) { - IGRAPH_ERRORF("Inverse alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(alpham1), 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) { @@ -311,47 +315,50 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, } if (!alpha && !alpham1) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); my_alpha = &v_alpha; - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpham1, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); my_alpham1 = &v_alpham1; IGRAPH_CHECK(igraph_maximum_cardinality_search(graph, - (igraph_vector_int_t*) my_alpha, - (igraph_vector_int_t*) my_alpham1)); + (igraph_vector_t*) my_alpha, + (igraph_vector_t*) my_alpham1)); } else if (alpha && !alpham1) { - igraph_integer_t v; - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpham1, no_of_nodes); + long int v; + IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); my_alpham1 = &v_alpham1; for (v = 0; v < no_of_nodes; v++) { - igraph_integer_t i = VECTOR(*my_alpha)[v]; + long int i = (long int) VECTOR(*my_alpha)[v]; VECTOR(*my_alpham1)[i] = v; } } else if (!alpha && alpham1) { - igraph_integer_t i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); + long int i; + IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); my_alpha = &v_alpha; for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t v = VECTOR(*my_alpham1)[i]; + long int v = (long int) VECTOR(*my_alpham1)[i]; VECTOR(*my_alpha)[v] = i; } } if (!fill_in && newgraph) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_fill_in, 0); + IGRAPH_VECTOR_INIT_FINALLY(&v_fill_in, 0); my_fill_in = &v_fill_in; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&f, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); + 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_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); + IGRAPH_CHECK(igraph_vector_long_init(&mark, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_long_destroy, &mark); if (my_fill_in) { - igraph_vector_int_clear(my_fill_in); + igraph_vector_clear(my_fill_in); } if (chordal) { - *chordal = true; + *chordal = 1; } /*********************/ @@ -360,13 +367,13 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, for (i = 0; i < no_of_nodes; i++) { igraph_vector_int_t *neis; - igraph_integer_t j, len; + long int j, len; /**********************************************/ /* w := alpham1(i); f(w) := w; index(w) := i; */ /**********************************************/ - w = VECTOR(*my_alpham1)[i]; + w = (long int) VECTOR(*my_alpham1)[i]; VECTOR(f)[w] = w; VECTOR(index)[w] = i; @@ -377,12 +384,12 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, neis = igraph_adjlist_get(&adjlist, w); len = igraph_vector_int_size(neis); for (j = 0; j < len; j++) { - v = VECTOR(*neis)[j]; + v = (long int) VECTOR(*neis)[j]; VECTOR(mark)[v] = w + 1; } for (j = 0; j < len; j++) { - v = VECTOR(*neis)[j]; + v = (long int) VECTOR(*neis)[j]; if (VECTOR(*my_alpha)[v] >= i) { continue; } @@ -412,12 +419,12 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, if (VECTOR(mark)[x] != w + 1) { if (chordal) { - *chordal = false; + *chordal = 0; } if (my_fill_in) { - IGRAPH_CHECK(igraph_vector_int_push_back(my_fill_in, x)); - IGRAPH_CHECK(igraph_vector_int_push_back(my_fill_in, w)); + IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, x)); + IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, w)); } if (!calc_edges) { @@ -446,10 +453,10 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, } } - igraph_vector_int_destroy(&mark); + igraph_vector_long_destroy(&mark); igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&index); - igraph_vector_int_destroy(&f); + igraph_vector_long_destroy(&index); + igraph_vector_long_destroy(&f); IGRAPH_FINALLY_CLEAN(4); if (newgraph) { @@ -460,19 +467,19 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, } if (!fill_in && newgraph) { - igraph_vector_int_destroy(&v_fill_in); + igraph_vector_destroy(&v_fill_in); IGRAPH_FINALLY_CLEAN(1); } if (!alpha && !alpham1) { - igraph_vector_int_destroy(&v_alpham1); - igraph_vector_int_destroy(&v_alpha); + igraph_vector_destroy(&v_alpham1); + igraph_vector_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(2); } else if (alpha && !alpham1) { - igraph_vector_int_destroy(&v_alpham1); + igraph_vector_destroy(&v_alpham1); IGRAPH_FINALLY_CLEAN(1); } else if (!alpha && alpham1) { - igraph_vector_int_destroy(&v_alpha); + igraph_vector_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(1); } diff --git a/src/vendor/cigraph/src/misc/cocitation.c b/src/vendor/cigraph/src/misc/cocitation.c index c1210ec42e7..6f9c787c798 100644 --- a/src/vendor/cigraph/src/misc/cocitation.c +++ b/src/vendor/cigraph/src/misc/cocitation.c @@ -23,7 +23,6 @@ */ #include "igraph_cocitation.h" - #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_interface.h" @@ -32,7 +31,7 @@ #include -static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, +int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_vector_t *weights); @@ -41,21 +40,21 @@ static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_mat * \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 + * 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 + * \param vids The vertex ids of the vertices for which the * calculation will be done. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), |V| is * the number of vertices in the graph, @@ -67,9 +66,9 @@ static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_mat * \example examples/simple/igraph_cocitation.c */ -igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, +int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids) { - return igraph_i_cocitation_real(graph, res, vids, IGRAPH_OUT, NULL); + return igraph_cocitation_real(graph, res, vids, IGRAPH_OUT, 0); } /** @@ -77,21 +76,21 @@ igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, * \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 + * 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 + * \param vids The vertex ids of the vertices for which the * calculation will be done. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in @@ -103,9 +102,9 @@ igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, * \example examples/simple/igraph_cocitation.c */ -igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, +int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids) { - return igraph_i_cocitation_real(graph, res, vids, IGRAPH_IN, NULL); + return igraph_cocitation_real(graph, res, vids, IGRAPH_IN, 0); } /** @@ -113,6 +112,7 @@ igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * \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 @@ -126,14 +126,13 @@ igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * * 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. - * https://doi.org/10.1016/S0378-8733(03)00009-1 * * \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 + * 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 + * \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: @@ -150,7 +149,7 @@ igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * degree. * \endclist * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in @@ -160,12 +159,11 @@ igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_inverse_log_weighted(const igraph_t *graph, +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_vector_int_t degrees; igraph_neimode_t mode0; - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int i, no_of_nodes; switch (mode) { case IGRAPH_OUT: mode0 = IGRAPH_IN; break; @@ -173,34 +171,32 @@ igraph_error_t igraph_similarity_inverse_log_weighted(const igraph_t *graph, default: mode0 = IGRAPH_ALL; } + no_of_nodes = igraph_vcount(graph); + IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode0, true)); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - VECTOR(weights)[i] = VECTOR(degrees)[i]; + 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_i_cocitation_real(graph, res, vids, mode0, &weights)); - igraph_vector_int_destroy(°rees); + IGRAPH_CHECK(igraph_cocitation_real(graph, res, vids, mode0, &weights)); igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; + IGRAPH_FINALLY_CLEAN(1); + return 0; } -static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, +int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_vids; - igraph_integer_t from, i, j; - igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; - igraph_vector_int_t vid_reverse_index; + 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)); @@ -210,40 +206,38 @@ static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_mat /* Create a mapping from vertex IDs to the row of the matrix where * the result for this vertex will appear */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&vid_reverse_index, no_of_nodes); - igraph_vector_int_fill(&vid_reverse_index, -1); + 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++) { - igraph_integer_t v = IGRAPH_VIT_GET(vit); + v = IGRAPH_VIT_GET(vit); if (v < 0 || v >= no_of_nodes) { - IGRAPH_ERROR("Invalid vertex ID in vertex selector.", IGRAPH_EINVVID); + IGRAPH_ERROR("invalid vertex ID in vertex selector", IGRAPH_EINVAL); } VECTOR(vid_reverse_index)[v] = i; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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; + igraph_real_t weight = 1; IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, mode)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, + (igraph_integer_t) from, mode)); if (weights) { weight = VECTOR(*weights)[from]; - } else { - weight = 1; } - for (i = 0; i < nei_count - 1; i++) { - igraph_integer_t u = VECTOR(neis)[i]; - igraph_integer_t k = VECTOR(vid_reverse_index)[u]; - for (j = i + 1; j < nei_count; j++) { - igraph_integer_t v = VECTOR(neis)[j]; - igraph_integer_t l = VECTOR(vid_reverse_index)[v]; + 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; } @@ -255,21 +249,20 @@ static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_mat } /* Clean up */ - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&vid_reverse_index); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&vid_reverse_index); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_neisets_intersect( - const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, - igraph_integer_t *len_union, igraph_integer_t *len_intersection -) { +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 */ - igraph_integer_t i, j, i0, jj0; + 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; @@ -283,7 +276,7 @@ static igraph_error_t igraph_i_neisets_intersect( j++; } } - return IGRAPH_SUCCESS; + return 0; } /** @@ -291,6 +284,7 @@ static igraph_error_t igraph_i_neisets_intersect( * \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 @@ -299,8 +293,8 @@ static igraph_error_t igraph_i_neisets_intersect( * \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 + * 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: @@ -320,7 +314,7 @@ static igraph_error_t igraph_i_neisets_intersect( * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -334,14 +328,13 @@ static igraph_error_t igraph_i_neisets_intersect( * * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, +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; - igraph_integer_t i, j; - igraph_integer_t len_union, len_intersection; + long int i, j, k; + long int len_union, len_intersection; igraph_vector_int_t *v1, *v2; - igraph_integer_t k; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -356,10 +349,9 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t 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, i); - IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) i); if (!igraph_vector_int_binsearch(v1, i, &k)) { - IGRAPH_CHECK(igraph_vector_int_insert(v1, k, i)); + igraph_vector_int_insert(v1, k, i); } } } @@ -372,13 +364,9 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t if (j <= i) { continue; } - v1 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit)); - IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); v2 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2)); - IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); - - IGRAPH_CHECK(igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection)); + 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 { @@ -393,7 +381,7 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t igraph_vit_destroy(&vit2); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -401,6 +389,7 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t * \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 @@ -432,7 +421,7 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -447,16 +436,17 @@ igraph_error_t igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t * * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, - const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { +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; - igraph_integer_t u, v; - igraph_integer_t len_union, len_intersection; + long int i, j, k, u, v; + long int len_union, len_intersection; igraph_vector_int_t *v1, *v2; + igraph_bool_t *seen; - igraph_integer_t k = igraph_vector_int_size(pairs); + k = igraph_vector_size(pairs); if (k % 2 != 0) { - IGRAPH_ERROR("Number of elements in `pairs' must be even.", IGRAPH_EINVAL); + IGRAPH_ERROR("number of elements in `pairs' must be even", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_resize(res, k / 2)); @@ -465,42 +455,41 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec 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); - igraph_vector_bool_t seen; - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen, igraph_vcount(graph)); - - for (igraph_integer_t i = 0; i < k; i++) { - igraph_integer_t j = VECTOR(*pairs)[i]; - if (VECTOR(seen)[j]) { + for (i = 0; i < k; i++) { + j = (long int) VECTOR(*pairs)[i]; + if (seen[j]) { continue; } - VECTOR(seen)[j] = true; - v1 = igraph_lazy_adjlist_get(&al, j); - IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + seen[j] = 1; + v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) j); if (!igraph_vector_int_binsearch(v1, j, &u)) { - IGRAPH_CHECK(igraph_vector_int_insert(v1, u, j)); + igraph_vector_int_insert(v1, u, j); } } - igraph_vector_bool_destroy(&seen); + IGRAPH_FREE(seen); IGRAPH_FINALLY_CLEAN(1); } - for (igraph_integer_t i = 0, j = 0; i < k; i += 2, j++) { - u = VECTOR(*pairs)[i]; - v = VECTOR(*pairs)[i + 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, u); - IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); - v2 = igraph_lazy_adjlist_get(&al, v); - IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); - - IGRAPH_CHECK(igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection)); + 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 { @@ -511,7 +500,7 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec igraph_lazy_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -519,6 +508,7 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec * \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 @@ -549,7 +539,7 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -565,15 +555,28 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec * * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, +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_int_t pairs; + IGRAPH_VECTOR_INIT_FINALLY(&v, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&pairs, 0); - IGRAPH_CHECK(igraph_edges(graph, es, &pairs)); - IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &pairs, mode, loops)); - igraph_vector_int_destroy(&pairs); + 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; @@ -584,15 +587,16 @@ igraph_error_t igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector * \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 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 + * 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: @@ -612,30 +616,30 @@ igraph_error_t igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * * Time complexity: O(|V|^2 d), - * where |V| is the number of vertices in the vertex iterator given, and - * d is the (maximum) degree of the vertices in the graph. + * |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 */ -igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t vids, - igraph_neimode_t mode, igraph_bool_t loops) { +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)); - igraph_integer_t nr = igraph_matrix_nrow(res); - igraph_integer_t nc = igraph_matrix_ncol(res); - for (igraph_integer_t i = 0; i < nr; i++) { - for (igraph_integer_t j = 0; j < nc; j++) { + 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); } @@ -649,6 +653,7 @@ igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *re * \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. @@ -679,7 +684,7 @@ igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *re * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -694,12 +699,13 @@ igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *re * * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, - const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { +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)); - igraph_integer_t n = igraph_vector_size(res); - for (igraph_integer_t i = 0; i < n; i++) { + 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); } @@ -712,6 +718,7 @@ igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector * \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 @@ -741,7 +748,7 @@ igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -757,12 +764,13 @@ igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector * * \example examples/simple/igraph_similarity.c */ -igraph_error_t igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, +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)); - igraph_integer_t n = igraph_vector_size(res); - for (igraph_integer_t i = 0; i < n; i++) { + 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); } diff --git a/src/vendor/cigraph/src/misc/coloring.c b/src/vendor/cigraph/src/misc/coloring.c index e8952139e8a..39b0ff9509b 100644 --- a/src/vendor/cigraph/src/misc/coloring.c +++ b/src/vendor/cigraph/src/misc/coloring.c @@ -19,21 +19,18 @@ */ #include "igraph_coloring.h" - -#include "igraph_adjlist.h" #include "igraph_interface.h" +#include "igraph_adjlist.h" -#include "core/genheap.h" #include "core/indheap.h" #include "core/interruption.h" -/* COLORED_NEIGHBORS: Choose vertices based on the number of already coloured neighbours. */ - -static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { - igraph_integer_t i, vertex, maxdeg; - igraph_integer_t vc = igraph_vcount(graph); +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 neighbors, nei_colors; + igraph_vector_int_t neigh_colors; + igraph_adjlist_t adjlist; IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); igraph_vector_int_fill(colors, 0); @@ -46,70 +43,64 @@ static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, 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_int_t degree; + igraph_vector_t degree; - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + 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_int_which_max(°ree); + vertex = igraph_vector_which_max(°ree); maxdeg = VECTOR(degree)[vertex]; - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_colors, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&nei_colors, maxdeg)); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&neighbors, maxdeg)); + 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); - /* two-way indexed heap holding number of already colored neighbors of yet-uncolored vertices */ IGRAPH_CHECK(igraph_2wheap_init(&cn, vc)); IGRAPH_FINALLY(igraph_2wheap_destroy, &cn); - for (i = 0; i < vc; ++i) { + 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 */ + igraph_2wheap_push_with_index(&cn, i, 0); /* should not fail since memory was already reserved */ } - } - /* Within this loop, a color of 0 means "uncolored", and valid color indices start at 1. - * At the beginning, all vertices are set as "uncolored", see the vector_int_fill() call above. - * Colors will be decremented to start at 0 later. */ - while (true) { - IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, vertex, IGRAPH_ALL)); - igraph_integer_t nei_count = igraph_vector_int_size(&neighbors); + while (1) { + igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, vertex); + long neigh_count = igraph_vector_int_size(neighbors); - /* Colour current vertex by finding smallest available non-0 color. - * Note that self-loops are effectively skipped as they merely prevent - * the current vertex from being colored with the color value it presently - * has, which is 0 (meaning uncolored). */ + /* colour current vertex */ { igraph_integer_t col; - IGRAPH_CHECK(igraph_vector_int_resize(&nei_colors, nei_count)); - for (i = 0; i < nei_count; ++i) { - VECTOR(nei_colors)[i] = VECTOR(*colors)[ VECTOR(neighbors)[i] ]; + 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(&nei_colors); + igraph_vector_int_sort(&neigh_colors); i = 0; col = 0; do { - while (i < nei_count && VECTOR(nei_colors)[i] == col) { + while (i < neigh_count && VECTOR(neigh_colors)[i] == col) { i++; } col++; - } while (i < nei_count && VECTOR(nei_colors)[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 < nei_count; ++i) { - igraph_integer_t idx = VECTOR(neighbors)[i]; + 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); } @@ -129,174 +120,42 @@ static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_add_constant(colors, -1); /* free data structures */ - igraph_vector_int_destroy(&neighbors); - igraph_vector_int_destroy(&nei_colors); + igraph_vector_int_destroy(&neigh_colors); + igraph_adjlist_destroy(&adjlist); igraph_2wheap_destroy(&cn); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } -/* DSATUR: Choose vertices based on the number of adjacent colours, i.e. "saturation degree" */ - -typedef struct { - igraph_integer_t saturation_degree; /* number of colors used by neighbors */ - igraph_integer_t edge_degree; /* degree in the subgraph induced by uncolored vertices */ -} dsatur_t; - -static int dsatur_t_compare(const void *left, const void *right) { - const dsatur_t *left_d = left; - const dsatur_t *right_d = right; - if (left_d->saturation_degree == right_d->saturation_degree) { - return left_d->edge_degree - right_d->edge_degree; - } - return left_d->saturation_degree - right_d->saturation_degree; -} - -static igraph_bool_t dsatur_is_color_used_by_neighbour( - const igraph_vector_int_t *colors, igraph_integer_t color, - const igraph_vector_int_t *neighbors -) { - igraph_integer_t nei_count = igraph_vector_int_size(neighbors); - - for (igraph_integer_t i=0; i < nei_count; i++) { - igraph_integer_t nei = VECTOR(*neighbors)[i]; - if (VECTOR(*colors)[nei] == color) { - return true; - } - } - - return false; -} - -static void dsatur_update_heap( - const igraph_adjlist_t *adjlist, igraph_gen2wheap_t *node_degrees_heap, - const igraph_vector_int_t *neighbors, const igraph_vector_int_t *colors, - igraph_integer_t color -) { - igraph_gen2wheap_delete_max(node_degrees_heap); - igraph_integer_t nei_count = igraph_vector_int_size(neighbors); - for (igraph_integer_t i=0; i < nei_count; i++) { - igraph_integer_t nei = VECTOR(*neighbors)[i]; - if (!igraph_gen2wheap_has_elem(node_degrees_heap, nei)) { - continue; - } - dsatur_t deg_data = *((dsatur_t*) igraph_gen2wheap_get(node_degrees_heap, nei)); - if (!dsatur_is_color_used_by_neighbour(colors, color, igraph_adjlist_get(adjlist, nei))) { - deg_data.saturation_degree++; - } - deg_data.edge_degree--; - igraph_gen2wheap_modify(node_degrees_heap, nei, °_data); - } -} - -static igraph_integer_t dsatur_get_first_viable_color(const igraph_vector_int_t *used_colors_sorted) { - igraph_integer_t color_count = igraph_vector_int_size(used_colors_sorted); - igraph_integer_t i = 0; - igraph_integer_t col = 0; - while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { - while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { - i++; - } - col++; - } - return col; -} - -static igraph_error_t igraph_i_vertex_coloring_dsatur( - const igraph_t *graph, igraph_vector_int_t *colors -) { - igraph_integer_t vcount = igraph_vcount(graph); - IGRAPH_CHECK(igraph_vector_int_resize(colors, vcount)); - - if (vcount == 0) { - return IGRAPH_SUCCESS; - } - - if (vcount == 1) { - VECTOR(*colors)[0] = 0; - return IGRAPH_SUCCESS; - } - - igraph_vector_int_fill(colors, -1); /* -1 as a color means uncolored */ - - /* Multi-edges and self-loops are removed from the adjacency list in order to ensure the correct - * updating of a vertex's neighbors' saturation degrees when that vertex is colored. */ - igraph_adjlist_t adjlist; - IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - - igraph_gen2wheap_t node_degrees_heap; - IGRAPH_CHECK(igraph_gen2wheap_init(&node_degrees_heap, dsatur_t_compare, sizeof(dsatur_t), vcount)); - IGRAPH_FINALLY(igraph_gen2wheap_destroy, &node_degrees_heap); - - for (igraph_integer_t vertex = 0; vertex < vcount; vertex++) { - dsatur_t dsatur; - dsatur.saturation_degree = 0; - dsatur.edge_degree = igraph_vector_int_size(igraph_adjlist_get(&adjlist, vertex)); - IGRAPH_CHECK(igraph_gen2wheap_push_with_index(&node_degrees_heap, vertex, &dsatur)); - } - - igraph_vector_int_t used_colors_sorted; - IGRAPH_VECTOR_INT_INIT_FINALLY(&used_colors_sorted, 0); - - while (! igraph_gen2wheap_empty(&node_degrees_heap)) { - igraph_integer_t node_to_color = igraph_gen2wheap_max_index(&node_degrees_heap); - igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, node_to_color); - igraph_integer_t nei_count = igraph_vector_int_size(neighbors); - igraph_vector_int_clear(&used_colors_sorted); - for (igraph_integer_t i=0; i < nei_count; i++) { - igraph_integer_t nei = VECTOR(*neighbors)[i]; - if (VECTOR(*colors)[nei] != -1) { - IGRAPH_CHECK(igraph_vector_int_push_back(&used_colors_sorted, VECTOR(*colors)[nei])); - } - } - igraph_vector_int_sort(&used_colors_sorted); - igraph_integer_t color = dsatur_get_first_viable_color(&used_colors_sorted); - dsatur_update_heap(&adjlist, &node_degrees_heap, neighbors, colors, color); - VECTOR(*colors)[node_to_color] = color; - - IGRAPH_ALLOW_INTERRUPTION(); - } - - igraph_vector_int_destroy(&used_colors_sorted); - igraph_gen2wheap_destroy(&node_degrees_heap); - igraph_adjlist_destroy(&adjlist); - 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 greedily, one by one, always choosing the smallest color - * index that differs from that of already colored neighbors. Vertices are picked - * in an order determined by the speified heuristic. - * Colors are represented by non-negative integers 0, 1, 2, ... + * 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 for more information. + * \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 */ -igraph_error_t igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic) { +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); - case IGRAPH_COLORING_GREEDY_DSATUR: - return igraph_i_vertex_coloring_dsatur(graph, colors); default: - IGRAPH_ERROR("Invalid heuristic for greedy vertex coloring.", IGRAPH_EINVAL); + return IGRAPH_EINVAL; } } diff --git a/src/vendor/cigraph/src/misc/conversion.c b/src/vendor/cigraph/src/misc/conversion.c index 85495ab0e87..b16a70e835a 100644 --- a/src/vendor/cigraph/src/misc/conversion.c +++ b/src/vendor/cigraph/src/misc/conversion.c @@ -33,25 +33,20 @@ #include "core/fixed_vectorlist.h" #include "graph/attributes.h" -#include "math/safe_intop.h" - -#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1) +#include "misc/conversion_internal.h" /** * \ingroup conversion * \function igraph_get_adjacency - * \brief The adjacency matrix of a graph. + * \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 in the unweighted - * case, or the total weight of edges connecting vertex i to vertex j in the - * weighted case. - * + * 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 specifying the type of the adjacency matrix to + * \param type Constant giving the type of the adjacency matrix to * create for undirected graphs. It is ignored for directed * graphs. Possible values: * \clist @@ -63,111 +58,131 @@ * the whole matrix is used, a symmetric matrix is returned * if the graph is undirected. * \endclist - * \param weights An optional vector containing the weight of each edge - * in the graph. Supply a null pointer here to make all edges have - * the same weight of 1. - * \param loops Constant specifying how loop edges should be handled. - * Possible values: - * \clist - * \cli IGRAPH_NO_LOOPS - * loop edges are ignored and the diagonal of the matrix will contain - * zeros only - * \cli IGRAPH_LOOPS_ONCE - * loop edges are counted once, i.e. a vertex with a single unweighted - * loop edge will have 1 in the corresponding diagonal entry - * \cli IGRAPH_LOOPS_TWICE - * loop edges are counted twice in \em undirected graphs, i.e. a vertex - * with a single unweighted loop edge in an undirected graph will have - * 2 in the corresponding diagonal entry. Loop edges in directed graphs - * are still counted as 1. Essentially, this means that the function is - * counting the incident edge \em stems , which makes more sense when - * using the adjacency matrix in linear algebra. - * \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 \ref igraph_get_adjacency_sparse() if you want a sparse matrix representation + * \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. + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. */ -igraph_error_t igraph_get_adjacency( - const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, - const igraph_vector_t *weights, igraph_loops_t loops -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(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); - igraph_integer_t i, from, to; + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - if (from != to || loops != IGRAPH_NO_LOOPS) { - MATRIX(*res, from, to) += WEIGHT_OF(i); + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); + 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) { - MATRIX(*res, to, from) += WEIGHT_OF(i); + if (eids) { + MATRIX(*res, to, from) = edge + 1; + } else { + MATRIX(*res, to, from) += 1; + } } else { - MATRIX(*res, from, to) += WEIGHT_OF(i); - } - if (to == from && loops == IGRAPH_LOOPS_TWICE) { - MATRIX(*res, to, to) += WEIGHT_OF(i); + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); + 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) { - MATRIX(*res, from, to) += WEIGHT_OF(i); + if (eids) { + MATRIX(*res, from, to) = edge + 1; + } else { + MATRIX(*res, from, to) += 1; + } } else { - MATRIX(*res, to, from) += WEIGHT_OF(i); - } - if (to == from && loops == IGRAPH_LOOPS_TWICE) { - MATRIX(*res, to, to) += WEIGHT_OF(i); + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - MATRIX(*res, from, to) += WEIGHT_OF(i); - if (from != to || loops == IGRAPH_LOOPS_TWICE) { - MATRIX(*res, to, from) += WEIGHT_OF(i); + 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); } - /* Erase the diagonal if we don't need loop edges */ - if (loops == IGRAPH_NO_LOOPS) { - for (i = 0; i < no_of_nodes; i++) { - MATRIX(*res, i, i) = 0; - } - } - - return IGRAPH_SUCCESS; + 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 a sparse matrix format. + * \brief Returns the adjacency matrix of a graph in sparse matrix format. * - * \param graph The input graph. - * \param res Pointer to an \em initialized sparse matrix. The result - * will be stored here. The matrix will be resized as needed. - * \param type Constant specifying the type of the adjacency matrix to + * + * 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 @@ -176,147 +191,91 @@ igraph_error_t igraph_get_adjacency( * \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. + * the whole matrix is used, a symmetric matrix is returned. * \endclist * \return Error code: * \c IGRAPH_EINVAL invalid type argument. * - * \sa \ref igraph_get_adjacency(), the dense version of this function. + * \sa igraph_get_adjacency if you would like to get a normal matrix + * ( \type igraph_matrix_t ) * - * Time complexity: TODO. + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. */ -igraph_error_t igraph_get_adjacency_sparse( - const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, - const igraph_vector_t *weights, igraph_loops_t loops -) { +int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, + igraph_get_adjacency_t type) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_eit_t edgeit; + long int no_of_nodes = igraph_vcount(graph); igraph_bool_t directed = igraph_is_directed(graph); - igraph_integer_t nzmax = directed ? no_of_edges : no_of_edges * 2; - igraph_integer_t i, from, to; + long int from, to; + igraph_integer_t ffrom, fto; - IGRAPH_CHECK(igraph_sparsemat_resize(res, no_of_nodes, no_of_nodes, nzmax)); + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - if (from != to || loops != IGRAPH_NO_LOOPS) { - IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); - } + 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) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); + while (!IGRAPH_EIT_END(edgeit)) { + igraph_edge(graph, IGRAPH_EIT_GET(edgeit), &ffrom, &fto); + from = ffrom; + to = fto; if (to < from) { - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); - } else if (to == from) { - switch (loops) { - case IGRAPH_LOOPS_ONCE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); - break; - case IGRAPH_LOOPS_TWICE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); - break; - case IGRAPH_NO_LOOPS: - default: - break; - } + igraph_spmatrix_add_e(res, to, from, 1); } else { - IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + igraph_spmatrix_add_e(res, from, to, 1); } + IGRAPH_EIT_NEXT(edgeit); } } else if (type == IGRAPH_GET_ADJACENCY_LOWER) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - if (to < from) { - IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); - } else if (to == from) { - switch (loops) { - case IGRAPH_LOOPS_ONCE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); - break; - case IGRAPH_LOOPS_TWICE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); - break; - case IGRAPH_NO_LOOPS: - default: - break; - } + 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_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + igraph_spmatrix_add_e(res, from, to, 1); } + IGRAPH_EIT_NEXT(edgeit); } } else if (type == IGRAPH_GET_ADJACENCY_BOTH) { - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - if (to == from) { - switch (loops) { - case IGRAPH_LOOPS_ONCE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); - break; - case IGRAPH_LOOPS_TWICE: - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); - break; - case IGRAPH_NO_LOOPS: - default: - break; - } - } else { - IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); - IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + 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_ERROR("Invalid type argument.", IGRAPH_EINVAL); } + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -#undef WEIGHT_OF - -/** - * \function igraph_get_sparsemat - * \brief Converts an igraph graph to a sparse matrix (deprecated). - * - * If the graph is undirected, then a symmetric matrix is created. - * - * - * This function is deprecated in favour of \ref igraph_get_adjacency_sparse(), - * but does not work in an identical way. This function takes an \em uninitialized - * \c igraph_sparsemat_t while \ref igraph_get_adjacency_sparse() takes - * an already initialized one. - * - * \param graph The input graph. - * \param res Pointer to an \em uninitialized sparse matrix. The result - * will be stored here. - * \return Error code. - * - * \deprecated-by igraph_get_adjacency_sparse 0.10.0 - */ - -igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges; - IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax)); - return igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); -} - /** * \ingroup conversion * \function igraph_get_edgelist - * \brief The list of edges in a graph. - * - * The order of the edges is given by the edge IDs. + * \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. @@ -326,19 +285,20 @@ igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *r * res[1]->res[|E|+1], etc. * \return Error code. * - * \sa \ref igraph_edges() to return the result only for some edge IDs. + * \sa \ref igraph_edges() to return the result only for some edge ids. * - * Time complexity: O(|E|), the number of edges in the graph. + * Time complexity: O(|E|), the + * number of edges in the graph. */ -igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol) { +int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol) { igraph_eit_t edgeit; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t vptr = 0; + long int no_of_edges = igraph_ecount(graph); + long int vptr = 0; igraph_integer_t from, to; - IGRAPH_CHECK(igraph_vector_int_resize(res, no_of_edges * 2)); + 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); @@ -362,15 +322,15 @@ igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *r igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_to_directed - * \brief Convert an undirected graph to a directed one. + * \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: @@ -396,10 +356,10 @@ igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *r * of edges. */ -igraph_error_t igraph_to_directed(igraph_t *graph, +int igraph_to_directed(igraph_t *graph, igraph_to_directed_t mode) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); if (igraph_is_directed(graph)) { return IGRAPH_SUCCESS; @@ -411,18 +371,19 @@ igraph_error_t igraph_to_directed(igraph_t *graph, case IGRAPH_TO_DIRECTED_ACYCLIC: { igraph_t newgraph; - igraph_vector_int_t edges; - igraph_integer_t size = no_of_edges * 2; + igraph_vector_t edges; + long int size = no_of_edges * 2; + long int i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, size); + IGRAPH_VECTOR_INIT_FINALLY(&edges, size); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); if (mode == IGRAPH_TO_DIRECTED_RANDOM) { RNG_BEGIN(); - for (igraph_integer_t i=0; i < no_of_edges; ++i) { + for (i=0; i < no_of_edges; ++i) { if (RNG_INTEGER(0,1)) { - igraph_integer_t temp = VECTOR(edges)[2*i]; + igraph_real_t temp = VECTOR(edges)[2*i]; VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; VECTOR(edges)[2*i+1] = temp; } @@ -437,9 +398,9 @@ igraph_error_t igraph_to_directed(igraph_t *graph, the implementation of the minimal API in type_indexededgelist.c. Therefore, we order the edge endpoints anyway in the following loop: */ - for (igraph_integer_t i=0; i < no_of_edges; ++i) { + for (i=0; i < no_of_edges; ++i) { if (VECTOR(edges)[2*i] > VECTOR(edges)[2*i+1]) { - igraph_integer_t temp = VECTOR(edges)[2*i]; + igraph_real_t temp = VECTOR(edges)[2*i]; VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; VECTOR(edges)[2*i+1] = temp; } @@ -447,12 +408,12 @@ igraph_error_t igraph_to_directed(igraph_t *graph, } IGRAPH_CHECK(igraph_create(&newgraph, &edges, - no_of_nodes, + (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); - igraph_vector_int_destroy(&edges); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); igraph_destroy(graph); @@ -463,32 +424,31 @@ igraph_error_t igraph_to_directed(igraph_t *graph, case IGRAPH_TO_DIRECTED_MUTUAL: { igraph_t newgraph; - igraph_vector_int_t edges; - igraph_vector_int_t index; - igraph_integer_t size; - - IGRAPH_SAFE_MULT(no_of_edges, 4, &size); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + 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_int_resize(&edges, size)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2); - for (igraph_integer_t i = 0; i < no_of_edges; i++) { + 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, - no_of_nodes, + (igraph_integer_t) no_of_nodes, IGRAPH_DIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges=*/false); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1,/*edges=*/0); IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index)); - igraph_vector_int_destroy(&index); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&index); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); igraph_destroy(graph); @@ -497,7 +457,7 @@ igraph_error_t igraph_to_directed(igraph_t *graph, break; } default: - IGRAPH_ERROR("Cannot direct graph, invalid mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot direct graph, invalid mode", IGRAPH_EINVAL); } return IGRAPH_SUCCESS; @@ -536,13 +496,13 @@ igraph_error_t igraph_to_directed(igraph_t *graph, * \example examples/simple/igraph_to_undirected.c */ -igraph_error_t igraph_to_undirected(igraph_t *graph, +int igraph_to_undirected(igraph_t *graph, igraph_to_undirected_t mode, const igraph_attribute_combination_t *edge_comb) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t edges; + 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(); @@ -553,25 +513,27 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, } if (!igraph_is_directed(graph)) { - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 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_int_reserve(&edges, no_of_edges * 2)); + 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)) { - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_FROM(graph, edge))); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_TO(graph, edge))); + 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); } @@ -580,49 +542,52 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - no_of_nodes, + (igraph_integer_t) no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); + 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_int_t inadj, outadj; - igraph_vector_int_t mergeinto; - igraph_integer_t actedge = 0; + igraph_vector_t inadj, outadj; + long int i; + igraph_vector_t mergeinto; + long int actedge = 0; if (attr) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); } - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t n_out, n_in; - igraph_integer_t p1 = -1, p2 = -1; - igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last; - IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT)); - IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN)); - n_out = igraph_vector_int_size(&outadj); - n_in = igraph_vector_int_size(&inadj); + 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 = VECTOR(outadj)[p1]; \ + e1 = (long int) VECTOR(outadj)[p1]; \ n1 = IGRAPH_TO(graph, e1); \ } #define STEPIN() if ( (++p2) < n_in) { \ - e2 = VECTOR(inadj )[p2]; \ + e2 = (long int) VECTOR(inadj )[p2]; \ n2 = IGRAPH_FROM(graph, e2); \ } #define ADD_NEW_EDGE() { \ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, last)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); \ + IGRAPH_CHECK(igraph_vector_push_back(&edges, last)); \ } #define MERGE_INTO_CURRENT_EDGE(which) { \ if (attr) { \ @@ -673,24 +638,26 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, #undef STEPOUT #undef STEPIN - igraph_vector_int_destroy(&outadj); - igraph_vector_int_destroy(&inadj); + igraph_vector_destroy(&outadj); + igraph_vector_destroy(&inadj); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - no_of_nodes, + (igraph_integer_t) no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */ + 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_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.vecs, edge_comb)); + IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.v, + edge_comb)); igraph_fixed_vectorlist_destroy(&vl); IGRAPH_FINALLY_CLEAN(1); @@ -701,40 +668,41 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, *graph = newgraph; if (attr) { - igraph_vector_int_destroy(&mergeinto); + igraph_vector_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(1); } } else if (mode == IGRAPH_TO_UNDIRECTED_MUTUAL) { - igraph_vector_int_t inadj, outadj; - igraph_vector_int_t mergeinto; - igraph_integer_t actedge = 0; + igraph_vector_t inadj, outadj; + long int i; + igraph_vector_t mergeinto; + long int actedge = 0; if (attr) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); - igraph_vector_int_fill(&mergeinto, -1); + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + igraph_vector_fill(&mergeinto, -1); } - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outadj, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outadj, 0); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t n_out, n_in; - igraph_integer_t p1 = -1, p2 = -1; - igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0; - IGRAPH_CHECK(igraph_incident(graph, &outadj, i, + 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, i, + IGRAPH_CHECK(igraph_incident(graph, &inadj, (igraph_integer_t) i, IGRAPH_IN)); - n_out = igraph_vector_int_size(&outadj); - n_in = igraph_vector_int_size(&inadj); + n_out = igraph_vector_size(&outadj); + n_in = igraph_vector_size(&inadj); #define STEPOUT() if ( (++p1) < n_out) { \ - e1 = VECTOR(outadj)[p1]; \ + e1 = (long int) VECTOR(outadj)[p1]; \ n1 = IGRAPH_TO(graph, e1); \ } #define STEPIN() if ( (++p2) < n_in) { \ - e2 = VECTOR(inadj )[p2]; \ + e2 = (long int) VECTOR(inadj )[p2]; \ n2 = IGRAPH_FROM(graph, e2); \ } @@ -743,8 +711,8 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, while (p1 < n_out && n1 <= i && p2 < n_in && n2 <= i) { if (n1 == n2) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, n1)); + 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; @@ -763,24 +731,26 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, #undef STEPOUT #undef STEPIN - igraph_vector_int_destroy(&outadj); - igraph_vector_int_destroy(&inadj); + igraph_vector_destroy(&outadj); + igraph_vector_destroy(&inadj); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - no_of_nodes, + (igraph_integer_t) no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */ + 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_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.vecs, edge_comb)); + IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.v, + edge_comb)); igraph_fixed_vectorlist_destroy(&vl); IGRAPH_FINALLY_CLEAN(1); @@ -791,158 +761,144 @@ igraph_error_t igraph_to_undirected(igraph_t *graph, *graph = newgraph; if (attr) { - igraph_vector_int_destroy(&mergeinto); + igraph_vector_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(1); } } - return IGRAPH_SUCCESS; + return 0; } -#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1) - /** * \function igraph_get_stochastic - * \brief Stochastic adjacency matrix of a graph. + * Stochastic adjacency matrix of a graph * * Stochastic matrix of a graph. The stochastic matrix of a graph is * its adjacency matrix, normalized row-wise or column-wise, such that * the sum of each row (or column) is one. - * * \param graph The input graph. - * \param res Pointer to an initialized matrix, the result is stored here. - * It will be resized as needed. - * \param column_wise Whether to normalize column-wise. + * \param sparsemat Pointer to an initialized matrix, the + * result is stored here. + * \param column_wise Whether to normalize column-wise. For undirected + * graphs this argument does not have any effect. * \return Error code. * - * Time complexity: O(|V||V|), |V| is the number of vertices in the graph. + * Time complexity: O(|V||V|), quadratic in the number of vertices. * - * \sa \ref igraph_get_stochastic_sparse(), the sparse version of this + * \sa igraph_get_stochastic_sparsemat(), the sparse version of this * function. */ -igraph_error_t igraph_get_stochastic( - const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t column_wise, - const igraph_vector_t *weights -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_bool_t directed = igraph_is_directed(graph); - igraph_integer_t i, from, to; - igraph_vector_t sums; - igraph_real_t sum; +int igraph_get_stochastic(const igraph_t *graph, + igraph_matrix_t *matrix, + igraph_bool_t column_wise) { - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); - igraph_matrix_null(res); - - IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_nodes); + int no_of_nodes = igraph_vcount(graph); + igraph_real_t sum; + int i, j; - if (directed) { - IGRAPH_CHECK(igraph_strength( - graph, &sums, igraph_vss_all(), - column_wise ? IGRAPH_IN : IGRAPH_OUT, - /* loops = */ true, weights - )); + IGRAPH_CHECK(igraph_get_adjacency(graph, matrix, + IGRAPH_GET_ADJACENCY_BOTH, /*eids=*/ 0)); - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - sum = VECTOR(sums)[column_wise ? to : from]; - MATRIX(*res, from, to) += WEIGHT_OF(i) / sum; + if (!column_wise) { + for (i = 0; i < no_of_nodes; i++) { + sum = 0.0; + for (j = 0; j < no_of_nodes; j++) { + sum += MATRIX(*matrix, i, j); + } + for (j = 0; j < no_of_nodes; j++) { + MATRIX(*matrix, i, j) /= sum; + } } } else { - IGRAPH_CHECK(igraph_strength( - graph, &sums, igraph_vss_all(), IGRAPH_ALL, - /* loops = */ true, weights - )); - - for (i = 0; i < no_of_edges; i++) { - from = IGRAPH_FROM(graph, i); - to = IGRAPH_TO(graph, i); - MATRIX(*res, from, to) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? to : from]; - MATRIX(*res, to, from) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? from: to]; + for (i = 0; i < no_of_nodes; i++) { + sum = 0.0; + for (j = 0; j < no_of_nodes; j++) { + sum += MATRIX(*matrix, j, i); + } + for (j = 0; j < no_of_nodes; j++) { + MATRIX(*matrix, j, i) /= sum; + } } } - igraph_vector_destroy(&sums); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } -#undef WEIGHT_OF -/** - * \function igraph_get_stochastic_sparse - * \brief The stochastic adjacency matrix of a graph. - * - * Stochastic matrix of a graph. The stochastic matrix of a graph is - * its adjacency matrix, normalized row-wise or column-wise, such that - * the sum of each row (or column) is one. - * - * \param graph The input graph. - * \param res Pointer to an \em initialized sparse matrix, the - * result is stored here. The matrix will be resized as needed. - * \param column_wise Whether to normalize column-wise. - * \return Error code. - * - * Time complexity: O(|V|+|E|), linear in the number of vertices and - * edges. - * - * \sa \ref igraph_get_stochastic(), the dense version of this function. - */ +int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise) { + igraph_vector_t sum; + int no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); + int i; -igraph_error_t igraph_get_stochastic_sparse( - const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise, - const igraph_vector_t *weights -) { - IGRAPH_CHECK(igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, weights, IGRAPH_LOOPS_TWICE)); + IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); - if (column_wise) { - IGRAPH_CHECK(igraph_sparsemat_normalize_cols(res, /* allow_zeros = */ 0)); + if (!column_wise) { + IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] == 0.0) { + IGRAPH_ERROR("Zero out-degree vertices not allowed", + IGRAPH_EINVAL); + } + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } + IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum)); } else { - IGRAPH_CHECK(igraph_sparsemat_normalize_rows(res, /* allow_zeros = */ 0)); + IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] == 0.0) { + IGRAPH_ERROR("Zero out-degree vertices not allowed", + IGRAPH_EINVAL); + } + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } + IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum)); } - return IGRAPH_SUCCESS; -} + igraph_vector_destroy(&sum); + IGRAPH_FINALLY_CLEAN(1); + return 0; +} /** * \function igraph_get_stochastic_sparsemat - * \brief Stochastic adjacency matrix of a graph (deprecated). - * - * This function is deprecated in favour of \ref igraph_get_stochastic_sparse(), - * but does not work in an identical way. This function takes an \em uninitialized - * \c igraph_sparsemat_t while \ref igraph_get_stochastic_sparse() takes - * an already initialized one. + * \brief Stochastic adjacency matrix of a graph * + * Stochastic matrix of a graph. The stochastic matrix of a graph is + * its adjacency matrix, normalized row-wise or column-wise, such that + * the sum of each row (or column) is one. * \param graph The input graph. - * \param res Pointer to an \em uninitialized sparse matrix, the - * result is stored here. The matrix will be resized as needed. + * \param sparsemat Pointer to an uninitialized sparse matrix, the + * result is stored here. * \param column_wise Whether to normalize column-wise. For undirected * graphs this argument does not have any effect. * \return Error code. * - * \deprecated-by igraph_get_stochastic_sparse 0.10.0 + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \sa igraph_get_stochastic(), the dense version of this function. */ -igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, - igraph_sparsemat_t *res, - igraph_bool_t column_wise) { +int igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise) { + + IGRAPH_CHECK(igraph_get_sparsemat(graph, sparsemat)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, sparsemat); + IGRAPH_CHECK(igraph_i_normalize_sparsemat(sparsemat, column_wise)); + IGRAPH_FINALLY_CLEAN(1); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges; - IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax)); - return igraph_get_stochastic_sparse(graph, res, column_wise, NULL); + return 0; } /** * \ingroup conversion * \function igraph_to_prufer - * \brief Converts a tree to its Prüfer sequence. + * \brief Converts a tree to its Prüfer sequence * * A Prüfer sequence is a unique sequence of integers associated * with a labelled tree. A tree on n >= 2 vertices can be represented by a @@ -963,20 +919,19 @@ igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, * \sa \ref igraph_from_prufer() * */ -igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* 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 + 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_int_t degrees; - igraph_vector_int_t neighbors; + igraph_vector_t degrees, neighbors; igraph_integer_t prufer_index = 0; igraph_integer_t n = igraph_vcount(graph); - igraph_bool_t is_tree = false; + igraph_bool_t is_tree = 0; IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL)); @@ -989,8 +944,8 @@ igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* pruf } IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2)); - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, n); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 1); + 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)); @@ -999,6 +954,7 @@ igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* pruf 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; @@ -1007,8 +963,8 @@ igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* pruf IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, leaf, IGRAPH_ALL)); /* Find the unique remaining neighbor of the leaf */ - neighbor_count = igraph_vector_int_size(&neighbors); - for (igraph_integer_t i = 0; i < neighbor_count; i++) { + neighbor_count = igraph_vector_size(&neighbors); + for (i = 0; i < neighbor_count; i++) { neighbor = VECTOR(neighbors)[i]; if (VECTOR(degrees)[neighbor] > 0) { break; @@ -1029,8 +985,8 @@ igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* pruf } } - igraph_vector_int_destroy(°rees); - igraph_vector_int_destroy(&neighbors); + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neighbors); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/misc/order_cycle.h b/src/vendor/cigraph/src/misc/conversion_internal.h similarity index 62% rename from src/vendor/cigraph/src/misc/order_cycle.h rename to src/vendor/cigraph/src/misc/conversion_internal.h index 2ed87f54ee2..e234a9b8b97 100644 --- a/src/vendor/cigraph/src/misc/order_cycle.h +++ b/src/vendor/cigraph/src/misc/conversion_internal.h @@ -1,6 +1,6 @@ /* IGraph library. - Copyright (C) 2022 The igraph development team + 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 @@ -16,20 +16,13 @@ along with this program. If not, see . */ -#ifndef IGRAPH_ORDER_CYCLE_H -#define IGRAPH_ORDER_CYCLE_H +#ifndef IGRAPH_MISC_CONVERSION_INTERNAL_H +#define IGRAPH_MISC_CONVERSION_INTERNAL_H -#include "igraph_decls.h" -#include "igraph_datatype.h" -#include "igraph_vector.h" +#include "igraph_sparsemat.h" +#include "igraph_types.h" -__BEGIN_DECLS +int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, + igraph_bool_t column_wise); -igraph_error_t igraph_i_order_cycle( - const igraph_t *graph, - const igraph_vector_int_t *cycle, - igraph_vector_int_t *res); - -__END_DECLS - -#endif /* IGRAPH_ORDER_CYCLE_H */ +#endif diff --git a/src/vendor/cigraph/src/misc/cycle_bases.c b/src/vendor/cigraph/src/misc/cycle_bases.c deleted file mode 100644 index 425a62cf5dd..00000000000 --- a/src/vendor/cigraph/src/misc/cycle_bases.c +++ /dev/null @@ -1,540 +0,0 @@ -/* - IGraph library. - Copyright (C) 2021-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, see . -*/ - -#include "igraph_cycles.h" - -#include "igraph_adjlist.h" -#include "igraph_components.h" -#include "igraph_dqueue.h" -#include "igraph_error.h" -#include "igraph_interface.h" - -#include "core/interruption.h" -#include "misc/order_cycle.h" - -/**** Fundamental cycles *****/ - -/* Computes fundamental cycles for the connected component containing 'start_vid', - * and appends them to 'result'. - * - * 'visited' must be a vector of length igraph_vcount(graph). - * visited[u] will be set to mark+1 or mark+2 for each visited vertex 'u'. - * No elements of 'visited' must have these values when calling this function. - * 'mark' can be specified in order to be able to re-use a 'visited' vector - * multiple times without having to re-set all its elements. - * - * During the operation of the function, mark+1 indicates that a vertex has been - * queued for processing, but not processed yet. mark+2 indicates that it has - * been processed. - */ -static igraph_error_t -igraph_i_fundamental_cycles_bfs( - const igraph_t *graph, - igraph_vector_int_list_t *result, - igraph_integer_t start_vid, - igraph_integer_t bfs_cutoff, - const igraph_inclist_t *inclist, - igraph_vector_int_t *visited, - igraph_integer_t mark /* mark used in 'visited' */) { - - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; - igraph_vector_int_t pred_edge; - igraph_vector_int_t u_back, v_back; - - if (start_vid < 0 || start_vid >= no_of_nodes) { - IGRAPH_ERROR("Invalid starting vertex id.", IGRAPH_EINVAL); - } - - if (mark > IGRAPH_INTEGER_MAX - 2) { - IGRAPH_ERROR("Graph too large for cycle basis.", IGRAPH_EOVERFLOW); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&pred_edge, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&u_back, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&v_back, 0); - - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 0); - - IGRAPH_CHECK(igraph_dqueue_int_push(&q, start_vid)); /* vertex id */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); /* distance from start_vid*/ - VECTOR(*visited)[start_vid] = mark + 1; /* mark as seen */ - VECTOR(pred_edge)[start_vid] = -1; /* non-valid predecessor edge id for root vertex */ - - while (! igraph_dqueue_int_empty(&q)) { - igraph_integer_t v = igraph_dqueue_int_pop(&q); - igraph_integer_t vdist = igraph_dqueue_int_pop(&q); - - igraph_vector_int_t *incs = igraph_inclist_get(inclist, v); - igraph_integer_t n = igraph_vector_int_size(incs); - igraph_integer_t i, j; - - IGRAPH_ALLOW_INTERRUPTION(); - - for (i=0; i < n; ++i) { - igraph_integer_t e = VECTOR(*incs)[i]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); - - if (e == VECTOR(pred_edge)[v]) { - /* do not follow the edge through which we came to v */ - continue; - } - - if (VECTOR(*visited)[u] == mark + 2) { - /* u has already been processed */ - continue; - } else if (VECTOR(*visited)[u] == mark + 1) { - /* u has been seen but not yet processed */ - - /* Found cycle edge u-v. Now we walk back up the BFS tree - * in order to find the common ancestor of u and v. We exploit - * that the distance of u from the start vertex is either the - * same as that of v, or one greater. */ - - igraph_integer_t up = u, vp = v; - igraph_integer_t u_back_len, v_back_len; - igraph_vector_int_t cycle; - - IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, e)); - for (;;) { - igraph_integer_t upe, vpe; - - if (up == vp) { - break; - } - - upe = VECTOR(pred_edge)[up]; - IGRAPH_CHECK(igraph_vector_int_push_back(&u_back, upe)); - up = IGRAPH_OTHER(graph, upe, up); - - if (up == vp) { - break; - } - - vpe = VECTOR(pred_edge)[vp]; - IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, vpe)); - vp = IGRAPH_OTHER(graph, vpe, vp); - } - - u_back_len = igraph_vector_int_size(&u_back); - v_back_len = igraph_vector_int_size(&v_back); - IGRAPH_VECTOR_INT_INIT_FINALLY(&cycle, u_back_len + v_back_len); - - for (j=0; j < v_back_len; ++j) { - VECTOR(cycle)[j] = VECTOR(v_back)[j]; - } - for (j=0; j < u_back_len; ++j) { - VECTOR(cycle)[v_back_len + j] = VECTOR(u_back)[u_back_len - j - 1]; - } - - igraph_vector_int_clear(&v_back); - igraph_vector_int_clear(&u_back); - - IGRAPH_CHECK(igraph_vector_int_list_push_back(result, &cycle)); - IGRAPH_FINALLY_CLEAN(1); /* pass ownership of 'cycle' to 'result' */ - } else { - /* encountering u for the first time, queue it for processing */ - - /* Only queue vertices with distance at most 'bfs_cutoff' from the root. */ - /* Negative 'bfs_cutoff' indicates no cutoff. */ - if (bfs_cutoff < 0 || vdist < bfs_cutoff) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, u)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, vdist + 1)); - VECTOR(*visited)[u] = mark + 1; - VECTOR(pred_edge)[u] = e; - } - } - } - - VECTOR(*visited)[v] = mark + 2; /* mark v as processed */ - } /* ! igraph_dqueue_int_empty(&q) */ - - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&v_back); - igraph_vector_int_destroy(&u_back); - igraph_vector_int_destroy(&pred_edge); - IGRAPH_FINALLY_CLEAN(4); - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_fundamental_cycles - * \brief Finds a fundamental cycle basis. - * - * \experimental - * - * This function computes a fundamental cycle basis associated with a breadth-first - * search tree of the graph. - * - * - * Edge directions are ignored. Multi-edges and self-loops are supported. - * - * \param graph The graph object. - * \param result An initialized integer vector list. The result will be stored here, - * each vector containing the edge IDs of a basis element. - * \param start_vid If negative, a complete fundamental cycle basis is returned. - * If a vertex ID, the fundamental cycles associated with the BFS tree rooted - * in that vertex will be returned, only for the weakly connected component - * containing that vertex. - * \param bfs_cutoff If negative, a complete cycle basis is returned. Otherwise, only - * cycles of length 2*bfs_cutoff + 1 or shorter are included. \p bfs_cutoff - * is used to limit the depth of the BFS tree when searching for cycle edges. - * \param weights Currently unused. - * \return Error code. - * - * Time complexity: O(|V| + |E|). - */ -igraph_error_t igraph_fundamental_cycles(const igraph_t *graph, - igraph_vector_int_list_t *result, - igraph_integer_t start_vid, - igraph_integer_t bfs_cutoff, - const igraph_vector_t *weights) { - - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t estimated_rank; - igraph_integer_t i; - igraph_inclist_t inclist; - igraph_vector_int_t visited; /* see comments before igraph_i_fundamental_cycles_bfs() */ - - IGRAPH_UNUSED(weights); - - if (start_vid >= no_of_nodes) { - IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVAL); - } - - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); - - /* Compute cycle rank assuming that the graph is connected. */ - estimated_rank = no_of_edges - no_of_nodes + 1; - estimated_rank = estimated_rank < 0 ? 0 : estimated_rank; - - igraph_vector_int_list_clear(result); - IGRAPH_CHECK(igraph_vector_int_list_reserve(result, estimated_rank)); - - if (start_vid < 0) { - for (i=0; i < no_of_nodes; ++i) { - if (! VECTOR(visited)[i]) { - IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, i, bfs_cutoff, &inclist, - &visited, /* mark */ 0)); - } - } - } else { - IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, start_vid, bfs_cutoff, &inclist, - &visited, /* mark */ 0)); - } - - igraph_vector_int_destroy(&visited); - igraph_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - - -/***** Minimum weight cycle basis *****/ - -/* In this implementation, the cycle vectors (basis elements) are stored as a sparse representation: - * a sorted list of edge indices. */ - - -/* qsort-compatible comparison for sparse cycle vectors: shorter ones come first, use lexicographic - * order for equal length ones. Lexicographic order helps keep row insertion into the reduced matrix - * efficient during Gaussian elimination, by ensuring that insertions usually happen near the end. */ -static int cycle_cmp(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { - igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); - - if (n1 < n2) { - return -1; - } else if (n1 > n2) { - return 1; - } else { - return igraph_vector_int_lex_cmp(v1, v2); - } -} - -/* Adding cycle vectors produces the symmetric difference of the corresponding edge sets. */ -static igraph_error_t cycle_add(const igraph_vector_int_t *a, const igraph_vector_int_t *b, igraph_vector_int_t *res) { - igraph_integer_t na = igraph_vector_int_size(a), nb = igraph_vector_int_size(b); - const igraph_integer_t *pa = VECTOR(*a), *pb = VECTOR(*b); - const igraph_integer_t *pa_end = pa + na, *pb_end = pb + nb; - - igraph_vector_int_clear(res); - for (;;) { - while (pa != pa_end && pb != pb_end && *pa < *pb) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); - pa++; - } - while (pa != pa_end && pb != pb_end && *pa == *pb) { - pa++; pb++; - } - while (pa != pa_end && pb != pb_end && *pb < *pa) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); - pb++; - } - if (pa == pa_end) { - while (pb != pb_end) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); - pb++; - } - break; - } - if (pb == pb_end) { - while (pa != pa_end) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); - pa++; - } - break; - } - } - - return IGRAPH_SUCCESS; -} - - -#define MATROW(m, i) (&VECTOR(m)[i]) -#define MATEL(m, i, j) VECTOR(*MATROW(m, i))[j] - -/* Gaussian elimination for sparse cycle vectors. 'reduced_matrix' is always maintained - * in row-echelon form. This function decides if 'cycle' is linearly independent of this - * matrix, and if not, it adds it to the matrix. */ -static igraph_error_t gaussian_elimination(igraph_vector_int_list_t *reduced_matrix, - const igraph_vector_int_t *cycle, - igraph_bool_t *independent) { - - const igraph_integer_t nrow = igraph_vector_int_list_size(reduced_matrix); - igraph_integer_t i; - - igraph_vector_int_t work, tmp; - - IGRAPH_CHECK(igraph_vector_int_init_copy(&work, cycle)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &work); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - - for (i=0; i < nrow; ++i) { - igraph_vector_int_t *row = MATROW(*reduced_matrix, i); - - if ( VECTOR(*row)[0] < VECTOR(work)[0] ) { - continue; - } else if ( VECTOR(*row)[0] == VECTOR(work)[0] ) { - IGRAPH_CHECK(cycle_add(row, &work, &tmp)); - if (igraph_vector_int_empty(&tmp)) { - *independent = false; - igraph_vector_int_destroy(&work); - igraph_vector_int_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; - } - IGRAPH_CHECK(igraph_vector_int_swap(&work, &tmp)); - } else { /* VECTOR(*row)[0] > VECTOR(work)[0] */ - break; - } - } - - /* 'cycle' was linearly independent, insert new row into matrix */ - *independent = true; - IGRAPH_CHECK(igraph_vector_int_list_insert(reduced_matrix, i, &work)); /* transfers ownership */ - - igraph_vector_int_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(2); /* +1, transferring ownership of 'work' to 'reduced_matrix' */ - - return IGRAPH_SUCCESS; -} - -#undef MATEL -#undef MATROW - - -/** - * \function igraph_minimum_cycle_basis - * \brief Computes a minimum weight cycle basis. - * - * \experimental - * - * This function computes a minimum weight cycle basis of a graph. Currently, - * a modified version of Horton's algorithm is used that allows for cutoffs. - * - * - * Edge directions are ignored. Multi-edges and self-loops are supported. - * - * - * References: - * - * - * Horton, J. D. (1987) - * A polynomial-time algorithm to find the shortest cycle basis of a graph, - * SIAM Journal on Computing, 16 (2): 358–366. - * https://doi.org/10.1137%2F0216026 - * - * \param graph The graph object. - * \param result An initialized integer vector list, the elements of the cycle - * basis will be stored here as vectors of edge IDs. - * \param bfs_cutoff If negative, an exact minimum cycle basis is returned. Otherwise - * only those cycles in the result will be part of some minimum cycle basis which - * are of size 2*bfs_cutoff + 1 or smaller. Cycles longer than this limit - * may not be of the smallest possible size. - * \p bfs_cutoff is used to limit the depth of the BFS tree when computing candidate - * cycles. Specifying a bfs_cutoff can speed up the computation substantially. - * \param complete Boolean value. Used only when \p bfs_cutoff was given. - * If true, a complete basis is returned. If false, only cycles not greater - * than 2*bfs_cutoff + 1 are returned. This may save computation - * time, however, the result will not span the entire cycle space. - * \param use_cycle_order If true, each cycle is returned in natural order: - * the edge IDs will appear ordered along the cycle. This comes at a small - * performance cost. If false, no guarantees are given about the ordering - * of edge IDs within cycles. This parameter exists solely to control - * performance tradeoffs. - * \param weights Currently unused. - * \return Error code. - * - * Time complexity: TODO. - */ -igraph_error_t igraph_minimum_cycle_basis(const igraph_t *graph, - igraph_vector_int_list_t *result, - igraph_integer_t bfs_cutoff, - igraph_bool_t complete, - igraph_bool_t use_cycle_order, - const igraph_vector_t *weights) { - - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t rank; - igraph_vector_int_list_t candidates; - - IGRAPH_UNUSED(weights); - - /* Compute candidate elements for the minimum weight basis. */ - { - igraph_inclist_t inclist; - igraph_vector_int_t visited; /* visited[v] % 3 is zero for unvisited vertices, see igraph_i_fundamental_cycles_bfs() */ - igraph_vector_int_t degrees; - igraph_integer_t no_of_comps; - igraph_integer_t mark; - - /* We use the degrees to avoid doing a BFS from vertices with d < 3, except in special cases. - * Degrees cannot be computed from the inclist because there we use IGRAPH_LOOPS_ONCE. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); - - IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_of_comps, IGRAPH_WEAK)); - rank = no_of_edges - no_of_nodes + no_of_comps; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); - - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - - /* TODO: estimate space to reserve. 'rank' is a lower bound only. */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&candidates, 0); - IGRAPH_CHECK(igraph_vector_int_list_reserve(&candidates, rank)); - - mark = 0; - for (igraph_integer_t i=0; i < no_of_nodes; ++i) { - igraph_integer_t degree = VECTOR(degrees)[i]; - igraph_bool_t vis = VECTOR(visited)[i] % 3 != 0; /* was vertex i visited already? */ - - /* Generally, we only need to run a BFS from vertices of degree 3 or greater. - * The exception is a connected component which is itself a cycle, and therefore - * only contains vertices of degree 2. Thus from unvisited vertices we always run - * a full BFS while from already visited ones only if their degree is at least 3. */ - - /* TODO: mark entire component as visited, not just vertex. */ - if (degree <= 1 || (vis && degree < 3)) { - continue; - } - - /* TODO: BFS is only necessary from a feedback vertex set, find fast FVS approximation algorithm. */ - - IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs( - graph, &candidates, i, (vis || !complete) ? bfs_cutoff : -1, &inclist, &visited, mark)); - mark += 3; - } - - igraph_inclist_destroy(&inclist); - igraph_vector_int_destroy(&visited); - igraph_vector_int_destroy(°rees); - IGRAPH_FINALLY_CLEAN(3); - } - - /* Sort candidates by size (= weight) and remove duplicates. */ - { - igraph_integer_t cand_count = igraph_vector_int_list_size(&candidates); - - for (igraph_integer_t i=0; i < cand_count; ++i) { - igraph_vector_int_sort(igraph_vector_int_list_get_ptr(&candidates, i)); - } - igraph_vector_int_list_sort(&candidates, &cycle_cmp); - igraph_vector_int_list_remove_consecutive_duplicates(&candidates, igraph_vector_int_all_e); - } - - igraph_vector_int_list_clear(result); - IGRAPH_CHECK(igraph_vector_int_list_reserve(result, rank)); - - /* Find a complete basis, starting with smallest elements. */ - /* This is typically the slowest part of the algorithm. */ - { - igraph_integer_t cand_len = igraph_vector_int_list_size(&candidates); - igraph_vector_int_list_t reduced_matrix; - igraph_bool_t independent; - - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&reduced_matrix, 0); - - for (igraph_integer_t i=0; i < cand_len; ++i) { - const igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(&candidates, i); - - IGRAPH_ALLOW_INTERRUPTION(); - - IGRAPH_CHECK(gaussian_elimination(&reduced_matrix, cycle, &independent)); - if (independent) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(result, cycle)); - } - - if (igraph_vector_int_list_size(&reduced_matrix) == rank) { - /* We have a complete basis. */ - break; - } - } - - igraph_vector_int_list_destroy(&reduced_matrix); - IGRAPH_FINALLY_CLEAN(1); - } - - igraph_vector_int_list_destroy(&candidates); - IGRAPH_FINALLY_CLEAN(1); - - if (use_cycle_order) { - igraph_integer_t result_size = igraph_vector_int_list_size(result); - igraph_vector_int_t tmp; - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - for (igraph_integer_t i=0; i < result_size; ++i) { - igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(result, i); - IGRAPH_CHECK(igraph_vector_int_update(&tmp, cycle)); - IGRAPH_CHECK(igraph_i_order_cycle(graph, &tmp, cycle)); - } - igraph_vector_int_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/misc/degree_sequence.cpp b/src/vendor/cigraph/src/misc/degree_sequence.cpp index 1debe31d473..fcc11305bd4 100644 --- a/src/vendor/cigraph/src/misc/degree_sequence.cpp +++ b/src/vendor/cigraph/src/misc/degree_sequence.cpp @@ -18,9 +18,7 @@ */ #include "igraph_constructors.h" - -#include "core/exceptions.h" -#include "math/safe_intop.h" +#include "igraph_interface.h" #include #include @@ -36,10 +34,10 @@ // (vertex, degree) pair struct vd_pair { - igraph_integer_t vertex; + long vertex; igraph_integer_t degree; - vd_pair(igraph_integer_t vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} + vd_pair(long vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} }; // (indegree, outdegree) @@ -47,10 +45,10 @@ typedef std::pair bidegree; // (vertex, bidegree) pair struct vbd_pair { - igraph_integer_t vertex; + long vertex; bidegree degree; - vbd_pair(igraph_integer_t vertex, bidegree degree) : vertex(vertex), degree(degree) {} + vbd_pair(long vertex, bidegree degree) : vertex(vertex), degree(degree) {} }; // Comparison function for vertex-degree pairs. @@ -71,14 +69,14 @@ template inline bool degree_less(const T &a, const T &b) { // 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 igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool largest) { - igraph_integer_t n = igraph_vector_int_size(deg); +static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *edges, bool largest) { + long n = igraph_vector_size(deg); - igraph_integer_t ec = 0; // number of edges added so far + long ec = 0; // number of edges added so far std::vector vertices; vertices.reserve(n); - for (igraph_integer_t i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); } @@ -102,7 +100,7 @@ static igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igra } if (largest) { - for (igraph_integer_t i = 0; i < vd.degree; ++i) { + for (int i = 0; i < vd.degree; ++i) { if (--(vertices[vertices.size() - 1 - i].degree) < 0) { goto fail; } @@ -113,7 +111,7 @@ static igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igra } else { // this loop can only be reached if all zero-degree nodes have already been removed // therefore decrementing remaining degrees is safe - for (igraph_integer_t i = 0; i < vd.degree; ++i) { + for (int i = 0; i < vd.degree; ++i) { vertices[i].degree--; VECTOR(*edges)[2 * (ec + i)] = vd.vertex; @@ -132,34 +130,34 @@ static igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igra // Choose vertices in the order of their IDs. -static igraph_error_t igraph_i_havel_hakimi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges) { - igraph_integer_t n = igraph_vector_int_size(deg); +static int igraph_i_havel_hakimi_index(const igraph_vector_t *deg, igraph_vector_t *edges) { + long n = igraph_vector_size(deg); - igraph_integer_t ec = 0; // number of edges added so far + long ec = 0; // number of edges added so far typedef std::list vlist; vlist vertices; - for (igraph_integer_t i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); } std::vector pointers; pointers.reserve(n); - for (auto it = vertices.begin(); it != vertices.end(); ++it) { + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { pointers.push_back(it); } - for (const auto &pt : pointers) { + for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { vertices.sort(degree_greater); - vd_pair vd = *pt; - vertices.erase(pt); + vd_pair vd = **pt; + vertices.erase(*pt); if (vd.degree == 0) { continue; } - igraph_integer_t k; + int k; vlist::iterator it; for (it = vertices.begin(), k = 0; k != vd.degree && it != vertices.end(); @@ -215,23 +213,23 @@ static void bubble_up(It first, It last, Compare comp) { // by adding loops on the last vertex. // If largest=false, and the degree sequence was potentially connected, the resulting // graph will be connected. -static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops, bool largest) { - igraph_integer_t vcount = igraph_vector_int_size(deg); +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 (igraph_integer_t i = 0; i < vcount; ++i) { - igraph_integer_t d = VECTOR(*deg)[i]; + 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); - igraph_integer_t ec = 0; + long ec = 0; while (! vertices.empty()) { // Remove any zero degrees, and error on negative ones. @@ -247,7 +245,7 @@ static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_ // or throw an error, depending on the 'loops' setting. if (vertices.size() == 1) { if (loops) { - for (igraph_integer_t i=0; i < w.degree/2; ++i) { + for (long i=0; i < w.degree/2; ++i) { VECTOR(*edges)[2*ec] = w.vertex; VECTOR(*edges)[2*ec+1] = w.vertex; ec++; @@ -289,39 +287,39 @@ static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_ } -static igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops) { - igraph_integer_t vcount = igraph_vector_int_size(deg); +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 (igraph_integer_t i = 0; i < vcount; ++i) { + for (int i = 0; i < vcount; ++i) { vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); } std::vector pointers; pointers.reserve(vcount); - for (auto it = vertices.begin(); it != vertices.end(); ++it) { + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { pointers.push_back(it); } // Initial sort vertices.sort(degree_greater); - igraph_integer_t ec = 0; - for (const auto &pt : pointers) { - vd_pair vd = *pt; - vertices.erase(pt); + 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) { - auto uit = vertices.begin(); + 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 (igraph_integer_t i=0; i < vd.degree/2; ++i) { + for (long i=0; i < vd.degree/2; ++i) { VECTOR(*edges)[2*ec] = vd.vertex; VECTOR(*edges)[2*ec+1] = vd.vertex; ec++; @@ -343,7 +341,7 @@ static igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vecto // 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) { - auto wit = uit; + vlist::iterator wit = uit; ++wit; if (wit->degree > uit->degree) { @@ -371,14 +369,14 @@ inline bool is_nonzero_outdeg(const vbd_pair &vd) { // 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 igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges, bool smallest) { - igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices +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 - igraph_integer_t ec = 0; // number of edges added so far + long ec = 0; // number of edges added so far std::vector vertices; vertices.reserve(n); - for (igraph_integer_t i = 0; i < n; ++i) { + for (int i = 0; i < n; ++i) { vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i]))); } @@ -408,13 +406,13 @@ static igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, } // are there a sufficient number of other vertices to connect to? - if (static_cast(vertices.size()) - 1 < vdp->degree.second) { + if (static_cast(vertices.size()) - 1 < vdp->degree.second) { goto fail; } // create the connections - igraph_integer_t k = 0; - for (auto it = vertices.begin(); + int k = 0; + for (std::vector::iterator it = vertices.begin(); k < vdp->degree.second; ++it) { if (it->vertex == vdp->vertex) { @@ -442,36 +440,36 @@ static igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, // Choose vertices in the order of their IDs. -static igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges) { - igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices +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 - igraph_integer_t ec = 0; // number of edges added so far + long ec = 0; // number of edges added so far typedef std::list vlist; vlist vertices; - for (igraph_integer_t i = 0; i < n; ++i) { + 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 (auto it = vertices.begin(); it != vertices.end(); ++it) { + for (vlist::iterator it = vertices.begin(); it != vertices.end(); ++it) { pointers.push_back(it); } - for (const auto &pt : pointers) { + 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; + vbd_pair &vd = **pt; if (vd.degree.second == 0) { continue; } - igraph_integer_t k = 0; + int k = 0; vlist::iterator it; for (it = vertices.begin(); k != vd.degree.second && it != vertices.end(); @@ -508,29 +506,27 @@ static igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *ou /***** Main functions *****/ /**************************/ -static igraph_error_t igraph_i_realize_undirected_degree_sequence( +static int igraph_i_realize_undirected_degree_sequence( igraph_t *graph, - const igraph_vector_int_t *deg, + const igraph_vector_t *deg, igraph_edge_type_sw_t allowed_edge_types, igraph_realize_degseq_t method) { - igraph_integer_t node_count = igraph_vector_int_size(deg); - igraph_integer_t deg_sum; - - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(deg, °_sum)); + 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_int_min(deg) < 0) { + if (node_count > 0 && igraph_vector_min(deg) < 0) { IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); } - igraph_vector_int_t edges; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, deg_sum); + igraph_vector_t edges; + IGRAPH_CHECK(igraph_vector_init(&edges, deg_sum)); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; 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) { @@ -592,39 +588,34 @@ static igraph_error_t igraph_i_realize_undirected_degree_sequence( * so no explanatory error message for now. */ return IGRAPH_UNIMPLEMENTED; } - IGRAPH_HANDLE_EXCEPTIONS_END; - IGRAPH_CHECK(igraph_create(graph, &edges, node_count, false)); + igraph_create(graph, &edges, igraph_integer_t(node_count), false); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_realize_directed_degree_sequence( +static int igraph_i_realize_directed_degree_sequence( igraph_t *graph, - const igraph_vector_int_t *outdeg, - const igraph_vector_int_t *indeg, + const igraph_vector_t *outdeg, + const igraph_vector_t *indeg, igraph_edge_type_sw_t allowed_edge_types, igraph_realize_degseq_t method) { - igraph_integer_t node_count = igraph_vector_int_size(outdeg); - igraph_integer_t edge_count, edge_count2, indeg_sum; + long node_count = igraph_vector_size(outdeg); + long edge_count = long(igraph_vector_sum(outdeg)); - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outdeg, &edge_count)); - - if (igraph_vector_int_size(indeg) != node_count) { + if (igraph_vector_size(indeg) != node_count) { IGRAPH_ERROR("In- and out-degree sequences must have the same length.", IGRAPH_EINVAL); } - - IGRAPH_CHECK(igraph_i_safe_vector_int_sum(indeg, &indeg_sum)); - if (indeg_sum != edge_count) { + 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_int_min(outdeg) < 0 || igraph_vector_int_min(indeg) < 0)) { + if (node_count > 0 && (igraph_vector_min(outdeg) < 0 || igraph_vector_min(indeg) < 0)) { IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); } @@ -633,11 +624,10 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( IGRAPH_ERROR("Realizing directed degree sequences as non-simple graphs is not implemented.", IGRAPH_UNIMPLEMENTED); } - igraph_vector_int_t edges; - IGRAPH_SAFE_MULT(edge_count, 2, &edge_count2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, edge_count2); + igraph_vector_t edges; + IGRAPH_CHECK(igraph_vector_init(&edges, 2 * edge_count)); + IGRAPH_FINALLY(igraph_vector_destroy, &edges); - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; switch (method) { case IGRAPH_REALIZE_DEGSEQ_SMALLEST: IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true)); @@ -651,11 +641,10 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( default: IGRAPH_ERROR("Invalid directed degree sequence realization method.", IGRAPH_EINVAL); } - IGRAPH_HANDLE_EXCEPTIONS_END; - IGRAPH_CHECK(igraph_create(graph, &edges, node_count, true)); + igraph_create(graph, &edges, igraph_integer_t(node_count), true); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -701,7 +690,7 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * 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/2098770 + * https://www.jstor.org/stable/2098746 * * * D. J. Kleitman and D. L. Wang, @@ -711,8 +700,8 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * * * Sz. Horvát and C. D. Modes, - * Connectedness matters: construction and exact random sampling of connected networks (2021). - * https://doi.org/10.1088/2632-072X/abced5 + * 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 @@ -739,7 +728,7 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * 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, 2021, as well as + * 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. @@ -769,20 +758,28 @@ static igraph_error_t igraph_i_realize_directed_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. * - * \example examples/simple/igraph_realize_degree_sequence.c */ -igraph_error_t igraph_realize_degree_sequence( +int igraph_realize_degree_sequence( igraph_t *graph, - const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, + const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_edge_type_sw_t allowed_edge_types, igraph_realize_degseq_t method) { - bool directed = indeg != NULL; + 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; - 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); + 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/vendor/cigraph/src/misc/embedding.c b/src/vendor/cigraph/src/misc/embedding.c index 3c4a31496bb..d873ff3c07f 100644 --- a/src/vendor/cigraph/src/misc/embedding.c +++ b/src/vendor/cigraph/src/misc/embedding.c @@ -25,14 +25,11 @@ #include "igraph_adjlist.h" #include "igraph_blas.h" +#include "igraph_centrality.h" #include "igraph_interface.h" #include "igraph_random.h" #include "igraph_structural.h" -#include "core/math.h" - -#include - typedef struct { const igraph_t *graph; const igraph_vector_t *cvec; @@ -45,13 +42,13 @@ typedef struct { /* Adjacency matrix, unweighted, undirected. Eigendecomposition is used */ -static igraph_error_t igraph_i_asembeddingu(igraph_real_t *to, const igraph_real_t *from, +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; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (A+cD) from */ for (i = 0; i < n; i++) { @@ -59,18 +56,18 @@ static igraph_error_t igraph_i_asembeddingu(igraph_real_t *to, const igraph_real nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; to[i] += from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Adjacency matrix, weighted, undirected. Eigendecomposition is used. */ -static igraph_error_t igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from, +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; @@ -78,7 +75,7 @@ static igraph_error_t igraph_i_asembeddinguw(igraph_real_t *to, const igraph_rea const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (A+cD) from */ for (i = 0; i < n; i++) { @@ -86,19 +83,19 @@ static igraph_error_t igraph_i_asembeddinguw(igraph_real_t *to, const igraph_rea nlen = igraph_vector_int_size(incs); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; + return 0; } /* Adjacency matrix, unweighted, directed. SVD. */ -static igraph_error_t igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, +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; @@ -106,7 +103,7 @@ static igraph_error_t igraph_i_asembedding(igraph_real_t *to, const igraph_real_ const igraph_vector_t *cvec = data->cvec; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* tmp = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -114,7 +111,7 @@ static igraph_error_t igraph_i_asembedding(igraph_real_t *to, const igraph_real_ nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; VECTOR(*tmp)[i] += from[nei]; } VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i]; @@ -126,23 +123,23 @@ static igraph_error_t igraph_i_asembedding(igraph_real_t *to, const igraph_real_ nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Adjacency matrix, unweighted, directed. SVD, right eigenvectors */ -static igraph_error_t igraph_i_asembedding_right(igraph_real_t *to, const igraph_real_t *from, +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; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -150,17 +147,17 @@ static igraph_error_t igraph_i_asembedding_right(igraph_real_t *to, const igraph nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; to[i] += from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Adjacency matrix, weighted, directed. SVD. */ -static igraph_error_t igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, +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; @@ -170,7 +167,7 @@ static igraph_error_t igraph_i_asembeddingw(igraph_real_t *to, const igraph_real const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *incs; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* tmp = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -178,8 +175,8 @@ static igraph_error_t igraph_i_asembeddingw(igraph_real_t *to, const igraph_real nlen = igraph_vector_int_size(incs); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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]; } @@ -192,19 +189,19 @@ static igraph_error_t igraph_i_asembeddingw(igraph_real_t *to, const igraph_real nlen = igraph_vector_int_size(incs); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; + return 0; } /* Adjacency matrix, weighted, directed. SVD, right eigenvectors. */ -static igraph_error_t igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *from, +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; @@ -212,7 +209,7 @@ static igraph_error_t igraph_i_asembeddingw_right(igraph_real_t *to, const igrap const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -220,25 +217,25 @@ static igraph_error_t igraph_i_asembeddingw_right(igraph_real_t *to, const igrap nlen = igraph_vector_int_size(incs); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; + return 0; } /* Laplacian D-A, unweighted, undirected. Eigendecomposition. */ -static igraph_error_t igraph_i_lsembedding_da(igraph_real_t *to, const igraph_real_t *from, +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; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (D-A) from */ for (i = 0; i < n; i++) { @@ -246,17 +243,17 @@ static igraph_error_t igraph_i_lsembedding_da(igraph_real_t *to, const igraph_re nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; to[i] -= from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian D-A, weighted, undirected. Eigendecomposition. */ -static igraph_error_t igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from, +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; @@ -264,7 +261,7 @@ static igraph_error_t igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_r const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = (D-A) from */ for (i = 0; i < n; i++) { @@ -272,19 +269,19 @@ static igraph_error_t igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_r nlen = igraph_vector_int_size(incs); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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 IGRAPH_SUCCESS; + return 0; } /* Laplacian DAD, unweighted, undirected. Eigendecomposition. */ -static igraph_error_t igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from, +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; @@ -292,7 +289,7 @@ static igraph_error_t igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_r const igraph_vector_t *cvec = data->cvec; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = D^1/2 from */ for (i = 0; i < n; i++) { @@ -305,7 +302,7 @@ static igraph_error_t igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_r nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -315,10 +312,10 @@ static igraph_error_t igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_r to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *from, +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; @@ -328,7 +325,7 @@ static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_ const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *incs; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = D^-1/2 from */ for (i = 0; i < n; i++) { @@ -341,8 +338,8 @@ static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_ nlen = igraph_vector_int_size(incs); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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]; } @@ -359,8 +356,8 @@ static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_ nlen = igraph_vector_int_size(incs); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*incs)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + 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]; } @@ -371,34 +368,37 @@ static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_ to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian I-DAD, unweighted, undirected. Eigendecomposition. */ -static igraph_error_t igraph_i_lsembedding_idad(igraph_real_t *to, const igraph_real_t *from, +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 (int i = 0; i < n; i++) { + for (i = 0; i < n; i++) { to[i] = from[i] - to[i]; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lsembedding_idadw(igraph_real_t *to, const igraph_real_t *from, +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 (int i = 0; i < n; i++) { + for (i = 0; i < n; i++) { to[i] = from[i] - to[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian OAP, unweighted, directed. SVD. */ -static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *from, +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; @@ -408,7 +408,7 @@ static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_ const igraph_vector_t *deg_out = data->cvec2; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* tmp = O' from */ for (i = 0; i < n; i++) { @@ -421,7 +421,7 @@ static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_ nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + int nei = VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } } @@ -442,7 +442,7 @@ static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_ nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + int nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -452,11 +452,11 @@ static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_ to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian OAP, unweighted, directed. SVD, right eigenvectors. */ -static igraph_error_t igraph_i_lseembedding_oap_right(igraph_real_t *to, +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; @@ -465,7 +465,7 @@ static igraph_error_t igraph_i_lseembedding_oap_right(igraph_real_t *to, const igraph_vector_t *deg_out = data->cvec2; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; + int i, j, nlen; /* to = O' from */ for (i = 0; i < n; i++) { @@ -478,7 +478,7 @@ static igraph_error_t igraph_i_lseembedding_oap_right(igraph_real_t *to, nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + int nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -488,11 +488,11 @@ static igraph_error_t igraph_i_lseembedding_oap_right(igraph_real_t *to, to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian OAP, weighted, directed. SVD. */ -static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *from, +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; @@ -504,9 +504,7 @@ static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; - igraph_integer_t edge, nei; - igraph_real_t w; + int i, j, nlen; /* tmp = O' from */ for (i = 0; i < n; i++) { @@ -519,9 +517,9 @@ static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - edge = VECTOR(*neis)[j]; - nei = IGRAPH_OTHER(graph, edge, i); - w = VECTOR(*weights)[edge]; + 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]; } } @@ -542,9 +540,9 @@ static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - edge = VECTOR(*neis)[j]; - nei = IGRAPH_OTHER(graph, edge, i); - w = VECTOR(*weights)[edge]; + 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]; } } @@ -554,11 +552,11 @@ static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } /* Laplacian OAP, weighted, directed. SVD, right eigenvectors. */ -static igraph_error_t igraph_i_lseembedding_oapw_right(igraph_real_t *to, +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; @@ -569,9 +567,7 @@ static igraph_error_t igraph_i_lseembedding_oapw_right(igraph_real_t *to, const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - igraph_integer_t i, j, nlen; - igraph_integer_t edge, nei; - igraph_real_t w; + int i, j, nlen; /* to = O' from */ for (i = 0; i < n; i++) { @@ -584,9 +580,9 @@ static igraph_error_t igraph_i_lseembedding_oapw_right(igraph_real_t *to, nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - edge = VECTOR(*neis)[j]; - nei = IGRAPH_OTHER(graph, edge, i); - w = VECTOR(*weights)[edge]; + 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]; } } @@ -596,10 +592,10 @@ static igraph_error_t igraph_i_lseembedding_oapw_right(igraph_real_t *to, to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, +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, @@ -620,7 +616,7 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, igraph_vector_t tmp; igraph_adjlist_t outlist, inlist; igraph_inclist_t eoutlist, einlist; - igraph_integer_t i, j, cveclen = igraph_vector_size(cvec); + int i, j, cveclen = igraph_vector_size(cvec); igraph_i_asembedding_data_t data; igraph_vector_t tmpD; @@ -658,14 +654,6 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, "the number of vertices or scalar", IGRAPH_EINVAL); } - if (vc > INT_MAX) { - IGRAPH_ERROR("Graph too large for ARPACK", IGRAPH_EOVERFLOW); - } - - if (no > INT_MAX) { - IGRAPH_ERROR("Too many eigenvectors requested from ARPACK", IGRAPH_EOVERFLOW); - } - IGRAPH_CHECK(igraph_matrix_resize(X, vc, no)); if (Y) { IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no)); @@ -677,10 +665,11 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, if (Y) { igraph_matrix_null(Y); } - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INIT_FINALLY(&tmp, vc); + 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); @@ -698,9 +687,9 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, } IGRAPH_VECTOR_INIT_FINALLY(&tmpD, no); - options->n = (int) vc; + options->n = vc; options->start = 1; /* no random start vector */ - options->nev = (int) no; + options->nev = no; switch (which) { case IGRAPH_EIGEN_LM: options->which[0] = 'L'; options->which[1] = 'M'; @@ -714,9 +703,9 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, default: break; } - options->ncv = options->nev + 3; - if (options->ncv > options->n) { - options->ncv = options->n; + options->ncv = no + 3; + if (options->ncv > vc) { + options->ncv = vc; } /* We provide a random start vector to ARPACK on our own to ensure that @@ -735,7 +724,7 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, for (i = 0; i < no; i++) { igraph_real_t norm; igraph_vector_t v; - callback_right(&MATRIX(*Y, 0, i), &MATRIX(*X, 0, i), (int) vc, &data); + 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); @@ -806,7 +795,7 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -867,15 +856,15 @@ static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, * 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. Supply \c NULL to use the defaults. 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. + * 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. * */ -igraph_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, +int igraph_adjacency_spectral_embedding(const igraph_t *graph, igraph_integer_t n, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -898,10 +887,6 @@ igraph_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, callback_right = 0; } - if (options == 0) { - options = igraph_arpack_options_get_default(); - } - return igraph_i_spectral_embedding(graph, n, weights, which, scaled, X, Y, D, cvec, /* deg2=*/ 0, options, callback, callback_right, @@ -909,7 +894,7 @@ igraph_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, /*eigen=*/ !directed, /*zapsmall=*/ 1); } -static igraph_error_t igraph_i_lse_und(const igraph_t *graph, +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, @@ -940,14 +925,15 @@ static igraph_error_t igraph_i_lse_und(const igraph_t *graph, } IGRAPH_VECTOR_INIT_FINALLY(°, 0); - IGRAPH_CHECK(igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1, weights)); + 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: { - igraph_integer_t i, n = igraph_vector_size(°); + int i, n = igraph_vector_size(°); for (i = 0; i < n; i++) { VECTOR(deg)[i] = 1.0 / sqrt(VECTOR(deg)[i]); } @@ -965,10 +951,10 @@ static igraph_error_t igraph_i_lse_und(const igraph_t *graph, igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, +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, @@ -985,7 +971,7 @@ static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, weights ? igraph_i_lseembedding_oapw_right : igraph_i_lseembedding_oap_right; igraph_vector_t deg_in, deg_out; - igraph_integer_t i, n = igraph_vcount(graph); + int i, n = igraph_vcount(graph); if (type != IGRAPH_EMBEDDING_OAP) { IGRAPH_ERROR("Invalid Laplacian spectral embedding type", IGRAPH_EINVAL); @@ -993,8 +979,10 @@ static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, IGRAPH_VECTOR_INIT_FINALLY(°_in, n); IGRAPH_VECTOR_INIT_FINALLY(°_out, n); - IGRAPH_CHECK(igraph_strength(graph, °_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, weights)); - IGRAPH_CHECK(igraph_strength(graph, °_out, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1, weights)); + 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]); @@ -1011,7 +999,7 @@ static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, igraph_vector_destroy(°_out); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -1064,17 +1052,17 @@ static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, * 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. Supply \c NULL to use the defaults. 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. + * 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. */ -igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, +int igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_integer_t n, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -1085,10 +1073,6 @@ igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_vector_t *D, igraph_arpack_options_t *options) { - if (options == 0) { - options = igraph_arpack_options_get_default(); - } - if (igraph_is_directed(graph)) { return igraph_i_lse_dir(graph, n, weights, which, type, scaled, X, Y, D, options); @@ -1100,7 +1084,7 @@ igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, /** * \function igraph_dim_select - * \brief Dimensionality selection. + * Dimensionality selection * * Dimensionality selection for singular values using * profile likelihood. @@ -1133,9 +1117,9 @@ igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, * \sa \ref igraph_adjacency_spectral_embedding(). */ -igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { +int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { - igraph_integer_t i, n = igraph_vector_size(sv); + 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; @@ -1149,7 +1133,7 @@ igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *di if (n == 1) { *dim = 1; - return IGRAPH_SUCCESS; + return 0; } for (i = 0; i < n; i++) { @@ -1159,7 +1143,7 @@ igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *di } for (i = 0; i < n - 1; i++) { - igraph_integer_t n1 = i + 1, n2 = n - i - 1, n1m1 = n1 - 1, n2m1 = n2 - 1; + 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; @@ -1197,5 +1181,5 @@ igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *di *dim = n; } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/misc/feedback_arc_set.c b/src/vendor/cigraph/src/misc/feedback_arc_set.c index 1cead4cf002..5f753a36e45 100644 --- a/src/vendor/cigraph/src/misc/feedback_arc_set.c +++ b/src/vendor/cigraph/src/misc/feedback_arc_set.c @@ -22,26 +22,27 @@ */ -#include "igraph_structural.h" -#include "misc/feedback_arc_set.h" - #include "igraph_components.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_vector_list.h" +#include "igraph_structural.h" #include "igraph_visitor.h" #include "internal/glpk_support.h" -#include "math/safe_intop.h" +#include "misc/feedback_arc_set.h" + +int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights); -#include /** * \ingroup structural * \function igraph_feedback_arc_set - * \brief Feedback arc set of a graph using exact or heuristic methods. + * \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. @@ -80,7 +81,7 @@ * * Time complexity: depends on \p algo, see the time complexities there. */ -igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_t *result, +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)) @@ -106,19 +107,17 @@ igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_ /** * Solves the feedback arc set problem for undirected graphs. */ -igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_int_t *result, - const igraph_vector_t *weights, igraph_vector_int_t *layering) { - - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t edges; +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_INT_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + 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_init_copy(&vcopy, weights)); + 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)); @@ -132,30 +131,32 @@ igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igrap /* 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_int_sort(&edges); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, -1)); /* guard element */ + igraph_vector_sort(&edges); + IGRAPH_CHECK(igraph_vector_push_back(&edges, -1)); /* guard element */ - if (result) { - igraph_vector_int_clear(result); - for (igraph_integer_t i = 0, j = 0; i < no_of_edges; i++) { + 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_int_push_back(result, i)); + IGRAPH_CHECK(igraph_vector_push_back(result, i)); } } - if (layering) { + if (layering != 0) { igraph_vector_t degrees; - igraph_vector_int_t roots; + igraph_vector_t roots; IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&roots, no_of_nodes); - IGRAPH_CHECK(igraph_strength(graph, °rees, igraph_vss_all(), - IGRAPH_ALL, /* loops */ false, weights)); - IGRAPH_CHECK(igraph_vector_qsort_ind(°rees, &roots, IGRAPH_DESCENDING)); + 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, @@ -164,7 +165,7 @@ igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igrap /* restricted = */ 0, /* order = */ 0, /* rank = */ 0, - /* parents = */ 0, + /* father = */ 0, /* pred = */ 0, /* succ = */ 0, /* dist = */ layering, @@ -172,11 +173,11 @@ igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igrap /* extra = */ 0)); igraph_vector_destroy(°rees); - igraph_vector_int_destroy(&roots); + igraph_vector_destroy(&roots); IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -185,32 +186,29 @@ igraph_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igrap /** * Solves the feedback arc set problem using the heuristics of Eades et al. */ -igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_int_t *result, - const igraph_vector_t *weights, igraph_vector_int_t *layers) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, k, v, eid, nodes_left; - igraph_dqueue_int_t sources, sinks; - igraph_vector_int_t neis; - igraph_vector_int_t indegrees, outdegrees; +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; - igraph_integer_t *ordering; - igraph_integer_t order_next_pos = 0, order_next_neg = -1; + long int* ordering; + long int order_next_pos = 0, order_next_neg = -1; igraph_real_t diff, maxdiff; - ordering = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(ordering, "Insufficient memory for finding feedback arc set."); + ordering = IGRAPH_CALLOC(no_of_nodes, long int); IGRAPH_FINALLY(igraph_free, ordering); - IGRAPH_VECTOR_INT_INIT_FINALLY(&indegrees, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outdegrees, no_of_nodes); + 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_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); - IGRAPH_CHECK(igraph_dqueue_int_init(&sinks, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sinks); + 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)); @@ -219,12 +217,8 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec 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_resize(&instrengths, no_of_nodes)); - IGRAPH_CHECK(igraph_vector_resize(&outstrengths, no_of_nodes)); - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - VECTOR(instrengths)[i] = VECTOR(indegrees)[i]; - VECTOR(outstrengths)[i] = VECTOR(outdegrees)[i]; - } + IGRAPH_CHECK(igraph_vector_update(&instrengths, &indegrees)); + IGRAPH_CHECK(igraph_vector_update(&outstrengths, &outdegrees)); } /* Find initial sources and sinks */ @@ -238,29 +232,29 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; } else { /* This is a source */ - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, i)); + igraph_dqueue_push(&sources, i); } } else if (VECTOR(outdegrees)[i] == 0) { /* This is a sink */ - IGRAPH_CHECK(igraph_dqueue_int_push(&sinks, i)); + 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_int_empty(&sources)) { - i = igraph_dqueue_int_pop(&sources); + 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, i, + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); - j = igraph_vector_int_size(&neis); + j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { - eid = VECTOR(neis)[i]; + eid = (long int) VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ @@ -269,15 +263,15 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, k)); + IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); } } nodes_left--; } /* (2) Remove the sinks one by one */ - while (!igraph_dqueue_int_empty(&sinks)) { - i = igraph_dqueue_int_pop(&sinks); + 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) { @@ -288,11 +282,11 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec /* 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, i, + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) i, IGRAPH_IN)); - j = igraph_vector_int_size(&neis); + j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { - eid = VECTOR(neis)[i]; + eid = (long int) VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ @@ -301,7 +295,7 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec VECTOR(outdegrees)[k]--; VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(outdegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sinks, k)); + IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); } } nodes_left--; @@ -324,11 +318,11 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec /* Remove vertex v */ ordering[v] = order_next_pos++; /* Remove outgoing edges */ - IGRAPH_CHECK(igraph_incident(graph, &neis, v, + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, IGRAPH_OUT)); - j = igraph_vector_int_size(&neis); + j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { - eid = VECTOR(neis)[i]; + eid = (long int) VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ @@ -337,15 +331,15 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, k)); + IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); } } /* Remove incoming edges */ - IGRAPH_CHECK(igraph_incident(graph, &neis, v, + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, IGRAPH_IN)); - j = igraph_vector_int_size(&neis); + j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { - eid = VECTOR(neis)[i]; + eid = (long int) VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ @@ -354,7 +348,7 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec 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_int_push(&sinks, k)); + IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); } } @@ -364,13 +358,13 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec } } - igraph_dqueue_int_destroy(&sinks); - igraph_dqueue_int_destroy(&sources); - igraph_vector_int_destroy(&neis); + igraph_dqueue_destroy(&sinks); + igraph_dqueue_destroy(&sources); + igraph_vector_destroy(&neis); igraph_vector_destroy(&outstrengths); igraph_vector_destroy(&instrengths); - igraph_vector_int_destroy(&outdegrees); - igraph_vector_int_destroy(&indegrees); + igraph_vector_destroy(&outdegrees); + igraph_vector_destroy(&indegrees); IGRAPH_FINALLY_CLEAN(7); /* Tidy up the ordering */ @@ -381,37 +375,39 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec } /* Find the feedback edges based on the ordering */ - if (result) { - igraph_vector_int_clear(result); - for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i), to = IGRAPH_TO(graph, i); + 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_int_push_back(result, i)); + IGRAPH_CHECK(igraph_vector_push_back(result, i)); } } } /* If we have also requested a layering, return that as well */ - if (layers) { - igraph_vector_int_t ranks; - igraph_vector_int_t order_vec; + if (layers != 0) { + igraph_vector_t ranks; + igraph_vector_long_t order_vec; - IGRAPH_CHECK(igraph_vector_int_resize(layers, no_of_nodes)); - igraph_vector_int_null(layers); + IGRAPH_CHECK(igraph_vector_resize(layers, no_of_nodes)); + igraph_vector_null(layers); - igraph_vector_int_view(&order_vec, ordering, no_of_nodes); + igraph_vector_long_view(&order_vec, ordering, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ranks, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ranks, 0); - IGRAPH_CHECK(igraph_vector_int_qsort_ind(&order_vec, &ranks, IGRAPH_ASCENDING)); + IGRAPH_CHECK((int) igraph_vector_long_qsort_ind(&order_vec, &ranks, 0)); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t from = VECTOR(ranks)[i]; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); - k = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t to = VECTOR(neis)[j]; + long int to = (long int) VECTOR(neis)[j]; if (from == to) { continue; } @@ -424,13 +420,13 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec } } - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&ranks); + igraph_vector_destroy(&neis); + igraph_vector_destroy(&ranks); IGRAPH_FINALLY_CLEAN(2); } /* Free the ordering vector */ - IGRAPH_FREE(ordering); + igraph_free(ordering); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -439,44 +435,68 @@ igraph_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vec /** * Solves the feedback arc set problem using integer programming. */ -igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_int_t *result, +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); + 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_int_t membership, *vec; - igraph_vector_int_t ordering, vertex_remapping; - igraph_vector_int_list_t vertices_by_components, edges_by_components; - igraph_integer_t i, j, k, l, m, n, from, to, no_of_rows, n_choose_2; + 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_INT_INIT_FINALLY(&membership, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ordering, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_remapping, no_of_vertices); + IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INIT_FINALLY(&ordering, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_remapping, no_of_vertices); - igraph_vector_int_clear(result); + igraph_vector_clear(result); /* Decompose the graph into connected components */ - IGRAPH_CHECK(igraph_connected_components(graph, &membership, 0, &no_of_components, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, + IGRAPH_WEAK)); /* Construct vertex and edge lists for each of the components */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vertices_by_components, no_of_components); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edges_by_components, no_of_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 = VECTOR(membership)[i]; - vec = igraph_vector_int_list_get_ptr(&vertices_by_components, j); - IGRAPH_CHECK(igraph_vector_int_push_back(vec, 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 = VECTOR(membership)[IGRAPH_FROM(graph, i)]; - vec = igraph_vector_int_list_get_ptr(&edges_by_components, j); - IGRAPH_CHECK(igraph_vector_int_push_back(vec, 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) @@ -493,8 +513,8 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector /* Solve an IP for feedback arc sets in each of the components */ for (i = 0; i < no_of_components; i++) { - igraph_vector_int_t *vertices_in_comp = igraph_vector_int_list_get_ptr(&vertices_by_components, i); - igraph_vector_int_t *edges_in_comp = igraph_vector_int_list_get_ptr(&edges_by_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). @@ -520,35 +540,31 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector * * max sum_{i INT_MAX) { - IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); - } - - if (n_choose_2 > 0) { - glp_add_cols(ip, (int) n_choose_2); - for (j = 1; j <= n_choose_2; j++) { + k = n * (n - 1) / 2; + if (k > 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_int_size(edges_in_comp); + k = igraph_vector_size(edges_in_comp); for (j = 0; j < k; j++) { - l = VECTOR(*edges_in_comp)[j]; - from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; - to = VECTOR(vertex_remapping)[IGRAPH_TO(graph, l)]; + 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; } @@ -566,27 +582,7 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector /* Add constraints */ if (n > 1) { - { - /* Overflow-safe block for: - * no_of_rows = n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3 - */ - - /* res = n * (n - 1) * (n - 2) / 3 */ - igraph_integer_t mod = n % 3; - igraph_integer_t res = n / 3; /* same as (n - mod) / 3 */ - - mod = (mod + 1) % 3; - IGRAPH_SAFE_MULT(res, n - mod, &res); - mod = (mod + 1) % 3; - IGRAPH_SAFE_MULT(res, n - mod, &res); - - /* no_of_rows = n * (n - 1) / 2 + res */ - IGRAPH_SAFE_ADD(n_choose_2, res, &no_of_rows); - } - if (no_of_rows > INT_MAX) { - IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); - } - glp_add_rows(ip, (int) no_of_rows); + 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]; @@ -617,10 +613,11 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector IGRAPH_GLPK_CHECK(glp_intopt(ip, &parm), "Feedback arc set using IP failed"); /* Find the ordering of the vertices */ - IGRAPH_CHECK(igraph_vector_int_resize(&ordering, n)); - igraph_vector_int_null(&ordering); + IGRAPH_CHECK(igraph_vector_resize(&ordering, n)); + igraph_vector_null(&ordering); + m = n * (n - 1) / 2; j = 0; k = 1; - for (l = 1; l <= n_choose_2; l++) { + 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) { @@ -637,13 +634,13 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector } /* Find the feedback edges */ - k = igraph_vector_int_size(edges_in_comp); + k = igraph_vector_size(edges_in_comp); for (j = 0; j < k; j++) { - l = VECTOR(*edges_in_comp)[j]; - from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; - to = VECTOR(vertex_remapping)[IGRAPH_TO(graph, l)]; + 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_int_push_back(result, l)); + IGRAPH_CHECK(igraph_vector_push_back(result, l)); } } @@ -652,11 +649,11 @@ igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_list_destroy(&vertices_by_components); - igraph_vector_int_list_destroy(&edges_by_components); - igraph_vector_int_destroy(&vertex_remapping); - igraph_vector_int_destroy(&ordering); - igraph_vector_int_destroy(&membership); + 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; diff --git a/src/vendor/cigraph/src/misc/feedback_arc_set.h b/src/vendor/cigraph/src/misc/feedback_arc_set.h index 7d34eb0b60c..7091e1f3ab9 100644 --- a/src/vendor/cigraph/src/misc/feedback_arc_set.h +++ b/src/vendor/cigraph/src/misc/feedback_arc_set.h @@ -29,17 +29,10 @@ __BEGIN_DECLS -igraph_error_t igraph_i_feedback_arc_set_eades( - const igraph_t *graph, igraph_vector_int_t *result, - const igraph_vector_t *weights, igraph_vector_int_t *layering -); -igraph_error_t igraph_i_feedback_arc_set_ip( - const igraph_t *graph, igraph_vector_int_t *result, - const igraph_vector_t *weights); -igraph_error_t igraph_i_feedback_arc_set_undirected( - const igraph_t *graph, igraph_vector_int_t *result, - const igraph_vector_t *weights, igraph_vector_int_t *layering -); +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 diff --git a/src/vendor/cigraph/src/misc/graphicality.c b/src/vendor/cigraph/src/misc/graphicality.c index 24d092de2e4..f468d348008 100644 --- a/src/vendor/cigraph/src/misc/graphicality.c +++ b/src/vendor/cigraph/src/misc/graphicality.c @@ -25,18 +25,18 @@ #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 igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res); +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 igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); -static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_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 igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res); -static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, 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); /** @@ -113,8 +113,8 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * * 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). */ -igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, - const igraph_vector_int_t *in_degrees, +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) { @@ -147,10 +147,6 @@ igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, /* Directed case: */ else { - if (igraph_vector_int_size(in_degrees) != igraph_vector_int_size(out_degrees)) { - IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); - } - 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); } @@ -220,8 +216,8 @@ igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, * Time complexity: O(n log n) for simple graphs, O(n) for multigraphs, * where n is the length of the larger degree sequence. */ -igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, - const igraph_vector_int_t *degrees2, +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) { @@ -242,16 +238,16 @@ igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, * * These conditions are valid regardless of whether multi-edges are allowed between distinct vertices. */ -static igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res) { - igraph_integer_t sum_parity = 0; /* 0 if the degree sum is even, 1 if it is odd */ - igraph_integer_t n = igraph_vector_int_size(degrees); - igraph_integer_t i; +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) { - igraph_integer_t d = VECTOR(*degrees)[i]; + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; if (d < 0) { - *res = false; + *res = 0; return IGRAPH_SUCCESS; } sum_parity = (sum_parity + d) & 1; @@ -268,23 +264,23 @@ static igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_ * - The sum of degrees must be even. * - The sum of degrees must be no smaller than 2*d_max. */ -static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res) { - igraph_integer_t i; - igraph_integer_t n = igraph_vector_int_size(degrees); - igraph_integer_t dsum, dmax; +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 = true; + *res = 1; return IGRAPH_SUCCESS; } dsum = 0; dmax = 0; - for (i = 0; i < n; ++i) { - igraph_integer_t d = VECTOR(*degrees)[i]; + for (i=0; i < n; ++i) { + long int d = VECTOR(*degrees)[i]; if (d < 0) { - *res = false; + *res = 0; return IGRAPH_SUCCESS; } dsum += d; @@ -304,15 +300,15 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igra * - The sum of degrees must be even. * - Use the modification of the Erdős-Gallai theorem due to Cairns and Mendan. */ -static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res) { - igraph_vector_int_t work; - igraph_integer_t w, b, s, c, n, k; +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_int_size(degrees); + n = igraph_vector_size(degrees); /* Zero-length sequences are considered graphical. */ if (n == 0) { - *res = true; + *res = 1; return IGRAPH_SUCCESS; } @@ -346,12 +342,12 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph * w and k are zero-based here, unlike in the statement of the theorem above. */ - IGRAPH_CHECK(igraph_vector_int_init_copy(&work, degrees)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &work); + IGRAPH_CHECK(igraph_vector_copy(&work, degrees)); + IGRAPH_FINALLY(igraph_vector_destroy, &work); - igraph_vector_int_reverse_sort(&work); + igraph_vector_reverse_sort(&work); - *res = true; + *res = 1; w = n - 1; b = 0; s = 0; c = 0; for (k = 0; k < n; k++) { b += VECTOR(work)[k]; @@ -362,7 +358,7 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph w--; } if (b > c + s + 2*(k + 1)) { - *res = false; + *res = 0; break; } if (w == k) { @@ -370,7 +366,7 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph } } - igraph_vector_int_destroy(&work); + igraph_vector_destroy(&work); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -382,17 +378,17 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph * - The sum of degrees must be even. * - Use the Erdős-Gallai theorem. */ -static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res) { +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 igraph_integer_t p = igraph_vector_int_size(degrees); - igraph_integer_t dmin, dmax, dsum; - igraph_integer_t n; /* number of non-zero degrees */ - igraph_integer_t k, sum_deg, sum_ni, sum_ini; - igraph_integer_t i, dk; - igraph_integer_t zverovich_bound; + 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 = true; + *res = 1; return IGRAPH_SUCCESS; } @@ -409,11 +405,11 @@ static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vecto IGRAPH_VECTOR_INT_INIT_FINALLY(&num_degs, p); dmin = p; dmax = 0; dsum = 0; n = 0; - for (i = 0; i < p; ++i) { - igraph_integer_t d = VECTOR(*degrees)[i]; + for (i=0; i < p; ++i) { + long int d = VECTOR(*degrees)[i]; if (d < 0 || d >= p) { - *res = false; + *res = 0; goto finish; } @@ -427,12 +423,12 @@ static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vecto } if (dsum % 2 != 0) { - *res = false; + *res = 0; goto finish; } if (n == 0) { - *res = true; + *res = 1; goto finish; /* all degrees are zero => graphical */ } @@ -460,16 +456,16 @@ static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vecto } if (dmin*n >= zverovich_bound) { - *res = true; + *res = 1; goto finish; } k = 0; sum_deg = 0; sum_ni = 0; sum_ini = 0; for (dk = dmax; dk >= dmin; --dk) { - igraph_integer_t run_size, v; + long int run_size, v; if (dk < k+1) { - *res = true; + *res = 1; goto finish; } @@ -485,13 +481,13 @@ static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vecto } k += run_size; if (sum_deg > k*(n-1) - k*sum_ni + sum_ini) { - *res = false; + *res = 0; goto finish; } } } - *res = true; + *res = 1; finish: igraph_vector_int_destroy(&num_degs); @@ -507,20 +503,22 @@ static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vecto * - Degrees must be non-negative. * - The sum of in- and out-degrees must be the same. */ -static igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { - igraph_integer_t sumdiff; /* difference between sum of in- and out-degrees */ - igraph_integer_t n = igraph_vector_int_size(out_degrees); - igraph_integer_t i; +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; - IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); + 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) { - igraph_integer_t dout = VECTOR(*out_degrees)[i]; - igraph_integer_t din = VECTOR(*in_degrees)[i]; + 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 = false; + *res = 0; return IGRAPH_SUCCESS; } @@ -539,21 +537,23 @@ static igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_ve * - The sum of out-degrees must be no smaller than d_max, * where d_max is the largest total degree. */ -static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { - igraph_integer_t i, sumin, sumout, dmax; - igraph_integer_t n = igraph_vector_int_size(out_degrees); +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); - IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); + 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) { - igraph_integer_t dout = VECTOR(*out_degrees)[i]; - igraph_integer_t din = VECTOR(*in_degrees)[i]; - igraph_integer_t d = dout + din; + 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 = false; + *res = 0; return IGRAPH_SUCCESS; } @@ -574,7 +574,13 @@ static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph * - Degrees must be non-negative. * - Equivalent to bipartite simple graph. */ -static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_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) { + 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); } @@ -586,15 +592,15 @@ static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_v */ typedef struct { - const igraph_vector_int_t* first; - const igraph_vector_int_t* second; + 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; - igraph_integer_t index1 = *((igraph_integer_t*)p1); - igraph_integer_t index2 = *((igraph_integer_t*)p2); + 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; } @@ -610,9 +616,9 @@ static int igraph_i_qsort_dual_vector_cmp_desc(void* data, const void *p1, const return 0; } -static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { - igraph_vector_int_t index_array; - igraph_integer_t i, j, vcount, lhs, rhs; +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. */ @@ -621,22 +627,22 @@ static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_ return IGRAPH_SUCCESS; } - vcount = igraph_vector_int_size(out_degrees); + vcount = igraph_vector_size(out_degrees); if (vcount == 0) { - *res = true; + *res = 1; return IGRAPH_SUCCESS; } /* Create an index vector that sorts the vertices by decreasing in-degree */ - IGRAPH_CHECK(igraph_vector_int_init_range(&index_array, 0, vcount)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &index_array); + 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(VECTOR(index_array)[0]), &sort_data, + 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 @@ -651,7 +657,7 @@ static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_ #define INDEGREE(x) (VECTOR(*in_degrees)[VECTOR(index_array)[x]]) #define OUTDEGREE(x) (VECTOR(*out_degrees)[VECTOR(index_array)[x]]) - *res = true; + *res = 1; lhs = 0; for (i = 0; i < vcount; i++) { lhs += INDEGREE(i); @@ -672,7 +678,7 @@ static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_ } if (lhs > rhs) { - *res = false; + *res = 0; break; } } @@ -680,7 +686,7 @@ static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_ #undef INDEGREE #undef OUTDEGREE - igraph_vector_int_destroy(&index_array); + igraph_vector_long_destroy(&index_array); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -694,17 +700,17 @@ static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_ * - Degrees must be non-negative. * - Sum of degrees must be the same in the two partitions. */ -static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { - igraph_integer_t i; - igraph_integer_t sum1, sum2; - igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); +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) { - igraph_integer_t d = VECTOR(*degrees1)[i]; + for (i=0; i < n1; ++i) { + long int d = VECTOR(*degrees1)[i]; if (d < 0) { - *res = false; + *res = 0; return IGRAPH_SUCCESS; } @@ -712,11 +718,11 @@ static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *d } sum2 = 0; - for (i = 0; i < n2; ++i) { - igraph_integer_t d = VECTOR(*degrees2)[i]; + for (i=0; i < n2; ++i) { + long int d = VECTOR(*degrees2)[i]; if (d < 0) { - *res = false; + *res = 0; return IGRAPH_SUCCESS; } @@ -734,14 +740,14 @@ static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *d * - Sum of degrees must be the same in the two partitions. * - Use the Gale-Ryser theorem. */ -static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { - igraph_vector_int_t sorted_deg1, sorted_deg2; - igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); - igraph_integer_t i, k; - igraph_integer_t lhs_sum, partial_rhs_sum; +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 = true; + *res = 1; return IGRAPH_SUCCESS; } @@ -753,8 +759,8 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * /* Ensure that degrees1 is the shorter vector as a minor optimization: */ if (n2 < n1) { - const igraph_vector_int_t *tmp; - igraph_integer_t n; + const igraph_vector_t *tmp; + long int n; tmp = degrees1; degrees1 = degrees2; @@ -767,13 +773,13 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * /* Copy and sort both vectors: */ - IGRAPH_CHECK(igraph_vector_int_init_copy(&sorted_deg1, degrees1)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &sorted_deg1); - igraph_vector_int_reverse_sort(&sorted_deg1); /* decreasing sort */ + 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_int_init_copy(&sorted_deg2, degrees2)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &sorted_deg2); - igraph_vector_int_sort(&sorted_deg2); /* increasing 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: @@ -797,11 +803,11 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * * of the inequality's right-hand-side. */ - *res = true; /* be optimistic */ + *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) { + 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 @@ -817,14 +823,137 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * /* 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 = false; + *res = 0; break; } } - igraph_vector_int_destroy(&sorted_deg2); - igraph_vector_int_destroy(&sorted_deg1); + 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/vendor/cigraph/src/misc/matching.c b/src/vendor/cigraph/src/misc/matching.c index e0555cbd7ee..7e58bd01e80 100644 --- a/src/vendor/cigraph/src/misc/matching.c +++ b/src/vendor/cigraph/src/misc/matching.c @@ -83,15 +83,15 @@ static void debug(const char* fmt, ...) { * * \example examples/simple/igraph_maximum_bipartite_matching.c */ -igraph_error_t igraph_is_matching(const igraph_t *graph, - const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, - igraph_bool_t *result) { - igraph_integer_t i, j, no_of_nodes = igraph_vcount(graph); +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_int_size(matching) != no_of_nodes) { - *result = false; return IGRAPH_SUCCESS; + if (igraph_vector_long_size(matching) != no_of_nodes) { + *result = 0; return IGRAPH_SUCCESS; } for (i = 0; i < no_of_nodes; i++) { @@ -99,7 +99,7 @@ igraph_error_t igraph_is_matching(const igraph_t *graph, /* Checking range of each element in the match vector */ if (j < -1 || j >= no_of_nodes) { - *result = false; return IGRAPH_SUCCESS; + *result = 0; return IGRAPH_SUCCESS; } /* When i is unmatched, we're done */ if (j == -1) { @@ -107,17 +107,17 @@ igraph_error_t igraph_is_matching(const igraph_t *graph, } /* Matches must be mutual */ if (VECTOR(*matching)[j] != i) { - *result = false; return IGRAPH_SUCCESS; + *result = 0; return IGRAPH_SUCCESS; } /* Matched vertices must be connected */ - IGRAPH_CHECK(igraph_are_connected(graph, i, - j, &conn)); + 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, j, - i, &conn)); + IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) j, + (igraph_integer_t) i, &conn)); if (!conn) { - *result = false; return IGRAPH_SUCCESS; + *result = 0; return IGRAPH_SUCCESS; } } } @@ -130,12 +130,12 @@ igraph_error_t igraph_is_matching(const igraph_t *graph, continue; } if (VECTOR(*types)[i] == VECTOR(*types)[j]) { - *result = false; return IGRAPH_SUCCESS; + *result = 0; return IGRAPH_SUCCESS; } } } - *result = true; + *result = 1; return IGRAPH_SUCCESS; } @@ -165,20 +165,19 @@ igraph_error_t igraph_is_matching(const igraph_t *graph, * * \example examples/simple/igraph_maximum_bipartite_matching.c */ -igraph_error_t igraph_is_maximal_matching(const igraph_t *graph, - const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, - igraph_bool_t *result) { - - igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t neis; +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 = false; return IGRAPH_SUCCESS; + *result = 0; return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); valid = 1; for (i = 0; i < no_of_nodes; i++) { @@ -187,36 +186,35 @@ igraph_error_t igraph_is_maximal_matching(const igraph_t *graph, continue; } - IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_ALL)); - n = igraph_vector_int_size(&neis); + n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { - if (VECTOR(*matching)[VECTOR(neis)[j]] == -1) { + if (VECTOR(*matching)[(long int)VECTOR(neis)[j]] == -1) { if (types == 0 || - VECTOR(*types)[i] != VECTOR(*types)[VECTOR(neis)[j]]) { + VECTOR(*types)[i] != VECTOR(*types)[(long int)VECTOR(neis)[j]]) { valid = 0; break; } } } } - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); *result = valid; return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( - const igraph_t *graph, - const igraph_vector_bool_t *types, igraph_integer_t *matching_size, - igraph_vector_int_t *matching); - -static igraph_error_t 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_int_t *matching, - const igraph_vector_t *weights, igraph_real_t eps); +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)) @@ -282,10 +280,10 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( * * \example examples/simple/igraph_maximum_bipartite_matching.c */ -igraph_error_t 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_int_t *matching, - const igraph_vector_t *weights, igraph_real_t eps) { +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)) { @@ -309,10 +307,10 @@ igraph_error_t igraph_maximum_bipartite_matching(const igraph_t *graph, } } -static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( +static int igraph_i_maximum_bipartite_matching_unweighted_relabel( const igraph_t* graph, - const igraph_vector_bool_t* types, igraph_vector_int_t* labels, - igraph_vector_int_t* matching, igraph_bool_t smaller_set); + 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 @@ -327,19 +325,19 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( * Avancée en Calcul Scientifique). * http://www.cerfacs.fr/algor/reports/2011/TR_PA_11_33.pdf */ -static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( - const igraph_t *graph, - const igraph_vector_bool_t *types, igraph_integer_t *matching_size, - igraph_vector_int_t *matching) { - igraph_integer_t i, j, k, n, no_of_nodes = igraph_vcount(graph); - igraph_integer_t num_matched; /* number of matched vertex pairs */ - igraph_vector_int_t match; /* will store the matching */ - igraph_vector_int_t labels; /* will store the labels */ - igraph_vector_int_t neis; /* used to retrieve the neighbors of a node */ - igraph_dqueue_int_t q; /* a FIFO for push ordering */ +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 */ - igraph_integer_t label_changed = 0; /* Counter to decide when to run a global relabeling */ - igraph_integer_t relabeling_freq = no_of_nodes / 2; + 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 @@ -348,15 +346,15 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( */ /* (1) Initialize data structures */ - IGRAPH_CHECK(igraph_vector_int_init(&match, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &match); - IGRAPH_VECTOR_INT_INIT_FINALLY(&labels, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + 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_int_fill(&match, -1); + 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. */ @@ -368,11 +366,11 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( if (MATCHED(i)) { continue; } - IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_ALL)); - n = igraph_vector_int_size(&neis); + n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { - k = VECTOR(neis)[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); } @@ -394,16 +392,16 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( /* (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_int_push(&q, i)); + 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_int_empty(&q)) { - igraph_integer_t v = igraph_dqueue_int_pop(&q); /* Line 13 */ - igraph_integer_t u = -1, label_u = 2 * no_of_nodes; - igraph_integer_t w; + 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 */ @@ -415,13 +413,13 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( 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, v, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); - n = igraph_vector_int_size(&neis); + n = igraph_vector_size(&neis); for (i = 0; i < n; i++) { - if (VECTOR(labels)[VECTOR(neis)[i]] < label_u) { - u = VECTOR(neis)[i]; - label_u = VECTOR(labels)[u]; + 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++; } } @@ -435,7 +433,7 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( 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_int_push(&q, w)); /* Line 10 */ + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); /* Line 10 */ debug(" Unmatching & activating vertex %ld\n", w); num_matched--; } @@ -449,73 +447,73 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( /* Fill the output parameters */ if (matching != 0) { - IGRAPH_CHECK(igraph_vector_int_update(matching, &match)); + IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); } if (matching_size != 0) { - *matching_size = num_matched; + *matching_size = (igraph_integer_t) num_matched; } /* Release everything */ - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(&labels); - igraph_vector_int_destroy(&match); + 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 igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( - const igraph_t *graph, - const igraph_vector_bool_t *types, igraph_vector_int_t *labels, - igraph_vector_int_t *match, igraph_bool_t smaller_set) { - igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph), matched_to; - igraph_dqueue_int_t q; - igraph_vector_int_t neis; +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_int_fill(labels, no_of_nodes); + igraph_vector_fill(labels, no_of_nodes); /* Allocate vector for neighbors */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_int_init(&q, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + 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_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); VECTOR(*labels)[i] = 0; } } /* Run the BFS */ - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t v = igraph_dqueue_int_pop(&q); - igraph_integer_t w; + while (!igraph_dqueue_long_empty(&q)) { + long int v = igraph_dqueue_long_pop(&q); + long int w; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, IGRAPH_ALL)); - n = igraph_vector_int_size(&neis); + n = igraph_vector_size(&neis); for (j = 0; j < n; j++) { - w = VECTOR(neis)[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_int_push(&q, matched_to)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, matched_to)); VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1; } } } } - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&neis); + igraph_dqueue_long_destroy(&q); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -535,25 +533,25 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( * 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 igraph_error_t 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_int_t *matching, - const igraph_vector_t *weights, igraph_real_t eps) { - igraph_integer_t i, j, k, n, no_of_nodes, no_of_edges; +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_int_t match; /* will store the matching */ + igraph_vector_long_t match; /* will store the matching */ igraph_vector_t slack; /* will store the slack on each edge */ - igraph_vector_int_t parent; /* parent vertices during a BFS */ - igraph_vector_int_t vec1, vec2; /* general temporary vectors */ + 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_int_t q; /* a FIFO for BST */ + 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 */ - igraph_integer_t smaller_set_size; /* size of the smaller set */ - igraph_integer_t larger_set_size; /* size 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 */ @@ -576,18 +574,18 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( } /* (1) Initialize data structures */ - IGRAPH_CHECK(igraph_vector_int_init(&match, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &match); + 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_INT_INIT_FINALLY(&vec1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vec2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vec2, 0); IGRAPH_VECTOR_INIT_FINALLY(&labels, no_of_nodes); - IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, 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, - no_of_nodes)); + (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); @@ -629,7 +627,7 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( neis = igraph_inclist_get(&inclist, i); n = igraph_vector_int_size(neis); for (j = 0, k = 0; j < n; j++) { - k = VECTOR(*neis)[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); @@ -643,24 +641,24 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( dual += max_weight; } - igraph_vector_int_clear(&vec1); + 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 = VECTOR(vec2)[j]; - v = VECTOR(vec2)[j + 1]; + 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_int_push_back(&vec1, u)); - IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, v)); + IGRAPH_CHECK(igraph_vector_push_back(&vec1, u)); + IGRAPH_CHECK(igraph_vector_push_back(&vec1, v)); } } - igraph_vector_int_clear(&vec2); + 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, - no_of_nodes, 0)); + (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); @@ -676,21 +674,21 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( IGRAPH_UNUSED(min_slack_u); /* (7) Fill the push queue with the unmatched nodes from the smaller set. */ - igraph_vector_int_clear(&vec1); - igraph_vector_int_clear(&vec2); - igraph_vector_int_fill(&parent, -1); + 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_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, i)); VECTOR(parent)[i] = i; - IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, i)); + IGRAPH_CHECK(igraph_vector_push_back(&vec1, i)); } } #ifdef MATCHING_DEBUG debug("Matching:"); - igraph_vector_int_print(&match); + igraph_vector_long_print(&match); debug("Unmatched vertices are marked by non-negative numbers:\n"); igraph_vector_print(&parent); debug("Labeling:"); @@ -701,10 +699,10 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( /* (8) Run the BFS */ alternating_path_endpoint = -1; - while (!igraph_dqueue_int_empty(&q)) { - v = igraph_dqueue_int_pop(&q); + while (!igraph_dqueue_long_empty(&q)) { + v = (int) igraph_dqueue_long_pop(&q); - debug("Considering vertex %ld\n", v); + 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 @@ -719,7 +717,7 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( neis = igraph_inclist_get(&inclist, v); n = igraph_vector_int_size(neis); for (i = 0; i < n; i++) { - j = VECTOR(*neis)[i]; + j = (long int) VECTOR(*neis)[i]; /* We only care about tight edges */ if (!IS_TIGHT(j)) { continue; @@ -729,10 +727,10 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( if (VECTOR(parent)[u] >= 0) { continue; } - debug(" Reached vertex %" IGRAPH_PRId " via edge %" IGRAPH_PRId "\n", u, j); + debug(" Reached vertex %ld via edge %ld\n", (long)u, (long)j); VECTOR(parent)[u] = v; - IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); - w = VECTOR(match)[u]; + 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 @@ -741,17 +739,17 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, w)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } - IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, w)); + 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 = VECTOR(*neis2)[i]; + u = (igraph_integer_t) VECTOR(*neis2)[i]; /* Have we seen u already? */ if (VECTOR(parent)[u] >= 0) { continue; @@ -760,13 +758,13 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( * 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)[v] + VECTOR(labels)[u]) > eps) { + if (fabs(VECTOR(labels)[(long int)v] + VECTOR(labels)[(long int)u]) > eps) { continue; } - debug(" Reached vertex %" IGRAPH_PRId " via tight phantom edge\n", u); + debug(" Reached vertex %ld via tight phantom edge\n", (long)u); VECTOR(parent)[u] = v; - IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); - w = VECTOR(match)[u]; + 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 @@ -775,10 +773,10 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( alternating_path_endpoint = u; break; /* since we don't need any more endpoints that come from v */ } else { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, w)); + IGRAPH_CHECK(igraph_dqueue_long_push(&q, w)); VECTOR(parent)[w] = u; } - IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, w)); + IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); } } @@ -790,40 +788,40 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( #endif /* Increase the size of the matching with the alternating path. */ v = alternating_path_endpoint; - u = VECTOR(parent)[v]; - debug("Extending matching with alternating path ending in %ld.\n", v); + u = (igraph_integer_t) VECTOR(parent)[v]; + debug("Extending matching with alternating path ending in %ld.\n", (long int)v); while (u != v) { - w = VECTOR(match)[v]; + w = (int) VECTOR(match)[v]; if (w != -1) { VECTOR(match)[w] = -1; } VECTOR(match)[v] = u; VECTOR(match)[v] = u; - w = VECTOR(match)[u]; + w = (int) VECTOR(match)[u]; if (w != -1) { VECTOR(match)[w] = -1; } VECTOR(match)[u] = v; - v = VECTOR(parent)[u]; - u = VECTOR(parent)[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_int_print(&match); - debug("Matching size is now: %" IGRAPH_PRId "\n", msize); + 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_int_print(&vec1); + igraph_vector_print(&vec1); igraph_vector_print(&vec2); #endif @@ -842,17 +840,17 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( * based on this. */ min_slack = IGRAPH_INFINITY; min_slack_u = min_slack_v = 0; - n = igraph_vector_int_size(&vec1); + 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 = i; + min_slack_v = (igraph_integer_t) i; } } min_slack_2 = IGRAPH_INFINITY; for (i = 0; i < n; i++) { - u = VECTOR(vec1)[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) { @@ -865,31 +863,31 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( } min_slack += min_slack_2; debug("Starting approximation for min_slack = %.4f (based on vertex pair %ld--%ld)\n", - min_slack, min_slack_u, min_slack_v); + min_slack, (long int)min_slack_u, (long int)min_slack_v); - n = igraph_vector_int_size(&vec1); + n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { - u = VECTOR(vec1)[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 %" IGRAPH_PRId "\n", u); + 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 %" IGRAPH_PRId " -- %" IGRAPH_PRId " (ID=%" IGRAPH_PRId ")\n", u, v, VECTOR(*neis)[j]); + 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(" %" IGRAPH_PRId " was reached, so we are not interested in it\n", v); + 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 = VECTOR(*neis)[j]; + v = (igraph_integer_t) VECTOR(*neis)[j]; if (VECTOR(slack)[v] < min_slack) { min_slack = VECTOR(slack)[v]; min_slack_u = u; @@ -899,39 +897,39 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( VECTOR(slack)[v], min_slack); } } - debug("Minimum slack: %.4f on edge %" IGRAPH_PRId "--%" IGRAPH_PRId "\n", min_slack, min_slack_u, min_slack_v); + 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_int_size(&vec1); + n = igraph_vector_size(&vec1); for (i = 0; i < n; i++) { - u = VECTOR(vec1)[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 %" IGRAPH_PRId " (%" IGRAPH_PRId "--%" IGRAPH_PRId ") by %.4f\n", - VECTOR(*neis)[j], u, - IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); - VECTOR(slack)[VECTOR(*neis)[j]] -= min_slack; + 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_int_size(&vec2); + n = igraph_vector_size(&vec2); for (i = 0; i < n; i++) { - u = VECTOR(vec2)[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 %" IGRAPH_PRId " (%" IGRAPH_PRId"--%" IGRAPH_PRId ") by %.4f\n", - VECTOR(*neis)[j], u, - IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); - VECTOR(slack)[VECTOR(*neis)[j]] += min_slack; + 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; } @@ -947,13 +945,13 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( u = VECTOR(smaller_set)[i]; for (j = 0; j < larger_set_size; j++) { v = VECTOR(larger_set)[j]; - if (VECTOR(labels)[u] + VECTOR(labels)[v] <= eps) { + 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: %" IGRAPH_PRId " -- %" IGRAPH_PRId "\n", u, v); + debug("New tight phantom edge: %ld -- %ld\n", (long)u, (long)v); IGRAPH_CHECK(igraph_vector_int_insert(neis2, k, v)); } } @@ -983,7 +981,7 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( /* Fill the output parameters */ if (matching != 0) { - IGRAPH_CHECK(igraph_vector_int_update(matching, &match)); + IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); } if (matching_size != 0) { *matching_size = msize; @@ -992,7 +990,7 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( *matching_weight = 0; for (i = 0; i < no_of_edges; i++) { if (IS_TIGHT(i)) { - IGRAPH_CHECK(igraph_edge(graph, i, &u, &v)); + IGRAPH_CHECK(igraph_edge(graph, (igraph_integer_t) i, &u, &v)); if (VECTOR(match)[u] == v) { *matching_weight += VECTOR(*weights)[i]; } @@ -1006,21 +1004,21 @@ static igraph_error_t igraph_i_maximum_bipartite_matching_weighted( igraph_vector_destroy(&smaller_set); igraph_inclist_destroy(&inclist); igraph_adjlist_destroy(&tight_phantom_edges); - igraph_vector_int_destroy(&parent); - igraph_dqueue_int_destroy(&q); + igraph_vector_destroy(&parent); + igraph_dqueue_long_destroy(&q); igraph_vector_destroy(&labels); - igraph_vector_int_destroy(&vec1); - igraph_vector_int_destroy(&vec2); + igraph_vector_destroy(&vec1); + igraph_vector_destroy(&vec2); igraph_vector_destroy(&slack); - igraph_vector_int_destroy(&match); + igraph_vector_long_destroy(&match); IGRAPH_FINALLY_CLEAN(11); return IGRAPH_SUCCESS; } -igraph_error_t igraph_maximum_matching(const igraph_t *graph, igraph_integer_t *matching_size, - igraph_real_t *matching_weight, igraph_vector_int_t *matching, - const igraph_vector_t *weights) { +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); diff --git a/src/vendor/cigraph/src/misc/microscopic_update.c b/src/vendor/cigraph/src/misc/microscopic_update.c index 25a0936b072..80fc7c2030f 100644 --- a/src/vendor/cigraph/src/misc/microscopic_update.c +++ b/src/vendor/cigraph/src/misc/microscopic_update.c @@ -94,7 +94,7 @@ * of \p vid. */ -static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t *graph, +static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, @@ -106,7 +106,7 @@ static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t * igraph_real_t C; /* cumulative probability */ igraph_real_t P; /* probability */ igraph_real_t S; /* sum of values */ - igraph_integer_t i; + 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 */ @@ -136,12 +136,12 @@ static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t * IGRAPH_CHECK(igraph_eit_create(graph, es, &A)); IGRAPH_FINALLY(igraph_eit_destroy, &A); while (!IGRAPH_EIT_END(A)) { - e = IGRAPH_EIT_GET(A); - S += VECTOR(*U)[e]; + 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 == 0.0) { + if (S == (igraph_real_t)0.0) { igraph_eit_destroy(&A); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); @@ -159,10 +159,10 @@ static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t * IGRAPH_EIT_RESET(A); IGRAPH_CHECK(igraph_vector_resize(V, IGRAPH_EIT_SIZE(A))); while (!IGRAPH_EIT_END(A)) { - e = IGRAPH_EIT_GET(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 = VECTOR(*U)[e] / S; + P = (igraph_real_t)VECTOR(*U)[e] / S; C += P; VECTOR(*V)[i] = C; i++; @@ -243,7 +243,7 @@ static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t * * perspective of vid. */ -static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t *graph, +static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, @@ -255,7 +255,7 @@ static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t * igraph_real_t S; /* sum of values */ igraph_vit_t A; /* all vertices in v's perspective */ igraph_vs_t vs; - igraph_integer_t i; + 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 */ @@ -289,15 +289,15 @@ static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t * IGRAPH_CHECK(igraph_vit_create(graph, vs, &A)); IGRAPH_FINALLY(igraph_vit_destroy, &A); while (!IGRAPH_VIT_END(A)) { - v = IGRAPH_VIT_GET(A); - S += VECTOR(*U)[v]; + v = (igraph_integer_t)IGRAPH_VIT_GET(A); + S += (igraph_real_t)VECTOR(*U)[v]; IGRAPH_VIT_NEXT(A); } if (islocal) { - S += VECTOR(*U)[vid]; + S += (igraph_real_t)VECTOR(*U)[vid]; } /* avoid division by zero later on */ - if (S == 0.0) { + if (S == (igraph_real_t)0.0) { igraph_vit_destroy(&A); igraph_vs_destroy(&vs); IGRAPH_FINALLY_CLEAN(2); @@ -319,10 +319,10 @@ static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t * IGRAPH_VIT_RESET(A); IGRAPH_CHECK(igraph_vector_resize(V, IGRAPH_VIT_SIZE(A))); while (!IGRAPH_VIT_END(A)) { - v = IGRAPH_VIT_GET(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 = VECTOR(*U)[v] / S; + P = (igraph_real_t)VECTOR(*U)[v] / S; C += P; VECTOR(*V)[i] = C; i++; @@ -417,16 +417,16 @@ static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t * * \endclist */ -static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, +static int igraph_i_microscopic_standard_tests(const igraph_t *graph, igraph_integer_t vid, const igraph_vector_t *quantities, - const igraph_vector_int_t *strategies, + const igraph_vector_t *strategies, igraph_neimode_t mode, igraph_bool_t *updates, igraph_bool_t islocal) { igraph_integer_t nvert; - igraph_vector_int_t degv; + igraph_vector_t degv; *updates = 1; /* sanity checks */ @@ -446,11 +446,11 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, IGRAPH_ERROR("Graph cannot be the empty graph", IGRAPH_EINVAL); } /* invalid vector length */ - if (nvert != igraph_vector_size(quantities)) { + 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_vector_int_size(strategies)) { + if (nvert != (igraph_integer_t)igraph_vector_size(strategies)) { IGRAPH_ERROR("Size of strategies vector different from number of vertices", IGRAPH_EINVAL); } @@ -479,13 +479,13 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, if (islocal) { /* Moving on ahead with vertex isolation test, since local perspective */ /* is requested. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(°v, 1); + 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_int_destroy(°v); + igraph_vector_destroy(°v); IGRAPH_FINALLY_CLEAN(1); } @@ -498,7 +498,7 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, * \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 optimum. Here "local" is with + * 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. @@ -572,15 +572,15 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, * \example examples/simple/igraph_deterministic_optimal_imitation.c */ -igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, +int igraph_deterministic_optimal_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_optimal_t optimality, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_integer_t i, k, v; igraph_real_t q; - igraph_vector_int_t adj; + igraph_vector_t adj; igraph_bool_t updates; IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, @@ -599,33 +599,33 @@ igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, /* 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_INT_INIT_FINALLY(&adj, 0); + IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); - IGRAPH_CHECK(igraph_vector_int_shuffle(&adj)); + IGRAPH_CHECK(igraph_vector_shuffle(&adj)); /* maximum deterministic imitation */ i = vid; - q = VECTOR(*quantities)[vid]; + q = (igraph_real_t)VECTOR(*quantities)[vid]; if (optimality == IGRAPH_MAXIMUM) { - for (k = 0; k < igraph_vector_int_size(&adj); k++) { - v = VECTOR(adj)[k]; - if (VECTOR(*quantities)[v] > q) { + 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 = VECTOR(*quantities)[v]; + q = (igraph_real_t)VECTOR(*quantities)[v]; } } } else { /* minimum deterministic imitation */ - for (k = 0; k < igraph_vector_int_size(&adj); k++) { - v = VECTOR(adj)[k]; - if (VECTOR(*quantities)[v] < q) { + 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 = VECTOR(*quantities)[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_int_destroy(&adj); + igraph_vector_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -725,23 +725,23 @@ igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, * \endclist */ -igraph_error_t igraph_moran_process(const igraph_t *graph, +int igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + 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_int_t deg; + 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; - igraph_integer_t i; + 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, @@ -754,7 +754,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, IGRAPH_ERROR("Weights vector is a null pointer", IGRAPH_EINVAL); } nedge = igraph_ecount(graph); - if (nedge != igraph_vector_size(weights)) { + if (nedge != (igraph_integer_t)igraph_vector_size(weights)) { IGRAPH_ERROR("Size of weights vector different from number of edges", IGRAPH_EINVAL); } @@ -784,9 +784,9 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, r = RNG_UNIF01(); RNG_END(); i = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(°, 1); + IGRAPH_VECTOR_INIT_FINALLY(°, 1); while (!IGRAPH_VIT_END(vA)) { - u = IGRAPH_VIT_GET(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) { @@ -832,7 +832,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, RNG_END(); i = 0; while (!IGRAPH_EIT_END(eA)) { - e = IGRAPH_EIT_GET(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. */ @@ -860,7 +860,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, igraph_eit_destroy(&eA); igraph_es_destroy(&es); - igraph_vector_int_destroy(°); + igraph_vector_destroy(°); igraph_vit_destroy(&vA); igraph_vs_destroy(&vs); igraph_vector_destroy(&V); @@ -965,11 +965,11 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, * \example examples/simple/igraph_roulette_wheel_imitation.c */ -igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, +int igraph_roulette_wheel_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_bool_t islocal, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; @@ -977,7 +977,7 @@ igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, igraph_vector_t V; /* vector of cumulative proportionate quantities */ igraph_vit_t A; /* all vertices in v's perspective */ igraph_vs_t vs; - igraph_integer_t i; + long int i; IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, @@ -1023,7 +1023,7 @@ igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, 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_VIT_GET(A); + u = (igraph_integer_t)IGRAPH_VIT_GET(A); VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; break; } @@ -1129,16 +1129,16 @@ igraph_error_t igraph_roulette_wheel_imitation(const igraph_t *graph, * \example examples/simple/igraph_stochastic_imitation.c */ -igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, +int igraph_stochastic_imitation(const igraph_t *graph, igraph_integer_t vid, igraph_imitate_algorithm_t algo, const igraph_vector_t *quantities, - igraph_vector_int_t *strategies, + igraph_vector_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; - igraph_vector_int_t adj; - igraph_integer_t i; + igraph_vector_t adj; + int i; /* sanity checks */ if (algo != IGRAPH_IMITATE_AUGMENTED && @@ -1155,7 +1155,7 @@ igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, } /* immediate neighbours of v */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + 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. */ @@ -1165,11 +1165,11 @@ igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, /* 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_int_push_back(&adj, vid)); + IGRAPH_CHECK(igraph_vector_push_back(&adj, vid)); RNG_BEGIN(); - i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); - u = VECTOR(adj)[i]; + 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 */ @@ -1179,9 +1179,9 @@ igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, /* retains its current strategy. */ else if (algo == IGRAPH_IMITATE_AUGMENTED) { RNG_BEGIN(); - i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); - u = VECTOR(adj)[i]; + u = (igraph_integer_t) VECTOR(adj)[i]; if (VECTOR(*quantities)[u] > VECTOR(*quantities)[vid]) { VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } @@ -1193,16 +1193,16 @@ igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, /* Otherwise v retains its current strategy. */ else if (algo == IGRAPH_IMITATE_CONTRACTED) { RNG_BEGIN(); - i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); + i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); RNG_END(); - u = VECTOR(adj)[i]; + u = (igraph_integer_t) VECTOR(adj)[i]; if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) { VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } } /* clean up */ - igraph_vector_int_destroy(&adj); + igraph_vector_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/misc/mixing.c b/src/vendor/cigraph/src/misc/mixing.c index d417ba7850b..d6da467d50f 100644 --- a/src/vendor/cigraph/src/misc/mixing.c +++ b/src/vendor/cigraph/src/misc/mixing.c @@ -24,11 +24,10 @@ #include "igraph_mixing.h" #include "igraph_interface.h" -#include "igraph_structural.h" /** * \function igraph_assortativity_nominal - * \brief Assortativity of a graph based on vertex categories. + * 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 @@ -37,82 +36,43 @@ * minus one, if the network is perfectly disassortative. For a * randomly connected network it is (asymptotically) zero. * - * - * The unnormalized version, computed when \p normalized is set to false, - * is identical to the modularity, and is defined as follows for - * directed networks: - * - * 1/m sum_ij (A_ij - k^out_i k^in_j / m) d(i,j), - * - * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, - * k^out and k^in are the out- and in-degrees, - * and d(i,j) is one if vertices \c i and \c j are in the same - * category and zero otherwise. - * - * - * The normalized assortativity coefficient is obtained by dividing the - * previous expression by - * - * 1/m sum_ij (m - k^out_i k^in_j d(i,j) / m). - * - * It can take any value within the interval [-1, 1]. - * - * - * Undirected graphs are effectively treated as directed ones with all-reciprocal - * edges. Thus, self-loops are taken into account twice in undirected graphs. - * - * - * References: - * - * - * M. E. J. Newman: Mixing patterns in networks, - * Phys. Rev. E 67, 026126 (2003) - * https://doi.org/10.1103/PhysRevE.67.026126. - * See section II and equation (2) for the definition of the concept. - * - * - * For an educational overview of assortativity, see - * M. E. J. Newman, - * Networks: An Introduction, Oxford University Press (2010). - * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. + * 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 Integer vector giving the vertex categories. The types - * are represented by integers starting at zero. + * \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. - * \param normalized Boolean, whether to compute the usual normalized - * assortativity. The unnormalized version is identical to - * modularity. Supply true here to compute the standard assortativity. * \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() for computing the assortativity - * based on continuous vertex values instead of discrete categories. - * \ref igraph_modularity() to compute generalized modularity. + * \sa \ref igraph_assortativity if the vertex types are defines by + * numeric values (e.g. vertex degree), instead of categories. * - * \example examples/simple/igraph_assortativity_nominal.c + * \example examples/simple/assortativity.c */ -igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, - const igraph_vector_int_t *types, - igraph_real_t *res, - igraph_bool_t directed, - igraph_bool_t normalized) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_real_t no_of_edges_real = no_of_edges; /* for divisions */ - igraph_integer_t no_of_types; - igraph_vector_int_t ai, bi, eii; +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_int_size(types) != no_of_nodes) { - IGRAPH_ERROR("Invalid types vector length.", IGRAPH_EINVAL); + if (igraph_vector_size(types) != no_of_nodes) { + IGRAPH_ERROR("Invalid `types' vector length", IGRAPH_EINVAL); } if (no_of_nodes == 0) { @@ -121,22 +81,22 @@ igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, } /* 'types' length > 0 here, safe to call vector_min() */ - if (igraph_vector_int_min(types) < 0) { - IGRAPH_ERROR("Vertex types must not be negative.", IGRAPH_EINVAL); + if (igraph_vector_min(types) < 0) { + IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL); } directed = directed && igraph_is_directed(graph); - no_of_types = igraph_vector_int_max(types) + 1; - IGRAPH_VECTOR_INT_INIT_FINALLY(&ai, no_of_types); - IGRAPH_VECTOR_INT_INIT_FINALLY(&bi, no_of_types); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eii, no_of_types); + 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 (igraph_integer_t e = 0; e < no_of_edges; e++) { - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_integer_t from_type = VECTOR(*types)[from]; - igraph_integer_t to_type = VECTOR(*types)[to]; + 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; @@ -152,9 +112,9 @@ igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, } } - for (igraph_integer_t i = 0; i < no_of_types; i++) { - sumaibi += (VECTOR(ai)[i] / no_of_edges_real) * (VECTOR(bi)[i] / no_of_edges_real); - sumeii += (VECTOR(eii)[i] / no_of_edges_real); + 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) { @@ -162,92 +122,46 @@ igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, sumeii /= 2.0; } - if (normalized) { - *res = (sumeii - sumaibi) / (1.0 - sumaibi); - } else { - *res = (sumeii - sumaibi); - } + *res = (sumeii - sumaibi) / (1.0 - sumaibi); - igraph_vector_int_destroy(&eii); - igraph_vector_int_destroy(&bi); - igraph_vector_int_destroy(&ai); + igraph_vector_destroy(&eii); + igraph_vector_destroy(&bi); + igraph_vector_destroy(&ai); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_assortativity - * \brief Assortativity based on numeric properties of vertices. - * - * This function calculates the assortativity coefficient of a - * graph based on given values \c x_i for each vertex \c i. This type of - * assortativity coefficient equals the Pearson correlation of the values - * at the two ends of the edges. - * - * - * The unnormalized covariance of values, computed when \p normalized is - * set to false, is defined as follows in a directed graph: - * - * cov(x_out, x_in) = 1/m sum_ij (A_ij - k^out_i k^in_j / m) x_i x_j, - * - * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, and - * k^out and k^in are the out- and in-degrees. - * \c x_out and \c x_in refer to the sets of vertex values at the start and end of - * the directed edges. + * Assortativity based on numeric properties of vertices * - * - * The normalized covariance, i.e. Pearson correlation, is obtained by dividing - * the previous expression by - * sqrt(var(x_out)) sqrt(var(x_in)), where - * - * var(x_out) = 1/m sum_i k^out_i x_i^2 - (1/m sum_i k^out_i x_i^2)^2 - * - * var(x_in) = 1/m sum_j k^in_j x_j^2 - (1/m sum_j k^in_j x_j^2)^2 + * 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. * - * - * Undirected graphs are effectively treated as directed graphs where all edges - * are reciprocal. Therefore, self-loops are effectively considered twice in - * undirected graphs. - * - * - * References: - * - * - * M. E. J. Newman: Mixing patterns + * See equation (21) in M. E. J. Newman: Mixing patterns * in networks, Phys. Rev. E 67, 026126 (2003) - * https://doi.org/10.1103/PhysRevE.67.026126. - * See section III and equation (21) for the definition, and equation (26) for - * performing the calculation in directed graphs with the degrees as values. - * - * + * (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://doi.org/10.1103/PhysRevLett.89.208701. - * See equation (4) for performing the calculation in undirected - * graphs with the degrees as values. - * - * - * For an educational overview of the concept of assortativity, see - * M. E. J. Newman, - * Networks: An Introduction, Oxford University Press (2010). - * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. + * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs. * * \param graph The input graph, it can be directed or undirected. - * \param values The vertex values, these can be arbitrary numeric + * \param types1 The vertex values, these can be arbitrary numeric * values. - * \param values_in A second value vector to be used for the incoming + * \param types2 A second value vector to be using for the incoming * edges when calculating assortativity for a directed graph. - * Supply \c NULL here if you want to use the same values + * 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 the undirected + * (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. - * \param normalized Boolean, whether to compute the normalized - * covariance, i.e. Pearson correlation. Supply true here to - * compute the standard assortativity. * \return Error code. * * Time complexity: O(|E|), linear in the number of edges of the @@ -256,121 +170,100 @@ igraph_error_t igraph_assortativity_nominal(const igraph_t *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 degrees. + * based on vertex degree. + * + * \example examples/simple/assortativity.c */ -igraph_error_t igraph_assortativity(const igraph_t *graph, - const igraph_vector_t *values, - const igraph_vector_t *values_in, +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, - igraph_bool_t normalized) { + igraph_bool_t directed) { - const igraph_integer_t no_of_nodes = igraph_vcount(graph); - const igraph_integer_t no_of_edges = igraph_ecount(graph); + 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 && values_in) { - IGRAPH_WARNING("Incoming vertex values ignored when calculating undirected assortativity."); + if (!directed && types2) { + IGRAPH_WARNING("Only `types1' is used for undirected case"); } - if (igraph_vector_size(values) != no_of_nodes) { - IGRAPH_ERROR("Invalid vertex values vector length.", IGRAPH_EINVAL); + if (igraph_vector_size(types1) != no_of_nodes) { + IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL); } - if (values_in && igraph_vector_size(values_in) != no_of_nodes) { - IGRAPH_ERROR("Invalid incoming vertex values 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 (igraph_integer_t e = 0; e < no_of_edges; e++) { - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_real_t from_value = VECTOR(*values)[from]; - igraph_real_t to_value = VECTOR(*values)[to]; + 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_value * to_value; - num2 += from_value + to_value; - if (normalized) { - den1 += from_value * from_value + to_value * to_value; - } + num1 += from_type * to_type; + num2 += from_type + to_type; + den1 += from_type * from_type + to_type * to_type; } num1 /= no_of_edges; - if (normalized) { - den1 /= no_of_edges * 2.0; - } - num2 /= no_of_edges * 2.0; + den1 /= no_of_edges * 2; + num2 /= no_of_edges * 2; num2 = num2 * num2; - if (normalized) { - *res = (num1 - num2) / (den1 - num2); - } else { - *res = (num1 - 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 (!values_in) { - values_in = values; + if (!types2) { + types2 = types1; } - for (igraph_integer_t e = 0; e < no_of_edges; e++) { - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_real_t from_value = VECTOR(*values)[from]; - igraph_real_t to_value = VECTOR(*values_in)[to]; - - num1 += from_value * to_value; - num2 += from_value; - num3 += to_value; - if (normalized) { - den1 += from_value * from_value; - den2 += to_value * to_value; - } + 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; - if (normalized) { - den = sqrt(den1 - num2 * num2 / no_of_edges) * - sqrt(den2 - num3 * num3 / no_of_edges); + den = sqrt(den1 - num2 * num2 / no_of_edges) * + sqrt(den2 - num3 * num3 / no_of_edges); - *res = num / den; - } else { - *res = num / no_of_edges; - } + *res = num / den; } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_assortativity_degree - * \brief Assortativity of a graph based on vertex 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. - * This function simply calls \ref igraph_assortativity() with - * the degrees as the vertex values and normalization enabled. - * In the directed case, it uses out-degrees as out-values and - * in-degrees as in-values. - * - * - * For regular graphs, i.e. graphs in which all vertices have the - * same degree, computing degree correlations is not meaningful, - * and this function returns NaN. * * \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 true here to do the natural thing, i.e. use + * 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. @@ -381,36 +274,34 @@ igraph_error_t igraph_assortativity(const igraph_t *graph, * \sa \ref igraph_assortativity() for the general function * calculating assortativity for any kind of numeric vertex values. * - * \example examples/simple/igraph_assortativity_degree.c + * \example examples/simple/assortativity.c */ -igraph_error_t igraph_assortativity_degree(const igraph_t *graph, +int igraph_assortativity_degree(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed) { directed = directed && igraph_is_directed(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - /* This function uses igraph_strength() instead of igraph_degree() in order to obtain - * a vector of reals instead of a vector of integers. */ if (directed) { igraph_vector_t indegree, outdegree; - 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, NULL)); - IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL)); - IGRAPH_CHECK(igraph_assortativity(graph, &outdegree, &indegree, res, /* directed */ true, /* normalized */ true)); + 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); - IGRAPH_FINALLY_CLEAN(2); } else { igraph_vector_t degree; - IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL)); - IGRAPH_CHECK(igraph_assortativity(graph, °ree, 0, res, /* directed */ false, /* normalized */ true)); + 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); - IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/misc/motifs.c b/src/vendor/cigraph/src/misc/motifs.c index 2b0231bed91..5b96f761abe 100644 --- a/src/vendor/cigraph/src/misc/motifs.c +++ b/src/vendor/cigraph/src/misc/motifs.c @@ -32,19 +32,19 @@ #include "core/interruption.h" #include "isomorphism/isoclasses.h" -#include "graph/internal.h" +#include "graph/neighbors.h" /** * Callback function for igraph_motifs_randesu that counts the motifs by * isomorphism class in a histogram. */ -static igraph_error_t igraph_i_motifs_randesu_update_hist( +static igraph_bool_t igraph_i_motifs_randesu_update_hist( const igraph_t *graph, - igraph_vector_int_t *vids, igraph_integer_t isoclass, void* extra) { + 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 IGRAPH_SUCCESS; + return 0; } /** @@ -66,8 +66,8 @@ static igraph_error_t igraph_i_motifs_randesu_update_hist( * * * 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. In this case, a sampling - * method can be used. This function is capable of doing sampling via the + * 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 @@ -105,18 +105,16 @@ static igraph_error_t igraph_i_motifs_randesu_update_hist( * 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; - * \ref igraph_graph_count() to find the number of graph on a given - * number of vertices, i.e. the length of the \p hist vector. + * subgraphs on more than 4 (directed) or 6 (undirected) vertices. * * Time complexity: TODO. * * \example examples/simple/igraph_motifs_randesu.c */ -igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, - igraph_integer_t size, const igraph_vector_t *cut_prob) { +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); - igraph_integer_t histlen; + int histlen; if (directed) { switch (size) { @@ -151,7 +149,7 @@ igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *his } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); } @@ -169,9 +167,10 @@ igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *his } } else if (size == 4) { if (directed) { - const int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27, - 28, 33, 34, 39, 62, 120 }; - size_t i, n = sizeof(not_connected) / sizeof(not_connected[0]); + 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; } @@ -181,17 +180,17 @@ igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *his } } else if (size == 5) { /* undirected only */ - const int not_connected[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 19 }; - size_t i, n = sizeof(not_connected) / sizeof(int); + 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 */ - const 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}; - size_t i, n = sizeof(not_connected) / sizeof(int); + 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; } @@ -235,29 +234,29 @@ igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *his * \example examples/simple/igraph_motifs_randesu.c */ -igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, +int igraph_motifs_randesu_callback(const igraph_t *graph, int size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_adjlist_t allneis, alloutneis; igraph_vector_int_t *neis; - igraph_integer_t father; - igraph_integer_t i, j, s; - igraph_integer_t motifs = 0; + 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_int_t vids; /* this is G */ - igraph_vector_int_t adjverts; /* this is V_E */ - igraph_stack_int_t stack; /* this is S */ - igraph_integer_t *added; + 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 = false; + igraph_bool_t terminate = 0; if (igraph_is_directed(graph)) { switch (size) { @@ -304,19 +303,19 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + 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, igraph_integer_t); + added = IGRAPH_CALLOC(no_of_nodes, long int); if (added == 0) { - IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, subg); @@ -325,69 +324,69 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte IGRAPH_CHECK(igraph_adjlist_init(graph, &alloutneis, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &alloutneis); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); - IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + 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++) { - igraph_integer_t level; + long int level; IGRAPH_ALLOW_INTERRUPTION(); - if (VECTOR(*cut_prob)[0] == 1 || RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + if (VECTOR(*cut_prob)[0] == 1 || + RNG_UNIF01() < VECTOR(*cut_prob)[0]) { continue; } /* init G */ - igraph_vector_int_clear(&vids); level = 0; - IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + 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_int_clear(&adjverts); + igraph_vector_clear(&adjverts); neis = igraph_adjlist_get(&allneis, father); s = igraph_vector_int_size(neis); for (i = 0; i < s; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; + long int nei = (long int) VECTOR(*neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_int_clear(&stack); + igraph_stack_clear(&stack); - while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_int_size(&adjverts) / 2; + s = igraph_vector_size(&adjverts) / 2; for (i = 0; i < s; i++) { - igraph_integer_t k, s2; - igraph_integer_t last; - igraph_error_t ret; + long int k, s2; + long int last; if (cp != 0 && RNG_UNIF01() < cp) { continue; } motifs += 1; - last = VECTOR(adjverts)[2 * i]; - IGRAPH_CHECK(igraph_vector_int_push_back(&vids, last)); + 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++) { - igraph_integer_t from = VECTOR(vids)[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++) { - igraph_integer_t nei = VECTOR(*neis)[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]; @@ -395,17 +394,11 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte } } - IGRAPH_CHECK_CALLBACK( - callback(graph, &vids, arr_code[code], extra), - &ret - ); - - if (ret == IGRAPH_STOP) { - terminate = true; + if (callback(graph, &vids, (int) arr_code[code], extra)) { + terminate = 1; break; } - - igraph_vector_int_pop_back(&vids); + igraph_vector_pop_back(&vids); subg[last] = 0; } } @@ -417,54 +410,54 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte /* can we step down? */ if (level < size - 1 && - !igraph_vector_int_empty(&adjverts)) { + !igraph_vector_empty(&adjverts)) { /* we might step down */ - igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); - igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + 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_int_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); subg[nei] = (char) level + 1; added[nei] += 1; level += 1; - IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + 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++) { - igraph_integer_t nei2 = VECTOR(*neis)[i]; + long int nei2 = (long int) VECTOR(*neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ - igraph_integer_t nei, neifather; - while (!igraph_stack_int_empty(&stack) && - level == igraph_stack_int_top(&stack) - 1) { - igraph_stack_int_pop(&stack); - nei = igraph_stack_int_pop(&stack); - neifather = igraph_stack_int_pop(&stack); - igraph_vector_int_push_back(&adjverts, nei); - igraph_vector_int_push_back(&adjverts, neifather); + 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 = igraph_vector_int_pop_back(&vids); + 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[ VECTOR(*neis)[i] ] -= 1; + added[ (long int) VECTOR(*neis)[i] ] -= 1; } - while (!igraph_vector_int_empty(&adjverts) && - igraph_vector_int_tail(&adjverts) == nei) { - igraph_vector_int_pop_back(&adjverts); - igraph_vector_int_pop_back(&adjverts); + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); } } @@ -481,7 +474,7 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte neis = igraph_adjlist_get(&allneis, father); s = igraph_vector_int_size(neis); for (i = 0; i < s; i++) { - added[ VECTOR(*neis)[i] ] -= 1; + added[ (long int) VECTOR(*neis)[i] ] -= 1; } } /* for father */ @@ -490,11 +483,11 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte IGRAPH_FREE(added); IGRAPH_FREE(subg); - igraph_vector_int_destroy(&vids); - igraph_vector_int_destroy(&adjverts); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&adjverts); igraph_adjlist_destroy(&alloutneis); igraph_adjlist_destroy(&allneis); - igraph_stack_int_destroy(&stack); + igraph_stack_destroy(&stack); IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; @@ -538,7 +531,7 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte * 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 + * 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. @@ -548,34 +541,38 @@ igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_inte * Time complexity: TODO. */ -igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, - igraph_integer_t size, const igraph_vector_t *cut_prob, +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_int_t *parsample) { + const igraph_vector_t *parsample) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t neis; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t neis; - igraph_vector_int_t vids; /* this is G */ - igraph_vector_int_t adjverts; /* this is V_E */ - igraph_stack_int_t stack; /* this is S */ - igraph_integer_t *added; - igraph_vector_int_t *sample; - igraph_integer_t sam; - igraph_integer_t i; + 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, size); + IGRAPH_EINVAL, (igraph_integer_t) size); } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + 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_int_isininterval(parsample, 0, no_of_nodes-1)) { - IGRAPH_ERROR("Sample vertex ID out of range.", IGRAPH_EINVVID); + 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) { @@ -583,29 +580,29 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte return IGRAPH_SUCCESS; } - added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + added = IGRAPH_CALLOC(no_of_nodes, long int); if (added == 0) { - IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); - IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_int_t); + sample = IGRAPH_CALLOC(1, igraph_vector_t); if (sample == NULL) { - IGRAPH_ERROR("Cannot estimate motifs.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot estimate motifs.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, sample); - IGRAPH_VECTOR_INT_INIT_FINALLY(sample, 0); + IGRAPH_VECTOR_INIT_FINALLY(sample, 0); IGRAPH_CHECK(igraph_random_sample(sample, 0, no_of_nodes - 1, sample_size)); } else { - sample = (igraph_vector_int_t*) parsample; - sample_size = igraph_vector_int_size(sample); + sample = (igraph_vector_t*)parsample; + sample_size = (igraph_integer_t) igraph_vector_size(sample); } *est = 0; @@ -613,8 +610,8 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte RNG_BEGIN(); for (sam = 0; sam < sample_size; sam++) { - igraph_integer_t father = VECTOR(*sample)[sam]; - igraph_integer_t level, s; + long int father = (long int) VECTOR(*sample)[sam]; + long int level, s; IGRAPH_ALLOW_INTERRUPTION(); @@ -624,31 +621,32 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte } /* init G */ - igraph_vector_int_clear(&vids); level = 0; - IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + igraph_vector_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); added[father] += 1; level += 1; /* init V_E */ - igraph_vector_int_clear(&adjverts); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[i]; + long int nei = (long int) VECTOR(neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_int_clear(&stack); + igraph_stack_clear(&stack); - while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_int_size(&adjverts) / 2; + s = igraph_vector_size(&adjverts) / 2; for (i = 0; i < s; i++) { if (cp != 0 && RNG_UNIF01() < cp) { continue; @@ -658,54 +656,56 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte } if (level < size - 1 && - !igraph_vector_int_empty(&adjverts)) { + !igraph_vector_empty(&adjverts)) { /* We might step down */ - igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); - igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + 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_int_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); added[nei] += 1; level += 1; - IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + 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, nei, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - igraph_integer_t nei2 = VECTOR(neis)[i]; + long int nei2 = (long int) VECTOR(neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ - igraph_integer_t nei, neifather; - while (!igraph_stack_int_empty(&stack) && - level == igraph_stack_int_top(&stack) - 1) { - igraph_stack_int_pop(&stack); - nei = igraph_stack_int_pop(&stack); - neifather = igraph_stack_int_pop(&stack); - igraph_vector_int_push_back(&adjverts, nei); - igraph_vector_int_push_back(&adjverts, neifather); + 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 = igraph_vector_int_pop_back(&vids); + nei = (long int) igraph_vector_pop_back(&vids); added[nei] -= 1; level -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - added[ VECTOR(neis)[i] ] -= 1; + added[ (long int) VECTOR(neis)[i] ] -= 1; } - while (!igraph_vector_int_empty(&adjverts) && - igraph_vector_int_tail(&adjverts) == nei) { - igraph_vector_int_pop_back(&adjverts); - igraph_vector_int_pop_back(&adjverts); + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); } } @@ -713,31 +713,32 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte /* clear the added vector */ added[father] -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - added[ VECTOR(neis)[i] ] -= 1; + added[ (long int) VECTOR(neis)[i] ] -= 1; } } /* for father */ RNG_END(); - (*est) *= ((igraph_real_t) no_of_nodes / sample_size); + (*est) *= ((double)no_of_nodes / sample_size); if (parsample == 0) { - igraph_vector_int_destroy(sample); + igraph_vector_destroy(sample); IGRAPH_FREE(sample); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_FREE(added); - igraph_vector_int_destroy(&vids); - igraph_vector_int_destroy(&adjverts); - igraph_stack_int_destroy(&stack); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&vids); + igraph_vector_destroy(&adjverts); + igraph_stack_destroy(&stack); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** @@ -762,45 +763,46 @@ igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_inte * Time complexity: TODO. */ -igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, - igraph_integer_t size, const igraph_vector_t *cut_prob) { +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_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t neis; - igraph_vector_int_t vids; /* this is G */ - igraph_vector_int_t adjverts; /* this is V_E */ - igraph_stack_int_t stack; /* this is S */ - igraph_integer_t *added; - igraph_integer_t father; - igraph_integer_t i; + 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, size); + IGRAPH_EINVAL, (igraph_integer_t) size); } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", + 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, igraph_integer_t); + added = IGRAPH_CALLOC(no_of_nodes, long int); if (added == 0) { - IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); - IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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++) { - igraph_integer_t level, s; + long int level, s; IGRAPH_ALLOW_INTERRUPTION(); @@ -810,31 +812,32 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t } /* init G */ - igraph_vector_int_clear(&vids); level = 0; - IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); + igraph_vector_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); added[father] += 1; level += 1; /* init V_E */ - igraph_vector_int_clear(&adjverts); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[i]; + long int nei = (long int) VECTOR(neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_int_clear(&stack); + igraph_stack_clear(&stack); - while (level > 1 || !igraph_vector_int_empty(&adjverts)) { + while (level > 1 || !igraph_vector_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_int_size(&adjverts) / 2; + s = igraph_vector_size(&adjverts) / 2; for (i = 0; i < s; i++) { if (cp != 0 && RNG_UNIF01() < cp) { continue; @@ -844,54 +847,56 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t } if (level < size - 1 && - !igraph_vector_int_empty(&adjverts)) { + !igraph_vector_empty(&adjverts)) { /* We might step down */ - igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); - igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); + 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_int_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); added[nei] += 1; level += 1; - IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); - IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); + 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, nei, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - igraph_integer_t nei2 = VECTOR(neis)[i]; + long int nei2 = (long int) VECTOR(neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); } added[nei2] += 1; } } } else { /* no, step back */ - igraph_integer_t nei, neifather; - while (!igraph_stack_int_empty(&stack) && - level == igraph_stack_int_top(&stack) - 1) { - igraph_stack_int_pop(&stack); - nei = igraph_stack_int_pop(&stack); - neifather = igraph_stack_int_pop(&stack); - igraph_vector_int_push_back(&adjverts, nei); - igraph_vector_int_push_back(&adjverts, neifather); + 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 = igraph_vector_int_pop_back(&vids); + nei = (long int) igraph_vector_pop_back(&vids); added[nei] -= 1; level -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - added[ VECTOR(neis)[i] ] -= 1; + added[ (long int) VECTOR(neis)[i] ] -= 1; } - while (!igraph_vector_int_empty(&adjverts) && - igraph_vector_int_tail(&adjverts) == nei) { - igraph_vector_int_pop_back(&adjverts); - igraph_vector_int_pop_back(&adjverts); + while (!igraph_vector_empty(&adjverts) && + igraph_vector_tail(&adjverts) == nei) { + igraph_vector_pop_back(&adjverts); + igraph_vector_pop_back(&adjverts); } } @@ -899,10 +904,11 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t /* clear the added vector */ added[father] -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); - s = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, + IGRAPH_ALL)); + s = igraph_vector_size(&neis); for (i = 0; i < s; i++) { - added[ VECTOR(neis)[i] ] -= 1; + added[ (long int) VECTOR(neis)[i] ] -= 1; } } /* for father */ @@ -910,18 +916,19 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t RNG_END(); IGRAPH_FREE(added); - igraph_vector_int_destroy(&vids); - igraph_vector_int_destroy(&adjverts); - igraph_stack_int_destroy(&stack); - igraph_vector_int_destroy(&neis); + 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 Dyad census, as defined by Holland and Leinhardt. + * \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 @@ -935,12 +942,13 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t * * \param graph The input graph. For an undirected graph, there are no * asymmetric connections. - * \param mut Pointer to a real, the number of mutual dyads is + * \param mut Pointer to an integer, the number of mutual dyads is * stored here. - * \param asym Pointer to a real, the number of asymmetric dyads + * \param asym Pointer to an integer, the number of asymmetric dyads * is stored here. - * \param null Pointer to a real, the number of null 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(). @@ -948,30 +956,26 @@ igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges. */ -igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, - igraph_real_t *asym, igraph_real_t *null) { - - /* This function operates with a floating point type instead of an - * integer type in order to avoid integer overflow, which is likely - * for 'null' in large graphs on 32-bit systems. */ +int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, + igraph_integer_t *asym, igraph_integer_t *null) { - igraph_real_t nonrec = 0, rec = 0; - igraph_vector_int_t inneis, outneis; + igraph_integer_t nonrec = 0, rec = 0; + igraph_vector_t inneis, outneis; igraph_integer_t vc = igraph_vcount(graph); - igraph_integer_t i; + long int i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); for (i = 0; i < vc; i++) { - igraph_integer_t ideg, odeg; - igraph_integer_t ip, op; + 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_int_size(&inneis); - odeg = igraph_vector_int_size(&outneis); + ideg = igraph_vector_size(&inneis); + odeg = igraph_vector_size(&outneis); ip = op = 0; while (ip < ideg && op < odeg) { @@ -990,28 +994,43 @@ igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, nonrec += (ideg - ip) + (odeg - op); } - igraph_vector_int_destroy(&inneis); - igraph_vector_int_destroy(&outneis); + igraph_vector_destroy(&inneis); + igraph_vector_destroy(&outneis); IGRAPH_FINALLY_CLEAN(2); *mut = rec / 2; *asym = nonrec / 2; - *null = 0.5 * vc * (vc - 1.0) - (*mut + *asym); - if (*null == 0.0) *null = 0.0; /* avoid returning -0.0 */ + 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; } -static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_real_t *res2, +/** + * \function igraph_triad_census_24 + * TODO + */ + +int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, igraph_real_t *res4) { - igraph_integer_t vc = igraph_vcount(graph); - igraph_vector_int_t seen; + long int vc = igraph_vcount(graph); + igraph_vector_long_t seen; igraph_vector_int_t *neis, *neis2; - igraph_integer_t i, j, k, s, neilen, neilen2, ign; + long int i, j, k, s, neilen, neilen2, ign; igraph_adjlist_t adjlist; - IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, vc); + 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; @@ -1025,7 +1044,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea VECTOR(seen)[i] = i + 1; ign = 0; for (j = 0; j < neilen; j++) { - igraph_integer_t nei = VECTOR(*neis)[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); @@ -1036,7 +1055,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea } for (j = 0; j < neilen; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; + long int nei = (long int) VECTOR(*neis)[j]; if (nei <= i || (j > 0 && nei == VECTOR(*neis)[j - 1])) { continue; } @@ -1044,7 +1063,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea neilen2 = igraph_vector_int_size(neis2); s = 0; for (k = 0; k < neilen2; k++) { - igraph_integer_t nei2 = VECTOR(*neis2)[k]; + long int nei2 = (long int) VECTOR(*neis2)[k]; if (k > 0 && nei2 == VECTOR(*neis2)[k - 1]) { continue; } @@ -1061,15 +1080,15 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea } igraph_adjlist_destroy(&adjlist); - igraph_vector_int_destroy(&seen); + igraph_vector_long_destroy(&seen); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_triad_census - * \brief Triad census, as defined by Davis and Leinhardt. + * \brief Triad census, as defined by Davis and Leinhardt * * * Calculating the triad census means classifying every triple of @@ -1134,7 +1153,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea * Time complexity: TODO. */ -igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { +int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { igraph_vector_t cut_prob; igraph_real_t m2, m4; @@ -1151,7 +1170,7 @@ igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) IGRAPH_CHECK(igraph_vector_resize(res, 16)); igraph_vector_null(res); IGRAPH_CHECK(igraph_motifs_randesu(graph, &tmp, 3, &cut_prob)); - IGRAPH_CHECK(igraph_i_triad_census_24(graph, &m2, &m4)); + IGRAPH_CHECK(igraph_triad_census_24(graph, &m2, &m4)); total = ((igraph_real_t)vc) * (vc - 1); total *= (vc - 2); @@ -1195,5 +1214,5 @@ igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/misc/order_cycle.cpp b/src/vendor/cigraph/src/misc/order_cycle.cpp deleted file mode 100644 index e977db593e3..00000000000 --- a/src/vendor/cigraph/src/misc/order_cycle.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - 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, see . -*/ - -#include "misc/order_cycle.h" - -#include "igraph_interface.h" - -#include "core/exceptions.h" - -#include -#include - -// Initialized to {-1, -1} -struct eid_pair_t : public std::pair { - eid_pair_t() : std::pair(-1, -1) { } -}; - -/** - * \function igraph_i_order_cycle - * \brief Reorders edges of a cycle in cycle order - * - * This function takes \p cycle, a vector of arbitrarily ordered edge IDs, - * representing a graph cycle. It produces a vector \p res containing the - * same IDs in cycle order. \p res must be initialized when calling this function. - */ -igraph_error_t igraph_i_order_cycle( - const igraph_t *graph, - const igraph_vector_int_t *cycle, - igraph_vector_int_t *res) { - - IGRAPH_HANDLE_EXCEPTIONS_BEGIN; - - igraph_integer_t n = igraph_vector_int_size(cycle); - IGRAPH_ASSERT(n > 0); - - std::map inclist; - for (igraph_integer_t i=0; i < n; ++i) { - igraph_integer_t eid = VECTOR(*cycle)[i]; - - { - igraph_integer_t from = IGRAPH_FROM(graph, eid); - auto &p = inclist[from]; - if (p.first < 0) { - p.first = eid; - } else { - IGRAPH_ASSERT(p.second < 0); - p.second = eid; - } - } - - { - igraph_integer_t to = IGRAPH_TO(graph, eid); - auto &p = inclist[to]; - if (p.first < 0) { - p.first = eid; - } else { - IGRAPH_ASSERT(p.second < 0); - p.second = eid; - } - } - } - - igraph_vector_int_clear(res); - IGRAPH_CHECK(igraph_vector_int_reserve(res, igraph_vector_int_size(cycle))); - igraph_integer_t current_e = VECTOR(*cycle)[0]; - igraph_integer_t current_v = IGRAPH_FROM(graph, current_e); - for (igraph_integer_t i=0; i < n; ++i) { - const auto &p = inclist.at(current_v); - igraph_vector_int_push_back(res, current_e); /* reserved */ - igraph_integer_t next_e = p.first; - if (next_e == current_e) { - next_e = p.second; - } - current_e = next_e; - igraph_integer_t next_v = IGRAPH_FROM(graph, current_e); - if (next_v == current_v) { - next_v = IGRAPH_TO(graph, current_e); - } - current_v = next_v; - } - - IGRAPH_HANDLE_EXCEPTIONS_END; - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/misc/other.c b/src/vendor/cigraph/src/misc/other.c index d6f5ca6ec99..45dbcbbb9df 100644 --- a/src/vendor/cigraph/src/misc/other.c +++ b/src/vendor/cigraph/src/misc/other.c @@ -21,11 +21,15 @@ */ -#include "igraph_interface.h" #include "igraph_nongraph.h" -#include "igraph_paths.h" +#include "igraph_random.h" +#include "igraph_types.h" #include "core/interruption.h" +#include "plfit/plfit_error.h" +#include "plfit/plfit.h" + +#include /** * \ingroup nongraph @@ -48,15 +52,15 @@ * the data vector. */ -igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, +int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth) { double sum = 0; - igraph_integer_t i; + long int i; /* Check */ if (igraph_vector_size(data) < binwidth) { - IGRAPH_ERRORF("Data vector length (%" IGRAPH_PRId ") smaller than bin width (%" IGRAPH_PRId ").", IGRAPH_EINVAL, 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); @@ -64,7 +68,7 @@ igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t /* Memory for result */ - IGRAPH_CHECK(igraph_vector_resize(res, (igraph_vector_size(data) - binwidth + 1))); + IGRAPH_CHECK(igraph_vector_resize(res, (long int)(igraph_vector_size(data) - binwidth + 1))); /* Initial bin */ for (i = 0; i < binwidth; i++) { @@ -76,7 +80,7 @@ igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t for (i = 1; i < igraph_vector_size(data) - binwidth + 1; i++) { IGRAPH_ALLOW_INTERRUPTION(); sum -= VECTOR(*data)[i - 1]; - sum += VECTOR(*data)[ (i + binwidth - 1)]; + sum += VECTOR(*data)[ (long int)(i + binwidth - 1)]; VECTOR(*res)[i] = sum / binwidth; } @@ -87,7 +91,7 @@ igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t /** * \ingroup nongraph * \function igraph_convex_hull - * \brief Determines the convex hull of a given set of points in the 2D plane. + * \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. @@ -111,35 +115,34 @@ igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t * \return Error code: * \c IGRAPH_ENOMEM: not enough memory * - * Time complexity: O(n log(n)) where n is the number of vertices. + * Time complexity: O(n log(n)) where n is the number of vertices + * + * \example examples/simple/igraph_convex_hull.c */ -igraph_error_t igraph_convex_hull( - const igraph_matrix_t *data, igraph_vector_int_t *resverts, - igraph_matrix_t *rescoords -) { +int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, + igraph_matrix_t *rescoords) { igraph_integer_t no_of_nodes; - igraph_integer_t i, pivot_idx = 0, last_idx, before_last_idx, next_idx, j; - igraph_vector_t angles; - igraph_vector_int_t order, stack; + 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_matrix_nrow(data); + no_of_nodes = (igraph_integer_t) igraph_matrix_nrow(data); if (igraph_matrix_ncol(data) != 2) { - IGRAPH_ERROR("Only two-dimensional point sets are supports, matrix must have two columns.", IGRAPH_EINVAL); + IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL); } if (no_of_nodes == 0) { - if (resverts) { - igraph_vector_int_clear(resverts); + if (resverts != 0) { + IGRAPH_CHECK(igraph_vector_resize(resverts, 0)); } - if (rescoords) { + if (rescoords != 0) { IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); } /**************************** this is an exit here *********/ - return IGRAPH_SUCCESS; + return 0; } IGRAPH_VECTOR_INIT_FINALLY(&angles, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&stack, 0); + IGRAPH_VECTOR_INIT_FINALLY(&stack, 0); /* Search for the pivot vertex */ for (i = 1; i < no_of_nodes; i++) { @@ -167,16 +170,16 @@ igraph_error_t igraph_convex_hull( } /* Sort points by angles */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); - IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, IGRAPH_ASCENDING)); + 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 = VECTOR(order)[0]; - pivot_idx = VECTOR(order)[no_of_nodes - 1]; + 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 = VECTOR(order)[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 */ @@ -200,11 +203,11 @@ igraph_error_t igraph_convex_hull( j = 0; last_idx = -1; before_last_idx = -1; - while (!igraph_vector_int_empty(&order)) { - next_idx = igraph_vector_int_tail(&order); + 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_int_pop_back(&order); + igraph_vector_pop_back(&order); continue; } /* Determine whether we are at a left or right turn */ @@ -218,161 +221,214 @@ igraph_error_t igraph_convex_hull( (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) ? VECTOR(stack)[j - 2] : -1; + before_last_idx = (j >= 2) ? (long int) VECTOR(stack)[j - 2] : -1; } } /* Create result vector */ if (resverts != 0) { - igraph_vector_int_clear(resverts); - IGRAPH_CHECK(igraph_vector_int_append(resverts, &stack)); + 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_int_destroy(&order); - igraph_vector_int_destroy(&stack); + igraph_vector_destroy(&order); + igraph_vector_destroy(&stack); igraph_vector_destroy(&angles); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } -/** - * \function igraph_expand_path_to_pairs - * \brief Helper function to convert a sequence of vertex IDs describing a path into a "pairs" vector. - * - * - * This function is useful when you have a sequence of vertex IDs in a graph and - * you would like to retrieve the IDs of the edges between them. The function - * duplicates all but the first and the last elements in the vector, effectively - * converting the path into a vector of vertex IDs that can be passed to - * \ref igraph_get_eids(). - * - * \param path the input vector. It will be modified in-place and it will be - * resized as needed. When the vector contains less than two vertex IDs, - * it will be cleared. - * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory to expand - * the vector. - */ -igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t* path) { - igraph_integer_t no_of_vertices = igraph_vector_int_size(path); - igraph_integer_t i, j, no_of_items = (no_of_vertices - 1) * 2; - if (no_of_vertices <= 1) { - igraph_vector_int_clear(path); - } else { - IGRAPH_CHECK(igraph_vector_int_resize(path, no_of_items)); - - i = no_of_vertices - 1; - j = no_of_items - 1; - VECTOR(*path)[j] = VECTOR(*path)[i]; - while (i > 1) { - i--; j--; - VECTOR(*path)[j] = VECTOR(*path)[i]; - j--; - VECTOR(*path)[j] = VECTOR(*path)[i]; - } - } +static const char* igraph_i_plfit_error_message = 0; - return IGRAPH_SUCCESS; +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; } /** - * \function igraph_vertex_path_from_edge_path - * \brief Converts a path of edge IDs to the traversed vertex IDs. + * \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. * * - * This function is useful when you have a sequence of edge IDs representing a - * continuous path in a graph and you would like to obtain the vertex IDs that - * the path traverses. The function is used implicitly by several shortest path - * related functions to convert a path of edge IDs to the corresponding - * representation that describes the path in terms of vertex IDs instead. + * 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). * - * \param graph the graph that the edge IDs refer to - * \param start the start vertex of the path - * \param edge_path the sequence of edge IDs that describe the path - * \param vertex_path the sequence of vertex IDs traversed will be returned here - * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory, - * \c IGRAPH_EINVAL if the edge path does not start at the given vertex - * or if there is at least one edge whose start vertex does not match - * the end vertex of the previous edge + * \example examples/simple/igraph_power_law_fit.c */ -igraph_error_t igraph_vertex_path_from_edge_path( - const igraph_t *graph, igraph_integer_t start, - const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, - igraph_neimode_t mode -) { - igraph_integer_t i, no_of_edges; - igraph_bool_t directed = igraph_is_directed(graph); - igraph_bool_t next_edge_ok; - igraph_integer_t next_start; - - igraph_vector_int_clear(vertex_path); - - no_of_edges = igraph_vector_int_size(edge_path); - IGRAPH_CHECK(igraph_vector_int_reserve(vertex_path, no_of_edges + 1)); - - if (!directed) { - mode = IGRAPH_ALL; +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; + } + } } - for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(*edge_path)[i]); - igraph_integer_t to = IGRAPH_TO(graph, VECTOR(*edge_path)[i]); + RNG_BEGIN(); - igraph_vector_int_push_back(vertex_path, start); /* reserved */ + 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; - switch (mode) { - case IGRAPH_OUT: - next_edge_ok = from == start; - next_start = to; - break; + 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); - case IGRAPH_IN: - next_edge_ok = to == start; - next_start = from; - break; + RNG_END(); - case IGRAPH_ALL: - if (from == start) { - next_edge_ok = true; - next_start = to; - } else if (to == start) { - next_edge_ok = true; - next_start = from; - } else { - next_edge_ok = false; - } - break; + switch (retval) { + case PLFIT_FAILURE: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_FAILURE); + break; - default: - IGRAPH_ERROR("Invalid neighborhood mode.", IGRAPH_EINVAL); - } + case PLFIT_EINVAL: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EINVAL); + break; - if (!next_edge_ok) { - IGRAPH_ERROR("Edge IDs do not form a continuous path.", IGRAPH_EINVAL); - } + 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; - start = next_start; + default: + break; } - igraph_vector_int_push_back(vertex_path, start); /* reserved */ + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/misc/power_law_fit.c b/src/vendor/cigraph/src/misc/power_law_fit.c deleted file mode 100644 index e9e036cfd2b..00000000000 --- a/src/vendor/cigraph/src/misc/power_law_fit.c +++ /dev/null @@ -1,328 +0,0 @@ -/* -*- 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 "plfit/plfit_error.h" -#include "plfit/plfit.h" - -#include - -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; -} - -static void igraph_i_plfit_prepare_continuous_options( - plfit_continuous_options_t* options, igraph_bool_t finite_size_correction -) { - plfit_continuous_options_init(options); - options->p_value_method = PLFIT_P_VALUE_SKIP; - options->xmin_method = PLFIT_STRATIFIED_SAMPLING; - options->finite_size_correction = (plfit_bool_t) finite_size_correction; -} - -static void igraph_i_plfit_prepare_discrete_options( - plfit_discrete_options_t* options, igraph_bool_t finite_size_correction -) { - plfit_discrete_options_init(options); - options->p_value_method = PLFIT_P_VALUE_SKIP; - options->finite_size_correction = (plfit_bool_t) finite_size_correction; -} - -/* Decides whether to use finite size correction for the given input data */ -static igraph_bool_t igraph_i_plfit_should_use_finite_size_correction(const igraph_vector_t* data) { - return igraph_vector_size(data) < 50; -} - -static igraph_error_t igraph_i_handle_plfit_error(int code) { - switch (code) { - case PLFIT_SUCCESS: - return IGRAPH_SUCCESS; - - 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); /* LCOV_EXCL_LINE */ - break; - - case PLFIT_OVERFLOW: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ - break; - - case PLFIT_ENOMEM: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - break; - - case PLFIT_EMAXITER: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_DIVERGED); /* LCOV_EXCL_LINE */ - break; - - default: - IGRAPH_ERRORF("Unknown error code returned from plfit (%d)", IGRAPH_FAILURE, code); - break; - } - - return IGRAPH_SUCCESS; -} - -/** - * \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. - * https://doi.org/10.1137/070710111 - * - * \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. Note that the p-value of the fit is \em not - * calculated by default as it is time-consuming; you need to call - * \ref igraph_plfit_result_calculate_p_value() to calculate the - * p-value itself - * \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 - */ -igraph_error_t 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 ? false : true; - igraph_bool_t finite_size_correction; - - int retval; - size_t i, n; - - finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(data); - n = (size_t) igraph_vector_size(data); - - if (discrete) { - /* Does the vector contain discrete values only? */ - for (i = 0; i < n; i++) { - if (trunc(VECTOR(*data)[i]) != VECTOR(*data)[i]) { - discrete = false; - break; - } - } - } - - RNG_BEGIN(); - - plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); - if (discrete) { - igraph_i_plfit_prepare_discrete_options(&disc_options, 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 { - igraph_i_plfit_prepare_continuous_options(&cont_options, 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(); - - IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); - - if (result) { - result->data = data; - result->continuous = !discrete; - result->alpha = plfit_result.alpha; - result->xmin = plfit_result.xmin; - result->L = plfit_result.L; - result->D = plfit_result.D; - } - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup nongraph - * \function igraph_plfit_result_calculate_p_value - * \brief Calculates the p-value of a fitted power-law model. - * - * - * The p-value is calculated by resampling the input data many times in a way - * that the part below the fitted \c x_min threshold is resampled from the - * input data itself, while the part above the fitted \c x_min threshold is - * drawn from the fitted power-law function. A Kolmogorov-Smirnov test is then - * performed for each resampled dataset and its test statistic is compared with the - * observed test statistic from the original dataset. The fraction of resampled - * datasets that have a \em higher test statistic is the returned p-value. - * - * - * Note that the precision of the returned p-value depends on the number of - * resampling attempts. The number of resampling trials is determined by - * 0.25 divided by the square of the required precision. For instance, a required - * precision of 0.01 means that 2500 samples will be drawn. - * - * - * If igraph is compiled with OpenMP support, this function will use parallel - * OpenMP threads for the resampling. Each OpenMP thread gets its own instance - * of a random number generator. However, since the scheduling of OpenMP threads - * is outside our control, we cannot guarantee how many resampling instances the - * threads are asked to execute, thus it may happen that the random number - * generators are used differently between runs. If you want to obtain - * reproducible results, seed igraph's master RNG appropriately, and force the - * number of OpenMP threads to 1 early in your program, either by calling - * omp_set_num_threads(1) or by setting the value of the \c OMP_NUM_THREADS - * environment variable to 1. - * - * \param model The fitted power-law model from the \ref igraph_power_law_fit() - * function - * \param result The calculated p-value is returned here - * \param precision The desired precision of the p-value. Higher values correspond - * to longer calculation time. - * @return igraph_error_t - */ -igraph_error_t igraph_plfit_result_calculate_p_value( - const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision -) { - int retval; - plfit_continuous_options_t cont_options; - plfit_discrete_options_t disc_options; - plfit_result_t plfit_result; - plfit_error_handler_t* plfit_stored_error_handler; - igraph_bool_t finite_size_correction; - - IGRAPH_ASSERT(model != NULL); - - plfit_result.alpha = model->alpha; - plfit_result.xmin = model->xmin; - plfit_result.L = model->L; - plfit_result.D = model->D; - - finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(model->data); - - RNG_BEGIN(); - - plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); - if (model->continuous) { - igraph_i_plfit_prepare_continuous_options(&cont_options, finite_size_correction); - cont_options.p_value_method = PLFIT_P_VALUE_EXACT; - cont_options.p_value_precision = precision; - retval = plfit_calculate_p_value_continuous( - VECTOR(*model->data), (size_t) igraph_vector_size(model->data), - &cont_options, /* xmin_fixed = */ 0, &plfit_result - ); - } else { - igraph_i_plfit_prepare_discrete_options(&disc_options, finite_size_correction); - disc_options.p_value_method = PLFIT_P_VALUE_EXACT; - disc_options.p_value_precision = precision; - retval = plfit_calculate_p_value_discrete( - VECTOR(*model->data), (size_t) igraph_vector_size(model->data), - &disc_options, /* xmin_fixed = */ 0, &plfit_result - ); - } - plfit_set_error_handler(plfit_stored_error_handler); - - RNG_END(); - - IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); - - if (result) { - *result = plfit_result.p; - } - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/misc/scan.c b/src/vendor/cigraph/src/misc/scan.c index 7fa64730ddf..3b56f674223 100644 --- a/src/vendor/cigraph/src/misc/scan.c +++ b/src/vendor/cigraph/src/misc/scan.c @@ -24,7 +24,10 @@ #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" @@ -32,6 +35,7 @@ #include "igraph_structural.h" #include "core/interruption.h" +#include "properties/properties_internal.h" /** * \section about_local_scan @@ -62,34 +66,39 @@ * */ -igraph_error_t igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, +int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode) { - return igraph_strength(graph, res, igraph_vss_all(), mode, /*loops=*/ 1, - weights); + 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 igraph_error_t igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t *il, +static int igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t *il, const igraph_vector_int_t *rank) { - igraph_integer_t i; - igraph_integer_t n = il->length; + long int i; + long int n = il->length; igraph_vector_int_t mark; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, n); + 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]; - igraph_integer_t j, l = igraph_vector_int_size(v); - igraph_integer_t irank = VECTOR(*rank)[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 */) { - igraph_integer_t edge = VECTOR(*v)[j]; - igraph_integer_t e = IGRAPH_OTHER(graph, edge, i); + 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++; @@ -103,42 +112,43 @@ static igraph_error_t igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_ igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /* This one handles both weighted and unweighted cases */ -static igraph_error_t igraph_i_local_scan_1_directed(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + int no_of_nodes = igraph_vcount(graph); igraph_inclist_t incs; - igraph_integer_t i, node; + 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_FINALLY(&neis, no_of_nodes); + 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_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); - igraph_integer_t edgeslen1 = igraph_vector_int_size(edges1); + 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++) { - igraph_integer_t e = VECTOR(*edges1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, e, node); + 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; @@ -146,16 +156,16 @@ static igraph_error_t igraph_i_local_scan_1_directed(const igraph_t *graph, /* Crawl neighbors */ for (i = 0; i < edgeslen1; i++) { - igraph_integer_t e2 = VECTOR(*edges1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, e2, node); + 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); - igraph_integer_t j, edgeslen2 = igraph_vector_int_size(edges2); + int j, edgeslen2 = igraph_vector_int_size(edges2); for (j = 0; j < edgeslen2; j++) { - igraph_integer_t e2 = VECTOR(*edges2)[j]; - igraph_integer_t nei2 = IGRAPH_OTHER(graph, e2, nei); + 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; @@ -169,30 +179,31 @@ static igraph_error_t igraph_i_local_scan_1_directed(const igraph_t *graph, igraph_inclist_destroy(&incs); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_local_scan_1_directed_all(const igraph_t *graph, +static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + int no_of_nodes = igraph_vcount(graph); igraph_inclist_t incs; - igraph_integer_t i, node; + int i, node; igraph_vector_int_t neis; IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); + 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_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); - igraph_integer_t edgeslen1 = igraph_vector_int_size(edges1); + int edgeslen1 = igraph_vector_int_size(edges1); IGRAPH_ALLOW_INTERRUPTION(); @@ -201,8 +212,8 @@ static igraph_error_t igraph_i_local_scan_1_directed_all(const igraph_t *graph, double count its incident edges later, when we are going over the incident edges of ego's neighbors. */ for (i = 0; i < edgeslen1; i++) { - igraph_integer_t e = VECTOR(*edges1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, e, node); + 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; @@ -215,18 +226,18 @@ static igraph_error_t igraph_i_local_scan_1_directed_all(const igraph_t *graph, only crawled once. We count all qualifying edges of ego, and then unmark ego to avoid double counting. */ for (i = 0; i < edgeslen1; i++) { - igraph_integer_t e2 = VECTOR(*edges1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, e2, node); + int e2 = VECTOR(*edges1)[i]; + int nei = IGRAPH_OTHER(graph, e2, node); igraph_vector_int_t *edges2; - igraph_integer_t j, edgeslen2; + 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++) { - igraph_integer_t e2 = VECTOR(*edges2)[j]; - igraph_integer_t nei2 = IGRAPH_OTHER(graph, e2, nei); + 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; @@ -241,37 +252,39 @@ static igraph_error_t igraph_i_local_scan_1_directed_all(const igraph_t *graph, igraph_inclist_destroy(&incs); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_local_scan_1_sumweights(const igraph_t *graph, +static int igraph_i_local_scan_1_sumweights(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t node, i, j, nn; + long int no_of_nodes = igraph_vcount(graph); + long int node, i, j, nn; igraph_inclist_t allinc; igraph_vector_int_t *neis1, *neis2; - igraph_integer_t neilen1, neilen2; - igraph_integer_t *neis; - igraph_integer_t maxdegree; + long int neilen1, neilen2; + long int *neis; + long int maxdegree; igraph_vector_int_t order; igraph_vector_int_t rank; - igraph_vector_int_t degree, *edge1 = °ree; /* reuse degree as edge1 */ + 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_FINALLY(&order, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + 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 = igraph_vector_int_max(°ree) + 1; - IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); + 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; } @@ -280,9 +293,9 @@ static igraph_error_t igraph_i_local_scan_1_sumweights(const igraph_t *graph, IGRAPH_FINALLY(igraph_inclist_destroy, &allinc); IGRAPH_CHECK(igraph_i_trans4_il_simplify(graph, &allinc, &rank)); - neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + neis = IGRAPH_CALLOC(no_of_nodes, long int); if (neis == 0) { - IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, neis); @@ -299,21 +312,21 @@ static igraph_error_t igraph_i_local_scan_1_sumweights(const igraph_t *graph, /* Mark the neighbors of the node */ for (i = 0; i < neilen1; i++) { - igraph_integer_t edge = VECTOR(*neis1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + 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++) { - igraph_integer_t edge = VECTOR(*neis1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + 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++) { - igraph_integer_t edge2 = VECTOR(*neis2)[j]; - igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge2, nei); + 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; @@ -327,11 +340,11 @@ static igraph_error_t igraph_i_local_scan_1_sumweights(const igraph_t *graph, igraph_free(neis); igraph_inclist_destroy(&allinc); igraph_vector_int_destroy(&rank); - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } /** @@ -351,7 +364,7 @@ static igraph_error_t igraph_i_local_scan_1_sumweights(const igraph_t *graph, * */ -igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, +int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode) { @@ -368,17 +381,18 @@ igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t return igraph_local_scan_k_ecount(graph, 1, res, weights, mode); } } + + return 0; } -static igraph_error_t igraph_i_local_scan_0_them_w(const igraph_t *us, const igraph_t *them, +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_int_t map2; - igraph_vector_t weights; - igraph_integer_t i, m; + igraph_vector_t map2; + int i, m; if (!weights_them) { IGRAPH_ERROR("Edge weights not given for weighted scan-0", @@ -388,26 +402,24 @@ static igraph_error_t igraph_i_local_scan_0_them_w(const igraph_t *us, const igr IGRAPH_ERROR("Invalid weights length for scan-0", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&map2, 0); - IGRAPH_CHECK(igraph_intersection(&is, us, them, /* edge_map1= */ 0, &map2)); + 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_int_size(&map2); - IGRAPH_VECTOR_INIT_FINALLY(&weights, m); + m = igraph_vector_size(&map2); for (i = 0; i < m; i++) { - VECTOR(weights)[i] = VECTOR(*weights_them)[ VECTOR(map2)[i] ]; + VECTOR(map2)[i] = VECTOR(*weights_them)[ (int) VECTOR(map2)[i] ]; } - IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, - /*weights=*/ &weights)); + igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, + /*weights=*/ &map2); igraph_destroy(&is); - igraph_vector_int_destroy(&map2); - igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(3); + igraph_vector_destroy(&map2); + IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -429,7 +441,7 @@ static igraph_error_t igraph_i_local_scan_0_them_w(const igraph_t *us, const igr * */ -igraph_error_t igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, +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) { @@ -447,15 +459,15 @@ igraph_error_t igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them return igraph_i_local_scan_0_them_w(us, them, res, weights_them, mode); } - IGRAPH_CHECK(igraph_intersection(&is, us, them, /*edge_map1=*/ 0, /*edge_map2=*/ 0)); + igraph_intersection(&is, us, them, /*edgemap1=*/ 0, /*edgemap2=*/ 0); IGRAPH_FINALLY(igraph_destroy, &is); - IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, /* weights = */ 0)); + igraph_degree(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS); igraph_destroy(&is); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -476,16 +488,16 @@ igraph_error_t igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them * \sa \ref igraph_local_scan_1_ecount() for the US statistics. */ -igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(us); + int no_of_nodes = igraph_vcount(us); igraph_adjlist_t adj_us; igraph_inclist_t incs_them; igraph_vector_int_t neis; - igraph_integer_t node; + int node; if (igraph_vcount(them) != no_of_nodes) { IGRAPH_ERROR("Number of vertices must match in scan-1", IGRAPH_EINVAL); @@ -506,7 +518,8 @@ igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_ IGRAPH_CHECK(igraph_inclist_init(them, &incs_them, mode, IGRAPH_LOOPS)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs_them); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); + 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); @@ -514,23 +527,23 @@ igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_ 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); - igraph_integer_t len1_us = igraph_vector_int_size(neis_us); - igraph_integer_t len1_them = igraph_vector_int_size(edges1_them); - igraph_integer_t i; + 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++) { - igraph_integer_t nei = VECTOR(*neis_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++) { - igraph_integer_t e = VECTOR(*edges1_them)[i]; - igraph_integer_t nei = IGRAPH_OTHER(them, e, node); + 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; @@ -538,12 +551,12 @@ igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_ } /* Then the rest */ for (i = 0; i < len1_us; i++) { - igraph_integer_t nei = VECTOR(*neis_us)[i]; + int nei = VECTOR(*neis_us)[i]; igraph_vector_int_t *edges2_them = igraph_inclist_get(&incs_them, nei); - igraph_integer_t j, len2_them = igraph_vector_int_size(edges2_them); + int j, len2_them = igraph_vector_int_size(edges2_them); for (j = 0; j < len2_them; j++) { - igraph_integer_t e2 = VECTOR(*edges2_them)[j]; - igraph_integer_t nei2 = IGRAPH_OTHER(them, e2, nei); + 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; @@ -563,7 +576,7 @@ igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_ igraph_adjlist_destroy(&adj_us); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -582,13 +595,13 @@ igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_ * */ -igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t node; + int no_of_nodes = igraph_vcount(graph); + int node; igraph_dqueue_int_t Q; igraph_vector_int_t marked; igraph_inclist_t incs; @@ -597,8 +610,8 @@ igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_ 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 (%" IGRAPH_PRId ") in k-scan should equal " - "the number of edges of the graph (%" IGRAPH_PRId ").", + 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)); } @@ -615,7 +628,8 @@ igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_ IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + 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); @@ -623,24 +637,24 @@ igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_ igraph_vector_null(res); for (node = 0 ; node < no_of_nodes ; node++) { - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, node)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + igraph_dqueue_int_push(&Q, node); + igraph_dqueue_int_push(&Q, 0); VECTOR(marked)[node] = node + 1; while (!igraph_dqueue_int_empty(&Q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&Q); - igraph_integer_t dist = igraph_dqueue_int_pop(&Q) + 1; + 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); - igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + int i, edgeslen = igraph_vector_int_size(edges); for (i = 0; i < edgeslen; i++) { - igraph_integer_t edge = VECTOR(*edges)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, act); + 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_CHECK(igraph_dqueue_int_push(&Q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, dist)); + igraph_dqueue_int_push(&Q, nei); + igraph_dqueue_int_push(&Q, dist); VECTOR(marked)[nei] = node + 1; } } @@ -662,7 +676,7 @@ igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_ /** * \function igraph_local_scan_k_ecount_them - * \brief Local THEM scan-statistics, edge count or sum of weights. + * 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. @@ -680,37 +694,31 @@ igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_ * \sa \ref igraph_local_scan_1_ecount() for the US statistics. */ -igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, - igraph_integer_t k, igraph_vector_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(us); - igraph_integer_t node; + 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("The number of vertices in the two graphs must " - "match in scan-k.", - IGRAPH_EINVAL); + IGRAPH_ERROR("Number of vertices must match in scan-k", IGRAPH_EINVAL); } if (igraph_is_directed(us) != igraph_is_directed(them)) { - IGRAPH_ERROR("Directedness in the two graphs must match " - "in scan-k", IGRAPH_EINVAL); + IGRAPH_ERROR("Directedness must match in scan-k", IGRAPH_EINVAL); } if (k < 0) { - IGRAPH_ERRORF("k must be non-negative in k-scan, got %" IGRAPH_PRId - ".", IGRAPH_EINVAL, k); + IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL); } if (weights_them && igraph_vector_size(weights_them) != igraph_ecount(them)) { - IGRAPH_ERRORF("The weight vector length (%" IGRAPH_PRId - ") must be equal to the number of edges (%" IGRAPH_PRId - ").", IGRAPH_EINVAL, igraph_vector_size(weights_them), - igraph_ecount(them)); + IGRAPH_ERROR("Invalid weight vector length in k-scan (them)", + IGRAPH_EINVAL); } if (k == 0) { @@ -725,7 +733,8 @@ igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_ IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - IGRAPH_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + 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)); @@ -744,30 +753,30 @@ igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_ IGRAPH_CHECK(igraph_stack_int_push(&ST, node)); VECTOR(marked)[node] = node + 1; while (!igraph_dqueue_int_empty(&Q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&Q); - igraph_integer_t dist = igraph_dqueue_int_pop(&Q) + 1; + 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); - igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + int i, edgeslen = igraph_vector_int_size(edges); for (i = 0; i < edgeslen; i++) { - igraph_integer_t edge = VECTOR(*edges)[i]; - igraph_integer_t nei = IGRAPH_OTHER(us, edge, act); + int edge = VECTOR(*edges)[i]; + int nei = IGRAPH_OTHER(us, edge, act); if (dist <= k && VECTOR(marked)[nei] != node + 1) { - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, dist)); + igraph_dqueue_int_push(&Q, nei); + igraph_dqueue_int_push(&Q, dist); VECTOR(marked)[nei] = node + 1; - IGRAPH_CHECK(igraph_stack_int_push(&ST, nei)); + igraph_stack_int_push(&ST, nei); } } } /* Now check the edges of all nodes in THEM */ while (!igraph_stack_int_empty(&ST)) { - igraph_integer_t act = igraph_stack_int_pop(&ST); + int act = igraph_stack_int_pop(&ST); igraph_vector_int_t *edges = igraph_inclist_get(&incs_them, act); - igraph_integer_t i, edgeslen = igraph_vector_int_size(edges); + int i, edgeslen = igraph_vector_int_size(edges); for (i = 0; i < edgeslen; i++) { - igraph_integer_t edge = VECTOR(*edges)[i]; - igraph_integer_t nei = IGRAPH_OTHER(them, edge, act); + 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; @@ -788,73 +797,80 @@ igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_ igraph_dqueue_int_destroy(&Q); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } + /** - * \function igraph_local_scan_subset_ecount - * \brief Local scan-statistics of subgraphs induced by subsets of vertices. + * \function igraph_local_scan_neighborhood_ecount + * Local scan-statistics with pre-calculated neighborhoods * - * Count the number of edges, or sum the edge weights in - * induced subgraphs from vertices given as a parameter. + * 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 subsets List of \type igraph_vector_int_t - * objects, the vertex subsets. + * \param neighborhoods List of igraph_vector_int_t + * objects, the neighborhoods, one for each vertex in the + * graph. * \return Error code. */ -igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, +int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, - const igraph_vector_int_list_t *subsets) { + const igraph_vector_ptr_t *neighborhoods) { - igraph_integer_t subset, no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_subsets = igraph_vector_int_list_size(subsets); + 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); + 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_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); + 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_TWICE)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs); - IGRAPH_CHECK(igraph_vector_resize(res, no_of_subsets)); + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); igraph_vector_null(res); - for (subset = 0; subset < no_of_subsets; subset++) { - igraph_vector_int_t *nei = igraph_vector_int_list_get_ptr(subsets, subset); - igraph_integer_t i, neilen = igraph_vector_int_size(nei); + 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++) { - igraph_integer_t vertex = VECTOR(*nei)[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_ERROR("Invalid vertex id in neighborhood list in local scan", IGRAPH_EINVAL); } - VECTOR(marked)[vertex] = subset + 1; + VECTOR(marked)[vertex] = node + 1; } for (i = 0; i < neilen; i++) { - igraph_integer_t vertex = VECTOR(*nei)[i]; + int vertex = VECTOR(*nei)[i]; igraph_vector_int_t *edges = igraph_inclist_get(&incs, vertex); - igraph_integer_t j, edgeslen = igraph_vector_int_size(edges); + int j, edgeslen = igraph_vector_int_size(edges); for (j = 0; j < edgeslen; j++) { - igraph_integer_t edge = VECTOR(*edges)[j]; - igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge, vertex); - if (VECTOR(marked)[nei2] == subset + 1) { + 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)[subset] += w; + VECTOR(*res)[node] += w; } } } if (!directed) { - VECTOR(*res)[subset] /= 2.0; + VECTOR(*res)[node] /= 2.0; } } @@ -862,39 +878,5 @@ igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, igraph_vector_int_destroy(&marked); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_local_scan_neighborhood_ecount - * Local scan-statistics with pre-calculated neighborhoods - * - * Count the number of edges, or sum the edge weights in - * neighborhoods given as a parameter. - * - * \deprecated-by igraph_local_scan_subset_ecount 0.10.0 - * - * \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 \type igraph_vector_int_t - * objects, the neighborhoods, one for each vertex in the - * graph. - * \return Error code. - */ - -igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vector_t *weights, - const igraph_vector_int_list_t *neighborhoods) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - - if (igraph_vector_int_list_size(neighborhoods) != no_of_nodes) { - IGRAPH_ERROR("Invalid neighborhood list length in local scan.", - IGRAPH_EINVAL); - } - - return igraph_local_scan_subset_ecount(graph, res, weights, neighborhoods); + return 0; } diff --git a/src/vendor/cigraph/src/misc/sir.c b/src/vendor/cigraph/src/misc/sir.c index 0e63307c16b..4adb12a1d3a 100644 --- a/src/vendor/cigraph/src/misc/sir.c +++ b/src/vendor/cigraph/src/misc/sir.c @@ -32,7 +32,7 @@ #include "core/interruption.h" -igraph_error_t igraph_sir_init(igraph_sir_t *sir) { +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)); @@ -41,7 +41,7 @@ igraph_error_t igraph_sir_init(igraph_sir_t *sir) { IGRAPH_FINALLY(igraph_vector_int_destroy, &sir->no_i); IGRAPH_CHECK(igraph_vector_int_init(&sir->no_r, 1)); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -59,7 +59,7 @@ void igraph_sir_destroy(igraph_sir_t *sir) { } static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { - igraph_integer_t i, n = igraph_vector_ptr_size(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]) ; @@ -80,7 +80,7 @@ static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { * 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 neighbors. Infected people become recovered + * their number of infected neigbors. Infected people become recovered * with a constant rate. See these parameters below. * * @@ -109,24 +109,27 @@ static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { * Time complexity: O(no_sim * (|V| + |E| log(|V|))). */ -igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, +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) { - igraph_integer_t infected; + int infected; igraph_vector_int_t status; igraph_adjlist_t adjlist; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j, ns, ni, nr; + 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; - igraph_integer_t neilen; + 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); } @@ -142,16 +145,6 @@ igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, if (!simple) { IGRAPH_ERROR("SIR model only works with simple graphs.", IGRAPH_EINVAL); } - if (igraph_is_directed(graph)) { - igraph_bool_t has_mutual; - IGRAPH_WARNING("Edge directions are ignored in SIR model."); - /* When the graph is directed, mutual edges are effectively multi-edges as we - * are ignoring edge directions. */ - IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); - if (has_mutual) { - 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); @@ -166,7 +159,7 @@ igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, 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); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot run SIR model.", IGRAPH_ENOMEM); } IGRAPH_CHECK(igraph_sir_init(sir)); VECTOR(*result)[i] = sir; @@ -205,14 +198,14 @@ igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, neis = igraph_adjlist_get(&adjlist, infected); neilen = igraph_vector_int_size(neis); for (i = 0; i < neilen; i++) { - igraph_integer_t nei = VECTOR(*neis)[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; - igraph_integer_t vchange; + long int vchange; IGRAPH_ALLOW_INTERRUPTION(); @@ -229,7 +222,7 @@ igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, ni--; nr++; IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, 0.0)); for (i = 0; i < neilen; i++) { - igraph_integer_t nei = VECTOR(*neis)[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)); @@ -241,7 +234,7 @@ igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, ns--; ni++; IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, gamma)); for (i = 0; i < neilen; i++) { - igraph_integer_t nei = VECTOR(*neis)[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)); diff --git a/src/vendor/cigraph/src/misc/spanning_trees.c b/src/vendor/cigraph/src/misc/spanning_trees.c index fd3c45e52c7..89d5aaf7a6c 100644 --- a/src/vendor/cigraph/src/misc/spanning_trees.c +++ b/src/vendor/cigraph/src/misc/spanning_trees.c @@ -1,6 +1,9 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2011-2023 The igraph development team + 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 @@ -13,7 +16,10 @@ GNU 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 . + 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" @@ -22,38 +28,40 @@ #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 igraph_error_t igraph_i_minimum_spanning_tree_unweighted( - const igraph_t *graph, igraph_vector_int_t *result); -static igraph_error_t igraph_i_minimum_spanning_tree_prim( - const igraph_t *graph, igraph_vector_int_t *result, const igraph_vector_t *weights); +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. * - * Finds a spanning tree of the graph. 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. + * + * 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. * * - * This function is deterministic, i.e. it always returns the same - * spanning tree. See \ref igraph_random_spanning_tree() for the uniform - * random sampling of spanning trees of a graph. + * 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_from_edges() to extract the spanning tree as + * \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 @@ -72,10 +80,9 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( * * \example examples/simple/igraph_minimum_spanning_tree.c */ -igraph_error_t igraph_minimum_spanning_tree( - const igraph_t *graph, igraph_vector_int_t *res, const igraph_vector_t *weights -) { - if (weights == NULL) { +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)); @@ -88,6 +95,7 @@ igraph_error_t igraph_minimum_spanning_tree( * \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. @@ -99,8 +107,7 @@ igraph_error_t igraph_minimum_spanning_tree( * 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. Edge directions will be ignored. + * \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 @@ -119,19 +126,19 @@ igraph_error_t igraph_minimum_spanning_tree( * edges that constitute the spanning tree. */ -igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, +int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst) { - igraph_vector_int_t edges; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; igraph_integer_t no_of_nodes = igraph_vcount(graph); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, &edges)); - IGRAPH_CHECK(igraph_subgraph_from_edges( - graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, + igraph_ess_vector(&edges), /* delete_vertices = */ 0)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -139,24 +146,26 @@ igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, * \function igraph_minimum_spanning_tree_prim * \brief Calculates one minimum spanning tree of a weighted graph. * - * Finds a spanning tree or spanning forest for which the sum of edge - * weights is the smallest. This function uses Prim's method for carrying - * out the computation. + * + * 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. * * - * Directed graphs are considered as undirected for this computation. + * If the graph has more than one minimum spanning tree, the current + * implementation returns always the same one. * * - * Reference: + * Directed graphs are considered as undirected for this computation. * * - * Prim, R.C.: Shortest connection networks and some - * generalizations, Bell System Technical - * Journal, Vol. 36, - * 1957, 1389--1401. - * https://doi.org/10.1002/j.1538-7305.1957.tb01515.x + * 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. Edge directions will be ignored. + * \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 @@ -182,166 +191,171 @@ igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, * \example examples/simple/igraph_minimum_spanning_tree.c */ -igraph_error_t igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, +int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, const igraph_vector_t *weights) { - igraph_vector_int_t edges; + igraph_vector_t edges = IGRAPH_VECTOR_NULL; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); + IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, &edges, weights)); - IGRAPH_CHECK(igraph_subgraph_from_edges( - graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_subgraph_edges(graph, mst, + igraph_ess_vector(&edges), /* delete_vertices = */ 0)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_int_t* res) { +static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_t* res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - bool *already_added, *added_edges; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + char *already_added; + char *added_edges; - igraph_dqueue_int_t q; - igraph_vector_int_t eids; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + long int i, j; - igraph_vector_int_clear(res); + igraph_vector_clear(res); - added_edges = IGRAPH_CALLOC(no_of_edges, bool); - IGRAPH_CHECK_OOM(added_edges, "Insufficient memory for unweighted spanning tree."); + 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, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for unweighted spanning tree."); + 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); - - /* Perform a BFS */ - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - if (already_added[i]) { + for (i = 0; i < no_of_nodes; i++) { + if (already_added[i] > 0) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[i] = true; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); - while (! igraph_dqueue_int_empty(&q)) { - igraph_integer_t eids_size; - igraph_integer_t act_node = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_incident(graph, &eids, act_node, + 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)); - eids_size = igraph_vector_int_size(&eids); - for (igraph_integer_t j = 0; j < eids_size; j++) { - igraph_integer_t edge = VECTOR(eids)[j]; - if (! added_edges[edge]) { + 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[to]) { - already_added[to] = true; - added_edges[edge] = true; - IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, to)); + 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_int_destroy(&q); - igraph_vector_int_destroy(&eids); + igraph_dqueue_destroy(&q); IGRAPH_FREE(already_added); + igraph_vector_destroy(&tmp); IGRAPH_FREE(added_edges); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_minimum_spanning_tree_prim( - const igraph_t* graph, igraph_vector_int_t* res, const igraph_vector_t *weights) { +static int igraph_i_minimum_spanning_tree_prim( + const igraph_t* graph, igraph_vector_t* res, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - bool *already_added, *added_edges; + 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; - const igraph_neimode_t mode = IGRAPH_ALL; + igraph_d_indheap_t heap = IGRAPH_D_INDHEAP_NULL; + igraph_integer_t mode = IGRAPH_ALL; - igraph_vector_int_t adj; + igraph_vector_t adj; - igraph_vector_int_clear(res); + long int i, j; - if (weights == NULL) { + 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("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid weights length", IGRAPH_EINVAL); } - if (igraph_vector_is_any_nan(weights)) { - IGRAPH_ERROR("Weigths must not contain NaN values.", IGRAPH_EINVAL); + added_edges = IGRAPH_CALLOC(no_of_edges, char); + if (added_edges == 0) { + IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM); } - - added_edges = IGRAPH_CALLOC(no_of_edges, bool); - IGRAPH_CHECK_OOM(added_edges, "Insufficient memory for minimum spanning tree calculation."); IGRAPH_FINALLY(igraph_free, added_edges); - - already_added = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for minimum spanning tree calculation."); + 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); - IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); - - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t adj_size; - if (already_added[i]) { + for (i = 0; i < no_of_nodes; i++) { + long int adj_size; + if (already_added[i] > 0) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[i] = true; + already_added[i] = 1; /* add all edges of the first vertex */ - IGRAPH_CHECK(igraph_incident(graph, &adj, i, mode)); - adj_size = igraph_vector_int_size(&adj); - for (igraph_integer_t j = 0; j < adj_size; j++) { - igraph_integer_t edgeno = VECTOR(adj)[j]; + 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[neighbor]) { - IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], i, edgeno)); + 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 */ - igraph_integer_t from, 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]) { + if (added_edges[edge] == 0) { igraph_integer_t to = IGRAPH_OTHER(graph, edge, from); /* Does it point to a visited node? */ - if (! already_added[to]) { - already_added[to] = true; - added_edges[edge] = true; - IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); + 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_CHECK(igraph_incident(graph, &adj, to, mode)); - adj_size = igraph_vector_int_size(&adj); - for (igraph_integer_t j = 0; j < adj_size; j++) { - igraph_integer_t edgeno = VECTOR(adj)[j]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, to); - if (! already_added[neighbor]) { - IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], to, edgeno)); + 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 */ @@ -351,7 +365,7 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( igraph_d_indheap_destroy(&heap); IGRAPH_FREE(already_added); - igraph_vector_int_destroy(&adj); + igraph_vector_destroy(&adj); IGRAPH_FREE(added_edges); IGRAPH_FINALLY_CLEAN(4); @@ -368,19 +382,19 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( * The walk is started from vertex start. comp_size must be the size of the connected * component containing start. */ -static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t 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_int_reserve(res, igraph_vector_int_size(res) + comp_size - 1)); + IGRAPH_CHECK(igraph_vector_reserve(res, igraph_vector_size(res) + comp_size - 1)); + + RNG_BEGIN(); VECTOR(*visited)[start] = 1; visited_count = 1; - RNG_BEGIN(); - while (visited_count < comp_size) { - igraph_integer_t degree, edge; + long degree, edge; igraph_vector_int_t *edges; edges = igraph_inclist_get(il, start); @@ -394,7 +408,7 @@ static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_t * /* if the next vertex hasn't been visited yet, register the edge we just traversed */ if (! VECTOR(*visited)[start]) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); + IGRAPH_CHECK(igraph_vector_push_back(res, edge)); VECTOR(*visited)[start] = 1; visited_count++; } @@ -409,7 +423,7 @@ static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_t * /** * \function igraph_random_spanning_tree - * \brief Uniformly samples the spanning trees of a graph. + * \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. @@ -424,7 +438,7 @@ static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_t * * \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_from_edges() to extract the spanning tree as + * \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 @@ -437,13 +451,13 @@ static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_t * * \sa \ref igraph_minimum_spanning_tree(), \ref igraph_random_walk() * */ -igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vid) { +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_ERROR("Invalid vertex id given for random spanning tree", IGRAPH_EINVVID); } IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); @@ -452,40 +466,41 @@ igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_ IGRAPH_CHECK(igraph_vector_bool_init(&visited, vcount)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); - igraph_vector_int_clear(res); + igraph_vector_clear(res); if (vid < 0) { /* generate random spanning forest: consider each component separately */ - igraph_vector_int_t membership, csize; + igraph_vector_t membership, csize; igraph_integer_t comp_count; + igraph_integer_t i; - IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); + IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); - IGRAPH_CHECK(igraph_connected_components(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_clusters(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); /* for each component ... */ - for (igraph_integer_t i = 0; i < comp_count; ++i) { + 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, VECTOR(csize)[i], &visited, &il)); + IGRAPH_CHECK(igraph_i_lerw(graph, res, j, (igraph_integer_t) VECTOR(csize)[i], &visited, &il)); } - igraph_vector_int_destroy(&membership); - igraph_vector_int_destroy(&csize); + igraph_vector_destroy(&membership); + igraph_vector_destroy(&csize); IGRAPH_FINALLY_CLEAN(2); } else { /* consider the component containing vid */ - igraph_vector_int_t comp_vertices; + igraph_vector_t comp_vertices; igraph_integer_t comp_size; /* we measure the size of the component */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&comp_vertices, 0); + IGRAPH_VECTOR_INIT_FINALLY(&comp_vertices, 0); IGRAPH_CHECK(igraph_subcomponent(graph, &comp_vertices, vid, IGRAPH_ALL)); - comp_size = igraph_vector_int_size(&comp_vertices); - igraph_vector_int_destroy(&comp_vertices); + 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)); diff --git a/src/vendor/cigraph/src/operators/add_edge.c b/src/vendor/cigraph/src/operators/add_edge.c index 70a5be54ca8..2f5bbcea87d 100644 --- a/src/vendor/cigraph/src/operators/add_edge.c +++ b/src/vendor/cigraph/src/operators/add_edge.c @@ -48,16 +48,17 @@ * Time complexity: O(|V|+|E|), the number of edges plus the number of * vertices. */ -igraph_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { - igraph_vector_int_t edges; +int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { + igraph_vector_t edges; + int ret; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2); VECTOR(edges)[0] = from; VECTOR(edges)[1] = to; - IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); + IGRAPH_CHECK(ret = igraph_add_edges(graph, &edges, 0)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return ret; } diff --git a/src/vendor/cigraph/src/operators/complementer.c b/src/vendor/cigraph/src/operators/complementer.c index 569617afe20..fe711882bc0 100644 --- a/src/vendor/cigraph/src/operators/complementer.c +++ b/src/vendor/cigraph/src/operators/complementer.c @@ -30,9 +30,9 @@ /** * \function igraph_complementer - * \brief Creates the complementer of a graph. + * \brief Create the complementer of a graph * - * The complementer graph means that all edges which are + * 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. @@ -48,17 +48,17 @@ * * \example examples/simple/igraph_complementer.c */ -igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, +int igraph_complementer(igraph_t *res, const igraph_t *graph, igraph_bool_t loops) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t edges; - igraph_vector_int_t neis; - igraph_integer_t i, j; - igraph_integer_t zero = 0, *limit; + 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_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); if (igraph_is_directed(graph)) { limit = &zero; @@ -68,35 +68,37 @@ igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, for (i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); + 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_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, 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_int_pop_back(&neis); + igraph_vector_pop_back(&neis); } } } else { for (j = no_of_nodes - 1; j >= *limit; j--) { - if (igraph_vector_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { + if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) { if (i != j) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); } } else { - igraph_vector_int_pop_back(&neis); + igraph_vector_pop_back(&neis); } } } } - IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); - igraph_vector_int_destroy(&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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/compose.c b/src/vendor/cigraph/src/operators/compose.c index 625c1021990..ad7aa710836 100644 --- a/src/vendor/cigraph/src/operators/compose.c +++ b/src/vendor/cigraph/src/operators/compose.c @@ -64,16 +64,16 @@ * * \example examples/simple/igraph_compose.c */ -igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { +int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { - igraph_integer_t no_of_nodes_left = igraph_vcount(g1); - igraph_integer_t no_of_nodes_right = igraph_vcount(g2); - igraph_integer_t no_of_nodes; + 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_int_t edges; - igraph_vector_int_t neis1, neis2; - igraph_integer_t i; + 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", @@ -83,52 +83,53 @@ igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t no_of_nodes = no_of_nodes_left > no_of_nodes_right ? no_of_nodes_left : no_of_nodes_right; - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis2, 0); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INIT_FINALLY(&neis2, 0); if (edge_map1) { - igraph_vector_int_clear(edge_map1); + igraph_vector_clear(edge_map1); } if (edge_map2) { - igraph_vector_int_clear(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, i, + IGRAPH_CHECK(igraph_incident(g1, &neis1, (igraph_integer_t) i, IGRAPH_OUT)); - while (!igraph_vector_int_empty(&neis1)) { - igraph_integer_t con = igraph_vector_int_pop_back(&neis1); - igraph_integer_t v1 = IGRAPH_OTHER(g1, con, i); + 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, v1, + IGRAPH_CHECK(igraph_incident(g2, &neis2, (igraph_integer_t) v1, IGRAPH_OUT)); } else { continue; } - while (!igraph_vector_int_empty(&neis2)) { - igraph_integer_t con2 = igraph_vector_int_pop_back(&neis2); - igraph_integer_t v2 = IGRAPH_OTHER(g2, con2, v1); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v2)); + 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_int_push_back(edge_map1, con)); + IGRAPH_CHECK(igraph_vector_push_back(edge_map1, con)); } if (edge_map2) { - IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, con2)); + IGRAPH_CHECK(igraph_vector_push_back(edge_map2, con2)); } } } } - igraph_vector_int_destroy(&neis1); - igraph_vector_int_destroy(&neis2); + igraph_vector_destroy(&neis1); + igraph_vector_destroy(&neis2); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + directed)); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/connect_neighborhood.c b/src/vendor/cigraph/src/operators/connect_neighborhood.c index af382f49088..1ab6f2a8da7 100644 --- a/src/vendor/cigraph/src/operators/connect_neighborhood.c +++ b/src/vendor/cigraph/src/operators/connect_neighborhood.c @@ -28,7 +28,7 @@ /** * \function igraph_connect_neighborhood - * \brief Graph power: connect each vertex to its 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 @@ -53,25 +53,24 @@ * considered as an undirected one. * \return Error code. * - * \sa \ref igraph_square_lattice() uses this function to connect the + * \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. */ -igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, +int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; - igraph_vector_int_t edges; - igraph_integer_t i, j, in; - igraph_integer_t *added; - igraph_vector_int_t neis; + 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_ERRORF("Order can not be negative, found %" IGRAPH_PRId ".", - IGRAPH_EINVAL, order); + IGRAPH_ERROR("Negative order, cannot connect neighborhood", IGRAPH_EINVAL); } if (order < 2) { @@ -82,65 +81,65 @@ igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t ord mode = IGRAPH_ALL; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + added = IGRAPH_CALLOC(no_of_nodes, long int); if (added == 0) { - IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_CHECK(igraph_neighbors(graph, &neis, i, mode)); - in = igraph_vector_int_size(&neis); + igraph_neighbors(graph, &neis, (igraph_integer_t) i, mode); + in = igraph_vector_size(&neis); if (order > 1) { for (j = 0; j < in; j++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; added[nei] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 1)); + igraph_dqueue_push(&q, nei); + igraph_dqueue_push(&q, 1); } } - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - igraph_integer_t n; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 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_int_push_back(&edges, nei)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); } else { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); } } } } } else { for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(neis)[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_int_push_back(&edges, nei)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); } else { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); } } } @@ -150,15 +149,15 @@ igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t ord } /* while q not empty */ } /* for i < no_of_nodes */ - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&q); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/contract.c b/src/vendor/cigraph/src/operators/contract.c index ea0787dab02..af2fbe2c273 100644 --- a/src/vendor/cigraph/src/operators/contract.c +++ b/src/vendor/cigraph/src/operators/contract.c @@ -24,29 +24,33 @@ #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 - * \brief Replace multiple vertices with a single one. - * - * This function modifies the graph by merging several vertices - * into one. The vertices in the modified graph correspond - * to groups of vertices in the input graph. No edges are removed, - * thus the modified graph will typically have self-loops - * (corresponding to in-group edges) and multi-edges - * (corresponding to multiple connections between two groups). - * Use \ref igraph_simplify() to eliminate self-loops and - * merge multi-edges. + * Replace multiple vertices with a single one. * - * \param graph The input graph. It will be modified in-place. + * 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 desired ID in the result graph. In order to create - * "orphan vertices" that have no corresponding vertices - * in the original graph, ensure that the IDs are consecutive - * integers starting from zero. + * 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 @@ -58,39 +62,38 @@ * or vertices plus edges. */ -igraph_error_t igraph_contract_vertices(igraph_t *graph, - const igraph_vector_int_t *mapping, +int igraph_contract_vertices(igraph_t *graph, + const igraph_vector_t *mapping, const igraph_attribute_combination_t *vertex_comb) { - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_integer_t e, last = -1; - igraph_integer_t no_new_vertices; + long int e, last = -1; + long int no_new_vertices; - if (igraph_vector_int_size(mapping) != no_of_nodes) { - IGRAPH_ERRORF("Mapping vector length (%" IGRAPH_PRId ") " - "not equal to number of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_int_size(mapping), no_of_nodes); + if (igraph_vector_size(mapping) != no_of_nodes) { + IGRAPH_ERROR("Invalid mapping vector length", + IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); if (no_of_nodes > 0) { - last = igraph_vector_int_max(mapping); + last = (long int) igraph_vector_max(mapping); } for (e = 0; e < no_of_edges; e++) { - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); + long int from = IGRAPH_FROM(graph, e); + long int to = IGRAPH_TO(graph, e); - igraph_integer_t nfrom = VECTOR(*mapping)[from]; - igraph_integer_t nto = VECTOR(*mapping)[to]; + long int nfrom = (long int) VECTOR(*mapping)[from]; + long int nto = (long int) VECTOR(*mapping)[to]; - igraph_vector_int_push_back(&edges, nfrom); - igraph_vector_int_push_back(&edges, nto); + igraph_vector_push_back(&edges, nfrom); + igraph_vector_push_back(&edges, nto); if (nfrom > last) { last = nfrom; @@ -102,10 +105,10 @@ igraph_error_t igraph_contract_vertices(igraph_t *graph, no_new_vertices = last + 1; - IGRAPH_CHECK(igraph_create(&res, &edges, no_new_vertices, + IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_new_vertices, igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &res); @@ -115,32 +118,50 @@ igraph_error_t igraph_contract_vertices(igraph_t *graph, /*vertex=*/ 0, /*edge=*/ 1); if (vattr) { - igraph_integer_t i; - igraph_vector_int_list_t merges; - igraph_vector_int_t sizes; - - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&merges, no_new_vertices); - IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, no_new_vertices); + 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++) { - igraph_integer_t to = VECTOR(*mapping)[i]; - igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&merges, to); + long int to = (long int) VECTOR(*mapping)[i]; VECTOR(sizes)[to] += 1; - IGRAPH_CHECK(igraph_vector_int_push_back(v, i)); + } + 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_int_destroy(&sizes); - igraph_vector_int_list_destroy(&merges); - IGRAPH_FINALLY_CLEAN(2); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/difference.c b/src/vendor/cigraph/src/operators/difference.c index f9c5786d65a..c229068f441 100644 --- a/src/vendor/cigraph/src/operators/difference.c +++ b/src/vendor/cigraph/src/operators/difference.c @@ -31,8 +31,9 @@ /** * \function igraph_difference - * \brief Calculates the difference of two graphs. + * \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 @@ -52,32 +53,32 @@ * * \example examples/simple/igraph_difference.c */ -igraph_error_t igraph_difference(igraph_t *res, +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. */ + vertex ids at the "other" end of the edge. */ - igraph_integer_t no_of_nodes_orig = igraph_vcount(orig); - igraph_integer_t no_of_nodes_sub = igraph_vcount(sub); - igraph_integer_t no_of_nodes = no_of_nodes_orig; - igraph_integer_t smaller_nodes; + 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_int_t edges; - igraph_vector_int_t edge_ids; + igraph_vector_t edges; + igraph_vector_t edge_ids; igraph_vector_int_t *nei1, *nei2; igraph_inclist_t inc_orig, inc_sub; - igraph_integer_t i; + long int i; igraph_integer_t v1, v2; if (directed != igraph_is_directed(sub)) { - IGRAPH_ERROR("Cannot subtract directed and undirected graphs.", + IGRAPH_ERROR("Cannot subtract directed and undirected graphs", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_ids, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + 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)); @@ -87,15 +88,15 @@ igraph_error_t igraph_difference(igraph_t *res, no_of_nodes_sub : no_of_nodes_orig; for (i = 0; i < smaller_nodes; i++) { - igraph_integer_t n1, n2, e1, e2; + 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 = VECTOR(*nei1)[n1]; - e2 = VECTOR(*nei2)[n2]; + e1 = (long int) VECTOR(*nei1)[n1]; + e2 = (long int) VECTOR(*nei2)[n2]; v1 = IGRAPH_OTHER(orig, e1, i); v2 = IGRAPH_OTHER(sub, e2, i); @@ -104,9 +105,9 @@ igraph_error_t igraph_difference(igraph_t *res, } else if (!directed && v2 < i) { n2--; } else if (v1 > v2) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); + 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) { @@ -122,12 +123,12 @@ igraph_error_t igraph_difference(igraph_t *res, /* Copy remaining edges */ while (n1 >= 0) { - e1 = VECTOR(*nei1)[n1]; + e1 = (long int) VECTOR(*nei1)[n1]; v1 = IGRAPH_OTHER(orig, e1, i); if (directed || v1 >= i) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); + 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) { @@ -140,16 +141,16 @@ igraph_error_t igraph_difference(igraph_t *res, /* copy remaining edges, use the previous value of 'i' */ for (; i < no_of_nodes_orig; i++) { - igraph_integer_t n1, e1; + long int n1, e1; nei1 = igraph_inclist_get(&inc_orig, i); n1 = igraph_vector_int_size(nei1) - 1; while (n1 >= 0) { - e1 = VECTOR(*nei1)[n1]; + e1 = (long int) VECTOR(*nei1)[n1]; v1 = IGRAPH_OTHER(orig, e1, i); if (directed || v1 >= i) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); + 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) { @@ -163,21 +164,20 @@ igraph_error_t igraph_difference(igraph_t *res, igraph_inclist_destroy(&inc_sub); igraph_inclist_destroy(&inc_orig); IGRAPH_FINALLY_CLEAN(2); - - IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); - - igraph_vector_int_destroy(&edges); + 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=*/ true, /*vertex=*/ true, /*edge=*/ false); + 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_int_destroy(&edge_ids); + igraph_vector_destroy(&edge_ids); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/disjoint_union.c b/src/vendor/cigraph/src/operators/disjoint_union.c index c8cedb8394b..0ea11a945bf 100644 --- a/src/vendor/cigraph/src/operators/disjoint_union.c +++ b/src/vendor/cigraph/src/operators/disjoint_union.c @@ -25,12 +25,15 @@ #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. + * \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 + * 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 @@ -57,53 +60,52 @@ * * \example examples/simple/igraph_disjoint_union.c */ -igraph_error_t igraph_disjoint_union(igraph_t *res, const igraph_t *left, +int igraph_disjoint_union(igraph_t *res, const igraph_t *left, const igraph_t *right) { - igraph_integer_t no_of_nodes_left = igraph_vcount(left); - igraph_integer_t no_of_nodes_right = igraph_vcount(right); - igraph_integer_t no_of_edges_left = igraph_ecount(left); - igraph_integer_t no_of_edges_right = igraph_ecount(right); - igraph_vector_int_t edges; + 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; - igraph_integer_t i; + long int i; if (directed_left != igraph_is_directed(right)) { - IGRAPH_ERROR("Cannot create disjoint union of directed and undirected graphs.", + IGRAPH_ERROR("Cannot union directed and undirected graphs", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, + 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, i, &from, &to); - igraph_vector_int_push_back(&edges, from); /* reserved */ - igraph_vector_int_push_back(&edges, to); /* reserved */ + 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, i, &from, &to); - igraph_vector_int_push_back(&edges, from + no_of_nodes_left); /* reserved */ - igraph_vector_int_push_back(&edges, to + no_of_nodes_left); /* reserved */ + 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_CHECK(igraph_create(res, &edges, (igraph_integer_t) (no_of_nodes_left + no_of_nodes_right), directed_left)); - - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + 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 + * 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. @@ -128,15 +130,15 @@ igraph_error_t igraph_disjoint_union(igraph_t *res, const igraph_t *left, * Time complexity: O(|V|+|E|), the number of vertices plus the number * of edges in the result. */ -igraph_error_t igraph_disjoint_union_many(igraph_t *res, +int igraph_disjoint_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs) { - igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); - igraph_bool_t directed = true; - igraph_vector_int_t edges; - igraph_integer_t no_of_edges = 0; - igraph_integer_t shift = 0; + 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; - igraph_integer_t i, j; + long int i, j; igraph_integer_t from, to; if (no_of_graphs != 0) { @@ -146,31 +148,29 @@ igraph_error_t igraph_disjoint_union_many(igraph_t *res, graph = VECTOR(*graphs)[i]; no_of_edges += igraph_ecount(graph); if (directed != igraph_is_directed(graph)) { - IGRAPH_ERROR("Cannot create disjoint union of directed and undirected graphs.", + IGRAPH_ERROR("Cannot union directed and undirected graphs", IGRAPH_EINVAL); } } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * no_of_edges)); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges)); for (i = 0; i < no_of_graphs; i++) { - igraph_integer_t ec; + long int ec; graph = VECTOR(*graphs)[i]; ec = igraph_ecount(graph); for (j = 0; j < ec; j++) { - igraph_edge(graph, j, &from, &to); - igraph_vector_int_push_back(&edges, from + shift); /* reserved */ - igraph_vector_int_push_back(&edges, to + shift); /* reserved */ + 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, shift, directed)); - - igraph_vector_int_destroy(&edges); + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) shift, directed)); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/intersection.c b/src/vendor/cigraph/src/operators/intersection.c index 9e5a9aeda68..610c3cd1a8a 100644 --- a/src/vendor/cigraph/src/operators/intersection.c +++ b/src/vendor/cigraph/src/operators/intersection.c @@ -25,39 +25,31 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_interface.h" +#include "igraph_memory.h" #include "igraph_qsort.h" -#include "igraph_vector_list.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. * - * - * The directedness of the operand graphs must be the same. - * - * - * Edge multiplicities are handled by taking the \em smaller of the two - * multiplicities in the input graphs. In other words, if the first graph - * has N edges between a vertex pair (u, v) and the second graph has M edges, - * the result graph will have min(N, M) edges between them. - * * \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 vector. + * \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. For the edges that - * are not in the intersection, -1 is stored. - * \param edge_map2 Null pointer, or an initialized vector. The same - * as \p edge_map1, but for the \p right input graph. For the edges that - * are not in the intersection, -1 is stored. + * 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 @@ -69,10 +61,10 @@ * * \example examples/simple/igraph_intersection.c */ -igraph_error_t igraph_intersection(igraph_t *res, +int igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, - igraph_vector_int_t *edge_map2) { + 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); } @@ -81,6 +73,7 @@ igraph_error_t igraph_intersection(igraph_t *res, * \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. @@ -89,25 +82,15 @@ igraph_error_t igraph_intersection(igraph_t *res, * The number of vertices in the result graph will be the maximum * number of vertices in the argument graphs. * - * - * The directedness of the argument graphs must be the same. - * If the graph list has length zero, the result will be a \em directed - * graph with no vertices. - * - * - * Edge multiplicities are handled by taking the \em minimum multiplicity of the - * all multiplicities for the same vertex pair (u, v) in the input graphs; this - * will be the multiplicity of (u, v) in the result graph. - * * \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 - * list of integer vectors, and the mappings of edges from the graphs to - * the result graph will be stored here, in the same order as + * 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_int_t object. For the edges that are not in + * \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, @@ -118,21 +101,19 @@ igraph_error_t igraph_intersection(igraph_t *res, * |E| is the number of edges in the smallest graph (i.e. the graph having * the less vertices). */ -igraph_error_t igraph_intersection_many( - igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_int_list_t *edgemaps -) { +int igraph_intersection_many(igraph_t *res, + const igraph_vector_ptr_t *graphs, + igraph_vector_ptr_t *edgemaps) { - igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); - igraph_integer_t no_of_nodes = 0; - igraph_bool_t directed = true; - igraph_vector_int_t edges; - igraph_vector_int_list_t edge_vects, order_vects; - igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; - igraph_vector_int_t no_edges; - igraph_bool_t allne = no_of_graphs > 0; - igraph_bool_t allsame = false; - igraph_integer_t idx = 0; + 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) { @@ -140,18 +121,24 @@ igraph_error_t igraph_intersection_many( } for (i = 1; i < no_of_graphs; i++) { if (directed != igraph_is_directed(VECTOR(*graphs)[i])) { - IGRAPH_ERROR("Cannot create intersection of directed and undirected graphs.", + IGRAPH_ERROR("Cannot intersect directed and undirected graphs", IGRAPH_EINVAL); } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &no_edges); + 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++) { - igraph_integer_t n = igraph_vcount(VECTOR(*graphs)[i]); + long int n = igraph_vcount(VECTOR(*graphs)[i]); if (n > no_of_nodes) { no_of_nodes = n; } @@ -160,38 +147,55 @@ igraph_error_t igraph_intersection_many( } if (edgemaps) { - IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); for (i = 0; i < no_of_graphs; i++) { - igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); - IGRAPH_CHECK(igraph_vector_int_resize(v, VECTOR(no_edges)[i])); - igraph_vector_int_fill(v, -1); + 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 */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); + 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++) { - igraph_integer_t k, j, n = VECTOR(no_edges)[i]; - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); - IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); + 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(*ev)[j] > VECTOR(*ev)[j + 1]) { - igraph_integer_t tmp = VECTOR(*ev)[j]; - VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; - VECTOR(*ev)[j + 1] = tmp; + 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; } } } - IGRAPH_CHECK(igraph_vector_int_resize(order, n)); for (k = 0; k < n; k++) { VECTOR(*order)[k] = k; } - igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, igraph_i_order_edgelist_cmp); } @@ -204,12 +208,11 @@ igraph_error_t igraph_intersection_many( while (allne) { /* Look for the smallest tail element */ - for (j = 0, tailfrom = IGRAPH_INTEGER_MAX, tailto = IGRAPH_INTEGER_MAX; j < no_of_graphs; j++) { - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); - igraph_integer_t edge = igraph_vector_int_tail(order); - igraph_integer_t from = VECTOR(*ev)[2 * edge]; - igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + 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; } @@ -218,17 +221,16 @@ igraph_error_t igraph_intersection_many( /* 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++) { - igraph_integer_t from = -1, to = -1; - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + long int from = -1, to = -1; while (1) { - igraph_integer_t edge = igraph_vector_int_tail(order); - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + 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_int_pop_back(order); - if (igraph_vector_int_empty(order)) { - allne = false; + igraph_vector_long_pop_back(VECTOR(order_vects)[j]); + if (igraph_vector_long_empty(VECTOR(order_vects)[j])) { + allne = 0; break; } } else { @@ -243,26 +245,25 @@ igraph_error_t igraph_intersection_many( /* Add the edge, if the smallest tail element was present in all graphs. */ if (allsame) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailto)); + 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++) { - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); - igraph_integer_t edge = igraph_vector_int_tail(order); - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); - igraph_integer_t from = VECTOR(*ev)[2 * edge]; - igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + 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_int_pop_back(order); - if (igraph_vector_int_empty(order)) { - allne = false; + 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_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); + igraph_vector_t *map = VECTOR(*edgemaps)[j]; VECTOR(*map)[edge] = idx; } } @@ -274,14 +275,22 @@ igraph_error_t igraph_intersection_many( } /* while allne */ - igraph_vector_int_list_destroy(&order_vects); - igraph_vector_int_list_destroy(&edge_vects); - igraph_vector_int_destroy(&no_edges); - IGRAPH_FINALLY_CLEAN(3); + 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, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/misc_internal.c b/src/vendor/cigraph/src/operators/misc_internal.c index f57d88d35d0..d2a48638aae 100644 --- a/src/vendor/cigraph/src/operators/misc_internal.c +++ b/src/vendor/cigraph/src/operators/misc_internal.c @@ -25,21 +25,44 @@ #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_int_t *edgelist = edges; - igraph_integer_t edge1 = (*(const igraph_integer_t*) e1) * 2; - igraph_integer_t edge2 = (*(const igraph_integer_t*) e2) * 2; - igraph_integer_t from1 = VECTOR(*edgelist)[edge1]; - igraph_integer_t from2 = VECTOR(*edgelist)[edge2]; + 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 { - igraph_integer_t to1 = VECTOR(*edgelist)[edge1 + 1]; - igraph_integer_t to2 = VECTOR(*edgelist)[edge2 + 1]; + long int to1 = VECTOR(*edgelist)[edge1 + 1]; + long int to2 = VECTOR(*edgelist)[edge2 + 1]; if (to1 < to2) { return -1; } else if (to1 > to2) { @@ -50,57 +73,54 @@ int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2) { } } -igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, +int igraph_i_merge(igraph_t *res, int mode, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { + igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { - igraph_integer_t no_of_nodes_left = igraph_vcount(left); - igraph_integer_t no_of_nodes_right = igraph_vcount(right); - igraph_integer_t no_of_nodes; - igraph_integer_t no_edges_left = igraph_ecount(left); - igraph_integer_t no_edges_right = igraph_ecount(right); + 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_int_t edges; - igraph_vector_int_t edges1, edges2; - igraph_vector_int_t order1, order2; - igraph_integer_t i, j, eptr = 0; - igraph_integer_t idx1, idx2, edge1 = -1, edge2 = -1, from1 = -1, from2 = -1, to1 = -1, to2 = -1; + 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 create union or intersection of directed and undirected graph.", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot make union or intersection of directed " + "and undirected graph", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges1, no_edges_left * 2); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges2, no_edges_right * 2); - IGRAPH_CHECK(igraph_vector_int_init(&order1, no_edges_left)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &order1); - IGRAPH_CHECK(igraph_vector_int_init(&order2, no_edges_right)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &order2); + 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_int_resize(edge_map1, no_edges_left)); + IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left)); break; case IGRAPH_MERGE_MODE_INTERSECTION: - igraph_vector_int_clear(edge_map1); + igraph_vector_clear(edge_map1); break; - default: - IGRAPH_FATAL("Invalid merge mode."); } } if (edge_map2) { switch (mode) { case IGRAPH_MERGE_MODE_UNION: - IGRAPH_CHECK(igraph_vector_int_resize(edge_map2, no_edges_right)); + IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right)); break; case IGRAPH_MERGE_MODE_INTERSECTION: - igraph_vector_int_clear(edge_map2); + igraph_vector_clear(edge_map2); break; - default: - IGRAPH_FATAL("Invalid merge mode."); } } @@ -109,22 +129,22 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, /* 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, the larger (non-smaller) vertex ID is in the + for every edge, that larger (non-smaller) vertex id is in the second column. */ - IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ false)); - IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ false)); + 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]) { - igraph_integer_t tmp = VECTOR(edges1)[j]; + 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]) { - igraph_integer_t tmp = VECTOR(edges2)[j]; + long int tmp = VECTOR(edges2)[j]; VECTOR(edges2)[j] = VECTOR(edges2)[j + 1]; VECTOR(edges2)[j + 1] = tmp; } @@ -176,8 +196,8 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, (idx1 < no_edges_left && from1 == from2 && to1 < to2)) { /* Edge from first graph */ if (mode == IGRAPH_MERGE_MODE_UNION) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); if (edge_map1) { VECTOR(*edge_map1)[edge1] = eptr; } @@ -189,8 +209,8 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, (idx2 < no_edges_right && from1 == from2 && to2 < to1)) { /* Edge from second graph */ if (mode == IGRAPH_MERGE_MODE_UNION) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from2)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to2)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, from2)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, to2)); if (edge_map2) { VECTOR(*edge_map2)[edge2] = eptr; } @@ -199,8 +219,8 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, INC2(); } else { /* Edge from both */ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to1)); + 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; @@ -210,10 +230,10 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, } } else if (mode == IGRAPH_MERGE_MODE_INTERSECTION) { if (edge_map1) { - IGRAPH_CHECK(igraph_vector_int_push_back(edge_map1, edge1)); + IGRAPH_CHECK(igraph_vector_push_back(edge_map1, edge1)); } if (edge_map2) { - IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, edge2)); + IGRAPH_CHECK(igraph_vector_push_back(edge_map2, edge2)); } } eptr++; @@ -226,15 +246,14 @@ igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, #undef INC1 #undef INC2 - igraph_vector_int_destroy(&order2); - igraph_vector_int_destroy(&order1); - igraph_vector_int_destroy(&edges2); - igraph_vector_int_destroy(&edges1); + 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_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/misc_internal.h b/src/vendor/cigraph/src/operators/misc_internal.h index fd519033b1f..2701fcc3483 100644 --- a/src/vendor/cigraph/src/operators/misc_internal.h +++ b/src/vendor/cigraph/src/operators/misc_internal.h @@ -26,21 +26,20 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS -typedef enum { - IGRAPH_MERGE_MODE_UNION = 1, - IGRAPH_MERGE_MODE_INTERSECTION = 2 -} igraph_i_merge_mode_t; +#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); -igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, +int igraph_i_merge(igraph_t *res, int mode, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); + 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 diff --git a/src/vendor/cigraph/src/operators/permute.c b/src/vendor/cigraph/src/operators/permute.c index 3fcfb3b8846..8ad0673f391 100644 --- a/src/vendor/cigraph/src/operators/permute.c +++ b/src/vendor/cigraph/src/operators/permute.c @@ -38,14 +38,14 @@ * \param inverse An initialized vector. The inverse of \p permutation will be stored here. * \return Error code. */ -static igraph_error_t igraph_i_invert_permutation(const igraph_vector_int_t *permutation, igraph_vector_int_t *inverse) { - const igraph_integer_t n = igraph_vector_int_size(permutation); +static int igraph_i_invert_permutation(const igraph_vector_t *permutation, igraph_vector_t *inverse) { + const long int n = igraph_vector_size(permutation); - IGRAPH_CHECK(igraph_vector_int_resize(inverse, n)); - igraph_vector_int_fill(inverse, -1); + IGRAPH_CHECK(igraph_vector_resize(inverse, n)); + igraph_vector_fill(inverse, -1); - for (igraph_integer_t i=0; i < n; i++) { - igraph_integer_t j = VECTOR(*permutation)[i]; + for (long int i=0; i < n; i++) { + long int j = VECTOR(*permutation)[i]; if (j < 0 || j >= n) { IGRAPH_ERROR("Invalid index in permutation vector.", IGRAPH_EINVAL); } @@ -78,51 +78,52 @@ static igraph_error_t igraph_i_invert_permutation(const igraph_vector_int_t *per * Time complexity: O(|V|+|E|), linear in terms of the number of * vertices and edges. */ -igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, - const igraph_vector_int_t *permutation) { +int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_t *permutation) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t edges; - igraph_vector_int_t index; - igraph_integer_t p; + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); + igraph_vector_t edges; + igraph_vector_t index; + long int p; - if (igraph_vector_int_size(permutation) != no_of_nodes) { + if (igraph_vector_size(permutation) != no_of_nodes) { IGRAPH_ERROR("Permute vertices: invalid permutation vector size.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_nodes); /* Also checks that 'permutation' is valid: */ IGRAPH_CHECK(igraph_i_invert_permutation(permutation, &index)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); p = 0; - for (igraph_integer_t i = 0; i < no_of_edges; i++) { - VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_FROM(graph, i) ]; - VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_TO(graph, i) ]; + for (long int 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, no_of_nodes, igraph_is_directed(graph))); + IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); IGRAPH_FINALLY(igraph_destroy, res); /* Attributes */ if (graph->attr) { - igraph_vector_int_t vtypes; + igraph_vector_t vtypes; IGRAPH_I_ATTRIBUTE_DESTROY(res); IGRAPH_I_ATTRIBUTE_COPY(res, graph, /*graph=*/1, /*vertex=*/0, /*edge=*/1); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vtypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); - if (igraph_vector_int_size(&vtypes) != 0) { + if (igraph_vector_size(&vtypes) != 0) { IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, &index)); } - igraph_vector_int_destroy(&vtypes); + igraph_vector_destroy(&vtypes); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&index); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&index); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); /* +1 for res */ return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/operators/reverse.c b/src/vendor/cigraph/src/operators/reverse.c index 6b6348498dc..5d298a52cc1 100644 --- a/src/vendor/cigraph/src/operators/reverse.c +++ b/src/vendor/cigraph/src/operators/reverse.c @@ -29,7 +29,6 @@ #include "igraph_vector.h" #include "graph/attributes.h" -#include "graph/internal.h" /** * \function igraph_reverse_edges @@ -48,13 +47,12 @@ * Pass igraph_ess_all(IGRAPH_EDGEORDER_ID) to reverse all edges. * \return Error code. * - * Time complexity: O(1) if all edges are reversed, otherwise - * O(|E|) where |E| is the number of edges in the graph. + * Time complexity: O(|E|) where |E| is the number of edges in the graph. */ -igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t edges; +int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { + long int no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t edges; igraph_eit_t eit; igraph_t new_graph; @@ -63,13 +61,8 @@ igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { return IGRAPH_SUCCESS; } - /* Use fast method when all edges are to be reversed. */ - if (igraph_es_is_all(&eids)) { - return igraph_i_reverse(graph); - } - /* Convert graph to edge list. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2*no_of_edges); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ 0)); /* Reverse the edges. */ @@ -78,8 +71,8 @@ igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { 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 tmp = VECTOR(edges)[2*eid]; + long int eid = IGRAPH_EIT_GET(eit); + long int tmp = VECTOR(edges)[2*eid]; VECTOR(edges)[2*eid] = VECTOR(edges)[2*eid + 1]; VECTOR(edges)[2*eid + 1] = tmp; } @@ -92,7 +85,7 @@ igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { IGRAPH_I_ATTRIBUTE_COPY(&new_graph, graph, 1, 1, 1); /* does IGRAPH_CHECK */ igraph_eit_destroy(&eit); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); igraph_destroy(graph); IGRAPH_FINALLY_CLEAN(3); diff --git a/src/vendor/cigraph/src/operators/rewire.c b/src/vendor/cigraph/src/operators/rewire.c index 6820c63027f..3cb784f8dcf 100644 --- a/src/vendor/cigraph/src/operators/rewire.c +++ b/src/vendor/cigraph/src/operators/rewire.c @@ -39,13 +39,12 @@ #define REWIRE_ADJLIST_THRESHOLD 10 /* Not declared static so that the testsuite can use it, but not part of the public API. */ -igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); +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_int_t eids; - igraph_vector_int_t edgevec, alledges; + igraph_vector_t eids, edgevec, alledges; igraph_bool_t directed, loops, ok; igraph_es_t es; igraph_adjlist_t al; @@ -59,7 +58,7 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir RNG_BEGIN(); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 2); + IGRAPH_VECTOR_INIT_FINALLY(&eids, 2); if (use_adjlist) { /* As well as the sorted adjacency list, we maintain an unordered @@ -67,10 +66,10 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir */ IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - IGRAPH_VECTOR_INT_INIT_FINALLY(&alledges, no_of_edges * 2); + IGRAPH_VECTOR_INIT_FINALLY(&alledges, no_of_edges * 2); igraph_get_edgelist(graph, &alledges, /*bycol=*/ 0); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&edgevec, 4); + IGRAPH_VECTOR_INIT_FINALLY(&edgevec, 4); es = igraph_ess_vector(&eids); } @@ -105,13 +104,15 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir /* Get the endpoints */ if (use_adjlist) { - a = VECTOR(alledges)[VECTOR(eids)[0] * 2]; - b = VECTOR(alledges)[VECTOR(eids)[0] * 2 + 1]; - c = VECTOR(alledges)[VECTOR(eids)[1] * 2]; - d = VECTOR(alledges)[VECTOR(eids)[1] * 2 + 1]; + 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, VECTOR(eids)[0], &a, &b)); - IGRAPH_CHECK(igraph_edge(graph, VECTOR(eids)[1], &c, &d)); + 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. @@ -122,8 +123,8 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir if (use_adjlist) { /* Flip the edge in the unordered edge-list, so the update later on * hits the correct end. */ - VECTOR(alledges)[VECTOR(eids)[1] * 2] = c; - VECTOR(alledges)[VECTOR(eids)[1] * 2 + 1] = d; + VECTOR(alledges)[((igraph_integer_t)VECTOR(eids)[1]) * 2] = c; + VECTOR(alledges)[(((igraph_integer_t)VECTOR(eids)[1]) * 2) + 1] = d; } } @@ -172,22 +173,22 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir /* If we are still okay, we can perform the rewiring */ if (ok) { - /* printf("Deleting: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", - a, b, c, d); */ + /* 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)[VECTOR(eids)[0] * 2 + 1] = d; - VECTOR(alledges)[VECTOR(eids)[1] * 2 + 1] = b; + 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: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", - a, d, c, b); */ - IGRAPH_CHECK(igraph_add_edges(graph, &edgevec, 0)); + /* printf("Adding: %ld -> %ld, %ld -> %ld\n", + (long)a, (long)d, (long)c, (long)b); */ + igraph_add_edges(graph, &edgevec, 0); } num_successful_swaps++; } @@ -208,18 +209,18 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir IGRAPH_PROGRESS("Random rewiring: ", 100.0, 0); if (use_adjlist) { - igraph_vector_int_destroy(&alledges); + igraph_vector_destroy(&alledges); igraph_adjlist_destroy(&al); } else { - igraph_vector_int_destroy(&edgevec); + igraph_vector_destroy(&edgevec); } - igraph_vector_int_destroy(&eids); + igraph_vector_destroy(&eids); IGRAPH_FINALLY_CLEAN(use_adjlist ? 3 : 2); RNG_END(); - return IGRAPH_SUCCESS; + return 0; } /** @@ -261,7 +262,7 @@ igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewir * * Time complexity: TODO. */ -igraph_error_t igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode) { +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/vendor/cigraph/src/operators/rewire_edges.c b/src/vendor/cigraph/src/operators/rewire_edges.c index 6c5b856bfe2..48211e2d59c 100644 --- a/src/vendor/cigraph/src/operators/rewire_edges.c +++ b/src/vendor/cigraph/src/operators/rewire_edges.c @@ -30,21 +30,21 @@ #include "graph/attributes.h" -static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, +static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, - igraph_vector_int_t *edges) { + igraph_vector_t *edges) { - igraph_integer_t no_verts = igraph_vcount(graph); - igraph_integer_t no_edges = igraph_ecount(graph); - igraph_vector_int_t eorder, tmp; + 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; - igraph_integer_t i, to_rewire, last_other = -1; + int i, to_rewire, last_other = -1; /* Create our special graph representation */ # define ADD_STUB(vertex, stub) do { \ if (VECTOR(first)[(vertex)]) { \ - VECTOR(prev)[VECTOR(first)[(vertex)]-1]=(stub)+1; \ + VECTOR(prev)[(int) VECTOR(first)[(vertex)]-1]=(stub)+1; \ } \ VECTOR(next)[(stub)]=VECTOR(first)[(vertex)]; \ VECTOR(prev)[(stub)]=0; \ @@ -63,9 +63,9 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ } while (0) # define MARK_NEIGHBORS(vertex) do { \ - igraph_integer_t xxx_ =VECTOR(first)[(vertex)]; \ + int xxx_ =VECTOR(first)[(vertex)]; \ while (xxx_) { \ - igraph_integer_t o= VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2]; \ + int o= (int) VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2]; \ VECTOR(marked)[o]=other+1; \ xxx_=VECTOR(next)[xxx_-1]; \ } \ @@ -78,18 +78,17 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ 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_INT_INIT_FINALLY(&eorder, no_edges); - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, no_edges); + IGRAPH_VECTOR_INIT_FINALLY(&eorder, no_edges); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges); for (i = 0; i < no_edges; i++) { - igraph_integer_t idx1 = 2 * i, idx2 = idx1 + 1; - igraph_integer_t from = VECTOR(*edges)[idx1]; - igraph_integer_t to = VECTOR(*edges)[idx2]; + 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_int_order1(&tmp, &eorder, no_verts)); - igraph_vector_int_destroy(&tmp); + 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)); @@ -97,22 +96,22 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ /* Rewire the stubs, part I */ - to_rewire = (igraph_integer_t) RNG_GEOM(prob); + to_rewire = (int) RNG_GEOM(prob); while (to_rewire < no_edges) { - igraph_integer_t stub = 2 * VECTOR(eorder)[to_rewire] + 1; - igraph_integer_t v = VECTOR(*edges)[stub]; - igraph_integer_t ostub = stub - 1; - igraph_integer_t other = VECTOR(*edges)[ostub]; - igraph_integer_t pot; + 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 = RNG_INTEGER(0, no_verts - 1); + pot = (int) RNG_INTEGER(0, no_verts - 1); } else { - pot = RNG_INTEGER(0, no_verts - 2); + pot = (int) RNG_INTEGER(0, no_verts - 2); pot = pot != other ? pot : no_verts - 1; } } while (VECTOR(marked)[pot] == other + 1 && pot != v); @@ -131,12 +130,12 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ /* Create the new index, from the potentially rewired stubs */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, no_edges); + 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_int_order1(&tmp, &eorder, no_verts)); - igraph_vector_int_destroy(&tmp); + IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts)); + igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); /* Rewire the stubs, part II */ @@ -144,22 +143,22 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ igraph_vector_int_null(&marked); last_other = -1; - to_rewire = (igraph_integer_t) RNG_GEOM(prob); + to_rewire = (int) RNG_GEOM(prob); while (to_rewire < no_edges) { - igraph_integer_t stub = (2 * VECTOR(eorder)[to_rewire]); - igraph_integer_t v = VECTOR(*edges)[stub]; - igraph_integer_t ostub = stub + 1; - igraph_integer_t other = VECTOR(*edges)[ostub]; - igraph_integer_t pot; + 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 = RNG_INTEGER(0, no_verts - 1); + pot = (int) RNG_INTEGER(0, no_verts - 1); } else { - pot = RNG_INTEGER(0, no_verts - 2); + pot = (int) RNG_INTEGER(0, no_verts - 2); pot = pot != other ? pot : no_verts - 1; } } while (VECTOR(marked)[pot] == other + 1 && pot != v); @@ -179,10 +178,10 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ igraph_vector_int_destroy(&prev); igraph_vector_int_destroy(&next); igraph_vector_int_destroy(&first); - igraph_vector_int_destroy(&eorder); + igraph_vector_destroy(&eorder); IGRAPH_FINALLY_CLEAN(5); - return IGRAPH_SUCCESS; + return 0; } #undef ADD_STUB @@ -216,15 +215,15 @@ static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_ * * Time complexity: O(|V|+|E|). */ -igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, +int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_bool_t multiple) { igraph_t newgraph; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t endpoints = no_of_edges * 2; - igraph_integer_t to_rewire; - igraph_vector_int_t edges; + 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", @@ -236,7 +235,7 @@ igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, endpoints); + IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints); RNG_BEGIN(); @@ -247,14 +246,14 @@ igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, so the "skips" between the really rewired endpoints follow a geometric distribution. */ IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); - to_rewire = RNG_GEOM(prob); + 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 { - igraph_integer_t opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1; - igraph_integer_t nei = VECTOR(edges)[opos]; - igraph_integer_t r = RNG_INTEGER(0, no_of_nodes - 2); + 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; @@ -268,9 +267,9 @@ igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, RNG_END(); - IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, + IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &newgraph); @@ -280,7 +279,7 @@ igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_destroy(graph); *graph = newgraph; - return IGRAPH_SUCCESS; + return 0; } /** @@ -319,7 +318,7 @@ igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, * * Time complexity: O(|E|). */ -igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, +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) { @@ -338,13 +337,13 @@ igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, if (igraph_is_directed(graph) && mode != IGRAPH_ALL) { igraph_t newgraph; - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t to_rewire; - igraph_integer_t offset = 0; - igraph_vector_int_t edges; + 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_INT_INIT_FINALLY(&edges, 2 * no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); switch (mode) { case IGRAPH_IN: @@ -366,8 +365,8 @@ igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, if (loops) { VECTOR(edges)[2 * to_rewire + offset] = RNG_INTEGER(0, no_of_nodes - 1); } else { - igraph_integer_t nei = VECTOR(edges)[2 * to_rewire + (1 - offset)]; - igraph_integer_t r = RNG_INTEGER(0, no_of_nodes - 2); + 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; @@ -375,9 +374,9 @@ igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, RNG_END(); - IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, + IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &newgraph); @@ -391,5 +390,5 @@ igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, IGRAPH_CHECK(igraph_rewire_edges(graph, prob, loops, /* multiple = */ 1)); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/rewire_internal.h b/src/vendor/cigraph/src/operators/rewire_internal.h index b82b10eec95..c9e5d7e5fa4 100644 --- a/src/vendor/cigraph/src/operators/rewire_internal.h +++ b/src/vendor/cigraph/src/operators/rewire_internal.h @@ -1,15 +1,8 @@ #ifndef IGRAPH_OPERATORS_REWIRE_INTERNAL_H #define IGRAPH_OPERATORS_REWIRE_INTERNAL_H -#include "igraph_decls.h" #include "igraph_interface.h" -__BEGIN_DECLS - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_rewire( - igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, - igraph_bool_t use_adjlist); - -__END_DECLS +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/vendor/cigraph/src/operators/simplify.c b/src/vendor/cigraph/src/operators/simplify.c index 3dd804cbb77..6924f18c988 100644 --- a/src/vendor/cigraph/src/operators/simplify.c +++ b/src/vendor/cigraph/src/operators/simplify.c @@ -38,7 +38,7 @@ * \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 unaffected. See the igraph manual section about attributes + * that were unaffeccted. See the igraph manual section about attributes * for details. * \return Error code: * \c IGRAPH_ENOMEM if we are out of memory. @@ -48,21 +48,21 @@ * \example examples/simple/igraph_simplify.c */ -igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, +int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_bool_t loops, const igraph_attribute_combination_t *edge_comb) { - igraph_vector_int_t edges; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t edge; + 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(); - igraph_integer_t from, to, pfrom = -1, pto = -2; + long int from, to, pfrom = -1, pto = -2; igraph_t res; igraph_es_t es; igraph_eit_t eit; - igraph_vector_int_t mergeinto; - igraph_integer_t actedge; + igraph_vector_t mergeinto; + long int actedge; if (!multiple && !loops) /* nothing to do */ @@ -71,11 +71,9 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, } if (!multiple) { - igraph_vector_int_t edges_to_delete; - /* removing loop edges only, this is simple. No need to combine anything * and the whole process can be done in-place */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_to_delete, 0); + 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)); @@ -86,7 +84,7 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, from = IGRAPH_FROM(graph, edge); to = IGRAPH_TO(graph, edge); if (from == to) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges_to_delete, edge)); + IGRAPH_CHECK(igraph_vector_push_back(&edges, edge)); } IGRAPH_EIT_NEXT(eit); } @@ -95,21 +93,21 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); - if (igraph_vector_int_size(&edges_to_delete) > 0) { - IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges_to_delete))); + if (igraph_vector_size(&edges) > 0) { + IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges))); } - igraph_vector_int_destroy(&edges_to_delete); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } if (attr) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + 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); @@ -133,8 +131,8 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, } } else { /* Edge to be kept */ - igraph_vector_int_push_back(&edges, from); - igraph_vector_int_push_back(&edges, to); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); if (attr) { actedge++; VECTOR(mergeinto)[edge] = actedge; @@ -147,9 +145,10 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_create(&res, &edges, no_of_nodes, igraph_is_directed(graph))); + IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_of_nodes, + igraph_is_directed(graph))); - igraph_vector_int_destroy(&edges); + igraph_vector_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &res); @@ -160,13 +159,15 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, if (attr) { igraph_fixed_vectorlist_t vl; - IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, actedge + 1)); + 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.vecs, edge_comb)); + IGRAPH_CHECK(igraph_i_attribute_combine_edges(graph, &res, &vl.v, + edge_comb)); igraph_fixed_vectorlist_destroy(&vl); - igraph_vector_int_destroy(&mergeinto); + igraph_vector_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(2); } @@ -174,5 +175,5 @@ igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_destroy(graph); *graph = res; - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/subgraph.c b/src/vendor/cigraph/src/operators/subgraph.c index 287b5e6dd9b..f798f5df7f0 100644 --- a/src/vendor/cigraph/src/operators/subgraph.c +++ b/src/vendor/cigraph/src/operators/subgraph.c @@ -27,45 +27,44 @@ #include "igraph_memory.h" #include "core/interruption.h" -#include "core/set.h" #include "graph/attributes.h" -#include "graph/internal.h" #include "operators/subgraph.h" /** * Subgraph creation, old version: it copies the graph and then deletes * unneeded vertices. */ -static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( - const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, - igraph_vector_int_t *map, igraph_vector_int_t *invmap) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t delete; - bool *remain; - igraph_integer_t i; +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_INT_INIT_FINALLY(&delete, 0); - - remain = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(remain, "Insufficient memory for taking subgraph."); + 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_int_reserve(&delete, no_of_nodes - IGRAPH_VIT_SIZE(vit))); + 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[ IGRAPH_VIT_GET(vit) ] = true; + remain[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } for (i = 0; i < no_of_nodes; i++) { + IGRAPH_ALLOW_INTERRUPTION(); - if (! remain[i]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + if (remain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); } } @@ -79,11 +78,10 @@ static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( IGRAPH_CHECK(igraph_delete_vertices_idx(res, igraph_vss_vector(&delete), map, invmap)); - igraph_vector_int_destroy(&delete); + igraph_vector_destroy(&delete); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -96,47 +94,45 @@ static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( * the _original_ graph) in cases when induced_subgraph() is repeatedly * called on the same graph; one example is igraph_decompose(). */ -static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( - const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, - igraph_vector_int_t *map, igraph_vector_int_t *invmap, - igraph_bool_t map_is_prepared) { - +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); - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_new_nodes = 0; - igraph_integer_t i, j, n; - igraph_integer_t to; + 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_int_t vids_old2new, vids_new2old; - igraph_vector_int_t eids_new2old; - igraph_vector_int_t vids_vec; - igraph_vector_int_t nei_edges; - igraph_vector_int_t new_edges; + 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_int_t *my_vids_old2new = &vids_old2new, - *my_vids_new2old = &vids_new2old; + 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_INT_INIT_FINALLY(&eids_new2old, 0); + IGRAPH_VECTOR_INIT_FINALLY(&eids_new2old, 0); if (invmap) { my_vids_new2old = invmap; - igraph_vector_int_clear(my_vids_new2old); + igraph_vector_clear(my_vids_new2old); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_new2old, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vids_new2old, 0); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_edges, 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_int_resize(map, no_of_nodes)); - igraph_vector_int_null(map); + IGRAPH_CHECK(igraph_vector_resize(map, no_of_nodes)); + igraph_vector_null(map); } } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_vec, 0); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -147,47 +143,45 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * 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. + * a vector first and then sort it. We temporarily use new_edges for that. */ - IGRAPH_CHECK(igraph_vit_as_vector(&vit, &vids_vec)); + IGRAPH_CHECK(igraph_vit_as_vector(&vit, &nei_edges)); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - igraph_vector_int_sort(&vids_vec); - n = igraph_vector_int_size(&vids_vec); + igraph_vector_sort(&nei_edges); + n = igraph_vector_size(&nei_edges); for (i = 0; i < n; i++) { - igraph_integer_t vid = VECTOR(vids_vec)[i]; + long int vid = (long int) VECTOR(nei_edges)[i]; if (VECTOR(*my_vids_old2new)[vid] == 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(my_vids_new2old, vid)); + IGRAPH_CHECK(igraph_vector_push_back(my_vids_new2old, vid)); no_of_new_nodes++; VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes; } } - igraph_vector_int_destroy(&vids_vec); - IGRAPH_FINALLY_CLEAN(1); /* Create the new edge list */ for (i = 0; i < no_of_new_nodes; i++) { - igraph_integer_t old_vid = VECTOR(*my_vids_new2old)[i]; - igraph_integer_t new_vid = 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_int_size(&nei_edges); + n = igraph_vector_size(&nei_edges); if (directed) { /* directed graph; this is easier */ for (j = 0; j < n; j++) { - eid = VECTOR(nei_edges)[j]; + eid = (igraph_integer_t) VECTOR(nei_edges)[j]; - to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ]; + to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ]; if (!to) { continue; } - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid)); - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to - 1)); - IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid)); + 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 @@ -195,14 +189,14 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * second loop edge */ skip_loop_edge = 0; for (j = 0; j < n; j++) { - eid = VECTOR(nei_edges)[j]; + eid = (igraph_integer_t) VECTOR(nei_edges)[j]; if (IGRAPH_FROM(graph, eid) != old_vid) { /* avoid processing edges twice */ continue; } - to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ]; + to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ]; if (!to) { continue; } @@ -216,27 +210,28 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( } } - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid)); - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to)); - IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid)); + 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_int_destroy(&vids_old2new); + igraph_vector_destroy(&vids_old2new); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&nei_edges); + igraph_vector_destroy(&nei_edges); IGRAPH_FINALLY_CLEAN(1); /* Create the new graph */ - IGRAPH_CHECK(igraph_create(res, &new_edges, no_of_new_nodes, directed)); + 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_int_destroy(&new_edges); + igraph_vector_destroy(&new_edges); IGRAPH_FINALLY_CLEAN(1); /* Make sure that the newly created graph is destroyed if something happens from @@ -248,20 +243,20 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( /* ga = */ 1, /* va = */ 0, /* ea = */ 0)); /* Copy the vertex attributes */ - IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, my_vids_new2old)); + 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)); - /* Get rid of the remaining stuff */ if (!invmap) { - igraph_vector_int_destroy(my_vids_new2old); + igraph_vector_destroy(my_vids_new2old); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&eids_new2old); + igraph_vector_destroy(&eids_new2old); IGRAPH_FINALLY_CLEAN(2); /* 1 + 1 since we don't need to destroy res */ - return IGRAPH_SUCCESS; + return 0; } /** @@ -272,8 +267,8 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * * 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. + * 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 @@ -297,7 +292,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex ID in + * \c IGRAPH_EINVVID, invalid vertex id in * \p vids. * * Time complexity: O(|V|+|E|), @@ -308,13 +303,13 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * \sa \ref igraph_delete_vertices() to delete the specified set of * vertices from a graph, the opposite of this function. */ -igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, +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 igraph_error_t igraph_i_induced_subgraph_suggest_implementation( +static int igraph_i_induced_subgraph_suggest_implementation( const igraph_t *graph, const igraph_vs_t vids, igraph_subgraph_implementation_t *result) { double ratio; @@ -334,14 +329,14 @@ static igraph_error_t igraph_i_induced_subgraph_suggest_implementation( *result = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH; } - return IGRAPH_SUCCESS; + return 0; } -igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +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_int_t *map, - igraph_vector_int_t *invmap, + igraph_vector_t *map, + igraph_vector_t *invmap, igraph_bool_t map_is_prepared) { if (impl == IGRAPH_SUBGRAPH_AUTO) { @@ -361,155 +356,24 @@ igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *re } } -/** - * \ingroup structural - * \function igraph_induced_subgraph_map - * \brief Creates an induced subraph and returns the mapping from the original. - * - * 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 be - * used 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. - * \param map Returns a map of the vertices in \p graph to the vertices - * in \p res. A 0 indicates a vertex is not mapped. An \c i + 1 at - * position \c j indicates the vertex \c j in \p graph is mapped - * to vertex i in \p res. - * \param invmap Returns a map of the vertices in \p res to the vertices - * in \p graph. An i at position \c j indicates the vertex \c i - * in \p graph is mapped to vertex j in \p res. - * - * \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. - */ -igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl, - igraph_vector_int_t *map, - igraph_vector_int_t *invmap) { - return igraph_i_induced_subgraph_map(graph, res,vids, impl, map, invmap, /* map_is_prepared = */ false); -} - -/** - * \function igraph_induced_subgraph_edges - * \brief The edges contained within an induced subgraph. - * - * This function finds the IDs of those edges which connect vertices from - * a given list, passed in the \p vids parameter. - * - * \param graph The graph. - * \param vids A vertex selector specifying the vertices that make up the subgraph. - * \param edges Integer vector. The IDs of edges within the subgraph induces by - * \p vids will be stored here. - * \return Error code. - * - * Time complexity: O(mv log(nv)) where nv is the number of vertices in \p vids - * and mv is the sum of degrees of vertices in \p vids. - */ -igraph_error_t igraph_induced_subgraph_edges(const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges) { - /* TODO: When the size of \p vids is large, is it faster to use a boolean vector instead of a set - * to test membership within \p vids? Benchmark to find out at what size it is worth switching - * to the alternative implementation. - */ - igraph_vit_t vit; - igraph_set_t vids_set; - igraph_vector_int_t incedges; - - if (igraph_vs_is_all(&vids)) { - IGRAPH_CHECK(igraph_vector_int_range(edges, 0, igraph_ecount(graph))); - return IGRAPH_SUCCESS; - } - - igraph_vector_int_clear(edges); - - IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); - IGRAPH_FINALLY(igraph_vit_destroy, &vit); - - IGRAPH_SET_INIT_FINALLY(&vids_set, IGRAPH_VIT_SIZE(vit)); - for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { - IGRAPH_CHECK(igraph_set_add(&vids_set, IGRAPH_VIT_GET(vit))); - } - - IGRAPH_VECTOR_INT_INIT_FINALLY(&incedges, 0); - - for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { - igraph_integer_t v = IGRAPH_VIT_GET(vit); - IGRAPH_CHECK(igraph_i_incident(graph, &incedges, v, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); - - igraph_integer_t d = igraph_vector_int_size(&incedges); - for (igraph_integer_t i=0; i < d; i++) { - igraph_integer_t e = VECTOR(incedges)[i]; - igraph_integer_t u = IGRAPH_OTHER(graph, e, v); - /* The v <= u check avoids adding non-loop edges twice. - * Loop edges only appear once due to the use of - * IGRAPH_LOOPS_ONCE in igraph_i_incident() */ - if (v <= u && igraph_set_contains(&vids_set, u)) { - IGRAPH_CHECK(igraph_vector_int_push_back(edges, e)); - } - } - } - - IGRAPH_FINALLY_CLEAN(3); - igraph_vector_int_destroy(&incedges); - igraph_set_destroy(&vids_set); - igraph_vit_destroy(&vit); - - return IGRAPH_SUCCESS; + 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 (deprecated alias). - * - * \deprecated-by igraph_subgraph_from_edges 0.10.3 - */ -igraph_error_t igraph_subgraph_edges( - const igraph_t *graph, igraph_t *res, const igraph_es_t eids, - igraph_bool_t delete_vertices -) { - return igraph_subgraph_from_edges(graph, res, eids, delete_vertices); -} - -/** - * \ingroup structural - * \function igraph_subgraph_from_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. Attributes are preserved. - * + * 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 @@ -517,13 +381,13 @@ igraph_error_t igraph_subgraph_edges( * 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 + * 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 + * \c IGRAPH_EINVEID, invalid edge id in * \p eids. * * Time complexity: O(|V|+|E|), @@ -535,44 +399,45 @@ igraph_error_t igraph_subgraph_edges( * edges from a graph, the opposite of this function. */ -igraph_error_t igraph_subgraph_from_edges( - const igraph_t *graph, igraph_t *res, const igraph_es_t eids, - igraph_bool_t delete_vertices -) { +int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, + const igraph_es_t eids, igraph_bool_t delete_vertices) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_vector_int_t delete = IGRAPH_VECTOR_NULL; - bool *vremain, *eremain; - igraph_integer_t i; + 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_INT_INIT_FINALLY(&delete, 0); - vremain = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(vremain, "Insufficient memory for taking subgraph based on edges."); + 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, bool); - IGRAPH_CHECK_OOM(eremain, "Insufficient memory for taking subgraph based on edges."); + 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_int_reserve(&delete, no_of_edges - IGRAPH_EIT_SIZE(eit))); + 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 eid = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); - eremain[eid] = vremain[from] = vremain[to] = true; + 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]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + if (eremain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); } } @@ -588,11 +453,11 @@ igraph_error_t igraph_subgraph_from_edges( if (delete_vertices) { /* Collect the vertex IDs to be deleted */ - igraph_vector_int_clear(&delete); + igraph_vector_clear(&delete); for (i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); - if (! vremain[i]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); + if (vremain[i] == 0) { + IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); } } } @@ -605,8 +470,8 @@ igraph_error_t igraph_subgraph_from_edges( IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete))); } - igraph_vector_int_destroy(&delete); + igraph_vector_destroy(&delete); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/operators/subgraph.h b/src/vendor/cigraph/src/operators/subgraph.h index d293f7b2f9f..a27aa8efad4 100644 --- a/src/vendor/cigraph/src/operators/subgraph.h +++ b/src/vendor/cigraph/src/operators/subgraph.h @@ -24,19 +24,12 @@ #ifndef IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H #define IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H -#include "igraph_decls.h" -#include "igraph_datatype.h" -#include "igraph_error.h" -#include "igraph_iterators.h" +#include "igraph_interface.h" -__BEGIN_DECLS - -IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_induced_subgraph_map( +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_int_t *map, - igraph_vector_int_t *invmap, igraph_bool_t map_is_prepared + igraph_subgraph_implementation_t impl, igraph_vector_t *map, + igraph_vector_t *invmap, igraph_bool_t map_is_prepared ); -__END_DECLS - #endif diff --git a/src/vendor/cigraph/src/operators/union.c b/src/vendor/cigraph/src/operators/union.c index d1fd87a6800..d55390d0f26 100644 --- a/src/vendor/cigraph/src/operators/union.c +++ b/src/vendor/cigraph/src/operators/union.c @@ -27,8 +27,8 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_interface.h" +#include "igraph_memory.h" #include "igraph_qsort.h" -#include "igraph_vector_list.h" #include "operators/misc_internal.h" @@ -36,19 +36,11 @@ * \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. * - * - * The directedness of the operand graphs must be the same. - * - * - * Edge multiplicities are handled by taking the \em larger of the two - * multiplicities in the input graphs. In other words, if the first graph - * has N edges between a vertex pair (u, v) and the second graph has M edges, - * the result graph will have max(N, M) edges between them. - * * \param res Pointer to an uninitialized graph object, the result * will be stored here. * \param left The first graph. @@ -69,9 +61,9 @@ * * \example examples/simple/igraph_union.c */ -igraph_error_t igraph_union(igraph_t *res, +int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { + 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); } @@ -80,33 +72,25 @@ igraph_error_t igraph_union(igraph_t *res, * \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 number of vertices in the result graph will be the maximum - * number of vertices in the argument graphs. - * - * - * The directedness of the argument graphs must be the same. + * 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. * - * - * Edge multiplicities are handled by taking the \em maximum multiplicity of the - * all multiplicities for the same vertex pair (u, v) in the input graphs; this - * will be the multiplicity of (u, v) in the result graph. - * * \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 - * list of integer vectors, and the mappings of edges from the graphs to - * the result graph will be stored here, in the same order as + * 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_int_t object. + * \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 @@ -118,19 +102,17 @@ igraph_error_t igraph_union(igraph_t *res, * * \example examples/simple/igraph_union.c */ -igraph_error_t igraph_union_many( - igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_int_list_t *edgemaps -) { - - igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); - igraph_integer_t no_of_nodes = 0; - igraph_bool_t directed = true; - igraph_vector_int_t edges; - igraph_vector_int_list_t edge_vects, order_vects; - igraph_vector_int_t no_edges; - igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; - igraph_integer_t idx = 0; +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) { @@ -139,18 +121,24 @@ igraph_error_t igraph_union_many( } for (i = 1; i < no_of_graphs; i++) { if (directed != igraph_is_directed(VECTOR(*graphs)[i])) { - IGRAPH_ERROR("Cannot create union of directed and undirected graphs.", + IGRAPH_ERROR("Cannot union directed and undirected graphs", IGRAPH_EINVAL); } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &no_edges); + 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++) { - igraph_integer_t n = igraph_vcount(VECTOR(*graphs)[i]); + long int n = igraph_vcount(VECTOR(*graphs)[i]); if (n > no_of_nodes) { no_of_nodes = n; } @@ -158,37 +146,54 @@ igraph_error_t igraph_union_many( } if (edgemaps) { - IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); for (i = 0; i < no_of_graphs; i++) { - igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); - IGRAPH_CHECK(igraph_vector_int_resize(v, VECTOR(no_edges)[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 */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); + 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++) { - igraph_integer_t k, j, n = VECTOR(no_edges)[i]; - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); - IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); + 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(*ev)[j] > VECTOR(*ev)[j + 1]) { - igraph_integer_t tmp = VECTOR(*ev)[j]; - VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; - VECTOR(*ev)[j + 1] = tmp; + 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; } } } - IGRAPH_CHECK(igraph_vector_int_resize(order, n)); for (k = 0; k < n; k++) { VECTOR(*order)[k] = k; } - igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, igraph_i_order_edgelist_cmp); } @@ -197,12 +202,11 @@ igraph_error_t igraph_union_many( /* Get the largest tail element */ tailfrom = tailto = -1; for (j = 0; j < no_of_graphs; j++) { - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); - if (!igraph_vector_int_empty(order)) { - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); - igraph_integer_t edge = igraph_vector_int_tail(order); - igraph_integer_t from = VECTOR(*ev)[2 * edge]; - igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + 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; } @@ -213,21 +217,20 @@ igraph_error_t igraph_union_many( } /* add the edge */ - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailfrom)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailto)); + 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++) { - igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); - if (!igraph_vector_int_empty(order)) { - igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); - igraph_integer_t edge = igraph_vector_int_tail(order); - igraph_integer_t from = VECTOR(*ev)[2 * edge]; - igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; + 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_int_pop_back(order); + igraph_vector_long_pop_back(VECTOR(order_vects)[j]); if (edgemaps) { - igraph_vector_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); + igraph_vector_t *map = VECTOR(*edgemaps)[j]; VECTOR(*map)[edge] = idx; } } @@ -237,14 +240,22 @@ igraph_error_t igraph_union_many( } - igraph_vector_int_list_destroy(&order_vects); - igraph_vector_int_list_destroy(&edge_vects); - igraph_vector_int_destroy(&no_edges); - IGRAPH_FINALLY_CLEAN(3); + 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, no_of_nodes, directed)); - igraph_vector_int_destroy(&edges); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/all_shortest_paths.c b/src/vendor/cigraph/src/paths/all_shortest_paths.c index 536586f80cd..d16ed57fd08 100644 --- a/src/vendor/cigraph/src/paths/all_shortest_paths.c +++ b/src/vendor/cigraph/src/paths/all_shortest_paths.c @@ -23,6 +23,7 @@ #include "igraph_paths.h" +#include "igraph_adjlist.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" @@ -31,6 +32,17 @@ #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. @@ -39,23 +51,13 @@ * all of them will be returned. * * \param graph The graph object. - * \param vertices The result, the IDs of the vertices along the paths. - * This is a list of integer vectors where each element is an - * \ref igraph_vector_int_t object. 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. The list will be resized as needed. 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. Each vector object contains the edges - * 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. The list will be resized as needed. Supply a null pointer here - * if you don't need these vectors. - * \param nrgeo Pointer to an initialized \ref igraph_vector_int_t object or + * \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 @@ -63,7 +65,7 @@ * 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 + * \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 @@ -82,7 +84,7 @@ * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p from is invalid vertex ID. + * \p from is invalid vertex id. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -92,26 +94,22 @@ * Time complexity: O(|V|+|E|) for most graphs, O(|V|^2) in the worst * case. */ - -igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_vector_int_t *nrgeo, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t *geodist; - igraph_vector_int_list_t paths; - igraph_vector_int_list_t path_edge; - igraph_dqueue_int_t q; - igraph_vector_int_t *vptr; - igraph_vector_int_t *vptr_e; - igraph_vector_int_t neis; - igraph_vector_int_t ptrlist; - igraph_vector_int_t ptrhead; - igraph_integer_t n; - igraph_integer_t to_reach, reached = 0, maxdist = 0; + 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; @@ -120,77 +118,79 @@ igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); + 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_VECTOR_INT_LIST_INIT_FINALLY(&paths, 0); - /* path_edge will store the shortest paths during the search */ - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&path_edge, 0); + 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_INT_INIT_FINALLY(&neis, 0); + 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_INT_INIT_FINALLY(&ptrlist, 0); + 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_INT_INIT_FINALLY(&ptrhead, no_of_nodes); + 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, igraph_integer_t); - IGRAPH_CHECK_OOM(geodist, "Insufficient memory for calculating shortest paths."); + 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_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); if (nrgeo) { - IGRAPH_CHECK(igraph_vector_int_resize(nrgeo, no_of_nodes)); - igraph_vector_int_null(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[ IGRAPH_VIT_GET(vit) ] == 0) { - geodist[ IGRAPH_VIT_GET(vit) ] = -1; + 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[ from ] < 0) { + if (geodist[ (long int) from ] < 0) { reached++; } /* from -> from */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); - IGRAPH_CHECK(igraph_vector_int_push_back(vptr, from)); - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); - - geodist[from] = 1; - VECTOR(ptrhead)[from] = 1; - IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, 0)); + 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)[from] = 1; + VECTOR(*nrgeo)[(long int)from] = 1; } /* Init queue */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, from)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + 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(); @@ -201,34 +201,20 @@ igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, * any of the nodes we wanted to reach */ if (actdist > maxdist) { /* safety check, maxdist should have been set when we reached the last node */ - IGRAPH_ASSERT(maxdist >= 0); + if (maxdist < 0) { + IGRAPH_ERROR("possible bug in igraph_get_all_shortest_paths, " + "maxdist is negative", IGRAPH_EINVAL); + } break; } } - /* If we need the edge-paths, we need to use igraph_incident() followed by an - * IGRAPH_OTHER() macro in the main loop. This is going to be slower than - * using igraph_neighbors() due to branch mispredictions in IGRAPH_OTHER(), so we - * use igraph_incident() only if the user needs the edge-paths */ - if (edges) { - IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); - } else { - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - } - - n = igraph_vector_int_size(&neis); - for (igraph_integer_t j = 0; j < n; j++) { - igraph_integer_t neighbor; - igraph_integer_t parentptr; - - if (edges) { - /* user needs the edge-paths, so 'neis' contains edge IDs, we need to resolve - * the next edge ID into a vertex ID */ - neighbor = IGRAPH_OTHER(graph, VECTOR(neis)[j], actnode); - } else { - /* user does not need the edge-paths, so 'neis' contains vertex IDs */ - neighbor = VECTOR(neis)[j]; - } + 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) { @@ -245,8 +231,8 @@ igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, } if (geodist[neighbor] <= 0) { /* this node was not reached yet, push it into the queue */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); if (geodist[neighbor] < 0) { reached++; } @@ -257,80 +243,76 @@ igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, geodist[neighbor] = actdist + 2; /* copy all existing paths to the parent */ - parentptr = VECTOR(ptrhead)[actnode]; - while (parentptr != 0) { - /* allocate a new igraph_vector_int_t at the end of paths */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); - IGRAPH_CHECK(igraph_vector_int_update(vptr, igraph_vector_int_list_get_ptr(&paths, parentptr - 1))); - IGRAPH_CHECK(igraph_vector_int_push_back(vptr, neighbor)); - - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); - if (actnode != from) { - /* If the previous vertex was the source then there is no edge to add*/ - IGRAPH_CHECK(igraph_vector_int_update(vptr_e, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1))); - } - IGRAPH_CHECK(igraph_vector_int_push_back(vptr_e, VECTOR(neis)[j])); - - IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, VECTOR(ptrhead)[neighbor])); - VECTOR(ptrhead)[neighbor] = igraph_vector_int_size(&ptrlist); - - parentptr = VECTOR(ptrlist)[parentptr - 1]; + 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_int_destroy(&q); + igraph_dqueue_destroy(&q); IGRAPH_FINALLY_CLEAN(1); /* mark the nodes for which we need the result */ - memset(geodist, 0, sizeof(geodist[0]) * (size_t) no_of_nodes); + 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[ IGRAPH_VIT_GET(vit) ] = 1; + geodist[ (long int) IGRAPH_VIT_GET(vit) ] = 1; } - if (vertices) { - igraph_vector_int_list_clear(vertices); - } - if (edges) { - igraph_vector_int_list_clear(edges); + /* 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]; + } + } } - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t parentptr = VECTOR(ptrhead)[i]; + 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, transfer them to the result vector */ - while (parentptr != 0) { - /* Given two vector lists, list1 and list2, an efficient way to transfer - * a vector from list1 to the end of list2 is to extend list2 with an - * empty vector, then swap that empty vector with the given element of - * list1. This approach avoids creating a full copy of the vector. */ - if (vertices) { - igraph_vector_int_t *p; - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &p)); - igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&paths, parentptr - 1)); - } - if (edges) { - igraph_vector_int_t *p; - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &p)); - igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1)); - } - parentptr = VECTOR(ptrlist)[parentptr - 1]; + /* 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_int_destroy(&ptrlist); - igraph_vector_int_destroy(&ptrhead); - igraph_vector_int_destroy(&neis); - igraph_vector_int_list_destroy(&paths); - igraph_vector_int_list_destroy(&path_edge); + igraph_vector_destroy(&ptrlist); + igraph_vector_destroy(&ptrhead); + igraph_vector_destroy(&neis); + igraph_vector_ptr_destroy(&paths); igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(7); + IGRAPH_FINALLY_CLEAN(6); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/astar.c b/src/vendor/cigraph/src/paths/astar.c deleted file mode 100644 index 3660f65e4b6..00000000000 --- a/src/vendor/cigraph/src/paths/astar.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - 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 - -*/ - -#include "igraph_paths.h" -#include "igraph_interface.h" -#include "igraph_adjlist.h" -#include "igraph_memory.h" - -#include "core/indheap.h" -#include "core/interruption.h" - -static igraph_error_t null_heuristic( - igraph_real_t *result, igraph_integer_t from, igraph_integer_t to, - void *extra -) { - IGRAPH_UNUSED(from); - IGRAPH_UNUSED(to); - IGRAPH_UNUSED(extra); - - *result = 0; - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_get_shortest_path_astar - * \brief A* gives the shortest path from one vertex to another, with heuristic. - * - * \experimental - * - * Calculates a shortest path from a single source vertex to a single - * target, using the A* algorithm. A* tries to find a shortest path by - * starting at \p from and moving to vertices that lie on a path with - * the lowest estimated length. This length estimate is the sum of two - * numbers: the distance from the source (\p from) to the intermediate vertex, - * and the value returned by the heuristic function. The heuristic function - * provides an estimate the distance between intermediate candidate - * vertices and the target vertex \p to. The A* algorithm is guaranteed - * to give the correct shortest path (if one exists) only if the heuristic - * does not overestimate distances, i.e. if the heuristic function is - * \em admissible. - * - * \param graph The input graph, it can be directed or undirected. - * \param vertices Pointer to an initialized vector or the \c NULL - * pointer. If not \c NULL, then the vertex IDs along - * the path are stored here, including the source and target - * vertices. - * \param edges Pointer to an initialized vector or the \c NULL - * pointer. If not \c NULL, 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 Optional edge weights. Supply \c NULL for unweighted graphs. - * All edge weights must be non-negative. Additionally, no - * edge weight may be NaN. If either case does not hold, an error - * is returned. - * \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. - * \param heuristic A function that provides distance estimates to the - * target vertex. See \ref igraph_astar_heuristic_func_t for - * more information. - * \param extra This is passed on to the heuristic function. - * \return Error code. - * - * Time complexity: In the worst case, O(|E|log|V|+|V|), where - * |V| is the number of vertices and - * |E| is the number of edges in the graph. - * The running time depends on the accuracy of the distance estimates - * returned by the heuristic function. Assuming that the heuristic - * is admissible, the better the estimates, the shortert the running - * time. - */ - -igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t from, - igraph_integer_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_astar_heuristic_func_t *heuristic, - void *extra) -{ - igraph_real_t heur_res; - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_2wheap_t Q; - igraph_lazy_inclist_t inclist; - igraph_vector_t dists; - igraph_integer_t *parent_eids; - - if (from < 0 || from >= no_of_nodes) { - IGRAPH_ERROR("Starting vertex out of range.", IGRAPH_EINVVID); - } - - if (to < 0 || to >= no_of_nodes) { - IGRAPH_ERROR("End vertex out of range.", IGRAPH_EINVVID); - } - - if (!heuristic) { - heuristic = null_heuristic; - } - - if (weights) { /* If there are no weights, they are treated as 1. */ - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - 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, found weight of %g.", IGRAPH_EINVAL, min); - } - else if (isnan(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_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS_TWICE)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); - - /* dists[v] is the length of the shortest path found so far between 'from' and 'v'. */ - IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); - igraph_vector_fill(&dists, IGRAPH_INFINITY); - - /* parent_eids[v] is the 1 + the ID of v's inbound edge in the shortest path tree. - * A value of 0 indicates unreachable vertices. */ - parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(parent_eids, "Can't calculate shortest paths."); - IGRAPH_FINALLY(igraph_free, parent_eids); - - VECTOR(dists)[from] = 0.0; - IGRAPH_CHECK(heuristic(&heur_res, from, to, extra)); - IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, from, -heur_res)); - - igraph_bool_t found = false; - while (!igraph_2wheap_empty(&Q)) { - IGRAPH_ALLOW_INTERRUPTION(); - - /* The from -> u -> to distance estimate is the sum of the - * from -> u distance and the u -> to distance estimate - * obtained from the heuristic. - * - * We use an indexed heap to process 'u' vertices in order - * of the smallest from -> u -> to distance estimate. Since - * we only have a maximum heap available, we store negated values - * in order to obtain smallest values first. The value taken off - * the heap is ignored, we just want the index of 'u'. */ - - igraph_integer_t u; - igraph_2wheap_delete_max_index(&Q, &u); - - /* Reached the target vertex, the search can be stopped. */ - if (u == to) { - found = true; - break; - } - - /* Now we check all neighbors 'u' for a path with a shorter actual (not estimated) - * length than what was found so far. */ - - igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, u); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - - igraph_integer_t nlen = igraph_vector_int_size(neis); - for (igraph_integer_t i = 0; i < nlen; i++) { - igraph_integer_t edge = VECTOR(*neis)[i]; - igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); - igraph_real_t altdist; /* candidate from -> v distance */ - if (weights) { - igraph_real_t weight = VECTOR(*weights)[edge]; - /* TODO: Should infinite-weight edges be skipped? - * See https://github.com/igraph/igraph/issues/2222 */ - if (weight == IGRAPH_INFINITY) continue; - altdist = VECTOR(dists)[u] + weight; - } else { - altdist = VECTOR(dists)[u] + 1; - } - igraph_real_t curdist = VECTOR(dists)[v]; - if (curdist == IGRAPH_INFINITY) { - /* This is the first finite from -> v distance we found. - * Here we rely on infinite weight edges having been skipped, see TODO above. */ - VECTOR(dists)[v] = altdist; - parent_eids[v] = edge + 1; - IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); - IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, v, -(altdist + heur_res))); - } else if (altdist < curdist) { - /* This is a shorter from -> v path than what was found before. */ - VECTOR(dists)[v] = altdist; - parent_eids[v] = edge + 1; - IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); - igraph_2wheap_modify(&Q, v, -(altdist + heur_res)); - } - } - } /* !igraph_2wheap_empty(&Q) */ - - if (!found) { - IGRAPH_WARNING("Couldn't reach the target vertex."); - } - - /* Reconstruct the shortest paths based on vertex and/or edge IDs */ - if (vertices || edges) { - igraph_integer_t size, act, edge; - - if (vertices) { - igraph_vector_int_clear(vertices); - } - if (edges) { - igraph_vector_int_clear(edges); - } - - IGRAPH_ALLOW_INTERRUPTION(); - - size = 0; - act = to; - while (parent_eids[act]) { - size++; - edge = parent_eids[act] - 1; - act = IGRAPH_OTHER(graph, edge, act); - } - if (vertices && (size > 0 || to == from)) { - IGRAPH_CHECK(igraph_vector_int_resize(vertices, size + 1)); - VECTOR(*vertices)[size] = to; - } - if (edges) { - IGRAPH_CHECK(igraph_vector_int_resize(edges, size)); - } - act = to; - while (parent_eids[act]) { - edge = parent_eids[act] - 1; - act = IGRAPH_OTHER(graph, edge, act); - size--; - if (vertices) { - VECTOR(*vertices)[size] = act; - } - if (edges) { - VECTOR(*edges)[size] = edge; - } - } - } - - - IGRAPH_FREE(parent_eids); - igraph_vector_destroy(&dists); - igraph_lazy_inclist_destroy(&inclist); - igraph_2wheap_destroy(&Q); - IGRAPH_FINALLY_CLEAN(4); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/paths/bellman_ford.c b/src/vendor/cigraph/src/paths/bellman_ford.c index 2bfaadb288e..06aea098241 100644 --- a/src/vendor/cigraph/src/paths/bellman_ford.c +++ b/src/vendor/cigraph/src/paths/bellman_ford.c @@ -24,33 +24,38 @@ #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_distances_bellman_ford + * \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_distances_dijkstra() . + * 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 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. - * \param weights The edge weights. There must not be any closed loop in + * \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_distances() is called. + * \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 @@ -60,30 +65,30 @@ * 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_distances() for a faster unweighted version - * or \ref igraph_distances_dijkstra() if you do not have negative + * \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 */ -igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_lazy_inclist_t inclist; - igraph_integer_t i; - igraph_integer_t no_of_from, no_of_to; - igraph_dqueue_int_t Q; - igraph_vector_bool_t clean_vertices; - igraph_vector_int_t num_queued; + 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; - int counter = 0; /* - speedup: a vertex is marked clean if its distance from the source @@ -94,25 +99,23 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, n times. */ if (!weights) { - return igraph_distances(graph, res, from, to, mode); + return igraph_shortest_paths(graph, res, from, to, mode); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - 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_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_INT_INIT_FINALLY(&Q, no_of_nodes); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&clean_vertices, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&num_queued, no_of_nodes); + 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); @@ -123,11 +126,6 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); IGRAPH_FINALLY(igraph_vit_destroy, &tovit); no_of_to = IGRAPH_VIT_SIZE(tovit); - - /* No need to check here whether the vertices in 'to' are unique because - * the loop below uses a temporary distance vector that is then copied - * into the result matrix at the end of the outer loop iteration, and - * this is safe even if 'to' contains the same vertex multiple times */ } IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); @@ -136,52 +134,47 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, for (IGRAPH_VIT_RESET(fromvit), i = 0; !IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { - igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + long int source = IGRAPH_VIT_GET(fromvit); - igraph_vector_fill(&dist, IGRAPH_INFINITY); + igraph_vector_fill(&dist, my_infinity); VECTOR(dist)[source] = 0; - igraph_vector_bool_null(&clean_vertices); - igraph_vector_int_null(&num_queued); + igraph_vector_null(&clean_vertices); + igraph_vector_null(&num_queued); /* Fill the queue with vertices to be checked */ - for (igraph_integer_t j = 0; j < no_of_nodes; j++) { - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, j)); + for (j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_push(&Q, j)); } - while (!igraph_dqueue_int_empty(&Q)) { - if (++counter >= 10000) { - counter = 0; - IGRAPH_ALLOW_INTERRUPTION(); - } + while (!igraph_dqueue_empty(&Q)) { + igraph_vector_int_t *neis; + long int nlen; - igraph_integer_t j = igraph_dqueue_int_pop(&Q); - VECTOR(clean_vertices)[j] = true; + 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("Negative loop in graph while calculating distances with Bellman-Ford algorithm.", - IGRAPH_ENEGLOOP); + 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 (VECTOR(dist)[j] == IGRAPH_INFINITY) { + if (!IGRAPH_FINITE(VECTOR(dist)[j])) { continue; } - igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); + nlen = igraph_vector_int_size(neis); - igraph_integer_t nlen = igraph_vector_int_size(neis); - for (igraph_integer_t k = 0; k < nlen; k++) { - igraph_integer_t nei = VECTOR(*neis)[k]; - igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); - igraph_real_t altdist = VECTOR(dist)[j] + VECTOR(*weights)[nei]; - if (VECTOR(dist)[target] > altdist) { + 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] = altdist; + VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = false; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + VECTOR(clean_vertices)[target] = 0; + IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); } } } @@ -191,10 +184,9 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, if (all_to) { igraph_matrix_set_row(res, &dist, i); } else { - igraph_integer_t j; for (IGRAPH_VIT_RESET(tovit), j = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { - igraph_integer_t v = IGRAPH_VIT_GET(tovit); + long int v = IGRAPH_VIT_GET(tovit); MATRIX(*res, i, j) = VECTOR(dist)[v]; } } @@ -209,29 +201,15 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, } igraph_vit_destroy(&fromvit); - igraph_dqueue_int_destroy(&Q); - igraph_vector_bool_destroy(&clean_vertices); - igraph_vector_int_destroy(&num_queued); + 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; + return 0; } -/** - * \function igraph_shortest_paths_bellman_ford - * \brief Weighted shortest path lengths between vertices, allowing negative weights (deprecated). - * - * \deprecated-by igraph_distances_bellman_ford 0.10.0 - */ -igraph_error_t 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) { - return igraph_distances_bellman_ford(graph, res, from, to, weights, mode); -} /** * \ingroup structural @@ -246,34 +224,44 @@ igraph_error_t igraph_shortest_paths_bellman_ford(const igraph_t *graph, * \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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * Supply a null pointer here if you don't need these vectors. + * \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 + * \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 must not be any closed loop in + * \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_get_shortest_paths() is called. + * 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 parents A pointer to an initialized igraph vector or null. - * If not null, a vector containing the parent of each vertex in + * \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 - * parent of vertex i in the tree is the vertex from which vertex i - * was reached. The parent of the start vertex (in the \c from - * argument) is -1. If the parent is -2, it means + * 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. @@ -292,7 +280,8 @@ igraph_error_t igraph_shortest_paths_bellman_ford(const igraph_t *graph, * \cli IGRAPH_EINVAL * The weight vector doesn't math the number of edges. * \cli IGRAPH_EINVVID - * \p from is invalid vertex ID + * \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 @@ -300,35 +289,35 @@ igraph_error_t igraph_shortest_paths_bellman_ford(const igraph_t *graph, * Time complexity: O(|E|*|V|), where |V| is the number of * vertices, |E| the number of edges. * - * \sa \ref igraph_get_shortest_paths() for a faster unweighted version - * or \ref igraph_get_shortest_paths_dijkstra() if you do not have negative + * \sa \ref igraph_shortest_paths() for a faster unweighted version + * or \ref igraph_shortest_paths_dijkstra() if you do not have negative * edge weights. */ -igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, +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_int_t *parents, - igraph_vector_int_t *inbound_edges) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t *parent_eids; + 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; - igraph_integer_t i, j, k; - igraph_dqueue_int_t Q; - igraph_vector_bool_t clean_vertices; - igraph_vector_int_t num_queued; + 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; - int counter = 0; if (!weights) { - return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, - parents, inbound_edges); + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + predecessors, inbound_edges); } if (from < 0 || from >= no_of_nodes) { @@ -339,104 +328,103 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, IGRAPH_ERROR("Weight vector length must match number of edges.", IGRAPH_EINVAL); } - IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, no_of_nodes); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&clean_vertices, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&num_queued, no_of_nodes); + 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(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_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(tovit))); + if (edges && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of `edges' and `to' should match.", IGRAPH_EINVAL); } - parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with Bellman-Ford."); - IGRAPH_FINALLY(igraph_free, parent_eids); - + 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, IGRAPH_INFINITY); + 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_int_push(&Q, j)); + IGRAPH_CHECK(igraph_dqueue_push(&Q, j)); } - while (!igraph_dqueue_int_empty(&Q)) { - if (++counter >= 10000) { - counter = 0; - IGRAPH_ALLOW_INTERRUPTION(); - } + while (!igraph_dqueue_empty(&Q)) { + igraph_vector_int_t *neis; + long int nlen; - j = igraph_dqueue_int_pop(&Q); - VECTOR(clean_vertices)[j] = true; + 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("Negative loop in graph while calculating distances with Bellman-Ford algorithm.", - IGRAPH_ENEGLOOP); + 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 (VECTOR(dist)[j] == IGRAPH_INFINITY) { + /* 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; } - igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); + nlen = igraph_vector_int_size(neis); - igraph_integer_t nlen = igraph_vector_int_size(neis); for (k = 0; k < nlen; k++) { - igraph_integer_t nei = VECTOR(*neis)[k]; - igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); - igraph_real_t altdist = VECTOR(dist)[j] + VECTOR(*weights)[nei]; - if (VECTOR(dist)[target] > altdist) { + 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] = altdist; - parent_eids[target] = nei + 1; + VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + parents[target] = nei + 1; if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = false; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + VECTOR(clean_vertices)[target] = 0; + IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); } } } } - /* Create `parents' if needed */ - if (parents) { - IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + /* 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(*parents)[i] = -1; - } else if (parent_eids[i] <= 0) { + VECTOR(*predecessors)[i] = i; + } else if (parents[i] <= 0) { /* i was not reached */ - VECTOR(*parents)[i] = -2; + VECTOR(*predecessors)[i] = -1; } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + /* 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_int_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (parent_eids[i] <= 0) { + if (parents[i] <= 0) { /* i was not reached */ VECTOR(*inbound_edges)[i] = -1; } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*inbound_edges)[i] = parents[i] - 1; } } } @@ -444,37 +432,37 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, /* 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(tovit); - igraph_integer_t size, act, edge; - igraph_vector_int_t *vvec = 0, *evec = 0; + long int node = IGRAPH_VIT_GET(tovit); + long int size, act, edge; + igraph_vector_t *vvec = 0, *evec = 0; if (vertices) { - vvec = igraph_vector_int_list_get_ptr(vertices, i); - igraph_vector_int_clear(vvec); + vvec = VECTOR(*vertices)[i]; + igraph_vector_clear(vvec); } if (edges) { - evec = igraph_vector_int_list_get_ptr(edges, i); - igraph_vector_int_clear(evec); + evec = VECTOR(*edges)[i]; + igraph_vector_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); size = 0; act = node; - while (parent_eids[act]) { + while (parents[act]) { size++; - edge = parent_eids[act] - 1; + edge = parents[act] - 1; act = IGRAPH_OTHER(graph, edge, act); } if (vvec && (size > 0 || node == from)) { - IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_resize(evec, size)); } act = node; - while (parent_eids[act]) { - edge = parent_eids[act] - 1; + while (parents[act]) { + edge = parents[act] - 1; act = IGRAPH_OTHER(graph, edge, act); size--; if (vvec) { @@ -493,10 +481,10 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, igraph_vit_destroy(&tovit); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_FREE(parent_eids); - igraph_dqueue_int_destroy(&Q); - igraph_vector_bool_destroy(&clean_vertices); - igraph_vector_int_destroy(&num_queued); + 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); @@ -505,10 +493,10 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, /** * \function igraph_get_shortest_path_bellman_ford - * \brief Weighted shortest path from one vertex to another one (Bellman-Ford). + * \brief Weighted shortest path from one vertex to another one. * - * Finds a weighted shortest path from a single source vertex to - * a single target using the Bellman-Ford algorithm. + * 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 @@ -516,15 +504,15 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, * * \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 + * 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 initialized vector or a null - * pointer. If not a null pointer, then the edge IDs along the + * \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 must not be any closed loop in + * \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 @@ -543,26 +531,28 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, * more target vertices. */ -igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, +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_int_list_t vertices2, *vp = &vertices2; - igraph_vector_int_list_t edges2, *ep = &edges2; + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; if (vertices) { - IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + 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_int_list_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; } else { ep = NULL; } @@ -571,16 +561,12 @@ igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, from, igraph_vss_1(to), weights, mode, NULL, NULL)); - /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the - result to the output parameter. */ if (edges) { - IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); - igraph_vector_int_list_destroy(&edges2); + igraph_vector_ptr_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); - igraph_vector_int_list_destroy(&vertices2); + igraph_vector_ptr_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } diff --git a/src/vendor/cigraph/src/paths/dijkstra.c b/src/vendor/cigraph/src/paths/dijkstra.c index 9207b52a0ca..2c1e32e56aa 100644 --- a/src/vendor/cigraph/src/paths/dijkstra.c +++ b/src/vendor/cigraph/src/paths/dijkstra.c @@ -24,33 +24,33 @@ #include "igraph_paths.h" #include "igraph_adjlist.h" +#include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_nongraph.h" #include "igraph_stack.h" -#include "igraph_vector_ptr.h" #include "core/indheap.h" #include "core/interruption.h" +#include "core/math.h" #include /* memset */ /** - * \function igraph_distances_dijkstra_cutoff - * \brief Weighted shortest path lengths between vertices, with cutoff. - * - * \experimental + * \function igraph_shortest_paths_dijkstra + * \brief Weighted shortest path lengths between vertices. * - * This function is similar to \ref igraph_distances_dijkstra(), but - * paths longer than \p cutoff will not be considered. + * 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 \p to argument. - * Vertices that are not reachable within distance \p cutoff will - * be assigned distance \c IGRAPH_INFINITY. + * 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. @@ -58,34 +58,29 @@ * 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_distances() is called. + * 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 cutoff The maximal length of paths that will be considered. - * When the distance of two vertices is greater than this value, - * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are - * treated as infinity. * \return Error code. * - * Time complexity: at most O(s |E| log|V| + |V|), where |V| is the number of - * vertices, |E| the number of edges and s the number of sources. The - * \p cutoff parameter will limit the number of edges traversed from each - * source vertex, which reduces the computation time. + * 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_distances_cutoff() for a (slightly) faster unweighted - * version. + * \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/distances.c + * \example examples/simple/dijkstra.c */ -igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, +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, - igraph_real_t cutoff) { + 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 @@ -94,37 +89,39 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, From now on we use a 2-way heap, so the distances can be queried directly from the heap. - Tricks: - - The opposite of the distance is stored in the heap, as it is a + 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. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_integer_t no_of_from, no_of_to; + long int no_of_from, no_of_to; igraph_lazy_inclist_t inclist; - igraph_integer_t i, j; + long int i, j; + igraph_real_t my_infinity = IGRAPH_INFINITY; igraph_bool_t all_to; - igraph_vector_int_t indexv; + igraph_vector_t indexv; if (!weights) { - return igraph_distances_cutoff(graph, res, from, to, mode, cutoff); + return igraph_shortest_paths(graph, res, from, to, mode); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - 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_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); - } else if (isnan(min)) { - IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + 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); } } @@ -141,21 +138,14 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, if (all_to) { no_of_to = no_of_nodes; } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&indexv, no_of_nodes); + 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); - - /* We need to check whether the vertices in 'tovit' are unique; this is - * because the inner while loop of the main algorithm updates the - * distance matrix whenever a shorter path is encountered from the - * source vertex 'i' to a target vertex, and we need to be able to - * map a target vertex to its column in the distance matrix. The mapping - * is constructed by the loop below */ for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { - igraph_integer_t v = IGRAPH_VIT_GET(tovit); + long int v = IGRAPH_VIT_GET(tovit); if (VECTOR(indexv)[v]) { - IGRAPH_ERROR("Target vertex list must not have any duplicates.", + IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", IGRAPH_EINVAL); } VECTOR(indexv)[v] = ++i; @@ -163,38 +153,28 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); - igraph_matrix_fill(res, IGRAPH_INFINITY); + igraph_matrix_fill(res, my_infinity); for (IGRAPH_VIT_RESET(fromvit), i = 0; !IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { - igraph_integer_t reached = 0; - igraph_integer_t source = IGRAPH_VIT_GET(fromvit); - + long int reached = 0; + long int source = IGRAPH_VIT_GET(fromvit); igraph_2wheap_clear(&Q); - - /* Many systems distinguish between +0.0 and -0.0. - * Since we store negative distances in the heap, - * we must insert -0.0 in order to get +0.0 as the - * final distance result. */ - igraph_2wheap_push_with_index(&Q, source, -0.0); + igraph_2wheap_push_with_index(&Q, source, -1.0); while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + long int minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - igraph_integer_t nlen; - - if (cutoff >= 0 && mindist > cutoff) { - continue; - } + long int nlen; if (all_to) { - MATRIX(*res, i, minnei) = mindist; + MATRIX(*res, i, minnei) = mindist - 1.0; } else { if (VECTOR(indexv)[minnei]) { - MATRIX(*res, i, VECTOR(indexv)[minnei] - 1) = mindist; + MATRIX(*res, i, (long int)(VECTOR(indexv)[minnei] - 1)) = mindist - 1.0; reached++; if (reached == no_of_to) { igraph_2wheap_clear(&Q); @@ -204,28 +184,21 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen = igraph_vector_int_size(neis); for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_real_t weight = VECTOR(*weights)[edge]; - - /* Optimization: do not follow infinite-weight edges. */ - if (weight == IGRAPH_INFINITY) continue; - - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); - igraph_real_t altdist = mindist + weight; - - if (! igraph_2wheap_has_elem(&Q, tto)) { + 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 (igraph_2wheap_has_active(&Q, tto)) { - igraph_real_t curdist = -igraph_2wheap_get(&Q, tto); - if (altdist < curdist) { - /* This is a shorter path */ - igraph_2wheap_modify(&Q, tto, -altdist); - } + } else if (altdist < curdist) { + /* This is a shorter path */ + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); } } @@ -235,7 +208,7 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, if (!all_to) { igraph_vit_destroy(&tovit); - igraph_vector_int_destroy(&indexv); + igraph_vector_destroy(&indexv); IGRAPH_FINALLY_CLEAN(2); } @@ -244,72 +217,7 @@ igraph_error_t igraph_distances_dijkstra_cutoff(const igraph_t *graph, igraph_vit_destroy(&fromvit); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_distances_dijkstra - * \brief Weighted shortest path lengths between vertices. - * - * This function implements Dijkstra's algorithm, which can find - * the weighted shortest path lengths from a source vertex to all - * other vertices. This function allows specifying a set of source - * and target vertices. The algorithm is run independently for each - * source and the results are retained only for the specified targets. - * This implementation uses a binary heap for efficiency. - * - * \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 \p to argument. - * Unreachable vertices have 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_distances() 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|V|+|V|), where |V| is the number of - * vertices, |E| the number of edges and s the number of sources. - * - * \sa \ref igraph_distances() for a (slightly) faster unweighted - * version or \ref igraph_distances_bellman_ford() for a weighted - * variant that works in the presence of negative edge weights (but no - * negative loops) - * - * \example examples/simple/distances.c - */ -igraph_error_t igraph_distances_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) { - return igraph_distances_dijkstra_cutoff(graph, res, from, to, weights, mode, -1); -} - -/** - * \function igraph_shortest_paths_dijkstra - * \brief Weighted shortest path lengths between vertices (deprecated). - * - * \deprecated-by igraph_distances_dijkstra 0.10.0 - */ -igraph_error_t 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) { - return igraph_distances_dijkstra(graph, res, from, to, weights, mode); + return 0; } /** @@ -321,17 +229,27 @@ igraph_error_t igraph_shortest_paths_dijkstra(const igraph_t *graph, * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * Supply a null pointer here if you don't need these vectors. + * \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 + * \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 @@ -350,12 +268,12 @@ igraph_error_t igraph_shortest_paths_dijkstra(const igraph_t *graph, * the directed graph is considered as an * undirected one for the computation. * \endclist - * \param parents A pointer to an initialized igraph vector or null. - * If not null, a vector containing the parent of each vertex in + * \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 - * parent of vertex i in the tree is the vertex from which vertex i - * was reached. The parent of the start vertex (in the \c from - * argument) is -1. If the parent is -2, it means + * 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. @@ -372,29 +290,30 @@ igraph_error_t igraph_shortest_paths_dijkstra(const igraph_t *graph, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p from is invalid vertex ID + * \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|V|+|V|), where |V| is the number of + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of * vertices and |E| is the number of edges * - * \sa \ref igraph_distances_dijkstra() if you only need the path length but + * \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 */ -igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, +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_int_t *parents, - igraph_vector_int_t *inbound_edges) { + 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 @@ -406,27 +325,27 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, - 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 isfinite() might involve a function call + 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. - - `parent_eids' assigns the inbound edge IDs of all vertices in the + - `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. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_integer_t *parent_eids; + long int *parents; igraph_bool_t *is_target; - igraph_integer_t i, to_reach; + long int i, to_reach; if (!weights) { return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, - parents, inbound_edges); + predecessors, inbound_edges); } if (from < 0 || from >= no_of_nodes) { @@ -434,26 +353,26 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + 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_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } - else if (isnan(min)) { - IGRAPH_ERROR("Weights must not contain NaN values.", 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(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_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); + 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)); @@ -464,33 +383,33 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); igraph_vector_fill(&dists, -1.0); - parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - if (parent_eids == 0) { - IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + parents = IGRAPH_CALLOC(no_of_nodes, long int); + if (parents == 0) { + IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); } - IGRAPH_FINALLY(igraph_free, parent_eids); + 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); /* LCOV_EXCL_LINE */ + 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[ IGRAPH_VIT_GET(vit) ]) { - is_target[ IGRAPH_VIT_GET(vit) ] = 1; + 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)[from] = 0.0; /* zero distance */ - parent_eids[from] = 0; + 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) { - igraph_integer_t nlen, minnei = igraph_2wheap_max_index(&Q); + long int nlen, minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_delete_max(&Q); igraph_vector_int_t *neis; @@ -502,24 +421,23 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen = igraph_vector_int_size(neis); for (i = 0; i < nlen; i++) { - igraph_integer_t edge = VECTOR(*neis)[i]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + 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; - parent_eids[tto] = edge + 1; + 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; - parent_eids[tto] = edge + 1; - igraph_2wheap_modify(&Q, tto, -altdist); + parents[tto] = edge + 1; + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); } } } /* !igraph_2wheap_empty(&Q) */ @@ -528,35 +446,35 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, IGRAPH_WARNING("Couldn't reach some vertices"); } - /* Create `parents' if needed */ - if (parents) { - IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + /* 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(*parents)[i] = -1; - } else if (parent_eids[i] <= 0) { + VECTOR(*predecessors)[i] = i; + } else if (parents[i] <= 0) { /* i was not reached */ - VECTOR(*parents)[i] = -2; + VECTOR(*predecessors)[i] = -1; } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + /* 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_int_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (parent_eids[i] <= 0) { + if (parents[i] <= 0) { /* i was not reached */ VECTOR(*inbound_edges)[i] = -1; } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; + /* i was reached via the edge with ID = parents[i] - 1 */ + VECTOR(*inbound_edges)[i] = parents[i] - 1; } } } @@ -564,37 +482,37 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, /* 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_integer_t size, act, edge; - igraph_vector_int_t *vvec = 0, *evec = 0; + long int node = IGRAPH_VIT_GET(vit); + long int size, act, edge; + igraph_vector_t *vvec = 0, *evec = 0; if (vertices) { - vvec = igraph_vector_int_list_get_ptr(vertices, i); - igraph_vector_int_clear(vvec); + vvec = VECTOR(*vertices)[i]; + igraph_vector_clear(vvec); } if (edges) { - evec = igraph_vector_int_list_get_ptr(edges, i); - igraph_vector_int_clear(evec); + evec = VECTOR(*edges)[i]; + igraph_vector_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); size = 0; act = node; - while (parent_eids[act]) { + while (parents[act]) { size++; - edge = parent_eids[act] - 1; + edge = parents[act] - 1; act = IGRAPH_OTHER(graph, edge, act); } if (vvec && (size > 0 || node == from)) { - IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_resize(evec, size)); } act = node; - while (parent_eids[act]) { - edge = parent_eids[act] - 1; + while (parents[act]) { + edge = parents[act] - 1; act = IGRAPH_OTHER(graph, edge, act); size--; if (vvec) { @@ -611,35 +529,33 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, igraph_2wheap_destroy(&Q); igraph_vector_destroy(&dists); IGRAPH_FREE(is_target); - IGRAPH_FREE(parent_eids); + IGRAPH_FREE(parents); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(6); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_get_shortest_path_dijkstra - * \brief Weighted shortest path from one vertex to another one (Dijkstra). + * \brief Weighted shortest path from one vertex to another one. * - * Finds a weighted shortest path from a single source vertex to - * a single target, using Dijkstra's algorithm. If more than one - * shortest path exists, an arbitrary one is returned. + * 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 + * 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 + * 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 initialized vector or a null - * pointer. If not a null pointer, then the edge IDs along the + * \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 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 @@ -652,55 +568,63 @@ igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, * ignored for undirected graphs. * \return Error code. * - * Time complexity: O(|E|log|V|+|V|), |V| is the number of vertices, + * 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. */ -igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, +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_int_list_t vertices2, *vp = &vertices2; - igraph_vector_int_list_t edges2, *ep = &edges2; + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; if (vertices) { - IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); + VECTOR(vertices2)[0] = vertices; } else { - vp = NULL; + vp = 0; } if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; } else { - ep = NULL; + ep = 0; } IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep, from, igraph_vss_1(to), - weights, mode, NULL, NULL)); + weights, mode, 0, 0)); - /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the - result to the output parameter. */ if (edges) { - IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); - igraph_vector_int_list_destroy(&edges2); + igraph_vector_ptr_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); - igraph_vector_int_list_destroy(&vertices2); + igraph_vector_ptr_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + 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)); } /** @@ -709,19 +633,13 @@ igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, * \brief All weighted shortest paths (geodesics) from a vertex. * * \param graph The graph object. - * \param vertices Pointer to an initialized integer vector list or NULL. - * If not NULL, then 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 edges Pointer to an initialized integer vector list or NULL. If - * not NULL, then each vector object contains the edges 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_int_t object or + * \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 @@ -729,7 +647,7 @@ igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, * 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 + * \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 @@ -753,24 +671,24 @@ igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p from is an invalid vertex ID + * \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|V|+|V|), where |V| is the number of + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of * vertices and |E| is the number of edges * - * \sa \ref igraph_distances_dijkstra() if you only need the path + * \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 */ -igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_vector_int_t *nrgeo, +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) { @@ -778,77 +696,57 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, it's basically the same. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_vector_int_t index; - igraph_vector_int_t order; - igraph_vector_ptr_t parents, parents_edge; - - unsigned char *is_target; /* uses more than two discrete values, can't be 'bool' */ - igraph_integer_t i, n, to_reach; - igraph_bool_t free_vertices = false; + 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, vertices, edges, nrgeo, from, to, mode); + return igraph_get_all_shortest_paths(graph, res, nrgeo, from, to, mode); } if (from < 0 || from >= no_of_nodes) { IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); } - if (vertices == NULL && nrgeo == NULL && edges == NULL) { + if (res == 0 && nrgeo == 0) { return IGRAPH_SUCCESS; } + if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); + 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_ERRORF("Edge weights must not be negative, got %g.", IGRAPH_EINVAL, min); + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } - else if (isnan(min)) { - IGRAPH_ERROR("Weights must not contain NaN values.", 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. Right now we do not use an - * igraph_vector_int_list_t because that would pre-initialize vectors - * for all the nodes even if the traversal would involve only a small part - * of the graph */ + * 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_vector_destroy); - - /* parents_edge stores a vector for each vertex, listing the parent edges - * of each vertex in the traversal */ - IGRAPH_CHECK(igraph_vector_ptr_init(&parents_edge, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents_edge); - IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&parents_edge, igraph_vector_destroy); - + igraph_vector_ptr_set_item_destructor(&parents, (igraph_finally_func_t*)igraph_vector_destroy); for (i = 0; i < no_of_nodes; i++) { - igraph_vector_int_t *parent_vec, *parent_edge_vec; - - parent_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(parent_vec, "Cannot calculate shortest paths."); - IGRAPH_FINALLY(igraph_free, parent_vec); - IGRAPH_CHECK(igraph_vector_int_init(parent_vec, 0)); + 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; - IGRAPH_FINALLY_CLEAN(1); - - parent_edge_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); - IGRAPH_CHECK_OOM(parent_edge_vec, "Cannot calculate shortest paths."); - IGRAPH_FINALLY(igraph_free, parent_edge_vec); - IGRAPH_CHECK(igraph_vector_int_init(parent_edge_vec, 0)); - VECTOR(parents_edge)[i] = parent_edge_vec; - IGRAPH_FINALLY_CLEAN(1); } /* distance of each vertex from the root */ @@ -857,11 +755,13 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* order lists the order of vertices in which they were found during * the traversal */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, 0); + 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); - IGRAPH_CHECK_OOM(is_target, "Cannot calculate shortest paths."); + 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 */ @@ -877,83 +777,76 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, 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[ IGRAPH_VIT_GET(vit) ]) { - is_target[ IGRAPH_VIT_GET(vit) ] = 1; + 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 */ + to_reach--; /* this node was given multiple times */ } } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - VECTOR(dists)[from] = 0.0; /* zero distance */ - igraph_2wheap_push_with_index(&Q, from, 0.0); + 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) { - igraph_integer_t nlen, minnei = igraph_2wheap_max_index(&Q); + 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_int_push_back(&order, minnei)); + IGRAPH_CHECK(igraph_vector_push_back(&order, minnei)); /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen = igraph_vector_int_size(neis); for (i = 0; i < nlen; i++) { - igraph_integer_t edge = VECTOR(*neis)[i]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + 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_int_t *parent_vec, *parent_edge_vec; + 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_int_t*)VECTOR(parents)[tto]; - IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); - parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; - IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); - + 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_int_t*) VECTOR(parents)[tto]; - IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); - parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[tto]; - IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + * 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_int_t*)VECTOR(parents)[tto]; - igraph_vector_int_clear(parent_vec); - IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); - parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; - igraph_vector_int_clear(parent_edge_vec); - IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); - - igraph_2wheap_modify(&Q, 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 of the requested target vertices."); + IGRAPH_WARNING("Couldn't reach some vertices"); } /* we don't need these anymore */ @@ -963,20 +856,20 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* printf("Order:\n"); - igraph_vector_int_print(&order); + igraph_vector_print(&order); printf("Parent vertices:\n"); for (i = 0; i < no_of_nodes; i++) { - if (igraph_vector_int_size(VECTOR(parents)[i]) > 0) { - printf("[%ld]: ", i); - igraph_vector_int_print(VECTOR(parents)[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_int_resize(nrgeo, no_of_nodes)); - igraph_vector_int_null(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 @@ -984,27 +877,26 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, * order vector anyway for building the final result, we could just as well * build nrgeo here. */ - VECTOR(*nrgeo)[from] = 1; - n = igraph_vector_int_size(&order); + VECTOR(*nrgeo)[(long int)from] = 1; + n = igraph_vector_size(&order); for (i = 1; i < n; i++) { - igraph_integer_t node, j, k; - igraph_vector_int_t *parent_vec; + long int node, j, k; + igraph_vector_t *parent_vec; - node = VECTOR(order)[i]; + node = (long int)VECTOR(order)[i]; /* now, take the parent vertices */ - parent_vec = (igraph_vector_int_t*)VECTOR(parents)[node]; - k = igraph_vector_int_size(parent_vec); + 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)[VECTOR(*parent_vec)[j]]; + VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[(long int)VECTOR(*parent_vec)[j]]; } } } - if (vertices || edges) { - igraph_vector_int_t *path, *parent_vec, *parent_edge_vec; - igraph_vector_t *paths_index; - igraph_stack_int_t stack; - igraph_integer_t j, node; + 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" @@ -1022,85 +914,77 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, } else { memset(is_target, 0, sizeof(unsigned char) * (size_t) no_of_nodes); - IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + 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 = IGRAPH_VIT_GET(vit); + i = (long int) IGRAPH_VIT_GET(vit); if (!is_target[i]) { is_target[i] = 1; - IGRAPH_CHECK(igraph_stack_int_push(&stack, i)); + IGRAPH_CHECK(igraph_stack_push(&stack, i)); } } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - while (!igraph_stack_int_empty(&stack)) { + while (!igraph_stack_empty(&stack)) { /* For each parent of node i, get its parents */ - igraph_integer_t el = igraph_stack_int_pop(&stack); - parent_vec = (igraph_vector_int_t*) VECTOR(parents)[el]; - i = igraph_vector_int_size(parent_vec); + 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 = VECTOR(*parent_vec)[j]; + n = (long int) VECTOR(*parent_vec)[j]; if (!is_target[n]) { is_target[n] = 2; - IGRAPH_CHECK(igraph_stack_int_push(&stack, n)); + IGRAPH_CHECK(igraph_stack_push(&stack, n)); } } } - igraph_stack_int_destroy(&stack); + 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 vertices where the shortest paths leading to vertex i + * 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_int_size(&order); + n = igraph_vector_size(&order); igraph_vector_null(paths_index); - if (edges) { - igraph_vector_int_list_clear(edges); - } - - if (vertices) { - igraph_vector_int_list_clear(vertices); - } else { - /* If the 'vertices' vector doesn't exist, then create one, in order - * for the algorithm to work. */ - vertices = IGRAPH_CALLOC(1, igraph_vector_int_list_t); - IGRAPH_CHECK_OOM(vertices, "Cannot calculate shortest paths."); - IGRAPH_FINALLY(igraph_free, vertices); - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(vertices, 0); - free_vertices = true; - } + /* 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 */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); - IGRAPH_CHECK(igraph_vector_int_push_back(path, from)); - - if (edges) { - /* the shortest path from the source to itself is empty */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); - } - VECTOR(*paths_index)[from] = 1; + 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++) { - igraph_integer_t m, path_count; - igraph_vector_int_t *parent_path, *parent_path_edge; + long int m, path_count; + igraph_vector_t *parent_path; - node = VECTOR(order)[i]; + 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 @@ -1113,13 +997,13 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* we are calculating the shortest paths of node now. */ /* first, we update the paths_index */ - path_count = igraph_vector_int_list_size(vertices); + 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_int_t*) VECTOR(parents)[node]; - parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[node]; - m = igraph_vector_int_size(parent_vec); + parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; + m = igraph_vector_size(parent_vec); /* printf("Calculating shortest paths to vertex %ld\n", node); @@ -1130,84 +1014,63 @@ igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, 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 */ - igraph_integer_t parent_node = VECTOR(*parent_vec)[j]; - igraph_integer_t parent_edge = VECTOR(*parent_edge_vec)[j]; - igraph_integer_t parent_path_idx = VECTOR(*paths_index)[parent_node] - 1; + 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 vertices\n", parent_path_idx); + 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_int_list_get_ptr(vertices, parent_path_idx); - if (igraph_vector_int_tail(parent_path) != parent_node) { + parent_path = (igraph_vector_t*)VECTOR(*res)[parent_path_idx]; + if (igraph_vector_tail(parent_path) != parent_node) { break; } - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); - - /* We need to re-read parent_path because the previous push_back_new() - * call might have reallocated the entire vector list */ - parent_path = igraph_vector_int_list_get_ptr(vertices, parent_path_idx); - IGRAPH_CHECK(igraph_vector_int_update(path, parent_path)); - IGRAPH_CHECK(igraph_vector_int_push_back(path, node)); - - if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); - if (parent_node != from) { - parent_path_edge = igraph_vector_int_list_get_ptr(edges, parent_path_idx); - IGRAPH_CHECK(igraph_vector_int_update(path, parent_path_edge)); - } - IGRAPH_CHECK(igraph_vector_int_push_back(path, parent_edge)); - } + 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)); } } } - /* free those paths from the result vector that we won't need */ - n = igraph_vector_int_list_size(vertices); - i = 0; - while (i < n) { - igraph_integer_t tmp; - path = igraph_vector_int_list_get_ptr(vertices, i); - tmp = igraph_vector_int_tail(path); - if (is_target[tmp] == 1) { + /* 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 */ - i++; + VECTOR(*res)[j] = path; + j++; } else { /* we don't need this path, free it */ - igraph_vector_int_list_discard_fast(vertices, i); - if (edges) { - igraph_vector_int_list_discard_fast(edges, i); - } - n--; + igraph_vector_destroy(path); free(path); } } + IGRAPH_CHECK(igraph_vector_ptr_resize(res, j)); - /* sort the remaining paths by the target vertices */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&index, 0); - igraph_vector_int_list_sort_ind(vertices, &index, igraph_vector_int_colex_cmp); - IGRAPH_CHECK(igraph_vector_int_list_permute(vertices, &index)); - if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_permute(edges, &index)); - } - igraph_vector_int_destroy(&index); - IGRAPH_FINALLY_CLEAN(1); + /* sort the paths by the target vertices */ + igraph_vector_ptr_sort(res, igraph_i_vector_tail_cmp); } /* free the allocated memory */ - if (free_vertices) { - igraph_vector_int_list_destroy(vertices); - IGRAPH_FREE(vertices); - IGRAPH_FINALLY_CLEAN(2); - } - - igraph_vector_int_destroy(&order); + igraph_vector_destroy(&order); IGRAPH_FREE(is_target); igraph_vector_destroy(&dists); igraph_vector_ptr_destroy_all(&parents); - igraph_vector_ptr_destroy_all(&parents_edge); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/distances.c b/src/vendor/cigraph/src/paths/distances.c index 66408de4889..1e5a91ef78a 100644 --- a/src/vendor/cigraph/src/paths/distances.c +++ b/src/vendor/cigraph/src/paths/distances.c @@ -2,7 +2,8 @@ /* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2021 The igraph development team + 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 @@ -15,7 +16,10 @@ GNU 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 . + 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" @@ -26,35 +30,35 @@ #include "igraph_vector.h" #include "igraph_interface.h" #include "igraph_adjlist.h" -#include "igraph_random.h" #include "core/interruption.h" -#include "core/indheap.h" -/* When vid_ecc is not NULL, only one vertex ID should be passed in vids. - * vid_ecc will then return the id of the vertex farthest from the one in - * vids. If unconn == false and not all other vertices were reachable from - * the single given vertex, -1 is returned in vid_ecc. */ -static igraph_error_t igraph_i_eccentricity(const igraph_t *graph, +static int igraph_i_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, - igraph_lazy_adjlist_t *adjlist, - igraph_integer_t *vid_ecc, - igraph_bool_t unconn) { + igraph_neimode_t mode, + const igraph_adjlist_t *adjlist) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; + int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_long_t q; igraph_vit_t vit; igraph_vector_int_t counted; - igraph_integer_t i, mark = 1; - igraph_integer_t min_degree = 0; + int i, mark = 1; + igraph_vector_t vneis; + igraph_vector_int_t *neis; - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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_VECTOR_INT_INIT_FINALLY(&counted, no_of_nodes); + 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); @@ -63,163 +67,61 @@ static igraph_error_t igraph_i_eccentricity(const igraph_t *graph, !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), mark++, i++) { - igraph_integer_t source; - igraph_integer_t nodes_reached = 1; + long int source; source = IGRAPH_VIT_GET(vit); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + 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_int_empty(&q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&q); - igraph_integer_t dist = igraph_dqueue_int_pop(&q); - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(adjlist, act); - igraph_integer_t j, n; + 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; - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + if (dist > VECTOR(*res)[i]) { + VECTOR(*res)[i] = dist; + } - n = igraph_vector_int_size(neis); - for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(*neis)[j]; - if (VECTOR(counted)[nei] != mark) { - VECTOR(counted)[nei] = mark; - nodes_reached++; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, dist + 1)); + 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)); + } } - } - if (vid_ecc) { - /* Return the vertex ID of the vertex which has the lowest - * degree of the vertices most distant from the starting - * vertex. Assumes there is only 1 vid in vids. Used for - * pseudo_diameter calculations. */ - if (dist > VECTOR(*res)[i] || (dist == VECTOR(*res)[i] && n < min_degree)) { - VECTOR(*res)[i] = dist; - *vid_ecc = act; - min_degree = n; + } 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)); + } } - } else if (dist > VECTOR(*res)[i]) { - VECTOR(*res)[i] = dist; } - } /* while !igraph_dqueue_int_empty(dqueue) */ + } /* while !igraph_dqueue_long_empty(dqueue) */ - if (nodes_reached != no_of_nodes && !unconn && vid_ecc) { - *vid_ecc = -1; - break; - } } /* 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_int_destroy(&q); + igraph_dqueue_long_destroy(&q); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; -} - -/** - * This function finds the weighted eccentricity and returns it via \p ecc. - * It's used for igraph_pseudo_diameter_dijkstra() and igraph_eccentricity_dijkstra(). - * \p vid_ecc returns the vertex id of the ecc with the greatest - * distance from \p vid_start. If two vertices have the same greatest distance, - * the one with the lowest degree is chosen. - * When the graph is not (strongly) connected and \p unconn is false, then \p ecc - * wil be set to infinity, and \p vid_ecc to -1; - */ -static igraph_error_t igraph_i_eccentricity_dijkstra( - const igraph_t *graph, - const igraph_vector_t *weights, - igraph_real_t *ecc, - igraph_integer_t vid_start, - igraph_integer_t *vid_ecc, - igraph_bool_t unconn, - igraph_lazy_inclist_t *inclist) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_2wheap_t Q; - igraph_vector_t vec_dist; - igraph_integer_t i; - igraph_real_t degree_ecc, dist; - igraph_integer_t degree_i; - igraph_vector_int_t *neis; - - IGRAPH_VECTOR_INIT_FINALLY(&vec_dist, no_of_nodes); - igraph_vector_fill(&vec_dist, IGRAPH_INFINITY); - IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); - IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); - - igraph_2wheap_clear(&Q); - igraph_2wheap_push_with_index(&Q, vid_start, -1.0); - - while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&Q); - igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); - igraph_integer_t nlen; - - VECTOR(vec_dist)[minnei] = mindist - 1.0; - - /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - nlen = igraph_vector_int_size(neis); - for (i = 0; i < nlen; i++) { - igraph_integer_t edge = VECTOR(*neis)[i]; - igraph_integer_t 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_2wheap_modify(&Q, tto, -altdist); - } - } - } - - *ecc = 0; - *vid_ecc = vid_start; - degree_ecc = 0; - - for (i = 0; i < no_of_nodes; i++) { - if (i == vid_start) { - continue; - } - dist = VECTOR(vec_dist)[i]; - - /* inclist is used to ignore multiple edges when finding the degree */ - neis = igraph_lazy_inclist_get(inclist, i); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - - degree_i = igraph_vector_int_size(neis); - - if (dist > *ecc) { - if (!isfinite(dist)) { - if (!unconn) { - *ecc = IGRAPH_INFINITY; - *vid_ecc = -1; - break; - } - } else { - *ecc = dist; - *vid_ecc = i; - degree_ecc = degree_i; - } - } else if (dist == *ecc) { - if (degree_i < degree_ecc) { - degree_ecc = degree_i; - *vid_ecc = i; - } - } - } - igraph_2wheap_destroy(&Q); - igraph_vector_destroy(&vec_dist); - IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** @@ -254,99 +156,12 @@ static igraph_error_t igraph_i_eccentricity_dijkstra( * \example examples/simple/igraph_eccentricity.c */ -igraph_error_t igraph_eccentricity(const igraph_t *graph, +int igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode) { - igraph_lazy_adjlist_t adjlist; - - 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_i_eccentricity(graph, res, vids, &adjlist, - /*vid_ecc*/ NULL, /*unconn*/ 1)); - igraph_lazy_adjlist_destroy(&adjlist); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_eccentricity_dijkstra - * \brief Eccentricity of some vertices, using weighted edges. - * - * 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 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_eccentricity() is called. - * \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. - * - */ - -igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_t *res, - igraph_vs_t vids, - igraph_neimode_t mode) { - igraph_lazy_inclist_t inclist; - igraph_vit_t vit; - igraph_integer_t dump; - igraph_real_t ecc; - igraph_integer_t no_of_edges = igraph_ecount(graph); - - if (weights == NULL) { - return igraph_eccentricity(graph, res, vids, mode); - } - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - 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 (isnan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - } - - IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, - IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); - - IGRAPH_CHECK(igraph_vector_resize(res, 0)); - IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); - - for (IGRAPH_VIT_RESET(vit); - !IGRAPH_VIT_END(vit); - IGRAPH_VIT_NEXT(vit)) { - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc, IGRAPH_VIT_GET(vit), /*vid_ecc*/ &dump, /*unconn*/ 1, &inclist)); - IGRAPH_CHECK(igraph_vector_push_back(res, ecc)); - } - igraph_lazy_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return igraph_i_eccentricity(graph, res, vids, mode, /*adjlist=*/ 0); } /** @@ -374,538 +189,26 @@ igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, * \example examples/simple/igraph_radius.c */ -igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, +int igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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_eccentricity(graph, &ecc, igraph_vss_all(), - mode)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc, igraph_vss_all(), + mode, &adjlist)); *radius = igraph_vector_min(&ecc); igraph_vector_destroy(&ecc); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_pseudo_diameter - * \brief Approximation and lower bound of diameter. - * - * This algorithm finds a pseudo-peripheral vertex and returns its - * eccentricity. This value can be used as an approximation - * and lower bound of the diameter of a graph. - * - * - * A pseudo-peripheral vertex is a vertex v, such that for every - * vertex u which is as far away from v as possible, v is also as - * far away from u as possible. The process of finding one depends - * on where the search starts, and for a disconnected graph the - * maximum diameter found will be that of the component \p vid_start - * is in. - * - * \param graph The input graph, if it is directed, its edge directions - * are ignored. - * \param diameter Pointer to a real variable, the result is stored - * here. - * \param vid_start Id of the starting vertex. If this is negative, a - * random starting vertex is chosen. - * \param from Pointer to an integer, if not \c NULL it will be set to the - * source vertex of the diameter path. If \p unconn is \c false, and - * a disconnected graph is detected, this is set to -1. - * \param to Pointer to an integer, if not \c NULL it will be set to the - * target vertex of the diameter path. If \p unconn is \c false, and - * a disconnected graph is detected, this is set to -1. - * \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. - * - * Time complexity: O(|V||E|)), where |V| is the number of - * vertices and |E| is the number of edges. - * - * \sa \ref igraph_eccentricity(), \ref igraph_diameter(). - * - */ -igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, - igraph_real_t *diameter, - igraph_integer_t vid_start, - igraph_integer_t *from, - igraph_integer_t *to, - igraph_bool_t directed, - igraph_bool_t unconn) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_real_t ecc_v; - igraph_real_t ecc_u; - igraph_integer_t vid_ecc; - igraph_integer_t ito, ifrom; - igraph_bool_t inf = false; - - if (vid_start >= no_of_nodes) { - IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); - } - - /* We will reach here when vid_start < 0 and the graph has no vertices. */ - if (no_of_nodes == 0) { - if (diameter) { - *diameter = IGRAPH_NAN; - } - if (from) { - *from = -1; - } - if (to) { - *to = -1; - } - return IGRAPH_SUCCESS; - } - - if (vid_start < 0) { - RNG_BEGIN(); - vid_start = RNG_INTEGER(0, no_of_nodes - 1); - RNG_END(); - } - - if (!igraph_is_directed(graph) || !directed) { - igraph_lazy_adjlist_t adjlist; - igraph_vector_t ecc_vec; - - IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, - IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); - ifrom = vid_start; - IGRAPH_VECTOR_INIT_FINALLY(&ecc_vec, no_of_nodes); - - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_start), - &adjlist, &vid_ecc, unconn)); - ecc_u = VECTOR(ecc_vec)[0]; - - if (!unconn && vid_ecc == -1) { - inf = true; - } else { - while (true) { - IGRAPH_ALLOW_INTERRUPTION(); - - ito = vid_ecc; - - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_ecc), - &adjlist, &vid_ecc, 1)); - - ecc_v = VECTOR(ecc_vec)[0]; - - if (ecc_u < ecc_v) { - ecc_u = ecc_v; - ifrom = ito; - } else { - break; - } - } - } - igraph_vector_destroy(&ecc_vec); - igraph_lazy_adjlist_destroy(&adjlist); - IGRAPH_FINALLY_CLEAN(2); - } else { - igraph_vector_t ecc_out; - igraph_vector_t ecc_in; - igraph_integer_t vid_ecc_in; - igraph_integer_t vid_ecc_out; - igraph_integer_t vid_end; - igraph_bool_t direction; - igraph_lazy_adjlist_t adjlist_in; - igraph_lazy_adjlist_t adjlist_out; - - IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_in, IGRAPH_IN, - IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_in); - IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_out, IGRAPH_OUT, - IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_out); - - IGRAPH_VECTOR_INIT_FINALLY(&ecc_in, igraph_vcount(graph)); - IGRAPH_VECTOR_INIT_FINALLY(&ecc_out, igraph_vcount(graph)); - - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_start), - &adjlist_out, &vid_ecc_out, unconn)); - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_start), - &adjlist_in, &vid_ecc_in, unconn)); - - /* A directed graph is strongly connected iff all vertices are reachable - * from vid_start both when moving along or moving opposite the edge directions. */ - if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { - inf = true; - } else { - if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { - vid_ecc = vid_ecc_out; - ecc_u = VECTOR(ecc_out)[0]; - } else { - vid_ecc = vid_ecc_in; - ecc_u = VECTOR(ecc_in)[0]; - } - - while (1) { - IGRAPH_ALLOW_INTERRUPTION(); - - vid_end = vid_ecc; - - /* TODO: In the undirected case, we break ties between vertices at the - * same distance based on their degree. In te directed case, should we - * use in-, out- or total degree? */ - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_ecc), - &adjlist_out, &vid_ecc_out, 1)); - IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_ecc), - &adjlist_in, &vid_ecc_in, 1)); - - if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { - vid_ecc = vid_ecc_out; - ecc_v = VECTOR(ecc_out)[0]; - direction = 1; - } else { - vid_ecc = vid_ecc_in; - ecc_v = VECTOR(ecc_in)[0]; - direction = 0; - } - - if (ecc_u < ecc_v) { - ecc_u = ecc_v; - vid_start = vid_end; - } else { - break; - } - } - - if (direction) { - ifrom = vid_end; - ito = vid_start; - } else { - ifrom = vid_start; - ito = vid_end; - } - - } - igraph_vector_destroy(&ecc_out); - igraph_vector_destroy(&ecc_in); - igraph_lazy_adjlist_destroy(&adjlist_in); - igraph_lazy_adjlist_destroy(&adjlist_out); - IGRAPH_FINALLY_CLEAN(4); - } - - if (inf) { - if (diameter) { - *diameter = IGRAPH_INFINITY; - } - if (from) { - *from = -1; - } - if (to) { - *to = -1; - } - } else { - if (diameter) { - *diameter = ecc_u; - } - if (from) { - *from = ifrom; - } - if (to) { - *to = ito; - } - } - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_pseudo_diameter_dijkstra - * \brief Approximation and lower bound of the diameter of a weighted graph. - * - * This algorithm finds a pseudo-peripheral vertex and returns its - * weighted eccentricity. This value can be used as an approximation - * and lower bound of the diameter of a graph. - * - * - * A pseudo-peripheral vertex is a vertex v, such that for every - * vertex u which is as far away from v as possible, v is also as - * far away from u as possible. The process of finding one depends - * on where the search starts, and for a disconnected graph the - * maximum diameter found will be that of the component \p vid_start - * is in. - * - * - * If the graph has no vertices, \c IGRAPH_NAN is returned. - * - * \param graph The input graph, can be directed or undirected. - * \param weights The edge weights of the graph. Can be \c NULL for an - * unweighted graph. All weights should be non-negative. - * \param diameter This will contain the weighted pseudo-diameter. - * \param vid_start Id of the starting vertex. If this is negative, a - * random starting vertex is chosen. - * \param from If not \c NULL this 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 to If not \c NULL this 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 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. - * - * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices, - * |E| is the number of edges. - * - * \sa \ref igraph_diameter_dijkstra() - */ -igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_real_t *diameter, - igraph_integer_t vid_start, - igraph_integer_t *from, - igraph_integer_t *to, - igraph_bool_t directed, - igraph_bool_t unconn) { - - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_real_t ecc_v; - igraph_real_t ecc_u; - igraph_integer_t vid_ecc; - igraph_integer_t ito, ifrom; - igraph_bool_t inf = false; - - if (vid_start >= no_of_nodes) { - IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); - } - - if (!weights) { - return igraph_pseudo_diameter(graph, diameter, vid_start, from, to, directed, unconn); - } - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - 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 (isnan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - } - - /* We will reach here when vid_start < 0 and the graph has no vertices. */ - if (no_of_nodes == 0) { - if (diameter) { - *diameter = IGRAPH_NAN; - } - if (from) { - *from = -1; - } - if (to) { - *to = -1; - } - return IGRAPH_SUCCESS; - } - - if (vid_start < 0) { - RNG_BEGIN(); - vid_start = RNG_INTEGER(0, no_of_nodes - 1); - RNG_END(); - } - - if (!igraph_is_directed(graph) || !directed) { - igraph_lazy_inclist_t inclist; - IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); - - ifrom = vid_start; - - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_u, vid_start, &vid_ecc, unconn, &inclist)); - - inf = !isfinite(ecc_u); - - if (!inf) { - while (1) { - IGRAPH_ALLOW_INTERRUPTION(); - - ito = vid_ecc; - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_v, vid_ecc, &vid_ecc, unconn, &inclist)); - - if (ecc_u < ecc_v) { - ecc_u = ecc_v; - ifrom = ito; - } else { - break; - } - } - } - igraph_lazy_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(1); - } else { - igraph_real_t ecc_out; - igraph_real_t ecc_in; - igraph_integer_t vid_ecc_in; - igraph_integer_t vid_ecc_out; - igraph_integer_t vid_end; - igraph_bool_t direction; - igraph_lazy_inclist_t inclist_out; - igraph_lazy_inclist_t inclist_in; - - IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_out, IGRAPH_OUT, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_out); - IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_in, IGRAPH_IN, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_in); - - - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_start, &vid_ecc_out, unconn, &inclist_out)); - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_start, &vid_ecc_in, unconn, &inclist_in)); - - /* A directed graph is strongly connected iff all vertices are reachable - * from vid_start both when moving along or moving opposite the edge directions. */ - if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { - inf = true; - } else { - if (ecc_out > ecc_in) { - vid_ecc = vid_ecc_out; - ecc_u = ecc_out; - } else { - vid_ecc = vid_ecc_in; - ecc_u = ecc_in; - } - - while (1) { - IGRAPH_ALLOW_INTERRUPTION(); - - vid_end = vid_ecc; - - /* TODO: In the undirected case, we break ties between vertices at the - * same distance based on their degree. In te directed case, should we - * use in-, out- or total degree? */ - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_ecc, &vid_ecc_out, unconn, &inclist_out)); - IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_ecc, &vid_ecc_in, unconn, &inclist_in)); - - if (ecc_out > ecc_in) { - vid_ecc = vid_ecc_out; - ecc_v = ecc_out; - direction = 1; - } else { - vid_ecc = vid_ecc_in; - ecc_v = ecc_in; - direction = 0; - } - - if (ecc_u < ecc_v) { - ecc_u = ecc_v; - vid_start = vid_end; - } else { - break; - } - } - - if (direction) { - ifrom = vid_end; - ito = vid_start; - } else { - ifrom = vid_start; - ito = vid_end; - } - } - igraph_lazy_inclist_destroy(&inclist_out); - igraph_lazy_inclist_destroy(&inclist_in); + igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(2); } - if (inf) { - if (diameter) { - *diameter = IGRAPH_INFINITY; - } - if (from) { - *from = -1; - } - if (to) { - *to = -1; - } - } else { - if (diameter) { - *diameter = ecc_u; - } - if (from) { - *from = ifrom; - } - if (to) { - *to = ito; - } - } - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_graph_center - * \brief Central vertices of a graph. - * - * The central vertices of a graph are calculated by finding the vertices - * with the minimum eccentricity. This concept is typically applied to - * connected graphs. In undirected disconnected graphs, the calculation - * is effectively done per connected component. - * - * \param graph The input graph, it can be directed or undirected. - * \param res Pointer to an initialized vector, 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(). - * - */ -igraph_error_t igraph_graph_center( - const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode -) { - - igraph_vector_t ecc; - - igraph_vector_int_clear(res); - if (igraph_vcount(graph) == 0) { - return IGRAPH_SUCCESS; - } - - IGRAPH_VECTOR_INIT_FINALLY(&ecc, 0); - IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), mode)); - - /* igraph_eccentricity() does not return infinity or NaN, and the null graph - * case was handled above, therefore calling vector_min() is safe. */ - igraph_real_t min_eccentricity = igraph_vector_min(&ecc); - igraph_real_t n = igraph_vector_size(&ecc); - for (igraph_integer_t i = 0; i < n; i++) { - if (VECTOR(ecc)[i] == min_eccentricity) { - IGRAPH_CHECK(igraph_vector_int_push_back(res, i)); - } - } - - igraph_vector_destroy(&ecc); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/eulerian.c b/src/vendor/cigraph/src/paths/eulerian.c index 54979962cbf..f1d15e0f015 100644 --- a/src/vendor/cigraph/src/paths/eulerian.c +++ b/src/vendor/cigraph/src/paths/eulerian.c @@ -40,44 +40,42 @@ 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 igraph_error_t 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) { +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_int_t degree; - igraph_vector_int_t csize; + igraph_vector_t degree, csize; /* boolean vector to mark singletons: */ - igraph_vector_int_t nonsingleton; - igraph_integer_t i, n, vsize; - igraph_integer_t cluster_count; + igraph_vector_t nonsingleton; + long int i, n, vsize; + long int cluster_count; /* number of self-looping singletons: */ - igraph_integer_t es; + long int es; /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ - igraph_integer_t ens; + 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 = true; - *has_cycle = true; + *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_INT_INIT_FINALLY(&csize, 0); - IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); cluster_count = 0; - vsize = igraph_vector_int_size(&csize); + 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 = false; - *has_cycle = false; - igraph_vector_int_destroy(&csize); + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -85,12 +83,12 @@ static igraph_error_t igraph_i_is_eulerian_undirected( } } - igraph_vector_int_destroy(&csize); + igraph_vector_destroy(&csize); IGRAPH_FINALLY_CLEAN(1); /* the graph is connected except for singletons */ /* find singletons (including those with self-loops) */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&nonsingleton, 0); + 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: @@ -98,13 +96,13 @@ static igraph_error_t igraph_i_is_eulerian_undirected( * - > 2 odd means no path * plus there are a few corner cases with singletons */ - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + 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++) { - igraph_integer_t deg = VECTOR(degree)[i]; + long int deg = (long int) VECTOR(degree)[i]; /* Eulerian is about edges, so skip free vertices */ if (deg == 0) continue; @@ -121,68 +119,66 @@ static igraph_error_t igraph_i_is_eulerian_undirected( 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 = false; - *has_cycle = false; - igraph_vector_int_destroy(&nonsingleton); - igraph_vector_int_destroy(°ree); + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } } - igraph_vector_int_destroy(&nonsingleton); + 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 = false; - *has_cycle = false; + *has_path = 0; + *has_cycle = 0; } else if (odd == 2) { - *has_path = true; - *has_cycle = false; + *has_path = 1; + *has_cycle = 0; } else { - *has_path = true; - *has_cycle = true; + *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 && VECTOR(degree)[i] > 0) || (!*has_cycle && VECTOR(degree)[i] %2 == 1)) { + 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_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static igraph_error_t 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) { +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; - igraph_integer_t i, vsize; - igraph_integer_t cluster_count; - igraph_vector_int_t out_degree, in_degree; - igraph_vector_int_t csize; + long int i, vsize; + long int cluster_count; + igraph_vector_t out_degree, in_degree, csize; /* boolean vector to mark singletons: */ - igraph_vector_int_t nonsingleton; + igraph_vector_t nonsingleton; /* number of self-looping singletons: */ - igraph_integer_t es; + long int es; /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ - igraph_integer_t ens; + 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 = true; - *has_cycle = true; + *has_path = 1; + *has_cycle = 1; return IGRAPH_SUCCESS; } @@ -192,19 +188,19 @@ static igraph_error_t igraph_i_is_eulerian_directed( /* 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_INT_INIT_FINALLY(&csize, 0); + IGRAPH_VECTOR_INIT_FINALLY(&csize, 0); - IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); cluster_count = 0; - vsize = igraph_vector_int_size(&csize); + 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 = false; - *has_cycle = false; - igraph_vector_int_destroy(&csize); + *has_path = 0; + *has_cycle = 0; + igraph_vector_destroy(&csize); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -212,28 +208,28 @@ static igraph_error_t igraph_i_is_eulerian_directed( } } - igraph_vector_int_destroy(&csize); + 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_INT_INIT_FINALLY(&nonsingleton, 0); + 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_INT_INIT_FINALLY(&out_degree, 0); + IGRAPH_VECTOR_INIT_FINALLY(&out_degree, 0); IGRAPH_CHECK(igraph_degree(graph, &out_degree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&in_degree, 0); + 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++) { - igraph_integer_t degin = VECTOR(in_degree)[i]; - igraph_integer_t degout = VECTOR(out_degree)[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; @@ -251,11 +247,11 @@ static igraph_error_t igraph_i_is_eulerian_directed( 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 = false; - *has_cycle = false; - igraph_vector_int_destroy(&nonsingleton); - igraph_vector_int_destroy(&in_degree); - igraph_vector_int_destroy(&out_degree); + *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; @@ -286,25 +282,25 @@ static igraph_error_t igraph_i_is_eulerian_directed( * 1. 1+ vertices have 2+ in/out * 2. 2+ nodes have 1+ in/out */ if (incoming_excess > 1 || outgoing_excess > 1) { - *has_path = false; - *has_cycle = false; - igraph_vector_int_destroy(&nonsingleton); - igraph_vector_int_destroy(&in_degree); - igraph_vector_int_destroy(&out_degree); + *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 = true; + *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_int_destroy(&nonsingleton); - igraph_vector_int_destroy(&in_degree); - igraph_vector_int_destroy(&out_degree); + igraph_vector_destroy(&nonsingleton); + igraph_vector_destroy(&in_degree); + igraph_vector_destroy(&out_degree); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -314,24 +310,23 @@ static igraph_error_t igraph_i_is_eulerian_directed( /** * \ingroup Eulerian * \function igraph_is_eulerian - * \brief Checks whether an Eulerian path or cycle exists. + * \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. - * Must not be \c NULL. * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. - * Must not be \c NULL. * \return Error code: - * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_ENOMEM, not enough memory for + * temporary data. * * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges. * */ -igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle) { +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)) { @@ -343,62 +338,67 @@ igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path } -static igraph_error_t igraph_i_eulerian_path_undirected( - const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, - igraph_integer_t start_of_path) { - - igraph_integer_t curr; +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_int_t path, tracker, edge_tracker, edge_path; + igraph_stack_t path, tracker, edge_tracker, edge_path; igraph_vector_bool_t visited_list; - igraph_vector_int_t degree; + igraph_vector_t degree; n = igraph_vcount(graph); m = igraph_ecount(graph); if (edge_res) { - igraph_vector_int_clear(edge_res); + igraph_vector_clear(edge_res); } if (vertex_res) { - igraph_vector_int_clear(vertex_res); + igraph_vector_clear(vertex_res); } if (m == 0 || n == 0) { return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); - IGRAPH_STACK_INT_INIT_FINALLY(&path, n); - IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); - IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); - IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); + 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_int_push(&tracker, start_of_path)); + 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_int_empty(&tracker)) { + while (!igraph_stack_empty(&tracker)) { if (VECTOR(degree)[curr] != 0) { igraph_vector_int_t *incedges; - igraph_integer_t nc, edge = -1; - igraph_integer_t j, next; - IGRAPH_CHECK(igraph_stack_int_push(&tracker, curr)); + 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 = VECTOR(*incedges)[j]; + edge = (long) VECTOR(*incedges)[j]; if (!VECTOR(visited_list)[edge]) { break; } @@ -406,7 +406,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( next = IGRAPH_OTHER(graph, edge, curr); - IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); /* remove edge here */ VECTOR(degree)[curr]--; @@ -416,97 +416,102 @@ static igraph_error_t igraph_i_eulerian_path_undirected( curr = next; } else { /* back track to find remaining circuit */ igraph_integer_t curr_e; - IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); - curr = igraph_stack_int_pop(&tracker); - if (!igraph_stack_int_empty(&edge_tracker)) { - curr_e = igraph_stack_int_pop(&edge_tracker); - IGRAPH_CHECK(igraph_stack_int_push(&edge_path, 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_int_reserve(edge_res, m)); - while (!igraph_stack_int_empty(&edge_path)) { - IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); + 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_int_reserve(vertex_res, m+1)); - while (!igraph_stack_int_empty(&path)) { - IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); + 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_int_destroy(&path); - igraph_stack_int_destroy(&tracker); - igraph_stack_int_destroy(&edge_path); - igraph_stack_int_destroy(&edge_tracker); + 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_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; } /* solution adapted from https://www.geeksforgeeks.org/hierholzers-algorithm-directed-graph/ */ -static igraph_error_t igraph_i_eulerian_path_directed( - const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, - igraph_integer_t start_of_path) { - - igraph_integer_t curr; +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_int_t path, tracker, edge_tracker, edge_path; + igraph_stack_t path, tracker, edge_tracker, edge_path; igraph_vector_bool_t visited_list; - igraph_vector_int_t remaining_out_edges; + igraph_vector_t remaining_out_edges; n = igraph_vcount(graph); m = igraph_ecount(graph); if (edge_res) { - igraph_vector_int_clear(edge_res); + igraph_vector_clear(edge_res); } if (vertex_res) { - igraph_vector_int_clear(vertex_res); + igraph_vector_clear(vertex_res); } if (m == 0 || n == 0) { return IGRAPH_SUCCESS; } - IGRAPH_STACK_INT_INIT_FINALLY(&path, n); - IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); - IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); - IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); + 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_int_push(&tracker, start_of_path)); + 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_INT_INIT_FINALLY(&remaining_out_edges, 0); + 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_int_empty(&tracker)) { + while (!igraph_stack_empty(&tracker)) { if (VECTOR(remaining_out_edges)[curr] != 0) { igraph_vector_int_t *incedges; - igraph_integer_t nc, edge = -1; - igraph_integer_t j, next; - IGRAPH_CHECK(igraph_stack_int_push(&tracker, curr)); + 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 = VECTOR(*incedges)[j]; + edge = (long) VECTOR(*incedges)[j]; if (!VECTOR(visited_list)[edge]) { break; } @@ -514,7 +519,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( next = IGRAPH_TO(graph, edge); - IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); + IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); /* remove edge here */ VECTOR(remaining_out_edges)[curr]--; @@ -523,35 +528,35 @@ static igraph_error_t igraph_i_eulerian_path_directed( curr = next; } else { /* back track to find remaining circuit */ igraph_integer_t curr_e; - IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); - curr = igraph_stack_int_pop(&tracker); - if (!igraph_stack_int_empty(&edge_tracker)) { - curr_e = igraph_stack_int_pop(&edge_tracker); - IGRAPH_CHECK(igraph_stack_int_push(&edge_path, 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_int_reserve(edge_res, m)); - while (!igraph_stack_int_empty(&edge_path)) { - IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_pop(&edge_path))); + 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_int_reserve(vertex_res, m+1)); - while (!igraph_stack_int_empty(&path)) { - IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); + 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_int_destroy(&path); - igraph_stack_int_destroy(&tracker); - igraph_stack_int_destroy(&edge_path); - igraph_stack_int_destroy(&edge_tracker); + 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_int_destroy(&remaining_out_edges); + igraph_vector_destroy(&remaining_out_edges); IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; @@ -561,15 +566,12 @@ static igraph_error_t igraph_i_eulerian_path_directed( /** * \ingroup Eulerian * \function igraph_eulerian_cycle - * \brief Finds an 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. * * - * If the graph has no edges, a zero-length cycle is returned. - * - * * This function uses Hierholzer's algorithm. * * \param graph The graph object. @@ -583,7 +585,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. - * \cli IGRAPH_ENOSOL + * \cli IGRAPH_EINVVID * graph does not have an Eulerian cycle. * \endclist * @@ -591,9 +593,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( * */ -igraph_error_t igraph_eulerian_cycle( - const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { - +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; @@ -602,7 +602,7 @@ igraph_error_t igraph_eulerian_cycle( 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_ENOSOL); + 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)); @@ -610,7 +610,7 @@ igraph_error_t igraph_eulerian_cycle( 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_ENOSOL); + 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)); @@ -623,15 +623,12 @@ igraph_error_t igraph_eulerian_cycle( /** * \ingroup Eulerian * \function igraph_eulerian_path - * \brief Finds an Eulerian path. + * \brief Finds an Eulerian path * * Finds an Eulerian path, if it exists. An Eulerian path traverses * each edge precisely once. * * - * If the graph has no edges, a zero-length path is returned. - * - * * This function uses Hierholzer's algorithm. * * \param graph The graph object. @@ -645,7 +642,7 @@ igraph_error_t igraph_eulerian_cycle( * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. - * \cli IGRAPH_ENOSOL + * \cli IGRAPH_EINVVID * graph does not have an Eulerian path. * \endclist * @@ -653,9 +650,7 @@ igraph_error_t igraph_eulerian_cycle( * */ -igraph_error_t igraph_eulerian_path( - const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { - +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; @@ -664,14 +659,14 @@ igraph_error_t igraph_eulerian_path( 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_ENOSOL); + 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_ENOSOL); + 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)); diff --git a/src/vendor/cigraph/src/paths/floyd_warshall.c b/src/vendor/cigraph/src/paths/floyd_warshall.c deleted file mode 100644 index 6ba9736875f..00000000000 --- a/src/vendor/cigraph/src/paths/floyd_warshall.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - IGraph library. - Copyright (C) 2022-2023 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_interface.h" -#include "igraph_stack.h" - -#include "core/interruption.h" -#include "internal/utils.h" - -static igraph_error_t igraph_distances_floyd_warshall_original( - const igraph_t *graph, igraph_matrix_t *res, - const igraph_vector_t *weights) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - - for (igraph_integer_t k = 0; k < no_of_nodes; k++) { - IGRAPH_ALLOW_INTERRUPTION(); - - /* Iteration order matters for performance! - * First j, then i, because matrices are stored as column-major. */ - for (igraph_integer_t j = 0; j < no_of_nodes; j++) { - igraph_real_t dkj = MATRIX(*res, k, j); - if (dkj == IGRAPH_INFINITY) { - continue; - } - - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_real_t di = MATRIX(*res, i, k) + dkj; - igraph_real_t dd = MATRIX(*res, i, j); - if (di < dd) { - MATRIX(*res, i, j) = di; - } - if (i == j && MATRIX(*res, i, i) < 0) { - IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", - IGRAPH_ENEGLOOP); - } - } - } - } - - return IGRAPH_SUCCESS; -} - - -static igraph_error_t igraph_distances_floyd_warshall_tree( - const igraph_t *graph, igraph_matrix_t *res, - const igraph_vector_t *weights) { - - /* This is the "Tree" algorithm of Brodnik et al. - * A difference from the paper is that instead of using the OUT_k tree of shortest - * paths _starting_ in k, we use the IN_k tree of shortest paths _ending_ in k. - * This makes it easier to iterate through matrices in column-major order, - * i.e. storage order, thus increasing performance. */ - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - - /* successors[v][u] is the second vertex on the shortest path from v to u, - i.e. the parent of v in the IN_u tree. */ - igraph_matrix_int_t successors; - IGRAPH_MATRIX_INT_INIT_FINALLY(&successors, no_of_nodes, no_of_nodes); - - /* children[children_start[u] + i] is the i-th child of u in a tree of shortest paths - rooted at k, and ending in k, in the main loop below (IN_k). There are no_of_nodes-1 - child vertices in total, as the root vertex is excluded. This is essentially a contiguously - stored adjacency list representation of IN_k. */ - igraph_vector_int_t children; - IGRAPH_VECTOR_INT_INIT_FINALLY(&children, no_of_nodes-1); - - /* children_start[u] indicates where the children of u are stored in children[]. - These are effectively the cumulative sums of no_of_children[], with the first - element being 0. The last element, children_start[no_of_nodes], is equal to the - total number of children in the tree, i.e. no_of_nodes-1. */ - igraph_vector_int_t children_start; - IGRAPH_VECTOR_INT_INIT_FINALLY(&children_start, no_of_nodes+1); - - /* no_of_children[u] is the number of children that u has in IN_k in the main loop below. */ - igraph_vector_int_t no_of_children; - IGRAPH_VECTOR_INT_INIT_FINALLY(&no_of_children, no_of_nodes); - - /* dfs_traversal and dfs_skip arrays for running time optimization, - see "Practical improvement" in Section 3.1 of the paper */ - igraph_vector_int_t dfs_traversal; - IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_traversal, no_of_nodes); - igraph_vector_int_t dfs_skip; - IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_skip, no_of_nodes); - - igraph_stack_int_t stack; - IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); - - for (igraph_integer_t u = 0; u < no_of_nodes; u++) { - for (igraph_integer_t v = 0; v < no_of_nodes; v++) { - MATRIX(successors, v, u) = u; - } - } - - for (igraph_integer_t k = 0; k < no_of_nodes; k++) { - IGRAPH_ALLOW_INTERRUPTION(); - - /* Count the children of each node in the shortest path tree, assuming that at - this point all elements of no_of_children[] are zeros. */ - for (igraph_integer_t v = 0; v < no_of_nodes; v++) { - if (v == k) continue; - igraph_integer_t parent = MATRIX(successors, v, k); - VECTOR(no_of_children)[parent]++; - } - - /* Note: we do not use igraph_vector_int_cumsum() here as that function produces - an output vector of the same length as the input vector. Here we need an output - one longer, with a 0 being prepended to what vector_cumsum() would produce. */ - igraph_integer_t cumsum = 0; - for (igraph_integer_t v = 0; v < no_of_nodes; v++) { - VECTOR(children_start)[v] = cumsum; - cumsum += VECTOR(no_of_children)[v]; - } - VECTOR(children_start)[no_of_nodes] = cumsum; - - /* Constructing the tree IN_k (as in the paper) and representing it - as a contiguously stored adjacency list. The entries of the no_of_children - vector as re-used as an index of where to insert child node indices. - At the end of the calculation, all elements of no_of_children[] will be zeros, - making this vector ready for the next iteration of the outer loop. */ - for (igraph_integer_t v = 0; v < no_of_nodes; v++) { - if (v == k) continue; - igraph_integer_t parent = MATRIX(successors, v, k); - VECTOR(no_of_children)[parent]--; - VECTOR(children)[ VECTOR(children_start)[parent] + VECTOR(no_of_children)[parent] ] = v; - } - - /* constructing dfs-traversal and dfs-skip arrays for the IN_k tree */ - IGRAPH_CHECK(igraph_stack_int_push(&stack, k)); - igraph_integer_t counter = 0; - while (!igraph_stack_int_empty(&stack)) { - igraph_integer_t parent = igraph_stack_int_pop(&stack); - if (parent >= 0) { - VECTOR(dfs_traversal)[counter] = parent; - counter++; - /* a negative marker -parent - 1 that is popped right after - all the descendants of the parent were processed */ - IGRAPH_CHECK(igraph_stack_int_push(&stack, -parent - 1)); - for (igraph_integer_t l = VECTOR(children_start)[parent]; l < VECTOR(children_start)[parent + 1]; l++) { - IGRAPH_CHECK(igraph_stack_int_push(&stack, VECTOR(children)[l])); - } - } else { - VECTOR(dfs_skip)[-(parent + 1)] = counter; - } - } - - /* main inner loop */ - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_real_t dki = MATRIX(*res, k, i); - if (dki == IGRAPH_INFINITY || i == k) { - continue; - } - igraph_integer_t counter = 1; - while (counter < no_of_nodes) { - igraph_integer_t j = VECTOR(dfs_traversal)[counter]; - igraph_real_t di = MATRIX(*res, j, k) + dki; - igraph_real_t dd = MATRIX(*res, j, i); - if (di < dd) { - MATRIX(*res, j, i) = di; - MATRIX(successors, j, i) = MATRIX(successors, j, k); - counter++; - } else { - counter = VECTOR(dfs_skip)[j]; - } - if (i == j && MATRIX(*res, i, i) < 0) { - IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", - IGRAPH_ENEGLOOP); - } - } - } - } - - igraph_stack_int_destroy(&stack); - igraph_vector_int_destroy(&dfs_traversal); - igraph_vector_int_destroy(&dfs_skip); - igraph_vector_int_destroy(&no_of_children); - igraph_vector_int_destroy(&children_start); - igraph_vector_int_destroy(&children); - igraph_matrix_int_destroy(&successors); - IGRAPH_FINALLY_CLEAN(7); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_distances_floyd_warshall - * \brief Weighted all-pairs shortest path lengths with the Floyd-Warshall algorithm. - * - * \experimental - * - * The Floyd-Warshall algorithm computes weighted shortest path lengths between - * all pairs of vertices at the same time. It is useful with very dense weighted graphs, - * as its running time is primarily determined by the vertex count, and is not sensitive - * to the graph density. In sparse graphs, other methods such as the Dijkstra or - * Bellman-Ford algorithms will perform significantly better. - * - * - * In addition to the original Floyd-Warshall algorithm, igraph contains implementations - * of variants that offer better asymptotic complexity as well as better practical - * running times for most instances. See the reference below for more information. - * - * - * Note that internally this function always computes the distance matrix - * for all pairs of vertices. The \p from and \p to parameters only serve - * to subset this matrix, but do not affect the time or memory taken by the - * calculation. - * - * - * Reference: - * - * - * Brodnik, A., Grgurovič, M., Požar, R.: - * Modifications of the Floyd-Warshall algorithm with nearly quadratic expected-time, - * Ars Mathematica Contemporanea, vol. 22, issue 1, p. #P1.01 (2021). - * https://doi.org/10.26493/1855-3974.2467.497 - * - * \param graph The graph object. - * \param res An intialized matrix, the distances will be stored here. - * \param from The source vertices. - * \param to The target vertices. - * \param weights The edge weights. If \c NULL, all weights are assumed to be 1. - * Negative weights are allowed, but the graph must not contain negative cycles. - * \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 method The type of the algorithm used. - * \clist - * \cli IGRAPH_FLOYD_WARSHALL_AUTOMATIC - * tried to select the best performing variant for the current graph; - * presently this option always uses the "Tree" method. - * \cli IGRAPH_FLOYD_WARSHALL_ORIGINAL - * the basic Floyd-Warshall algorithm. - * \cli IGRAPH_FLOYD_WARSHALL_TREE - * the "Tree" speedup of Brodnik et al., faster than the original algorithm - * in most cases. - * \endclist - * \return Error code. \c IGRAPH_ENEGLOOP is returned if a negative-weight - * cycle is found. - * - * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(), - * \ref igraph_distances_bellman_ford(), \ref igraph_distances_johnson() - * - * Time complexity: - * The original variant has complexity O(|V|^3 + |E|). - * The "Tree" variant has expected-case complexity of O(|V|^2 log^2 |V|) - * according to Brodnik et al., while its worst-time complexity remains O(|V|^3). - * Here |V| denotes the number of vertices and |E| is the number of edges. - */ -igraph_error_t igraph_distances_floyd_warshall( - const igraph_t *graph, igraph_matrix_t *res, - igraph_vs_t from, igraph_vs_t to, - const igraph_vector_t *weights, igraph_neimode_t mode, - const igraph_floyd_warshall_algorithm_t method) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_bool_t in = false, out = false; - - if (weights && igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); - } - - if (! igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - switch (mode) { - case IGRAPH_ALL: - in = out = true; - break; - case IGRAPH_OUT: - out = true; - break; - case IGRAPH_IN: - in = true; - break; - default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); - } - - if (weights && igraph_vector_is_any_nan(weights)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); - igraph_matrix_fill(res, IGRAPH_INFINITY); - - for (igraph_integer_t v = 0; v < no_of_nodes; v++) { - MATRIX(*res, v, v) = 0; - } - - for (igraph_integer_t e = 0; e < no_of_edges; e++) { - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; - - if (w < 0) { - if (mode == IGRAPH_ALL) { - IGRAPH_ERRORF("Negative edge weight (%g) found in undirected graph " - "while calculating distances with Floyd-Warshall.", - IGRAPH_ENEGLOOP, w); - } else if (to == from) { - IGRAPH_ERRORF("Self-loop with negative weight (%g) found " - "while calculating distances with Floyd-Warshall.", - IGRAPH_ENEGLOOP, w); - } - } - - if (out && MATRIX(*res, from, to) > w) { - MATRIX(*res, from, to) = w; - } - if (in && MATRIX(*res, to, from) > w) { - MATRIX(*res, to, from) = w; - } - } - - /* If there are zero or one vertices, nothing needs to be done. - * This is special-cased so that at later stages we can rely on no_of_nodes - 1 >= 0. */ - if (no_of_nodes <= 1) { - return IGRAPH_SUCCESS; - } - - switch (method) { - case IGRAPH_FLOYD_WARSHALL_ORIGINAL: - IGRAPH_CHECK(igraph_distances_floyd_warshall_original(graph, res, weights)); - break; - case IGRAPH_FLOYD_WARSHALL_AUTOMATIC: - case IGRAPH_FLOYD_WARSHALL_TREE: - IGRAPH_CHECK(igraph_distances_floyd_warshall_tree(graph, res, weights)); - break; - default: - IGRAPH_ERROR("Invalid method.", IGRAPH_EINVAL); - } - - IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/paths/histogram.c b/src/vendor/cigraph/src/paths/histogram.c index 1f5d9e9467e..a84858d7fc5 100644 --- a/src/vendor/cigraph/src/paths/histogram.c +++ b/src/vendor/cigraph/src/paths/histogram.c @@ -53,23 +53,23 @@ * Time complexity: O(|V||E|), the number of vertices times the number * of edges. * - * \sa \ref igraph_average_path_length() and \ref igraph_distances() + * \sa \ref igraph_average_path_length() and \ref igraph_shortest_paths() */ -igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, +int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *unconnected, igraph_bool_t directed) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j, n; - igraph_vector_int_t already_added; - igraph_integer_t nodes_reached; + 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_int_t q = IGRAPH_DQUEUE_NULL; + 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; - igraph_integer_t ressize; + long int ressize; if (directed) { dirmode = IGRAPH_OUT; @@ -77,33 +77,33 @@ igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *r dirmode = IGRAPH_ALL; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&already_added, no_of_nodes); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); - + 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_vector_clear(res); + 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_int_push(&q, i)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + 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_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + 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++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; + long int neighbor = (long int) VECTOR(*neis)[j]; if (VECTOR(already_added)[neighbor] == i + 1) { continue; } @@ -117,10 +117,10 @@ igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *r } VECTOR(*res)[actdist] += 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_int_empty */ + } /* while !igraph_dqueue_empty */ unconn += (no_of_nodes - nodes_reached); @@ -136,8 +136,8 @@ igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *r unconn /= 2; } - igraph_vector_int_destroy(&already_added); - igraph_dqueue_int_destroy(&q); + igraph_vector_long_destroy(&already_added); + igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(3); @@ -145,5 +145,5 @@ igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *r *unconnected = unconn; } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/johnson.c b/src/vendor/cigraph/src/paths/johnson.c index 8ed7bd059e0..90eebdcf910 100644 --- a/src/vendor/cigraph/src/paths/johnson.c +++ b/src/vendor/cigraph/src/paths/johnson.c @@ -26,35 +26,23 @@ #include "igraph_conversion.h" #include "igraph_interface.h" -#include "math/safe_intop.h" /** - * \function igraph_distances_johnson + * \function igraph_shortest_paths_johnson * \brief Weighted shortest path lengths between vertices, using Johnson's algorithm. * - * This algorithm supports directed graphs with negative edge weights, and performs - * better than the Bellman-Ford method when distances are calculated from many different - * sources, the typical use case being all-pairs distance calculations. It works by using - * a single-source Bellman-Ford run to transform all edge weights to non-negative ones, - * then invoking Dijkstra's algorithm with the new weights. See the Wikipedia page - * for more details: http://en.wikipedia.org/wiki/Johnson's_algorithm. - * - * - * If no edge weights are supplied, then the unweighted version, \ref igraph_distances() - * is called. If none of the supplied edge weights are negative, then Dijkstra's algorithm - * is used by calling \ref igraph_distances_dijkstra(). - * - * - * Note that Johnson's algorithm applies only to directed graphs. This function rejects - * undirected graphs with \em any negative edge weights, even when the \p from and \p to - * vertices are all in connected components that are free of negative weights. + * See Wikipedia at http://en.wikipedia.org/wiki/Johnson's_algorithm + * for Johnson's algorithm. This algorithm works even if the graph + * contains negative edge weights, and it is worth using it if we + * calculate the shortest paths from many sources. * * - * References: + * If no edge weights are supplied, then the unweighted + * version, \ref igraph_shortest_paths() is called. * * - * Donald B. Johnson: Efficient Algorithms for Shortest Paths in Sparse Networks. - * J. ACM 24, 1 (1977), 1–13. - * https://doi.org/10.1145/321992.321993 + * 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. @@ -65,64 +53,61 @@ * \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_distances() will - * be called. + * 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_distances() for a faster unweighted version, - * \ref igraph_distances_dijkstra() if you do not have negative - * edge weights, \ref igraph_distances_bellman_ford() if you only + * \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. */ -igraph_error_t igraph_distances_johnson(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_t newgraph; - igraph_vector_int_t edges; - igraph_vector_t newweights; + igraph_vector_t edges, newweights; igraph_matrix_t bfres; - igraph_integer_t i, ptr; - igraph_integer_t nr, nc; + long int i, ptr; + long int nr, nc; igraph_vit_t fromvit; - igraph_integer_t no_edges_reserved; /* If no weights, then we can just run the unweighted version */ if (!weights) { - return igraph_distances(graph, res, from, to, IGRAPH_OUT); + return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - 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_distances(graph, res, from, to, IGRAPH_OUT); + 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 (isnan(min_weight)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + if (igraph_is_nan(min_weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); } if (min_weight >= 0) { - return igraph_distances_dijkstra(graph, res, from, to, weights, IGRAPH_OUT); + 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_ERROR("Johnson's shortest path: undirected graph and negative weight", IGRAPH_EINVAL); } @@ -132,36 +117,34 @@ igraph_error_t igraph_distances_johnson(const igraph_t *graph, IGRAPH_MATRIX_INIT_FINALLY(&bfres, 0, 0); IGRAPH_VECTOR_INIT_FINALLY(&newweights, 0); - IGRAPH_CHECK(igraph_empty(&newgraph, no_of_nodes + 1, igraph_is_directed(graph))); + IGRAPH_CHECK(igraph_empty(&newgraph, (igraph_integer_t) no_of_nodes + 1, + igraph_is_directed(graph))); IGRAPH_FINALLY(igraph_destroy, &newgraph); - IGRAPH_SAFE_MULT(no_of_nodes, 2, &no_edges_reserved); - IGRAPH_SAFE_ADD(no_edges_reserved, no_of_edges * 2, &no_edges_reserved); - /* Add a new node to the graph, plus edges from it to all the others. */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges_reserved); - igraph_get_edgelist(graph, &edges, /*bycol=*/ 0); /* reserved */ - igraph_vector_int_resize(&edges, no_edges_reserved); /* reserved */ + 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_int_destroy(&edges); + 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); /* reserved */ - igraph_vector_resize(&newweights, no_of_edges + no_of_nodes); /* reserved */ + 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 Bellman-Ford algorithm on the new graph, starting from the + /* Run Bellmann-Ford algorithm on the new graph, starting from the new vertex. */ - IGRAPH_CHECK(igraph_distances_bellman_ford(&newgraph, &bfres, - igraph_vss_1(no_of_nodes), + 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); @@ -171,10 +154,10 @@ igraph_error_t igraph_distances_johnson(const igraph_t *graph, 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); /* reserved */ + igraph_vector_resize(&newweights, no_of_edges); for (i = 0; i < no_of_edges; i++) { - igraph_integer_t ffrom = IGRAPH_FROM(graph, i); - igraph_integer_t tto = IGRAPH_TO(graph, 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); /* If a weight becomes slightly negative due to roundoff errors, @@ -183,7 +166,7 @@ igraph_error_t igraph_distances_johnson(const igraph_t *graph, } /* Run Dijkstra's algorithm on the new weights */ - IGRAPH_CHECK(igraph_distances_dijkstra(graph, res, from, + IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, res, from, to, &newweights, IGRAPH_OUT)); @@ -198,20 +181,20 @@ igraph_error_t igraph_distances_johnson(const igraph_t *graph, IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); for (i = 0; i < nr; i++, IGRAPH_VIT_NEXT(fromvit)) { - igraph_integer_t v1 = IGRAPH_VIT_GET(fromvit); + long int v1 = IGRAPH_VIT_GET(fromvit); if (igraph_vs_is_all(&to)) { - igraph_integer_t v2; + 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 { - igraph_integer_t j; + 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)) { - igraph_integer_t v2 = IGRAPH_VIT_GET(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; } @@ -226,17 +209,3 @@ igraph_error_t igraph_distances_johnson(const igraph_t *graph, return IGRAPH_SUCCESS; } - -/** - * \function igraph_shortest_paths_johnson - * \brief Weighted shortest path lengths between vertices, using Johnson's algorithm (deprecated). - * - * \deprecated-by igraph_distances_johnson 0.10.0 - */ -igraph_error_t 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) { - return igraph_distances_johnson(graph, res, from, to, weights); -} diff --git a/src/vendor/cigraph/src/paths/random_walk.c b/src/vendor/cigraph/src/paths/random_walk.c index f4cee7f9a67..a629d015920 100644 --- a/src/vendor/cigraph/src/paths/random_walk.c +++ b/src/vendor/cigraph/src/paths/random_walk.c @@ -27,60 +27,84 @@ #include "igraph_interface.h" #include "igraph_random.h" #include "igraph_memory.h" -#include "igraph_vector_ptr.h" #include "core/interruption.h" /** - * This function performs a random walk with a given length on a graph, - * from the given start vertex. - * It's used for igraph_random_walk when the given graph is unweighted, - * and only vertex IDs of the vertices on the walk are needed (edge IDs are not needed). - * \param vertices An allocated vector, the result is stored here as - * a list of vertex IDs. It will be resized as needed. - * It includes the starting vertex id as well. + * \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. */ -static igraph_error_t igraph_i_random_walk_adjlist(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_integer_t start, - igraph_neimode_t mode, - igraph_integer_t steps, - igraph_random_walk_stuck_t stuck) { - igraph_integer_t i; + + +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 (vertices == NULL) { - /* Nothing to do */ - return IGRAPH_SUCCESS; + 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_int_resize(vertices, steps + 1)); + IGRAPH_CHECK(igraph_vector_resize(walk, steps)); RNG_BEGIN(); - VECTOR(*vertices)[0] = start; - for (i = 1; i <= steps; i++) { + 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); - - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - nn = igraph_vector_int_size(neis); + if (IGRAPH_UNLIKELY(nn == 0)) { - igraph_vector_int_resize(vertices, i); /* shrinks */ + igraph_vector_resize(walk, i); if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { break; } else { IGRAPH_ERROR("Random walk got stuck.", IGRAPH_ERWSTUCK); } } - start = VECTOR(*vertices)[i] = VECTOR(*neis)[RNG_INTEGER(0, nn - 1)]; - - IGRAPH_ALLOW_INTERRUPTION(); + start = VECTOR(*walk)[i] = VECTOR(*neis)[ RNG_INTEGER(0, nn - 1) ]; } RNG_END(); @@ -92,7 +116,7 @@ static igraph_error_t igraph_i_random_walk_adjlist(const igraph_t *graph, } -/* Used as item destructor for 'cdfs' in igraph_i_random_walk_inclist(). */ +/* 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); @@ -101,53 +125,91 @@ static void vec_destr(igraph_vector_t *vec) { /** - * This function performs a random walk with a given length on a graph, - * from the given start vertex. - * It's used for igraph_random_walk: - * - when weights are used or when edge IDs of the traversed edges - * and/or vertex IDs of the visited vertices are requested. + * \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 vertices An allocated vector, the result is stored here as - * a list of vertex IDs. It will be resized as needed. - * It includes the starting vertex id as well. - * \param edges An initialized vector, the indices of traversed + * \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. + * */ -static igraph_error_t igraph_i_random_walk_inclist( - const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t start, - igraph_neimode_t mode, - igraph_integer_t steps, - igraph_random_walk_stuck_t stuck) { - +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 i, next; + igraph_integer_t ec = igraph_ecount(graph); + igraph_integer_t i; + igraph_inclist_t il; igraph_vector_t weight_temp; - igraph_lazy_inclist_t il; igraph_vector_ptr_t cdfs; /* cumulative distribution vectors for each node, used for weighted choice */ - if (vertices) { - IGRAPH_CHECK(igraph_vector_int_resize(vertices, steps + 1)); /* size: steps + 1 because vertices includes start vertex */ + 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 (edges) { - IGRAPH_CHECK(igraph_vector_int_resize(edges, steps)); + + if (start < 0 || start >= vc) { + IGRAPH_ERROR("Invalid start vertex.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); - IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &il); + 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; that's why we are still using - * igraph_vector_ptr_t as it does not require us to pre-initialize all - * the vectors in the vector list */ + /* 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); @@ -157,25 +219,15 @@ static igraph_error_t igraph_i_random_walk_inclist( RNG_BEGIN(); - if (vertices) { - VECTOR(*vertices)[0] = start; - } for (i = 0; i < steps; ++i) { - igraph_integer_t degree, edge, idx; - igraph_vector_int_t *inc_edges = igraph_lazy_inclist_get(&il, start); + long degree, edge, idx; + igraph_vector_int_t *edges = igraph_inclist_get(&il, start); - IGRAPH_CHECK_OOM(inc_edges, "Failed to query incident edges."); - degree = igraph_vector_int_size(inc_edges); + degree = igraph_vector_int_size(edges); /* are we stuck? */ if (IGRAPH_UNLIKELY(degree == 0)) { - /* can't fail since size is reduced, skip IGRAPH_CHECK */ - if (vertices) { - igraph_vector_int_resize(vertices, i + 1); /* size: i + 1 because vertices includes start vertex */ - } - if (edges) { - igraph_vector_int_resize(edges, i); - } + igraph_vector_resize(edgewalk, i); /* can't fail since size is reduced, skip IGRAPH_CHECK */ if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { break; } else { @@ -185,57 +237,49 @@ static igraph_error_t igraph_i_random_walk_inclist( 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]); + 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)) { - igraph_integer_t j; + long j; *cd = IGRAPH_CALLOC(1, igraph_vector_t); if (*cd == NULL) { - IGRAPH_ERROR("Random walk failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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(*inc_edges)[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]); + r = RNG_UNIF(0, VECTOR( **cd )[degree - 1]); igraph_vector_binsearch(*cd, r, &idx); - } - else { + } else { /* unweighted: choose an out-edge at random */ idx = RNG_INTEGER(0, degree - 1); } - edge = VECTOR(*inc_edges)[idx]; - if (edges) { - VECTOR(*edges)[i] = edge; - } + 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: - next = IGRAPH_TO(graph, edge); + start = IGRAPH_TO(graph, edge); break; case IGRAPH_IN: - next = IGRAPH_FROM(graph, edge); + start = IGRAPH_FROM(graph, edge); break; case IGRAPH_ALL: - next = IGRAPH_OTHER(graph, edge, start); + start = IGRAPH_OTHER(graph, edge, start); break; } - if (vertices) { - VECTOR(*vertices)[i + 1] = next; /* index i + 1 because vertices includes start vertex at position 0 */ - } - start = next; - IGRAPH_ALLOW_INTERRUPTION(); } @@ -243,156 +287,8 @@ static igraph_error_t igraph_i_random_walk_inclist( igraph_vector_ptr_destroy_all(&cdfs); igraph_vector_destroy(&weight_temp); - igraph_lazy_inclist_destroy(&il); + igraph_inclist_destroy(&il); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } - - -/** - * \function igraph_random_walk - * \brief Performs 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 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 \c NULL, all edges are considered to have equal weight. - * \param vertices An allocated vector, the result is stored here as - * a list of vertex IDs. It will be resized as needed. - * It includes the vertex IDs of starting and ending vertices. - * Length of the vertices vector: \p steps + 1 - * \param edges An initialized vector, the indices of traversed - * edges are stored here. It will be resized as needed. - * Length of the edges vector: \p steps - * \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. - * \p steps is the number of edges to traverse during the walk. - * \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 \c IGRAPH_ERWSTUCK error is reported. - * In both cases, \p vertices and \p edges are truncated to contain - * the actual interrupted walk. - * \return Error code: \c IGRAPH_ERWSTUCK if the walk got stuck. - * - * Time complexity: - * O(l + d) for unweighted graphs and - * O(l * log(k) + d) for weighted graphs, - * where \c l is the length of the walk, \c d is the total degree of the visited nodes - * and \c k is the average degree of vertices of the given graph. - */ - - -igraph_error_t igraph_random_walk(const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - 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); - - if (!(mode == IGRAPH_ALL || mode == IGRAPH_IN || mode == IGRAPH_OUT)) { - IGRAPH_ERROR("Invalid mode parameter.", IGRAPH_EINVMODE); - } - - if (start < 0 || start >= vc) { - IGRAPH_ERRORF("Starting vertex must be between 0 and the " - "number of vertices in the graph (%" IGRAPH_PRId - "), got %" IGRAPH_PRId ".", IGRAPH_EINVAL, - vc, start); - } - if (steps < 0) { - IGRAPH_ERRORF("Number of steps should be non-negative, got %" - IGRAPH_PRId ".", IGRAPH_EINVAL, steps); - } - - 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 (isnan(min)) { - IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); - } - } - } - - if (!igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - if (edges || weights) { - return igraph_i_random_walk_inclist(graph, weights, vertices, edges, - start, mode, steps, stuck); - } else { - return igraph_i_random_walk_adjlist(graph, vertices, - start, mode, steps, stuck); - } -} - - -/** - * \function igraph_random_edge_walk - * \brief Performs a random walk on a graph and returns 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 \c IGRAPH_ERWSTUCK error is reported. In both cases, - * \p edgewalk is truncated to contain the actual interrupted walk. - * - * \return Error code. - * - * \deprecated-by igraph_random_walk 0.10.0 - */ -igraph_error_t igraph_random_edge_walk( - const igraph_t *graph, - const igraph_vector_t *weights, - igraph_vector_int_t *edgewalk, - igraph_integer_t start, igraph_neimode_t mode, - igraph_integer_t steps, - igraph_random_walk_stuck_t stuck) { - - return igraph_random_walk(graph, weights, NULL, edgewalk, - start, mode, steps, stuck); -} diff --git a/src/vendor/cigraph/src/paths/shortest_paths.c b/src/vendor/cigraph/src/paths/shortest_paths.c index e53b02706ed..0c967e619b5 100644 --- a/src/vendor/cigraph/src/paths/shortest_paths.c +++ b/src/vendor/cigraph/src/paths/shortest_paths.c @@ -37,7 +37,7 @@ /* 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 igraph_error_t igraph_i_average_path_length_unweighted( +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 */ @@ -45,22 +45,23 @@ static igraph_error_t igraph_i_average_path_length_unweighted( const igraph_bool_t invert, /* average inverse distances instead of distances */ const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t source, j, n; - igraph_integer_t *already_added; + 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_int_t q = IGRAPH_DQUEUE_NULL; + 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, igraph_integer_t); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for average path length."); + 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_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init( graph, &allneis, @@ -70,20 +71,20 @@ static igraph_error_t igraph_i_average_path_length_unweighted( IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); for (source = 0; source < no_of_nodes; source++) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + 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_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + 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++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; + long int neighbor = (long int) VECTOR(*neis)[j]; if (already_added[neighbor] == source + 1) { continue; } @@ -94,10 +95,10 @@ static igraph_error_t igraph_i_average_path_length_unweighted( *res += actdist + 1.0; } no_of_conn_pairs += 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_int_empty */ + } /* while !igraph_dqueue_empty */ } /* for source < no_of_nodes */ @@ -128,7 +129,7 @@ static igraph_error_t igraph_i_average_path_length_unweighted( /* clean */ IGRAPH_FREE(already_added); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(3); @@ -140,7 +141,7 @@ static igraph_error_t igraph_i_average_path_length_unweighted( * 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 igraph_error_t igraph_i_average_path_length_dijkstra( +static int igraph_i_average_path_length_dijkstra( const igraph_t *graph, igraph_real_t *res, igraph_real_t *unconnected_pairs, @@ -161,15 +162,15 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( - 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 isfinite() might involve a function call + computation, as IGRAPH_FINITE() might involve a function call and we want to spare that. -1 will denote infinity instead. */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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; - igraph_integer_t source, j; + 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 */ @@ -178,7 +179,7 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match the number of edges (%" IGRAPH_PRId ").", + 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) { @@ -186,7 +187,7 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( if (min < 0) { IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); } - else if (isnan(min)) { + else if (igraph_is_nan(min)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -215,10 +216,10 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( igraph_2wheap_push_with_index(&Q, source, -1.0); while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + long int minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - igraph_integer_t nlen; + long int nlen; if (minnei != source) { if (invert) { @@ -230,12 +231,11 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); nlen = igraph_vector_int_size(neis); for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + 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); @@ -245,7 +245,7 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); } else if (altdist < curdist) { /* This is a shorter path */ - igraph_2wheap_modify(&Q, tto, -altdist); + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); } } } /* !igraph_2wheap_empty(&Q) */ @@ -288,7 +288,7 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( * * * 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, + * 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. @@ -299,8 +299,8 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( * \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 + * \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 @@ -312,7 +312,7 @@ static igraph_error_t igraph_i_average_path_length_dijkstra( * \example examples/simple/igraph_average_path_length.c */ -igraph_error_t igraph_average_path_length(const igraph_t *graph, +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) { @@ -327,7 +327,7 @@ igraph_error_t igraph_average_path_length(const igraph_t *graph, * * * 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, + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE, * NaN is returned. * * @@ -345,8 +345,8 @@ igraph_error_t igraph_average_path_length(const igraph_t *graph, * 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 + * \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 @@ -364,7 +364,7 @@ igraph_error_t igraph_average_path_length(const igraph_t *graph, * \example examples/simple/igraph_grg_game.c */ -igraph_error_t igraph_average_path_length_dijkstra(const igraph_t *graph, +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) @@ -416,7 +416,7 @@ igraph_error_t igraph_average_path_length_dijkstra(const igraph_t *graph, * */ -igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, +int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *weights, igraph_bool_t directed) { @@ -428,37 +428,37 @@ igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *re /***** Local efficiency *****/ /****************************/ -static igraph_error_t igraph_i_local_efficiency_unweighted( +static int igraph_i_local_efficiency_unweighted( const igraph_t *graph, const igraph_adjlist_t *adjlist, - igraph_dqueue_int_t *q, - igraph_integer_t *already_counted, - igraph_vector_int_t *vertex_neis, + 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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t vertex_neis_size; - igraph_integer_t neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ - igraph_integer_t i, j; + 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_int_clear(q); + 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(already_counted[0])); + memset(already_counted, 0, no_of_nodes * sizeof(long int)); IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); - vertex_neis_size = igraph_vector_int_size(vertex_neis); + 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) { - igraph_integer_t v = VECTOR(*vertex_neis)[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++; @@ -473,8 +473,8 @@ static igraph_error_t igraph_i_local_efficiency_unweighted( } for (i=0; i < vertex_neis_size; ++i) { - igraph_integer_t source = VECTOR(*vertex_neis)[i]; - igraph_integer_t reached = 0; + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -486,21 +486,21 @@ static igraph_error_t igraph_i_local_efficiency_unweighted( VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */ - IGRAPH_CHECK(igraph_dqueue_int_push(q, source)); - IGRAPH_CHECK(igraph_dqueue_int_push(q, 0)); + IGRAPH_CHECK(igraph_dqueue_push(q, source)); + IGRAPH_CHECK(igraph_dqueue_push(q, 0)); already_counted[source] = i + 1; - while (!igraph_dqueue_int_empty(q)) { + while (!igraph_dqueue_empty(q)) { igraph_vector_int_t *act_neis; - igraph_integer_t act_neis_size; - igraph_integer_t act = igraph_dqueue_int_pop(q); - igraph_integer_t actdist = igraph_dqueue_int_pop(q); + 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_int_clear(q); + igraph_dqueue_clear(q); break; } } @@ -508,14 +508,14 @@ static igraph_error_t igraph_i_local_efficiency_unweighted( act_neis = igraph_adjlist_get(adjlist, act); act_neis_size = igraph_vector_int_size(act_neis); for (j = 0; j < act_neis_size; j++) { - igraph_integer_t neighbor = VECTOR(*act_neis)[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_int_push(q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(q, actdist + 1)); } } } @@ -525,11 +525,11 @@ static igraph_error_t igraph_i_local_efficiency_unweighted( return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_local_efficiency_dijkstra( +static int igraph_i_local_efficiency_dijkstra( const igraph_t *graph, igraph_lazy_inclist_t *inclist, igraph_2wheap_t *Q, - igraph_vector_int_t *vertex_neis, + 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, @@ -548,21 +548,21 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( - 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 isfinite() might involve a function call + computation, as IGRAPH_FINITE() might involve a function call and we want to spare that. -1 will denote infinity instead. */ - igraph_integer_t i, j; - igraph_integer_t vertex_neis_size; - igraph_integer_t neighbor_count; /* unlike 'inc_edges_size', 'neighbor_count' does not count self-loops or multi-edges */ + 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_int_size(vertex_neis); + 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) { - igraph_integer_t v = VECTOR(*vertex_neis)[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++; @@ -577,8 +577,8 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( } for (i=0; i < vertex_neis_size; ++i) { - igraph_integer_t source = VECTOR(*vertex_neis)[i]; - igraph_integer_t reached = 0; + long int source = VECTOR(*vertex_neis)[i]; + long int reached = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -594,10 +594,10 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( igraph_2wheap_push_with_index(Q, source, -1.0); while (!igraph_2wheap_empty(Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(Q); + long int minnei = igraph_2wheap_max_index(Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q); igraph_vector_int_t *neis; - igraph_integer_t nlen; + long int nlen; if (minnei != source && VECTOR(*nei_mask)[minnei]) { *res += 1.0/(mindist - 1.0); @@ -609,14 +609,13 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(inclist, minnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + 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; - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + long int edge = (long int) VECTOR(*neis)[j]; + long int tto = IGRAPH_OTHER(graph, edge, minnei); if (tto == vertex) continue; @@ -630,7 +629,7 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( IGRAPH_CHECK(igraph_2wheap_push_with_index(Q, tto, -altdist)); } else if (altdist < curdist) { /* This is a shorter path */ - igraph_2wheap_modify(Q, tto, -altdist); + IGRAPH_CHECK(igraph_2wheap_modify(Q, tto, -altdist)); } } @@ -703,18 +702,18 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( * */ -igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t nodes_to_calc; /* no. of vertices includes in computation */ + 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_int_t vertex_neis; + igraph_vector_t vertex_neis; igraph_vector_char_t nei_mask; - igraph_integer_t i; + 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' @@ -726,7 +725,7 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r */ IGRAPH_CHECK(igraph_vector_char_init(&nei_mask, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_char_destroy, &nei_mask); - IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_neis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vertex_neis, 0); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -737,12 +736,14 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r if (! weights) /* unweighted case */ { - igraph_integer_t *already_counted; + long int *already_counted; igraph_adjlist_t adjlist; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for local efficiency calculation."); + 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( @@ -752,7 +753,7 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r )); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); for (IGRAPH_VIT_RESET(vit), i=0; ! IGRAPH_VIT_END(vit); @@ -764,7 +765,7 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode)); } - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_adjlist_destroy(&adjlist); IGRAPH_FREE(already_counted); IGRAPH_FINALLY_CLEAN(3); @@ -775,15 +776,15 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r 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); + 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_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } - else if (isnan(min)) { - IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); } } @@ -810,7 +811,7 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r } igraph_vit_destroy(&vit); - igraph_vector_int_destroy(&vertex_neis); + igraph_vector_destroy(&vertex_neis); igraph_vector_char_destroy(&nei_mask); IGRAPH_FINALLY_CLEAN(3); @@ -857,11 +858,11 @@ igraph_error_t igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *r * */ -igraph_error_t igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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 @@ -901,24 +902,21 @@ igraph_error_t igraph_average_local_efficiency(const igraph_t *graph, igraph_rea * If the graph has no vertices, \c IGRAPH_NAN is returned. * * \param graph The graph object. - * \param res Pointer to a real number, if not \c NULL then it will contain + * \param pres Pointer to a real number, if not \c NULL then it will contain * the diameter (the actual distance). - * \param from Pointer to an integer, if not \c NULL it will be set to the + * \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 to Pointer to an integer, if not \c NULL it will be set to the + * \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 vertex_path Pointer to an initialized vector. If not \c NULL the actual - * longest geodesic path in terms of vertices will be stored here. The vector will be - * resized as needed. - * \param edge_path Pointer to an initialized vector. If not \c NULL the actual - * longest geodesic path in terms of edges will be stored here. The vector will be + * \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 + * \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 @@ -932,21 +930,19 @@ igraph_error_t igraph_average_local_efficiency(const igraph_t *graph, igraph_rea * \example examples/simple/igraph_diameter.c */ -igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, - igraph_integer_t *from, igraph_integer_t *to, - igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_path, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t i, j, n; - igraph_integer_t *already_added; - igraph_integer_t nodes_reached; - /* from/to are initialized to 0 because in a singleton graph, or in an edgeless graph - * with unconn = true, the diameter path will be considered to consist of vertex 0 only. */ - igraph_integer_t ifrom = 0, ito = 0; - igraph_real_t ires = 0; + 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_int_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; igraph_vector_int_t *neis; igraph_neimode_t dirmode; igraph_adjlist_t allneis; @@ -954,20 +950,17 @@ igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, /* 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 (res) { - *res = IGRAPH_NAN; + if (pres) { + *pres = IGRAPH_NAN; } - if (vertex_path) { - igraph_vector_int_clear(vertex_path); + if (path) { + igraph_vector_clear(path); } - if (edge_path) { - igraph_vector_int_clear(edge_path); + if (pfrom) { + *pfrom = -1; } - if (from) { - *from = -1; - } - if (to) { - *to = -1; + if (pto) { + *pto = -1; } return IGRAPH_SUCCESS; } @@ -977,53 +970,54 @@ igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, } else { dirmode = IGRAPH_ALL; } - already_added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for diameter calculation."); + 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_INT_INIT_FINALLY(&q, 100); + 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_int_push(&q, i)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + 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_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - if (actdist > ires) { - ires = actdist; - ifrom = i; - ito = actnode; + 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++) { - igraph_integer_t neighbor = VECTOR(*neis)[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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_int_empty */ + } /* while !igraph_dqueue_empty */ /* not connected, return IGRAPH_INFINITY */ if (nodes_reached != no_of_nodes && !unconn) { - ires = IGRAPH_INFINITY; - ifrom = -1; - ito = -1; + res = IGRAPH_INFINITY; + from = -1; + to = -1; break; } } /* for i 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); + IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL); } - else if (isnan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + else if (igraph_is_nan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); } } @@ -1203,13 +1188,13 @@ igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, nodes_reached = 0.0; while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + long int minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - igraph_integer_t nlen; + long int nlen; - if (mindist > ires) { - ires = mindist; ifrom = source; ito = minnei; + if (mindist > res) { + res = mindist; from = source; to = minnei; } nodes_reached++; @@ -1217,8 +1202,8 @@ igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, neis = igraph_inclist_get(&inclist, minnei); nlen = igraph_vector_int_size(neis); for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + 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); @@ -1229,7 +1214,7 @@ igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); } else if (altdist < curdist) { /* A shorter path */ - igraph_2wheap_modify(&Q, tto, -altdist); + IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist)); } } @@ -1237,15 +1222,15 @@ igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, /* not connected, return infinity */ if (nodes_reached != no_of_nodes && !unconn) { - ires = IGRAPH_INFINITY; - ifrom = ito = -1; + res = IGRAPH_INFINITY; + from = to = -1; break; } } /* source < no_of_nodes */ /* Compensate for the +1 that we have added to distances */ - ires -= 1; + res -= 1; igraph_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); @@ -1253,416 +1238,33 @@ igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL); - if (res) { - *res = ires; + if (pres) { + *pres = res; } - if (from) { - *from = ifrom; + if (pfrom) { + *pfrom = (igraph_integer_t) from; } - if (to) { - *to = ito; + if (pto) { + *pto = (igraph_integer_t) to; } - if ((vertex_path) || (edge_path)) { - if (!isfinite(ires)) { - if (vertex_path){ - igraph_vector_int_clear(vertex_path); - } - if (edge_path) { - igraph_vector_int_clear(edge_path); - } + if (path) { + if (!igraph_finite(res)) { + igraph_vector_clear(path); } else { - IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, - /*vertices=*/ vertex_path, /*edges=*/ edge_path, - ifrom, ito, - weights, dirmode)); - } - } - return IGRAPH_SUCCESS; -} - -/** - * Temporarily removes all edges incident on the vertex with the given ID from - * the graph by setting the weights of these edges to infinity. - * - * \param graph the graph - * \param weights the weights of the edges of the graph - * \param vid the ID of the vertex to remove - * \param edges_removed vector that records the IDs of the edges that were - * "removed" (i.e. their weights were set to infinity) - * \param eids temporary vector that is used to retrieve the IDs of the - * incident edges, to make this function free of memory allocations - */ -static igraph_error_t igraph_i_semidelete_vertex( - const igraph_t *graph, igraph_vector_t *weights, - igraph_integer_t vid, igraph_vector_int_t *edges_removed, - igraph_vector_int_t *eids -) { - igraph_integer_t j, n; - - IGRAPH_CHECK(igraph_incident(graph, eids, vid, IGRAPH_ALL)); - - n = igraph_vector_int_size(eids); - for (j = 0; j < n; j++) { - igraph_integer_t eid = VECTOR(*eids)[j]; - IGRAPH_CHECK(igraph_vector_int_push_back(edges_removed, eid)); - VECTOR(*weights)[eid] = IGRAPH_INFINITY; - } - - return IGRAPH_SUCCESS; -} - -static igraph_bool_t igraph_i_has_edge_with_infinite_weight( - const igraph_vector_int_t* path, const igraph_vector_t* weights -) { - igraph_integer_t i, n; - - n = weights ? igraph_vector_int_size(path) : 0; - for (i = 0; i < n; i++) { - igraph_integer_t edge = VECTOR(*path)[i]; - if (!isfinite(VECTOR(*weights)[edge])) { - return true; - } - } - - return false; -} - -static igraph_real_t igraph_i_get_total_weight_of_path( - igraph_vector_int_t* path, const igraph_vector_t* weights -) { - igraph_integer_t i, n = igraph_vector_int_size(path); - igraph_real_t result; - - if (weights) { - result = 0; - for (i = 0; i < n; i++) { - igraph_integer_t edge = VECTOR(*path)[i]; - result += VECTOR(*weights)[edge]; - } - } else { - result = n; - } - - return result; -} - -/** - * \function igraph_get_k_shortest_paths - * \brief k shortest paths between two vertices. - * - * This function returns the \p k shortest paths between two vertices, in order of - * increasing lengths. - * - * - * Reference: - * - * - * Yen, Jin Y.: - * An algorithm for finding shortest routes from all source nodes to a given - * destination in general networks. - * Quarterly of Applied Mathematics. 27 (4): 526–530. (1970) - * https://doi.org/10.1090/qam/253822 - * - * \param graph The graph object. - * \param weights The edge weights of the graph. Can be \c NULL for an - * unweighted graph. Infinite weights will be treated as missing - * edges. - * \param vertex_paths Pointer to an initialized list of integer vectors, the result - * will be stored here in \ref igraph_vector_int_t objects. Each vector - * object contains the vertex IDs along the kth shortest path - * between \p from and \p to, where \c k is the vector list index. May - * be \c NULL if the vertex paths are not needed. - * \param edge_paths Pointer to an initialized list of integer vectors, the result - * will be stored here in \ref igraph_vector_int_t objects. Each vector - * object contains the edge IDs along the kth shortest path - * between \p from and \p to, where \c k is the vector list index. May be - * \c NULL if the edge paths are not needed. - * \param k The number of paths. - * \param from The ID of the vertex from which the paths are calculated. - * \param to The ID of the vertex to which the paths are calculated. - * \param mode The type of paths to be used for the - * calculation in directed graphs. Possible values: - * \clist - * \cli IGRAPH_OUT - * The outgoing paths of \p from are calculated. - * \cli IGRAPH_IN - * The incoming paths of \p from 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 or \p to is an invalid vertex id. - * \cli IGRAPH_EINVMODE - * Invalid mode argument. - * \cli IGRAPH_EINVAL - * Invalid argument. - * \endclist - * - * \sa \ref igraph_get_all_simple_paths(), \ref igraph_get_shortest_paths(), - * \ref igraph_get_shortest_paths_dijkstra() - * - * Time complexity: k |V| (|V| log|V| + |E|), where |V| is the number of vertices, - * and |E| is the number of edges. - */ -igraph_error_t igraph_get_k_shortest_paths( - const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_int_list_t *vertex_paths, - igraph_vector_int_list_t *edge_paths, - igraph_integer_t k, igraph_integer_t from, igraph_integer_t to, - igraph_neimode_t mode -) { - igraph_vector_int_list_t paths_pot; /* potential shortest paths */ - igraph_integer_t vertex_spur; - igraph_vector_int_t path_spur, path_root, path_total, path_shortest; - igraph_integer_t nr_edges_root, i_path_current, i_path, edge_path_root, vertex_root_del; - igraph_integer_t i, n; - igraph_vector_t current_weights; - igraph_vector_int_t edges_removed; - igraph_integer_t nr_edges = igraph_ecount(graph); - igraph_bool_t infinite_path, already_in_potential_paths; - igraph_vector_int_t *path_0; - igraph_vector_int_t eids; - igraph_real_t path_weight, shortest_path_weight; - igraph_integer_t edge_paths_owned = 0; - - if (!igraph_is_directed(graph) && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) { - mode = IGRAPH_ALL; - } - - if (vertex_paths) { - igraph_vector_int_list_clear(vertex_paths); - } - - if (!edge_paths) { - /* We will need our own instance */ - edge_paths = IGRAPH_CALLOC(1, igraph_vector_int_list_t); - IGRAPH_CHECK_OOM(edge_paths, "Cannot allocate vector for storing edge paths."); - IGRAPH_FINALLY(igraph_free, edge_paths); - edge_paths_owned = 1; - - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(edge_paths, 0); - edge_paths_owned = 2; - } - - igraph_vector_int_list_clear(edge_paths); - - if (k == 0) { - goto cleanup; - } - - IGRAPH_CHECK(igraph_vector_int_list_resize(edge_paths, 1)); - path_0 = igraph_vector_int_list_get_ptr(edge_paths, 0); - - IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, - NULL, - path_0, - from, - to, - weights, - mode)); - - /* Check if there's a path. */ - infinite_path = igraph_i_has_edge_with_infinite_weight(path_0, weights); - if (infinite_path || (from != to && igraph_vector_int_size(path_0) == 0)) { - /* No path found. */ - igraph_vector_int_list_clear(edge_paths); - goto cleanup; - } - - IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&paths_pot, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&path_spur, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&path_root, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&path_total, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_removed, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); - IGRAPH_VECTOR_INIT_FINALLY(¤t_weights, nr_edges); - - /* If weights are NULL we use a uniform weight vector where each edge has - * a weight of 1. Later on, we replace the weights of removed edges with - * infinities. Note that we work on a copy of the weight vector so the - * original vector remains intact. - */ - if (weights) { - igraph_vector_update(¤t_weights, weights); - } else { - igraph_vector_fill(¤t_weights, 1); - } - - for (i_path_current = 1; i_path_current < k; i_path_current++) { - igraph_vector_int_t *path_previous = igraph_vector_int_list_tail_ptr(edge_paths); - igraph_integer_t path_previous_length = igraph_vector_int_size(path_previous); - for (nr_edges_root = 0; nr_edges_root < path_previous_length; nr_edges_root++) { - /* Determine spur node. */ - if (mode == IGRAPH_OUT) { - vertex_spur = IGRAPH_FROM(graph, VECTOR(*path_previous)[nr_edges_root]); - } else if (mode == IGRAPH_IN) { - vertex_spur = IGRAPH_TO(graph, VECTOR(*path_previous)[nr_edges_root]); - } else { - igraph_integer_t eid = VECTOR(*path_previous)[nr_edges_root]; - igraph_integer_t vertex_spur_1 = IGRAPH_FROM(graph, eid); - igraph_integer_t vertex_spur_2 = IGRAPH_TO(graph, eid); - igraph_integer_t vertex_spur_3; - igraph_integer_t vertex_spur_4; - if (nr_edges_root < path_previous_length-1) { - igraph_integer_t eid_next = VECTOR(*path_previous)[nr_edges_root + 1]; - vertex_spur_3 = IGRAPH_FROM(graph, eid_next); - vertex_spur_4 = IGRAPH_TO(graph, eid_next); - } else { - vertex_spur_3 = vertex_spur_4 = to; - } - if (vertex_spur_1 == vertex_spur_3 || vertex_spur_1 == vertex_spur_4) { - vertex_spur = vertex_spur_2; - } else { - vertex_spur = vertex_spur_1; - } - } - - /* Determine root path. */ - IGRAPH_CHECK(igraph_vector_int_resize(&path_root, nr_edges_root)); - for (i = 0; i < nr_edges_root; i++) { - VECTOR(path_root)[i] = VECTOR(*path_previous)[i]; - } - - /* Remove edges that are part of the previous shortest paths which share the same root path. */ - for (i_path = 0; i_path < i_path_current; i_path++) { - igraph_vector_int_t *path_check = igraph_vector_int_list_get_ptr(edge_paths, i_path); - igraph_bool_t equal = true; - for (i = 0; i < nr_edges_root; i++) { - if (VECTOR(path_root)[i] != VECTOR(*path_check)[i]) { - equal = false; - break; - } - } - if (equal) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges_removed, VECTOR(*path_check)[nr_edges_root])); - VECTOR(current_weights)[VECTOR(*path_check)[nr_edges_root]] = IGRAPH_INFINITY; - } - } - - /* pseudocode: for each node rootPathNode in rootPath except spurNode: - * remove rootPathNode from Graph; - */ - for (edge_path_root = 0; edge_path_root < nr_edges_root; edge_path_root++) { - if (mode == IGRAPH_OUT) { - vertex_root_del = IGRAPH_FROM(graph, VECTOR(path_root)[edge_path_root]); - } else if (mode == IGRAPH_IN) { - vertex_root_del = IGRAPH_TO(graph, VECTOR(path_root)[edge_path_root]); - } else { - igraph_integer_t eid = VECTOR(*path_previous)[edge_path_root]; - igraph_integer_t eid_next = VECTOR(*path_previous)[edge_path_root + 1]; - igraph_integer_t vertex_root_del_1 = IGRAPH_FROM(graph, eid); - igraph_integer_t vertex_root_del_2 = IGRAPH_TO(graph, eid); - igraph_integer_t vertex_root_del_3 = IGRAPH_FROM(graph, eid_next); - igraph_integer_t vertex_root_del_4 = IGRAPH_TO(graph, eid_next); - if (vertex_root_del_1 == vertex_root_del_3 || vertex_root_del_1 == vertex_root_del_4) { - vertex_root_del = vertex_root_del_2; - } else { - vertex_root_del = vertex_root_del_1; - } - } - /* Remove vertex by setting incident edges to infinity */ - IGRAPH_CHECK(igraph_i_semidelete_vertex( - graph, ¤t_weights, vertex_root_del, &edges_removed, - &eids - )); - } - - /* Determine spur path */ - IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, - NULL, - &path_spur, - vertex_spur, - to, - ¤t_weights, - mode)); - infinite_path = igraph_i_has_edge_with_infinite_weight(&path_spur, ¤t_weights); - - /* Add total (root + spur) path to potential paths if it's not in there yet. */ - if (!infinite_path) { - IGRAPH_CHECK(igraph_vector_int_update(&path_total, &path_root)); - IGRAPH_CHECK(igraph_vector_int_append(&path_total, &path_spur)); - - already_in_potential_paths = 0; - n = igraph_vector_int_list_size(&paths_pot); - for (i = 0; i < n; i++) { - if (igraph_vector_int_all_e(&path_total, igraph_vector_int_list_get_ptr(&paths_pot, i))) { - already_in_potential_paths = 1; - break; - } - } - - if (!already_in_potential_paths) { - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(&paths_pot, &path_total)); - } - } - - /* Cleanup */ - n = igraph_vector_int_size(&edges_removed); - for (i = 0; i < n; i++) { - VECTOR(current_weights)[VECTOR(edges_removed)[i]] = - weights ? VECTOR(*weights)[VECTOR(edges_removed)[i]] : 1; - } - igraph_vector_int_clear(&edges_removed); + 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); } - - /* Add shortest potential path to shortest paths */ - n = igraph_vector_int_list_size(&paths_pot); - if (n == 0) { - break; - } - - shortest_path_weight = igraph_i_get_total_weight_of_path( - igraph_vector_int_list_get_ptr(&paths_pot, 0), weights - ); - i_path = 0; - for (i = 1; i < n; i++) { - path_weight = igraph_i_get_total_weight_of_path( - igraph_vector_int_list_get_ptr(&paths_pot, i), weights - ); - if (path_weight < shortest_path_weight) { - i_path = i; - shortest_path_weight = path_weight; - } - } - - IGRAPH_CHECK(igraph_vector_int_list_remove_fast(&paths_pot, i_path, &path_shortest)); - IGRAPH_CHECK(igraph_vector_int_list_push_back(edge_paths, &path_shortest)); } - igraph_vector_destroy(¤t_weights); - igraph_vector_int_destroy(&eids); - igraph_vector_int_destroy(&edges_removed); - igraph_vector_int_destroy(&path_total); - igraph_vector_int_destroy(&path_root); - igraph_vector_int_destroy(&path_spur); - igraph_vector_int_list_destroy(&paths_pot); - IGRAPH_FINALLY_CLEAN(7); - - if (vertex_paths) { - igraph_integer_t no_of_edge_paths = igraph_vector_int_list_size(edge_paths); - - IGRAPH_CHECK(igraph_vector_int_list_resize(vertex_paths, no_of_edge_paths)); - for (i = 0; i < no_of_edge_paths; i++) { - igraph_vector_int_t* edge_path = igraph_vector_int_list_get_ptr(edge_paths, i); - igraph_vector_int_t* vertex_path = igraph_vector_int_list_get_ptr(vertex_paths, i); - IGRAPH_CHECK(igraph_vertex_path_from_edge_path(graph, from, edge_path, vertex_path, mode)); - } - } - -cleanup: - if (edge_paths_owned >= 2) { - igraph_vector_int_list_destroy(edge_paths); - IGRAPH_FINALLY_CLEAN(1); - } - if (edge_paths_owned >= 1) { - igraph_free(edge_paths); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/simple_paths.c b/src/vendor/cigraph/src/paths/simple_paths.c index 056216aa829..7ca7bad8439 100644 --- a/src/vendor/cigraph/src/paths/simple_paths.c +++ b/src/vendor/cigraph/src/paths/simple_paths.c @@ -1,6 +1,8 @@ +/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2014-2022 The igraph development team + 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 @@ -13,15 +15,19 @@ GNU 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 . -*/ + 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" @@ -35,15 +41,15 @@ * * 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 when the - * graph has many cycles. Consider using the \p cutoff - * parameter when you do not need long paths. + * 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. The paths are - * returned here in terms of their vertices, separated - * by -1 markers. The paths are included in arbitrary - * order, as they are found. + * \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 @@ -52,13 +58,11 @@ * for undirected graphs. * \return Error code. * - * \sa \ref igraph_get_k_shortest_paths() - * * Time complexity: O(n!) in the worst case, n is the number of * vertices. */ -igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, +int igraph_get_all_simple_paths(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t from, const igraph_vs_t to, @@ -68,9 +72,10 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, 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_bool_t markto, added; + igraph_vector_char_t added; igraph_vector_int_t nptr; int iteration = 0; @@ -79,24 +84,29 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, } if (!toall) { - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&markto, no_nodes); + 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) ] = true; + VECTOR(markto)[ IGRAPH_VIT_GET(vit) ] = 1; } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); } - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&added, no_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&stack, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&dist, 100); + 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_VECTOR_INT_INIT_FINALLY(&nptr, no_nodes); + IGRAPH_CHECK(igraph_vector_int_init(&nptr, no_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nptr); igraph_vector_int_clear(res); @@ -104,20 +114,16 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, igraph_vector_int_clear(&dist); igraph_vector_int_push_back(&stack, from); igraph_vector_int_push_back(&dist, 0); - VECTOR(added)[from] = true; + VECTOR(added)[from] = 1; while (!igraph_vector_int_empty(&stack)) { - igraph_integer_t act = igraph_vector_int_tail(&stack); - igraph_integer_t curdist = igraph_vector_int_tail(&dist); + 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); - igraph_integer_t n; - igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, 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; - igraph_integer_t nei; - - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - - n = igraph_vector_int_size(neis); + int nei; if (iteration == 0) { IGRAPH_ALLOW_INTERRUPTION(); @@ -126,9 +132,9 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, within_dist = (curdist < cutoff || cutoff < 0); if (within_dist) { /* Search for a neighbor that was not yet visited */ - any = false; + any = 0; while (!any && (*ptr) < n) { - nei = VECTOR(*neis)[(*ptr)]; + nei = (int) VECTOR(*neis)[(*ptr)]; any = !VECTOR(added)[nei]; (*ptr) ++; } @@ -137,7 +143,7 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, /* 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] = true; + VECTOR(added)[nei] = 1; /* Add to results */ if (toall || VECTOR(markto)[nei]) { IGRAPH_CHECK(igraph_vector_int_append(res, &stack)); @@ -145,9 +151,9 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, } } else { /* There is no such neighbor, finished with the subtree */ - igraph_integer_t up = igraph_vector_int_pop_back(&stack); + int up = igraph_vector_int_pop_back(&stack); igraph_vector_int_pop_back(&dist); - VECTOR(added)[up] = false; + VECTOR(added)[up] = 0; VECTOR(nptr)[up] = 0; } @@ -161,13 +167,13 @@ igraph_error_t igraph_get_all_simple_paths(const igraph_t *graph, igraph_lazy_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&dist); igraph_vector_int_destroy(&stack); - igraph_vector_bool_destroy(&added); + igraph_vector_char_destroy(&added); IGRAPH_FINALLY_CLEAN(5); if (!toall) { - igraph_vector_bool_destroy(&markto); + igraph_vector_char_destroy(&markto); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/sparsifier.c b/src/vendor/cigraph/src/paths/sparsifier.c deleted file mode 100644 index 5c7d103e0cf..00000000000 --- a/src/vendor/cigraph/src/paths/sparsifier.c +++ /dev/null @@ -1,460 +0,0 @@ -/* - 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 . -*/ - - -#include "igraph_paths.h" - -#include "igraph_adjlist.h" -#include "igraph_error.h" -#include "igraph_interface.h" -#include "igraph_random.h" - -#include "core/interruption.h" - -/* - * This internal function gets the adjacency and incidence list representation - * of the current residual graph, the weight vector, the current assignment of - * the vertices to clusters, whether the i-th cluster is sampled, and the - * index of a single node v. The function updates the given lightest_eid vector - * such that the i-th element contains the ID of the lightest edge that leads - * from node v to cluster i. Similarly, the lightest_weight vector is updated - * to contain the weights of these edges. - * - * When the is_cluster_sampled vector is provided, the - * nearest_neighboring_sampled_cluster pointer is also updated to the index of - * the cluster that has the smallest weight among the _sampled_ ones. - * - * As a pre-condition, this function requires the lightest_eid vector to be - * filled with -1 and the lightest_weight vector to be filled with infinity. - * This is _not_ checked within the function. - * - * Use the igraph_i_clean_lightest_edges_to_clusters() function to clear these vectors - * after you are done with them. Avoid using igraph_vector_fill() because that - * one is O(|V|), while igraph_i_clean_lightest_edge_vector() is O(d) where d - * is the degree of the vertex. - */ -static igraph_error_t igraph_i_collect_lightest_edges_to_clusters( - const igraph_adjlist_t *adjlist, - const igraph_inclist_t *inclist, - const igraph_vector_t *weights, - const igraph_vector_int_t *clustering, - const igraph_vector_bool_t *is_cluster_sampled, - igraph_integer_t v, - igraph_vector_int_t *lightest_eid, - igraph_vector_t *lightest_weight, - igraph_vector_int_t *dirty_vids, - igraph_integer_t *nearest_neighboring_sampled_cluster -) { - // This internal function gets the residual graph, the clustering, the sampled clustering and - // the vector and return the lightest edge to each neighboring cluster and the index of the lightest - // sampled cluster (if any) - - igraph_real_t lightest_weight_to_sampled = INFINITY; - igraph_vector_int_t* adjacent_nodes = igraph_adjlist_get(adjlist, v); - igraph_vector_int_t* incident_edges = igraph_inclist_get(inclist, v); - igraph_integer_t i, nlen = igraph_vector_int_size(incident_edges); - - for (i = 0; i < nlen; i++) { - igraph_integer_t neighbor_node = VECTOR(*adjacent_nodes)[i]; - igraph_integer_t edge = VECTOR(*incident_edges)[i]; - igraph_integer_t neighbor_cluster = VECTOR(*clustering)[neighbor_node]; - igraph_real_t weight = weights ? VECTOR(*weights)[edge] : 1; - - // If the weight of the edge being considered is smaller than the weight - // of the lightest edge found so far that connects v to the same - // cluster, remember the new minimum. - if (VECTOR(*lightest_weight)[neighbor_cluster] > weight) { - VECTOR(*lightest_weight)[neighbor_cluster] = weight; - VECTOR(*lightest_eid)[neighbor_cluster] = edge; - - IGRAPH_CHECK(igraph_vector_int_push_back(dirty_vids, neighbor_cluster)); - - // Also, if this cluster happens to be a sampled cluster, also update - // the variables that store which is the lightest edge that connects - // v to any of the sampled clusters. - if (is_cluster_sampled) { - if ((VECTOR(*is_cluster_sampled)[neighbor_cluster]) && (lightest_weight_to_sampled > weight)) { - lightest_weight_to_sampled = weight; - *nearest_neighboring_sampled_cluster = neighbor_cluster; - } - } - } - } - - return IGRAPH_SUCCESS; -} - -static void igraph_i_clear_lightest_edges_to_clusters( - igraph_vector_int_t *dirty_vids, - igraph_vector_int_t *lightest_eid, - igraph_vector_t *lightest_weight -) { - igraph_integer_t i, n = igraph_vector_int_size(dirty_vids); - for (i = 0; i < n; i++) { - igraph_integer_t vid = VECTOR(*dirty_vids)[i]; - VECTOR(*lightest_weight)[vid] = INFINITY; - VECTOR(*lightest_eid)[vid] = -1; - } - - igraph_vector_int_clear(dirty_vids); -} - -/** - * \ingroup structural - * \function igraph_spanner - * \brief Calculates a spanner of a graph with a given stretch factor. - * - * A spanner of a graph G = (V,E) with a stretch t is a subgraph - * H = (V,Es) such that Es is a subset of E and the distance - * between any pair of nodes in H is at most t times the distance - * in G. The returned graph is always a spanner of the - * given graph with the specified stretch. For weighted graphs the - * number of edges in the spanner is O(k * n^(1 + 1 / k)), where k is - * k = (stretch + 1) / 2, m is the number of edges and n is the number - * of nodes in G. For unweighted graphs the number of edges is - * O(n^(1 + 1 / k) + kn). - * - * - * This function is based on the algorithm of Baswana and Sen: "A Simple and - * Linear Time Randomized Algorithm for Computing Sparse Spanners in - * Weighted Graphs". https://doi.org/10.1002/rsa.20130 - * - * \param graph An undirected connected graph object. If the graph - * is directed, the directions of the edges will be ignored. - * \param spanner An initialized vector, the IDs of the edges that constitute - * the calculated spanner will be returned here. Use - * \ref igraph_subgraph_from_edges() to extract the spanner as a separate - * graph object. - * \param stretch The stretch factor of the spanner. - * \param weights The edge weights or NULL. - * - * \return Error code: - * \clist - * \cli IGRAPH_ENOMEM - * not enough memory for temporary data. - * \endclist - * - * Time complexity: The algorithm is a randomized Las Vegas algorithm. The expected - * running time is O(km) where k is the value mentioned above. - */ -igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanner, - igraph_real_t stretch, const igraph_vector_t *weights) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, v, nlen, neighbor, cluster; - igraph_real_t sample_prob, k = (stretch + 1) / 2, weight, lightest_sampled_weight; - igraph_vector_int_t clustering, lightest_eid; - igraph_vector_t lightest_weight; - igraph_vector_bool_t is_cluster_sampled; - igraph_vector_bool_t is_edge_in_spanner; - igraph_vector_int_t new_clustering; - igraph_vector_int_t dirty_vids; - igraph_vector_int_t *adjacent_vertices; - igraph_vector_int_t *incident_edges; - igraph_adjlist_t adjlist; - igraph_inclist_t inclist; - igraph_integer_t edge; - igraph_integer_t index; - - if (spanner == NULL) { - return IGRAPH_SUCCESS; - } - - /* Test validity of stretch factor */ - if (stretch < 1) { - IGRAPH_ERROR("Stretch factor must be at least 1", IGRAPH_EINVAL); - } - - /* Test validity of weights vector */ - if (weights) { - 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 (isnan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); - } - } - } - - // Clear the vector that will contain the IDs of the edges in the spanner - igraph_vector_int_clear(spanner); - - // Create an incidence list representation of the graph and also create the - // corresponding adjacency list. The residual graph will not be constructed - // explicitly; it will only exist in terms of the incidence and the adjacency - // lists, maintained in parallel as the edges are removed from the residual - // graph. - IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_NO_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - IGRAPH_CHECK(igraph_adjlist_init_from_inclist(graph, &adjlist, &inclist)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - - // Phase 1: forming the clusters - // Create a vector which maps the nodes to the centers of the corresponding - // clusters. At the beginning each node is its own cluster center. - IGRAPH_CHECK(igraph_vector_int_init_range(&clustering, 0, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &clustering); - - // A mapping vector which indicates the neighboring edge with the smallest - // weight for each cluster central, for a single vertex of interest. - // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() - // are enforced here. - IGRAPH_VECTOR_INT_INIT_FINALLY(&lightest_eid, no_of_nodes); - igraph_vector_int_fill(&lightest_eid, -1); - - // A mapping vector which indicated the minimum weight to each neighboring - // cluster, for a single vertex of interest. - // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() - // are enforced here. - IGRAPH_VECTOR_INIT_FINALLY(&lightest_weight, no_of_nodes); - igraph_vector_fill(&lightest_weight, IGRAPH_INFINITY); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_clustering, no_of_nodes); - - // A boolean vector whose i-th element is 1 if the i-th vertex is a cluster - // center that is sampled in the current iteration, 0 otherwise - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&is_cluster_sampled, no_of_nodes); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&is_edge_in_spanner, no_of_edges); - - // Temporary vector used by igraph_i_collect_lightest_edges_to_clusters() - // to keep track of the nodes that it has written to - IGRAPH_VECTOR_INT_INIT_FINALLY(&dirty_vids, 0); - - sample_prob = pow(no_of_nodes, -1 / k); - -#define ADD_EDGE_TO_SPANNER \ - if (!VECTOR(is_edge_in_spanner)[edge]) { \ - VECTOR(is_edge_in_spanner)[edge] = true; \ - IGRAPH_CHECK(igraph_vector_int_push_back(spanner, edge)); \ - } - - igraph_vector_fill(&lightest_weight, INFINITY); - - for (i = 0; i < k - 1; i++) { - IGRAPH_ALLOW_INTERRUPTION(); - - igraph_vector_int_fill(&new_clustering, -1); - igraph_vector_bool_fill(&is_cluster_sampled, false); - - // Step 1: sample cluster centers - RNG_BEGIN(); - for (j = 0; j < no_of_nodes; j++) { - if (VECTOR(clustering)[j] == j && RNG_UNIF01() < sample_prob) { - VECTOR(is_cluster_sampled)[j] = true; - } - } - RNG_END(); - - // Step 2 and 3 - for (v = 0; v < no_of_nodes; v++) { - // If v is inside a cluster and the cluster of v is sampled, then continue - cluster = VECTOR(clustering)[v]; - if (cluster != -1 && VECTOR(is_cluster_sampled)[cluster]) { - VECTOR(new_clustering)[v] = cluster; - continue; - } - - // Step 2: find the lightest edge that connects vertex v to its - // neighboring sampled clusters - igraph_integer_t nearest_neighboring_sampled_cluster = -1; - IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( - &adjlist, - &inclist, - weights, - &clustering, - &is_cluster_sampled, - v, - &lightest_eid, - &lightest_weight, - &dirty_vids, - &nearest_neighboring_sampled_cluster - )); - - // Step 3: add edges to spanner - if (nearest_neighboring_sampled_cluster == -1) { - // Case 3(a) from the paper: v is not adjacent to any of the - // sampled clusters. - - // Add lightest edge which connects vertex v to each neighboring - // cluster (none of which are sampled) - for (j = 0; j < no_of_nodes; j++) { - edge = VECTOR(lightest_eid)[j]; - if (edge != -1) { - ADD_EDGE_TO_SPANNER; - } - } - - // Remove all edges incident on v from the graph. Note that each - // edge being removed occurs twice in the adjacency / incidence - // lists - adjacent_vertices = igraph_adjlist_get(&adjlist, v); - incident_edges = igraph_inclist_get(&inclist, v); - nlen = igraph_vector_int_size(incident_edges); - for (j = 0; j < nlen; j++) { - neighbor = VECTOR(*adjacent_vertices)[j]; - if (neighbor == v) { - /* should not happen as we did not ask for loop edges in - * the adjacency / incidence lists, but let's be defensive */ - continue; - } - - if (igraph_vector_int_search( - igraph_inclist_get(&inclist, neighbor), - 0, - VECTOR(*incident_edges)[j], - &index - )) { - igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); - igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); - } - } - igraph_vector_int_clear(adjacent_vertices); - igraph_vector_int_clear(incident_edges); - } else { - // Case 3(b) from the paper: v is adjacent to at least one of - // the sampled clusters - - // add the edge connecting to the lightest sampled cluster - edge = VECTOR(lightest_eid)[nearest_neighboring_sampled_cluster]; - ADD_EDGE_TO_SPANNER; - - // 'lightest_sampled_weight' is the weight of the lightest edge connecting v to - // one of the sampled clusters. This is where v will belong in - // the new clustering. - lightest_sampled_weight = VECTOR(lightest_weight)[nearest_neighboring_sampled_cluster]; - VECTOR(new_clustering)[v] = nearest_neighboring_sampled_cluster; - - // Add to the spanner light edges with weight less than 'lightest_sampled_weight' - for (j = 0; j < no_of_nodes; j++) { - if (VECTOR(lightest_weight)[j] < lightest_sampled_weight) { - edge = VECTOR(lightest_eid)[j]; - ADD_EDGE_TO_SPANNER; - } - } - - // Remove edges to centers with edge weight less than 'lightest_sampled_weight' - adjacent_vertices = igraph_adjlist_get(&adjlist, v); - incident_edges = igraph_inclist_get(&inclist, v); - nlen = igraph_vector_int_size(incident_edges); - for (j = 0; j < nlen; j++) { - neighbor = VECTOR(*adjacent_vertices)[j]; - if (neighbor == v) { - /* should not happen as we did not ask for loop edges in - * the adjacency / incidence lists, but let's be defensive */ - continue; - } - - cluster = VECTOR(clustering)[neighbor]; - weight = VECTOR(lightest_weight)[cluster]; - if ((cluster == nearest_neighboring_sampled_cluster) || (weight < lightest_sampled_weight)) { - edge = VECTOR(*incident_edges)[j]; - - if (igraph_vector_int_search( - igraph_inclist_get(&inclist, neighbor), 0, edge, &index - )) { - igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); - igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); - } - - igraph_vector_int_remove_fast(adjacent_vertices, j); - igraph_vector_int_remove_fast(incident_edges, j); - - j--; - nlen--; - } - } - } - - // We don't need lightest_eids and lightest_weights any more so - // clear them in O(d) time - igraph_i_clear_lightest_edges_to_clusters( - &dirty_vids, &lightest_eid, &lightest_weight - ); - } - - // Commit the new clustering - igraph_vector_int_update(&clustering, &new_clustering); - - // Remove intra-cluster edges - for (v = 0; v < no_of_nodes; v++) { - adjacent_vertices = igraph_adjlist_get(&adjlist, v); - incident_edges = igraph_inclist_get(&inclist, v); - nlen = igraph_vector_int_size(incident_edges); - for (j = 0; j < nlen; j++) { - neighbor = VECTOR(*adjacent_vertices)[j]; - edge = VECTOR(*incident_edges)[j]; - - if (VECTOR(clustering)[neighbor] == VECTOR(clustering)[v]) { - /* We don't need to bother with removing the other copy - * of the edge from the incidence lists (and the corresponding - * vertices from the adjacency lists) because we will find - * them anyway as we are iterating over all nodes */ - igraph_vector_int_remove_fast(adjacent_vertices, j); - igraph_vector_int_remove_fast(incident_edges, j); - j--; - nlen--; - } - } - } - } - - // Phase 2: vertex_clustering joining - for (v = 0; v < no_of_nodes; v++) { - if (VECTOR(clustering)[v] != -1) { - IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( - &adjlist, - &inclist, - weights, - &clustering, - /* is_cluster_sampled = */ NULL, - v, - &lightest_eid, - &lightest_weight, - &dirty_vids, - NULL - )); - for (j = 0; j < no_of_nodes; j++) { - edge = VECTOR(lightest_eid)[j]; - if (edge != -1) { - ADD_EDGE_TO_SPANNER; - } - } - igraph_i_clear_lightest_edges_to_clusters(&dirty_vids, &lightest_eid, &lightest_weight); - } - } - - // Free memory - igraph_vector_int_destroy(&dirty_vids); - igraph_vector_bool_destroy(&is_edge_in_spanner); - igraph_vector_bool_destroy(&is_cluster_sampled); - igraph_vector_int_destroy(&new_clustering); - igraph_vector_destroy(&lightest_weight); - igraph_vector_int_destroy(&lightest_eid); - igraph_vector_int_destroy(&clustering); - igraph_adjlist_destroy(&adjlist); - igraph_inclist_destroy(&inclist); - IGRAPH_FINALLY_CLEAN(9); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/paths/unweighted.c b/src/vendor/cigraph/src/paths/unweighted.c index 976af966a4b..60503b543f2 100644 --- a/src/vendor/cigraph/src/paths/unweighted.c +++ b/src/vendor/cigraph/src/paths/unweighted.c @@ -29,24 +29,19 @@ /** * \ingroup structural - * \function igraph_distances_cutoff - * \brief Length of the shortest paths between vertices, with cutoff. - * - * \experimental - * - * This function is similar to \ref igraph_distances(), but - * paths longer than \p cutoff will not be considered. + * \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 \p from + * number of rows as the length of the \c from * argument, and its number of columns is the number of - * vertices in the \p to argument. One row of the matrix shows the - * distances from/to a given vertex to the ones in \p to. - * For the unreachable vertices \c IGRAPH_INFINITY is returned. - * \param from The source vertices._d + * 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 @@ -60,48 +55,46 @@ * the directed graph is considered as an undirected one for * the computation. * \endclist - * \param cutoff The maximal length of paths that will be considered. - * When the distance of two vertices is greater than this value, - * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are - * treated as infinity. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary * data. * \cli IGRAPH_EINVVID - * invalid vertex ID passed. + * invalid vertex id passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * - * Time complexity: O(s |E| + |V|), where s is the number of source vertices to use, - * and |V| and |E| are the number of vertices and edges in the graph. - * - * \sa \ref igraph_distances_dijkstra_cutoff() for the weighted version with non-negative - * weights. + * 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. * - * \example examples/simple/distances.c + * \sa \ref igraph_get_shortest_paths() to get the paths themselves, + * \ref igraph_shortest_paths_dijkstra() for the weighted version. */ -igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, +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, igraph_real_t cutoff) { + igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_from, no_of_to; - igraph_integer_t *already_counted; + 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_int_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; igraph_vector_int_t *neis; igraph_bool_t all_to; - igraph_integer_t i, j; + long int i, j; igraph_vit_t fromvit, tovit; - igraph_vector_int_t indexv; + 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_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); @@ -111,24 +104,25 @@ igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *r 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, igraph_integer_t); - IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for graph distance calculation."); + 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_INT_INIT_FINALLY(&q, 100); + 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_INT_INIT_FINALLY(&indexv, no_of_nodes); + 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)) { - igraph_integer_t v = IGRAPH_VIT_GET(tovit); + long int v = IGRAPH_VIT_GET(tovit); if (VECTOR(indexv)[v]) { - IGRAPH_ERROR("Target vertex list must not have any duplicates.", + IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", IGRAPH_EINVAL); } VECTOR(indexv)[v] = ++i; @@ -136,49 +130,45 @@ igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *r } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); - igraph_matrix_fill(res, IGRAPH_INFINITY); + igraph_matrix_fill(res, my_infinity); for (IGRAPH_VIT_RESET(fromvit), i = 0; !IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { - igraph_integer_t reached = 0; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(fromvit))); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - already_counted[ IGRAPH_VIT_GET(fromvit) ] = i + 1; + 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_int_empty(&q)) { - igraph_integer_t act = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - - if (cutoff >= 0 && actdist > cutoff) { - continue; - } + 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, VECTOR(indexv)[act] - 1) = actdist; + MATRIX(*res, i, (long int)(VECTOR(indexv)[act] - 1)) = actdist; reached++; if (reached == no_of_to) { - igraph_dqueue_int_clear(&q); + igraph_dqueue_clear(&q); break; } } } neis = igraph_adjlist_get(&adjlist, act); - igraph_integer_t nei_count = igraph_vector_int_size(neis); + long int nei_count = igraph_vector_int_size(neis); for (j = 0; j < nei_count; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); } } } @@ -186,87 +176,17 @@ igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *r /* Clean */ if (!all_to) { igraph_vit_destroy(&tovit); - igraph_vector_int_destroy(&indexv); + igraph_vector_destroy(&indexv); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_FREE(already_counted); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); igraph_vit_destroy(&fromvit); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; -} - -/** - * \ingroup structural - * \function igraph_distances - * \brief 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 \p from - * argument, and its number of columns is the number of - * vertices in the \p to argument. One row of the matrix shows the - * distances from/to a given vertex to the ones in \p to. - * For the unreachable vertices \c 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_distances_dijkstra() for the weighted version with non-negative - * weights, \ref igraph_distances_bellman_ford() if you also have negative - * weights. - * - * \example examples/simple/distances.c - */ -igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, - const igraph_vs_t from, const igraph_vs_t to, - igraph_neimode_t mode) { - return igraph_distances_cutoff(graph, res, from, to, mode, -1); -} - -/** - * \function igraph_shortest_paths - * \brief Length of the shortest paths between vertices. - * - * \deprecated-by igraph_distances 0.10.0 - */ -igraph_error_t 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) { - return igraph_distances(graph, res, from, to, mode); + return 0; } /** @@ -278,17 +198,23 @@ igraph_error_t igraph_shortest_paths(const igraph_t *graph, * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * Supply a null pointer here if you don't need these vectors. + * \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 + * \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 @@ -302,12 +228,12 @@ igraph_error_t igraph_shortest_paths(const igraph_t *graph, * the directed graph is considered as an * undirected one for the computation. * \endclist - * \param parents A pointer to an initialized igraph vector or null. - * If not null, a vector containing the parent of each vertex in + * \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 - * parent of vertex i in the tree is the vertex from which vertex i - * was reached. The parent of the start vertex (in the \c from - * argument) is -1. If the parent is -2, it means + * 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. @@ -325,7 +251,8 @@ igraph_error_t igraph_shortest_paths(const igraph_t *graph, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p from is invalid vertex ID + * \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 @@ -335,104 +262,105 @@ igraph_error_t igraph_shortest_paths(const igraph_t *graph, * |E| the number of edges in the * graph. * - * \sa \ref igraph_distances() if you only need the path lengths but + * \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 */ -igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, +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_int_t *parents, - igraph_vector_int_t *inbound_edges) { + igraph_vector_long_t *predecessors, + igraph_vector_long_t *inbound_edges) { /* TODO: use inclist_t if to is long (longer than 1?) */ - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t *parent_eids; + long int no_of_nodes = igraph_vcount(graph); + long int *father; - igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - igraph_integer_t i, j, vsize; - igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; + long int i, j, vsize; + igraph_vector_t tmp = IGRAPH_VECTOR_NULL; igraph_vit_t vit; - igraph_integer_t to_reach; - igraph_integer_t reached = 0; + long int to_reach; + long int reached = 0; if (from < 0 || from >= no_of_nodes) { IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); + IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); } IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); - if (vertices) { - IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(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_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); + if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { + IGRAPH_ERROR("Size of the `edges' and the `to' should match", IGRAPH_EINVAL); } - parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest path calculation."); - IGRAPH_FINALLY(igraph_free, parent_eids); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + 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 (parent_eids[ IGRAPH_VIT_GET(vit) ] == 0) { - parent_eids[ IGRAPH_VIT_GET(vit) ] = -1; + 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 parent_eids[i]: + /* Meaning of father[i]: * - * - If parent_eids[i] < 0, it means that vertex i has to be reached and has not + * - If father[i] < 0, it means that vertex i has to be reached and has not * been reached yet. * - * - If parent_eids[i] = 0, it means that vertex i does not have to be reached and + * - If father[i] = 0, it means that vertex i does not have to be reached and * it has not been reached yet. * - * - If parent_eids[i] = 1, it means that vertex i is the start vertex. + * - If father[i] = 1, it means that vertex i is the start vertex. * - * - Otherwise, parent_eids[i] is the ID of the edge from which vertex i was + * - Otherwise, father[i] is the ID of the edge from which vertex i was * reached plus 2. */ - IGRAPH_CHECK(igraph_dqueue_int_push(&q, from + 1)); - if (parent_eids[ from ] < 0) { + IGRAPH_CHECK(igraph_dqueue_push(&q, from + 1)); + if (father[ (long int) from ] < 0) { reached++; } - parent_eids[ from ] = 1; + father[ (long int)from ] = 1; - while (!igraph_dqueue_int_empty(&q) && reached < to_reach) { - igraph_integer_t act = igraph_dqueue_int_pop(&q) - 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, act, mode)); - vsize = igraph_vector_int_size(&tmp); + IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act, mode)); + vsize = igraph_vector_size(&tmp); for (j = 0; j < vsize; j++) { - igraph_integer_t edge = VECTOR(tmp)[j]; - igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, act); - if (parent_eids[neighbor] > 0) { + long int edge = (long int) VECTOR(tmp)[j]; + long int neighbor = IGRAPH_OTHER(graph, edge, act); + if (father[neighbor] > 0) { continue; - } else if (parent_eids[neighbor] < 0) { + } else if (father[neighbor] < 0) { reached++; } - parent_eids[neighbor] = edge + 2; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor + 1)); + father[neighbor] = edge + 2; + IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor + 1)); } } @@ -440,35 +368,35 @@ igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, IGRAPH_WARNING("Couldn't reach some vertices"); } - /* Create `parents' if needed */ - if (parents) { - IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + /* 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 (parent_eids[i] <= 0) { + if (father[i] <= 0) { /* i was not reached */ - VECTOR(*parents)[i] = -2; - } else if (parent_eids[i] == 1) { + VECTOR(*predecessors)[i] = -1; + } else if (father[i] == 1) { /* i is the start vertex */ - VECTOR(*parents)[i] = -1; + VECTOR(*predecessors)[i] = i; } else { - /* i was reached via the edge with ID = parent_eids[i] - 2 */ - VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 2, i); + /* 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_int_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (parent_eids[i] <= 1) { + 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 = parent_eids[i] - 2 */ - VECTOR(*inbound_edges)[i] = parent_eids[i] - 2; + /* i was reached via the edge with ID = father[i] - 2 */ + VECTOR(*inbound_edges)[i] = father[i] - 2; } } } @@ -478,39 +406,39 @@ igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, for (IGRAPH_VIT_RESET(vit), j = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), j++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_vector_int_t *vvec = 0, *evec = 0; + long int node = IGRAPH_VIT_GET(vit); + igraph_vector_t *vvec = 0, *evec = 0; if (vertices) { - vvec = igraph_vector_int_list_get_ptr(vertices, j); - igraph_vector_int_clear(vvec); + vvec = VECTOR(*vertices)[j]; + igraph_vector_clear(vvec); } if (edges) { - evec = igraph_vector_int_list_get_ptr(edges, j); - igraph_vector_int_clear(evec); + evec = VECTOR(*edges)[j]; + igraph_vector_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); - if (parent_eids[node] > 0) { - igraph_integer_t act = node; - igraph_integer_t size = 0; - igraph_integer_t edge; - while (parent_eids[act] > 1) { + if (father[node] > 0) { + long int act = node; + long int size = 0; + long int edge; + while (father[act] > 1) { size++; - edge = parent_eids[act] - 2; + edge = father[act] - 2; act = IGRAPH_OTHER(graph, edge, act); } if (vvec) { - IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_resize(evec, size)); } act = node; - while (parent_eids[act] > 1) { + while (father[act] > 1) { size--; - edge = parent_eids[act] - 2; + edge = father[act] - 2; act = IGRAPH_OTHER(graph, edge, act); if (vvec) { VECTOR(*vvec)[size] = act; @@ -524,13 +452,13 @@ igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, } /* Clean */ - IGRAPH_FREE(parent_eids); - igraph_dqueue_int_destroy(&q); - igraph_vector_int_destroy(&tmp); + IGRAPH_FREE(father); + igraph_dqueue_destroy(&q); + igraph_vector_destroy(&tmp); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return IGRAPH_SUCCESS; + return 0; } /** @@ -538,25 +466,24 @@ igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, * \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 is more than one shortest - * path 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. + * 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 + * 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 initialized vector or a null - * pointer. If not a null pointer, then the edge IDs along the + * \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 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; @@ -572,44 +499,42 @@ igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, * vertices. */ -igraph_error_t igraph_get_shortest_path(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, +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_int_list_t vertices2, *vp = &vertices2; - igraph_vector_int_list_t edges2, *ep = &edges2; + igraph_vector_ptr_t vertices2, *vp = &vertices2; + igraph_vector_ptr_t edges2, *ep = &edges2; if (vertices) { - IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + IGRAPH_CHECK(igraph_vector_ptr_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vertices2); + VECTOR(vertices2)[0] = vertices; } else { - vp = NULL; + vp = 0; } if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); + VECTOR(edges2)[0] = edges; } else { - ep = NULL; + ep = 0; } IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from, - igraph_vss_1(to), mode, NULL, NULL)); + igraph_vss_1(to), mode, 0, 0)); - /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the - result to the output parameter. */ if (edges) { - IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); - igraph_vector_int_list_destroy(&edges2); + igraph_vector_ptr_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); - igraph_vector_int_list_destroy(&vertices2); + igraph_vector_ptr_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/paths/voronoi.c b/src/vendor/cigraph/src/paths/voronoi.c deleted file mode 100644 index 1af802c1b56..00000000000 --- a/src/vendor/cigraph/src/paths/voronoi.c +++ /dev/null @@ -1,425 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_paths.h" - -#include "igraph_adjlist.h" -#include "igraph_dqueue.h" -#include "igraph_interface.h" -#include "igraph_nongraph.h" -#include "igraph_random.h" - -#include "core/indheap.h" -#include "core/interruption.h" - -static igraph_error_t igraph_i_voronoi( - const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_vector_t *mindist, - const igraph_vector_int_t *generators, - igraph_neimode_t mode, - igraph_voronoi_tiebreaker_t tiebreaker) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_generators = igraph_vector_int_size(generators); - igraph_adjlist_t al; - igraph_dqueue_int_t q; - - igraph_vector_int_t already_counted; - - /* tie_count[vid] is the number of generators that vid is an equal distance from. - * This value is needed to pick one of these generators uniformly at random - * without needing to store all of them. */ - igraph_vector_int_t tie_count; - - IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - - IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); - - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); - RNG_BEGIN(); - } - - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, -1); - - IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); - igraph_vector_fill(mindist, IGRAPH_INFINITY); - - /* Loop through all generator points and compute shortest paths to all other vertices. - * As we go, we keep track of the shortest distance from any generator to each vertex - * in 'mindist'. If we find that the distance from the current generator to a vertex - * is shorter than what was recorded so far in 'mindist', we update 'mindist' and - * assign that vertex to the current generator. - */ - for (igraph_integer_t i=0; i < no_of_generators; i++) { - igraph_integer_t g = VECTOR(*generators)[i]; - - IGRAPH_ALLOW_INTERRUPTION(); - - /* BFS-based unweighted shortest path implementation */ - - igraph_dqueue_int_clear(&q); - - VECTOR(already_counted)[g] = i+1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, g)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t vid = igraph_dqueue_int_pop(&q); - igraph_integer_t dist = igraph_dqueue_int_pop(&q); - - /* Attention! This must be igraph_real_t, not igraph_integer_t - * because later it will be compared with another igraph_real_t - * whose value may be infinite. */ - igraph_real_t md = VECTOR(*mindist)[vid]; - - if (dist > md) { - /* This vertex is reachable at a shorter distance from - * another generator. Thus all its descendants in the shortest - * path tree are also reachable at a shorter distance from the - * other generator than from the current one. Therefore - * we do not need to search further from here. */ - continue; - } else if (dist < md) { - /* This vertex is closest to the current generator so far. - * Assign it to the current partition. */ - VECTOR(*mindist)[vid] = dist; - VECTOR(*membership)[vid] = i; - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - VECTOR(tie_count)[vid] = 1; - } - } else { /* md == dist, we have a tie */ - switch (tiebreaker) { - case IGRAPH_VORONOI_FIRST: - /* Never replace existing generator assignment. */ - break; - case IGRAPH_VORONOI_LAST: - /* Always replace existing generator assignment. */ - VECTOR(*membership)[vid] = i; - break; - case IGRAPH_VORONOI_RANDOM: - /* We replace the membership assignment with probability 1/k upon - * encountering the kth same-distance generator. This ensures - * that one of these generators is selected uniformly at random. */ - VECTOR(tie_count)[vid]++; - if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { - VECTOR(*membership)[vid] = i; - } - break; - } - } - - igraph_vector_int_t *neis = igraph_adjlist_get(&al, vid); - igraph_integer_t nei_count = igraph_vector_int_size(neis); - for (igraph_integer_t j = 0; j < nei_count; j++) { - igraph_integer_t neighbor = VECTOR(*neis)[j]; - if (VECTOR(already_counted)[neighbor] == i + 1) { - continue; - } - VECTOR(already_counted)[neighbor] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, dist + 1)); - } - } - } - - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - RNG_END(); - igraph_vector_int_destroy(&tie_count); - IGRAPH_FINALLY_CLEAN(1); - } - - igraph_vector_int_destroy(&already_counted); - igraph_dqueue_int_destroy(&q); - igraph_adjlist_destroy(&al); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} - - -static igraph_error_t igraph_i_voronoi_dijkstra( - const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_vector_t *mindist, - const igraph_vector_int_t *generators, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_voronoi_tiebreaker_t tiebreaker) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_generators = igraph_vector_int_size(generators); - igraph_inclist_t il; - igraph_2wheap_t q; - - /* tie_count[vid] is the number of generators that vid is an equal distance from. - * We use this value to be able to randomly select one of the generators. */ - igraph_vector_int_t tie_count; - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - 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 (isnan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - } - - IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); - IGRAPH_FINALLY(igraph_inclist_destroy, &il); - - IGRAPH_CHECK(igraph_2wheap_init(&q, no_of_nodes)); - IGRAPH_FINALLY(igraph_2wheap_destroy, &q); - - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); - RNG_BEGIN(); - } - - IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, -1); - - IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); - igraph_vector_fill(mindist, IGRAPH_INFINITY); - - /* Loop through all generator points and compute shortest paths to all other vertices. - * As we go, we keep track of the shortest distance from any generator to each vertex - * in 'mindist'. If we find that the distance from the current generator to a vertex - * is shorter than what was recorded so far in 'mindist', we update 'mindist' and - * assign that vertex to the current generator. - */ - for (igraph_integer_t i=0; i < no_of_generators; i++) { - igraph_integer_t g = VECTOR(*generators)[i]; - - /* Weighted shortest path implementation using Dijkstra's algorithm */ - - IGRAPH_ALLOW_INTERRUPTION(); - - igraph_2wheap_clear(&q); - - /* We store negative distances in the maximum heap. Since some systems - * distinguish between -0.0 and +0.0, we need -0.0 to ensure +0.0 as - * the final result. */ - IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, g, -0.0)); - - while (!igraph_2wheap_empty(&q)) { - igraph_integer_t vid = igraph_2wheap_max_index(&q); - igraph_real_t dist = -igraph_2wheap_deactivate_max(&q); - - igraph_real_t md = VECTOR(*mindist)[vid]; - - int cmp_result = igraph_cmp_epsilon(dist, md, IGRAPH_SHORTEST_PATH_EPSILON); - if (cmp_result > 0) { /* dist > md */ - /* This vertex is reachable at a shorter distance from - * another generator. Thus all its descendants in the shortest - * path tree are also reachable at a shorter distance from the - * other generator than from the current one. Therefore - * we do not need to search further from here. */ - continue; - } else if (cmp_result < 0) { /* dist < md */ - /* This vertex is closest to the current generator so far. - * Assign it to the current partition. */ - VECTOR(*mindist)[vid] = dist; - VECTOR(*membership)[vid] = i; - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - VECTOR(tie_count)[vid] = 1; - } - } else { /* md == dist, we have a tie */ - switch (tiebreaker) { - case IGRAPH_VORONOI_FIRST: - /* Never replace existing generator assignment. */ - break; - case IGRAPH_VORONOI_LAST: - /* Always replace existing generator assignment. */ - VECTOR(*membership)[vid] = i; - break; - case IGRAPH_VORONOI_RANDOM: - /* We replace the membership assignment with probability 1/k upon - * encountering the kth same-distance generator. This ensures - * that one of these generators is selected uniformly at random. */ - VECTOR(tie_count)[vid]++; - if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { - VECTOR(*membership)[vid] = i; - } - break; - } - } - - igraph_vector_int_t *inc_edges = igraph_inclist_get(&il, vid); - igraph_integer_t inc_count = igraph_vector_int_size(inc_edges); - for (igraph_integer_t j=0; j < inc_count; j++) { - igraph_integer_t edge = VECTOR(*inc_edges)[j]; - igraph_real_t weight = VECTOR(*weights)[edge]; - - /* Optimization: do not follow infinite-weight edges. */ - if (weight == IGRAPH_INFINITY) { - continue; - } - - igraph_integer_t to = IGRAPH_OTHER(graph, edge, vid); - igraph_real_t altdist = dist + weight; - - if (! igraph_2wheap_has_elem(&q, to)) { - /* This is the first non-infinite distance */ - IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, to, -altdist)); - } else if (igraph_2wheap_has_active(&q, to)) { - igraph_real_t curdist = -igraph_2wheap_get(&q, to); - if (altdist < curdist) { - /* This is a shorter path */ - igraph_2wheap_modify(&q, to, -altdist); - } - } - } - } /* !igraph_2wheap_empty(&q) */ - } - - if (tiebreaker == IGRAPH_VORONOI_RANDOM) { - RNG_END(); - igraph_vector_int_destroy(&tie_count); - IGRAPH_FINALLY_CLEAN(1); - } - - igraph_2wheap_destroy(&q); - igraph_inclist_destroy(&il); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_voronoi - * \brief Voronoi partitioning of a graph. - * - * \experimental - * - * To obtain a Voronoi partitioning of a graph, we start with a set of generator - * vertices, which will define the partitions. Each vertex is assigned to the generator - * vertex from (or to) which it is closest. - * - * - * This function uses a BFS search for unweighted graphs and Dijkstra's algorithm - * for weights ones. - * - * \param graph The graph to partition. - * \param membership If not \c NULL, the Voronoi partition of each vertex - * will be stored here. membership[v] will be set to the index - * in \p generators of the generator vertex that \c v belongs to. For vertices - * that are not reachable from any generator, -1 is returned. - * \param distances If not \c NULL, the distance of each vertex to its respective - * generator will be stored here. For vertices which are not reachable from - * any generator, \c IGRAPH_INFINITY is returned. - * \param generators Vertex IDs of the generator vertices. - * \param weights The edge weights, interpreted as lengths in the shortest - * path calculation. All weights must be non-negative. - * \param mode In directed graphs, whether to compute distances \em from - * generator vertices to other vertices (\c IGRAPH_OUT), \em to generator - * vertices from other vertices (\c IGRAPH_IN), or ignore edge directions - * entirely (\c IGRAPH_ALL). - * \param tiebreaker Controls which generator vertex to assign a vertex to - * when it is at equal distance from/to multiple generator vertices. - * \clist - * \cli IGRAPH_VORONOI_FIRST assign the vertex to the first generator vertex. - * \cli IGRAPH_VORONOI_LAST assign the vertex to the last generator vertex. - * \cli IGRAPH_VORONOI_RANDOM assign the vertex to a random generator vertex. - * \endclist - * Note that \c IGRAPH_VORONOI_RANDOM does not guarantee that all partitions - * will be contiguous. For example, if 1 and 2 are chosen as generators for the - * graph 1-3, 2-3, 3-4, then 3 and 4 are at equal distance from - * both generators. If 3 is assigned to 2 but 4 is assigned to 1, then the - * partition {1, 4} will not induce a connected subgraph. - * \return Error code. - * - * Time complexity: In weighted graphs, O((log s) |E| log |V| + |V|), and in - * unweighted graphs O((log s) |E| + |V|), where s is the number of generator - * vertices and |V| and |E| are the number of vertices and edges in the graph. - * - * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(). - */ -igraph_error_t igraph_voronoi( - const igraph_t *graph, - igraph_vector_int_t *membership, - igraph_vector_t *distances, - const igraph_vector_int_t *generators, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_voronoi_tiebreaker_t tiebreaker) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t *pmembership; - igraph_vector_int_t imembership; - igraph_vector_t *pdistances; - igraph_vector_t idistances; - - if (! igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - if (tiebreaker != IGRAPH_VORONOI_FIRST && - tiebreaker != IGRAPH_VORONOI_LAST && - tiebreaker != IGRAPH_VORONOI_RANDOM) { - IGRAPH_ERROR("Invalid tiebreaker specification during Voronoi partitioning.", IGRAPH_EINVAL); - } - - if (! igraph_vector_int_isininterval(generators, 0, igraph_vcount(graph)-1)) { - IGRAPH_ERROR("Invalid vertex ID given as Voronoi generator.", IGRAPH_EINVVID); - } - - if (membership) { - pmembership = membership; - } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&imembership, no_of_nodes); - pmembership = &imembership; - } - - if (distances) { - pdistances = distances; - } else { - IGRAPH_VECTOR_INIT_FINALLY(&idistances, no_of_nodes); - pdistances = &idistances; - } - - if (weights) { - IGRAPH_CHECK(igraph_i_voronoi_dijkstra(graph, pmembership, pdistances, generators, weights, mode, tiebreaker)); - } else { - IGRAPH_CHECK(igraph_i_voronoi(graph, pmembership, pdistances, generators, mode, tiebreaker)); - } - - if (! distances) { - igraph_vector_destroy(&idistances); - IGRAPH_FINALLY_CLEAN(1); - } - - if (! membership) { - igraph_vector_int_destroy(&imembership); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/paths/widest_paths.c b/src/vendor/cigraph/src/paths/widest_paths.c deleted file mode 100644 index b76af01effe..00000000000 --- a/src/vendor/cigraph/src/paths/widest_paths.c +++ /dev/null @@ -1,727 +0,0 @@ -/* -*- 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_interface.h" -#include "igraph_memory.h" - -#include "core/indheap.h" -#include "core/interruption.h" -#include "internal/utils.h" - -/** - * \function igraph_get_widest_paths - * \brief Widest paths from a single vertex. - * - * Calculates the widest paths from a single node to all other specified nodes, - * using a modified Dijkstra's algorithm. If there is more than one path with - * the largest width 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * 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 list of integer vectors where each element is an - * \ref igraph_vector_int_t object. The list will be resized as needed. - * Supply a null pointer here if you don't need these vectors. - * \param from The id of the vertex from/to which the widest paths are - * calculated. - * \param to Vertex sequence with the IDs of the vertices to/from which the - * widest paths will be calculated. A vertex might be given multiple - * times. - * \param weights The edge weights. Edge weights can be negative. If this - * is a null pointer or if any edge weight is NaN, then an error - * is returned. - * \param mode The type of widest 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 parents A pointer to an initialized igraph vector or null. - * If not null, a vector containing the parent of each vertex in - * the single source widest path tree is returned here. The - * parent of vertex i in the tree is the vertex from which vertex i - * was reached. The parent of the start vertex (in the \c from - * argument) is -1. If the parent is -2, 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 widest 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 - * \cli IGRAPH_EINVMODE - * invalid mode argument. - * \endclist - * - * Time complexity: O(|E|log|E|+|V|), where |V| is the number of - * vertices in the graph and |E| is the number of edges - * - * \sa \ref igraph_widest_path_widths_dijkstra() or - * \ref igraph_widest_path_widths_floyd_warshall() if you only need the - * widths of the paths but not the paths themselves. - */ -igraph_error_t igraph_get_widest_paths(const igraph_t *graph, - igraph_vector_int_list_t *vertices, - igraph_vector_int_list_t *edges, - igraph_integer_t from, - igraph_vs_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode, - igraph_vector_int_t *parents, - igraph_vector_int_t *inbound_edges) { - - /* Implementation details: This is a Dijkstra algorithm with a - binary heap, modified to support widest paths. The heap is indexed, - so it stores both the widest path to a node, as well as it's index. We - use a 2 way heap so that we can query indexes directly in the heap. - - To adapt a Dijkstra to handle widest path, instead of prioritising candidate - nodes with the minimum distance, we prioritise those with the maximum - width instead. When adding a node into our set of 'completed' nodes, we - update all neighbouring nodes with a width that is equal to the min of the - width to the current node and the width of the edge. - - We denote the widest path from a node to itself as infinity, and the widest - path from a node to a node it cannot reach as negative infinity. - */ - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_real_t my_posinfinity = IGRAPH_POSINFINITY; - igraph_real_t my_neginfinity = IGRAPH_NEGINFINITY; - igraph_vit_t vit; - igraph_2wheap_t Q; - igraph_lazy_inclist_t inclist; - igraph_vector_t widths; - igraph_integer_t *parent_eids; - igraph_bool_t *is_target; - igraph_integer_t i, to_reach; - - if (!weights) { - IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); - } - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - igraph_vector_size(weights), no_of_edges); - } - - if (igraph_vector_is_any_nan(weights)) { - 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); - } - if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_resize(edges, 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(&widths, no_of_nodes); - igraph_vector_fill(&widths, my_neginfinity); - - parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - if (parent_eids == 0) { - IGRAPH_ERROR("Can't calculate widest paths.", IGRAPH_ENOMEM); - } - IGRAPH_FINALLY(igraph_free, parent_eids); - is_target = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); - if (is_target == 0) { - IGRAPH_ERROR("Can't calculate widest 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[ IGRAPH_VIT_GET(vit) ]) { - is_target[ IGRAPH_VIT_GET(vit) ] = 1; - } else { - to_reach--; /* this node was given multiple times */ - } - } - - VECTOR(widths)[from] = my_posinfinity; - parent_eids[from] = 0; - igraph_2wheap_push_with_index(&Q, from, my_posinfinity); - - while (!igraph_2wheap_empty(&Q) && to_reach > 0) { - igraph_integer_t nlen, maxnei = igraph_2wheap_max_index(&Q); - igraph_real_t maxwidth = igraph_2wheap_delete_max(&Q); - igraph_vector_int_t *neis; - - IGRAPH_ALLOW_INTERRUPTION(); - - if (is_target[maxnei]) { - is_target[maxnei] = 0; - to_reach--; - } - - /* Now check all neighbors of 'maxnei' for a wider path */ - neis = igraph_lazy_inclist_get(&inclist, maxnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - nlen = igraph_vector_int_size(neis); - for (i = 0; i < nlen; i++) { - igraph_integer_t edge = VECTOR(*neis)[i]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); - igraph_real_t edgewidth = VECTOR(*weights)[edge]; - igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; - igraph_real_t curwidth = VECTOR(widths)[tto]; - if (curwidth < 0) { - /* This is the first assigning a width to this vertex */ - VECTOR(widths)[tto] = altwidth; - parent_eids[tto] = edge + 1; - IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); - } else if (altwidth > curwidth) { - /* This is a wider path */ - VECTOR(widths)[tto] = altwidth; - parent_eids[tto] = edge + 1; - igraph_2wheap_modify(&Q, tto, altwidth); - } - } - } /* !igraph_2wheap_empty(&Q) */ - - - if (to_reach > 0) { - IGRAPH_WARNING("Couldn't reach some vertices."); - } - - /* Create `parents' if needed */ - if (parents) { - IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); - - for (i = 0; i < no_of_nodes; i++) { - if (i == from) { - /* i is the start vertex */ - VECTOR(*parents)[i] = -1; - } else if (parent_eids[i] <= 0) { - /* i was not reached */ - VECTOR(*parents)[i] = -2; - } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); - } - } - } - - /* Create `inbound_edges' if needed */ - if (inbound_edges) { - IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); - - for (i = 0; i < no_of_nodes; i++) { - if (parent_eids[i] <= 0) { - /* i was not reached */ - VECTOR(*inbound_edges)[i] = -1; - } else { - /* i was reached via the edge with ID = parent_eids[i] - 1 */ - VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; - } - } - } - /* Reconstruct the widest 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_integer_t size, act, edge; - igraph_vector_int_t *vvec = 0, *evec = 0; - - if (vertices) { - vvec = igraph_vector_int_list_get_ptr(vertices, i); - igraph_vector_int_clear(vvec); - } - if (edges) { - evec = igraph_vector_int_list_get_ptr(edges, i); - igraph_vector_int_clear(evec); - } - - IGRAPH_ALLOW_INTERRUPTION(); - - size = 0; - act = node; - while (parent_eids[act]) { - size++; - edge = parent_eids[act] - 1; - act = IGRAPH_OTHER(graph, edge, act); - } - if (vvec && (size > 0 || node == from)) { - IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); - VECTOR(*vvec)[size] = node; - } - if (evec) { - IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); - } - act = node; - while (parent_eids[act]) { - edge = parent_eids[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(&widths); - IGRAPH_FREE(is_target); - IGRAPH_FREE(parent_eids); - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(6); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_get_widest_path - * \brief Widest path from one vertex to another one. - * - * Calculates a single widest 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_widest_paths(). - * - * \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 initialized 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. Edge weights can be negative. If this - * is a null pointer or if any edge weight is NaN, then an error - * is returned. - * \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_widest_paths() for the version with - * more target vertices. - */ -igraph_error_t igraph_get_widest_path(const igraph_t *graph, - igraph_vector_int_t *vertices, - igraph_vector_int_t *edges, - igraph_integer_t from, - igraph_integer_t to, - const igraph_vector_t *weights, - igraph_neimode_t mode) { - - igraph_vector_int_list_t vertices2, *vp = &vertices2; - igraph_vector_int_list_t edges2, *ep = &edges2; - - if (vertices) { - IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); - } else { - vp = NULL; - } - if (edges) { - IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); - } else { - ep = NULL; - } - - IGRAPH_CHECK(igraph_get_widest_paths(graph, vp, ep, - from, igraph_vss_1(to), - weights, mode, 0, 0)); - - if (edges) { - IGRAPH_CHECK(igraph_vector_int_update(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); - igraph_vector_int_list_destroy(&edges2); - IGRAPH_FINALLY_CLEAN(1); - } - if (vertices) { - IGRAPH_CHECK(igraph_vector_int_update(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); - igraph_vector_int_list_destroy(&vertices2); - IGRAPH_FINALLY_CLEAN(1); - } - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_widest_path_widths_floyd_warshall - * \brief Widths of widest paths between vertices. - * - * This function implements a modified Floyd-Warshall algorithm, - * to find the widest path widths between a set of source and target - * vertices. It is primarily useful for all-pairs path widths in very dense - * graphs, as its running time is manily determined by the vertex count, - * and is not sensitive to the graph density. In sparse graphs, other methods - * such as the Dijkstra algorithm, will perform better. - * - * - * Note that internally this function always computes the path width matrix - * for all pairs of vertices. The \p from and \p to parameters only serve - * to subset this matrix, but do not affect the time taken by the - * calculation. - * - * \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 widths from a single source, to the - * vertices given in the \c to argument. - * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices - * have a width of \c IGRAPH_POSINFINITY to themselves. - * \param from The source vertices. - * \param to The target vertices. - * \param weights The edge weights. Edge weights can be negative. If this - * is a null pointer or if any edge weight is NaN, then an error - * is returned. - * \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(|V|^3), where |V| is the number of vertices in the graph. - * - * \sa \ref igraph_widest_path_widths_dijkstra() for a variant that runs faster - * on sparse graphs. - */ -igraph_error_t igraph_widest_path_widths_floyd_warshall(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 a modified Floyd Warshall algorithm - which computes the widest path between every pair of nodes. The key - difference between this and the regular Floyd Warshall is that instead - of updating the distance between two nodes to be the minimum of itself - and the distance through an intermediate node, we instead set the width - to be the maximum of itself and the width through the intermediate node. - - We denote the widest path from a node to itself as infinity, and the widest - path from a node to a node it cannot reach as negative infinity. - */ - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_bool_t in = false, out = false; - - if (! weights) { - IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); - } - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - igraph_vector_size(weights), no_of_edges); - } - - if (igraph_vector_is_any_nan(weights)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); - } - - if (! igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - switch (mode) { - case IGRAPH_ALL: - in = out = true; - break; - case IGRAPH_OUT: - out = true; - break; - case IGRAPH_IN: - in = true; - break; - default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); - } - - /* Fill out adjacency matrix */ - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); - igraph_matrix_fill(res, IGRAPH_NEGINFINITY); - for (igraph_integer_t i=0; i < no_of_nodes; i++) { - MATRIX(*res, i, i) = IGRAPH_POSINFINITY; - } - - for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); - igraph_real_t w = VECTOR(*weights)[edge]; - - if (out && MATRIX(*res, from, to) < w) MATRIX(*res, from, to) = w; - if (in && MATRIX(*res, to, from) < w) MATRIX(*res, to, from) = w; - } - - /* Run modified Floyd Warshall */ - for (igraph_integer_t k = 0; k < no_of_nodes; k++) { - /* Iterate in column-major order for better performance */ - for (igraph_integer_t j = 0; j < no_of_nodes; j++) { - igraph_real_t width_kj = MATRIX(*res, k, j); - if (j == k || width_kj == IGRAPH_NEGINFINITY) continue; - - IGRAPH_ALLOW_INTERRUPTION(); - - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - if (i == j || i == k) continue; - - /* alternative_width := min(A(i,k), A(k,j)) - A(i,j) := max(A(i,j), alternative_width) */ - - igraph_real_t altwidth = MATRIX(*res, i, k); - if (width_kj < altwidth) { - altwidth = width_kj; - } - if (altwidth > MATRIX(*res, i, j)) { - MATRIX(*res, i, j) = altwidth; - } - } - } - } - - IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_widest_path_widths_dijkstra - * \brief Widths of widest paths between vertices. - * - * This function implements a modified Dijkstra's algorithm, which - * can find the widest path widths from a source vertex to all - * other vertices. This function allows specifying a set of source - * and target vertices. The algorithm is run independently for each - * source and the results are retained only for the specified targets. - * This implementation uses a binary heap for efficiency. - * - * \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 widths from a single source, to the - * vertices given in the \c to argument. - * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices - * have a width of \c IGRAPH_POSINFINITY to themselves. - * \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. Edge weights can be negative. If this - * is a null pointer or if any edge weight is NaN, then an error - * is returned. - * \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 in the graph, |E| the number of edges and s the number of sources. - * - * \sa \ref igraph_widest_path_widths_floyd_warshall() for a variant that runs faster - * on dense graphs. - */ -igraph_error_t igraph_widest_path_widths_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 a Dijkstra algorithm with a - binary heap, modified to support widest paths. The heap is indexed, - so it stores both the widest path to a node, as well as it's index. We - use a 2 way heap so that we can query indexes directly in the heap. - - To adapt a Dijkstra to handle widest path, instead of prioritising candidate - nodes with the minimum distance, we prioritise those with the maximum - width instead. When adding a node into our set of 'completed' nodes, we - update all neighbouring nodes with a width that is equal to the min of the - width to the current node and the width of the edge. - - We denote the widest path from a node to itself as infinity, and the widest - path from a node to a node it cannot reach as negative infinity. - */ - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_2wheap_t Q; - igraph_vit_t fromvit, tovit; - igraph_integer_t no_of_from, no_of_to; - igraph_lazy_inclist_t inclist; - igraph_integer_t i, j; - igraph_bool_t all_to; - igraph_vector_int_t indexv; - - if (!weights) { - IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); - } - - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, - igraph_vector_size(weights), no_of_edges); - } - - if (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_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_INT_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)) { - igraph_integer_t 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, IGRAPH_NEGINFINITY); - - for (IGRAPH_VIT_RESET(fromvit), i = 0; - !IGRAPH_VIT_END(fromvit); - IGRAPH_VIT_NEXT(fromvit), i++) { - - igraph_integer_t reached = 0; - igraph_integer_t source = IGRAPH_VIT_GET(fromvit); - igraph_2wheap_clear(&Q); - igraph_2wheap_push_with_index(&Q, source, IGRAPH_POSINFINITY); - - while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t maxnei = igraph_2wheap_max_index(&Q); - igraph_real_t maxwidth = igraph_2wheap_deactivate_max(&Q); - igraph_vector_int_t *neis; - igraph_integer_t nlen; - - IGRAPH_ALLOW_INTERRUPTION(); - - if (all_to) { - MATRIX(*res, i, maxnei) = maxwidth; - } else { - if (VECTOR(indexv)[maxnei]) { - MATRIX(*res, i, VECTOR(indexv)[maxnei] - 1) = maxwidth; - reached++; - if (reached == no_of_to) { - igraph_2wheap_clear(&Q); - break; - } - } - } - - /* Now check all neighbors of 'maxnei' for a wider path*/ - neis = igraph_lazy_inclist_get(&inclist, maxnei); - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - nlen = igraph_vector_int_size(neis); - for (j = 0; j < nlen; j++) { - igraph_integer_t edge = VECTOR(*neis)[j]; - igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); - igraph_real_t edgewidth = VECTOR(*weights)[edge]; - igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; - igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); - igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); - igraph_real_t curwidth = active ? igraph_2wheap_get(&Q, tto) : IGRAPH_POSINFINITY; - if (!has) { - /* This is the first time assigning a width to this vertex */ - IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); - } else if (altwidth > curwidth) { - /* This is a wider path */ - igraph_2wheap_modify(&Q, tto, altwidth); - } - } - - } /* !igraph_2wheap_empty(&Q) */ - - } /* !IGRAPH_VIT_END(fromvit) */ - - if (!all_to) { - igraph_vit_destroy(&tovit); - igraph_vector_int_destroy(&indexv); - IGRAPH_FINALLY_CLEAN(2); - } - - igraph_lazy_inclist_destroy(&inclist); - igraph_2wheap_destroy(&Q); - igraph_vit_destroy(&fromvit); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/properties/basic_properties.c b/src/vendor/cigraph/src/properties/basic_properties.c index 3de324b751d..58bed348b2e 100644 --- a/src/vendor/cigraph/src/properties/basic_properties.c +++ b/src/vendor/cigraph/src/properties/basic_properties.c @@ -52,25 +52,25 @@ * \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 \c true then + * 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 \c false then the result is only correct if the graph does not + * this is FALSE then the result is only correct if the graph does not * contain loops. * \return Error code. * * Time complexity: O(1). */ -igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, +int igraph_density(const igraph_t *graph, igraph_real_t *res, igraph_bool_t loops) { - igraph_real_t no_of_nodes = (igraph_real_t) igraph_vcount(graph); - igraph_real_t no_of_edges = (igraph_real_t) igraph_ecount(graph); + 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 IGRAPH_SUCCESS; + return 0; } if (!loops) { @@ -89,12 +89,12 @@ igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, } } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_diversity - * \brief Structural diversity index of the vertices. + * 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, @@ -117,7 +117,7 @@ igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, * 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 + * \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 @@ -127,12 +127,12 @@ igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, * Time complexity: O(|V|+|E|), linear. * */ -igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, +int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, const igraph_vs_t vids) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t k, i; - igraph_vector_int_t incident; + 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; @@ -157,12 +157,12 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we igraph_real_t minweight = igraph_vector_min(weights); if (minweight < 0) { IGRAPH_ERROR("Weight vector must be non-negative.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { + } else if (igraph_is_nan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } - IGRAPH_VECTOR_INT_INIT_FINALLY(&incident, 10); + IGRAPH_VECTOR_INIT_FINALLY(&incident, 10); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -172,10 +172,10 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { igraph_real_t d; - igraph_integer_t v = IGRAPH_VIT_GET(vit); + long int v = IGRAPH_VIT_GET(vit); IGRAPH_CHECK(igraph_incident(graph, &incident, v, /*mode=*/ IGRAPH_ALL)); - k = igraph_vector_int_size(&incident); /* degree */ + k = igraph_vector_size(&incident); /* degree */ /* * Non-normalized diversity is defined as @@ -196,7 +196,7 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we } else { igraph_real_t s = 0.0, ent = 0.0; for (i = 0; i < k; i++) { - igraph_real_t w = VECTOR(*weights)[VECTOR(incident)[i]]; + igraph_real_t w = VECTOR(*weights)[(long int)VECTOR(incident)[i]]; if (w == 0) continue; s += w; ent += (w * log(w)); @@ -208,7 +208,7 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we } igraph_vit_destroy(&vit); - igraph_vector_int_destroy(&incident); + igraph_vector_destroy(&incident); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -257,37 +257,37 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we * * \example examples/simple/igraph_reciprocity.c */ -igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, +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_int_t inneis, outneis; - igraph_integer_t i; - igraph_integer_t no_of_nodes = igraph_vcount(graph); + 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); + IGRAPH_ERROR("Invalid reciprocity type", IGRAPH_EINVAL); } /* THIS IS AN EXIT HERE !!!!!!!!!!!!!! */ if (!igraph_is_directed(graph)) { *res = 1.0; - return IGRAPH_SUCCESS; + return 0; } - IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); for (i = 0; i < no_of_nodes; i++) { - igraph_integer_t ip, op; - IGRAPH_CHECK(igraph_neighbors(graph, &inneis, i, IGRAPH_IN)); - IGRAPH_CHECK(igraph_neighbors(graph, &outneis, i, IGRAPH_OUT)); + 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_int_size(&inneis) && - op < igraph_vector_int_size(&outneis)) { + while (ip < igraph_vector_size(&inneis) && + op < igraph_vector_size(&outneis)) { if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) { nonrec += 1; ip++; @@ -310,8 +310,8 @@ igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, op++; } } - nonrec += (igraph_vector_int_size(&inneis) - ip) + - (igraph_vector_int_size(&outneis) - op); + nonrec += (igraph_vector_size(&inneis) - ip) + + (igraph_vector_size(&outneis) - op); } if (mode == IGRAPH_RECIPROCITY_DEFAULT) { @@ -324,8 +324,8 @@ igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, *res = (igraph_real_t) rec / (rec + nonrec); } - igraph_vector_int_destroy(&inneis); - igraph_vector_int_destroy(&outneis); + igraph_vector_destroy(&inneis); + igraph_vector_destroy(&outneis); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/constraint.c b/src/vendor/cigraph/src/properties/constraint.c index 878eb25c21c..15e5e95d17f 100644 --- a/src/vendor/cigraph/src/properties/constraint.c +++ b/src/vendor/cigraph/src/properties/constraint.c @@ -24,7 +24,6 @@ #include "igraph_centrality.h" #include "igraph_interface.h" -#include "igraph_structural.h" /** * \function igraph_constraint @@ -75,19 +74,19 @@ * graph. If the weights argument is \c NULL then the time complexity * is O(|V|+n*d^2). */ -igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, +int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + long int no_of_nodes = igraph_vcount(graph); + long int no_of_edges = igraph_ecount(graph); igraph_vit_t vit; - igraph_integer_t nodes_to_calc; - igraph_integer_t a, b, c, i, j, q, vsize, vsize2; - igraph_integer_t edge, edge2; + 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_int_t ineis_in, ineis_out, jneis_in, jneis_out; + 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); @@ -95,16 +94,27 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, IGRAPH_VECTOR_INIT_FINALLY(&contrib, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ineis_in, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ineis_out, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&jneis_in, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&jneis_out, 0); + 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); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS, weights)); + 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); @@ -113,54 +123,54 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, i = IGRAPH_VIT_GET(vit); /* get neighbors of i */ - IGRAPH_CHECK(igraph_incident(graph, &ineis_in, i, + IGRAPH_CHECK(igraph_incident(graph, &ineis_in, (igraph_integer_t) i, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &ineis_out, i, + IGRAPH_CHECK(igraph_incident(graph, &ineis_out, (igraph_integer_t) i, IGRAPH_OUT)); /* NaN for isolates */ - if (igraph_vector_int_size(&ineis_in) == 0 && - igraph_vector_int_size(&ineis_out) == 0) { + if (igraph_vector_size(&ineis_in) == 0 && + igraph_vector_size(&ineis_out) == 0) { VECTOR(*res)[a] = IGRAPH_NAN; } /* zero their contribution */ - vsize = igraph_vector_int_size(&ineis_in); + vsize = igraph_vector_size(&ineis_in); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_in)[b]; - j = IGRAPH_OTHER(graph, edge, i); + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); VECTOR(contrib)[j] = 0.0; } - vsize = igraph_vector_int_size(&ineis_out); + vsize = igraph_vector_size(&ineis_out); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_out)[b]; - j = IGRAPH_OTHER(graph, edge, i); + 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_int_size(&ineis_in); + vsize = igraph_vector_size(&ineis_in); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_in)[b]; - j = IGRAPH_OTHER(graph, edge, i); + 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)[edge] / VECTOR(degree)[i]; + 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_int_size(&ineis_out); + vsize = igraph_vector_size(&ineis_out); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_out)[b]; - j = IGRAPH_OTHER(graph, edge, i); + 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)[edge] / VECTOR(degree)[i]; + VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i]; } else { VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; } @@ -169,26 +179,26 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } /* add the indirect contributions, in-in, in-out, out-in, out-out */ - vsize = igraph_vector_int_size(&ineis_in); + vsize = igraph_vector_size(&ineis_in); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_in)[b]; - j = IGRAPH_OTHER(graph, edge, i); + 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, j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, IGRAPH_OUT)); - vsize2 = igraph_vector_int_size(&jneis_in); + vsize2 = igraph_vector_size(&jneis_in); for (c = 0; c < vsize2; c++) { - edge2 = VECTOR(jneis_in)[c]; - q = IGRAPH_OTHER(graph, edge2, j); + 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)[edge] * - VECTOR(*weights)[edge2] / + 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]; @@ -196,15 +206,15 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } } if (igraph_is_directed(graph)) { - vsize2 = igraph_vector_int_size(&jneis_out); + vsize2 = igraph_vector_size(&jneis_out); for (c = 0; c < vsize2; c++) { - edge2 = VECTOR(jneis_out)[c]; - q = IGRAPH_OTHER(graph, edge2, j); + 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)[edge] * - VECTOR(*weights)[edge2] / + 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]; @@ -214,41 +224,41 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } } if (igraph_is_directed(graph)) { - vsize = igraph_vector_int_size(&ineis_out); + vsize = igraph_vector_size(&ineis_out); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_out)[b]; - j = IGRAPH_OTHER(graph, edge, i); + 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, j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, IGRAPH_OUT)); - vsize2 = igraph_vector_int_size(&jneis_in); + vsize2 = igraph_vector_size(&jneis_in); for (c = 0; c < vsize2; c++) { - edge2 = VECTOR(jneis_in)[c]; - q = IGRAPH_OTHER(graph, edge2, j); + 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)[edge] * - VECTOR(*weights)[edge2] / + 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_int_size(&jneis_out); + vsize2 = igraph_vector_size(&jneis_out); for (c = 0; c < vsize2; c++) { - edge2 = VECTOR(jneis_out)[c]; - q = IGRAPH_OTHER(graph, edge2, j); + 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)[edge] * - VECTOR(*weights)[edge2] / + 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]; @@ -259,10 +269,10 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } /* squared sum of the contributions */ - vsize = igraph_vector_int_size(&ineis_in); + vsize = igraph_vector_size(&ineis_in); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_in)[b]; - j = IGRAPH_OTHER(graph, edge, i); + edge = (igraph_integer_t) VECTOR(ineis_in)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } @@ -270,10 +280,10 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, VECTOR(contrib)[j] = 0.0; } if (igraph_is_directed(graph)) { - vsize = igraph_vector_int_size(&ineis_out); + vsize = igraph_vector_size(&ineis_out); for (b = 0; b < vsize; b++) { - edge = VECTOR(ineis_out)[b]; - j = IGRAPH_OTHER(graph, edge, i); + edge = (igraph_integer_t) VECTOR(ineis_out)[b]; + j = (long int) IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } @@ -284,13 +294,13 @@ igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } igraph_vit_destroy(&vit); - igraph_vector_int_destroy(&jneis_out); - igraph_vector_int_destroy(&jneis_in); - igraph_vector_int_destroy(&ineis_out); - igraph_vector_int_destroy(&ineis_in); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/convergence_degree.c b/src/vendor/cigraph/src/properties/convergence_degree.c index 661818f6aac..8230b5e650f 100644 --- a/src/vendor/cigraph/src/properties/convergence_degree.c +++ b/src/vendor/cigraph/src/properties/convergence_degree.c @@ -70,23 +70,23 @@ * * Time complexity: O(|V||E|), the number of vertices times the number of edges. */ -igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, +int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *ins, igraph_vector_t *outs) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t i, j, k, n; - igraph_integer_t *geodist; + 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_int_t q; + 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_int_init(&q, 100)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_destroy, &q); if (ins == 0) { ins_p = &ins_v; @@ -106,9 +106,9 @@ igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t igraph_vector_null(outs_p); } - geodist = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + geodist = IGRAPH_CALLOC(no_of_nodes, long int); if (geodist == 0) { - IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, geodist); @@ -121,19 +121,19 @@ igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t 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_int_clear(&q); - memset(geodist, 0, sizeof(geodist[0]) * (size_t) no_of_nodes); + igraph_dqueue_clear(&q); + memset(geodist, 0, sizeof(long int) * (size_t) no_of_nodes); geodist[i] = 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + 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++) { - igraph_integer_t neighbor = IGRAPH_OTHER(graph, VECTOR(*eids)[j], actnode); + 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) { @@ -141,30 +141,30 @@ igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t * increase either the size of the infield or the outfield */ if (!directed) { if (actnode < neighbor) { - VECTOR(*ins_p)[VECTOR(*eids)[j]] += 1; + VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1; } else { - VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; + VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; } } else { - vec[VECTOR(*eids)[j]] += 1; + 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_int_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + 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)[VECTOR(*eids)[j]] += 1; + VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1; } else { - VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; + VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; } } else { - vec[VECTOR(*eids)[j]] += 1; + vec[(long int)VECTOR(*eids)[j]] += 1; } geodist[neighbor] = actdist + 2; } @@ -201,8 +201,8 @@ igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t } IGRAPH_FREE(geodist); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/dag.c b/src/vendor/cigraph/src/properties/dag.c index 186ce551476..69632095f1b 100644 --- a/src/vendor/cigraph/src/properties/dag.c +++ b/src/vendor/cigraph/src/properties/dag.c @@ -59,14 +59,13 @@ * * \example examples/simple/igraph_topological_sorting.c */ -igraph_error_t igraph_topological_sorting( - const igraph_t* graph, igraph_vector_int_t *res, igraph_neimode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degrees; - igraph_vector_int_t neis; - igraph_dqueue_int_t sources; +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; - igraph_integer_t node, i, j; + 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); @@ -78,49 +77,49 @@ igraph_error_t igraph_topological_sorting( IGRAPH_ERROR("Invalid mode", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); - IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); + 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_int_clear(res); + 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_int_push(&sources, i)); + IGRAPH_CHECK(igraph_dqueue_push(&sources, i)); } } /* Take all nodes with no incoming vertices and remove them */ - while (!igraph_dqueue_int_empty(&sources)) { - node = igraph_dqueue_int_pop(&sources); + while (!igraph_dqueue_empty(&sources)) { + igraph_real_t tmp = igraph_dqueue_pop(&sources); node = (long) tmp; /* Add the node to the result vector */ - IGRAPH_CHECK(igraph_vector_int_push_back(res, node)); + 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, node, mode)); - j = igraph_vector_int_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) node, mode)); + j = igraph_vector_size(&neis); for (i = 0; i < j; i++) { - VECTOR(degrees)[ VECTOR(neis)[i] ]--; - if (VECTOR(degrees)[ VECTOR(neis)[i] ] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, VECTOR(neis)[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_int_size(res) < no_of_nodes) { + 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_int_destroy(°rees); - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&sources); + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } /** @@ -130,14 +129,6 @@ igraph_error_t igraph_topological_sorting( * * A directed acyclic graph (DAG) is a directed graph with no cycles. * - * - * This function returns false for undirected graphs. - * - * - * The return value of this function is cached in the graph itself; calling - * the function multiple times with no modifications to the graph in between - * will return a cached value in O(1) time. - * * \param graph The input graph. * \param res Pointer to a boolean constant, the result * is stored here. @@ -149,68 +140,64 @@ igraph_error_t igraph_topological_sorting( * \sa \ref igraph_topological_sorting() to get a possible topological * sorting of a DAG. */ -igraph_error_t igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degrees; - igraph_vector_int_t neis; - igraph_dqueue_int_t sources; +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 = false; + *res = 0; return IGRAPH_SUCCESS; } - IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_IS_DAG, res); - - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&sources, 0); + 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)); - IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_IN, /* loops */ true)); - - igraph_integer_t vertices_left = no_of_nodes; + vertices_left = no_of_nodes; /* Do we have nodes with no incoming edges? */ - for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (i = 0; i < no_of_nodes; i++) { if (VECTOR(degrees)[i] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, i)); + IGRAPH_CHECK(igraph_dqueue_push(&sources, i)); } } /* Take all nodes with no incoming edges and remove them */ - while (!igraph_dqueue_int_empty(&sources)) { - igraph_integer_t node = igraph_dqueue_int_pop(&sources); + 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, node, IGRAPH_OUT)); - igraph_integer_t n = igraph_vector_int_size(&neis); - for (igraph_integer_t i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(neis)[i]; + 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) { - /* Found a self-loop, graph is not a DAG */ - *res = false; - goto finalize; + continue; } VECTOR(degrees)[nei]--; if (VECTOR(degrees)[nei] == 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&sources, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&sources, nei)); } } } - IGRAPH_ASSERT(vertices_left >= 0); *res = (vertices_left == 0); + if (vertices_left < 0) { + IGRAPH_WARNING("vertices_left < 0 in igraph_is_dag, possible bug"); + } -finalize: - igraph_vector_int_destroy(°rees); - igraph_vector_int_destroy(&neis); - igraph_dqueue_int_destroy(&sources); + igraph_vector_destroy(°rees); + igraph_vector_destroy(&neis); + igraph_dqueue_destroy(&sources); IGRAPH_FINALLY_CLEAN(3); - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_DAG, *res); - return IGRAPH_SUCCESS; } @@ -218,15 +205,16 @@ igraph_error_t igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { This is fairly simple, we just collect all ancestors of a vertex using a depth-first search. */ -igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t deg; - igraph_vector_int_t new_edges; - igraph_vector_int_t ancestors; - igraph_integer_t root; - igraph_vector_int_t neighbors; - igraph_stack_int_t path; +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)) { @@ -234,12 +222,12 @@ igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *cl IGRAPH_EINVAL); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestors, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); - IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &path); + 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); @@ -252,38 +240,38 @@ igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *cl if (VECTOR(deg)[root] != 0) { continue; } - IGRAPH_CHECK(igraph_stack_int_push(&path, root)); + IGRAPH_CHECK(igraph_stack_push(&path, root)); - while (!igraph_stack_int_empty(&path)) { - igraph_integer_t node = igraph_stack_int_top(&path); + while (!igraph_stack_empty(&path)) { + long int node = (long int) igraph_stack_top(&path); if (node == STAR) { /* Leaving a node */ - igraph_integer_t j, n; - igraph_stack_int_pop(&path); - node = igraph_stack_int_pop(&path); + long int j, n; + igraph_stack_pop(&path); + node = (long int) igraph_stack_pop(&path); if (!VECTOR(done)[node]) { - igraph_vector_int_pop_back(&ancestors); - VECTOR(done)[node] = true; + igraph_vector_pop_back(&ancestors); + VECTOR(done)[node] = 1; } - n = igraph_vector_int_size(&ancestors); + n = igraph_vector_size(&ancestors); for (j = 0; j < n; j++) { - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, node)); - IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, + 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 */ - igraph_integer_t n, j; + long int n, j; if (!VECTOR(done)[node]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&ancestors, node)); + IGRAPH_CHECK(igraph_vector_push_back(&ancestors, node)); } IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, - node, IGRAPH_IN)); - n = igraph_vector_int_size(&neighbors); - IGRAPH_CHECK(igraph_stack_int_push(&path, STAR)); + (igraph_integer_t) node, IGRAPH_IN)); + n = igraph_vector_size(&neighbors); + IGRAPH_CHECK(igraph_stack_push(&path, STAR)); for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(neighbors)[j]; - IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); + long int nei = (long int) VECTOR(neighbors)[j]; + IGRAPH_CHECK(igraph_stack_push(&path, nei)); } } } @@ -292,17 +280,17 @@ igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *cl #undef STAR igraph_vector_bool_destroy(&done); - igraph_stack_int_destroy(&path); - igraph_vector_int_destroy(&neighbors); - igraph_vector_int_destroy(&ancestors); - igraph_vector_int_destroy(°); + 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, no_of_nodes, + IGRAPH_CHECK(igraph_create(closure, &new_edges, (igraph_integer_t)no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_int_destroy(&new_edges); + igraph_vector_destroy(&new_edges); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/degrees.c b/src/vendor/cigraph/src/properties/degrees.c index 814e595df90..41f00b67498 100644 --- a/src/vendor/cigraph/src/properties/degrees.c +++ b/src/vendor/cigraph/src/properties/degrees.c @@ -29,6 +29,7 @@ * \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. @@ -47,35 +48,35 @@ * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex ID. + * \c IGRAPH_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * - * Time complexity: O(v) if \p loops is \c true, and O(v*d) otherwise. v is the number + * 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. */ -igraph_error_t igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, +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_int_t tmp; + igraph_vector_t tmp; - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops)); - if (igraph_vector_int_size(&tmp) == 0) { + if (igraph_vector_size(&tmp) == 0) { *res = 0; } else { - *res = igraph_vector_int_max(&tmp); + *res = (igraph_integer_t) igraph_vector_max(&tmp); } - igraph_vector_int_destroy(&tmp); + igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, +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, @@ -83,15 +84,14 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph igraph_vector_t *knnk, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t neis, edge_neis; - igraph_integer_t i, j, no_vids; + 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; - igraph_vector_int_t deg; + igraph_vector_t strength, deg; igraph_integer_t maxdeg; - igraph_vector_int_t deghist; + igraph_vector_t deghist; igraph_real_t mynan = IGRAPH_NAN; if (igraph_vector_size(weights) != igraph_ecount(graph)) { @@ -110,7 +110,7 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph } /* Get degree of neighbours */ - IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + 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); @@ -122,30 +122,30 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph /* Get maximum degree for initialization */ IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), mode, IGRAPH_LOOPS)); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg); - IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_neis, maxdeg); - igraph_vector_int_clear(&neis); - igraph_vector_int_clear(&edge_neis); + 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, maxdeg)); + IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); igraph_vector_null(knnk); - IGRAPH_VECTOR_INT_INIT_FINALLY(°hist, maxdeg); + 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; - igraph_integer_t v = IGRAPH_VIT_GET(vit); - igraph_integer_t nv; + 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, v, mode)); - IGRAPH_CHECK(igraph_incident(graph, &edge_neis, v, mode)); - nv = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; - igraph_integer_t e = VECTOR(edge_neis)[j]; - igraph_real_t w = VECTOR(*weights)[e]; + 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) { @@ -159,13 +159,13 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph } } - igraph_vector_int_destroy(&edge_neis); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&edge_neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); if (knnk) { for (i = 0; i < maxdeg; i++) { - igraph_integer_t dh = VECTOR(deghist)[i]; + igraph_real_t dh = VECTOR(deghist)[i]; if (dh != 0) { VECTOR(*knnk)[i] /= dh; } else { @@ -173,12 +173,12 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph } } - igraph_vector_int_destroy(°hist); + igraph_vector_destroy(°hist); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&strength); - igraph_vector_int_destroy(°); + igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(2); if (!knn) { @@ -189,7 +189,7 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + return 0; } /** @@ -254,7 +254,7 @@ static igraph_error_t igraph_i_avg_nearest_neighbor_degree_weighted(const igraph * * \example examples/simple/igraph_knn.c */ -igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, +int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_neimode_t mode, igraph_neimode_t neighbor_degree_mode, @@ -262,14 +262,14 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vector_t *knnk, const igraph_vector_t *weights) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t neis; - igraph_integer_t i, j, no_vids; + 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_int_t deg; + igraph_vector_t deg; igraph_integer_t maxdeg; - igraph_vector_int_t deghist; + igraph_vector_t deghist; igraph_real_t mynan = IGRAPH_NAN; igraph_bool_t simple; @@ -295,27 +295,27 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); } - IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + 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_INT_INIT_FINALLY(&neis, maxdeg); - igraph_vector_int_clear(&neis); + IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); + igraph_vector_resize(&neis, 0); if (knnk) { - IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); + IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); igraph_vector_null(knnk); - IGRAPH_VECTOR_INT_INIT_FINALLY(°hist, maxdeg); + 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; - igraph_integer_t v = IGRAPH_VIT_GET(vit); - igraph_integer_t nv; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode)); - nv = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; sum += VECTOR(deg)[nei]; } if (nv != 0) { @@ -331,19 +331,19 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, if (knnk) { for (i = 0; i < maxdeg; i++) { - igraph_integer_t dh = VECTOR(deghist)[i]; + long int dh = (long int) VECTOR(deghist)[i]; if (dh != 0) { VECTOR(*knnk)[i] /= dh; } else { VECTOR(*knnk)[i] = mynan; } } - igraph_vector_int_destroy(°hist); + igraph_vector_destroy(°hist); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&neis); - igraph_vector_int_destroy(°); + igraph_vector_destroy(&neis); + igraph_vector_destroy(°); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); @@ -352,17 +352,16 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_strength - * \brief Strength of the vertices, also called weighted vertex degree. + * 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. @@ -370,7 +369,7 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, * \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 \c NULL + * \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. @@ -380,64 +379,54 @@ igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, * * \sa \ref igraph_degree() for the traditional, non-weighted version. */ -igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; - igraph_integer_t no_vids; - igraph_vector_int_t degrees; - igraph_vector_int_t neis; - igraph_integer_t i; + long int no_vids; + igraph_vector_t neis; + long int i; if (!weights) { - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); - IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); - for (i = 0; i < no_of_nodes; i++) { - VECTOR(*res)[i] = VECTOR(degrees)[i]; - } - igraph_vector_int_destroy(°rees); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; + 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_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_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&neis, no_of_nodes)); + 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++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); - igraph_integer_t j, n; - IGRAPH_CHECK(igraph_incident(graph, &neis, vid, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t edge = VECTOR(neis)[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++) { - igraph_integer_t vid = IGRAPH_VIT_GET(vit); - igraph_integer_t j, n; - IGRAPH_CHECK(igraph_incident(graph, &neis, vid, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t edge = VECTOR(neis)[j]; - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); + 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]; } @@ -446,24 +435,24 @@ igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, } igraph_vit_destroy(&vit); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_sort_vertex_ids_by_degree - * \brief Calculate a list of vertex IDs sorted by degree of the corresponding vertex. + * \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 + * 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 + * 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, @@ -477,41 +466,40 @@ igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, * (\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 \ref igraph_vs_all() or \ref igraph_vss_all(), - * because in this case the indices and vertex IDs are the + * 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_EINVVID: invalid vertex id. * \c IGRAPH_EINVMODE: invalid mode argument. * */ -igraph_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, - igraph_vector_int_t *outvids, +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) { - igraph_integer_t i, n; - igraph_vector_int_t degrees; - igraph_vector_int_t vs_vec; - IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, 0); + 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(igraph_vector_int_qsort_ind(°rees, outvids, order)); + IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, outvids, + order == IGRAPH_DESCENDING)); if (only_indices || igraph_vs_is_all(&vids) ) { - igraph_vector_int_destroy(°rees); + igraph_vector_destroy(°rees); IGRAPH_FINALLY_CLEAN(1); } else { - IGRAPH_VECTOR_INT_INIT_FINALLY(&vs_vec, 0); + IGRAPH_VECTOR_INIT_FINALLY(&vs_vec, 0); IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec)); - n = igraph_vector_int_size(outvids); - for (i = 0; i < n; i++) { - VECTOR(*outvids)[i] = VECTOR(vs_vec)[VECTOR(*outvids)[i]]; + for (i = 0; i < igraph_vector_size(outvids); i++) { + VECTOR(*outvids)[i] = VECTOR(vs_vec)[(long int)VECTOR(*outvids)[i]]; } - igraph_vector_int_destroy(&vs_vec); - igraph_vector_int_destroy(°rees); + igraph_vector_destroy(&vs_vec); + igraph_vector_destroy(°rees); IGRAPH_FINALLY_CLEAN(2); } - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/ecc.c b/src/vendor/cigraph/src/properties/ecc.c deleted file mode 100644 index a81717d4f7b..00000000000 --- a/src/vendor/cigraph/src/properties/ecc.c +++ /dev/null @@ -1,411 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_transitivity.h" - -#include "igraph_interface.h" -#include "igraph_iterators.h" -#include "igraph_adjlist.h" - -#include "core/interruption.h" - -/* Computes the size of the intersection of two sorted vectors, treated as sets. - * It is assumed that the vectors contain no duplicates. - * - * We rely on (lazy_)adjlist_get() producing sorted neighbor lists and - * (lazy_)adjlist_init() being called with IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE - * to prevent duplicate entries. - */ -static igraph_integer_t vector_int_intersection_size_sorted( - const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { - igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); - igraph_integer_t i1 = 0, i2 = 0; - igraph_integer_t count = 0; - - while (i1 < n1 && i2 < n2) { - igraph_integer_t e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; - if (e1 < e2) { - i1++; - } else if (e1 == e2) { - count++; - i1++; i2++; - } else { /* e2 > e1 */ - i2++; - } - } - - return count; -} - - -/* Optimized for the case when computing ECC for all edges. */ -static igraph_error_t igraph_i_ecc3_1( - const igraph_t *graph, igraph_vector_t *res, const igraph_es_t eids, - igraph_bool_t offset, igraph_bool_t normalize) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degree; - igraph_adjlist_t al; - igraph_eit_t eit; - const igraph_real_t c = offset ? 1.0 : 0.0; - - IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); - - IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); - - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); - - for (igraph_integer_t i=0; - ! IGRAPH_EIT_END(eit); - IGRAPH_EIT_NEXT(eit), i++) { - - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); - - igraph_real_t z; /* number of triangles the edge participates in */ - igraph_real_t s; /* max number of triangles the edge could be part of */ - - IGRAPH_ALLOW_INTERRUPTION(); - - if (v1 == v2) { - /* A self-loop isn't, and cannot be part of any triangles. */ - z = 0.0; - s = 0.0; - } else { - const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1), *a2 = igraph_adjlist_get(&al, v2); - igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; - - z = vector_int_intersection_size_sorted(a1, a2); - s = (d1 < d2 ? d1 : d2) - 1.0; - } - - VECTOR(*res)[i] = z + c; - if (normalize) VECTOR(*res)[i] /= s; - } - - igraph_eit_destroy(&eit); - igraph_vector_int_destroy(°ree); - igraph_adjlist_destroy(&al); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} - - -/* Optimized for computing ECC for a small subset of edges. */ -static igraph_error_t igraph_i_ecc3_2( - const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { - - igraph_lazy_adjlist_t al; - igraph_eit_t eit; - const igraph_real_t c = offset ? 1.0 : 0.0; - - IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); - - IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); - - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); - - for (igraph_integer_t i=0; - ! IGRAPH_EIT_END(eit); - IGRAPH_EIT_NEXT(eit), i++) { - - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); - - igraph_real_t z; /* number of triangles the edge participates in */ - igraph_real_t s; /* max number of triangles the edge could be part of */ - - IGRAPH_ALLOW_INTERRUPTION(); - - if (v1 == v2) { - /* A self-loop isn't, and cannot be part of any triangles. */ - z = 0.0; - s = 0.0; - } else { - igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); - igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); - - igraph_integer_t d1, d2; - IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); - IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); - - z = vector_int_intersection_size_sorted(a1, a2); - s = (d1 < d2 ? d1 : d2) - 1.0; - } - - VECTOR(*res)[i] = z + c; - if (normalize) VECTOR(*res)[i] /= s; - } - - igraph_eit_destroy(&eit); - igraph_lazy_adjlist_destroy(&al); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - - -/* Optimized for the case when computing ECC for all edges. */ -static igraph_error_t igraph_i_ecc4_1( - const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { - - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t degree; - igraph_adjlist_t al; - igraph_eit_t eit; - igraph_real_t c = offset ? 1.0 : 0.0; - - IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &al); - - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); - - IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); - - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); - - for (igraph_integer_t i=0; - ! IGRAPH_EIT_END(eit); - IGRAPH_EIT_NEXT(eit), i++) { - - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); - - igraph_real_t z; /* number of 4-cycles the edge participates in */ - igraph_real_t s; /* max number of 4-cycles the edge could be part of */ - - IGRAPH_ALLOW_INTERRUPTION(); - - if (v1 == v2) { - z = 0.0; - s = 0.0; - } else { - /* ensure that v1 is the vertex with the smaller degree */ - if (VECTOR(degree)[v1] > VECTOR(degree)[v2]) { - igraph_integer_t tmp = v1; - v1 = v2; - v2 = tmp; - } - - z = 0.0; - const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1); - const igraph_integer_t n = igraph_vector_int_size(a1); - for (igraph_integer_t j=0; j < n; j++) { - igraph_integer_t v3 = VECTOR(*a1)[j]; - - /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ - - if (v3 == v2) continue; - - const igraph_vector_int_t *a2 = igraph_adjlist_get(&al, v2), *a3 = igraph_adjlist_get(&al, v3); - - z += vector_int_intersection_size_sorted(a2, a3) - 1.0; - } - - igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; - s = (d1 - 1.0) * (d2 - 1.0); - } - - VECTOR(*res)[i] = z + c; - if (normalize) VECTOR(*res)[i] /= s; - } - - igraph_eit_destroy(&eit); - igraph_vector_int_destroy(°ree); - igraph_adjlist_destroy(&al); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} - - -/* Optimized for computing ECC for a small subset of edges. */ -static igraph_error_t igraph_i_ecc4_2( - const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { - - igraph_lazy_adjlist_t al; - igraph_eit_t eit; - igraph_real_t c = offset ? 1.0 : 0.0; - - IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); - - IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); - IGRAPH_FINALLY(igraph_eit_destroy, &eit); - - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); - - for (igraph_integer_t i=0; - ! IGRAPH_EIT_END(eit); - IGRAPH_EIT_NEXT(eit), i++) { - - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); - - igraph_real_t z; /* number of 4-cycles the edge participates in */ - igraph_real_t s; /* max number of 4-cycles the edge could be part of */ - - IGRAPH_ALLOW_INTERRUPTION(); - - igraph_integer_t d1, d2; - IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); - IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); - - if (v1 == v2) { - z = 0.0; - s = 0.0; - } else { - /* ensure that v1 is the vertex with the smaller degree */ - if (d1 > d2) { - igraph_integer_t tmp = v1; - v1 = v2; - v2 = tmp; - - tmp = d1; - d1 = d2; - d2 = tmp; - } - - z = 0.0; - - igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); - - const igraph_integer_t n = igraph_vector_int_size(a1); - for (igraph_integer_t j=0; j < n; j++) { - igraph_integer_t v3 = VECTOR(*a1)[j]; - - /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ - - if (v3 == v2) continue; - - igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); - igraph_vector_int_t *a3 = igraph_lazy_adjlist_get(&al, v3); - - z += vector_int_intersection_size_sorted(a2, a3) - 1.0; - } - - s = (d1 - 1.0) * (d2 - 1.0); - } - - VECTOR(*res)[i] = z + c; - if (normalize) VECTOR(*res)[i] /= s; - } - - igraph_eit_destroy(&eit); - igraph_lazy_adjlist_destroy(&al); - IGRAPH_FINALLY_CLEAN(2); - - return IGRAPH_SUCCESS; -} - - -/** - * \function igraph_ecc - * \brief Edge clustering coefficient of some edges. - * - * \experimental - * - * The edge clustering coefficient C^(k)_ij of an edge (i, j) - * is defined based on the number of k-cycles the edge participates in, - * z^(k)_ij, and the largest number of such cycles it could - * participate in given the degree of its endpoints, s^(k)_ij. - * The original definition given in the reference below is: - * - * - * C^(k)_ij = (z^(k)_ij + 1) / s^(k)_ij - * - * - * For k=3, s^(k)_ij = min(d_i - 1, d_j - 1), - * where \c d_i and \c d_j are the edge endpoint degrees. - * For k=4, s^(k)_ij = (d_i - 1) (d_j - 1). - * - * - * The \p normalize and \p offset parameters allow for skipping normalization - * by s^(k) and offsetting the cycle count z^(k) - * by one in the numerator of C^(k). Set both to \c true to - * compute the original definition of this metric. - * - * - * This function ignores edge multiplicities when listing k-cycles - * (i.e. z^(k)), but not when computing the maximum number of - * cycles an edge can participate in (s^(k)). - * - * - * Reference: - * - * - * F. Radicchi, C. Castellano, F. Cecconi, V. Loreto, and D. Parisi, - * PNAS 101, 2658 (2004). - * https://doi.org/10.1073/pnas.0400054101 - * - * \param graph The input graph. - * \param res Initialized vector, the result will be stored here. - * \param eids The edges for which the edge clustering coefficient will be computed. - * \param k Size of cycles to use in calculation. Must be at least 3. Currently - * only values of 3 and 4 are supported. - * \param offset Boolean, whether to add one to cycle counts. When \c false, - * z^(k) is used instead of z^(k) + 1. In this case - * the maximum value of the normalized metric is 1. For k=3 this - * is achieved for all edges in a complete graph. - * \param normalize Boolean, whether to normalize cycle counts by the maximum - * possible count s^(k) given the degrees. - * \return Error code. - * - * Time complexity: When \p k is 3, O(|V| d log d + |E| d). - * When \p k is 4, O(|V| d log d + |E| d^2). d denotes the degree of vertices. - */ -igraph_error_t igraph_ecc(const igraph_t *graph, igraph_vector_t *res, - const igraph_es_t eids, igraph_integer_t k, - igraph_bool_t offset, igraph_bool_t normalize) { - - if (k < 3) { - IGRAPH_ERRORF("Cycle size for edge clustering coefficient must be at least 3, got %" IGRAPH_PRId ".", - IGRAPH_EINVAL, k); - } - - switch (k) { - case 3: - if (igraph_es_is_all(&eids)) { - return igraph_i_ecc3_1(graph, res, eids, offset, normalize); - } else { - return igraph_i_ecc3_2(graph, res, eids, offset, normalize); - } - case 4: - if (igraph_es_is_all(&eids)) { - return igraph_i_ecc4_1(graph, res, eids, offset, normalize); - } else { - return igraph_i_ecc4_2(graph, res, eids, offset, normalize); - } - default: - IGRAPH_ERROR("Edge clustering coefficient calculation is only implemented for cycle sizes 3 and 4.", - IGRAPH_UNIMPLEMENTED); - } -} diff --git a/src/vendor/cigraph/src/properties/girth.c b/src/vendor/cigraph/src/properties/girth.c index 20bf0b6dfc2..4462206895f 100644 --- a/src/vendor/cigraph/src/properties/girth.c +++ b/src/vendor/cigraph/src/properties/girth.c @@ -30,6 +30,8 @@ #include "core/interruption.h" +#include + /** * \function igraph_girth * \brief The girth of a graph is the length of the shortest cycle in it. @@ -41,7 +43,10 @@ * * * For graphs that contain no cycles, and only for such graphs, - * infinity is returned. + * 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: @@ -50,9 +55,9 @@ * 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 \c igraph_real_t, if not \c NULL then the result + * \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 + * \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. @@ -64,25 +69,26 @@ * * \example examples/simple/igraph_girth.c */ -igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, - igraph_vector_int_t *circle) { +int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, + igraph_vector_t *circle) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; igraph_lazy_adjlist_t adjlist; - igraph_integer_t mincirc = IGRAPH_INTEGER_MAX, minvertex = 0; - igraph_integer_t node; - igraph_bool_t triangle = false; + long int mincirc = LONG_MAX, minvertex = 0; + long int node; + igraph_bool_t triangle = 0; igraph_vector_int_t *neis; - igraph_vector_int_t level; - igraph_integer_t stoplevel = no_of_nodes + 1; - igraph_bool_t anycircle = false; - igraph_integer_t t1 = 0, t2 = 0; + 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_INT_INIT_FINALLY(&q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&level, no_of_nodes); + 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++) { @@ -97,29 +103,27 @@ igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, } anycircle = 0; - igraph_dqueue_int_clear(&q); - igraph_vector_int_null(&level); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + 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_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actlevel = VECTOR(level)[actnode]; - igraph_integer_t i, n; + 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, actnode); - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - + neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode); n = igraph_vector_int_size(neis); for (i = 0; i < n; i++) { - igraph_integer_t nei = VECTOR(*neis)[i]; - igraph_integer_t neilevel = VECTOR(level)[nei]; + long int nei = (long int) VECTOR(*neis)[i]; + long int neilevel = VECTOR(level)[nei]; if (neilevel != 0) { if (neilevel == actlevel - 1) { continue; @@ -142,7 +146,7 @@ igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, } } } else { - igraph_dqueue_int_push(&q, nei); + igraph_dqueue_push(&q, nei); VECTOR(level)[nei] = actlevel + 1; } } @@ -150,38 +154,33 @@ igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, } /* while q !empty */ } /* node */ - if (girth) { - if (mincirc == IGRAPH_INTEGER_MAX) { - *girth = IGRAPH_INFINITY; - } else { - *girth = mincirc; - } + if (mincirc == LONG_MAX) { + mincirc = 0; } - if (mincirc == IGRAPH_INTEGER_MAX) { - mincirc = 0; + if (girth) { + *girth = (igraph_integer_t) mincirc; } /* Store the actual circle, if needed */ if (circle) { - IGRAPH_CHECK(igraph_vector_int_resize(circle, mincirc)); + IGRAPH_CHECK(igraph_vector_resize(circle, mincirc)); if (mincirc != 0) { - igraph_integer_t i, n, idx = 0; - igraph_dqueue_int_clear(&q); - igraph_vector_int_null(&level); /* used for father pointers */ + 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_int_push(&q, minvertex)); + IGRAPH_CHECK(igraph_dqueue_push(&q, minvertex)); FATHER(minvertex) = minvertex; while (FATHER(t1) == 0 || FATHER(t2) == 0) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - neis = igraph_lazy_adjlist_get(&adjlist, actnode); - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + 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++) { - igraph_integer_t nei = VECTOR(*neis)[i]; + long int nei = (long int) VECTOR(*neis)[i]; if (FATHER(nei) == 0) { FATHER(nei) = actnode + 1; - igraph_dqueue_int_push(&q, nei); + igraph_dqueue_push(&q, nei); } } } /* while q !empty */ @@ -200,10 +199,10 @@ igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, } /* circle */ #undef FATHER - igraph_vector_int_destroy(&level); - igraph_dqueue_int_destroy(&q); + igraph_vector_long_destroy(&level); + igraph_dqueue_destroy(&q); igraph_lazy_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(3); - return IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/loops.c b/src/vendor/cigraph/src/properties/loops.c index 6cf329c8170..7728d5720a3 100644 --- a/src/vendor/cigraph/src/properties/loops.c +++ b/src/vendor/cigraph/src/properties/loops.c @@ -31,12 +31,6 @@ * * * A loop edge is an edge from a vertex to itself. - * - * - * The return value of this function is cached in the graph itself; calling - * the function multiple times with no modifications to the graph in between - * will return a cached value in O(1) time. - * * \param graph The input graph. * \param res Pointer to an initialized boolean vector for storing the result. * @@ -46,23 +40,19 @@ * * \example examples/simple/igraph_has_loop.c */ -igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { - igraph_integer_t i, m = igraph_ecount(graph); +int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { + long int i, m = igraph_ecount(graph); - IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_LOOP, res); - - *res = false; + *res = 0; for (i = 0; i < m; i++) { if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) { - *res = true; + *res = 1; break; } } - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, *res); - - return IGRAPH_SUCCESS; + return 0; } /** @@ -83,10 +73,10 @@ igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { * * \example examples/simple/igraph_is_loop.c */ -igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, +int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; - igraph_integer_t i; + long int i; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); @@ -94,12 +84,12 @@ igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t e = IGRAPH_EIT_GET(eit); - VECTOR(*res)[i] = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)); + 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 IGRAPH_SUCCESS; + return 0; } diff --git a/src/vendor/cigraph/src/properties/multiplicity.c b/src/vendor/cigraph/src/properties/multiplicity.c index 587d9f000e0..db2ea935b0d 100644 --- a/src/vendor/cigraph/src/properties/multiplicity.c +++ b/src/vendor/cigraph/src/properties/multiplicity.c @@ -31,6 +31,7 @@ * \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. * @@ -46,54 +47,35 @@ * * Time complexity: O(|V|+|E|). */ -igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { - igraph_integer_t vc = igraph_vcount(graph); - igraph_integer_t ec = igraph_ecount(graph); - - if ( - igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && - igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) - ) { - /* use the cached result */ - *res = ( - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP) && - !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI) - ); - return IGRAPH_SUCCESS; - } +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 = true; + *res = 1; } else { - igraph_vector_int_t neis; - igraph_integer_t i, j, n; - igraph_bool_t found = false; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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, i, IGRAPH_OUT)); - n = igraph_vector_int_size(&neis); + 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 = true; break; + found = 1; break; } if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { - found = true; break; + found = 1; break; } } } *res = !found; - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } - /* If the graph turned out to be simple, we can cache that it has no loop - * and no multiple edges */ - if (*res) { - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, 0); - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, 0); - } - - return IGRAPH_SUCCESS; + return 0; } @@ -101,14 +83,10 @@ igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { * \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. * - * - * The return value of this function is cached in the graph itself; calling - * the function multiple times with no modifications to the graph in between - * will return a cached value in O(1) time. - * * \param graph The input graph. * \param res Pointer to a boolean variable, the result will be stored here. * \return Error code. @@ -121,62 +99,58 @@ igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { * * \example examples/simple/igraph_has_multiple.c */ -igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { - igraph_integer_t vc = igraph_vcount(graph); - igraph_integer_t ec = igraph_ecount(graph); +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); - IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_MULTI, res); - if (vc == 0 || ec == 0) { - *res = false; + *res = 0; } else { - igraph_vector_int_t neis; - igraph_integer_t i, j, n; - igraph_bool_t found = false; - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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, i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, IGRAPH_OUT)); - n = igraph_vector_int_size(&neis); + 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 = true; break; + found = 1; break; } else if (VECTOR(neis)[j - 1] != i) { /* Undirected, but not a loop edge */ - found = true; break; + found = 1; break; } else if (j < n - 1 && VECTOR(neis)[j] == VECTOR(neis)[j + 1]) { /* Undirected, loop edge, multiple times */ - found = true; break; + found = 1; break; } } } } *res = found; - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, *res); - - return IGRAPH_SUCCESS; + 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. @@ -192,10 +166,10 @@ igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { * * \example examples/simple/igraph_is_multiple.c */ -igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, +int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; - igraph_integer_t i, j, n; + long int i, j, n; igraph_lazy_inclist_t inclist; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -207,21 +181,25 @@ igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *r IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t e = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, from); - - IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + 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] = false; + VECTOR(*res)[i] = 0; n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - igraph_integer_t e2 = VECTOR(*neis)[j]; - igraph_integer_t to2 = IGRAPH_OTHER(graph, e2, from); + long int e2 = (long int) VECTOR(*neis)[j]; + long int to2 = IGRAPH_OTHER(graph, e2, from); if (to2 == to && e2 < e) { - VECTOR(*res)[i] = true; + VECTOR(*res)[i] = 1; } } } @@ -229,17 +207,21 @@ igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *r igraph_lazy_inclist_destroy(&inclist); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } + /** * \function igraph_count_multiple - * \brief The multiplicity of some edges in a graph. + * \brief Count the number of appearances of the edges in a graph. * - * An edge is called a multiple edge when there is one or more other - * edge between the same two vertices. The multiplicity of an edge - * is the number of edges between its endpoints. + * + * 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. @@ -247,101 +229,60 @@ igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *r * to check all edges. * \return Error code. * - * \sa \ref igraph_count_multiple_1() if you only need the multiplicity of a - * single edge; \ref igraph_is_multiple() if you are only interested in whether - * the graph has at least one edge with multiplicity greater than one; - * \ref igraph_simplify() to ensure that the graph has no multiple edges. + * \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. */ -igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es) { +int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es) { igraph_eit_t eit; - igraph_integer_t i, j, n; - igraph_lazy_adjlist_t adjlist; + 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_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); - IGRAPH_CHECK(igraph_vector_int_resize(res, IGRAPH_EIT_SIZE(eit))); + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t e = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, e); - igraph_integer_t to = IGRAPH_TO(graph, e); - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, from); - - IGRAPH_CHECK_OOM(neis, "Failed to query adjacent vertices."); + 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++) { - if (VECTOR(*neis)[j] == to) { - VECTOR(*res)[i]++; + 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_adjlist_destroy(&adjlist); + igraph_lazy_inclist_destroy(&inclist); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } -/** - * \function igraph_count_multiple_1 - * \brief The multiplicity of a single edge in a graph. - * - * \param graph The input graph. - * \param res Pointer to an iteger, the result will be stored here. - * \param eid The ID of the edge to check. - * \return Error code. - * - * \sa \ref igraph_count_multiple() if you need the multiplicity of multiple - * edges; \ref igraph_is_multiple() if you are only interested in whether the - * graph has at least one edge with multiplicity greater than one; - * \ref igraph_simplify() to ensure that the graph has no multiple edges. - * - * Time complexity: O(d), where d is the out-degree of the tail of the edge. - */ -igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid) -{ - igraph_integer_t i, n, count; - igraph_integer_t from = IGRAPH_FROM(graph, eid); - igraph_integer_t to = IGRAPH_TO(graph, eid); - igraph_vector_int_t vids; - - IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); - IGRAPH_CHECK(igraph_neighbors(graph, &vids, from, IGRAPH_OUT)); - - count = 0; - n = igraph_vector_int_size(&vids); - for (i = 0; i < n; i++) { - if (VECTOR(vids)[i] == to) { - count++; - } - } - - igraph_vector_int_destroy(&vids); - IGRAPH_FINALLY_CLEAN(1); - - *res = count; - - return IGRAPH_SUCCESS; -} - /** * \function igraph_is_mutual * \brief Check whether some edges of a directed graph are mutual. * - * An (A,B) non-loop directed edge is mutual if the graph contains - * the (B,A) edge too. Whether directed self-loops are considered mutual - * is controlled by the \p loops parameter. + * An (A,B) edge is mutual if the graph contains the (B,A) edge too. * * * An undirected graph only has mutual edges, by definition. @@ -351,13 +292,14 @@ igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t * * (A,B) edges and one (B,A) edge, then all three are considered to be * mutual. * + * + * Self-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 * \ref igraph_ess_all() to check all edges. - * \param loops Boolean, whether to consider directed self-loops - * to be mutual. * \return Error code. * * Time complexity: O(n log(d)), n is the number of edges supplied, d @@ -365,11 +307,11 @@ igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t * * supplied edges. An upper limit of the time complexity is O(n log(|E|)), * |E| is the number of edges in the graph. */ -igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops) { +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; - igraph_integer_t i; + long int i; /* How many edges do we have? */ IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -379,7 +321,7 @@ igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res /* An undirected graph has mutual edges by definition, res is already properly resized */ if (! igraph_is_directed(graph)) { - igraph_vector_bool_fill(res, true); + igraph_vector_bool_fill(res, 1); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -389,19 +331,16 @@ igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); for (i = 0; ! IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { - igraph_integer_t edge = IGRAPH_EIT_GET(eit); - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); - - if (from == to) { - VECTOR(*res)[i] = loops; - continue; /* no need to do binsearch for self-loops */ - } + 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 */ - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) to); + if (neis == NULL) { + IGRAPH_ERROR("Failed to query neighbors.", IGRAPH_ENOMEM); + } VECTOR(*res)[i] = igraph_vector_int_binsearch2(neis, from); } @@ -411,93 +350,3 @@ igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res return IGRAPH_SUCCESS; } - - -/** - * \function igraph_has_mutual - * \brief Check whether a directed graph has any mutual edges. - * - * An (A,B) non-loop directed edge is mutual if the graph contains - * the (B,A) edge too. Whether directed self-loops are considered mutual - * is controlled by the \p loops parameter. - * - * - * In undirected graphs, all edges are considered mutual by definition. - * Thus for undirected graph, this function returns false only when there - * are no edges. - * - * - * To check whether a graph is an oriented graph, use this function in - * conjunction with \ref igraph_is_directed(). - * - * \param graph The input graph. - * \param res Pointer to a boolean, the result will be stored here. - * \param loops Boolean, whether to consider directed self-loops - * to be mutual. - * \return Error code. - * - * Time complexity: O(|E| log(d)) where d is the maximum in-degree. - */ -igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops) { - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_lazy_adjlist_t adjlist; - - if (! igraph_is_directed(graph)) { - /* In undirected graphs, all edges are considered mutual, so we just check - * if there are any edges. */ - *res = no_of_edges > 0; - return IGRAPH_SUCCESS; - } - - if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MUTUAL)) { - if (igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MUTUAL)) { - /* we know that the graph has at least one mutual non-loop edge - * (because the cache only stores non-loop edges) */ - *res = true; - return IGRAPH_SUCCESS; - } else if (loops) { - /* no non-loop mutual edges, but maybe we have loops? */ - return igraph_has_loop(graph, res); - } else { - /* no non-loop mutual edges, and loops are not to be treated as mutual */ - *res = false; - 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); - - *res = false; /* assume no mutual edges */ - for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); - - if (from == to) { - if (loops) { - *res = true; - break; - } - continue; /* no need to do binsearch for self-loops */ - } - - /* Check whether there is a to->from edge, search for from in the - out-list of to */ - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); - IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - if (igraph_vector_int_binsearch2(neis, from)) { - *res = true; - break; - } - } - - igraph_lazy_adjlist_destroy(&adjlist); - IGRAPH_FINALLY_CLEAN(1); - - /* cache the result if loops are not treated as mutual */ - if (!loops) { - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MUTUAL, *res); - } - - return IGRAPH_SUCCESS; -} diff --git a/src/vendor/cigraph/src/properties/neighborhood.c b/src/vendor/cigraph/src/properties/neighborhood.c index 98fbbe1ed57..5a654154474 100644 --- a/src/vendor/cigraph/src/properties/neighborhood.c +++ b/src/vendor/cigraph/src/properties/neighborhood.c @@ -67,17 +67,17 @@ * 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. */ -igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; igraph_vit_t vit; - igraph_integer_t i, j; - igraph_integer_t *added; - igraph_vector_int_t neis; + long int i, j; + long int *added; + igraph_vector_t neis; if (order < 0) { IGRAPH_ERRORF("Negative order in neighborhood size: %" IGRAPH_PRId ".", @@ -89,41 +89,42 @@ igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int IGRAPH_EINVAL, order, mindist); } - added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); + 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_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_vector_int_resize(res, IGRAPH_VIT_SIZE(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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_integer_t size = mindist == 0 ? 1 : 0; + long int node = IGRAPH_VIT_GET(vit); + long int size = mindist == 0 ? 1 : 0; added[node] = i + 1; - igraph_dqueue_int_clear(&q); + igraph_dqueue_clear(&q); if (order > 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); } - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - igraph_integer_t n; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { size++; } @@ -132,7 +133,7 @@ igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int } else { /* we just count them, but don't add them */ for (j = 0; j < n; j++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; if (actdist + 1 >= mindist) { @@ -147,9 +148,9 @@ igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int VECTOR(*res)[i] = size; } /* for VIT, i */ - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(4); @@ -171,8 +172,10 @@ igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int * neighborhood of the specified vertices. * * \param graph The input graph. - * \param res An initialized list of integer vectors. The result of the - * calculation will be stored here. The list will be resized as needed. + * \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 @@ -196,17 +199,18 @@ igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int * the calculation is performed, d is the average degree, o is the * order. */ -igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; igraph_vit_t vit; - igraph_integer_t i, j; - igraph_integer_t *added; - igraph_vector_int_t neis; - igraph_vector_int_t tmp; + 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); @@ -217,58 +221,58 @@ igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list IGRAPH_EINVAL); } - added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); + 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_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); - igraph_vector_int_list_clear(res); + 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); + long int node = IGRAPH_VIT_GET(vit); added[node] = i + 1; - igraph_vector_int_clear(&tmp); + igraph_vector_clear(&tmp); if (mindist == 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); } if (order > 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); } - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - igraph_integer_t n; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + 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++) { - igraph_integer_t nei = VECTOR(neis)[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_int_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); } } } @@ -276,13 +280,20 @@ igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list } /* while q not empty */ - IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); + 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_int_destroy(&tmp); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(5); @@ -308,9 +319,10 @@ igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list * The first version of this function was written by * Vincent Matossian, thanks Vincent. * \param graph The input graph. - * \param res Pointer to a list of graphs, the result will be stored - * here. Each item in the list is an \c igraph_t object. The list will be - * resized as needed. + * \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 @@ -334,18 +346,18 @@ igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list * which the calculation is performed, |V| and |E| are the number of * vertices and edges in the original input graph. */ -igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_t *res, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_dqueue_int_t q; + long int no_of_nodes = igraph_vcount(graph); + igraph_dqueue_t q; igraph_vit_t vit; - igraph_integer_t i, j; - igraph_integer_t *added; - igraph_vector_int_t neis; - igraph_vector_int_t tmp; - igraph_t newg; + 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); @@ -356,58 +368,58 @@ igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_li IGRAPH_EINVAL); } - added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size"); + 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_INT_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); - - igraph_graph_list_clear(res); + 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++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); + long int node = IGRAPH_VIT_GET(vit); added[node] = i + 1; - igraph_vector_int_clear(&tmp); + igraph_vector_clear(&tmp); if (mindist == 0) { - IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); + IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); } if (order > 0) { - IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + igraph_dqueue_push(&q, node); + igraph_dqueue_push(&q, 0); } - while (!igraph_dqueue_int_empty(&q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - igraph_integer_t actdist = igraph_dqueue_int_pop(&q); - igraph_integer_t n; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); - n = igraph_vector_int_size(&neis); + 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++) { - igraph_integer_t nei = VECTOR(neis)[j]; + long int nei = (long int) VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; - IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); - IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); + 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++) { - igraph_integer_t nei = VECTOR(neis)[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_int_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); } } } @@ -415,23 +427,26 @@ igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_li } /* while q not empty */ - if (igraph_vector_int_size(&tmp) < no_of_nodes) { - IGRAPH_CHECK(igraph_induced_subgraph(graph, &newg, + 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)); + IGRAPH_CHECK(igraph_copy(newg, graph)); } - - IGRAPH_FINALLY(igraph_destroy, &newg); - IGRAPH_CHECK(igraph_graph_list_push_back(res, &newg)); - IGRAPH_FINALLY_CLEAN(1); /* ownership of `newg' taken by `res' */ + VECTOR(*res)[i] = newg; + IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&tmp); - igraph_vector_int_destroy(&neis); + igraph_vector_destroy(&tmp); + igraph_vector_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_int_destroy(&q); + igraph_dqueue_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(5); diff --git a/src/vendor/cigraph/src/properties/perfect.c b/src/vendor/cigraph/src/properties/perfect.c deleted file mode 100644 index de1b42623c8..00000000000 --- a/src/vendor/cigraph/src/properties/perfect.c +++ /dev/null @@ -1,191 +0,0 @@ -/* -*- 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, see . -*/ - -#include "igraph_structural.h" - -#include "igraph_bipartite.h" -#include "igraph_constructors.h" -#include "igraph_interface.h" -#include "igraph_operators.h" -#include "igraph_topology.h" - -#include "core/interruption.h" - -/** - * \function igraph_is_perfect - * \brief Checks if the graph is perfect. - * - * A perfect graph is an undirected graph in which the chromatic number of every induced - * subgraph equals the order of the largest clique of that subgraph. - * The chromatic number of a graph G is the smallest number of colors needed to - * color the vertices of G so that no two adjacent vertices share the same color. - * - * - * Warning: This function may create the complement of the graph internally, - * which consumes a lot of memory. For moderately sized graphs, consider - * decomposing them into biconnected components and running the check separately - * on each component. - * - * - * This implementation is based on the strong perfect graph theorem which was - * conjectured by Claude Berge and proved by Maria Chudnovsky, Neil Robertson, - * Paul Seymour, and Robin Thomas. - * - * \param graph The input graph. It is expected to be undirected and simple. - * \param perfect Pointer to an integer, the result will be stored here. - * \return Error code. - * - * Time complexity: worst case exponenital, often faster in practice. - */ -igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect) { - - igraph_bool_t is_bipartite, is_chordal, iso, is_simple; - igraph_real_t girth, comp_girth; - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t start; - igraph_integer_t cycle_len; - igraph_t comp_graph, cycle; - - // If the graph is directed return error. - if (igraph_is_directed(graph)) { - IGRAPH_ERROR("The concept of perfect graphs is only defined for undirected graphs.", IGRAPH_EINVAL); - } - - // If the graph isn't simple then return an error. - IGRAPH_CHECK(igraph_is_simple(graph, &is_simple)); - if (!is_simple) { - IGRAPH_ERROR("Perfect graph testing is implemented for simple graphs only. Simplify the graph.", IGRAPH_EINVAL); - } - - // All graphs with less than 5 vertices are perfect. - if (no_of_nodes < 5) { - *perfect = true; - return IGRAPH_SUCCESS; - } - - // Graphs with less than 5 edges or a complement with less than 5 edges - // are also perfect. The following check handles most 5-vertex graphs, - // but its usefulness quickly diminishes, with only 0.3% of unlabelled - // 8-vertex graphs handled. - // In order to avoid bad results due to integer overflow with large graphs, - // we limit this check for small graphs only. - if ( no_of_nodes < 10000 && - (no_of_edges < 5 || no_of_edges > (no_of_nodes - 1) * no_of_nodes / 2 - 5)) { - *perfect = true; - return IGRAPH_SUCCESS; - } - - // Chordal and bipartite graph types are perfect. - // Possibly more optimizations found here: http://www.or.uni-bonn.de/~hougardy/paper/ClassesOfPerfectGraphs.pdf - IGRAPH_CHECK(igraph_is_bipartite(graph, &is_bipartite, NULL)); - if (is_bipartite) { - *perfect = true; - return IGRAPH_SUCCESS; - } - - IGRAPH_CHECK(igraph_is_chordal(graph, NULL, NULL, &is_chordal, NULL, NULL)); - if (is_chordal) { - *perfect = true; - return IGRAPH_SUCCESS; - } - - // The weak perfect graph theorem: - // A graph is perfect iff its complement is perfect. - IGRAPH_CHECK(igraph_complementer(&comp_graph, graph, 0)); - IGRAPH_FINALLY(igraph_destroy, &comp_graph); - - IGRAPH_CHECK(igraph_is_bipartite(&comp_graph, &is_bipartite, NULL)); - if (is_bipartite) { - *perfect = true; - goto clean1; - } - - IGRAPH_CHECK(igraph_is_chordal(&comp_graph, NULL, NULL, &is_chordal, NULL, NULL)); - if (is_chordal) { - *perfect = true; - goto clean1; - } - - // Since igraph_is_bipartite also catches trees, at this point the girth - // of the graph and its complementer (to be stored in girth and comp_girth) - // are both guaranteed to be finite. - - // If the girth (or the smallest circle in the graph) is bigger than 3 and have odd number of vertices then - // the graph isn't perfect. - IGRAPH_CHECK(igraph_girth(graph, &girth, NULL)); - if ((girth > 3) && (((igraph_integer_t)girth) % 2 == 1)) { - *perfect = false; - goto clean1; - } - - IGRAPH_CHECK(igraph_girth(&comp_graph, &comp_girth, NULL)); - if ((comp_girth > 3) && (((igraph_integer_t)comp_girth) % 2 == 1)) { - *perfect = false; - goto clean1; - } - - // At this point girth and comp_girth are both at least 3. - - // Strong perfect graph theorem: - // A graph is perfect iff neither it or its complement contains an induced odd cycle of length >= 5 - // (i.e. an odd hole). TODO: Find a more efficient way to check for odd holes. - start = (igraph_integer_t) (girth < comp_girth ? girth : comp_girth); - start = start % 2 == 0 ? start + 1 : start + 2; - for (cycle_len = start; cycle_len <= no_of_nodes ; cycle_len += 2) { - - IGRAPH_ALLOW_INTERRUPTION(); - - IGRAPH_CHECK(igraph_ring(&cycle, cycle_len, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 1)); - IGRAPH_FINALLY(igraph_destroy, &cycle); - - if (cycle_len > girth) { - IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); - if (iso) { - *perfect = false; - goto clean2; - } - } - - if (cycle_len > comp_girth) { - IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, &comp_graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); - if (iso) { - *perfect = false; - goto clean2; - } - } - - igraph_destroy(&cycle); - IGRAPH_FINALLY_CLEAN(1); - } - - *perfect = true; - -clean1: - /* normal exit route */ - igraph_destroy(&comp_graph); - IGRAPH_FINALLY_CLEAN(1); - return IGRAPH_SUCCESS; - -clean2: - /* exit route if we also have a cycle to destroy */ - igraph_destroy(&cycle); - IGRAPH_FINALLY_CLEAN(1); - goto clean1; -} diff --git a/src/vendor/cigraph/src/properties/properties_internal.h b/src/vendor/cigraph/src/properties/properties_internal.h index b12b1b43341..f3daab84c08 100644 --- a/src/vendor/cigraph/src/properties/properties_internal.h +++ b/src/vendor/cigraph/src/properties/properties_internal.h @@ -21,14 +21,11 @@ #define IGRAPH_PROPERTIES_INTERNAL_H #include "igraph_adjlist.h" -#include "igraph_decls.h" +#include "igraph_constants.h" #include "igraph_iterators.h" +#include "igraph_types.h" -__BEGIN_DECLS - -igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, - const igraph_vector_int_t *rank); - -__END_DECLS +int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank); #endif diff --git a/src/vendor/cigraph/src/properties/spectral.c b/src/vendor/cigraph/src/properties/spectral.c index e362cef7454..8bd78b32656 100644 --- a/src/vendor/cigraph/src/properties/spectral.c +++ b/src/vendor/cigraph/src/properties/spectral.c @@ -25,407 +25,412 @@ #include "igraph_structural.h" #include "igraph_interface.h" -#include "math/safe_intop.h" - #include -static igraph_error_t igraph_i_laplacian_validate_weights( - const igraph_t* graph, const igraph_vector_t* weights -) { - igraph_integer_t no_of_edges; - - if (weights == NULL) { - return IGRAPH_SUCCESS; - } +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) { - no_of_edges = igraph_ecount(graph); + 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 weight vector length.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid edge 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 non-negative.", IGRAPH_EINVAL); - } else if (isnan(minweight)) { - IGRAPH_ERROR("Weight vector must not contain NaN values.", 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); } - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_get_laplacian - * \brief Returns the Laplacian matrix of a graph. - * - * The Laplacian matrix \c L of a graph is defined as - * L_ij = - A_ij when i != j and - * L_ii = d_i - A_ii. Here \c A denotes the (possibly weighted) - * adjacency matrix and d_i is the degree (or strength, if weighted) - * of vertex \c i. In directed graphs, the \p mode parameter controls whether to use - * out- or in-degrees. Correspondingly, the rows or columns will sum to zero. - * In undirected graphs, A_ii is taken to be \em twice the number - * (or total weight) of self-loops, ensuring that d_i = \sum_j A_ij. - * Thus, the Laplacian of an undirected graph is the same as the Laplacian - * of a directed one obtained by replacing each undirected edge with two reciprocal - * directed ones. - * - * - * More compactly, L = D - A where the \c D is a diagonal matrix - * containing the degrees. The Laplacian matrix can also be normalized, with several - * conventional normalization methods. See \ref igraph_laplacian_normalization_t for - * the methods available in igraph. - * - * - * 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. - * \param mode Controls whether to use out- or in-degrees in directed graphs. - * If set to \c IGRAPH_ALL, edge directions will be ignored. - * \param normalization The normalization method to use when calculating the - * Laplacian matrix. See \ref igraph_laplacian_normalization_t for - * possible values. - * \param weights An optional vector containing non-negative 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|^2), |V| is the number of vertices in the graph. - * - * \example examples/simple/igraph_get_laplacian.c - */ - -igraph_error_t igraph_get_laplacian( - const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, - igraph_laplacian_normalization_t normalization, - const igraph_vector_t *weights -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_bool_t directed = igraph_is_directed(graph); - igraph_vector_t degree; - igraph_integer_t i; - - IGRAPH_ASSERT(res != NULL); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); - IGRAPH_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); - igraph_matrix_null(res); + if (directed) { - IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); + 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); + } - /* Value of 'mode' is validated in igraph_strength() call above. */ - if (! directed) { - mode = IGRAPH_ALL; - } else if (mode == IGRAPH_ALL) { - directed = 0; - } + /* 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])); + } + } - for (i = 0; i < no_of_nodes; i++) { - switch (normalization) { - case IGRAPH_LAPLACIAN_UNNORMALIZED: - MATRIX(*res, i, i) = VECTOR(degree)[i]; - break; + } 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); + } - case IGRAPH_LAPLACIAN_SYMMETRIC: - if (VECTOR(degree)[i] > 0) { - MATRIX(*res, i, i) = 1; - VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); + 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)); + } } - break; - case IGRAPH_LAPLACIAN_LEFT: - case IGRAPH_LAPLACIAN_RIGHT: - if (VECTOR(degree)[i] > 0) { - MATRIX(*res, i, i) = 1; - VECTOR(degree)[i] = 1.0 / 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) { + 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); } - break; - default: - IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); } - } - for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; - igraph_real_t norm; - - switch (normalization) { - case IGRAPH_LAPLACIAN_UNNORMALIZED: - MATRIX(*res, from, to) -= weight; - if (!directed) { - MATRIX(*res, to, from) -= weight; - } - break; - - case IGRAPH_LAPLACIAN_SYMMETRIC: - norm = VECTOR(degree)[from] * VECTOR(degree)[to]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero %s-%s, " - "cannot perform symmetric normalization of Laplacian with '%s' mode.", - IGRAPH_EINVAL, - mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); - } - weight *= norm; - MATRIX(*res, from, to) -= weight; - if (!directed) { - MATRIX(*res, to, from) -= weight; + } 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); } - break; - - case IGRAPH_LAPLACIAN_LEFT: - norm = VECTOR(degree)[from]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero in-%s, " - "cannot perform left stochastic normalization of Laplacian with 'in' mode.", - IGRAPH_EINVAL, - weights ? "strength" : "degree"); + + /* 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])); + } } - MATRIX(*res, from, to) -= weight * norm; - if (!directed) { - /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ - MATRIX(*res, to, from) -= weight * VECTOR(degree)[to]; + + } 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); } - break; - - case IGRAPH_LAPLACIAN_RIGHT: - norm = VECTOR(degree)[to]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero out-%s, " - "cannot perform right stochastic normalization of Laplacian with 'out' mode.", - IGRAPH_EINVAL, - weights ? "strength" : "degree"); + + 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]); } - MATRIX(*res, from, to) -= weight * norm; - if (!directed) { - /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ - MATRIX(*res, to, from) -= weight * VECTOR(degree)[from]; + + 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); } - break; + } + } igraph_vector_destroy(°ree); - IGRAPH_FINALLY_CLEAN(1); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); - return IGRAPH_SUCCESS; + return 0; } - /** - * \function igraph_get_laplacian_sparse - * \brief Returns the Laplacian of a graph in a sparse matrix format. + * \function igraph_laplacian + * \brief Returns the Laplacian matrix of a graph * - * See \ref igraph_get_laplacian() for the definition of the Laplacian matrix. + * + * 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 first version of this function was written by Vincent Matossian. + * 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. - * \param mode Controls whether to use out- or in-degrees in directed graphs. - * If set to \c IGRAPH_ALL, edge directions will be ignored. - * \param normalization The normalization method to use when calculating the - * Laplacian matrix. See \ref igraph_laplacian_normalization_t for - * possible values. - * \param weights An optional vector containing non-negative edge weights, - * to calculate the weighted Laplacian matrix. Set it to a null pointer to + * 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(|E|), |E| is the number of edges in the graph. + * Time complexity: O(|V||V|), + * |V| is the + * number of vertices in the graph. * - * \example examples/simple/igraph_get_laplacian_sparse.c + * \example examples/simple/igraph_laplacian.c */ -igraph_error_t igraph_get_laplacian_sparse( - const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, - igraph_laplacian_normalization_t normalization, - const igraph_vector_t *weights -) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); +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; - igraph_integer_t i; - igraph_integer_t nz; + int i; - if (directed) { - IGRAPH_SAFE_ADD(no_of_edges, no_of_nodes, &nz); - } else { - IGRAPH_SAFE_ADD(no_of_edges * 2, no_of_nodes, &nz); + if (!res && !sparseres) { + IGRAPH_ERROR("Laplacian: give at least one of `res' or `sparseres'", + IGRAPH_EINVAL); } - IGRAPH_ASSERT(sparseres != NULL); - - IGRAPH_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); + if (weights) { + return igraph_i_weighted_laplacian(graph, res, sparseres, normalized, + weights); + } - IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz)); + 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_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); - - for (i = 0; i < no_of_nodes; i++) { - switch (normalization) { - case IGRAPH_LAPLACIAN_UNNORMALIZED: - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, VECTOR(degree)[i])); - break; - - case IGRAPH_LAPLACIAN_SYMMETRIC: - if (VECTOR(degree)[i] > 0) { - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); - VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); - } - break; - case IGRAPH_LAPLACIAN_LEFT: - case IGRAPH_LAPLACIAN_RIGHT: - if (VECTOR(degree)[i] > 0) { - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); - VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; - } - break; + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_OUT, IGRAPH_NO_LOOPS)); - default: - IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); - } - } - - for (i = 0; i < no_of_edges; i++) { - igraph_integer_t from = IGRAPH_FROM(graph, i); - igraph_integer_t to = IGRAPH_TO(graph, i); - igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; - igraph_real_t norm; - - switch (normalization) { - case IGRAPH_LAPLACIAN_UNNORMALIZED: - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); - if (!directed) { - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); - } - break; - - case IGRAPH_LAPLACIAN_SYMMETRIC: - norm = VECTOR(degree)[from] * VECTOR(degree)[to]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero %s-%s, " - "cannot perform symmetric normalization of Laplacian with '%s' mode.", - IGRAPH_EINVAL, - mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); - } - weight *= norm; - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); - if (!directed) { - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); - } - break; - - case IGRAPH_LAPLACIAN_LEFT: - norm = VECTOR(degree)[from]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero in-%s, " - "cannot perform left stochastic normalization of Laplacian with 'in' mode.", - IGRAPH_EINVAL, - weights ? "strength" : "degree"); + 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])); + } } - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); - if (!directed) { - /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[to])); + 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); } - break; - - case IGRAPH_LAPLACIAN_RIGHT: - norm = VECTOR(degree)[to]; - if (norm == 0 && weight != 0) { - IGRAPH_ERRORF( - "Found non-isolated vertex with zero out-%s, " - "cannot perform right stochastic normalization of Laplacian with 'out' mode.", - IGRAPH_EINVAL, - weights ? "strength" : "degree"); + } 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]; + } } - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); - if (!directed) { - /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ - IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[from])); + + 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); } - break; } - } - igraph_vector_destroy(°ree); - IGRAPH_FINALLY_CLEAN(1); - - return IGRAPH_SUCCESS; -} - -/** - * \function igraph_laplacian - * \brief Returns the Laplacian matrix of a graph (deprecated). - * - * This function produces the Laplacian matrix of a graph in either dense or - * sparse format. When \p normalized is set to true, the type of normalization - * used depends on the directnedness of the graph: symmetric normalization - * is used for undirected graphs and left stochastic normalization for - * directed graphs. - * - * \param graph Pointer to the graph to convert. - * \param res Pointer to an initialized matrix object or \c NULL. The dense matrix - * result will be stored here. - * \param sparseres Pointer to an initialized sparse matrix object or \c NULL. - * The sparse matrix result will be stored here. - * \param mode Controls whether to use out- or in-degrees in directed graphs. - * If set to \c IGRAPH_ALL, edge directions will be ignored. - * \param normalized Boolean, whether to normalize the result. - * \param weights An optional vector containing non-negative edge weights, - * to calculate the weighted Laplacian matrix. Set it to a null pointer to - * calculate the unweighted Laplacian. - * \return Error code. - * - * \deprecated-by igraph_get_laplacian 0.10.0 - */ - -igraph_error_t igraph_laplacian( - const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, - igraph_bool_t normalized, const igraph_vector_t *weights -) { - igraph_laplacian_normalization_t norm_method = IGRAPH_LAPLACIAN_UNNORMALIZED; + } else { - if (!res && !sparseres) { - IGRAPH_ERROR("Laplacian: specify at least one of 'res' or 'sparseres'", - IGRAPH_EINVAL); - } + 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])); + } + } - if (normalized) { - if (igraph_is_directed(graph)) { - norm_method = IGRAPH_LAPLACIAN_LEFT; + 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 { - norm_method = IGRAPH_LAPLACIAN_SYMMETRIC; - } - } + 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]); + } - if (res) { - IGRAPH_CHECK(igraph_get_laplacian(graph, res, IGRAPH_OUT, norm_method, weights)); - } + 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); + } + } - if (sparseres) { - IGRAPH_CHECK(igraph_get_laplacian_sparse(graph, sparseres, IGRAPH_OUT, norm_method, weights)); } - return IGRAPH_SUCCESS; + igraph_vector_destroy(°ree); + igraph_eit_destroy(&edgeit); + IGRAPH_FINALLY_CLEAN(2); + return 0; } diff --git a/src/vendor/cigraph/src/properties/trees.c b/src/vendor/cigraph/src/properties/trees.c index 0f08d1424cd..e2bc8ea61ee 100644 --- a/src/vendor/cigraph/src/properties/trees.c +++ b/src/vendor/cigraph/src/properties/trees.c @@ -22,8 +22,8 @@ */ #include "igraph_structural.h" -#include "igraph_topology.h" +#include "igraph_adjlist.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "igraph_interface.h" @@ -31,12 +31,11 @@ /** * \function igraph_unfold_tree - * \brief Unfolding a graph into a tree, by possibly multiplicating its vertices. + * 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 @@ -55,116 +54,116 @@ * Time complexity: O(n+m), linear in the number vertices and edges. * */ -igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, - igraph_neimode_t mode, const igraph_vector_int_t *roots, - igraph_vector_int_t *vertex_index) { +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); - igraph_integer_t no_of_roots = igraph_vector_int_size(roots); - igraph_integer_t tree_vertex_count = no_of_nodes; + 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_int_t edges; + igraph_vector_t edges; igraph_vector_bool_t seen_vertices; igraph_vector_bool_t seen_edges; - igraph_dqueue_int_t Q; - igraph_vector_int_t neis; + igraph_dqueue_t Q; + igraph_vector_t neis; - igraph_integer_t v_ptr = no_of_nodes; + long int i, n, r, v_ptr = no_of_nodes; - if (! igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { - IGRAPH_ERROR("All roots should be vertices of the graph.", IGRAPH_EINVVID); - } + /* TODO: handle not-connected graphs, multiple root vertices */ - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); - IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + 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_int_range(vertex_index, 0, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(vertex_index, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*vertex_index)[i] = i; + } } - for (igraph_integer_t r = 0; r < no_of_roots; r++) { + for (r = 0; r < no_of_roots; r++) { - igraph_integer_t root = VECTOR(*roots)[r]; - VECTOR(seen_vertices)[root] = true; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, root)); + long int root = (long int) VECTOR(*roots)[r]; + VECTOR(seen_vertices)[root] = 1; + igraph_dqueue_push(&Q, root); - while (!igraph_dqueue_int_empty(&Q)) { - igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + while (!igraph_dqueue_empty(&Q)) { + long int actnode = (long int) igraph_dqueue_pop(&Q); - IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); + IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) actnode, mode)); + n = igraph_vector_size(&neis); + for (i = 0; i < n; i++) { - igraph_integer_t n = igraph_vector_int_size(&neis); - for (igraph_integer_t i = 0; i < n; i++) { - - igraph_integer_t edge = VECTOR(neis)[i]; - igraph_integer_t from = IGRAPH_FROM(graph, edge); - igraph_integer_t to = IGRAPH_TO(graph, edge); - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, actnode); + 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] = true; + VECTOR(seen_edges)[edge] = 1; if (! VECTOR(seen_vertices)[nei]) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + igraph_vector_push_back(&edges, from); + igraph_vector_push_back(&edges, to); - VECTOR(seen_vertices)[nei] = true; - IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + VECTOR(seen_vertices)[nei] = 1; + IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); } else { tree_vertex_count++; if (vertex_index) { - IGRAPH_CHECK(igraph_vector_int_push_back(vertex_index, nei)); + IGRAPH_CHECK(igraph_vector_push_back(vertex_index, nei)); } if (from == nei) { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + igraph_vector_push_back(&edges, v_ptr++); + igraph_vector_push_back(&edges, to); } else { - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); + 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. @@ -242,13 +241,11 @@ static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_int * Time complexity: At most O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * - * \sa \ref igraph_is_connected() + * \sa igraph_is_weakly_connected() * - * \example examples/simple/igraph_kary_tree.c + * \example examples/simple/igraph_tree.c */ -igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) { - igraph_bool_t is_tree = false; - igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; +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; @@ -256,34 +253,20 @@ igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_ vcount = igraph_vcount(graph); ecount = igraph_ecount(graph); - /* For undirected graphs and for directed graphs with mode == IGRAPH_ALL, - * we can return early if we know from the cache that the graph is weakly - * connected and is a forest. We can do this even if the user wants the - * root vertex because we always return zero as the root vertex for - * undirected graphs */ - if (treat_as_undirected && - igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && - igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && - igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST) && - igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) - ) { - is_tree = true; - iroot = 0; - goto success; - } - /* A tree must have precisely vcount-1 edges. */ /* By convention, the zero-vertex graph will not be considered a tree. */ if (ecount != vcount - 1) { - is_tree = false; - goto success; + *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) { - is_tree = true; - iroot = 0; - goto success; + *res = 1; + if (root) { + *root = 0; + } + return IGRAPH_SUCCESS; } /* For higher vertex counts we cannot short-circuit due to the possibility @@ -304,7 +287,7 @@ igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_ * we choose 0. */ - is_tree = true; /* assume success */ + *res = 1; /* assume success */ switch (mode) { case IGRAPH_ALL: @@ -313,11 +296,11 @@ igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_ case IGRAPH_IN: case IGRAPH_OUT: { - igraph_vector_int_t degree; + igraph_vector_t degree; igraph_integer_t i; - IGRAPH_CHECK(igraph_vector_int_init(°ree, 0)); - IGRAPH_FINALLY(igraph_vector_int_destroy, °ree); + 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)); @@ -333,369 +316,36 @@ igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_ * improve performance when the graph is indeed a tree, persumably * the most common case. Thus we only check until finding the root. */ - is_tree = false; + *res = 0; break; } } /* If no suitable root is found, the graph is not a tree. */ - if (is_tree && i == vcount) { - is_tree = false; + if (*res && i == vcount) { + *res = 0; } else { iroot = i; } - igraph_vector_int_destroy(°ree); + igraph_vector_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } break; default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); + IGRAPH_ERROR("Invalid mode,", IGRAPH_EINVMODE); } /* if no suitable root was found, skip visiting vertices */ - if (is_tree) { + if (*res) { IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); - is_tree = visited_count == vcount; - } - -success: - if (res) { - *res = is_tree; + *res = visited_count == vcount; } if (root) { *root = iroot; } - if (is_tree && treat_as_undirected) { - /* For undirected graphs (or directed graphs that are treated as - * undirected in this calculation), a tree is weakly connected and is - * a forest, so we can cache this */ - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, 1); - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, 1); - } - - return IGRAPH_SUCCESS; -} - -/* igraph_is_forest() -- check if a graph is a forest */ - -/* Verify that the graph has no cycles and count the number of reachable vertices. - * This function performs a DFS starting from 'root'. - * If it finds a cycle, it sets *res to false, otherwise it does not change it. - * *visited_count will be incremented by the number of vertices reachable from 'root', - * including 'root' itself. - */ -static igraph_error_t igraph_i_is_forest_visitor( - const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, - igraph_vector_bool_t *visited, igraph_stack_int_t *stack, igraph_vector_int_t *neis, - igraph_integer_t *visited_count, igraph_bool_t *res) -{ - igraph_integer_t i; - - igraph_stack_int_clear(stack); - - /* push the root onto the stack */ - IGRAPH_CHECK(igraph_stack_int_push(stack, root)); - - while (! igraph_stack_int_empty(stack)) { - igraph_integer_t u; - igraph_integer_t ncount; - - /* Take a vertex from stack and check if it is already visited. - * If yes, then we found a cycle: the graph is not a forest. - * Otherwise mark it as visited and continue. - */ - u = igraph_stack_int_pop(stack); - if (IGRAPH_LIKELY(! VECTOR(*visited)[u])) { - VECTOR(*visited)[u] = true; - *visited_count += 1; - } - else { - *res = 0; - break; - } - - /* Vertex discovery: Register all its neighbours for future processing */ - IGRAPH_CHECK(igraph_neighbors(graph, neis, u, mode)); - ncount = igraph_vector_int_size(neis); - - for (i = 0; i < ncount; ++i) { - igraph_integer_t v = VECTOR(*neis)[i]; - - if (mode == IGRAPH_ALL) { - /* In the undirected case, we avoid returning to the predecessor - * vertex of 'v' in the DFS tree by skipping visited vertices. - * - * Note that in order to succcessfully detect a cycle, a vertex - * within that cycle must end up on the stack more than once. - * Does skipping visited vertices preclude this sometimes? - * No, because any visited vertex can only be accessed through - * an already discovered vertex (i.e. one that has already been - * pushed onto the stack). - */ - if (IGRAPH_LIKELY(! VECTOR(*visited)[v])) { - IGRAPH_CHECK(igraph_stack_int_push(stack, v)); - } - /* To check for a self-loop in undirected graph */ - else if (v == u) { - *res = 0; - break; - } - } - else { - IGRAPH_CHECK(igraph_stack_int_push(stack, v)); - } - } - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_i_is_forest( - const igraph_t *graph, igraph_bool_t *res, - igraph_vector_int_t *roots, igraph_neimode_t mode -); - -/** - * \ingroup structural - * \function igraph_is_forest - * \brief Decides whether the graph is a forest. - * - * An undirected graph is a forest if it has no cycles. - * - * - * In the directed case, a possible additional requirement is that edges in each - * tree are oriented away from the root (out-trees or arborescences) or all edges - * are oriented towards the root (in-trees or anti-arborescences). - * This test can be controlled using the \p mode parameter. - * - * - * By convention, the null graph (i.e. the graph with no vertices) is considered to be a forest. - * - * - * The \p res return value of this function is cached in the graph itself if - * \p mode is set to \c IGRAPH_ALL or if the graph is undirected. Calling the - * function multiple times with no modifications to the graph in between - * will return a cached value in O(1) time if the roots are not asked for. - * - * \param graph The graph object to analyze. - * \param res Pointer to a logical variable. If not \c NULL, then the result will be stored - * here. - * \param roots If not \c NULL, the root nodes will be stored here. When \p mode - * is \c IGRAPH_ALL or the graph is undirected, any one vertex from each - * component can be the root. When \p mode is \c IGRAPH_OUT - * or \c IGRAPH_IN, all the vertices with zero in- or out-degree, - * respectively are considered as root nodes. - * \param mode For a directed graph this specifies whether to test for an - * out-forest, an in-forest 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_EINVMODE: invalid mode argument. - * - * Time complexity: At most O(|V|+|E|), the - * number of vertices plus the number of edges in the graph. - */ -igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, - igraph_vector_int_t *roots, igraph_neimode_t mode) { - /* Caching is enabled only if the graph is undirected or mode == IGRAPH_ALL. - * Also, we can't return early if we need to calculate the roots */ - igraph_bool_t use_cache = ( - !igraph_is_directed(graph) || mode == IGRAPH_ALL - ); - - if (!roots && !res) { - return IGRAPH_SUCCESS; - } - - if (use_cache && !roots && res) { - IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_IS_FOREST, res); - } - - IGRAPH_CHECK(igraph_i_is_forest(graph, res, roots, mode)); - - /* At this point we know whether the graph is a forest if we have at least - * one of 'res' or 'roots' */ - if (res) { - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, *res); - } else if (roots) { - igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, !igraph_vector_int_empty(roots)); - } - return IGRAPH_SUCCESS; } - -static igraph_error_t igraph_i_is_forest( - const igraph_t *graph, igraph_bool_t *res, - igraph_vector_int_t *roots, igraph_neimode_t mode -) { - igraph_vector_bool_t visited; - igraph_vector_int_t neis; - igraph_stack_int_t stack; - igraph_integer_t visited_count = 0; - igraph_integer_t vcount, ecount; - igraph_integer_t v; - igraph_bool_t result; - - vcount = igraph_vcount(graph); - ecount = igraph_ecount(graph); - - if (roots) { - igraph_vector_int_clear(roots); - } - - /* Any graph with 0 edges is a forest. */ - if (ecount == 0) { - if (res) { - *res = true; - } - if (roots) { - for (v = 0; v < vcount; v++) { - IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); - } - } - return IGRAPH_SUCCESS; - } - - /* A forest can have at most vcount-1 edges. */ - if (ecount > vcount - 1) { - if (res) { - *res = false; - } - return IGRAPH_SUCCESS; - } - - /* Ignore mode for undirected graphs. */ - if (! igraph_is_directed(graph)) { - mode = IGRAPH_ALL; - } - - result = true; /* assume success */ - - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, vcount); - - IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); - - IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - - /* The main algorithm: - * - * Undirected Graph:- We add each unvisited vertex to the roots vector, and - * mark all other vertices that are reachable from it as visited. - * - * Directed Graph:- For each tree, the root is the node with no - * incoming/outgoing connections, depending on 'mode'. We add each vertex - * with zero degree to the roots vector and mark all other vertices that are - * reachable from it as visited. - * - * If all the vertices are visited exactly once, then the graph is a forest. - */ - - switch (mode) { - case IGRAPH_ALL: - { - for (v = 0; v < vcount; ++v) { - if (!result) { - break; - } - if (! VECTOR(visited)[v]) { - if (roots) { - IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); - } - IGRAPH_CHECK(igraph_i_is_forest_visitor( - graph, v, mode, - &visited, &stack, &neis, - &visited_count, &result)); - } - } - break; - } - - case IGRAPH_IN: - case IGRAPH_OUT: - { - igraph_vector_int_t degree; - - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); - IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), - IGRAPH_REVERSE_MODE(mode), /* loops = */ 1)); - - for (v = 0; v < vcount; ++v) { - /* In an out-tree, roots have in-degree 0, - * and all other vertices have in-degree 1. */ - if (VECTOR(degree)[v] > 1 || !result) { - result = false; - break; - } - if (VECTOR(degree)[v] == 0) { - if (roots) { - IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); - } - IGRAPH_CHECK(igraph_i_is_forest_visitor( - graph, v, mode, - &visited, &stack, &neis, - &visited_count, &result)); - } - } - - igraph_vector_int_destroy(°ree); - IGRAPH_FINALLY_CLEAN(1); - break; - } - - default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); - } - - if (result) { - /* In a forest, all vertices are reachable from the roots. */ - result = (visited_count == vcount); - } - - if (res) { - *res = result; - } - - /* If the graph is not a forest then the root vector will be empty. */ - if (!result && roots) { - igraph_vector_int_clear(roots); - } - - igraph_vector_int_destroy(&neis); - igraph_stack_int_destroy(&stack); - igraph_vector_bool_destroy(&visited); - IGRAPH_FINALLY_CLEAN(3); - - return IGRAPH_SUCCESS; -} - -/** - * \ingroup structural - * \function igraph_is_acyclic - * \brief Checks whether a graph is acyclic or not. - * - * This function checks whether a graph is acyclic or not. - * - * \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. - */ -igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res) { - if (igraph_is_directed(graph)) { - /* igraph_is_dag is cached */ - return igraph_is_dag(graph, res); - } else { - /* igraph_is_forest is cached if mode == IGRAPH_ALL and we don't need - * the roots */ - return igraph_is_forest(graph, res, NULL, IGRAPH_ALL); - } -} diff --git a/src/vendor/cigraph/src/properties/triangles.c b/src/vendor/cigraph/src/properties/triangles.c index c3069116690..4e787f34602 100644 --- a/src/vendor/cigraph/src/properties/triangles.c +++ b/src/vendor/cigraph/src/properties/triangles.c @@ -77,7 +77,7 @@ * graph and d is the average degree. */ -igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, +int igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { @@ -97,7 +97,7 @@ igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, IGRAPH_CHECK(igraph_transitivity_local_undirected(graph, &vec, igraph_vss_all(), mode)); for (i = 0, nans = 0; i < no_of_nodes; i++) { - if (!isnan(VECTOR(vec)[i])) { + if (!igraph_is_nan(VECTOR(vec)[i])) { sum += VECTOR(vec)[i]; } else { nans++; @@ -113,7 +113,7 @@ igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, return IGRAPH_SUCCESS; } -static igraph_error_t igraph_transitivity_local_undirected1(const igraph_t *graph, +int igraph_transitivity_local_undirected1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { @@ -125,21 +125,19 @@ static igraph_error_t igraph_transitivity_local_undirected1(const igraph_t *grap return IGRAPH_SUCCESS; } -static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *graph, +int igraph_transitivity_local_undirected2(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; - igraph_integer_t nodes_to_calc, affected_nodes; - igraph_integer_t maxdegree = 0; - igraph_integer_t i, j, k, nn; + long int nodes_to_calc, affected_nodes; + long int maxdegree = 0; + long int i, j, k, nn; igraph_lazy_adjlist_t adjlist; - igraph_vector_int_t degree; - igraph_vector_t indexv, avids, rank, triangles; - igraph_vector_int_t order; - igraph_integer_t *neis; + 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); @@ -153,20 +151,18 @@ static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *grap IGRAPH_CHECK(igraph_vector_reserve(&avids, nodes_to_calc)); k = 0; for (i = 0; i < nodes_to_calc; IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t v = IGRAPH_VIT_GET(vit); + long int v = IGRAPH_VIT_GET(vit); igraph_vector_int_t *neis2; - igraph_integer_t neilen; + 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, v); - IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); - + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); neilen = igraph_vector_int_size(neis2); for (j = 0; j < neilen; j++) { - igraph_integer_t nei = VECTOR(*neis2)[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)); @@ -176,66 +172,65 @@ static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *grap /* Degree, ordering, ranking */ affected_nodes = igraph_vector_size(&avids); - IGRAPH_VECTOR_INT_INIT_FINALLY(&order, 0); - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, affected_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&order, 0); + IGRAPH_VECTOR_INIT_FINALLY(°ree, affected_nodes); for (i = 0; i < affected_nodes; i++) { - igraph_integer_t v = VECTOR(avids)[i]; + long int v = (long int) VECTOR(avids)[i]; igraph_vector_int_t *neis2; - igraph_integer_t deg; - neis2 = igraph_lazy_adjlist_get(&adjlist, v); - IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + 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_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree + 1)); - igraph_vector_int_destroy(°ree); + 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)[ VECTOR(order)[i] ] = affected_nodes - i - 1; + VECTOR(rank)[ (long int) VECTOR(order)[i] ] = affected_nodes - i - 1; } - neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + neis = IGRAPH_CALLOC(no_of_nodes, long int); if (neis == 0) { - IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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--) { - igraph_integer_t node = VECTOR(avids) [ VECTOR(order)[nn] ]; + long int node = (long int) VECTOR(avids) [ (long int) VECTOR(order)[nn] ]; igraph_vector_int_t *neis1, *neis2; - igraph_integer_t neilen1, neilen2; - igraph_integer_t nodeindex = VECTOR(indexv)[node]; - igraph_integer_t noderank = VECTOR(rank) [nodeindex - 1]; + long int neilen1, neilen2; + long int nodeindex = (long int) VECTOR(indexv)[node]; + long int noderank = (long int) VECTOR(rank) [nodeindex - 1]; - IGRAPH_ALLOW_INTERRUPTION(); + /* fprintf(stderr, "node %li (indexv %li, rank %li)\n", node, */ + /* (long int)VECTOR(indexv)[node]-1, noderank); */ - neis1 = igraph_lazy_adjlist_get(&adjlist, node); - IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); + 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++) { - igraph_integer_t nei = VECTOR(*neis1)[i]; + long int nei = (long int) VECTOR(*neis1)[i]; neis[nei] = node + 1; } for (i = 0; i < neilen1; i++) { - igraph_integer_t nei = VECTOR(*neis1)[i]; - igraph_integer_t neiindex = VECTOR(indexv)[nei]; - igraph_integer_t neirank = VECTOR(rank)[neiindex - 1]; + 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, nei); - IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) nei); neilen2 = igraph_vector_int_size(neis2); for (j = 0; j < neilen2; j++) { - igraph_integer_t nei2 = VECTOR(*neis2)[j]; - igraph_integer_t nei2index = VECTOR(indexv)[nei2]; - igraph_integer_t nei2rank = VECTOR(rank)[nei2index - 1]; + 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; @@ -256,14 +251,11 @@ static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *grap 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)) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_integer_t idx = VECTOR(indexv)[node] - 1; - igraph_vector_int_t *neis2 = igraph_lazy_adjlist_get(&adjlist, node); - igraph_integer_t deg; - - IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); - - deg = igraph_vector_int_size(neis2); + 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 { @@ -275,35 +267,91 @@ static igraph_error_t igraph_transitivity_local_undirected2(const igraph_t *grap igraph_vector_destroy(&triangles); igraph_free(neis); igraph_vector_destroy(&rank); - igraph_vector_int_destroy(&order); + 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 IGRAPH_SUCCESS; + 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 */ -igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, +int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, const igraph_vector_int_t *rank) { - igraph_integer_t i; - igraph_integer_t n = al->length; + long int i; + long int n = al->length; igraph_vector_int_t mark; - - IGRAPH_CHECK(igraph_vector_int_init(&mark, n)); + 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]; - igraph_integer_t j, l = igraph_vector_int_size(v); - igraph_integer_t irank = VECTOR(*rank)[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 */) { - igraph_integer_t e = VECTOR(*v)[j]; + long int e = (long int) VECTOR(*v)[j]; if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) { VECTOR(mark)[e] = i + 1; j++; @@ -317,11 +365,11 @@ igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); + return 0; - return IGRAPH_SUCCESS; } -static igraph_error_t igraph_transitivity_local_undirected4(const igraph_t *graph, +int igraph_transitivity_local_undirected4(const igraph_t *graph, igraph_vector_t *res, igraph_transitivity_mode_t mode) { @@ -329,12 +377,12 @@ static igraph_error_t igraph_transitivity_local_undirected4(const igraph_t *grap #include "properties/triangles_template.h" #undef TRANSIT - return IGRAPH_SUCCESS; + return 0; } /** * \function igraph_transitivity_local_undirected - * \brief The local transitivity (clustering coefficient) of some vertices. + * \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 @@ -372,7 +420,7 @@ static igraph_error_t igraph_transitivity_local_undirected4(const igraph_t *grap * the transitivity is calculated, d is the average vertex degree. */ -igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, +int igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { @@ -381,7 +429,7 @@ igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, return igraph_transitivity_local_undirected4(graph, res, mode); } else { igraph_vit_t vit; - igraph_integer_t size; + long int size; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); size = IGRAPH_VIT_SIZE(vit); @@ -395,17 +443,17 @@ igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, } } -static igraph_error_t igraph_adjacent_triangles1(const igraph_t *graph, +static int igraph_adjacent_triangles1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { # include "properties/triangles_template1.h" - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t igraph_adjacent_triangles4(const igraph_t *graph, +static int igraph_adjacent_triangles4(const igraph_t *graph, igraph_vector_t *res) { # include "properties/triangles_template.h" - return IGRAPH_SUCCESS; + return 0; } /** @@ -423,7 +471,7 @@ static igraph_error_t igraph_adjacent_triangles4(const igraph_t *graph, * queried vertices, n is their number. */ -igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, +int igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { if (igraph_vs_is_all(&vids)) { @@ -437,16 +485,10 @@ igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, * \function igraph_list_triangles * \brief Find all triangles in a graph. * - * - * The triangles are reported as a long list of vertex ID triplets. Use - * the \c int variant of \ref igraph_matrix_view_from_vector() to create a - * matrix view into the vector where each triangle is stored in a column of the - * matrix (see the example). - * * \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. + * 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. @@ -457,11 +499,9 @@ igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, * * Time complexity: O(d^2 n), d is the average degree, n is the number * of vertices. - * - * \example examples/simple/igraph_list_triangles.c */ -igraph_error_t igraph_list_triangles(const igraph_t *graph, +int igraph_list_triangles(const igraph_t *graph, igraph_vector_int_t *res) { # define TRIANGLES # include "properties/triangles_template.h" @@ -514,74 +554,74 @@ igraph_error_t igraph_list_triangles(const igraph_t *graph, * \example examples/simple/igraph_transitivity.c */ -igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, +int igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_real_t triples = 0, triangles = 0; - igraph_integer_t node, nn; - igraph_integer_t maxdegree; - igraph_integer_t *neis; - igraph_vector_int_t order; + long int node, nn; + long int maxdegree; + long int *neis; + igraph_vector_t order; igraph_vector_t rank; - igraph_vector_int_t degree; + igraph_vector_t degree; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; - igraph_integer_t i, j, neilen1, neilen2; + 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_INT_INIT_FINALLY(&order, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + 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 = igraph_vector_int_max(°ree) + 1; - IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); - igraph_vector_int_destroy(°ree); + 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)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; + 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, igraph_integer_t); + neis = IGRAPH_CALLOC(no_of_nodes, long int); if (! neis) { - IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + 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 = VECTOR(order)[nn]; + node = (long int) VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1 = igraph_adjlist_get(&allneis, node); neilen1 = igraph_vector_int_size(neis1); - triples += (igraph_real_t)neilen1 * (neilen1 - 1); + triples += (double)neilen1 * (neilen1 - 1); /* Mark the neighbors of 'node' */ for (i = 0; i < neilen1; i++) { - igraph_integer_t nei = VECTOR(*neis1)[i]; + long int nei = (long int) VECTOR(*neis1)[i]; neis[nei] = node + 1; } for (i = 0; i < neilen1; i++) { - igraph_integer_t nei = VECTOR(*neis1)[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++) { - igraph_integer_t nei2 = VECTOR(*neis2)[j]; + long int nei2 = (long int) VECTOR(*neis2)[j]; if (neis[nei2] == node + 1) { triangles += 1.0; } @@ -593,7 +633,7 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, IGRAPH_FREE(neis); igraph_adjlist_destroy(&allneis); igraph_vector_destroy(&rank); - igraph_vector_int_destroy(&order); + igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(4); if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) { @@ -602,24 +642,23 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, *res = triangles / triples * 2.0; } - return IGRAPH_SUCCESS; + return 0; } -static igraph_error_t 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) { +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + long int no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; - igraph_integer_t nodes_to_calc; + long int nodes_to_calc; igraph_vector_int_t *adj1, *adj2; - igraph_vector_int_t neis; + igraph_vector_long_t neis; igraph_vector_t actw; igraph_lazy_inclist_t incident; - igraph_integer_t i; + long int i; igraph_vector_t strength; /* Precondition: weight vector is not null, its length equals the number of @@ -631,8 +670,8 @@ static igraph_error_t igraph_i_transitivity_barrat1( IGRAPH_FINALLY(igraph_vit_destroy, &vit); nodes_to_calc = IGRAPH_VIT_SIZE(vit); - IGRAPH_CHECK(igraph_vector_int_init(&neis, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + 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); @@ -646,19 +685,18 @@ static igraph_error_t igraph_i_transitivity_barrat1( IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); - igraph_integer_t adjlen1, adjlen2, j, k; + 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, node); - IGRAPH_CHECK_OOM(adj1, "Failed to query incident edges."); + 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++) { - igraph_integer_t edge = VECTOR(*adj1)[j]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + 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]; } @@ -666,15 +704,14 @@ static igraph_error_t igraph_i_transitivity_barrat1( triangles = 0.0; for (j = 0; j < adjlen1; j++) { - igraph_integer_t edge1 = VECTOR(*adj1)[j]; + long int edge1 = (long int) VECTOR(*adj1)[j]; igraph_real_t weight1 = VECTOR(*weights)[edge1]; - igraph_integer_t v = IGRAPH_OTHER(graph, edge1, node); - adj2 = igraph_lazy_inclist_get(&incident, v); - IGRAPH_CHECK_OOM(adj2, "Failed to query incident edges."); + 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++) { - igraph_integer_t edge2 = VECTOR(*adj2)[k]; - igraph_integer_t v2 = IGRAPH_OTHER(graph, edge2, v); + 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; } @@ -690,58 +727,54 @@ static igraph_error_t igraph_i_transitivity_barrat1( igraph_lazy_inclist_destroy(&incident); igraph_vector_destroy(&strength); igraph_vector_destroy(&actw); - igraph_vector_int_destroy(&neis); + igraph_vector_long_destroy(&neis); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_transitivity_barrat4( - const igraph_t *graph, - igraph_vector_t *res, - const igraph_vector_t *weights, - igraph_transitivity_mode_t mode) { +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_int_t order; - igraph_vector_int_t degree; - igraph_vector_t strength; - igraph_vector_t rank; - igraph_integer_t maxdegree; + long int no_of_nodes = igraph_vcount(graph); + igraph_vector_t order, degree, rank; + long int maxdegree; igraph_inclist_t incident; - igraph_vector_int_t neis; + igraph_vector_long_t neis; igraph_vector_int_t *adj1, *adj2; igraph_vector_t actw; - igraph_integer_t i, nn; + 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_INT_INIT_FINALLY(&order, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes); + 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 = igraph_vector_int_max(°ree) + 1; - IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); + maxdegree = (long int) igraph_vector_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree)); - IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), IGRAPH_ALL, + 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)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; + 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_int_init(&neis, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + 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); @@ -749,35 +782,35 @@ static igraph_error_t igraph_i_transitivity_barrat4( igraph_vector_null(res); for (nn = no_of_nodes - 1; nn >= 0; nn--) { - igraph_integer_t adjlen1, adjlen2; + long int adjlen1, adjlen2; igraph_real_t triples; - igraph_integer_t node = VECTOR(order)[nn]; + 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(strength)[node] * (adjlen1 - 1) / 2.0; + triples = VECTOR(degree)[node] * (adjlen1 - 1) / 2.0; /* Mark the neighbors of the node */ for (i = 0; i < adjlen1; i++) { - igraph_integer_t edge = VECTOR(*adj1)[i]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); + 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++) { - igraph_integer_t edge1 = VECTOR(*adj1)[i]; + long int edge1 = (long int) VECTOR(*adj1)[i]; igraph_real_t weight1 = VECTOR(*weights)[edge1]; - igraph_integer_t nei = IGRAPH_OTHER(graph, edge1, node); - igraph_integer_t j; + 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++) { - igraph_integer_t edge2 = VECTOR(*adj2)[j]; + long int edge2 = (long int) VECTOR(*adj2)[j]; igraph_real_t weight2 = VECTOR(*weights)[edge2]; - igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge2, nei); + long int nei2 = IGRAPH_OTHER(graph, edge2, nei); if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) { continue; } @@ -798,20 +831,19 @@ static igraph_error_t igraph_i_transitivity_barrat4( } igraph_vector_destroy(&actw); - igraph_vector_int_destroy(&neis); + igraph_vector_long_destroy(&neis); igraph_inclist_destroy(&incident); igraph_vector_destroy(&rank); - igraph_vector_int_destroy(°ree); - igraph_vector_destroy(&strength); - igraph_vector_int_destroy(&order); - IGRAPH_FINALLY_CLEAN(7); + igraph_vector_destroy(°ree); + igraph_vector_destroy(&order); + IGRAPH_FINALLY_CLEAN(6); return IGRAPH_SUCCESS; } /** * \function igraph_transitivity_barrat - * \brief Weighted local transitivity of some vertices, as defined by A. Barrat. + * \brief 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 @@ -847,13 +879,13 @@ static igraph_error_t igraph_i_transitivity_barrat4( * (non-weighted) transitivity. */ -igraph_error_t igraph_transitivity_barrat(const igraph_t *graph, +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) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_integer_t no_of_edges = igraph_ecount(graph); + 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 */ @@ -865,8 +897,8 @@ igraph_error_t igraph_transitivity_barrat(const igraph_t *graph, } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Edge weight vector length (%" IGRAPH_PRId ") not equal to " - "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + IGRAPH_ERRORF("Edge weight vector length (%ld) not equal to " + "number of edges (%ld).", IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); } @@ -876,20 +908,17 @@ igraph_error_t igraph_transitivity_barrat(const igraph_t *graph, } IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); - if (! has_multiple && igraph_is_directed(graph)) { - /* When the graph is directed, mutual edges are effectively multi-edges as we - * are ignoring edge directions. */ - IGRAPH_CHECK(igraph_has_mutual(graph, &has_multiple, false)); - } if (has_multiple) { - IGRAPH_ERROR("Barrat's weighted transitivity measure works only if the graph has no multi-edges.", - IGRAPH_EINVAL); + 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, weights, mode); + 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/vendor/cigraph/src/properties/triangles_template.h b/src/vendor/cigraph/src/properties/triangles_template.h index 8b83da7317f..61418fdb197 100644 --- a/src/vendor/cigraph/src/properties/triangles_template.h +++ b/src/vendor/cigraph/src/properties/triangles_template.h @@ -26,21 +26,21 @@ #define TRANSIT_TRIEDGES #endif -igraph_integer_t no_of_nodes = igraph_vcount(graph); -igraph_integer_t node, i, j, nn; +long int no_of_nodes = igraph_vcount(graph); +long int node, i, j, nn; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; -igraph_integer_t neilen1, neilen2; -igraph_integer_t *neis; -igraph_integer_t maxdegree; +long int neilen1, neilen2; +long int *neis; +long int maxdegree; #ifdef TRANSIT_TRIEDGES -igraph_integer_t deg1; +long int deg1; #endif igraph_vector_int_t order; igraph_vector_int_t rank; -igraph_vector_int_t degree; +igraph_vector_t degree; if (no_of_nodes == 0) { #ifndef TRIANGLES @@ -51,8 +51,9 @@ if (no_of_nodes == 0) { return IGRAPH_SUCCESS; } -IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); -IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); +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); @@ -61,18 +62,19 @@ for (i = 0; i < no_of_nodes; i++) { VECTOR(degree)[i] = igraph_vector_int_size(igraph_adjlist_get(&allneis, i)); } -maxdegree = igraph_vector_int_max(°ree) + 1; -IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); -IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); +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, igraph_integer_t); +neis = IGRAPH_CALLOC(no_of_nodes, long int); if (neis == 0) { - IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, neis); @@ -92,20 +94,20 @@ for (nn = no_of_nodes - 1; nn >= 0; nn--) { neilen1 = igraph_vector_int_size(neis1); #ifdef TRANSIT_TRIEDGES - deg1 = VECTOR(degree)[node]; + deg1 = (long int) VECTOR(degree)[node]; #endif /* Mark the neighbors of the node */ for (i = 0; i < neilen1; i++) { - neis[ VECTOR(*neis1)[i] ] = node + 1; + neis[ (long int) VECTOR(*neis1)[i] ] = node + 1; } for (i = 0; i < neilen1; i++) { - igraph_integer_t nei = VECTOR(*neis1)[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++) { - igraph_integer_t nei2 = VECTOR(*neis2)[j]; + long int nei2 = (long int) VECTOR(*neis2)[j]; if (neis[nei2] == node + 1) { #ifndef TRIANGLES VECTOR(*res)[nei2] += 1; @@ -132,7 +134,7 @@ for (nn = no_of_nodes - 1; nn >= 0; nn--) { igraph_free(neis); igraph_adjlist_destroy(&allneis); igraph_vector_int_destroy(&rank); -igraph_vector_int_destroy(°ree); +igraph_vector_destroy(°ree); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(5); diff --git a/src/vendor/cigraph/src/properties/triangles_template1.h b/src/vendor/cigraph/src/properties/triangles_template1.h index 25329ea032f..3ab191f2647 100644 --- a/src/vendor/cigraph/src/properties/triangles_template1.h +++ b/src/vendor/cigraph/src/properties/triangles_template1.h @@ -22,14 +22,14 @@ */ -igraph_integer_t no_of_nodes = igraph_vcount(graph); +long int no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; -igraph_integer_t nodes_to_calc; +long int nodes_to_calc; igraph_vector_int_t *neis1, *neis2; igraph_real_t triangles; -igraph_integer_t i, j, k; -igraph_integer_t neilen1, neilen2; -igraph_integer_t *neis; +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)); @@ -43,9 +43,9 @@ if (nodes_to_calc == 0) { return IGRAPH_SUCCESS; } -neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); +neis = IGRAPH_CALLOC(no_of_nodes, long int); if (neis == 0) { - IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); } IGRAPH_FINALLY(igraph_free, neis); @@ -55,25 +55,23 @@ IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOO IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - igraph_integer_t node = IGRAPH_VIT_GET(vit); + long int node = IGRAPH_VIT_GET(vit); IGRAPH_ALLOW_INTERRUPTION(); - neis1 = igraph_lazy_adjlist_get(&adjlist, node); - IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); + neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); neilen1 = igraph_vector_int_size(neis1); for (j = 0; j < neilen1; j++) { - neis[ VECTOR(*neis1)[j] ] = i + 1; + neis[ (long int)VECTOR(*neis1)[j] ] = i + 1; } triangles = 0; for (j = 0; j < neilen1; j++) { - igraph_integer_t v = VECTOR(*neis1)[j]; - neis2 = igraph_lazy_adjlist_get(&adjlist, v); - IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + 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++) { - igraph_integer_t v2 = VECTOR(*neis2)[k]; + long int v2 = (long int) VECTOR(*neis2)[k]; if (neis[v2] == i + 1) { triangles += 1.0; } diff --git a/src/vendor/cigraph/src/random/random.c b/src/vendor/cigraph/src/random/random.c index a37495e8042..40c423791ca 100644 --- a/src/vendor/cigraph/src/random/random.c +++ b/src/vendor/cigraph/src/random/random.c @@ -21,41 +21,32 @@ */ - #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 "math/safe_intop.h" -#include "random/random_internal.h" - -#include "config.h" /* IGRAPH_THREAD_LOCAL, HAVE___UINT128_T, HAVE__UMUL128 */ - -#if defined(HAVE__UMUL128) || defined(HAVE___UMULH) -#include /* _umul128() or __umulh() are defined in intrin.h */ -#endif -#include +#include "config.h" #include -#include /* DBL_MANT_DIG */ +#include /** * \section about_rngs * *
- * About random numbers in igraph + * About random numbers in igraph, use cases * * - * Some algorithms in igraph, such as sampling from random graph models, - * require random number generators (RNGs). igraph includes a flexible - * RNG framework that allows hooking up arbitrary random number generators, - * and comes with several ready-to-use generators. This framework is used - * in igraph's high-level interfaces to integrate with the host language's - * own RNG. + * 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. * *
* @@ -131,6 +122,377 @@ /* ------------------------------------ */ +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 @@ -145,22 +507,18 @@ * igraph_rng_set_default() function. */ -extern IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default; /* defined in rng_pcg32.c */ +IGRAPH_THREAD_LOCAL 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. * - * This function \em copies the internal structure of the given \type igraph_rng_t - * object to igraph's internal default RNG structure. The structure itself - * contains two pointers only, one to the "methods" of the RNG and one to the - * memory buffer holding the internal state of the RNG. This means that if you - * keep on generating random numbers from the RNG after setting it as the - * default, it will affect the state of the default RNG as well because the two - * share the same state pointer. However, do \em not expect - * \ref igraph_rng_default() to return the same pointer as the one you passed - * in here - the state is shared, but the entire structure is not. - * * \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 @@ -182,7 +540,7 @@ void igraph_rng_set_default(igraph_rng_t *rng) { * * \return A pointer to the default random number generator. * - * \sa \ref igraph_rng_set_default() + * \sa igraph_rng_set_default() */ igraph_rng_t *igraph_rng_default(void) { @@ -191,42 +549,29 @@ igraph_rng_t *igraph_rng_default(void) { /* ------------------------------------ */ -static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits); -static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits); - -static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng); -static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range); - -static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng); -static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range); - -#if IGRAPH_INTEGER_SIZE == 64 -static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng); -static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range); -#endif - -static double igraph_i_norm_rand(igraph_rng_t *rng); -static double igraph_i_exp_rand(igraph_rng_t *rng); -static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp); -static double igraph_i_rexp(igraph_rng_t *rng, double rate); -static double igraph_i_rgamma(igraph_rng_t *rng, double shape, double scale); -static double igraph_i_rpois(igraph_rng_t *rng, double rate); +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 Initializes a random number generator. + * \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, such as \ref igraph_rngtype_mt19937, - * \ref igraph_rngtype_glibc2, \ref igraph_rngtype_pcg32 or - * \ref igraph_rngtype_pcg64. + * \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). */ -igraph_error_t igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) { +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; @@ -234,7 +579,7 @@ igraph_error_t igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) /** * \function igraph_rng_destroy - * \brief Deallocates memory associated with a random number generator. + * \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. @@ -248,7 +593,7 @@ void igraph_rng_destroy(igraph_rng_t *rng) { /** * \function igraph_rng_seed - * \brief Seeds a random number generator. + * \brief Set the seed of a random number generator. * * \param rng The RNG. * \param seed The new seed. @@ -257,295 +602,67 @@ void igraph_rng_destroy(igraph_rng_t *rng) { * Time complexity: usually O(1), but may depend on the type of the * RNG. */ -igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed) { +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)); - rng->is_seeded = 1; return IGRAPH_SUCCESS; } /** - * \function igraph_rng_bits - * \brief The number of random bits that a random number generator can produces in a single round. + * \function igraph_rng_max + * \brief Query the maximum possible integer for a random number generator. * * \param rng The RNG. - * \return The number of random bits that can be generated in a single round - * with 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). */ -IGRAPH_EXPORT igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng) { - return rng->type->bits; + +unsigned long int igraph_rng_max(igraph_rng_t *rng) { + const igraph_rng_type_t *type = rng->type; + return type->max; } /** - * \function igraph_rng_max - * \brief The maximum possible integer for a random number generator. + * \function igraph_rng_min + * \brief Query the minimum possible integer for a random number generator. * - * Note that this number is only for informational purposes; it returns the - * maximum possible integer that can be generated with the RNG with a single - * call to its internals. It is derived directly from the number of random - * \em bits that the RNG can generate in a single round. When this is smaller - * than what would be needed by other RNG functions like \ref igraph_rng_get_integer(), - * igraph will call the RNG multiple times to generate more random bits. + * This function will be removed in a future version. Assume zero + * as the retun value. * * \param rng The RNG. - * \return The largest possible integer that can be generated in a single round - * with 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 */ -igraph_uint_t igraph_rng_max(const igraph_rng_t *rng) { +unsigned long int igraph_rng_min(igraph_rng_t *rng) { const igraph_rng_type_t *type = rng->type; -#if IGRAPH_INTEGER_SIZE == 64 - return (type->bits >= 64) ? 0xFFFFFFFFFFFFFFFFULL : ((1ULL << type->bits) - 1); -#else - return (type->bits >= 32) ? 0xFFFFFFFFUL : ((1ULL << type->bits) - 1); -#endif + IGRAPH_WARNING("igraph_rng_min() is deprecated; assume 0 as the return value."); + return type->min; } /** * \function igraph_rng_name - * \brief The type of a random number generator. + * \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. + * change the returned string pointer. * * Time complexity: O(1). */ -const char *igraph_rng_name(const igraph_rng_t *rng) { +const char *igraph_rng_name(igraph_rng_t *rng) { const igraph_rng_type_t *type = rng->type; return type->name; } -/** - * Generates a given number of random bits, possibly invoking the underlying - * RNG multiple times if needed, and returns the result in an \c igraph_uint_t . - * - * \param rng The RNG. - * \param bits The number of random bits needed. Must be smaller than or equal - * to the size of the \c igraph_uint_t data type. Passing a value larger - * than the size of \c igraph_uint_t will throw away random bits except - * the last few that are needed to fill an \c igraph_uint_t . - * \return The random bits, packed into the low bits of an \c igraph_uint_t . - * The upper, unused bits of \c igraph_uint_t will be set to zero. - */ -static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits) { - const igraph_rng_type_t *type = rng->type; - igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); - igraph_uint_t result; - - if (rng_bitwidth >= bits) { - /* keep the high bits as RNGs sometimes tend to have lower entropy in - * low bits than in high bits */ - result = type->get(rng->state) >> (rng_bitwidth - bits); - } else { - result = 0; - do { - result = (result << rng_bitwidth) + type->get(rng->state); - bits -= rng_bitwidth; - } while (bits > rng_bitwidth); - - /* and now the last piece */ - result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); - } - - return result; -} - -/** - * Generates a given number of random bits, possibly invoking the underlying - * RNG multiple times if needed, and returns the result in an \c uint64_t . - * - * Prefer \c igraph_i_rng_get_random_bits() if you know that you need at most - * 32 bits due to the type of the return value. This function might perform - * worse on 32-bit platforms because the result is always 64 bits. - * - * \param rng The RNG. - * \param bits The number of random bits needed. Must be smaller than or equal - * to the size of the \c uint64_t data type. Passing a value larger - * than the size of \c uint64_t will throw away random bits except - * the last few that are needed to fill an \c uint64_t . - * \return The random bits, packed into the low bits of an \c uint64_t . - * The upper, unused bits of \c uint64_t will be set to zero. - */ -static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits) { - const igraph_rng_type_t *type = rng->type; - igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); - uint64_t result; - - if (rng_bitwidth >= bits) { - /* keep the high bits as RNGs sometimes tend to have lower entropy in - * low bits than in high bits */ - result = type->get(rng->state) >> (rng_bitwidth - bits); - } else { - result = 0; - do { - result = (result << rng_bitwidth) + type->get(rng->state); - bits -= rng_bitwidth; - } while (bits > rng_bitwidth); - - /* and now the last piece */ - result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); - } - - return result; -} - -/** - * Generates a random integer in the full range of the \c igraph_uint_t - * data type. - * - * \param rng The RNG. - * \return The random integer. - */ -static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng) { - return igraph_i_rng_get_random_bits(rng, sizeof(igraph_uint_t) * 8); -} - -/** - * Generates a random integer in the full range of the \c uint32_t - * data type. - * - * \param rng The RNG. - * \return The random integer. - */ -static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng) { - return igraph_i_rng_get_random_bits(rng, 32); -} - -/** - * Generates a random integer in the range [0; range) (upper bound exclusive), - * restricted to at most 32 bits. - * - * \param rng The RNG. - * \param range The upper bound (exclusive). - * \return The random integer. - */ -static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range) { - /* Debiased integer multiplication -- Lemire's method - * from https://www.pcg-random.org/posts/bounded-rands.html */ - uint32_t x, l, t = (-range) % range; - uint64_t m; - do { - x = igraph_i_rng_get_uint32(rng); - m = (uint64_t)(x) * (uint64_t)(range); - l = (uint32_t)m; - } while (l < t); - return m >> 32; -} - -#if IGRAPH_INTEGER_SIZE == 64 -/** - * Generates a random integer in the full range of the \c uint64_t - * data type. - * - * \param rng The RNG. - * \param range The upper bound (inclusive). - * \return The random integer. - */ -static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng) { - return igraph_i_rng_get_random_bits(rng, 64); -} - -#if !defined(HAVE___UINT128_T) -static uint64_t igraph_i_umul128(uint64_t a, uint64_t b, uint64_t *hi) { -#if defined(HAVE__UMUL128) - /* MSVC has _umul128() on x64 but not on arm64 */ - return _umul128(a, b, hi); -#elif defined(HAVE___UMULH) - /* MSVC has __umulh() on arm64 */ - *hi = __umulh(a, b); - return a*b; -#else - /* Portable but slow fallback implementation of unsigned - * 64-bit multiplication obtaining a 128-bit result. - * Based on https://stackoverflow.com/a/28904636/695132 - */ - - uint64_t a_lo = (uint32_t) a; - uint64_t a_hi = a >> 32; - uint64_t b_lo = (uint32_t) b; - uint64_t b_hi = b >> 32; - - uint64_t a_x_b_hi = a_hi * b_hi; - uint64_t a_x_b_mid = a_hi * b_lo; - uint64_t b_x_a_mid = b_hi * a_lo; - uint64_t a_x_b_lo = a_lo * b_lo; - - uint64_t carry_bit = ((uint64_t) (uint32_t) a_x_b_mid + - (uint64_t) (uint32_t) b_x_a_mid + - (a_x_b_lo >> 32) ) >> 32; - - *hi = a_x_b_hi + - (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + - carry_bit; - - return a*b; -#endif -} -#endif /* !defined(HAVE___UINT128_T) */ - -/** - * Generates a random integer in the range [0; range) (upper bound exclusive), - * restricted to at most 64 bits. - * - * \param rng The RNG. - * \param range The upper bound (exclusive). - * \return The random integer. - */ -static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range) { - /* Debiased integer multiplication -- Lemire's method - * from https://www.pcg-random.org/posts/bounded-rands.html */ - uint64_t x, l, t = (-range) % range; -#if defined(HAVE___UINT128_T) - /* gcc and clang have __uint128_t */ - __uint128_t m; - do { - x = igraph_i_rng_get_uint64(rng); - m = (__uint128_t)(x) * (__uint128_t)(range); - l = (uint64_t)m; - } while (l < t); - return m >> 64; -#else - uint64_t hi; - do { - x = igraph_i_rng_get_uint64(rng); - l = igraph_i_umul128(x, range, &hi); - } while (l < t); - return hi; -#endif -} - -#endif /* IGRAPH_INTEGER_SIZE == 64 */ - -/** - * Generates a random integer in the range [0; range) (upper bound exclusive). - * - * \param rng The RNG. - * \param range The upper bound (exclusive). - * \return The random integer. - */ -static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range) { - /* We must make this function behave the same way for range < 2^32 so igraph - * behaves the same way on 32-bit and 64-bit platforms as long as we stick - * to integers less than 2^32. This is to ensure that the unit tests are - * consistent */ - -#if IGRAPH_INTEGER_SIZE == 32 - return igraph_i_rng_get_uint32_bounded(rng, range); -#else - if (range <= UINT32_MAX) { - return igraph_i_rng_get_uint32_bounded(rng, range); - } else { - return igraph_i_rng_get_uint64_bounded(rng, range); - } -#endif -} - /** * \function igraph_rng_get_integer * \brief Generate an integer random number from an interval. @@ -557,57 +674,43 @@ static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uin * should be at least l. * \return The generated random integer. * - * Time complexity: O(log2(h-l) / bits) where bits is the value of - * \ref igraph_rng_bits(rng). + * Time complexity: depends on the generator, but should be usually + * O(1). */ -igraph_integer_t igraph_rng_get_integer( - igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t h -) { +long int igraph_rng_get_integer(igraph_rng_t *rng, + long int l, long int h) { const igraph_rng_type_t *type = rng->type; - igraph_uint_t range; - - assert(h >= l); - - if (h == l) { - return l; - } - - if (type->get_int) { - return type->get_int(rng->state, l, h); - } - - if (IGRAPH_UNLIKELY(l == IGRAPH_INTEGER_MIN && h == IGRAPH_INTEGER_MAX)) { - /* Full uint range is needed, we can just grab a random number from - * the uint range and cast it to a signed integer */ - return (igraph_integer_t) igraph_i_rng_get_uint(rng); - } else if (l >= 0 || h < 0) { - /* this is okay, (h - l) will not overflow an igraph_integer_t */ - range = (igraph_uint_t)(h - l) + 1; - } else { - /* (h - l) could potentially overflow so we need to play it safe. If we - * are here, l < 0 and h >= 0 so we can cast -l into an igraph_uint_t - * safely and do the subtraction that way */ - range = ((igraph_uint_t)(h)) + ((igraph_uint_t)(-l)) + 1; + /* 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; } - - return l + igraph_i_rng_get_uint_bounded(rng, range); + IGRAPH_FATAL("Internal random generator error"); } /** * \function igraph_rng_get_normal - * \brief Samples from a normal distribution. - * - * Generates random variates from a normal distribution with probability - * density - * - * - * exp( -(x - m)^2 / (2 s^2) ). + * \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 The standard deviation. + * \param s Standard deviation. * \return The generated normally distributed random number. * * Time complexity: depends on the type of the RNG. @@ -619,16 +722,13 @@ igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, if (type->get_norm) { return type->get_norm(rng->state) * s + m; } else { - return igraph_i_norm_rand(rng) * s + m; + return igraph_norm_rand(rng) * s + m; } } /** * \function igraph_rng_get_unif - * \brief Samples real numbers from a given interval. - * - * Generates uniformly distributed real numbers from the [l, h) - * half-open interval. + * \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. @@ -642,24 +742,19 @@ igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, igraph_real_t l, igraph_real_t h) { - assert(h >= l); - - if (l == h) return h; - - /* Ensure that 'h' is never produced due to numerical roundoff errors, except when l == h. */ - igraph_real_t r; - do { - r = igraph_rng_get_unif01(rng) * (h - l) + l; - } while (IGRAPH_UNLIKELY(r == h)); - return r; + 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 Samples uniformly from the unit interval. - * - * Generates uniformly distributed real numbers from the [0, 1) - * half-open interval. + * \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. @@ -671,371 +766,114 @@ igraph_real_t igraph_rng_get_unif(igraph_rng_t *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 { - /* We extract 52 random bits from a 64-bit uint and fill that directly - * into the mantissa of a double, bit-by-bit, clear the sign bit and - * set the exponent to 2^0. This way we get a 52-bit random double - * between 1 (inclusive) and 2 (exclusive), uniformly distributed. - * Then we subtract 1 to arrive at the [0; 1) interval. This is fast - * but we lose one bit of precision as there are 2^53 possible doubles - * between 0 and 1. */ - uint64_t r = (igraph_i_rng_get_random_bits_uint64(rng, 52) & 0xFFFFFFFFFFFFFull) | 0x3FF0000000000000ull; - return *(double *)(&r) - 1.0; - } -} - -/** - * \function igraph_rng_get_geom - * \brief Samples from a geometric distribution. - * - * Generates random variates from a geometric distribution. The number \c k is - * generated with probability - * - * - * (1 - p)^k p, k = 0, 1, 2, .... - * - * \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 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 (!isfinite(p) || p <= 0 || p > 1) { - return IGRAPH_NAN; - } - if (type->get_geom) { - return type->get_geom(rng->state, p); - } else { - return igraph_rng_get_pois(rng, igraph_i_exp_rand(rng) * ((1 - p) / p)); - } -} - -/** - * \function igraph_rng_get_binom - * \brief Samples from a binomial distribution. - * - * Generates random variates from a binomial distribution. The number \c k is generated - * with probability - * - * - * (n \choose k) p^k (1-p)^(n-k), k = 0, 1, ..., n. - * - * \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 RNG. - */ - -igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, igraph_integer_t 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_i_rbinom(rng, n, p); - } -} - -/** - * \function igraph_rng_get_gamma - * \brief Samples from a gamma distribution. - * - * Generates random variates from a gamma distribution with probability - * density proportional to - * - * - * x^(shape-1) exp(-x / scale). - * - * \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 the 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_i_rgamma(rng, shape, scale); - } -} - -/** - * \function igraph_rng_get_exp - * \brief Samples from an exponential distribution. - * - * Generates random variates from an exponential distribution with probability - * density proportional to - * - * - * exp(-rate x). - * - * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() - * here to use the default igraph RNG. - * \param rate Rate parameter. - * \return The generated sample. - * - * Time complexity: depends on the RNG. - */ - -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_i_rexp(rng, rate); - } -} - -/** - * \function igraph_rng_get_pois - * \brief Samples from a Poisson distribution. - * - * Generates random variates from a Poisson distribution. The number \c k is generated - * with probability - * - * - * rate^k * exp(-rate) / k!, k = 0, 1, 2, .... - * - * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() - * here to use the default igraph RNG. - * \param rate The rate parameter of the Poisson distribution. Must not be negative. - * \return The generated geometrically distributed random number. - * - * Time complexity: depends on the RNG. - */ - -igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate) { - const igraph_rng_type_t *type = rng->type; - if (isnan(rate) || rate < 0) { - return IGRAPH_NAN; - } else if (rate == 0) { - return 0; - } else if (type->get_pois) { - return type->get_pois(rng->state, rate); - } else { - return igraph_i_rpois(rng, rate); - } -} - - -/** - * \ingroup internal - * - * This function appends the rest of the needed random numbers to the - * result vector. It is Algoirthm A in Vitter's paper. - */ - -static void igraph_i_random_sample_alga(igraph_vector_int_t *res, - igraph_integer_t l, igraph_integer_t h, - igraph_integer_t length) { - /* Vitter: Variables V, quot, Nreal, and top are of type real */ - - igraph_integer_t N = h - l + 1; - igraph_integer_t n = length; - - igraph_real_t top = N - n; - igraph_real_t Nreal = N; - igraph_integer_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_int_push_back(res, l); /* allocated */ - Nreal = -1.0 + Nreal; n = -1 + n; - } - - S = trunc(round(Nreal) * RNG_UNIF01()); - l += S + 1; - igraph_vector_int_push_back(res, l); /* allocated */ -} - -/** - * \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. - * - * - * Reference: - * - * - * J. S. Vitter. An efficient algorithm for sequential random sampling. - * ACM Transactions on Mathematical Software, 13(1):58--67, 1987. - * https://doi.org/10.1145/23002.23003 - * - * \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. - * \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. - * \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). - * - * \example examples/simple/igraph_random_sample.c - */ - -igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, - igraph_integer_t length) { - igraph_integer_t N; /* := h - l + 1 */ - IGRAPH_SAFE_ADD(h, -l, &N); - IGRAPH_SAFE_ADD(N, 1, &N); - - igraph_integer_t n = length; - - 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_integer_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_integer_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_int_resize(res, 1)); - VECTOR(*res)[0] = l; - return IGRAPH_SUCCESS; - } - if (length == 0) { - igraph_vector_int_clear(res); - return IGRAPH_SUCCESS; - } - if (length == N) { - IGRAPH_CHECK(igraph_vector_int_resize(res, length)); - for (igraph_integer_t i = 0; i < length; i++) { - VECTOR(*res)[i] = l++; - } - return IGRAPH_SUCCESS; + 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"); +} - igraph_vector_int_clear(res); - IGRAPH_CHECK(igraph_vector_int_reserve(res, length)); +/** + * \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. + */ - RNG_BEGIN(); +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); + } +} - Vprime = exp(log(RNG_UNIF01()) * ninv); - l = l - 1; +/** + * \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. + */ - 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; +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); + } +} - y1 = exp(log(U * Nreal / qu1real) * nmin1inv); - Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real)); - if (Vprime <= 1.0) { - break; - } +/** + * \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. + */ - 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); - } +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); + } +} - l += S + 1; - igraph_vector_int_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; +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); } +} - if (n > 1) { - igraph_i_random_sample_alga(res, l + 1, h, n); +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 { - S = floor(N * Vprime); - l += S + 1; - igraph_vector_int_push_back(res, l); /* allocated */ + return igraph_rexp(rng, rate); } +} - RNG_END(); - return IGRAPH_SUCCESS; -} +/* + * \ingroup internal + * + * This function appends the rest of the needed random number to the + * result vector. + */ -static void igraph_i_random_sample_alga_real(igraph_vector_t *res, - igraph_real_t l, igraph_real_t h, - igraph_real_t length) { +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; @@ -1057,25 +895,35 @@ static void igraph_i_random_sample_alga_real(igraph_vector_t *res, quot = (quot * top) / Nreal; } l += S; - igraph_vector_push_back(res, l); /* allocated */ + igraph_vector_push_back(res, l); /* allocated */ Nreal = -1.0 + Nreal; n = -1 + n; } - S = trunc(round(Nreal) * RNG_UNIF01()); + S = floor(round(Nreal) * RNG_UNIF01()); l += S + 1; - igraph_vector_push_back(res, l); /* allocated */ + igraph_vector_push_back(res, l); /* allocated */ + + return IGRAPH_SUCCESS; } /** * \ingroup nongraph - * \function igraph_random_sample_real - * \brief Generates an increasing random sequence of integers (igraph_real_t version). - * - * This function is the 'real' version of \ref igraph_random_sample(), and was added - * so \ref igraph_erdos_renyi_game() and related function can use a random sample - * of doubles instead of integers to prevent overflows on systems with 32-bit - * \type igraph_integer_t. + * \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 @@ -1091,16 +939,26 @@ static void igraph_i_random_sample_alga_real(igraph_vector_t *res, * \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 */ -igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, - igraph_real_t h, igraph_integer_t length) { - /* This function is the 'real' version of igraph_random_sample, and was added - * so erdos_renyi_game can use a random sample of doubles instead of integers - * to prevent overflows on systems with 32-bits igraph_integer_t. - */ +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; @@ -1114,16 +972,11 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, /* getting back some sense of sanity */ if (l > h) { - IGRAPH_ERROR("Lower limit is greater than upper limit.", IGRAPH_EINVAL); + 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); - } - - /* ensure that we work in the range where igraph_real_t can represent integers exactly */ - if (h > IGRAPH_MAX_EXACT_REAL || l < -IGRAPH_MAX_EXACT_REAL || N > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Sampling interval too large.", IGRAPH_EOVERFLOW); + IGRAPH_ERROR("Sample size exceeds size of candidate pool", IGRAPH_EINVAL); } /* treat rare cases quickly */ @@ -1137,8 +990,9 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, return IGRAPH_SUCCESS; } if (length == N) { + long int i = 0; IGRAPH_CHECK(igraph_vector_resize(res, length)); - for (igraph_integer_t i = 0; i < length; i++) { + for (i = 0; i < length; i++) { VECTOR(*res)[i] = l++; } return IGRAPH_SUCCESS; @@ -1206,8 +1060,11 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, } if (n > 1) { - igraph_i_random_sample_alga_real(res, l + 1, h, n); + 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 */ @@ -1215,7 +1072,7 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, RNG_END(); - return IGRAPH_SUCCESS; + return retval; } /* @@ -1319,6 +1176,53 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, #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 */ @@ -1380,12 +1284,12 @@ igraph_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, #define R_D_negInonint(x) (x < 0. || R_D_nonint(x)) #define R_D_nonint_check(x) \ - if (R_D_nonint(x)) { \ + if(R_D_nonint(x)) { \ MATHLIB_WARNING("non-integer x = %f", x); \ return R_D__0; \ } -static double igraph_i_qnorm5(double p, double mu, double sigma, igraph_bool_t lower_tail, igraph_bool_t log_p) { +double igraph_qnorm5(double p, double mu, double sigma, int lower_tail, int log_p) { double p_, q, r, val; #ifdef IEEE_754 @@ -1485,44 +1389,125 @@ static double igraph_i_qnorm5(double p, double mu, double sigma, igraph_bool_t l return mu + sigma * val; } -static igraph_integer_t imax2(igraph_integer_t x, igraph_integer_t y) { +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 igraph_integer_t imin2(igraph_integer_t x, igraph_integer_t y) { +static int imin2(int x, int y) { return (x < y) ? x : y; } -static double igraph_i_norm_rand(igraph_rng_t *rng) { - double r; +static double igraph_norm_rand(igraph_rng_t *rng) { - /* Use the inversion method based on uniform variates from (0, 1). - * We exclude 0.0 as it would lead to generating -infinity. - * It is assumed that unif01() provides sufficient accuracy. - * A resolution of 2^-32 may not be sufficient. igraph's default - * implementaton provides an accuracy of 2^-52. - */ - do { - r = igraph_rng_get_unif01(rng); - } while (r == 0.0); + double u1; - return igraph_i_qnorm5(r, 0.0, 1.0, 1, 0); +#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); } /* - * The following function is igraph code (not R / Mathlib). + * 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); * - * We use simple inverse transform sampling, with the assumption that the - * quality/resolution of uniform variates is high (52 bits in the default - * implementation). The quantile function is -log(1 - r) but given that - * r is sampled uniformly form the unit interval, -log(r) is equivalent. - * r = 0 is disallowed as it would yield infinity. + * 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. */ -static double igraph_i_exp_rand(igraph_rng_t *rng) { - igraph_real_t r = igraph_rng_get_unif01(rng); - if (r == 0.0) r = 1.0; /* sample from (0, 1] instead of [0, 1) */ - return -log(r); +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]; } /* @@ -1580,15 +1565,14 @@ static double igraph_i_exp_rand(igraph_rng_t *rng) { #define TRUE 1 #define M_1_SQRT_2PI 0.398942280401432677939946059934 /* 1/sqrt(2pi) */ -static double igraph_i_rpois(igraph_rng_t *rng, double mu) { +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 IGRAPH_THREAD_LOCAL int l; - static IGRAPH_THREAD_LOCAL igraph_integer_t m; + static IGRAPH_THREAD_LOCAL int l, m; static IGRAPH_THREAD_LOCAL double b1, b2, c, c0, c1, c2, c3; static IGRAPH_THREAD_LOCAL double pp[36], p0, p, q, s, d, omega; @@ -1600,7 +1584,7 @@ static double igraph_i_rpois(igraph_rng_t *rng, double mu) { double pois = -1.; int k, kflag, big_mu, new_big_mu = FALSE; - if (!isfinite(mu) || mu < 0) { + if (!igraph_finite(mu)) { ML_ERR_return_NAN; } @@ -1633,7 +1617,7 @@ static double igraph_i_rpois(igraph_rng_t *rng, double mu) { /*muprev = 0.;-* such that next time, mu != muprev ..*/ if (mu != muprev) { muprev = mu; - m = imax2(1, (igraph_integer_t) mu); + m = imax2(1, (int) mu); l = 0; /* pp[] is already ok up to pp[l] */ q = p0 = p = exp(-mu); } @@ -1678,7 +1662,7 @@ static double igraph_i_rpois(igraph_rng_t *rng, double mu) { /* Only if mu >= 10 : ----------------------- */ /* Step N. normal sample */ - g = mu + s * igraph_i_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ + g = mu + s * igraph_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ if (g >= 0.) { pois = floor(g); @@ -1726,12 +1710,12 @@ static double igraph_i_rpois(igraph_rng_t *rng, double mu) { repeat { /* Step E. Exponential Sample */ - E = igraph_i_exp_rand(rng);/* ~ Exp(1) (standard exponential) */ + 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 + copysign(E, u); + t = 1.8 + fsign(E, u); if (t > -0.6744) { pois = floor(mu + s * t); fk = pois; @@ -1787,24 +1771,41 @@ static double igraph_i_rpois(igraph_rng_t *rng, double mu) { #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_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) { +static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { + /* FIXME: These should become THREAD_specific globals : */ static IGRAPH_THREAD_LOCAL double c, fm, npq, p1, p2, p3, p4, qn; static IGRAPH_THREAD_LOCAL double xl, xll, xlr, xm, xr; static IGRAPH_THREAD_LOCAL double psave = -1.0; - static IGRAPH_THREAD_LOCAL igraph_integer_t nsave = -1; - static IGRAPH_THREAD_LOCAL igraph_integer_t m; + static IGRAPH_THREAD_LOCAL int nsave = -1; + static IGRAPH_THREAD_LOCAL 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; - igraph_integer_t i, ix, k; + 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 (!isfinite(pp) || + if (!igraph_finite(pp) || /* n=0, p=0, p=1 are not errors */ n < 0 || pp < 0. || pp > 1.) { ML_ERR_return_NAN; @@ -1840,11 +1841,7 @@ static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) m = ffm; fm = m; npq = np * q; - /* Note (igraph): Original code used a cast to (int) for rounding. However, - * the max npq = n*p*(1-p) value is 0.25*n, thus 2.195 * sqrt(npq) may be - * as large as 1.0975 * sqrt(n). This is not representable on a 32-bit signed - * integer when n is a 64-bit signed integer. Thus we use trunc() instead. */ - p1 = trunc(2.195 * sqrt(npq) - 4.6 * q) + 0.5; + p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5; xm = fm + 0.5; xl = xm - p1; xr = xm + p1; @@ -1896,7 +1893,7 @@ static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) } } /* determine appropriate way to perform accept/reject test */ - k = imaxabs(ix - m); + k = abs(ix - m); if (k <= 20 || k >= npq / 2 - 1) { /* explicit evaluation */ f = 1.0; @@ -1964,15 +1961,75 @@ static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) return (double)ix; } -static igraph_real_t igraph_i_rexp(igraph_rng_t *rng, double rate) { +static igraph_real_t igraph_rexp(igraph_rng_t *rng, double rate) { igraph_real_t scale = 1.0 / rate; - if (!isfinite(scale) || scale <= 0.0) { + if (!IGRAPH_FINITE(scale) || scale <= 0.0) { if (scale == 0.0) { return 0.0; } return IGRAPH_NAN; } - return scale * igraph_i_exp_rand(rng); + 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 */ @@ -2026,7 +2083,7 @@ static igraph_real_t igraph_i_rexp(igraph_rng_t *rng, double rate) { * Output: a variate from the gamma(a)-distribution */ -static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { +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 */ @@ -2059,7 +2116,7 @@ static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { double e, p, q, r, t, u, v, w, x, ret_val; - if (!isfinite(a) || !isfinite(scale) || a < 0.0 || scale <= 0.0) { + if (!igraph_finite(a) || !igraph_finite(scale) || a < 0.0 || scale <= 0.0) { if (scale == 0.) { return 0.; } @@ -2075,12 +2132,12 @@ static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { p = e * igraph_rng_get_unif01(rng); if (p >= 1.0) { x = -log((e - p) / a); - if (igraph_i_exp_rand(rng) >= (1.0 - a) * log(x)) { + if (igraph_exp_rand(rng) >= (1.0 - a) * log(x)) { break; } } else { x = exp(log(p) / a); - if (igraph_i_exp_rand(rng) >= x) { + if (igraph_exp_rand(rng) >= x) { break; } } @@ -2101,7 +2158,7 @@ static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { x = (s,1/2) -normal deviate. */ /* immediate acceptance (i) */ - t = igraph_i_norm_rand(rng); + t = igraph_norm_rand(rng); x = s + 0.5 * t; ret_val = x * x; if (t >= 0.0) { @@ -2163,7 +2220,7 @@ static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { /* Step 8: e = standard exponential deviate * u = 0,1 -uniform deviate * t = (b,si)-double exponential (laplace) sample */ - e = igraph_i_exp_rand(rng); + e = igraph_exp_rand(rng); u = igraph_rng_get_unif01(rng); u = u + u - 1.0; if (u < 0.0) { @@ -2198,7 +2255,7 @@ static double igraph_i_rgamma(igraph_rng_t *rng, double a, double scale) { return scale * x * x; } -igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, +int igraph_rng_get_dirichlet(igraph_rng_t *rng, const igraph_vector_t *alpha, igraph_vector_t *result) { @@ -2207,11 +2264,11 @@ igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, igraph_real_t sum = 0.0; if (len < 2) { - IGRAPH_ERROR("Dirichlet parameter vector too short, must have at least two entries.", - IGRAPH_EINVAL); + 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_ERROR("Dirichlet concentration parameters must be positive", IGRAPH_EINVAL); } diff --git a/src/vendor/cigraph/src/random/random_internal.h b/src/vendor/cigraph/src/random/random_internal.h deleted file mode 100644 index 25110da1a34..00000000000 --- a/src/vendor/cigraph/src/random/random_internal.h +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- 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_RANDOM_INTERNAL_H -#define IGRAPH_RANDOM_INTERNAL_H - -#include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -igraph_error_t igraph_random_sample_real( - igraph_vector_t *res, igraph_real_t l, igraph_real_t h, - igraph_integer_t length); - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/src/random/rng_glibc2.c b/src/vendor/cigraph/src/random/rng_glibc2.c deleted file mode 100644 index 5a687f9155a..00000000000 --- a/src/vendor/cigraph/src/random/rng_glibc2.c +++ /dev/null @@ -1,144 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_random.h" - -#include "igraph_memory.h" -#include "igraph_types.h" - -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; - - /* The original implementation used x[*i] += x[*j] here. Considering that - * x is signed, this is undefined behaviour according to the C standard. - * Therefore, we temporarily cast to unsigned long int to achieve what the - * original intention was */ - x[*i] = ((unsigned long int)x[*i]) + ((unsigned long int)x[*j]); - k = (x[*i] >> 1) & 0x7FFFFFFF; - - (*i)++; - if (*i == n) { - *i = 0; - } - - (*j)++ ; - if (*j == n) { - *j = 0; - } - - return k; -} - -static igraph_uint_t 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); -} - -/* 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] = s ; - } -} - -static igraph_error_t igraph_rng_glibc2_seed(void *vstate, igraph_uint_t 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, (unsigned long) seed); - - state->i = 3; - state->j = 0; - - for (i = 0; i < 10 * 31; i++) { - igraph_rng_glibc2_get(state); - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_rng_glibc2_init(void **state) { - igraph_i_rng_glibc2_state_t *st; - - st = IGRAPH_CALLOC(1, igraph_i_rng_glibc2_state_t); - IGRAPH_CHECK_OOM(st, "Cannot initialize GNU libc 2 RNG."); - (*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", - /* bits= */ 31, - /* init= */ igraph_rng_glibc2_init, - /* destroy= */ igraph_rng_glibc2_destroy, - /* seed= */ igraph_rng_glibc2_seed, - /* get= */ igraph_rng_glibc2_get, - /* get_int= */ 0, - /* get_real= */ 0, - /* get_norm= */ 0, - /* get_geom= */ 0, - /* get_binom= */ 0, - /* get_exp= */ 0, - /* get_gamma= */ 0, - /* get_pois= */ 0 -}; diff --git a/src/vendor/cigraph/src/random/rng_mt19937.c b/src/vendor/cigraph/src/random/rng_mt19937.c deleted file mode 100644 index 9caf97670ac..00000000000 --- a/src/vendor/cigraph/src/random/rng_mt19937.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_random.h" - -#include "igraph_memory.h" -#include "igraph_types.h" - -#include /* memset() */ -#include - - -#define N 624 /* Period parameters */ -#define M 397 - -/* most significant w-r bits */ -static const uint32_t UPPER_MASK = UINT32_C(0x80000000); - -/* least significant r bits */ -static const uint32_t LOWER_MASK = UINT32_C(0x7fffffff); - -typedef struct { - uint32_t mt[N]; - int mti; -} igraph_i_rng_mt19937_state_t; - -static igraph_uint_t igraph_rng_mt19937_get(void *vstate) { - igraph_i_rng_mt19937_state_t *state = vstate; - - uint32_t k; - uint32_t *const mt = state->mt; - -#define MAGIC(y) (((y) & 0x1) ? UINT32_C(0x9908b0df) : 0) - - if (state->mti >= N) { - /* generate N words at one time */ - int kk; - - for (kk = 0; kk < N - M; kk++) { - uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); - mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y); - } - for (; kk < N - 1; kk++) { - uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); - mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y); - } - - { - uint32_t 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) & UINT32_C(0x9d2c5680); - k ^= (k << 15) & UINT32_C(0xefc60000); - k ^= (k >> 18); - - state->mti++; - - return k; -} - -static igraph_error_t igraph_rng_mt19937_seed(void *vstate, igraph_uint_t 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 & UINT32_C(0xffffffff); - - for (i = 1; i < N; i++) { - /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd - Ed. p.106 for multiplier. */ - state->mt[i] = - (UINT32_C(1812433253) * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) + - (uint32_t) i); - state->mt[i] &= UINT32_C(0xffffffff); - } - - state->mti = i; - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_rng_mt19937_init(void **state) { - igraph_i_rng_mt19937_state_t *st; - - st = IGRAPH_CALLOC(1, igraph_i_rng_mt19937_state_t); - IGRAPH_CHECK_OOM(st, "Cannot initialize MT19937 RNG."); - (*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 igraph_rng_mt19937_seed() 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", - /* bits= */ 32, - /* init= */ igraph_rng_mt19937_init, - /* destroy= */ igraph_rng_mt19937_destroy, - /* seed= */ igraph_rng_mt19937_seed, - /* get= */ igraph_rng_mt19937_get, - /* get_int= */ 0, - /* get_real= */ 0, - /* get_norm= */ 0, - /* get_geom= */ 0, - /* get_binom= */ 0, - /* get_exp= */ 0, - /* get_gamma= */ 0, - /* get_pois= */ 0 -}; - -#undef N -#undef M diff --git a/src/vendor/cigraph/src/random/rng_pcg32.c b/src/vendor/cigraph/src/random/rng_pcg32.c deleted file mode 100644 index fb51b1ba2f8..00000000000 --- a/src/vendor/cigraph/src/random/rng_pcg32.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_random.h" - -#include "igraph_memory.h" -#include "igraph_types.h" - -#include "pcg/pcg_variants.h" - -#include "config.h" /* IGRAPH_THREAD_LOCAL */ - -/* The original implementation of the 32-bit PCG random number generator in this - * file was obtained from https://github.com/imneme/pcg-c - * - * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible - * with igraph's GPLv2 license. License notices for PCG are to be found in the - * pcg_variants.h header - */ - -static const pcg32_random_t pcg32_initializer = PCG32_INITIALIZER; - -static igraph_uint_t igraph_rng_pcg32_get(void *vstate) { - pcg32_random_t *state = (pcg32_random_t*) vstate; - return pcg32_random_r(state); -} - -static igraph_error_t igraph_rng_pcg32_seed(void *vstate, igraph_uint_t seed) { - pcg32_random_t *state = (pcg32_random_t*) vstate; - - /* PCG32 is seeded by a 64-bit state and a 64-bit sequence number (well, only - * 63 bits are used from the sequence number, though). Since the unified - * igraph RNG seeding interface provides a single igraph_uint_t as the seed, - * we use the seed to fill in the sequence number and use the state from - * PCG32_INITIALIZER */ - if (seed == 0) { - /* If you feel the temptation to unify the two branches by running - * seed = pcg32_initializer.inc >> 1, don't. - * seed is an igraph_uint_t, so it can be 32-bit or 64-bit. - * pcg32_initializer.inc is always 64-bit. - */ - pcg32_srandom_r(state, pcg32_initializer.state, pcg32_initializer.inc >> 1); - } else { - pcg32_srandom_r(state, pcg32_initializer.state, seed); - } - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_rng_pcg32_init(void **state) { - pcg32_random_t *st; - - st = IGRAPH_CALLOC(1, pcg32_random_t); - IGRAPH_CHECK_OOM(st, "Cannot initialize PCG32 RNG."); - (*state) = st; - - igraph_rng_pcg32_seed(st, 0); - - return IGRAPH_SUCCESS; -} - -static void igraph_rng_pcg32_destroy(void *vstate) { - pcg32_random_t *state = (pcg32_random_t*) vstate; - IGRAPH_FREE(state); -} - -/** - * \var igraph_rngtype_pcg32 - * \brief The PCG random number generator (32-bit version). - * - * This is an implementation of the PCG random number generator; see - * https://www.pcg-random.org for more details. This implementation returns - * 32 random bits in a single iteration. - * - * - * The generator was ported from the original source code published by the - * authors at https://github.com/imneme/pcg-c. - */ - -const igraph_rng_type_t igraph_rngtype_pcg32 = { - /* name= */ "PCG32", - /* bits= */ 32, - /* init= */ igraph_rng_pcg32_init, - /* destroy= */ igraph_rng_pcg32_destroy, - /* seed= */ igraph_rng_pcg32_seed, - /* get= */ igraph_rng_pcg32_get, - /* get_int= */ 0, - /* get_real= */ 0, - /* get_norm= */ 0, - /* get_geom= */ 0, - /* get_binom= */ 0, - /* get_exp= */ 0, - /* get_gamma= */ 0, - /* get_pois= */ 0 -}; - -/***** Default RNG, used upon igraph startup *****/ - -#define addr(a) (&a) - -static pcg32_random_t igraph_i_rng_default_state = PCG32_INITIALIZER; - -IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { - addr(igraph_rngtype_pcg32), - addr(igraph_i_rng_default_state), - /* is_seeded = */ true -}; - -#undef addr diff --git a/src/vendor/cigraph/src/random/rng_pcg64.c b/src/vendor/cigraph/src/random/rng_pcg64.c deleted file mode 100644 index e763ff06b87..00000000000 --- a/src/vendor/cigraph/src/random/rng_pcg64.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - 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, see . -*/ - -#include "igraph_random.h" - -#include "igraph_memory.h" -#include "igraph_types.h" - -#include "config.h" - -/* The original implementation of the 64-bit PCG random number generator in this - * file was obtained from https://github.com/imneme/pcg-c - * - * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible - * with igraph's GPLv2 license. License notices for PCG are to be found in the - * pcg_variants.h header - */ - -#if IGRAPH_INTEGER_SIZE == 64 && defined(HAVE___UINT128_T) - -#include "pcg/pcg_variants.h" - -static const pcg64_random_t pcg64_initializer = PCG64_INITIALIZER; - -static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { - pcg64_random_t *state = (pcg64_random_t*) vstate; - return pcg64_random_r(state); -} - -static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { - pcg64_random_t *state = (pcg64_random_t*) vstate; - - if (seed == 0) { - seed = (pcg64_initializer.inc >> 1); - } - - /* PCG64 is seeded by a 128-bit state and a 128-bit sequence number (well, only - * 63 bits are used from the sequence number, though). Since the unified - * igraph RNG seeding interface provides a single igraph_uint_t as the seed, - * we use the seed to fill in the sequence number and use the state from - * PCG64_INITIALIZER */ - pcg64_srandom_r(state, pcg64_initializer.state, seed); - - return IGRAPH_SUCCESS; -} - -static igraph_error_t igraph_rng_pcg64_init(void **state) { - pcg64_random_t *st; - - st = IGRAPH_CALLOC(1, pcg64_random_t); - IGRAPH_CHECK_OOM(st, "Cannot initialize PCG64 RNG."); - (*state) = st; - - igraph_rng_pcg64_seed(st, 0); - - return IGRAPH_SUCCESS; -} - -static void igraph_rng_pcg64_destroy(void *vstate) { - pcg64_random_t *state = (pcg64_random_t*) vstate; - IGRAPH_FREE(state); -} - -#else - -/* Dummy implementation if the compiler does not support __uint128_t */ - -static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { - IGRAPH_UNUSED(vstate); - return 0; -} - -static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { - IGRAPH_UNUSED(vstate); IGRAPH_UNUSED(seed); - IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); -} - -static igraph_error_t igraph_rng_pcg64_init(void **state) { - IGRAPH_UNUSED(state); - IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); -} - -static void igraph_rng_pcg64_destroy(void *vstate) { - IGRAPH_UNUSED(vstate); -} - -#endif - -/** - * \var igraph_rngtype_pcg64 - * \brief The PCG random number generator (64-bit version). - * - * This is an implementation of the PCG random number generator; see - * https://www.pcg-random.org for more details. This implementation returns - * 64 random bits in a single iteration. It is only available on 64-bit plaforms - * with compilers that provide the __uint128_t type. - * - * - * PCG64 typically provides better performance than PCG32 when sampling floating - * point numbers or very large integers, as it can provide twice as many random - * bits in a single generation round. - * - * - * The generator was ported from the original source code published by the - * authors at https://github.com/imneme/pcg-c. - */ - -const igraph_rng_type_t igraph_rngtype_pcg64 = { - /* name= */ "PCG64", - /* bits= */ 64, - /* init= */ igraph_rng_pcg64_init, - /* destroy= */ igraph_rng_pcg64_destroy, - /* seed= */ igraph_rng_pcg64_seed, - /* get= */ igraph_rng_pcg64_get, - /* get_int= */ 0, - /* get_real= */ 0, - /* get_norm= */ 0, - /* get_geom= */ 0, - /* get_binom= */ 0, - /* get_exp= */ 0, - /* get_gamma= */ 0, - /* get_pois= */ 0 -}; diff --git a/src/vendor/cigraph/src/scg/scg.c b/src/vendor/cigraph/src/scg/scg.c new file mode 100644 index 00000000000..ab435d1d269 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_approximate_methods.c b/src/vendor/cigraph/src/scg/scg_approximate_methods.c new file mode 100644 index 00000000000..fe8022d17bd --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_exact_scg.c b/src/vendor/cigraph/src/scg/scg_exact_scg.c new file mode 100644 index 00000000000..6495eb7075e --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_headers.h b/src/vendor/cigraph/src/scg/scg_headers.h new file mode 100644 index 00000000000..876ac312b63 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_kmeans.c b/src/vendor/cigraph/src/scg/scg_kmeans.c new file mode 100644 index 00000000000..067af308957 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_optimal_method.c b/src/vendor/cigraph/src/scg/scg_optimal_method.c new file mode 100644 index 00000000000..68eca3f36e7 --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/scg/scg_utils.c b/src/vendor/cigraph/src/scg/scg_utils.c new file mode 100644 index 00000000000..ed5500d01ca --- /dev/null +++ b/src/vendor/cigraph/src/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/vendor/cigraph/src/version.c b/src/vendor/cigraph/src/version.c index f0bf43917df..72af7ca93f4 100644 --- a/src/vendor/cigraph/src/version.c +++ b/src/vendor/cigraph/src/version.c @@ -1,6 +1,8 @@ +/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2008-2022 The igraph development team + 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 @@ -13,7 +15,10 @@ GNU 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 . + 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" @@ -24,29 +29,32 @@ static const char *igraph_version_string = IGRAPH_VERSION; /** * \function igraph_version - * \brief The version of the igraph C library. + * 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.9.11" or "0.10.0". This - * string must not be modified or deallocated. + * 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.9.11" this is 0. + * 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.9.11" this is 11. + * 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.9.11" this is 11. + * 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 */ -void igraph_version(const char **version_string, - int *major, - int *minor, - int *subminor) { +int igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor) { int i1, i2, i3; - int *p1 = major ? major : &i1; - int *p2 = minor ? minor : &i2; - int *p3 = subminor ? subminor : &i3; + int *p1 = major ? major : &i1, + *p2 = minor ? minor : &i2, + *p3 = subminor ? subminor : &i3; if (version_string) { *version_string = igraph_version_string; @@ -54,4 +62,6 @@ void igraph_version(const char **version_string, *p1 = *p2 = *p3 = 0; sscanf(IGRAPH_VERSION, "%i.%i.%i", p1, p2, p3); + + return 0; } diff --git a/src/vendor/cigraph/vendor/CMakeLists.txt b/src/vendor/cigraph/vendor/CMakeLists.txt index 9befa6fa1ae..de793750494 100644 --- a/src/vendor/cigraph/vendor/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/CMakeLists.txt @@ -3,5 +3,4 @@ add_subdirectory(f2c) add_subdirectory(glpk) add_subdirectory(lapack) add_subdirectory(mini-gmp) -add_subdirectory(pcg) add_subdirectory(plfit) diff --git a/src/vendor/cigraph/vendor/cs/CMakeLists.txt b/src/vendor/cigraph/vendor/cs/CMakeLists.txt index bef000d88c6..e4bccefb6a3 100644 --- a/src/vendor/cigraph/vendor/cs/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/cs/CMakeLists.txt @@ -62,6 +62,8 @@ target_include_directories( PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} ) if (BUILD_SHARED_LIBS) @@ -87,7 +89,7 @@ if (MSVC) else() target_compile_options( cxsparse_vendored PRIVATE - $<$:-Wno-unused-variable> + $<$:-Wno-unused-variable> ) endif() diff --git a/src/vendor/cigraph/vendor/cs/SuiteSparse_config.h b/src/vendor/cigraph/vendor/cs/SuiteSparse_config.h new file mode 100644 index 00000000000..bd0ccedbe8b --- /dev/null +++ b/src/vendor/cigraph/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/vendor/cigraph/vendor/cs/cs.h b/src/vendor/cigraph/vendor/cs/cs.h index a505ca55aa6..0e58521e14f 100644 --- a/src/vendor/cigraph/vendor/cs/cs.h +++ b/src/vendor/cigraph/vendor/cs/cs.h @@ -1,14 +1,3 @@ -/* This is a MODIFIED version of the original CXSparse/Include/cs.h file from - * SuiteSparse 5.12.0 (CXSparse version 3.2.0). The modifications are outlined - * here: - * - * - Dependency on SuiteSparse_long was removed - * - CXSparse is configured to use igraph_integer_t as cs_long_t - * - CXSparse function prefix is set to cs_igraph instead of cs_igraph - * - Unneeded CXSparse function variants are removed - * - * The remaining comments below are from the original cs.h header */ - /* ========================================================================== */ /* CXSparse/Include/cs.h file */ /* ========================================================================== */ @@ -39,7 +28,6 @@ #include "mex.h" #endif -#include "igraph_types.h" #ifdef __cplusplus #ifndef NCOMPLEX @@ -61,9 +49,144 @@ extern "C" { #define CS_COPYRIGHT "Copyright (c) Timothy A. Davis, 2006-2016" #define CXSPARSE -#define cs_long_t igraph_integer_t -#define cs_long_t_id "%" IGRAPH_PRId -#define cs_long_t_max IGRAPH_INTEGER_MAX +#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 */ @@ -71,7 +194,7 @@ extern "C" { /* --- primary CSparse routines and data structures ------------------------- */ -typedef struct cs_igraph_sparse /* matrix in compressed-column or triplet form */ +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 */ @@ -80,35 +203,35 @@ typedef struct cs_igraph_sparse /* matrix in compressed-column or triplet form 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_igraph ; - -cs_igraph *cs_igraph_add (const cs_igraph *A, const cs_igraph *B, double alpha, double beta) ; -cs_long_t cs_igraph_cholsol (cs_long_t order, const cs_igraph *A, double *b) ; -cs_long_t cs_igraph_dupl (cs_igraph *A) ; -cs_long_t cs_igraph_entry (cs_igraph *T, cs_long_t i, cs_long_t j, double x) ; -cs_long_t cs_igraph_lusol (cs_long_t order, const cs_igraph *A, double *b, double tol) ; -cs_long_t cs_igraph_gaxpy (const cs_igraph *A, const double *x, double *y) ; -cs_igraph *cs_igraph_multiply (const cs_igraph *A, const cs_igraph *B) ; -cs_long_t cs_igraph_qrsol (cs_long_t order, const cs_igraph *A, double *b) ; -cs_igraph *cs_igraph_transpose (const cs_igraph *A, cs_long_t values) ; -cs_igraph *cs_igraph_compress (const cs_igraph *T) ; -double cs_igraph_norm (const cs_igraph *A) ; -/*cs_long_t cs_igraph_print (const cs_igraph *A, cs_long_t brief) ;*/ -cs_igraph *cs_igraph_load (FILE *f) ; +} 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_igraph_calloc (cs_long_t n, size_t size) ; -void *cs_igraph_free (void *p) ; -void *cs_igraph_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; -cs_igraph *cs_igraph_spalloc (cs_long_t m, cs_long_t n, cs_long_t nzmax, cs_long_t values, +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_igraph *cs_igraph_spfree (cs_igraph *A) ; -cs_long_t cs_igraph_sprealloc (cs_igraph *A, cs_long_t nzmax) ; -void *cs_igraph_malloc (cs_long_t n, size_t size) ; +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_igraph_symbolic /* symbolic Cholesky, LU, or QR analysis */ +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 */ @@ -118,17 +241,17 @@ typedef struct cs_igraph_symbolic /* symbolic Cholesky, LU, or QR analysis */ 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_igraphs ; +} cs_dls ; -typedef struct cs_igraph_numeric /* numeric Cholesky, LU, or QR factorization */ +typedef struct cs_dl_numeric /* numeric Cholesky, LU, or QR factorization */ { - cs_igraph *L ; /* L for LU and Cholesky, V for QR */ - cs_igraph *U ; /* U for LU, r for QR, not used for Cholesky */ + 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_igraphn ; +} cs_dln ; -typedef struct cs_igraph_dmperm_results /* cs_igraph_dmperm or cs_igraph_scc output */ +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 */ @@ -137,86 +260,395 @@ typedef struct cs_igraph_dmperm_results /* cs_igraph_dmperm or cs_igraph_scc 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_igraphd ; - -cs_long_t *cs_igraph_amd (cs_long_t order, const cs_igraph *A) ; -cs_igraphn *cs_igraph_chol (const cs_igraph *A, const cs_igraphs *S) ; -cs_igraphd *cs_igraph_dmperm (const cs_igraph *A, cs_long_t seed) ; -cs_long_t cs_igraph_droptol (cs_igraph *A, double tol) ; -cs_long_t cs_igraph_dropzeros (cs_igraph *A) ; -cs_long_t cs_igraph_happly (const cs_igraph *V, cs_long_t i, double beta, double *x) ; -cs_long_t cs_igraph_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; -cs_long_t cs_igraph_lsolve (const cs_igraph *L, double *x) ; -cs_long_t cs_igraph_ltsolve (const cs_igraph *L, double *x) ; -cs_igraphn *cs_igraph_lu (const cs_igraph *A, const cs_igraphs *S, double tol) ; -cs_igraph *cs_igraph_permute (const cs_igraph *A, const cs_long_t *pinv, const cs_long_t *q, +} 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_igraph_pinv (const cs_long_t *p, cs_long_t n) ; -cs_long_t cs_igraph_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; -cs_igraphn *cs_igraph_qr (const cs_igraph *A, const cs_igraphs *S) ; -cs_igraphs *cs_igraph_schol (cs_long_t order, const cs_igraph *A) ; -cs_igraphs *cs_igraph_sqr (cs_long_t order, const cs_igraph *A, cs_long_t qr) ; -cs_igraph *cs_igraph_symperm (const cs_igraph *A, const cs_long_t *pinv, cs_long_t values) ; -cs_long_t cs_igraph_usolve (const cs_igraph *U, double *x) ; -cs_long_t cs_igraph_utsolve (const cs_igraph *U, double *x) ; -cs_long_t cs_igraph_updown (cs_igraph *L, cs_long_t sigma, const cs_igraph *C, +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_igraphs *cs_igraph_sfree (cs_igraphs *S) ; -cs_igraphn *cs_igraph_nfree (cs_igraphn *N) ; -cs_igraphd *cs_igraph_dfree (cs_igraphd *D) ; +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_igraph_counts (const cs_igraph *A, const cs_long_t *parent, +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_igraph_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; -cs_long_t cs_igraph_dfs (cs_long_t j, cs_igraph *G, cs_long_t top, cs_long_t *xi, +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_igraph_etree (const cs_igraph *A, cs_long_t ata) ; -cs_long_t cs_igraph_fkeep (cs_igraph *A, +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_igraph_house (double *x, double *beta, cs_long_t n) ; -cs_long_t *cs_igraph_maxtrans (const cs_igraph *A, cs_long_t seed) ; -cs_long_t *cs_igraph_post (const cs_long_t *parent, cs_long_t n) ; -cs_igraphd *cs_igraph_scc (cs_igraph *A) ; -cs_long_t cs_igraph_scatter (const cs_igraph *A, cs_long_t j, double beta, cs_long_t *w, - double *x, cs_long_t mark,cs_igraph *C, cs_long_t nz) ; -cs_long_t cs_igraph_tdfs (cs_long_t j, cs_long_t k, cs_long_t *head, const cs_long_t *next, +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_igraph_leaf (cs_long_t i, cs_long_t j, const cs_long_t *first, +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_igraph_reach (cs_igraph *G, const cs_igraph *B, cs_long_t k, cs_long_t *xi, +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_igraph_spsolve (cs_igraph *L, const cs_igraph *B, cs_long_t k, cs_long_t *xi, +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_igraph_ereach (const cs_igraph *A, cs_long_t k, const cs_long_t *parent, +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_igraph_randperm (cs_long_t n, cs_long_t seed) ; +cs_long_t *cs_cl_randperm (cs_long_t n, cs_long_t seed) ; /* utilities */ -cs_igraphd *cs_igraph_dalloc (cs_long_t m, cs_long_t n) ; -cs_igraph *cs_igraph_done (cs_igraph *C, void *w, void *x, cs_long_t ok) ; -cs_long_t *cs_igraph_idone (cs_long_t *p, cs_igraph *C, void *w, cs_long_t ok) ; -cs_igraphn *cs_igraph_ndone (cs_igraphn *N, cs_igraph *C, void *w, void *x, cs_long_t ok) ; -cs_igraphd *cs_igraph_ddone (cs_igraphd *D, cs_igraph *C, void *w, cs_long_t ok) ; +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_igraph ## nm -#define cs cs_igraph +#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)) @@ -309,6 +741,17 @@ cs_igraphd *cs_igraph_ddone (cs_igraphd *D, cs_igraph *C, void *w, cs_long_t ok) #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 diff --git a/src/vendor/cigraph/vendor/f2c/CMakeLists.txt b/src/vendor/cigraph/vendor/f2c/CMakeLists.txt index 63db5d68cad..304ef005038 100644 --- a/src/vendor/cigraph/vendor/f2c/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/f2c/CMakeLists.txt @@ -15,15 +15,13 @@ set( if(F2C_EXTERNAL_ARITH_HEADER) configure_file(${F2C_EXTERNAL_ARITH_HEADER} arith.h COPYONLY) else() - if (CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) + if (CMAKE_CROSSCOMPILING) # Warn only, as in some circumstances, such as macOS with Rosetta, - # arithchk can still be run through emulation and the build with not fail. + # arithchk can be run through emulation and the build with not fail. message(WARNING - "Cross-compiling with internal ARPACK, BLAS or LAPACK, but " - "F2C_EXTERNAL_ARITH_HEADER was not set and no cross-compiling " - "emulator was provided in CMAKE_CROSSCOMPILING_EMULATOR either. " - "The build is likely to fail. See igraph's installation instructions " - "for more information.") + "Cross-compiling with internal ARPACK, BLAS or LAPACK, " + "but F2C_EXTERNAL_ARITH_HEADER was not set. The build is likely to fail. " + "See igraph's installation instructions for more information.") endif() add_custom_command( OUTPUT arith.h @@ -105,10 +103,6 @@ target_include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) -# Since these are included as object files, they should call the -# function as is (without visibility specification) -target_compile_definitions(f2c_vendored PRIVATE IGRAPH_STATIC) - if (WIN32) target_compile_definitions(f2c_vendored PRIVATE MSDOS) endif() @@ -117,7 +111,7 @@ if (MSVC) target_include_directories( f2c_vendored PUBLIC - ${PROJECT_SOURCE_DIR}/msvc/include + ${PROJECT_SOURCE_DIR}/msvc/include ) endif() @@ -134,11 +128,11 @@ if(MSVC) ) else() target_compile_options(arithchk PRIVATE - $<$:-Wno-format-zero-length> + $<$:-Wno-format-zero-length> ) target_compile_options( f2c_vendored PRIVATE - $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration -Wno-format-zero-length> + $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration -Wno-format-zero-length> $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration> ) endif() diff --git a/src/vendor/cigraph/vendor/f2c/exit_.c b/src/vendor/cigraph/vendor/f2c/exit_.c index dd5793a1a3e..08e9d07067c 100644 --- a/src/vendor/cigraph/vendor/f2c/exit_.c +++ b/src/vendor/cigraph/vendor/f2c/exit_.c @@ -33,7 +33,7 @@ exit_(integer *rc) #ifdef NO_ONEXIT f_exit(); #endif - IGRAPH_FATAL("exit_() called from f2c code"); + exit(*rc); } #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/main.c b/src/vendor/cigraph/vendor/f2c/main.c new file mode 100644 index 00000000000..d95fdc92cbc --- /dev/null +++ b/src/vendor/cigraph/vendor/f2c/main.c @@ -0,0 +1,148 @@ +/* STARTUP PROCEDURE FOR UNIX FORTRAN PROGRAMS */ + +#include "stdio.h" +#include "signal1.h" + +#ifndef SIGIOT +#ifdef SIGABRT +#define SIGIOT SIGABRT +#endif +#endif + +#ifndef KR_headers +#undef VOID +#include "stdlib.h" +#ifdef __cplusplus +extern "C" { +#endif +#endif + +#ifndef VOID +#define VOID void +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef NO__STDC +#define ONEXIT onexit +extern VOID f_exit(); +#else +#ifndef KR_headers +extern void f_exit(void); +#ifndef NO_ONEXIT +#define ONEXIT atexit +extern int atexit(void (*)(void)); +#endif +#else +#ifndef NO_ONEXIT +#define ONEXIT onexit +extern VOID f_exit(); +#endif +#endif +#endif + +#ifdef KR_headers +extern VOID f_init(), sig_die(); +extern int MAIN__(); +#define Int /* int */ +#else +extern void f_init(void), sig_die(const char*, int); +extern int MAIN__(void); +#define Int int +#endif + +static VOID sigfdie(Sigarg) +{ +Use_Sigarg; +sig_die("Floating Exception", 1); +} + + +static VOID sigidie(Sigarg) +{ +Use_Sigarg; +sig_die("IOT Trap", 1); +} + +#ifdef SIGQUIT +static VOID sigqdie(Sigarg) +{ +Use_Sigarg; +sig_die("Quit signal", 1); +} +#endif + + +static VOID sigindie(Sigarg) +{ +Use_Sigarg; +sig_die("Interrupt", 0); +} + +static VOID sigtdie(Sigarg) +{ +Use_Sigarg; +sig_die("Killed", 0); +} + +#ifdef SIGTRAP +static VOID sigtrdie(Sigarg) +{ +Use_Sigarg; +sig_die("Trace trap", 1); +} +#endif + + +int xargc; +char **xargv; + +#ifdef __cplusplus + } +#endif + + int +#ifdef KR_headers +main(argc, argv) int argc; char **argv; +#else +main(int argc, char **argv) +#endif +{ +xargc = argc; +xargv = argv; +signal1(SIGFPE, sigfdie); /* ignore underflow, enable overflow */ +#ifdef SIGIOT +signal1(SIGIOT, sigidie); +#endif +#ifdef SIGTRAP +signal1(SIGTRAP, sigtrdie); +#endif +#ifdef SIGQUIT +if(signal1(SIGQUIT,sigqdie) == SIG_IGN) + signal1(SIGQUIT, SIG_IGN); +#endif +if(signal1(SIGINT, sigindie) == SIG_IGN) + signal1(SIGINT, SIG_IGN); +signal1(SIGTERM,sigtdie); + +#ifdef pdp11 + ldfps(01200); /* detect overflow as an exception */ +#endif + +f_init(); +#ifndef NO_ONEXIT +ONEXIT(f_exit); +#endif +MAIN__(); +#ifdef NO_ONEXIT +f_exit(); +#endif +exit(0); /* exit(0) rather than return(0) to bypass Cray bug */ +return 0; /* For compilers that complain of missing return values; */ + /* others will complain that this is unreachable code. */ +} +#ifdef __cplusplus +} +#endif diff --git a/src/vendor/cigraph/vendor/f2c/s_paus.c b/src/vendor/cigraph/vendor/f2c/s_paus.c index 44ea4b62b96..51d80eb0878 100644 --- a/src/vendor/cigraph/vendor/f2c/s_paus.c +++ b/src/vendor/cigraph/vendor/f2c/s_paus.c @@ -39,8 +39,6 @@ s_1paus(fin) FILE *fin; s_1paus(FILE *fin) #endif { - IGRAPH_FATAL("s_1paus() called from f2c code"); - /* fprintf(stderr, "To resume execution, type go. Other input will terminate the job.\n"); fflush(stderr); @@ -51,7 +49,6 @@ s_1paus(FILE *fin) #endif exit(0); } - */ } int @@ -61,8 +58,6 @@ s_paus(s, n) char *s; ftnlen n; s_paus(char *s, ftnlen n) #endif { - IGRAPH_FATAL("s_paus() called from f2c code"); - /* fprintf(stderr, "PAUSE "); if(n > 0) fprintf(stderr, " %.*s", (int)n, s); @@ -91,7 +86,6 @@ s_paus(char *s, ftnlen n) } fprintf(stderr, "Execution resumes after PAUSE.\n"); fflush(stderr); - */ return 0; /* NOT REACHED */ #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/s_stop.c b/src/vendor/cigraph/vendor/f2c/s_stop.c index aa4cf4642fb..68233aea7e3 100644 --- a/src/vendor/cigraph/vendor/f2c/s_stop.c +++ b/src/vendor/cigraph/vendor/f2c/s_stop.c @@ -20,8 +20,6 @@ void f_exit(void); int s_stop(char *s, ftnlen n) #endif { - IGRAPH_FATAL("STOP statement executed from f2c code"); - /* int i; if(n > 0) @@ -35,7 +33,6 @@ if(n > 0) f_exit(); #endif exit(0); -*/ /* We cannot avoid (useless) compiler diagnostics here: */ /* some compilers complain if there is no return statement, */ diff --git a/src/vendor/cigraph/vendor/f2c/sig_die.c b/src/vendor/cigraph/vendor/f2c/sig_die.c index 19b72da99de..63a73d91183 100644 --- a/src/vendor/cigraph/vendor/f2c/sig_die.c +++ b/src/vendor/cigraph/vendor/f2c/sig_die.c @@ -1,8 +1,6 @@ #include "stdio.h" #include "signal.h" -#include "igraph_error.h" - #ifndef SIGIOT #ifdef SIGABRT #define SIGIOT SIGABRT @@ -36,13 +34,14 @@ void sig_die(const char *s, int kill) #ifdef SIGIOT signal(SIGIOT, SIG_DFL); #endif + abort(); } else { #ifdef NO_ONEXIT f_exit(); #endif + exit(1); } - IGRAPH_FATAL("sig_die() called from f2c code"); } #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/uninit.c b/src/vendor/cigraph/vendor/f2c/uninit.c index ba495a92053..82b62429fee 100644 --- a/src/vendor/cigraph/vendor/f2c/uninit.c +++ b/src/vendor/cigraph/vendor/f2c/uninit.c @@ -8,8 +8,6 @@ #include #include "arith.h" -#include "igraph_error.h" - #define TYSHORT 2 #define TYLONG 3 #define TYREAL 4 @@ -67,8 +65,9 @@ double _0 = 0.; void unsupported_error() { - IGRAPH_FATAL("Runtime Error: Your Architecture is not supported by the" - " -trapuv option of f2c"); + fprintf(stderr,"Runtime Error: Your Architecture is not supported by the" + " -trapuv option of f2c\n"); + exit(-1); } @@ -218,7 +217,7 @@ ieeeuserhand(unsigned exception[5], int val[2]) else if(exception[0]==_INVALID) fprintf(stderr,"invalid operation\n"); else fprintf(stderr,"\tunknown reason\n"); fflush(stderr); - IGRAPH_FATAL("ieee0() aborting"); + abort(); } static void @@ -229,7 +228,7 @@ ieeeuserhand2(unsigned int **j) #endif { fprintf(stderr,"ieee0() aborting because of confusion\n"); - IGRAPH_FATAL("ieee0() aborting"); + abort(); } static void diff --git a/src/vendor/cigraph/vendor/glpk/CMakeLists.txt b/src/vendor/cigraph/vendor/glpk/CMakeLists.txt index 9e27d977980..212068fa471 100644 --- a/src/vendor/cigraph/vendor/glpk/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/glpk/CMakeLists.txt @@ -106,6 +106,6 @@ if (MSVC) else() target_compile_options(glpk_vendored PRIVATE $<$:-wd161 -Wno-return-type> - $<$:-Wno-return-type -Wno-unused-value -Wno-dangling-else -Wno-logical-op-parentheses> + $<$:-Wno-return-type -Wno-unused-value -Wno-dangling-else -Wno-logical-op-parentheses> ) endif() diff --git a/src/vendor/cigraph/vendor/lapack/CMakeLists.txt b/src/vendor/cigraph/vendor/lapack/CMakeLists.txt index a0ff3c1b594..d2602f18457 100644 --- a/src/vendor/cigraph/vendor/lapack/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/lapack/CMakeLists.txt @@ -72,9 +72,9 @@ endif() # mess around with the source of lapack too much to fix these if(NOT MSVC) target_compile_options(blas_vendored PRIVATE - $<$:-Wno-logical-op-parentheses> + $<$:-Wno-logical-op-parentheses> ) target_compile_options(lapack_vendored PRIVATE - $<$:-Wno-logical-op-parentheses -Wno-shift-op-parentheses> + $<$:-Wno-logical-op-parentheses -Wno-shift-op-parentheses> ) endif() diff --git a/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt b/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt index 8de182ecf7a..ee8d8a95e2b 100644 --- a/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt @@ -31,7 +31,7 @@ if(MSVC) else() target_compile_options( gmp_vendored PRIVATE - $<$:-Wno-unused-variable> + $<$:-Wno-unused-variable> ) endif() diff --git a/src/vendor/cigraph/vendor/pcg/CMakeLists.txt b/src/vendor/cigraph/vendor/pcg/CMakeLists.txt deleted file mode 100644 index c106f5e1c1b..00000000000 --- a/src/vendor/cigraph/vendor/pcg/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# Declare the files needed to compile our vendored copy of the PCG random -# number generator -add_library( - pcg - OBJECT - EXCLUDE_FROM_ALL - pcg-advance-64.c - pcg-advance-128.c - pcg-output-32.c - pcg-output-64.c - pcg-output-128.c - pcg-rngs-64.c - pcg-rngs-128.c -) - -if (BUILD_SHARED_LIBS) - set_property(TARGET pcg PROPERTY POSITION_INDEPENDENT_CODE ON) -endif() - -use_all_warnings(pcg) - diff --git a/src/vendor/cigraph/vendor/pcg/LICENSE.txt b/src/vendor/cigraph/vendor/pcg/LICENSE.txt deleted file mode 100644 index 23159603635..00000000000 --- a/src/vendor/cigraph/vendor/pcg/LICENSE.txt +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c b/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c deleted file mode 100644 index eceb90e431f..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * Repetative C code is derived using C preprocessor metaprogramming - * techniques. - */ - -#include "pcg_variants.h" - -/* Multi-step advance functions (jump-ahead, jump-back) - * - * The method used here is based on Brown, "Random Number Generation - * with Arbitrary Stride,", Transactions of the American Nuclear - * Society (Nov. 1994). The algorithm is very similar to fast - * exponentiation. - * - * Even though delta is an unsigned integer, we can pass a - * signed integer to go backwards, it just goes "the long way round". - */ - -#if PCG_HAS_128BIT_OPS -pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, - pcg128_t cur_plus) -{ - pcg128_t acc_mult = 1u; - pcg128_t acc_plus = 0u; - while (delta > 0) { - if (delta & 1) { - acc_mult *= cur_mult; - acc_plus = acc_plus * cur_mult + cur_plus; - } - cur_plus = (cur_mult + 1) * cur_plus; - cur_mult *= cur_mult; - delta /= 2; - } - return acc_mult * state + acc_plus; -} -#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c b/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c deleted file mode 100644 index ecd05deac94..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * Repetative C code is derived using C preprocessor metaprogramming - * techniques. - */ - -#include "pcg_variants.h" - -/* Multi-step advance functions (jump-ahead, jump-back) - * - * The method used here is based on Brown, "Random Number Generation - * with Arbitrary Stride,", Transactions of the American Nuclear - * Society (Nov. 1994). The algorithm is very similar to fast - * exponentiation. - * - * Even though delta is an unsigned integer, we can pass a - * signed integer to go backwards, it just goes "the long way round". - */ - -uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, uint64_t cur_mult, - uint64_t cur_plus) -{ - uint64_t acc_mult = 1u; - uint64_t acc_plus = 0u; - while (delta > 0) { - if (delta & 1) { - acc_mult *= cur_mult; - acc_plus = acc_plus * cur_mult + cur_plus; - } - cur_plus = (cur_mult + 1) * cur_plus; - cur_mult *= cur_mult; - delta /= 2; - } - return acc_mult * state + acc_plus; -} diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-128.c b/src/vendor/cigraph/vendor/pcg/pcg-output-128.c deleted file mode 100644 index 39554991987..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-output-128.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * The contents of this file were mechanically derived from pcg_variants.h - * (every inline function defined there gets a generated extern declaration). - */ - -#include "pcg_variants.h" - -/* - * Rotate helper functions. - */ - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot); -#endif - -/* - * Output functions. These are the core of the PCG generation scheme. - */ - -/* XSH RS */ - -/* XSH RR */ - -/* RXS M XS */ - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state); -#endif - -/* RXS M */ - -/* XSL RR (only defined for >= 64 bits) */ - -/* XSL RR RR (only defined for >= 64 bits) */ - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state); -#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-32.c b/src/vendor/cigraph/vendor/pcg/pcg-output-32.c deleted file mode 100644 index dfe0cc5dae7..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-output-32.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * The contents of this file were mechanically derived from pcg_variants.h - * (every inline function defined there gets a generated extern declaration). - */ - -#include "pcg_variants.h" - -/* - * Rotate helper functions. - */ - -extern inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot); - -/* - * Output functions. These are the core of the PCG generation scheme. - */ - -/* XSH RS */ - -extern inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state); - -/* XSH RR */ - -extern inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state); - -/* RXS M XS */ - -extern inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state); - -/* RXS M */ - -extern inline uint32_t pcg_output_rxs_m_64_32(uint64_t state); - -/* XSL RR (only defined for >= 64 bits) */ - -extern inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state); - -/* XSL RR RR (only defined for >= 64 bits) */ diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-64.c b/src/vendor/cigraph/vendor/pcg/pcg-output-64.c deleted file mode 100644 index ba3598d5433..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-output-64.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * The contents of this file were mechanically derived from pcg_variants.h - * (every inline function defined there gets a generated extern declaration). - */ - -#include "pcg_variants.h" - -/* - * Rotate helper functions. - */ - -extern inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot); - -/* - * Output functions. These are the core of the PCG generation scheme. - */ - -/* XSH RS */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state); -#endif - -/* XSH RR */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state); -#endif - -/* RXS M XS */ - -extern inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state); - -/* RXS M */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state); -#endif - -/* XSL RR (only defined for >= 64 bits) */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state); -#endif - -/* XSL RR RR (only defined for >= 64 bits) */ - -extern inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state); diff --git a/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c b/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c deleted file mode 100644 index 6f8797f06bc..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * The contents of this file were mechanically derived from pcg_variants.h - * (every inline function defined there gets a generated extern declaration). - */ - -#include "pcg_variants.h" - -/* Functions to advance the underlying LCG, one version for each size and - * each style. These functions are considered semi-private. There is rarely - * a good reason to call them directly. - */ - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, - pcg128_t delta); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_mcg_128_step_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, - pcg128_t delta); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_unique_128_step_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, - pcg128_t delta); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, - pcg128_t delta); -#endif - -/* Functions to seed the RNG state, one version for each size and each - * style. Unlike the step functions, regular users can and should call - * these functions. - */ - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, - pcg128_t initstate); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, - pcg128_t initstate); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, - pcg128_t initstate); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, - pcg128_t initstate, - pcg128_t initseq); -#endif - -/* Now, finally we create each of the individual generators. We provide - * a random_r function that provides a random number of the appropriate - * type (using the full range of the type) and a boundedrand_r version - * that provides - * - * Implementation notes for boundedrand_r: - * - * To avoid bias, we need to make the range of the RNG a multiple of - * bound, which we do by dropping output less than a threshold. - * Let's consider a 32-bit case... A naive scheme to calculate the - * threshold would be to do - * - * uint32_t threshold = 0x100000000ull % bound; - * - * but 64-bit div/mod is slower than 32-bit div/mod (especially on - * 32-bit platforms). In essence, we do - * - * uint32_t threshold = (0x100000000ull-bound) % bound; - * - * because this version will calculate the same modulus, but the LHS - * value is less than 2^32. - * - * (Note that using modulo is only wise for good RNGs, poorer RNGs - * such as raw LCGs do better using a technique based on division.) - * Empirical tests show that division is preferable to modulus for - * reducing the range of an RNG. It's faster, and sometimes it can - * even be statistically prefereable. - */ - -/* Generation functions for XSH RS */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); -#endif - -/* Generation functions for XSH RR */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); -#endif - -/* Generation functions for RXS M XS (no MCG versions because they - * don't make sense when you want to use the entire state) - */ - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, - pcg128_t bound); -#endif - -/* Generation functions for RXS M */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); -#endif - -/* Generation functions for XSL RR (only defined for "large" types) */ - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline uint64_t -pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); -#endif - -/* Generation functions for XSL RR RR (only defined for "large" types) */ - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng); -#endif - -#if PCG_HAS_128BIT_OPS -extern inline pcg128_t -pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, - pcg128_t bound); -#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c b/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c deleted file mode 100644 index a22df3116dd..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * The contents of this file were mechanically derived from pcg_variants.h - * (every inline function defined there gets a generated extern declaration). - */ - -#include "pcg_variants.h" - -/* Functions to advance the underlying LCG, one version for each size and - * each style. These functions are considered semi-private. There is rarely - * a good reason to call them directly. - */ - -extern inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng); - -extern inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, - uint64_t delta); - -extern inline void pcg_mcg_64_step_r(struct pcg_state_64* rng); - -extern inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, - uint64_t delta); - -extern inline void pcg_unique_64_step_r(struct pcg_state_64* rng); - -extern inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, - uint64_t delta); - -extern inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng); - -extern inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, - uint64_t delta); - -/* Functions to seed the RNG state, one version for each size and each - * style. Unlike the step functions, regular users can and should call - * these functions. - */ - -extern inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, - uint64_t initstate); - -extern inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, - uint64_t initstate); - -extern inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, - uint64_t initstate); - -extern inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, - uint64_t initstate, - uint64_t initseq); - -/* Now, finally we create each of the individual generators. We provide - * a random_r function that provides a random number of the appropriate - * type (using the full range of the type) and a boundedrand_r version - * that provides - * - * Implementation notes for boundedrand_r: - * - * To avoid bias, we need to make the range of the RNG a multiple of - * bound, which we do by dropping output less than a threshold. - * Let's consider a 32-bit case... A naive scheme to calculate the - * threshold would be to do - * - * uint32_t threshold = 0x100000000ull % bound; - * - * but 64-bit div/mod is slower than 32-bit div/mod (especially on - * 32-bit platforms). In essence, we do - * - * uint32_t threshold = (0x100000000ull-bound) % bound; - * - * because this version will calculate the same modulus, but the LHS - * value is less than 2^32. - * - * (Note that using modulo is only wise for good RNGs, poorer RNGs - * such as raw LCGs do better using a technique based on division.) - * Empirical tests show that division is preferable to modulus for - * reducing the range of an RNG. It's faster, and sometimes it can - * even be statistically prefereable. - */ - -/* Generation functions for XSH RS */ - -extern inline uint32_t -pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint32_t -pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound); - -extern inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -/* Generation functions for XSH RR */ - -extern inline uint32_t -pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint32_t -pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound); - -extern inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -/* Generation functions for RXS M XS (no MCG versions because they - * don't make sense when you want to use the entire state) - */ - -extern inline uint64_t -pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); - -extern inline uint64_t -pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound); - -extern inline uint64_t -pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); - -extern inline uint64_t -pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound); - -extern inline uint64_t -pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint64_t -pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, - uint64_t bound); - -/* Generation functions for RXS M */ - -extern inline uint32_t -pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint32_t -pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound); - -extern inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -/* Generation functions for XSL RR (only defined for "large" types) */ - -extern inline uint32_t -pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -extern inline uint32_t -pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint32_t -pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound); - -extern inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng); - -extern inline uint32_t -pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); - -/* Generation functions for XSL RR RR (only defined for "large" types) */ - -extern inline uint64_t -pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); - -extern inline uint64_t -pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound); - -extern inline uint64_t -pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); - -extern inline uint64_t -pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound); - -extern inline uint64_t -pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng); - -extern inline uint64_t -pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, - uint64_t bound); diff --git a/src/vendor/cigraph/vendor/pcg/pcg_variants.h b/src/vendor/cigraph/vendor/pcg/pcg_variants.h deleted file mode 100644 index 25ca2e71366..00000000000 --- a/src/vendor/cigraph/vendor/pcg/pcg_variants.h +++ /dev/null @@ -1,2557 +0,0 @@ -/* - * PCG Random Number Generation for C. - * - * Copyright 2014-2019 Melissa O'Neill , - * and the PCG Project contributors. - * - * SPDX-License-Identifier: (Apache-2.0 OR MIT) - * - * Licensed under the Apache License, Version 2.0 (provided in - * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) - * or under the MIT license (provided in LICENSE-MIT.txt and at - * http://opensource.org/licenses/MIT), at your option. This file may not - * be copied, modified, or distributed except according to those terms. - * - * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either - * express or implied. See your chosen license for details. - * - * For additional information about the PCG random number generation scheme, - * visit http://www.pcg-random.org/. - */ - -/* - * This code is derived from the canonical C++ PCG implementation, which - * has many additional features and is preferable if you can use C++ in - * your project. - * - * Much of the derivation was performed mechanically. In particular, the - * output functions were generated by compiling the C++ output functions - * into LLVM bitcode and then transforming that using the LLVM C backend - * (from https://github.com/draperlaboratory/llvm-cbe), and then - * postprocessing and hand editing the output. - * - * Much of the remaining code was generated by C-preprocessor metaprogramming. - */ - -#ifndef PCG_VARIANTS_H_INCLUDED -#define PCG_VARIANTS_H_INCLUDED 1 - -#include - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable:4146) /* "unary minus operator applied to unsigned type, result still unsigned" */ -#endif - -#if __SIZEOF_INT128__ - typedef __uint128_t pcg128_t; - #define PCG_128BIT_CONSTANT(high,low) \ - ((((pcg128_t)high) << 64) + low) - #define PCG_HAS_128BIT_OPS 1 -#endif - -/* Checking for !__GNUC_STDC_INLINE__ is a hack to work around a bug in the - * Intel compiler where it defined both __GNUC_GNU_INLINE__ and __GNUC_STDC_INLINE__ - * to 1 when using -std=gnu99. igraph is always compiled with -std=gnu99. - * - * Tested with icc (ICC) 2021.3.0 20210609 on Linux */ -#if __GNUC_GNU_INLINE__ && !__GNUC_STDC_INLINE__ && !defined(__cplusplus) - #error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. - /* We could instead use macros PCG_INLINE and PCG_EXTERN_INLINE - but better to just reject ancient C code. */ -#endif - -#if __cplusplus -extern "C" { -#endif - -/* - * Rotate helper functions. - */ - -inline uint8_t pcg_rotr_8(uint8_t value, unsigned int rot) -{ -/* Unfortunately, clang is kinda pathetic when it comes to properly - * recognizing idiomatic rotate code, so for clang we actually provide - * assembler directives (enabled with PCG_USE_INLINE_ASM). Boo, hiss. - */ -#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) - asm ("rorb %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); - return value; -#else - return (value >> rot) | (value << ((- rot) & 7)); -#endif -} - -inline uint16_t pcg_rotr_16(uint16_t value, unsigned int rot) -{ -#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) - asm ("rorw %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); - return value; -#else - return (value >> rot) | (value << ((- rot) & 15)); -#endif -} - -inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) -{ -#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) - asm ("rorl %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); - return value; -#else - return (value >> rot) | (value << ((- rot) & 31)); -#endif -} - -inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) -{ -#if 0 && PCG_USE_INLINE_ASM && __clang__ && __x86_64__ - /* For whatever reason, clang actually *does* generate rotq by - itself, so we don't need this code. */ - asm ("rorq %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); - return value; -#else - return (value >> rot) | (value << ((- rot) & 63)); -#endif -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot) -{ - return (value >> rot) | (value << ((- rot) & 127)); -} -#endif - -/* - * Output functions. These are the core of the PCG generation scheme. - */ - -/* XSH RS */ - -inline uint8_t pcg_output_xsh_rs_16_8(uint16_t state) -{ - return (uint8_t)(((state >> 7u) ^ state) >> ((state >> 14u) + 3u)); -} - -inline uint16_t pcg_output_xsh_rs_32_16(uint32_t state) -{ - return (uint16_t)(((state >> 11u) ^ state) >> ((state >> 30u) + 11u)); -} - -inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state) -{ - - return (uint32_t)(((state >> 22u) ^ state) >> ((state >> 61u) + 22u)); -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state) -{ - return (uint64_t)(((state >> 43u) ^ state) >> ((state >> 124u) + 45u)); -} -#endif - -/* XSH RR */ - -inline uint8_t pcg_output_xsh_rr_16_8(uint16_t state) -{ - return pcg_rotr_8(((state >> 5u) ^ state) >> 5u, state >> 13u); -} - -inline uint16_t pcg_output_xsh_rr_32_16(uint32_t state) -{ - return pcg_rotr_16(((state >> 10u) ^ state) >> 12u, state >> 28u); -} - -inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state) -{ - return pcg_rotr_32(((state >> 18u) ^ state) >> 27u, state >> 59u); -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state) -{ - return pcg_rotr_64(((state >> 35u) ^ state) >> 58u, state >> 122u); -} -#endif - -/* RXS M XS */ - -inline uint8_t pcg_output_rxs_m_xs_8_8(uint8_t state) -{ - uint8_t word = ((state >> ((state >> 6u) + 2u)) ^ state) * 217u; - return (word >> 6u) ^ word; -} - -inline uint16_t pcg_output_rxs_m_xs_16_16(uint16_t state) -{ - uint16_t word = ((state >> ((state >> 13u) + 3u)) ^ state) * 62169u; - return (word >> 11u) ^ word; -} - -inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state) -{ - uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; - return (word >> 22u) ^ word; -} - -inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state) -{ - uint64_t word = ((state >> ((state >> 59u) + 5u)) ^ state) - * 12605985483714917081ull; - return (word >> 43u) ^ word; -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state) -{ - pcg128_t word = ((state >> ((state >> 122u) + 6u)) ^ state) - * (PCG_128BIT_CONSTANT(17766728186571221404ULL, - 12605985483714917081ULL)); - /* 327738287884841127335028083622016905945 */ - return (word >> 86u) ^ word; -} -#endif - -/* RXS M */ - -inline uint8_t pcg_output_rxs_m_16_8(uint16_t state) -{ - return (((state >> ((state >> 13u) + 3u)) ^ state) * 62169u) >> 8u; -} - -inline uint16_t pcg_output_rxs_m_32_16(uint32_t state) -{ - return (((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u) >> 16u; -} - -inline uint32_t pcg_output_rxs_m_64_32(uint64_t state) -{ - return (((state >> ((state >> 59u) + 5u)) ^ state) - * 12605985483714917081ull) >> 32u; -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state) -{ - return (((state >> ((state >> 122u) + 6u)) ^ state) - * (PCG_128BIT_CONSTANT(17766728186571221404ULL, - 12605985483714917081ULL))) >> 64u; - /* 327738287884841127335028083622016905945 */ -} -#endif - -/* XSL RR (only defined for >= 64 bits) */ - -inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state) -{ - return pcg_rotr_32(((uint32_t)(state >> 32u)) ^ (uint32_t)state, - state >> 59u); -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) -{ - return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, - state >> 122u); -} -#endif - -/* XSL RR RR (only defined for >= 64 bits) */ - -inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state) -{ - uint32_t rot1 = (uint32_t)(state >> 59u); - uint32_t high = (uint32_t)(state >> 32u); - uint32_t low = (uint32_t)state; - uint32_t xored = high ^ low; - uint32_t newlow = pcg_rotr_32(xored, rot1); - uint32_t newhigh = pcg_rotr_32(high, newlow & 31u); - return (((uint64_t)newhigh) << 32u) | newlow; -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state) -{ - uint32_t rot1 = (uint32_t)(state >> 122u); - uint64_t high = (uint64_t)(state >> 64u); - uint64_t low = (uint64_t)state; - uint64_t xored = high ^ low; - uint64_t newlow = pcg_rotr_64(xored, rot1); - uint64_t newhigh = pcg_rotr_64(high, newlow & 63u); - return (((pcg128_t)newhigh) << 64u) | newlow; -} -#endif - -#define PCG_DEFAULT_MULTIPLIER_8 141U -#define PCG_DEFAULT_MULTIPLIER_16 12829U -#define PCG_DEFAULT_MULTIPLIER_32 747796405U -#define PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL - -#define PCG_DEFAULT_INCREMENT_8 77U -#define PCG_DEFAULT_INCREMENT_16 47989U -#define PCG_DEFAULT_INCREMENT_32 2891336453U -#define PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL - -#if PCG_HAS_128BIT_OPS -#define PCG_DEFAULT_MULTIPLIER_128 \ - PCG_128BIT_CONSTANT(2549297995355413924ULL,4865540595714422341ULL) -#define PCG_DEFAULT_INCREMENT_128 \ - PCG_128BIT_CONSTANT(6364136223846793005ULL,1442695040888963407ULL) -#endif - -/* - * Static initialization constants (if you can't call srandom for some - * bizarre reason). - */ - -#define PCG_STATE_ONESEQ_8_INITIALIZER { 0xd7U } -#define PCG_STATE_ONESEQ_16_INITIALIZER { 0x20dfU } -#define PCG_STATE_ONESEQ_32_INITIALIZER { 0x46b56677U } -#define PCG_STATE_ONESEQ_64_INITIALIZER { 0x4d595df4d0f33173ULL } -#if PCG_HAS_128BIT_OPS -#define PCG_STATE_ONESEQ_128_INITIALIZER \ - { PCG_128BIT_CONSTANT(0xb8dc10e158a92392ULL, 0x98046df007ec0a53ULL) } -#endif - -#define PCG_STATE_UNIQUE_8_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER -#define PCG_STATE_UNIQUE_16_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER -#define PCG_STATE_UNIQUE_32_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER -#define PCG_STATE_UNIQUE_64_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER -#if PCG_HAS_128BIT_OPS -#define PCG_STATE_UNIQUE_128_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER -#endif - -#define PCG_STATE_MCG_8_INITIALIZER { 0xe5U } -#define PCG_STATE_MCG_16_INITIALIZER { 0xa5e5U } -#define PCG_STATE_MCG_32_INITIALIZER { 0xd15ea5e5U } -#define PCG_STATE_MCG_64_INITIALIZER { 0xcafef00dd15ea5e5ULL } -#if PCG_HAS_128BIT_OPS -#define PCG_STATE_MCG_128_INITIALIZER \ - { PCG_128BIT_CONSTANT(0x0000000000000000ULL, 0xcafef00dd15ea5e5ULL) } -#endif - -#define PCG_STATE_SETSEQ_8_INITIALIZER { 0x9bU, 0xdbU } -#define PCG_STATE_SETSEQ_16_INITIALIZER { 0xe39bU, 0x5bdbU } -#define PCG_STATE_SETSEQ_32_INITIALIZER { 0xec02d89bU, 0x94b95bdbU } -#define PCG_STATE_SETSEQ_64_INITIALIZER \ - { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } -#if PCG_HAS_128BIT_OPS -#define PCG_STATE_SETSEQ_128_INITIALIZER \ - { PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL), \ - PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) } -#endif - -/* Representations for the oneseq, mcg, and unique variants */ - -struct pcg_state_8 { - uint8_t state; -}; - -struct pcg_state_16 { - uint16_t state; -}; - -struct pcg_state_32 { - uint32_t state; -}; - -struct pcg_state_64 { - uint64_t state; -}; - -#if PCG_HAS_128BIT_OPS -struct pcg_state_128 { - pcg128_t state; -}; -#endif - -/* Representations setseq variants */ - -struct pcg_state_setseq_8 { - uint8_t state; - uint8_t inc; -}; - -struct pcg_state_setseq_16 { - uint16_t state; - uint16_t inc; -}; - -struct pcg_state_setseq_32 { - uint32_t state; - uint32_t inc; -}; - -struct pcg_state_setseq_64 { - uint64_t state; - uint64_t inc; -}; - -#if PCG_HAS_128BIT_OPS -struct pcg_state_setseq_128 { - pcg128_t state; - pcg128_t inc; -}; -#endif - -/* Multi-step advance functions (jump-ahead, jump-back) */ - -extern uint8_t pcg_advance_lcg_8(uint8_t state, uint8_t delta, uint8_t cur_mult, - uint8_t cur_plus); -extern uint16_t pcg_advance_lcg_16(uint16_t state, uint16_t delta, - uint16_t cur_mult, uint16_t cur_plus); -extern uint32_t pcg_advance_lcg_32(uint32_t state, uint32_t delta, - uint32_t cur_mult, uint32_t cur_plus); -extern uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, - uint64_t cur_mult, uint64_t cur_plus); - -#if PCG_HAS_128BIT_OPS -extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, - pcg128_t cur_mult, pcg128_t cur_plus); -#endif - -/* Functions to advance the underlying LCG, one version for each size and - * each style. These functions are considered semi-private. There is rarely - * a good reason to call them directly. - */ - -inline void pcg_oneseq_8_step_r(struct pcg_state_8* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 - + PCG_DEFAULT_INCREMENT_8; -} - -inline void pcg_oneseq_8_advance_r(struct pcg_state_8* rng, uint8_t delta) -{ - rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, - PCG_DEFAULT_INCREMENT_8); -} - -inline void pcg_mcg_8_step_r(struct pcg_state_8* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8; -} - -inline void pcg_mcg_8_advance_r(struct pcg_state_8* rng, uint8_t delta) -{ - rng->state - = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, 0u); -} - -inline void pcg_unique_8_step_r(struct pcg_state_8* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 - + (uint8_t)(((intptr_t)rng) | 1u); -} - -inline void pcg_unique_8_advance_r(struct pcg_state_8* rng, uint8_t delta) -{ - rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, - (uint8_t)(((intptr_t)rng) | 1u)); -} - -inline void pcg_setseq_8_step_r(struct pcg_state_setseq_8* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + rng->inc; -} - -inline void pcg_setseq_8_advance_r(struct pcg_state_setseq_8* rng, - uint8_t delta) -{ - rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, - rng->inc); -} - -inline void pcg_oneseq_16_step_r(struct pcg_state_16* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 - + PCG_DEFAULT_INCREMENT_16; -} - -inline void pcg_oneseq_16_advance_r(struct pcg_state_16* rng, uint16_t delta) -{ - rng->state = pcg_advance_lcg_16( - rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, PCG_DEFAULT_INCREMENT_16); -} - -inline void pcg_mcg_16_step_r(struct pcg_state_16* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16; -} - -inline void pcg_mcg_16_advance_r(struct pcg_state_16* rng, uint16_t delta) -{ - rng->state - = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, 0u); -} - -inline void pcg_unique_16_step_r(struct pcg_state_16* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 - + (uint16_t)(((intptr_t)rng) | 1u); -} - -inline void pcg_unique_16_advance_r(struct pcg_state_16* rng, uint16_t delta) -{ - rng->state - = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, - (uint16_t)(((intptr_t)rng) | 1u)); -} - -inline void pcg_setseq_16_step_r(struct pcg_state_setseq_16* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + rng->inc; -} - -inline void pcg_setseq_16_advance_r(struct pcg_state_setseq_16* rng, - uint16_t delta) -{ - rng->state = pcg_advance_lcg_16(rng->state, delta, - PCG_DEFAULT_MULTIPLIER_16, rng->inc); -} - -inline void pcg_oneseq_32_step_r(struct pcg_state_32* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 - + PCG_DEFAULT_INCREMENT_32; -} - -inline void pcg_oneseq_32_advance_r(struct pcg_state_32* rng, uint32_t delta) -{ - rng->state = pcg_advance_lcg_32( - rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, PCG_DEFAULT_INCREMENT_32); -} - -inline void pcg_mcg_32_step_r(struct pcg_state_32* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32; -} - -inline void pcg_mcg_32_advance_r(struct pcg_state_32* rng, uint32_t delta) -{ - rng->state - = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, 0u); -} - -inline void pcg_unique_32_step_r(struct pcg_state_32* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 - + (uint32_t)(((intptr_t)rng) | 1u); -} - -inline void pcg_unique_32_advance_r(struct pcg_state_32* rng, uint32_t delta) -{ - rng->state - = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, - (uint32_t)(((intptr_t)rng) | 1u)); -} - -inline void pcg_setseq_32_step_r(struct pcg_state_setseq_32* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + rng->inc; -} - -inline void pcg_setseq_32_advance_r(struct pcg_state_setseq_32* rng, - uint32_t delta) -{ - rng->state = pcg_advance_lcg_32(rng->state, delta, - PCG_DEFAULT_MULTIPLIER_32, rng->inc); -} - -inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 - + PCG_DEFAULT_INCREMENT_64; -} - -inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, uint64_t delta) -{ - rng->state = pcg_advance_lcg_64( - rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, PCG_DEFAULT_INCREMENT_64); -} - -inline void pcg_mcg_64_step_r(struct pcg_state_64* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64; -} - -inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, uint64_t delta) -{ - rng->state - = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, 0u); -} - -inline void pcg_unique_64_step_r(struct pcg_state_64* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 - + (uint64_t)(((intptr_t)rng) | 1u); -} - -inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, uint64_t delta) -{ - rng->state - = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, - (uint64_t)(((intptr_t)rng) | 1u)); -} - -inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + rng->inc; -} - -inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, - uint64_t delta) -{ - rng->state = pcg_advance_lcg_64(rng->state, delta, - PCG_DEFAULT_MULTIPLIER_64, rng->inc); -} - -#if PCG_HAS_128BIT_OPS -inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 - + PCG_DEFAULT_INCREMENT_128; -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) -{ - rng->state - = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, - PCG_DEFAULT_INCREMENT_128); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_mcg_128_step_r(struct pcg_state_128* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128; -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) -{ - rng->state = pcg_advance_lcg_128(rng->state, delta, - PCG_DEFAULT_MULTIPLIER_128, 0u); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_unique_128_step_r(struct pcg_state_128* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 - + (pcg128_t)(((intptr_t)rng) | 1u); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) -{ - rng->state - = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, - (pcg128_t)(((intptr_t)rng) | 1u)); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng) -{ - rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, - pcg128_t delta) -{ - rng->state = pcg_advance_lcg_128(rng->state, delta, - PCG_DEFAULT_MULTIPLIER_128, rng->inc); -} -#endif - -/* Functions to seed the RNG state, one version for each size and each - * style. Unlike the step functions, regular users can and should call - * these functions. - */ - -inline void pcg_oneseq_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) -{ - rng->state = 0U; - pcg_oneseq_8_step_r(rng); - rng->state += initstate; - pcg_oneseq_8_step_r(rng); -} - -inline void pcg_mcg_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) -{ - rng->state = initstate | 1u; -} - -inline void pcg_unique_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) -{ - rng->state = 0U; - pcg_unique_8_step_r(rng); - rng->state += initstate; - pcg_unique_8_step_r(rng); -} - -inline void pcg_setseq_8_srandom_r(struct pcg_state_setseq_8* rng, - uint8_t initstate, uint8_t initseq) -{ - rng->state = 0U; - rng->inc = (initseq << 1u) | 1u; - pcg_setseq_8_step_r(rng); - rng->state += initstate; - pcg_setseq_8_step_r(rng); -} - -inline void pcg_oneseq_16_srandom_r(struct pcg_state_16* rng, - uint16_t initstate) -{ - rng->state = 0U; - pcg_oneseq_16_step_r(rng); - rng->state += initstate; - pcg_oneseq_16_step_r(rng); -} - -inline void pcg_mcg_16_srandom_r(struct pcg_state_16* rng, uint16_t initstate) -{ - rng->state = initstate | 1u; -} - -inline void pcg_unique_16_srandom_r(struct pcg_state_16* rng, - uint16_t initstate) -{ - rng->state = 0U; - pcg_unique_16_step_r(rng); - rng->state += initstate; - pcg_unique_16_step_r(rng); -} - -inline void pcg_setseq_16_srandom_r(struct pcg_state_setseq_16* rng, - uint16_t initstate, uint16_t initseq) -{ - rng->state = 0U; - rng->inc = (initseq << 1u) | 1u; - pcg_setseq_16_step_r(rng); - rng->state += initstate; - pcg_setseq_16_step_r(rng); -} - -inline void pcg_oneseq_32_srandom_r(struct pcg_state_32* rng, - uint32_t initstate) -{ - rng->state = 0U; - pcg_oneseq_32_step_r(rng); - rng->state += initstate; - pcg_oneseq_32_step_r(rng); -} - -inline void pcg_mcg_32_srandom_r(struct pcg_state_32* rng, uint32_t initstate) -{ - rng->state = initstate | 1u; -} - -inline void pcg_unique_32_srandom_r(struct pcg_state_32* rng, - uint32_t initstate) -{ - rng->state = 0U; - pcg_unique_32_step_r(rng); - rng->state += initstate; - pcg_unique_32_step_r(rng); -} - -inline void pcg_setseq_32_srandom_r(struct pcg_state_setseq_32* rng, - uint32_t initstate, uint32_t initseq) -{ - rng->state = 0U; - rng->inc = (initseq << 1u) | 1u; - pcg_setseq_32_step_r(rng); - rng->state += initstate; - pcg_setseq_32_step_r(rng); -} - -inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, - uint64_t initstate) -{ - rng->state = 0U; - pcg_oneseq_64_step_r(rng); - rng->state += initstate; - pcg_oneseq_64_step_r(rng); -} - -inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, uint64_t initstate) -{ - rng->state = initstate | 1u; -} - -inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, - uint64_t initstate) -{ - rng->state = 0U; - pcg_unique_64_step_r(rng); - rng->state += initstate; - pcg_unique_64_step_r(rng); -} - -inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, - uint64_t initstate, uint64_t initseq) -{ - rng->state = 0U; - rng->inc = (initseq << 1u) | 1u; - pcg_setseq_64_step_r(rng); - rng->state += initstate; - pcg_setseq_64_step_r(rng); -} - -#if PCG_HAS_128BIT_OPS -inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, - pcg128_t initstate) -{ - rng->state = 0U; - pcg_oneseq_128_step_r(rng); - rng->state += initstate; - pcg_oneseq_128_step_r(rng); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, pcg128_t initstate) -{ - rng->state = initstate | 1u; -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, - pcg128_t initstate) -{ - rng->state = 0U; - pcg_unique_128_step_r(rng); - rng->state += initstate; - pcg_unique_128_step_r(rng); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, - pcg128_t initstate, pcg128_t initseq) -{ - rng->state = 0U; - rng->inc = (initseq << 1u) | 1u; - pcg_setseq_128_step_r(rng); - rng->state += initstate; - pcg_setseq_128_step_r(rng); -} -#endif - -/* Now, finally we create each of the individual generators. We provide - * a random_r function that provides a random number of the appropriate - * type (using the full range of the type) and a boundedrand_r version - * that provides - * - * Implementation notes for boundedrand_r: - * - * To avoid bias, we need to make the range of the RNG a multiple of - * bound, which we do by dropping output less than a threshold. - * Let's consider a 32-bit case... A naive scheme to calculate the - * threshold would be to do - * - * uint32_t threshold = 0x100000000ull % bound; - * - * but 64-bit div/mod is slower than 32-bit div/mod (especially on - * 32-bit platforms). In essence, we do - * - * uint32_t threshold = (0x100000000ull-bound) % bound; - * - * because this version will calculate the same modulus, but the LHS - * value is less than 2^32. - * - * (Note that using modulo is only wise for good RNGs, poorer RNGs - * such as raw LCGs do better using a technique based on division.) - * Empricical tests show that division is preferable to modulus for - * reducting the range of an RNG. It's faster, and sometimes it can - * even be statistically prefereable. - */ - -/* Generation functions for XSH RS */ - -inline uint8_t pcg_oneseq_16_xsh_rs_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_oneseq_16_step_r(rng); - return pcg_output_xsh_rs_16_8(oldstate); -} - -inline uint8_t pcg_oneseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_oneseq_16_xsh_rs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_oneseq_32_xsh_rs_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_oneseq_32_step_r(rng); - return pcg_output_xsh_rs_32_16(oldstate); -} - -inline uint16_t pcg_oneseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_oneseq_32_xsh_rs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_xsh_rs_64_32(oldstate); -} - -inline uint32_t pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_oneseq_64_xsh_rs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_xsh_rs_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_128_xsh_rs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_unique_16_xsh_rs_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_unique_16_step_r(rng); - return pcg_output_xsh_rs_16_8(oldstate); -} - -inline uint8_t pcg_unique_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_unique_16_xsh_rs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_unique_32_xsh_rs_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_unique_32_step_r(rng); - return pcg_output_xsh_rs_32_16(oldstate); -} - -inline uint16_t pcg_unique_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_unique_32_xsh_rs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_xsh_rs_64_32(oldstate); -} - -inline uint32_t pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_unique_64_xsh_rs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_xsh_rs_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_128_xsh_rs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_setseq_16_xsh_rs_8_random_r(struct pcg_state_setseq_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_setseq_16_step_r(rng); - return pcg_output_xsh_rs_16_8(oldstate); -} - -inline uint8_t -pcg_setseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_setseq_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_setseq_16_xsh_rs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t -pcg_setseq_32_xsh_rs_16_random_r(struct pcg_state_setseq_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_setseq_32_step_r(rng); - return pcg_output_xsh_rs_32_16(oldstate); -} - -inline uint16_t -pcg_setseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_setseq_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_setseq_32_xsh_rs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t -pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_xsh_rs_64_32(oldstate); -} - -inline uint32_t -pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_setseq_64_xsh_rs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_xsh_rs_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_128_xsh_rs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_mcg_16_xsh_rs_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_mcg_16_step_r(rng); - return pcg_output_xsh_rs_16_8(oldstate); -} - -inline uint8_t pcg_mcg_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_mcg_16_xsh_rs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_mcg_32_xsh_rs_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_mcg_32_step_r(rng); - return pcg_output_xsh_rs_32_16(oldstate); -} - -inline uint16_t pcg_mcg_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_mcg_32_xsh_rs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_mcg_64_step_r(rng); - return pcg_output_xsh_rs_64_32(oldstate); -} - -inline uint32_t pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_mcg_64_xsh_rs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng) -{ - pcg_mcg_128_step_r(rng); - return pcg_output_xsh_rs_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_mcg_128_xsh_rs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/* Generation functions for XSH RR */ - -inline uint8_t pcg_oneseq_16_xsh_rr_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_oneseq_16_step_r(rng); - return pcg_output_xsh_rr_16_8(oldstate); -} - -inline uint8_t pcg_oneseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_oneseq_16_xsh_rr_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_oneseq_32_xsh_rr_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_oneseq_32_step_r(rng); - return pcg_output_xsh_rr_32_16(oldstate); -} - -inline uint16_t pcg_oneseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_oneseq_32_xsh_rr_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_xsh_rr_64_32(oldstate); -} - -inline uint32_t pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_oneseq_64_xsh_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_xsh_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_128_xsh_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_unique_16_xsh_rr_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_unique_16_step_r(rng); - return pcg_output_xsh_rr_16_8(oldstate); -} - -inline uint8_t pcg_unique_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_unique_16_xsh_rr_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_unique_32_xsh_rr_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_unique_32_step_r(rng); - return pcg_output_xsh_rr_32_16(oldstate); -} - -inline uint16_t pcg_unique_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_unique_32_xsh_rr_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_xsh_rr_64_32(oldstate); -} - -inline uint32_t pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_unique_64_xsh_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_xsh_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_128_xsh_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_setseq_16_xsh_rr_8_random_r(struct pcg_state_setseq_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_setseq_16_step_r(rng); - return pcg_output_xsh_rr_16_8(oldstate); -} - -inline uint8_t -pcg_setseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_setseq_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_setseq_16_xsh_rr_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t -pcg_setseq_32_xsh_rr_16_random_r(struct pcg_state_setseq_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_setseq_32_step_r(rng); - return pcg_output_xsh_rr_32_16(oldstate); -} - -inline uint16_t -pcg_setseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_setseq_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_setseq_32_xsh_rr_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t -pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_xsh_rr_64_32(oldstate); -} - -inline uint32_t -pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_setseq_64_xsh_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_xsh_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_128_xsh_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_mcg_16_xsh_rr_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_mcg_16_step_r(rng); - return pcg_output_xsh_rr_16_8(oldstate); -} - -inline uint8_t pcg_mcg_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_mcg_16_xsh_rr_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_mcg_32_xsh_rr_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_mcg_32_step_r(rng); - return pcg_output_xsh_rr_32_16(oldstate); -} - -inline uint16_t pcg_mcg_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_mcg_32_xsh_rr_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_mcg_64_step_r(rng); - return pcg_output_xsh_rr_64_32(oldstate); -} - -inline uint32_t pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_mcg_64_xsh_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_mcg_128_step_r(rng); - return pcg_output_xsh_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_mcg_128_xsh_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/* Generation functions for RXS M XS (no MCG versions because they - * don't make sense when you want to use the entire state) - */ - -inline uint8_t pcg_oneseq_8_rxs_m_xs_8_random_r(struct pcg_state_8* rng) -{ - uint8_t oldstate = rng->state; - pcg_oneseq_8_step_r(rng); - return pcg_output_rxs_m_xs_8_8(oldstate); -} - -inline uint8_t pcg_oneseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_8* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_oneseq_8_rxs_m_xs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_oneseq_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_oneseq_16_step_r(rng); - return pcg_output_rxs_m_xs_16_16(oldstate); -} - -inline uint16_t -pcg_oneseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_oneseq_16_rxs_m_xs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_oneseq_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_oneseq_32_step_r(rng); - return pcg_output_rxs_m_xs_32_32(oldstate); -} - -inline uint32_t -pcg_oneseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_oneseq_32_rxs_m_xs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint64_t pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_rxs_m_xs_64_64(oldstate); -} - -inline uint64_t -pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_64_rxs_m_xs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_rxs_m_xs_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_oneseq_128_rxs_m_xs_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint16_t pcg_unique_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_unique_16_step_r(rng); - return pcg_output_rxs_m_xs_16_16(oldstate); -} - -inline uint16_t -pcg_unique_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_unique_16_rxs_m_xs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_unique_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_unique_32_step_r(rng); - return pcg_output_rxs_m_xs_32_32(oldstate); -} - -inline uint32_t -pcg_unique_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_unique_32_rxs_m_xs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint64_t pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_rxs_m_xs_64_64(oldstate); -} - -inline uint64_t -pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_64_rxs_m_xs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_rxs_m_xs_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_unique_128_rxs_m_xs_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_setseq_8_rxs_m_xs_8_random_r(struct pcg_state_setseq_8* rng) -{ - uint8_t oldstate = rng->state; - pcg_setseq_8_step_r(rng); - return pcg_output_rxs_m_xs_8_8(oldstate); -} - -inline uint8_t -pcg_setseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_setseq_8* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_setseq_8_rxs_m_xs_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t -pcg_setseq_16_rxs_m_xs_16_random_r(struct pcg_state_setseq_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_setseq_16_step_r(rng); - return pcg_output_rxs_m_xs_16_16(oldstate); -} - -inline uint16_t -pcg_setseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_setseq_16* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_setseq_16_rxs_m_xs_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t -pcg_setseq_32_rxs_m_xs_32_random_r(struct pcg_state_setseq_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_setseq_32_step_r(rng); - return pcg_output_rxs_m_xs_32_32(oldstate); -} - -inline uint32_t -pcg_setseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_setseq_32* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_setseq_32_rxs_m_xs_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint64_t -pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_rxs_m_xs_64_64(oldstate); -} - -inline uint64_t -pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_64_rxs_m_xs_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_rxs_m_xs_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_setseq_128_rxs_m_xs_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/* Generation functions for RXS M */ - -inline uint8_t pcg_oneseq_16_rxs_m_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_oneseq_16_step_r(rng); - return pcg_output_rxs_m_16_8(oldstate); -} - -inline uint8_t pcg_oneseq_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_oneseq_16_rxs_m_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_oneseq_32_rxs_m_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_oneseq_32_step_r(rng); - return pcg_output_rxs_m_32_16(oldstate); -} - -inline uint16_t pcg_oneseq_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_oneseq_32_rxs_m_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_rxs_m_64_32(oldstate); -} - -inline uint32_t pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_oneseq_64_rxs_m_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_rxs_m_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_128_rxs_m_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_unique_16_rxs_m_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_unique_16_step_r(rng); - return pcg_output_rxs_m_16_8(oldstate); -} - -inline uint8_t pcg_unique_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_unique_16_rxs_m_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_unique_32_rxs_m_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_unique_32_step_r(rng); - return pcg_output_rxs_m_32_16(oldstate); -} - -inline uint16_t pcg_unique_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_unique_32_rxs_m_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_rxs_m_64_32(oldstate); -} - -inline uint32_t pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_unique_64_rxs_m_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_rxs_m_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_128_rxs_m_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_setseq_16_rxs_m_8_random_r(struct pcg_state_setseq_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_setseq_16_step_r(rng); - return pcg_output_rxs_m_16_8(oldstate); -} - -inline uint8_t -pcg_setseq_16_rxs_m_8_boundedrand_r(struct pcg_state_setseq_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_setseq_16_rxs_m_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_setseq_32_rxs_m_16_random_r(struct pcg_state_setseq_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_setseq_32_step_r(rng); - return pcg_output_rxs_m_32_16(oldstate); -} - -inline uint16_t -pcg_setseq_32_rxs_m_16_boundedrand_r(struct pcg_state_setseq_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_setseq_32_rxs_m_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_rxs_m_64_32(oldstate); -} - -inline uint32_t -pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_setseq_64_rxs_m_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_rxs_m_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_128_rxs_m_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint8_t pcg_mcg_16_rxs_m_8_random_r(struct pcg_state_16* rng) -{ - uint16_t oldstate = rng->state; - pcg_mcg_16_step_r(rng); - return pcg_output_rxs_m_16_8(oldstate); -} - -inline uint8_t pcg_mcg_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, - uint8_t bound) -{ - uint8_t threshold = ((uint8_t)(-bound)) % bound; - for (;;) { - uint8_t r = pcg_mcg_16_rxs_m_8_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint16_t pcg_mcg_32_rxs_m_16_random_r(struct pcg_state_32* rng) -{ - uint32_t oldstate = rng->state; - pcg_mcg_32_step_r(rng); - return pcg_output_rxs_m_32_16(oldstate); -} - -inline uint16_t pcg_mcg_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, - uint16_t bound) -{ - uint16_t threshold = ((uint16_t)(-bound)) % bound; - for (;;) { - uint16_t r = pcg_mcg_32_rxs_m_16_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_mcg_64_step_r(rng); - return pcg_output_rxs_m_64_32(oldstate); -} - -inline uint32_t pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_mcg_64_rxs_m_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng) -{ - pcg_mcg_128_step_r(rng); - return pcg_output_rxs_m_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_mcg_128_rxs_m_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/* Generation functions for XSL RR (only defined for "large" types) */ - -inline uint32_t pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_xsl_rr_64_32(oldstate); -} - -inline uint32_t pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_oneseq_64_xsl_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_xsl_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_128_xsl_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint32_t pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_xsl_rr_64_32(oldstate); -} - -inline uint32_t pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_unique_64_xsl_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_xsl_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_128_xsl_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint32_t -pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_xsl_rr_64_32(oldstate); -} - -inline uint32_t -pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_setseq_64_xsl_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_xsl_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t -pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_mcg_64_step_r(rng); - return pcg_output_xsl_rr_64_32(oldstate); -} - -inline uint32_t pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, - uint32_t bound) -{ - uint32_t threshold = -bound % bound; - for (;;) { - uint32_t r = pcg_mcg_64_xsl_rr_32_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng) -{ - pcg_mcg_128_step_r(rng); - return pcg_output_xsl_rr_128_64(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline uint64_t pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_mcg_128_xsl_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/* Generation functions for XSL RR RR (only defined for "large" types) */ - -inline uint64_t pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_oneseq_64_step_r(rng); - return pcg_output_xsl_rr_rr_64_64(oldstate); -} - -inline uint64_t -pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_oneseq_64_xsl_rr_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) -{ - pcg_oneseq_128_step_r(rng); - return pcg_output_xsl_rr_rr_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_oneseq_128_xsl_rr_rr_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint64_t pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_unique_64_step_r(rng); - return pcg_output_xsl_rr_rr_64_64(oldstate); -} - -inline uint64_t -pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_unique_64_xsl_rr_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) -{ - pcg_unique_128_step_r(rng); - return pcg_output_xsl_rr_rr_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_unique_128_xsl_rr_rr_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -inline uint64_t -pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng) -{ - uint64_t oldstate = rng->state; - pcg_setseq_64_step_r(rng); - return pcg_output_xsl_rr_rr_64_64(oldstate); -} - -inline uint64_t -pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, - uint64_t bound) -{ - uint64_t threshold = -bound % bound; - for (;;) { - uint64_t r = pcg_setseq_64_xsl_rr_rr_64_random_r(rng); - if (r >= threshold) - return r % bound; - } -} - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng) -{ - pcg_setseq_128_step_r(rng); - return pcg_output_xsl_rr_rr_128_128(rng->state); -} -#endif - -#if PCG_HAS_128BIT_OPS -inline pcg128_t -pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, - pcg128_t bound) -{ - pcg128_t threshold = -bound % bound; - for (;;) { - pcg128_t r = pcg_setseq_128_xsl_rr_rr_128_random_r(rng); - if (r >= threshold) - return r % bound; - } -} -#endif - -/*** Typedefs */ -typedef struct pcg_state_setseq_64 pcg32_random_t; -typedef struct pcg_state_64 pcg32s_random_t; -typedef struct pcg_state_64 pcg32u_random_t; -typedef struct pcg_state_64 pcg32f_random_t; -/*** random_r */ -#define pcg32_random_r pcg_setseq_64_xsh_rr_32_random_r -#define pcg32s_random_r pcg_oneseq_64_xsh_rr_32_random_r -#define pcg32u_random_r pcg_unique_64_xsh_rr_32_random_r -#define pcg32f_random_r pcg_mcg_64_xsh_rs_32_random_r -/*** boundedrand_r */ -#define pcg32_boundedrand_r pcg_setseq_64_xsh_rr_32_boundedrand_r -#define pcg32s_boundedrand_r pcg_oneseq_64_xsh_rr_32_boundedrand_r -#define pcg32u_boundedrand_r pcg_unique_64_xsh_rr_32_boundedrand_r -#define pcg32f_boundedrand_r pcg_mcg_64_xsh_rs_32_boundedrand_r -/*** srandom_r */ -#define pcg32_srandom_r pcg_setseq_64_srandom_r -#define pcg32s_srandom_r pcg_oneseq_64_srandom_r -#define pcg32u_srandom_r pcg_unique_64_srandom_r -#define pcg32f_srandom_r pcg_mcg_64_srandom_r -/*** advance_r */ -#define pcg32_advance_r pcg_setseq_64_advance_r -#define pcg32s_advance_r pcg_oneseq_64_advance_r -#define pcg32u_advance_r pcg_unique_64_advance_r -#define pcg32f_advance_r pcg_mcg_64_advance_r - -#if PCG_HAS_128BIT_OPS -/*** Typedefs */ -typedef struct pcg_state_setseq_128 pcg64_random_t; -typedef struct pcg_state_128 pcg64s_random_t; -typedef struct pcg_state_128 pcg64u_random_t; -typedef struct pcg_state_128 pcg64f_random_t; -/*** random_r */ -#define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r -#define pcg64s_random_r pcg_oneseq_128_xsl_rr_64_random_r -#define pcg64u_random_r pcg_unique_128_xsl_rr_64_random_r -#define pcg64f_random_r pcg_mcg_128_xsl_rr_64_random_r -/*** boundedrand_r */ -#define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r -#define pcg64s_boundedrand_r pcg_oneseq_128_xsl_rr_64_boundedrand_r -#define pcg64u_boundedrand_r pcg_unique_128_xsl_rr_64_boundedrand_r -#define pcg64f_boundedrand_r pcg_mcg_128_xsl_rr_64_boundedrand_r -/*** srandom_r */ -#define pcg64_srandom_r pcg_setseq_128_srandom_r -#define pcg64s_srandom_r pcg_oneseq_128_srandom_r -#define pcg64u_srandom_r pcg_unique_128_srandom_r -#define pcg64f_srandom_r pcg_mcg_128_srandom_r -/*** advance_r */ -#define pcg64_advance_r pcg_setseq_128_advance_r -#define pcg64s_advance_r pcg_oneseq_128_advance_r -#define pcg64u_advance_r pcg_unique_128_advance_r -#define pcg64f_advance_r pcg_mcg_128_advance_r -#endif - -/*** Typedefs */ -typedef struct pcg_state_8 pcg8si_random_t; -typedef struct pcg_state_16 pcg16si_random_t; -typedef struct pcg_state_32 pcg32si_random_t; -typedef struct pcg_state_64 pcg64si_random_t; -/*** random_r */ -#define pcg8si_random_r pcg_oneseq_8_rxs_m_xs_8_random_r -#define pcg16si_random_r pcg_oneseq_16_rxs_m_xs_16_random_r -#define pcg32si_random_r pcg_oneseq_32_rxs_m_xs_32_random_r -#define pcg64si_random_r pcg_oneseq_64_rxs_m_xs_64_random_r -/*** boundedrand_r */ -#define pcg8si_boundedrand_r pcg_oneseq_8_rxs_m_xs_8_boundedrand_r -#define pcg16si_boundedrand_r pcg_oneseq_16_rxs_m_xs_16_boundedrand_r -#define pcg32si_boundedrand_r pcg_oneseq_32_rxs_m_xs_32_boundedrand_r -#define pcg64si_boundedrand_r pcg_oneseq_64_rxs_m_xs_64_boundedrand_r -/*** srandom_r */ -#define pcg8si_srandom_r pcg_oneseq_8_srandom_r -#define pcg16si_srandom_r pcg_oneseq_16_srandom_r -#define pcg32si_srandom_r pcg_oneseq_32_srandom_r -#define pcg64si_srandom_r pcg_oneseq_64_srandom_r -/*** advance_r */ -#define pcg8si_advance_r pcg_oneseq_8_advance_r -#define pcg16si_advance_r pcg_oneseq_16_advance_r -#define pcg32si_advance_r pcg_oneseq_32_advance_r -#define pcg64si_advance_r pcg_oneseq_64_advance_r - -#if PCG_HAS_128BIT_OPS -typedef struct pcg_state_128 pcg128si_random_t; -#define pcg128si_random_r pcg_oneseq_128_rxs_m_xs_128_random_r -#define pcg128si_boundedrand_r pcg_oneseq_128_rxs_m_xs_128_boundedrand_r -#define pcg128si_srandom_r pcg_oneseq_128_srandom_r -#define pcg128si_advance_r pcg_oneseq_128_advance_r -#endif - -/*** Typedefs */ -typedef struct pcg_state_setseq_8 pcg8i_random_t; -typedef struct pcg_state_setseq_16 pcg16i_random_t; -typedef struct pcg_state_setseq_32 pcg32i_random_t; -typedef struct pcg_state_setseq_64 pcg64i_random_t; -/*** random_r */ -#define pcg8i_random_r pcg_setseq_8_rxs_m_xs_8_random_r -#define pcg16i_random_r pcg_setseq_16_rxs_m_xs_16_random_r -#define pcg32i_random_r pcg_setseq_32_rxs_m_xs_32_random_r -#define pcg64i_random_r pcg_setseq_64_rxs_m_xs_64_random_r -/*** boundedrand_r */ -#define pcg8i_boundedrand_r pcg_setseq_8_rxs_m_xs_8_boundedrand_r -#define pcg16i_boundedrand_r pcg_setseq_16_rxs_m_xs_16_boundedrand_r -#define pcg32i_boundedrand_r pcg_setseq_32_rxs_m_xs_32_boundedrand_r -#define pcg64i_boundedrand_r pcg_setseq_64_rxs_m_xs_64_boundedrand_r -/*** srandom_r */ -#define pcg8i_srandom_r pcg_setseq_8_srandom_r -#define pcg16i_srandom_r pcg_setseq_16_srandom_r -#define pcg32i_srandom_r pcg_setseq_32_srandom_r -#define pcg64i_srandom_r pcg_setseq_64_srandom_r -/*** advance_r */ -#define pcg8i_advance_r pcg_setseq_8_advance_r -#define pcg16i_advance_r pcg_setseq_16_advance_r -#define pcg32i_advance_r pcg_setseq_32_advance_r -#define pcg64i_advance_r pcg_setseq_64_advance_r - -#if PCG_HAS_128BIT_OPS -typedef struct pcg_state_setseq_128 pcg128i_random_t; -#define pcg128i_random_r pcg_setseq_128_rxs_m_xs_128_random_r -#define pcg128i_boundedrand_r pcg_setseq_128_rxs_m_xs_128_boundedrand_r -#define pcg128i_srandom_r pcg_setseq_128_srandom_r -#define pcg128i_advance_r pcg_setseq_128_advance_r -#endif - -extern uint32_t pcg32_random(void); -extern uint32_t pcg32_boundedrand(uint32_t bound); -extern void pcg32_srandom(uint64_t seed, uint64_t seq); -extern void pcg32_advance(uint64_t delta); - -#if PCG_HAS_128BIT_OPS -extern uint64_t pcg64_random(void); -extern uint64_t pcg64_boundedrand(uint64_t bound); -extern void pcg64_srandom(pcg128_t seed, pcg128_t seq); -extern void pcg64_advance(pcg128_t delta); -#endif - -/* - * Static initialization constants (if you can't call srandom for some - * bizarre reason). - */ - -#define PCG32_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER -#define PCG32U_INITIALIZER PCG_STATE_UNIQUE_64_INITIALIZER -#define PCG32S_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER -#define PCG32F_INITIALIZER PCG_STATE_MCG_64_INITIALIZER - -#if PCG_HAS_128BIT_OPS -#define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER -#define PCG64U_INITIALIZER PCG_STATE_UNIQUE_128_INITIALIZER -#define PCG64S_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER -#define PCG64F_INITIALIZER PCG_STATE_MCG_128_INITIALIZER -#endif - -#define PCG8SI_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER -#define PCG16SI_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER -#define PCG32SI_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER -#define PCG64SI_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER -#if PCG_HAS_128BIT_OPS -#define PCG128SI_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER -#endif - -#define PCG8I_INITIALIZER PCG_STATE_SETSEQ_8_INITIALIZER -#define PCG16I_INITIALIZER PCG_STATE_SETSEQ_16_INITIALIZER -#define PCG32I_INITIALIZER PCG_STATE_SETSEQ_32_INITIALIZER -#define PCG64I_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER -#if PCG_HAS_128BIT_OPS -#define PCG128I_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER -#endif - -#if __cplusplus -} -#endif - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -#endif /* PCG_VARIANTS_H_INCLUDED */ diff --git a/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h b/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h index 83ed11e6eb5..c58c98a75ad 100644 --- a/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h +++ b/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h @@ -51,7 +51,7 @@ inline static void vecfree(void *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; } diff --git a/src/vendor/cigraph/vendor/plfit/gss.c b/src/vendor/cigraph/vendor/plfit/gss.c index 5c5ffbd1a50..9f332c0dc8e 100644 --- a/src/vendor/cigraph/vendor/plfit/gss.c +++ b/src/vendor/cigraph/vendor/plfit/gss.c @@ -45,7 +45,7 @@ */ static const gss_parameter_t _defparam = { /* .epsilon = */ DBL_MIN, - /* .on_error = */ GSS_ERROR_STOP + /* .on_error = */ GSS_ERROR_STOP }; /** @@ -58,7 +58,7 @@ void gss_parameter_init(gss_parameter_t *param) { } unsigned short int gss_get_warning_flag(void) { - return gss_i_warning_flag; + return gss_i_warning_flag; } #define TERMINATE { \ @@ -80,7 +80,7 @@ unsigned short int gss_get_warning_flag(void) { retval = proc_progress(instance, x, fx, min, fmin, \ (a < b) ? a : b, (a < b) ? b : a, k); \ if (retval) { \ - TERMINATE; \ + TERMINATE; \ return PLFIT_SUCCESS; \ } \ } \ @@ -97,7 +97,7 @@ int gss(double a, double b, double *_min, double *_fmin, gss_parameter_t param = _param ? (*_param) : _defparam; - gss_i_warning_flag = 0; + gss_i_warning_flag = 0; if (a > b) { c = a; a = b; b = c; @@ -113,12 +113,12 @@ int gss(double a, double b, double *_min, double *_fmin, EVALUATE(c, fc); if (fc >= fa || fc >= fb) { - if (param.on_error == GSS_ERROR_STOP) { - return PLFIT_FAILURE; - } else { - gss_i_warning_flag = 1; - } - } + if (param.on_error == GSS_ERROR_STOP) { + return PLFIT_FAILURE; + } else { + gss_i_warning_flag = 1; + } + } while (fabs(a-b) > param.epsilon) { k++; @@ -127,12 +127,12 @@ int gss(double a, double b, double *_min, double *_fmin, 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 (param.on_error == GSS_ERROR_STOP) { + successful = 0; + break; + } else { + gss_i_warning_flag = 1; + } } if (fc <= fd) { @@ -146,7 +146,7 @@ int gss(double a, double b, double *_min, double *_fmin, c = (a+b) / 2.0; k++; EVALUATE(c, fc); - TERMINATE; + TERMINATE; } return successful ? PLFIT_SUCCESS : PLFIT_FAILURE; diff --git a/src/vendor/cigraph/vendor/plfit/hzeta.c b/src/vendor/cigraph/vendor/plfit/hzeta.c index 861d1182f63..eefd70c0e5c 100644 --- a/src/vendor/cigraph/vendor/plfit/hzeta.c +++ b/src/vendor/cigraph/vendor/plfit/hzeta.c @@ -10,7 +10,7 @@ /* `hsl/specfunc/hzeta.c' C source file // HSL - Home Scientific Library -// Copyright (C) 2017-2022 Jerome Benoit +// 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 @@ -97,7 +97,7 @@ typedef struct gsl_sf_result_struct gsl_sf_result; // B_{2j}/(2j) static -double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={ +double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ +1.0, +1.0/12.0, -1.0/720.0, @@ -130,12 +130,12 @@ double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SER +1.01530758555695563116307139454e-46, -2.57180415824187174992481940976e-48, +6.51445603523381493155843485864e-50, - -1.65013099068965245550609878048e-51, - NAN}; // hsl_sf_hzeta_eulermaclaurin_series_coeffs + -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+2]={ +double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ -2.0, +1.0/6.0, +1.0/360.0, @@ -168,8 +168,8 @@ double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLA +2.03061517111391126232614278906e-46, +5.14360831648374349984963881946e-48, +1.30289120704676298631168697172e-49, - +3.30026198137930491101219756091e-51, - NAN}; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios + +3.30026198137930491101219756091e-51 + }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios extern @@ -234,7 +234,6 @@ int hsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) { ratio=scp*pcp; if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break; } - if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 0, x <- t, y <- y. - - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, + - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, x <- t, y <- x. */ if (*fx < *ft) { diff --git a/src/vendor/cigraph/vendor/plfit/lbfgs.h b/src/vendor/cigraph/vendor/plfit/lbfgs.h index c36a0b1f832..f26ae874e49 100644 --- a/src/vendor/cigraph/vendor/plfit/lbfgs.h +++ b/src/vendor/cigraph/vendor/plfit/lbfgs.h @@ -59,7 +59,7 @@ typedef double lbfgsfloatval_t; #endif -/** +/** * \addtogroup liblbfgs_api libLBFGS API * @{ * @@ -68,7 +68,7 @@ typedef double lbfgsfloatval_t; /** * Return values of lbfgs(). - * + * * Roughly speaking, a negative value indicates an error. */ enum { @@ -365,7 +365,7 @@ typedef struct { * 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 @@ -502,14 +502,14 @@ void lbfgs_parameter_init(lbfgs_parameter_t *param); * 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. */ diff --git a/src/vendor/cigraph/vendor/plfit/mt.c b/src/vendor/cigraph/vendor/plfit/mt.c index 349a0a4d477..f9c2b74609b 100644 --- a/src/vendor/cigraph/vendor/plfit/mt.c +++ b/src/vendor/cigraph/vendor/plfit/mt.c @@ -18,7 +18,7 @@ #include "plfit_mt.h" static uint16_t get_random_uint16(void) { - return RNG_INTEGER(0, 0xffff); + return RNG_INT31() & 0xFFFF; } void plfit_mt_init(plfit_mt_rng_t* rng) { @@ -32,7 +32,7 @@ void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { 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] = (((uint32_t) get_random_uint16()) << 16) + get_random_uint16(); + rng->mt_buffer[i] = (get_random_uint16() << 16) + get_random_uint16(); } } else { for (i = 0; i < PLFIT_MT_LEN; i++) { @@ -56,7 +56,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { int idx = rng->mt_index; uint32_t s; int i; - + if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { idx = 0; i = 0; @@ -68,7 +68,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { 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); } @@ -79,7 +79,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { 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; diff --git a/src/vendor/cigraph/vendor/plfit/plfit.c b/src/vendor/cigraph/vendor/plfit/plfit.c index 05d72624ca3..266a9b02add 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit.c +++ b/src/vendor/cigraph/vendor/plfit/plfit.c @@ -116,7 +116,7 @@ static double* extract_smaller(double* begin, double* end, double xmin, size_t counter = count_smaller(begin, end, xmin); double *p, *result; - result = calloc(counter > 0 ? counter : 1, sizeof(double)); + result = calloc(counter, sizeof(double)); if (result == 0) return 0; @@ -350,7 +350,7 @@ static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, #endif /* Allocate memory to sample into */ - ys = calloc(n > 0 ? n : 1, sizeof(double)); + ys = calloc(n, sizeof(double)); if (ys == 0) { retval = PLFIT_ENOMEM; } else { @@ -540,7 +540,7 @@ static int plfit_i_continuous_xmin_opt_linear_scan( 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]; + local_best_n = local_opt_data.end - local_opt_data.probes[i] + 1; } } @@ -583,16 +583,10 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt int success; size_t i, best_n, num_uniques = 0; - double x, *px, **uniques, **strata; - int error_code, retval = PLFIT_SUCCESS; + double x, *px, **uniques; DATA_POINTS_CHECK; - /* Set up pointers that we will allocate */ - opt_data.begin = NULL; - uniques = NULL; - strata = NULL; - /* Sane defaults */ best_n = n; if (!options) @@ -605,10 +599,8 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt /* 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) { - free(opt_data.begin); + 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 @@ -642,6 +634,7 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt 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]; @@ -651,8 +644,8 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt 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) { - retval = error_code; - goto cleanup; + free(strata); + return error_code; } opt_data.num_probes = 0; @@ -671,17 +664,14 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt } } - free(strata); strata = NULL; - + free(strata); if (opt_data.num_probes > 0) { /* Do a strict linear scan in the subrange determined above */ - error_code = plfit_i_continuous_xmin_opt_linear_scan( - &opt_data, &best_result, &best_n - ); - if (error_code) { - retval = error_code; - goto cleanup; - } + 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 */ @@ -699,47 +689,26 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt /* More advanced search methods failed or were skipped; try linear search */ opt_data.probes = uniques; opt_data.num_probes = num_uniques; - error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); - if (error_code) { - retval = error_code; - goto cleanup; - } + 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); uniques = NULL; + free(uniques); /* Sort out the result */ *result = best_result; if (options->finite_size_correction) plfit_i_perform_finite_size_correction(result, best_n); - error_code = plfit_log_likelihood_continuous( - opt_data.begin + n - best_n, best_n, result->alpha, result->xmin, - &result->L - ); - if (error_code) { - retval = error_code; - goto cleanup; - } - - error_code = plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result); - if (error_code) { - retval = error_code; - goto cleanup; - } + 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)); -cleanup: - if (strata) { - free(strata); - } - if (uniques) { - free(uniques); - } + /* Get rid of the copied data as well */ free(opt_data.begin); - return retval; + return PLFIT_SUCCESS; } /********** Discrete power law distribution fitting **********/ @@ -1071,7 +1040,7 @@ static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, #endif /* Allocate memory to sample into */ - ys = calloc(n > 0 ? n : 1, sizeof(double)); + ys = calloc(n, sizeof(double)); if (ys == 0) { retval = PLFIT_ENOMEM; } else { @@ -1251,7 +1220,7 @@ int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options 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, + 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)); diff --git a/src/vendor/cigraph/vendor/plfit/plfit_error.c b/src/vendor/cigraph/vendor/plfit/plfit_error.c index 81bb31b5ba1..d93d1c0db32 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_error.c +++ b/src/vendor/cigraph/vendor/plfit/plfit_error.c @@ -22,14 +22,13 @@ #include "plfit_error.h" #include "platform.h" -static const char *plfit_i_error_strings[] = { +static char *plfit_i_error_strings[] = { "No error", "Failed", "Invalid value", "Underflow", "Overflow", - "Not enough memory", - "Maximum number of iterations exceeded" + "Not enough memory" }; #ifndef USING_R diff --git a/src/vendor/cigraph/vendor/plfit/plfit_error.h b/src/vendor/cigraph/vendor/plfit/plfit_error.h index b9170b380a8..e5429975a07 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_error.h +++ b/src/vendor/cigraph/vendor/plfit/plfit_error.h @@ -38,8 +38,7 @@ enum { PLFIT_EINVAL = 2, PLFIT_UNDRFLOW = 3, PLFIT_OVERFLOW = 4, - PLFIT_ENOMEM = 5, - PLFIT_EMAXITER = 6 + PLFIT_ENOMEM = 5 }; #if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) @@ -56,7 +55,7 @@ enum { if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\ return plfit_i_ret; \ } \ - } while (0) + } while(0) #define PLFIT_ERROR(reason,plfit_errno) \ do {\ diff --git a/src/vendor/cigraph/vendor/plfit/plfit_version.h b/src/vendor/cigraph/vendor/plfit/plfit_version.h index 37da90abc28..1212dd1cd1f 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_version.h +++ b/src/vendor/cigraph/vendor/plfit/plfit_version.h @@ -22,7 +22,7 @@ #define PLFIT_VERSION_MAJOR 0 #define PLFIT_VERSION_MINOR 9 -#define PLFIT_VERSION_PATCH 4 -#define PLFIT_VERSION_STRING "0.9.4" +#define PLFIT_VERSION_PATCH 3 +#define PLFIT_VERSION_STRING "0.9.3" #endif diff --git a/src/vendor/cigraph/vendor/plfit/sampling.c b/src/vendor/cigraph/vendor/plfit/sampling.c index cf0ff00242d..4becf134c20 100644 --- a/src/vendor/cigraph/vendor/plfit/sampling.c +++ b/src/vendor/cigraph/vendor/plfit/sampling.c @@ -187,11 +187,11 @@ int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, ps_end = ps + n; /* Initialize indexes and probs */ - sampler->indexes = (long int*)calloc(n > 0 ? n : 1, sizeof(long int)); + sampler->indexes = (long int*)calloc(n, sizeof(long int)); if (sampler->indexes == 0) { return PLFIT_ENOMEM; } - sampler->probs = (double*)calloc(n > 0 ? n : 1, sizeof(double)); + sampler->probs = (double*)calloc(n, sizeof(double)); if (sampler->probs == 0) { free(sampler->indexes); return PLFIT_ENOMEM; @@ -215,13 +215,13 @@ int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, } /* Allocate space for short & long stick indexes */ - long_sticks = (long int*)calloc(num_long_sticks > 0 ? num_long_sticks : 1, sizeof(long int)); + 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_short_sticks > 0 ? num_short_sticks : 1, sizeof(long int)); + short_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); if (short_sticks == 0) { free(sampler->probs); free(sampler->indexes); From d673ba74895b0d5a12dc1ec99cfe3f26553165a9 Mon Sep 17 00:00:00 2001 From: Antonov548 Date: Wed, 28 Jun 2023 13:33:37 +0200 Subject: [PATCH 2/2] update vendor cigraph --- src/vendor/cigraph/.all-contributorsrc | 135 + src/vendor/cigraph/.azure/build-win.yml | 14 +- src/vendor/cigraph/.azure/build.yml | 17 +- src/vendor/cigraph/.pre-commit-config.yaml | 1 - src/vendor/cigraph/.travis.yml | 63 +- src/vendor/cigraph/.zenodo.json | 22 +- src/vendor/cigraph/ACKNOWLEDGEMENTS.md | 17 +- src/vendor/cigraph/AUTHORS | 1 + src/vendor/cigraph/CHANGELOG.md | 546 +++- src/vendor/cigraph/CMakeLists.txt | 70 +- src/vendor/cigraph/CONTRIBUTING.md | 4 +- src/vendor/cigraph/CONTRIBUTORS.md | 19 + src/vendor/cigraph/ONEWS | 4 +- src/vendor/cigraph/README.md | 8 +- src/vendor/cigraph/appveyor.yml | 4 +- src/vendor/cigraph/azure-pipelines.yml | 47 +- src/vendor/cigraph/codecov.yml | 1 + .../cigraph/etc/cmake/CTestCustom.cmake.in | 9 - .../cigraph/etc/cmake/CheckTLSSupport.cmake | 4 +- .../cigraph/etc/cmake/FindCXSparse.cmake | 75 - .../etc/cmake/PreventInSourceBuilds.cmake | 12 +- .../cigraph/etc/cmake/attribute_support.cmake | 27 + src/vendor/cigraph/etc/cmake/compilers.cmake | 42 +- .../etc/cmake/cpack_install_script.cmake | 21 +- .../cigraph/etc/cmake/dependencies.cmake | 10 +- src/vendor/cigraph/etc/cmake/features.cmake | 14 + .../etc/cmake/ieee754_endianness.cmake | 54 + .../etc/cmake/ieee754_endianness_check.c | 24 + .../cigraph/etc/cmake/pkgconfig_helpers.cmake | 6 +- .../cigraph/etc/cmake/run_legacy_test.cmake | 19 +- .../cigraph/etc/cmake/safe_math_support.cmake | 18 + src/vendor/cigraph/etc/cmake/summary.cmake | 9 + .../cigraph/etc/cmake/test_helpers.cmake | 12 +- src/vendor/cigraph/etc/cmake/tls.cmake | 16 +- .../cigraph/etc/cmake/uint128_support.cmake | 43 + src/vendor/cigraph/igraph.pc.in | 1 + src/vendor/cigraph/include/igraph.h | 5 +- src/vendor/cigraph/include/igraph_adjlist.h | 111 +- src/vendor/cigraph/include/igraph_arpack.h | 33 +- src/vendor/cigraph/include/igraph_array.h | 6 +- src/vendor/cigraph/include/igraph_array_pmt.h | 19 +- .../cigraph/include/igraph_attributes.h | 214 +- src/vendor/cigraph/include/igraph_bipartite.h | 47 +- src/vendor/cigraph/include/igraph_blas.h | 10 +- .../cigraph/include/igraph_centrality.h | 90 +- src/vendor/cigraph/include/igraph_cliques.h | 78 +- .../cigraph/include/igraph_cocitation.h | 23 +- .../cigraph/include/igraph_cohesive_blocks.h | 11 +- src/vendor/cigraph/include/igraph_coloring.h | 17 +- src/vendor/cigraph/include/igraph_community.h | 150 +- src/vendor/cigraph/include/igraph_complex.h | 16 +- .../cigraph/include/igraph_components.h | 43 +- src/vendor/cigraph/include/igraph_config.h.in | 55 + src/vendor/cigraph/include/igraph_constants.h | 48 +- .../cigraph/include/igraph_constructors.h | 78 +- .../cigraph/include/igraph_conversion.h | 44 +- src/vendor/cigraph/include/igraph_cycles.h | 30 + src/vendor/cigraph/include/igraph_datatype.h | 53 +- src/vendor/cigraph/include/igraph_dqueue.h | 16 +- .../cigraph/include/igraph_dqueue_pmt.h | 27 +- src/vendor/cigraph/include/igraph_eigen.h | 18 +- src/vendor/cigraph/include/igraph_embedding.h | 8 +- src/vendor/cigraph/include/igraph_epidemics.h | 5 +- src/vendor/cigraph/include/igraph_error.h | 390 ++- src/vendor/cigraph/include/igraph_eulerian.h | 7 +- src/vendor/cigraph/include/igraph_flow.h | 76 +- src/vendor/cigraph/include/igraph_foreign.h | 72 +- src/vendor/cigraph/include/igraph_games.h | 118 +- .../cigraph/include/igraph_graph_list.h | 60 + .../cigraph/include/igraph_graphicality.h | 24 +- src/vendor/cigraph/include/igraph_graphlets.h | 21 +- src/vendor/cigraph/include/igraph_heap.h | 6 +- src/vendor/cigraph/include/igraph_heap_pmt.h | 17 +- src/vendor/cigraph/include/igraph_hrg.h | 75 +- src/vendor/cigraph/include/igraph_interface.h | 68 +- src/vendor/cigraph/include/igraph_interrupt.h | 4 +- src/vendor/cigraph/include/igraph_iterators.h | 223 +- src/vendor/cigraph/include/igraph_lapack.h | 15 +- src/vendor/cigraph/include/igraph_layout.h | 132 +- src/vendor/cigraph/include/igraph_lsap.h | 3 +- src/vendor/cigraph/include/igraph_matching.h | 18 +- src/vendor/cigraph/include/igraph_matrix.h | 23 +- .../cigraph/include/igraph_matrix_list.h | 59 + .../cigraph/include/igraph_matrix_pmt.h | 221 +- src/vendor/cigraph/include/igraph_memory.h | 11 +- .../include/igraph_microscopic_update.h | 17 +- src/vendor/cigraph/include/igraph_mixing.h | 19 +- src/vendor/cigraph/include/igraph_motifs.h | 50 +- .../cigraph/include/igraph_neighborhood.h | 10 +- src/vendor/cigraph/include/igraph_nongraph.h | 71 +- src/vendor/cigraph/include/igraph_operators.h | 73 +- src/vendor/cigraph/include/igraph_paths.h | 351 ++- src/vendor/cigraph/include/igraph_pmt.h | 110 +- src/vendor/cigraph/include/igraph_pmt_off.h | 30 +- src/vendor/cigraph/include/igraph_progress.h | 7 +- src/vendor/cigraph/include/igraph_psumtree.h | 15 +- src/vendor/cigraph/include/igraph_random.h | 142 +- src/vendor/cigraph/include/igraph_scan.h | 29 +- src/vendor/cigraph/include/igraph_scg.h | 143 - .../cigraph/include/igraph_separators.h | 20 +- src/vendor/cigraph/include/igraph_sparsemat.h | 220 +- src/vendor/cigraph/include/igraph_spmatrix.h | 109 - src/vendor/cigraph/include/igraph_stack.h | 22 +- src/vendor/cigraph/include/igraph_stack_pmt.h | 12 +- src/vendor/cigraph/include/igraph_statusbar.h | 11 +- .../cigraph/include/igraph_structural.h | 126 +- src/vendor/cigraph/include/igraph_strvector.h | 88 +- src/vendor/cigraph/include/igraph_topology.h | 170 +- .../cigraph/include/igraph_transitivity.h | 26 +- .../cigraph/include/igraph_typed_list_pmt.h | 119 + src/vendor/cigraph/include/igraph_types.h | 114 +- src/vendor/cigraph/include/igraph_vector.h | 79 +- .../cigraph/include/igraph_vector_list.h | 73 + .../cigraph/include/igraph_vector_pmt.h | 220 +- .../cigraph/include/igraph_vector_ptr.h | 53 +- .../cigraph/include/igraph_version.h.in | 8 +- src/vendor/cigraph/include/igraph_visitor.h | 55 +- src/vendor/cigraph/interfaces/CMakeLists.txt | 2 +- src/vendor/cigraph/interfaces/functions.yaml | 1467 ++++++---- src/vendor/cigraph/interfaces/types.yaml | 299 +- src/vendor/cigraph/src/CMakeLists.txt | 101 +- .../cigraph/src/centrality/betweenness.c | 1729 ++++++----- .../src/centrality/centrality_internal.h | 37 + .../cigraph/src/centrality/centrality_other.c | 1548 +--------- .../cigraph/src/centrality/centralization.c | 75 +- src/vendor/cigraph/src/centrality/closeness.c | 267 +- src/vendor/cigraph/src/centrality/coreness.c | 68 +- .../cigraph/src/centrality/eigenvector.c | 549 ++++ .../cigraph/src/centrality/hub_authority.c | 495 ++++ src/vendor/cigraph/src/centrality/pagerank.c | 708 +++++ src/vendor/cigraph/src/centrality/prpack.cpp | 134 +- .../centrality/prpack/prpack_base_graph.cpp | 6 +- .../src/centrality/prpack/prpack_base_graph.h | 4 + .../centrality/prpack/prpack_igraph_graph.cpp | 91 +- .../centrality/prpack/prpack_igraph_graph.h | 23 +- .../src/centrality/prpack/prpack_result.h | 3 +- .../src/centrality/prpack/prpack_solver.cpp | 12 +- .../src/centrality/prpack/prpack_solver.h | 2 + .../src/centrality/prpack/prpack_utils.cpp | 3 +- .../cigraph/src/centrality/prpack_internal.h | 6 +- src/vendor/cigraph/src/centrality/truss.cpp | 286 ++ .../src/cliques/cliquer/CMakeLists.txt | 3 +- .../cigraph/src/cliques/cliquer/cliquer.c | 430 +-- .../cigraph/src/cliques/cliquer/cliquer.h | 35 +- src/vendor/cigraph/src/cliques/cliquer/set.h | 8 +- .../cigraph/src/cliques/cliquer_internal.h | 40 +- .../cigraph/src/cliques/cliquer_wrapper.c | 265 +- src/vendor/cigraph/src/cliques/cliques.c | 701 ++--- src/vendor/cigraph/src/cliques/glet.c | 481 ++-- .../cigraph/src/cliques/maximal_cliques.c | 286 +- .../src/cliques/maximal_cliques_template.h | 175 +- .../cigraph/src/community/community_misc.c | 373 +-- .../cigraph/src/community/edge_betweenness.c | 373 +-- .../cigraph/src/community/fast_modularity.c | 364 +-- src/vendor/cigraph/src/community/fluid.c | 130 +- .../cigraph/src/community/infomap/infomap.cc | 232 +- .../community/infomap/infomap_FlowGraph.cc | 299 +- .../src/community/infomap/infomap_FlowGraph.h | 47 +- .../src/community/infomap/infomap_Greedy.cc | 401 ++- .../src/community/infomap/infomap_Greedy.h | 38 +- .../src/community/infomap/infomap_Node.cc | 71 - .../src/community/infomap/infomap_Node.h | 29 +- .../cigraph/src/community/label_propagation.c | 206 +- .../src/community/leading_eigenvector.c | 330 +-- src/vendor/cigraph/src/community/leiden.c | 489 ++-- src/vendor/cigraph/src/community/louvain.c | 400 ++- src/vendor/cigraph/src/community/modularity.c | 69 +- .../src/community/optimal_modularity.c | 59 +- .../src/community/spinglass/NetRoutines.cpp | 22 +- .../src/community/spinglass/NetRoutines.h | 2 +- .../src/community/spinglass/clustertool.cpp | 149 +- .../src/community/spinglass/pottsmodel_2.cpp | 183 +- .../src/community/spinglass/pottsmodel_2.h | 28 +- .../src/community/walktrap/walktrap.cpp | 149 +- .../walktrap/walktrap_communities.cpp | 229 +- .../community/walktrap/walktrap_communities.h | 28 +- .../src/community/walktrap/walktrap_graph.cpp | 80 +- .../src/community/walktrap/walktrap_graph.h | 5 +- .../src/community/walktrap/walktrap_heap.cpp | 103 +- .../src/community/walktrap/walktrap_heap.h | 30 +- src/vendor/cigraph/src/config.h.in | 36 +- .../src/connectivity/cohesive_blocks.c | 397 ++- .../cigraph/src/connectivity/components.c | 919 +++--- .../cigraph/src/connectivity/separators.c | 501 ++-- .../cigraph/src/constructors/adjacency.c | 1250 ++++++-- .../cigraph/src/constructors/atlas-edges.h | 22 +- src/vendor/cigraph/src/constructors/atlas.c | 16 +- .../src/constructors/basic_constructors.c | 75 +- .../cigraph/src/constructors/circulant.c | 111 + .../cigraph/src/constructors/de_bruijn.c | 46 +- src/vendor/cigraph/src/constructors/famous.c | 85 +- src/vendor/cigraph/src/constructors/full.c | 273 +- .../src/constructors/generalized_petersen.c | 95 + src/vendor/cigraph/src/constructors/kautz.c | 100 +- .../cigraph/src/constructors/lattices.c | 603 ++++ src/vendor/cigraph/src/constructors/lcf.c | 62 +- .../cigraph/src/constructors/linegraph.c | 102 +- src/vendor/cigraph/src/constructors/prufer.c | 36 +- src/vendor/cigraph/src/constructors/regular.c | 607 +++- src/vendor/cigraph/src/constructors/trees.c | 163 ++ src/vendor/cigraph/src/core/array.c | 4 +- src/vendor/cigraph/src/core/array.pmt | 50 +- src/vendor/cigraph/src/core/buckets.c | 94 +- src/vendor/cigraph/src/core/buckets.h | 47 +- src/vendor/cigraph/src/core/cutheap.c | 55 +- src/vendor/cigraph/src/core/cutheap.h | 22 +- src/vendor/cigraph/src/core/dqueue.c | 10 +- src/vendor/cigraph/src/core/dqueue.pmt | 194 +- src/vendor/cigraph/src/core/error.c | 157 +- src/vendor/cigraph/src/core/estack.c | 30 +- src/vendor/cigraph/src/core/estack.h | 21 +- src/vendor/cigraph/src/core/exceptions.h | 25 +- .../cigraph/src/core/fixed_vectorlist.c | 56 +- .../cigraph/src/core/fixed_vectorlist.h | 13 +- src/vendor/cigraph/src/core/genheap.c | 307 ++ src/vendor/cigraph/src/core/genheap.h | 73 + src/vendor/cigraph/src/core/grid.c | 190 +- src/vendor/cigraph/src/core/grid.h | 36 +- src/vendor/cigraph/src/core/heap.c | 4 +- src/vendor/cigraph/src/core/heap.pmt | 137 +- src/vendor/cigraph/src/core/indheap.c | 399 ++- src/vendor/cigraph/src/core/indheap.h | 99 +- src/vendor/cigraph/src/core/interruption.c | 10 +- src/vendor/cigraph/src/core/interruption.h | 14 +- src/vendor/cigraph/src/core/marked_queue.c | 76 +- src/vendor/cigraph/src/core/marked_queue.h | 47 +- src/vendor/cigraph/src/core/math.h | 95 +- src/vendor/cigraph/src/core/matrix.c | 290 +- src/vendor/cigraph/src/core/matrix.pmt | 795 +++-- src/vendor/cigraph/src/core/matrix_list.c | 54 + src/vendor/cigraph/src/core/memory.c | 95 +- src/vendor/cigraph/src/core/printing.c | 219 +- src/vendor/cigraph/src/core/progress.c | 7 +- src/vendor/cigraph/src/core/psumtree.c | 46 +- src/vendor/cigraph/src/core/set.c | 111 +- src/vendor/cigraph/src/core/set.h | 13 +- src/vendor/cigraph/src/core/sparsemat.c | 1211 +++++--- src/vendor/cigraph/src/core/spmatrix.c | 1066 ------- src/vendor/cigraph/src/core/stack.c | 39 - src/vendor/cigraph/src/core/stack.pmt | 155 +- src/vendor/cigraph/src/core/statusbar.c | 21 +- src/vendor/cigraph/src/core/strvector.c | 745 +++-- src/vendor/cigraph/src/core/trie.c | 296 +- src/vendor/cigraph/src/core/trie.h | 30 +- src/vendor/cigraph/src/core/typed_list.pmt | 1099 +++++++ src/vendor/cigraph/src/core/vector.c | 526 ++-- src/vendor/cigraph/src/core/vector.pmt | 1201 +++++--- src/vendor/cigraph/src/core/vector_list.c | 171 ++ src/vendor/cigraph/src/core/vector_ptr.c | 313 +- src/vendor/cigraph/src/f2c.h | 3 +- src/vendor/cigraph/src/flow/flow.c | 1172 ++++---- src/vendor/cigraph/src/flow/flow_conversion.c | 93 + src/vendor/cigraph/src/flow/flow_internal.h | 19 +- src/vendor/cigraph/src/flow/st-cuts.c | 1198 ++++---- src/vendor/cigraph/src/games/barabasi.c | 481 ++-- .../cigraph/src/games/callaway_traits.c | 55 +- src/vendor/cigraph/src/games/citations.c | 174 +- src/vendor/cigraph/src/games/correlated.c | 106 +- .../cigraph/src/games/degree_sequence.c | 316 +- .../degree_sequence_vl/degree_sequence_vl.h | 34 + .../degree_sequence_vl/gengraph_box_list.cpp | 110 - .../degree_sequence_vl/gengraph_box_list.h | 83 - .../degree_sequence_vl/gengraph_definitions.h | 36 +- .../gengraph_degree_sequence.cpp | 321 +-- .../gengraph_degree_sequence.h | 44 +- .../gengraph_graph_molloy_hash.cpp | 688 +---- .../gengraph_graph_molloy_hash.h | 110 +- .../gengraph_graph_molloy_optimized.cpp | 1317 +-------- .../gengraph_graph_molloy_optimized.h | 145 +- .../games/degree_sequence_vl/gengraph_hash.h | 61 +- .../degree_sequence_vl/gengraph_header.h | 2 +- .../gengraph_mr-connected.cpp | 82 +- .../degree_sequence_vl/gengraph_powerlaw.cpp | 272 -- .../degree_sequence_vl/gengraph_powerlaw.h | 86 - .../games/degree_sequence_vl/gengraph_qsort.h | 363 +-- .../degree_sequence_vl/gengraph_random.cpp | 2 - .../degree_sequence_vl/gengraph_random.h | 2 +- .../gengraph_vertex_cover.h | 72 - src/vendor/cigraph/src/games/dotproduct.c | 79 +- src/vendor/cigraph/src/games/erdos_renyi.c | 227 +- src/vendor/cigraph/src/games/establishment.c | 48 +- src/vendor/cigraph/src/games/forestfire.c | 113 +- src/vendor/cigraph/src/games/grg.c | 31 +- src/vendor/cigraph/src/games/growing_random.c | 46 +- src/vendor/cigraph/src/games/islands.c | 129 +- src/vendor/cigraph/src/games/k_regular.c | 32 +- src/vendor/cigraph/src/games/preference.c | 324 +-- src/vendor/cigraph/src/games/recent_degree.c | 150 +- src/vendor/cigraph/src/games/sbm.c | 354 +-- src/vendor/cigraph/src/games/static_fitness.c | 47 +- src/vendor/cigraph/src/games/tree.c | 23 +- src/vendor/cigraph/src/games/watts_strogatz.c | 32 +- src/vendor/cigraph/src/graph/adjlist.c | 743 +++-- src/vendor/cigraph/src/graph/attributes.c | 205 +- src/vendor/cigraph/src/graph/attributes.h | 66 +- src/vendor/cigraph/src/graph/basic_query.c | 19 +- src/vendor/cigraph/src/graph/caching.c | 185 ++ src/vendor/cigraph/src/graph/caching.h | 52 + src/vendor/cigraph/src/graph/cattributes.c | 2505 ++++++++-------- src/vendor/cigraph/src/graph/graph_list.c | 61 + .../src/graph/{neighbors.h => internal.h} | 29 +- src/vendor/cigraph/src/graph/iterators.c | 1202 ++++---- src/vendor/cigraph/src/graph/type_common.c | 207 ++ .../cigraph/src/graph/type_indexededgelist.c | 1688 +++++------ src/vendor/cigraph/src/graph/visitors.c | 452 +-- src/vendor/cigraph/src/hrg/dendro.h | 8 +- src/vendor/cigraph/src/hrg/graph.h | 3 +- src/vendor/cigraph/src/hrg/hrg.cc | 657 +++-- src/vendor/cigraph/src/hrg/hrg_types.cc | 39 +- .../cigraph/src/internal/glpk_support.c | 17 +- .../cigraph/src/internal/glpk_support.h | 17 +- src/vendor/cigraph/src/internal/hacks.c | 29 +- src/vendor/cigraph/src/internal/hacks.h | 31 +- src/vendor/cigraph/src/internal/lsap.c | 115 +- src/vendor/cigraph/src/internal/pstdint.h | 817 ------ src/vendor/cigraph/src/internal/qsort.c | 7 +- src/vendor/cigraph/src/internal/utils.c | 95 + src/vendor/cigraph/src/internal/utils.h | 32 + src/vendor/cigraph/src/internal/zeroin.c | 39 +- src/vendor/cigraph/src/io/dimacs.c | 247 +- src/vendor/cigraph/src/io/dl-header.h | 17 +- src/vendor/cigraph/src/io/dl-lexer.l | 34 +- src/vendor/cigraph/src/io/dl-parser.y | 187 +- src/vendor/cigraph/src/io/dl.c | 58 +- src/vendor/cigraph/src/io/dot.c | 113 +- src/vendor/cigraph/src/io/edgelist.c | 79 +- src/vendor/cigraph/src/io/gml-header.h | 12 +- src/vendor/cigraph/src/io/gml-lexer.l | 39 +- src/vendor/cigraph/src/io/gml-parser.y | 277 +- src/vendor/cigraph/src/io/gml-tree.c | 159 +- src/vendor/cigraph/src/io/gml-tree.h | 63 +- src/vendor/cigraph/src/io/gml.c | 1283 ++++++--- src/vendor/cigraph/src/io/graphdb.c | 82 +- src/vendor/cigraph/src/io/graphml.c | 1195 +++++--- src/vendor/cigraph/src/io/leda.c | 158 +- src/vendor/cigraph/src/io/lgl-header.h | 7 +- src/vendor/cigraph/src/io/lgl-lexer.l | 2 +- src/vendor/cigraph/src/io/lgl-parser.y | 68 +- src/vendor/cigraph/src/io/lgl.c | 165 +- src/vendor/cigraph/src/io/ncol-header.h | 5 +- src/vendor/cigraph/src/io/ncol-lexer.l | 2 +- src/vendor/cigraph/src/io/ncol-parser.y | 67 +- src/vendor/cigraph/src/io/ncol.c | 142 +- src/vendor/cigraph/src/io/pajek-header.h | 28 +- src/vendor/cigraph/src/io/pajek-lexer.l | 105 +- src/vendor/cigraph/src/io/pajek-parser.y | 524 ++-- src/vendor/cigraph/src/io/pajek.c | 365 +-- src/vendor/cigraph/src/io/parse_utils.c | 381 +++ src/vendor/cigraph/src/io/parse_utils.h | 41 + src/vendor/cigraph/src/isomorphism/bliss.cc | 194 +- .../src/isomorphism/bliss/CMakeLists.txt | 2 +- .../cigraph/src/isomorphism/bliss/graph.hh | 14 +- .../cigraph/src/isomorphism/isoclasses.c | 129 +- .../src/isomorphism/isomorphism_misc.c | 28 +- src/vendor/cigraph/src/isomorphism/lad.c | 495 ++-- src/vendor/cigraph/src/isomorphism/queries.c | 133 +- src/vendor/cigraph/src/isomorphism/vf2.c | 911 +++--- src/vendor/cigraph/src/layout/circular.c | 104 +- .../cigraph/src/layout/davidson_harel.c | 248 +- .../cigraph/src/layout/drl/DensityGrid.cpp | 29 +- .../cigraph/src/layout/drl/DensityGrid_3d.cpp | 29 +- src/vendor/cigraph/src/layout/drl/drl_Node.h | 8 +- .../cigraph/src/layout/drl/drl_Node_3d.h | 8 +- .../cigraph/src/layout/drl/drl_graph.cpp | 67 +- src/vendor/cigraph/src/layout/drl/drl_graph.h | 30 +- .../cigraph/src/layout/drl/drl_graph_3d.cpp | 65 +- .../cigraph/src/layout/drl/drl_graph_3d.h | 30 +- .../cigraph/src/layout/drl/drl_layout.cpp | 18 +- .../cigraph/src/layout/drl/drl_layout_3d.cpp | 12 +- .../cigraph/src/layout/fruchterman_reingold.c | 145 +- src/vendor/cigraph/src/layout/gem.c | 124 +- src/vendor/cigraph/src/layout/graphopt.c | 88 +- src/vendor/cigraph/src/layout/kamada_kawai.c | 65 +- src/vendor/cigraph/src/layout/large_graph.c | 157 +- .../cigraph/src/layout/layout_bipartite.c | 16 +- src/vendor/cigraph/src/layout/layout_grid.c | 22 +- .../cigraph/src/layout/layout_internal.h | 32 +- src/vendor/cigraph/src/layout/layout_random.c | 38 +- src/vendor/cigraph/src/layout/mds.c | 103 +- src/vendor/cigraph/src/layout/merge_dla.c | 106 +- src/vendor/cigraph/src/layout/merge_grid.c | 51 +- src/vendor/cigraph/src/layout/merge_grid.h | 21 +- .../cigraph/src/layout/reingold_tilford.c | 397 +-- src/vendor/cigraph/src/layout/sugiyama.c | 556 ++-- src/vendor/cigraph/src/layout/umap.c | 1260 ++++++++ src/vendor/cigraph/src/linalg/arpack.c | 195 +- .../cigraph/src/linalg/arpack_internal.h | 5 + src/vendor/cigraph/src/linalg/blas.c | 123 +- src/vendor/cigraph/src/linalg/blas_internal.h | 5 + src/vendor/cigraph/src/linalg/eigen.c | 344 +-- src/vendor/cigraph/src/linalg/lapack.c | 312 +- .../cigraph/src/linalg/lapack_internal.h | 5 + src/vendor/cigraph/src/math/bfgs.c | 221 -- src/vendor/cigraph/src/math/complex.c | 13 +- src/vendor/cigraph/src/math/safe_intop.c | 156 + src/vendor/cigraph/src/math/safe_intop.h | 136 + src/vendor/cigraph/src/math/utils.c | 289 +- src/vendor/cigraph/src/misc/bipartite.c | 564 ++-- src/vendor/cigraph/src/misc/chordality.c | 157 +- src/vendor/cigraph/src/misc/cocitation.c | 276 +- src/vendor/cigraph/src/misc/coloring.c | 221 +- src/vendor/cigraph/src/misc/conversion.c | 750 ++--- src/vendor/cigraph/src/misc/cycle_bases.c | 540 ++++ .../cigraph/src/misc/degree_sequence.cpp | 193 +- src/vendor/cigraph/src/misc/embedding.c | 264 +- .../cigraph/src/misc/feedback_arc_set.c | 343 +-- .../cigraph/src/misc/feedback_arc_set.h | 15 +- src/vendor/cigraph/src/misc/graphicality.c | 379 +-- src/vendor/cigraph/src/misc/matching.c | 396 +-- .../cigraph/src/misc/microscopic_update.c | 128 +- src/vendor/cigraph/src/misc/mixing.c | 339 ++- src/vendor/cigraph/src/misc/motifs.c | 621 ++-- src/vendor/cigraph/src/misc/order_cycle.cpp | 100 + .../{conversion_internal.h => order_cycle.h} | 23 +- src/vendor/cigraph/src/misc/other.c | 340 +-- src/vendor/cigraph/src/misc/power_law_fit.c | 328 +++ src/vendor/cigraph/src/misc/scan.c | 442 ++- src/vendor/cigraph/src/misc/sir.c | 41 +- src/vendor/cigraph/src/misc/spanning_trees.c | 311 +- src/vendor/cigraph/src/operators/add_edge.c | 13 +- .../cigraph/src/operators/complementer.c | 48 +- src/vendor/cigraph/src/operators/compose.c | 61 +- .../src/operators/connect_neighborhood.c | 85 +- src/vendor/cigraph/src/operators/contract.c | 121 +- src/vendor/cigraph/src/operators/difference.c | 70 +- .../cigraph/src/operators/disjoint_union.c | 86 +- .../cigraph/src/operators/intersection.c | 205 +- .../cigraph/src/operators/misc_internal.c | 129 +- .../cigraph/src/operators/misc_internal.h | 13 +- src/vendor/cigraph/src/operators/permute.c | 53 +- src/vendor/cigraph/src/operators/reverse.c | 25 +- src/vendor/cigraph/src/operators/rewire.c | 57 +- .../cigraph/src/operators/rewire_edges.c | 125 +- .../cigraph/src/operators/rewire_internal.h | 9 +- src/vendor/cigraph/src/operators/simplify.c | 95 +- src/vendor/cigraph/src/operators/subgraph.c | 373 ++- src/vendor/cigraph/src/operators/subgraph.h | 15 +- src/vendor/cigraph/src/operators/union.c | 173 +- .../cigraph/src/paths/all_shortest_paths.c | 266 +- src/vendor/cigraph/src/paths/astar.c | 275 ++ src/vendor/cigraph/src/paths/bellman_ford.c | 390 +-- src/vendor/cigraph/src/paths/dijkstra.c | 841 +++--- src/vendor/cigraph/src/paths/distances.c | 844 +++++- src/vendor/cigraph/src/paths/eulerian.c | 335 +-- src/vendor/cigraph/src/paths/floyd_warshall.c | 367 +++ src/vendor/cigraph/src/paths/histogram.c | 48 +- src/vendor/cigraph/src/paths/johnson.c | 127 +- src/vendor/cigraph/src/paths/random_walk.c | 380 ++- src/vendor/cigraph/src/paths/shortest_paths.c | 884 ++++-- src/vendor/cigraph/src/paths/simple_paths.c | 85 +- src/vendor/cigraph/src/paths/sparsifier.c | 460 +++ src/vendor/cigraph/src/paths/unweighted.c | 429 +-- src/vendor/cigraph/src/paths/voronoi.c | 425 +++ src/vendor/cigraph/src/paths/widest_paths.c | 736 +++++ .../cigraph/src/properties/basic_properties.c | 74 +- .../cigraph/src/properties/constraint.c | 154 +- .../src/properties/convergence_degree.c | 56 +- src/vendor/cigraph/src/properties/dag.c | 202 +- src/vendor/cigraph/src/properties/degrees.c | 220 +- src/vendor/cigraph/src/properties/ecc.c | 411 +++ src/vendor/cigraph/src/properties/girth.c | 99 +- src/vendor/cigraph/src/properties/loops.c | 30 +- .../cigraph/src/properties/multiplicity.c | 337 ++- .../cigraph/src/properties/neighborhood.c | 241 +- src/vendor/cigraph/src/properties/perfect.c | 191 ++ .../src/properties/properties_internal.h | 11 +- src/vendor/cigraph/src/properties/spectral.c | 673 +++-- src/vendor/cigraph/src/properties/trees.c | 512 +++- src/vendor/cigraph/src/properties/triangles.c | 397 ++- .../src/properties/triangles_template.h | 40 +- .../src/properties/triangles_template1.h | 28 +- src/vendor/cigraph/src/random/random.c | 1657 ++++++----- .../cigraph/src/random/random_internal.h | 38 + src/vendor/cigraph/src/random/rng_glibc2.c | 144 + src/vendor/cigraph/src/random/rng_mt19937.c | 179 ++ src/vendor/cigraph/src/random/rng_pcg32.c | 124 + src/vendor/cigraph/src/random/rng_pcg64.c | 138 + src/vendor/cigraph/src/scg/scg.c | 2303 --------------- .../cigraph/src/scg/scg_approximate_methods.c | 173 -- src/vendor/cigraph/src/scg/scg_exact_scg.c | 69 - src/vendor/cigraph/src/scg/scg_headers.h | 128 - src/vendor/cigraph/src/scg/scg_kmeans.c | 102 - .../cigraph/src/scg/scg_optimal_method.c | 241 -- src/vendor/cigraph/src/scg/scg_utils.c | 94 - src/vendor/cigraph/src/version.c | 40 +- src/vendor/cigraph/vendor/CMakeLists.txt | 1 + src/vendor/cigraph/vendor/cs/CMakeLists.txt | 4 +- .../cigraph/vendor/cs/SuiteSparse_config.h | 221 -- src/vendor/cigraph/vendor/cs/cs.h | 631 +--- src/vendor/cigraph/vendor/f2c/CMakeLists.txt | 22 +- src/vendor/cigraph/vendor/f2c/exit_.c | 2 +- src/vendor/cigraph/vendor/f2c/main.c | 148 - src/vendor/cigraph/vendor/f2c/s_paus.c | 6 + src/vendor/cigraph/vendor/f2c/s_stop.c | 3 + src/vendor/cigraph/vendor/f2c/sig_die.c | 5 +- src/vendor/cigraph/vendor/f2c/uninit.c | 11 +- src/vendor/cigraph/vendor/glpk/CMakeLists.txt | 2 +- .../cigraph/vendor/glpk/minisat/minisat.h | 5 + .../cigraph/vendor/lapack/CMakeLists.txt | 4 +- .../cigraph/vendor/mini-gmp/CMakeLists.txt | 2 +- src/vendor/cigraph/vendor/pcg/CMakeLists.txt | 21 + src/vendor/cigraph/vendor/pcg/LICENSE.txt | 19 + .../cigraph/vendor/pcg/pcg-advance-128.c | 61 + .../cigraph/vendor/pcg/pcg-advance-64.c | 59 + .../cigraph/vendor/pcg/pcg-output-128.c | 63 + src/vendor/cigraph/vendor/pcg/pcg-output-32.c | 63 + src/vendor/cigraph/vendor/pcg/pcg-output-64.c | 73 + src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c | 378 +++ src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c | 255 ++ src/vendor/cigraph/vendor/pcg/pcg_variants.h | 2557 +++++++++++++++++ .../cigraph/vendor/plfit/arithmetic_ansi.h | 2 +- src/vendor/cigraph/vendor/plfit/gss.c | 34 +- src/vendor/cigraph/vendor/plfit/hzeta.c | 19 +- src/vendor/cigraph/vendor/plfit/lbfgs.c | 6 +- src/vendor/cigraph/vendor/plfit/lbfgs.h | 10 +- src/vendor/cigraph/vendor/plfit/mt.c | 10 +- src/vendor/cigraph/vendor/plfit/plfit.c | 77 +- src/vendor/cigraph/vendor/plfit/plfit_error.c | 5 +- src/vendor/cigraph/vendor/plfit/plfit_error.h | 5 +- .../cigraph/vendor/plfit/plfit_version.h | 4 +- src/vendor/cigraph/vendor/plfit/sampling.c | 8 +- 521 files changed, 57270 insertions(+), 42300 deletions(-) delete mode 100644 src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in delete mode 100644 src/vendor/cigraph/etc/cmake/FindCXSparse.cmake create mode 100644 src/vendor/cigraph/etc/cmake/attribute_support.cmake create mode 100644 src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake create mode 100644 src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c create mode 100644 src/vendor/cigraph/etc/cmake/safe_math_support.cmake create mode 100644 src/vendor/cigraph/etc/cmake/uint128_support.cmake create mode 100644 src/vendor/cigraph/include/igraph_config.h.in create mode 100644 src/vendor/cigraph/include/igraph_cycles.h create mode 100644 src/vendor/cigraph/include/igraph_graph_list.h create mode 100644 src/vendor/cigraph/include/igraph_matrix_list.h delete mode 100644 src/vendor/cigraph/include/igraph_scg.h delete mode 100644 src/vendor/cigraph/include/igraph_spmatrix.h create mode 100644 src/vendor/cigraph/include/igraph_typed_list_pmt.h create mode 100644 src/vendor/cigraph/include/igraph_vector_list.h create mode 100644 src/vendor/cigraph/src/centrality/centrality_internal.h create mode 100644 src/vendor/cigraph/src/centrality/eigenvector.c create mode 100644 src/vendor/cigraph/src/centrality/hub_authority.c create mode 100644 src/vendor/cigraph/src/centrality/pagerank.c create mode 100644 src/vendor/cigraph/src/centrality/truss.cpp delete mode 100644 src/vendor/cigraph/src/community/infomap/infomap_Node.cc create mode 100644 src/vendor/cigraph/src/constructors/circulant.c create mode 100644 src/vendor/cigraph/src/constructors/generalized_petersen.c create mode 100644 src/vendor/cigraph/src/constructors/lattices.c create mode 100644 src/vendor/cigraph/src/constructors/trees.c create mode 100644 src/vendor/cigraph/src/core/genheap.c create mode 100644 src/vendor/cigraph/src/core/genheap.h create mode 100644 src/vendor/cigraph/src/core/matrix_list.c delete mode 100644 src/vendor/cigraph/src/core/spmatrix.c create mode 100644 src/vendor/cigraph/src/core/typed_list.pmt create mode 100644 src/vendor/cigraph/src/core/vector_list.c create mode 100644 src/vendor/cigraph/src/flow/flow_conversion.c create mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h delete mode 100644 src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h create mode 100644 src/vendor/cigraph/src/graph/caching.c create mode 100644 src/vendor/cigraph/src/graph/caching.h create mode 100644 src/vendor/cigraph/src/graph/graph_list.c rename src/vendor/cigraph/src/graph/{neighbors.h => internal.h} (55%) create mode 100644 src/vendor/cigraph/src/graph/type_common.c delete mode 100644 src/vendor/cigraph/src/internal/pstdint.h create mode 100644 src/vendor/cigraph/src/internal/utils.c create mode 100644 src/vendor/cigraph/src/internal/utils.h create mode 100644 src/vendor/cigraph/src/io/parse_utils.c create mode 100644 src/vendor/cigraph/src/io/parse_utils.h create mode 100644 src/vendor/cigraph/src/layout/umap.c delete mode 100644 src/vendor/cigraph/src/math/bfgs.c create mode 100644 src/vendor/cigraph/src/math/safe_intop.c create mode 100644 src/vendor/cigraph/src/math/safe_intop.h create mode 100644 src/vendor/cigraph/src/misc/cycle_bases.c create mode 100644 src/vendor/cigraph/src/misc/order_cycle.cpp rename src/vendor/cigraph/src/misc/{conversion_internal.h => order_cycle.h} (62%) create mode 100644 src/vendor/cigraph/src/misc/power_law_fit.c create mode 100644 src/vendor/cigraph/src/paths/astar.c create mode 100644 src/vendor/cigraph/src/paths/floyd_warshall.c create mode 100644 src/vendor/cigraph/src/paths/sparsifier.c create mode 100644 src/vendor/cigraph/src/paths/voronoi.c create mode 100644 src/vendor/cigraph/src/paths/widest_paths.c create mode 100644 src/vendor/cigraph/src/properties/ecc.c create mode 100644 src/vendor/cigraph/src/properties/perfect.c create mode 100644 src/vendor/cigraph/src/random/random_internal.h create mode 100644 src/vendor/cigraph/src/random/rng_glibc2.c create mode 100644 src/vendor/cigraph/src/random/rng_mt19937.c create mode 100644 src/vendor/cigraph/src/random/rng_pcg32.c create mode 100644 src/vendor/cigraph/src/random/rng_pcg64.c delete mode 100644 src/vendor/cigraph/src/scg/scg.c delete mode 100644 src/vendor/cigraph/src/scg/scg_approximate_methods.c delete mode 100644 src/vendor/cigraph/src/scg/scg_exact_scg.c delete mode 100644 src/vendor/cigraph/src/scg/scg_headers.h delete mode 100644 src/vendor/cigraph/src/scg/scg_kmeans.c delete mode 100644 src/vendor/cigraph/src/scg/scg_optimal_method.c delete mode 100644 src/vendor/cigraph/src/scg/scg_utils.c delete mode 100644 src/vendor/cigraph/vendor/cs/SuiteSparse_config.h delete mode 100644 src/vendor/cigraph/vendor/f2c/main.c create mode 100644 src/vendor/cigraph/vendor/pcg/CMakeLists.txt create mode 100644 src/vendor/cigraph/vendor/pcg/LICENSE.txt create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-advance-128.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-advance-64.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-128.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-32.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-output-64.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c create mode 100644 src/vendor/cigraph/vendor/pcg/pcg_variants.h diff --git a/src/vendor/cigraph/.all-contributorsrc b/src/vendor/cigraph/.all-contributorsrc index ce094a9d7a6..9c26f453ded 100644 --- a/src/vendor/cigraph/.all-contributorsrc +++ b/src/vendor/cigraph/.all-contributorsrc @@ -378,6 +378,141 @@ "contributions": [ "code" ] + }, + { + "login": "Katterrina", + "name": "Kateřina Č.", + "avatar_url": "https://avatars.githubusercontent.com/u/31630249?v=4", + "profile": "https://github.com/Katterrina", + "contributions": [ + "code" + ] + }, + { + "login": "valdaarhun", + "name": "valdaarhun", + "avatar_url": "https://avatars.githubusercontent.com/u/39989901?v=4", + "profile": "https://github.com/valdaarhun", + "contributions": [ + "code" + ] + }, + { + "login": "YuliYudith", + "name": "YuliYudith", + "avatar_url": "https://avatars.githubusercontent.com/u/54366258?v=4", + "profile": "https://github.com/YuliYudith", + "contributions": [ + "code" + ] + }, + { + "login": "alexsyou", + "name": "alexsyou", + "avatar_url": "https://avatars.githubusercontent.com/u/54590871?v=4", + "profile": "https://github.com/alexsyou", + "contributions": [ + "code" + ] + }, + { + "login": "rohitt28", + "name": "Rohit Tawde", + "avatar_url": "https://avatars.githubusercontent.com/u/67415747?v=4", + "profile": "https://github.com/rohitt28", + "contributions": [ + "code" + ] + }, + { + "login": "alexperrone", + "name": "alexperrone", + "avatar_url": "https://avatars.githubusercontent.com/u/4990236?v=4", + "profile": "https://github.com/alexperrone", + "contributions": [ + "code" + ] + }, + { + "login": "borsgeorgica", + "name": "Georgica Bors", + "avatar_url": "https://avatars.githubusercontent.com/u/15649138?v=4", + "profile": "https://github.com/borsgeorgica", + "contributions": [ + "code" + ] + }, + { + "login": "meetpatel0963", + "name": "MEET PATEL", + "avatar_url": "https://avatars.githubusercontent.com/u/63169740?v=4", + "profile": "https://www.linkedin.com/in/meet-patel-b1329a16b/", + "contributions": [ + "code" + ] + }, + { + "login": "kwofach", + "name": "kwofach", + "avatar_url": "https://avatars.githubusercontent.com/u/97578264?v=4", + "profile": "https://github.com/kwofach", + "contributions": [ + "code" + ] + }, + { + "login": "Gomango999", + "name": "Kevin Zhu", + "avatar_url": "https://avatars.githubusercontent.com/u/37771462?v=4", + "profile": "https://github.com/Gomango999", + "contributions": [ + "code" + ] + }, + { + "login": "pradkrish", + "name": "Pradeep Krishnamurthy", + "avatar_url": "https://avatars.githubusercontent.com/u/47261443?v=4", + "profile": "https://github.com/pradkrish", + "contributions": [ + "code" + ] + }, + { + "login": "flange-ipb", + "name": "flange-ipb", + "avatar_url": "https://avatars.githubusercontent.com/u/34936695?v=4", + "profile": "https://github.com/flange-ipb", + "contributions": [ + "code" + ] + }, + { + "login": "JJ", + "name": "Juan Julián Merelo Guervós", + "avatar_url": "https://avatars.githubusercontent.com/u/500?v=4", + "profile": "http://goo.gl/IlWG8U", + "contributions": [ + "code" + ] + }, + { + "login": "rfulekjames", + "name": "Radoslav Fulek", + "avatar_url": "https://avatars.githubusercontent.com/u/54232342?v=4", + "profile": "https://github.com/rfulekjames", + "contributions": [ + "code" + ] + }, + { + "login": "professorcode1", + "name": "professorcode1", + "avatar_url": "https://avatars.githubusercontent.com/u/42749164?v=4", + "profile": "https://github.com/professorcode1", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7 diff --git a/src/vendor/cigraph/.azure/build-win.yml b/src/vendor/cigraph/.azure/build-win.yml index dfc938cbbc1..6ddc3a2b63d 100644 --- a/src/vendor/cigraph/.azure/build-win.yml +++ b/src/vendor/cigraph/.azure/build-win.yml @@ -8,9 +8,6 @@ parameters: - name: int_arpack type: boolean default: true - - name: int_cxsparse - type: boolean - default: true - name: int_gmp type: boolean default: true @@ -28,7 +25,7 @@ parameters: default: true - name: build_type type: string - default: 'RelWithDebInfo' + default: 'Release' - name: extra_cmake_args type: string default: '' @@ -68,7 +65,9 @@ steps: - task: Cache@2 condition: eq('${{ parameters.use_ccache }}', true) inputs: - key: 'ccache | "$(Agent.OS)"' + key: 'ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" | "$(Build.SourceVersion)"' + restoreKeys: | + ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" path: $(CCACHE_DIR) displayName: Ccache @@ -94,7 +93,6 @@ steps: -DIGRAPH_USE_INTERNAL_LAPACK=${{ parameters.int_lapack }} ^ -DIGRAPH_USE_INTERNAL_ARPACK=${{ parameters.int_arpack }} ^ -DIGRAPH_USE_INTERNAL_GLPK=${{ parameters.int_glpk }} ^ - -DIGRAPH_USE_INTERNAL_CXSPARSE=${{ parameters.int_cxsparse }} ^ -DIGRAPH_USE_INTERNAL_GMP=${{ parameters.int_gmp }} ^ -DIGRAPH_VERIFY_FINALLY_STACK=${{ parameters.verify_finally }} ^ -DBUILD_SHARED_LIBS=${{ parameters.build_shared }} ^ @@ -104,6 +102,7 @@ steps: -DIGRAPH_PRINT_ARITH_HEADER=${{ parameters.print_arith_header }} ^ -DVCPKG_TARGET_TRIPLET=${{ parameters.vcpkg_target_triplet }} ^ -DCMAKE_TOOLCHAIN_FILE=%VCPKG_INSTALLATION_ROOT%/scripts/buildsystems/vcpkg.cmake ^ + -DFLEX_KEEP_LINE_NUMBERS=ON ^ ${{ parameters.extra_cmake_args }} cmake --build . --target build_tests @@ -111,3 +110,6 @@ steps: - script: cd build && ctest -j 4 --output-on-failure ${{ parameters.extra_ctest_args }} --timeout 60 displayName: Test + + - script: ccache -s + displayName: Ccache statistics diff --git a/src/vendor/cigraph/.azure/build.yml b/src/vendor/cigraph/.azure/build.yml index d072cb6850b..494df29fc90 100644 --- a/src/vendor/cigraph/.azure/build.yml +++ b/src/vendor/cigraph/.azure/build.yml @@ -8,9 +8,6 @@ parameters: - name: int_arpack type: boolean default: true - - name: int_cxsparse - type: boolean - default: true - name: int_gmp type: boolean default: true @@ -28,7 +25,7 @@ parameters: default: true - name: build_type type: string - default: 'RelWithDebInfo' + default: 'Release' - name: extra_cmake_args type: string default: '' @@ -41,6 +38,9 @@ parameters: - name: print_arith_header type: boolean default: true + - name: force_colored_output + type: boolean + default: true steps: - task: CMake@1 @@ -52,7 +52,6 @@ steps: -DIGRAPH_USE_INTERNAL_LAPACK=${{ parameters.int_lapack }} -DIGRAPH_USE_INTERNAL_ARPACK=${{ parameters.int_arpack }} -DIGRAPH_USE_INTERNAL_GLPK=${{ parameters.int_glpk }} - -DIGRAPH_USE_INTERNAL_CXSPARSE=${{ parameters.int_cxsparse }} -DIGRAPH_USE_INTERNAL_GMP=${{ parameters.int_gmp }} -DIGRAPH_VERIFY_FINALLY_STACK=${{ parameters.verify_finally }} -DBUILD_SHARED_LIBS=${{ parameters.build_shared }} @@ -60,13 +59,16 @@ steps: -DUSE_CCACHE=${{ parameters.use_ccache }} -DCMAKE_BUILD_TYPE=${{ parameters.build_type }} -DIGRAPH_PRINT_ARITH_HEADER=${{ parameters.print_arith_header }} + -DFORCE_COLORED_OUTPUT=${{ parameters.force_colored_output }} -DFLEX_KEEP_LINE_NUMBERS=ON ${{ parameters.extra_cmake_args }} - task: Cache@2 condition: eq('${{ parameters.use_ccache }}', true) inputs: - key: 'ccache | "$(Agent.OS)"' + key: 'ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" | "$(Build.SourceVersion)"' + restoreKeys: | + ccache | "$(Agent.OS)" | "$(Agent.JobName)" | "$(Build.SourceBranch)" path: $(CCACHE_DIR) displayName: Ccache @@ -78,3 +80,6 @@ steps: # TODO: use -j `nproc` on Linux , -j `sysctl -n hw.ncpu` on Darwin - script: cd build && ctest -j 4 --output-on-failure ${{ parameters.extra_ctest_args }} --timeout 60 displayName: Test + + - script: ccache -s + displayName: Ccache statistics diff --git a/src/vendor/cigraph/.pre-commit-config.yaml b/src/vendor/cigraph/.pre-commit-config.yaml index 038e7e4e76b..ca953ac7637 100644 --- a/src/vendor/cigraph/.pre-commit-config.yaml +++ b/src/vendor/cigraph/.pre-commit-config.yaml @@ -14,4 +14,3 @@ repos: exclude: "\\.out$" - id: check-merge-conflict - id: fix-byte-order-marker - diff --git a/src/vendor/cigraph/.travis.yml b/src/vendor/cigraph/.travis.yml index 9805c4a5a65..295272b1d35 100644 --- a/src/vendor/cigraph/.travis.yml +++ b/src/vendor/cigraph/.travis.yml @@ -1,8 +1,8 @@ language: c cache: ccache -# dist: xenial os: linux +dist: focal # Ignore branches with names starting with certain keywords: branches: @@ -21,27 +21,27 @@ git: addons: apt: + update: true packages: - - ninja-build - - flex - - bison -# - docbook2x -# - xmlto -# - texinfo -# - source-highlight -# - libxml2-utils -# - xsltproc -# - fop - - libgmp-dev - - libglpk-dev - - libarpack2-dev -# - libblas-dev -# - liblapack-dev - - libopenblas-dev - - libsuitesparse-dev - - libxml2-dev - - git - - colordiff + - ninja-build + - flex + - bison +# - docbook2x +# - xmlto +# - texinfo +# - source-highlight +# - libxml2-utils +# - xsltproc +# - fop + - libgmp-dev + - libglpk-dev + - libarpack2-dev +# - libblas-dev +# - liblapack-dev + - libopenblas-dev + - libxml2-dev + - git + - colordiff snaps: - name: cmake confinement: classic @@ -50,7 +50,7 @@ addons: # if this phase fails, the build stops immediately before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_CXSPARSE=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address\;Undefined + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address # building and testing is in script # use && to ensure that ctest is not run if the build failed @@ -67,31 +67,28 @@ jobs: - name: "Linux arm64" os: linux - arch: arm64-graviton2 # faster than arm64 + arch: arm64 - name: "Linux arm64 external" os: linux - arch: arm64-graviton2 # faster than arm64 + arch: arm64 before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=OFF -DIGRAPH_USE_INTERNAL_LAPACK=OFF -DIGRAPH_USE_INTERNAL_ARPACK=OFF -DIGRAPH_USE_INTERNAL_GLPK=OFF -DIGRAPH_USE_INTERNAL_CXSPARSE=OFF -DIGRAPH_USE_INTERNAL_GMP=OFF -DIGRAPH_VERIFY_FINALLY_STACK=ON -DBLA_VENDOR=OpenBLAS -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address\;Undefined + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=OFF -DIGRAPH_USE_INTERNAL_LAPACK=OFF -DIGRAPH_USE_INTERNAL_ARPACK=OFF -DIGRAPH_USE_INTERNAL_GLPK=OFF -DIGRAPH_USE_INTERNAL_GMP=OFF -DIGRAPH_VERIFY_FINALLY_STACK=OFF -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -DUSE_SANITIZER=Address - name: "Linux ppc64" os: linux - dist: focal # Snap fails with ppc64 on earlier Ubuntu. arch: ppc64le - name: "Linux s390x" os: linux - dist: focal # Some packages are missing in earlier Ubuntu on this platform. arch: s390x # Do not enable ASan, as it leads to linking errors. - # This is possibly a conflict with LTO. before_script: - mkdir build && cd build - - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_CXSPARSE=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_ENABLE_LTO=AUTO -DIGRAPH_PRINT_ARITH_HEADER=ON + - cmake .. -DIGRAPH_USE_INTERNAL_BLAS=ON -DIGRAPH_USE_INTERNAL_LAPACK=ON -DIGRAPH_USE_INTERNAL_ARPACK=ON -DIGRAPH_USE_INTERNAL_GLPK=ON -DIGRAPH_USE_INTERNAL_GMP=ON -DIGRAPH_VERIFY_FINALLY_STACK=ON -DCMAKE_BUILD_TYPE=Debug -DIGRAPH_PRINT_ARITH_HEADER=ON -notifications: - email: - on_success: change - on_failure: always +#notifications: +# email: +# on_success: change +# on_failure: always diff --git a/src/vendor/cigraph/.zenodo.json b/src/vendor/cigraph/.zenodo.json index d3545885a24..231fa545526 100644 --- a/src/vendor/cigraph/.zenodo.json +++ b/src/vendor/cigraph/.zenodo.json @@ -7,7 +7,27 @@ ], "creators": [ { - "name": "The igraph Core Team" + "name": "Csárdi, Gábor", + "orcid": "0000-0001-7098-9676" + }, + { + "name": "Nepusz, Tamás", + "orcid": "0000-0002-1451-338X" + }, + { + "name": "Horvát, Szabolcs", + "orcid": "0000-0002-3100-523X" + }, + { + "name": "Traag, Vincent", + "orcid": "0000-0003-3170-3879" + }, + { + "name": "Zanini, Fabio", + "orcid": "0000-0001-7097-8539" + }, + { + "name": "Noom, Daniel" } ] } diff --git a/src/vendor/cigraph/ACKNOWLEDGEMENTS.md b/src/vendor/cigraph/ACKNOWLEDGEMENTS.md index 579c46c6667..f32713268c9 100644 --- a/src/vendor/cigraph/ACKNOWLEDGEMENTS.md +++ b/src/vendor/cigraph/ACKNOWLEDGEMENTS.md @@ -87,13 +87,6 @@ Copyright (C) 2006-2008 Aaron Clauset. License: [GNU GPLv2][gpl2] or later -#### SCGlib (Spectral Coarse Graining) - -Copyright (C) 2008 David Morton de Lachapelle - -License: [GNU GPLv2][gpl2] or later - - #### Spinglass community detection Copyright (C) 2004 by Joerg Reichardt. @@ -185,12 +178,12 @@ use or performance of this software. License: [GNU LGPLv2.1][lgpl2] or later -#### [GLPK (GNU Linear Programming Kit) Version 4.45](https://www.gnu.org/software/glpk/) +#### [GLPK (GNU Linear Programming Kit) Version 5.0](https://www.gnu.org/software/glpk/) + +Copyright (C) 2000-2020 Free Software Foundation, Inc. -Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, -2009, 2010 Andrew Makhorin, Department for Applied Informatics, -Moscow Aviation Institute, Moscow, Russia. All rights reserved. -E-mail: . +Written by Andrew Makhorin, Department for Applied Informatics, +Moscow Aviation Institute, Moscow, Russia. E-mail: . License: [GNU GPLv3][gpl3] or later diff --git a/src/vendor/cigraph/AUTHORS b/src/vendor/cigraph/AUTHORS index 5abbf975b79..446955dd5d9 100644 --- a/src/vendor/cigraph/AUTHORS +++ b/src/vendor/cigraph/AUTHORS @@ -3,3 +3,4 @@ Tamas Nepusz Szabolcs Horvat Vincent Traag Fabio Zanini +Daniel Noom diff --git a/src/vendor/cigraph/CHANGELOG.md b/src/vendor/cigraph/CHANGELOG.md index 4c9f37dc51a..faa6a6dfa44 100644 --- a/src/vendor/cigraph/CHANGELOG.md +++ b/src/vendor/cigraph/CHANGELOG.md @@ -1,5 +1,541 @@ # igraph C library changelog +## [master] + +### Changes + + - `igraph_community_walktrap()` no longer requires `modularity` and `merges` to be non-NULL when `membership` is non-NULL. + - `igraph_isomorphic()` now supports multigraphs. + - Shortest path related functions now consistently ignore edges with positive infinite weights. + +### Fixed + + - `igraph_hub_and_authority_scores()`, `igraph_hub_score()` and `igraph_authority_score()` considered self-loops only once on the diagonal of the adjacency matrix of undirected graphs, thus the result was not identical to that obtained by `igraph_eigenvector_centrality()` on loopy undirected graphs. This is now corrected. + - `igraph_community_infomap()` now checks edge and vertex weights for validity. + - `igraph_minimum_spanning_tree()` and `igraph_minimum_spanning_tree_prim()` now check that edge weights are not NaN. + - Fixed an initialization error in the string attribute combiner of the C attribute handler. + - Fixed an issue with the weighted clique number calculation when all the weights were the same. + - HRG functions now require a graph with at least 3 vertices; previous versions crashed with smaller graphs. + - `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()`, i.e. the ARPACK interface in igraph, are now interruptible. As a result, several other functions that rely on ARPACK (eigenvector centrality, hub and authority scores, etc.) also became interruptible. + - `igraph_get_shortest_paths_dijkstra()`, `igraph_get_all_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now validate the `from` vertex. + - Fixed bugs in `igraph_local_scan_1_ecount()` for weighted undirected graphs which would miscount loops and multi-edges. + +### Deprecated + +- `igraph_automorphisms()` is now deprecated; its new name is `igraph_count_automorphisms()`. The old name is kept available until at least igraph 0.11. +- `igraph_hub_score()` and `igraph_authority_score()` are now deprecated. Use `igraph_hub_and_authority_scores()` instead. +- `igraph_get_incidence()` is now deprecated; its new name is `igraph_get_biadjacency()` to reflect that the returned matrix is an _adjacency_ matrix between pairs of vertices and not an _incidence_ matrix between vertices and edges. The new name is kept available until at least igraph 0.11. We plan to re-use the name in later versions to provide a proper incidence matrix where the rows are vertices and the columns are edges. +- `igraph_hrg_dendrogram()` is deprecated because it requires an attribute handler and it goes against the convention of returning attributes in vectors where possible. Use `igraph_from_hrg_dendrogram()` instead, which constructs the dendrogram as an igraph graph _and_ returns the associated probabilities in a vector. + +### Other + + - Improved performance for `igraph_vertex_connectivity()`. + - `igraph_simplify()` makes use of the cache, and avoids simplification when the graph is already known to be simple. + - Documentation improvements. + +## [0.10.4] - 2023-01-26 + +### Added + + - `igraph_get_shortest_path_astar()` finds a shortest path with the A* algorithm. + - `igraph_vertex_coloring_greedy()` now supports the DSatur heuristics through `IGRAPH_COLORING_GREEDY_DSATUR` (#2284, thanks to @professorcode1). + +### Changed + + - The `test` build target now only _runs_ the unit tests, but it does not _build_ them. In order to both build and run tests, use the `check` target, which continues to behave as before (PR #2291). + - The experimental function `igraph_distances_floyd_warshall()` now has `from` and `to` parameters for choosing source and target vertices. + - The experimental function `igraph_distances_floyd_warshall()` now has an additional `method` parameter to select a specific algorithm. A faster "Tree" variant of the Floyd-Warshall algorithm is now available (#2267, thanks to @rfulekjames). + +### Fixed + + - The Bellman-Ford shortest path finder is now interruptible. + - The Floyd-Warshall shortest path finder is now interruptible. + - Running CTest no longer builds the tests automatically, as this interfered with VSCode, which would invoke the `ctest` executable after configuring a project in order to determine test executables. Use the `build_tests` target to build the tests first, or use the `check` target to both _build_ and _run_ all unit tests (PR #2291). + +### Other + + - Improved the performance and memory usage of `igraph_widest_path_widths_floyd_warshall()`. + - Documentation improvements. + +## [0.10.3] - 2022-12-30 + +### Added + + - `igraph_matrix_init_array()` to initialize an igraph matrix by copying an existing C array in column-major or row-major order. + - `igraph_layout_umap_compute_weights()` computes weights for the UMAP layout algorithm from distances. This used to be part of `igraph_layout_umap()`, but it is now in a separate function to allow the user to experiment with different weighting schemes. + - `igraph_triangular_lattice()` to generate triangular lattices of various kinds (#2235, thanks to @rfulekjames). + - `igraph_hexagonal_lattice()` to generate hexagonal lattices of various kinds (#2262, thanks to @rfulekjames). + - `igraph_tree_from_parent_vector()` to create a tree or a forest from a parent vector (i.e. a vector that encodes the parent vertex of each vertex). + - `igraph_induced_subgraph_edges()` produces the IDs of edges contained within a subgraph induced by the given vertices. + +### Changed + + - The signature of the experimental `igraph_layout_umap()` function changed; the last argument is now a Boolean that specifies whether distances should already be treated as weights, and the sampling probability argument was removed. + +### Fixed + + - `igraph_transitivity_barrat()`, `igraph_community_fluid_communities()`, `igraph_sir()`, `igraph_trussness()` and graphlet functions did not correctly detect when a directed input graph had effective multi-edges due to ignoring edge directions. Such graphs are now rejected by these functions. + - Fixed a bug in `igraph_2dgrid_move()` that sometimes crashed the Large Graph Layout function when a grid cell became empty. + - `igraph_pagerank()` and `igraph_personalized_pagerank()` would fail to converge when the ARPACK implementation was used and a vertex had more than one outgoing edge but all these edges had zero weights. + - `igraph_pagerank()` and `igraph_personalized_pagerank()` no longer allow negative weights. Previously, edges with negative weights were silently ignored when using the PRPACK implementation. The ARPACK implementation would issue a warning saying that they are ignored, but in fact it computed an incorrect result. + - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` no longer trigger the "Finally stack too large" fatal error when called on certain large graphs. This was a regression in igraph 0.10. + - `igraph_community_label_propagation()` no longer rounds weights to integers. This was a regression in igraph 0.10. + - `igraph_read_graph_graphdb()` does more thorough checks on the input file. + - `igraph_calloc()` did not zero-initialize the allocated memory. This is now corrected. Note that the macro `IGRAPH_CALLOC()` was _not_ affected. + - Fixed new warnings issued by the Xcode 14.1 toolchain. + +### Deprecated + +- `igraph_subgraph_edges()` is now deprecated to avoid confusion with `igraph_induced_subgraph_edges()`; its new name is `igraph_subgraph_from_edges()`. The old name is kept available until at least igraph 0.11. + +### Other + + - Significantly improved performance for `igraph_matrix_transpose()`. + - Documentation improvements. + +## [0.10.2] - 2022-10-14 + +### Added + + - `igraph_distances_cutoff()` and `igraph_distances_dijkstra_cutoff()` calculate shortest paths with an upper limit on the path length (experimental functions). + - `igraph_distances_floyd_warshall()` for computing all-pairs shortest path lengths in dense graphs (experimental function). + - `igraph_ecc()` computes the edge clustering coefficient of some edges (experimental function). + - `igraph_voronoi()` computes a Voronoi partitioning of vertices (experimental function). + - `igraph_count_multiple_1()` determines the multiplicity of a single edge in the graph. + - `igraph_dqueue_get()` accesses an element in a queue by index. + - `igraph_degree_1()` efficiently retrieves the degee of a single vertex. + - `igraph_lazy_adjlist_has()` and `igraph_lazy_inclist_has()` to check if adjacent vertices / incident edges have already been computed and stored for a given vertex in a lazy adjlist / inclist. + +### Changed + + - `igraph_edge()` now verifies that the input edge ID is valid. + - `igraph_community_leading_eigenvector()`, `igraph_adjacency_spectral_embedding()`, `igraph_laplacian_spectral_embedding()`, `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()` now generate a random starting vector using igraph's own RNG if needed instead of relying on LAPACK or ARPACK to do so. This makes sure that the results obtained from these functions remain the same if igraph's RNG is seeded with the same value. + - `igraph_community_leading_eigenvector()` does not stop the splitting process any more when there are multiple equally likely splits (indicated by the multiplicity of the leading eigenvector being larger than 1). The algorithm picks an arbitrary split instead and proceeds normally. + +### Fixed + + - Fixed a bug in `igraph_get_k_shortest_paths()` that sometimes yielded incorrect results on undirected graphs when the `mode` argument was set to `IGRAPH_OUT` or `IGRAPH_IN`. + - `igraph_trussness()` is now interruptible. + - `igraph_spanner()` is now interruptible. + - `igraph_layout_umap()` and `igraph_layout_umap3d()` are now interruptible. + - In some rare cases, roundoff errors would cause `igraph_distance_johnson()` to fail on graphs with negative weights. + - `igraph_eulerian_cycle()` and `igraph_eulerian_path()` now returns a more specific error code (`IGRAPH_ENOSOL`) when the graph contains no Eulerian cycle or path. + - `igraph_heap_init_array()` did not copy the array data correctly for non-real specializations. + - `igraph_layout_umap_3d()` now actually uses three dimensions. + - `igraph_layout_umap()` and `igraph_layout_umap_3d()` are now interruptible. + - `igraph_vit_create()` and `igraph_eit_create()` no longer fails when trying to create an iterator for the null graph or edgeless graph from an empty range-based vertex or edge selector. + - `igraph_write_graph_leda()` did not correctly print attribute names in some warning messages. + - Addressed new warnings introduced by Clang 15. + - In the generated pkg-config file, libxml2 is now placed in the `Requires.private` section instead of the `Libs.private` one. + +### Removed + + - Removed unused and undocumented `igraph_bfgs()` function. + - Removed the undocumented function `igraph_complex_mod()`. Use `igraph_complex_abs()` instead, as it has identical functionality. + +### Deprecated + + - The `IGRAPH_EDRL` error code was deprecated; the DrL algorithm now returns `IGRAPH_FAILURE` when it used to return `IGRAPH_EDRL` (not likely to happen in practice). + - The undocumented function `igraph_dqueue_e()` is now deprecated and replaced by `igraph_dqueue_get()`. + - `igraph_finite()`, `igraph_is_nan()`, `igraph_is_inf()`, `igraph_is_posinf()` and `igraph_is_neginf()` are now deprecated. They were relics from a time when no standard alternatives existed. Use the C99 standard `isfinite()`, `isnan()` and `isinf()` instead. + +### Other + + - Documentation improvements. + +## [0.10.1] - 2022-09-08 + +### Fixed + + - Corrected a regression (compared to igraph 0.9) in weighted clique search functions. + - `igraph_girth()` no longer fails when the graph has no cycles and the `girth` parameter is set to `NULL`. + - `igraph_write_graph_gml()` did not respect entity encoding options when writing the `Creator` line. + - Fixed potential memory leak on out-of-memory condition in `igraph_asymmetric_preference_game()`, `igraph_vs_copy()` and `igraph_es_copy()`. + - Fixed an assertion failure in `igraph_barabasi_game()` and `igraph_barabasi_aging_game()` when passing in negative degree exponents. + - Fixed a compilation failure with some old Clang versions. + +### Changed + + - `igraph_write_graph_leda()` can now write boolean attributes. + +### Other + + - Support for ARM64 on Windows. + - Documentation improvements. + +## [0.10.0] - 2022-09-05 + +### Release notes + +This release focuses on infrastructural improvements, stability, and making the igraph interface more consistent, more predictable and easier to use. It contains many API-breaking changes and function renamings, in preparation for a future 1.0 release, at which point the API will become stable. Changes in this direction are likely to continue through a 0.11 release. It is recommended that you migrate your code from 0.9 to 0.10 soon, to make the eventual transition to 1.0 easier. + +Some of the highlights are: + + - A consistent use of `igraph_integer_t` for all indices and most integer quantities, both in the API and internally. This type is 64-bit by default on all 64-bit systems, bringing support for very large graphs with more than 2 billion vertices. Previously, vertex and edge indices were often represented as `igraph_real_t`. The move to an `igraph_integer_t` also implies a change from `igraph_vector_t` to `igraph_vector_int_t` in many functions. + - The random number generation framework has been overhauled. Sampling from the full range of `igraph_integer_t` is now possible. Similarly, the sampling of random reals has been improved to utilize almost the full range of the mantissa of an `igraph_real_t`. + - There is a new fully memory-managed container type for lists of vectors (`igraph_vector_list_t`), replacing most previous uses of the non-managed `igraph_vector_ptr_t`. Functions that previously used `igraph_vector_ptr_t` to return results and relied on the user to manage memory appropriately are now using `igraph_vector_list_t`, `igraph_graph_list_t` or similar and manage memory on their own. + - Some simple graph properties, such as whether a graph contains self-loops or multi-edges, or whether it is connected, are now cached in the graph data structure. Querying these properties for a second time will take constant computational time. The `igraph_invalidate_cache()` function is provided for debugging purposes. It will invaidate all cache entries. + - File format readers are much more robust and more tolerant of invalid input. + - igraph is much more resilient to overflow errors. + - Many improvements to robustness and reliability, made possible by internal refactorings. + +### Breaking changes + + - igraph now requires CMake 3.18 or later. + - In order to facilitate the usage of graphs with more than 2 billion vertices and edges, we have made the size of the `igraph_integer_t` data type to be 32 bits on 32-bit platforms and 64 bits on 64-bit platforms by default. You also have the option to compile a 32-bit igraph variant on a 64-bit platform by changing the `IGRAPH_INTEGER_SIZE` build variable in CMake to 32. + - `igraph_bool_t` is now a C99 `bool` and not an `int`. Similarly, `igraph_vector_bool_t` now consumes `sizeof(bool)` bytes per entry only, not `sizeof(int)`. The standard constants `true` and `false` may be used for Boolean values for readability. + - The random number generator interface, `igraph_rng_type_t`, has been overhauled. Check the declaration of the type for details. + - The default random number generator has been changed from Mersenne Twister to PCG32. + - Functions related to spectral coarse graining (i.e. all functions starting with `igraph_scg_...`) were separated into a project of its own. If you wish to keep on using these functions, please refer to the repository hosting the spectral coarse graining code at https://github.com/igraph/igraph-scg . The spectral coarse graining code was updated to support igraph 0.10. + - Since `igraph_integer_t` aims to be the largest integer size that is feasible on a particular platform, there is no need for generic data types based on `long int` any more. The `long` variants of generic data types (e.g., `igraph_vector_long_t`) are therefore removed; you should use the corresponding `int` variant instead, whose elements are of type `igraph_integer_t`. + - Generic data types based on `float` were removed as they were not used anywhere in the library. + - Several igraph functions that used to take a `long int` or return a `long int` now takes or returns an `igraph_integer_t` instead to make the APIs more consistent. Similarly, igraph functions that used `igraph_vector_t` for arguments that take or return _integral_ vectors (e.g., vertex or edge indices) now take `igraph_vector_int_t` instead. Graph-related functions where the API was changed due to this reason are listed below, one by one. + - Similarly, igraph functions that used to accept the `long` variant of a generic igraph data type (e.g., `igraph_vector_long_t`) now take the `int` variant of the same data type. + - The type `igraph_stack_ptr_t` and its associated functions were removed. Use `igraph_vector_ptr_t` and associated functions instead. + - Error handlers should no longer perform a `longjmp()`. Doing so will introduce memory leaks, as resource cleanup is now done in multiple stages, through multiple calls to the error handler. Thus, the error handler should either abort execution immediately (as the default handler does), or report the error, call `IGRAPH_FINALLY_FREE()`, and return normally. + - Most callback functions now return an error code. In previous versions they returned a boolean value indicating whether to terminate the search. A request to stop the search is now indicated with the special return code `IGRAPH_STOP`. + - `igraph_add_edges()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_adjacency()` no longer accepts a negative number of edges in its adjacency matrix. When negative entries are found, an error is generated. + - `igraph_adjacency()` gained an additional `loops` argument that lets you specify whether the diagonal entries should be ignored or should be interpreted as raw edge counts or _twice_ the number of edges (which is common in linear algebra contexts). + - `igraph_all_minimal_st_separators()` now returns the separators in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. + - `igraph_all_st_cuts()` and `igraph_all_st_mincuts()` now return the cuts in an `igraph_vector_int_list_t` containing `igraph_vector_int_t` vectors. + - `igraph_arpack_unpack_complex()` now uses `igraph_integer_t` for its `nev` argument instead of `long int`. + - `igraph_articulation_points()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. + - `igraph_assortativity_nominal()` now accepts vertex types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_asymmetric_preferennce_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. + - `igraph_atlas()` now uses `igraph_integer_t` for its `number` argument. + - `igraph_automorphism_group()` now returns the generators in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_barabasi_game()`, `igraph_barabasi_aging_game()`, `igraph_recent_degree_game()` and `igraph_recent_degree_aging_game()` now use an `igraph_vector_int_t` for the out-degree sequence of the nodes being generated instead of an `igraph_vector_t`. + - `igraph_bfs()` now takes an `igraph_vector_int_t` for its `roots`, `restricted`, `order`, `father`, `pred`, `succ` and `dist` arguments instead of an `igraph_vector_t`. + - `igraph_bfs_simple()` now takes `igraph_vector_int_t` for its `vids`, `layers` and `parents` arguments instead of an `igraph_vector_t`. + - `igraph_bfs_simple()` now returns -1 in `parents` for the root node of the traversal, and -2 for unreachable vertices. This is now consistent with other functions that return a parent vector. + - `igraph_biconnected_components()` now uses an `igraph_vector_int_t` to return the list of articulation points, not an `igraph_vector_t`. Also, the container used for the edges and vertices of the components is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_bipartite_projection()` now uses `igraph_vector_int_t` to return `multiplicity1` and `multiplicity2`, not `igraph_vector_t`. + - `igraph_bridges()` now uses an `igraph_vector_int_t` to return the list of bridges, not an `igraph_vector_t`. + - `igraph_callaway_traits_game()` returns the node types in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_canonical_permutation()` now uses an `igraph_vector_int_t` for its labeling parameter. + - `igraph_cattribute_list()` now uses `igraph_vector_int_t` to return `gtypes`, `vtypes` and `etypes`. + - `igraph_cited_type_game()` now uses an `igraph_vector_int_t` for its types parameter. + - `igraph_citing_cited_type_game()` now uses an `igraph_vector_int_t` for its + types parameter. + - `igraph_clique_handler_t` now uses an `igraph_vector_int_t` for its `clique` parameter, and must return an `igraph_error_t`. Use `IGRAPH_STOP` as the return code to terminate the search prematurely. The vector that the handler receives is owned by the clique search routine. If you want to hold on to the vector for a longer period of time, you need to make a copy of it in the handler. Cliques passed to the callback are marked as `const` as a reminder to this change. + - The `res` parameter of `igraph_cliques()` is now an `igraph_vector_int_list_t`. + - Callbacks used by `igraph_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. + - `igraph_closeness()` and `igraph_closeness_cutoff()` now use an `igraph_vector_int_t` to return `reachable_count`, not an `igraph_vector_t`. + - `igraph_cohesive_blocks()` now uses an `igraph_vector_int_t` to return the mapping from block indices to parent block indices, and the `cohesion`; also, it uses an `igraph_vector_int_list_t` to return the blocks themselves instead of a pointer vector of `igraph_vector_t`. + - The `igraph_community_eb_get_merges()` bridges parameter now starts the indices into the edge removal vector at 0, not 1. + - The `igraph_community_eb_get_merges()` now reports an error when not all edges in the graph are removed, instead of a nonsensical result. + - `igraph_community_edge_betweenness()` now uses an `igraph_vector_int_t` to return the edge IDs in the order of their removal as well as the list of edge IDs whose removal broke a single component into two. + - `igraph_community_fluid_communities()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. + - `igraph_community_infomap()` now uses `igraph_integer_t` for its `nb_trials` argument. + - `igraph_community_label_propagation()` now uses an `igraph_vector_int_t` for its `initial` parameter. It also takes a `mode` argument that specifies how labels should be propagated along edges (forward, backward or ignoring edge directions). + - `igraph_community_label_propagation()` does not provide the modularity in a separate output argument any more; use `igraph_modularity()` to retrieve the modularity if you need it. + - `igraph_community_leiden()` has an additional parameter to indicate the number of iterations to perform (PR #2177). + - `igraph_community_walktrap()`, `igraph_community_edge_betweenness()`, `igraph_community_eb_get_merges()`, `igraph_community_fastgreedy()`, `igraph_community_to_membership()`, `igraph_le_community_to_membership()`, `igraph_community_leading_eigenvector()` now use an `igraph_vector_int_t` for their `merges` parameter. + - `igraph_community_walktrap()` now uses `igraph_integer_t` for its `steps` argument. + - `igraph_coreness()` now uses an `igraph_vector_int_t` to return the coreness + values. + - `igraph_convex_hull()` now uses an `igraph_vector_int_t` to return the indices of the input vertices that were chosen to be in the convex hull. + - `igraph_correlated_game()` and `igraph_correlated_pair_game()` now take an `igraph_vector_int_t` as the permutation vector, not an `igraph_vector_t`. + - `igraph_create()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_create_bipartite()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_compose()` now returns the edge maps in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_count_multiple()` now returns the multiplicities in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_decompose()` now uses an `igraph_integer_t` for its `maxcompno` and `minelements` arguments instead of a `long int`. + - `igraph_degree()` now uses an `igraph_vector_int_t` to return the degrees. If you need the degrees in a vector containing floating-point numbers instead (e.g., because you want to pass them on to some other function that takes an `igraph_vector_t`), use `igraph_strength()` instead with a null weight vector. + - `igraph_degree_sequence_game()` now takes degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_degseq_t`, used by `igraph_degree_sequence_game()`, uses new names for its constants. The old names are deprecated, but retained for compatibility. See `igraph_constants.h` to see which new name corresponds to which old one. + - `igraph_delete_vertices_idx()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. + - `igraph_deterministic_optimal_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_dfs()` now takes an `igraph_vector_int_t` for its `order`, `order_out`, `father` and `dist` arguments instead of an `igraph_vector_t`. Furthermore, these vectors will contain -2 for vertices that have not been visited; in earlier versions, they used to contain NaN instead. Note that -1 is still used in the `father` vector to indicate the root of a DFS tree. + - `igraph_diameter()` and `igraph_diameter_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the diameter. + - `igraph_dominator_tree()` now takes an `igraph_vector_int_t` for its `dom` and `leftout` arguments instead of an `igraph_vector_t`. + - `igraph_dyad_census()` now uses `igraph_real_t` instead of `igraph_integer_t` for its output arguments, and it no longer returns -1 when overflow occurs. + - `igraph_edges()` now takes an `igraph_vector_int_t` for its `edges` argument instead of an `igraph_vector_t`. + - `igraph_es_multipairs()` was removed; you can use the newly added `igraph_es_all_between()` instead. + - `igraph_establishment_game()` now takes an `igraph_vector_int_t` for its `node_type_vec` argument instead of an `igraph_vector_t`. + - `igraph_eulerian_path()` and `igraph_eulerian_cycle()` now use `igraph_vector_int_t` to return the list of edge and vertex IDs participating in an Eulerian path or cycle instead of an `igraph_vector_t`. + - `igraph_feedback_arc_set()` now uses an `igraph_vector_int_t` to return the IDs of the edges in the feedback arc set instead of an `igraph_vector_t`. + - `igraph_get_adjacency()` no longer has the `eids` argument, which would produce an adjacency matrix where non-zero values were 1-based (not 0-based) edge IDs. If you need a matrix with edge IDs, create it manually. + - `igraph_get_adjacency_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. + - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now has a `loops` argument that lets the user specify how loop edges should be handled. + - `igraph_get_edgelist()` now uses an `igraph_vector_int_t` for its `res` parameter. + - `igraph_get_eids()` now uses `igraph_vector_int_t` to return lists of edge IDs and to receive lists of vertex IDs. + - The `path` argument of `igraph_get_eids()` was removed. You can replicate the old behaviour by constructing the list of vertex IDs explicitly from the path by duplicating each vertex in the path except the first and last ones. A helper function called `igraph_expand_path_to_pairs()` is provided to ease the transition. + - `igraph_get_eids_multi()` was removed as its design was fundamentally broken; there was no way to retrieve the IDs of all edges between a specific pair of vertices without knowing in advance how many such edges there are in the graph. Use `igraph_get_all_eids_between()` instead. + - `igraph_get_incidence()` now returns the vertex IDs corresponding to the rows and columns of the incidence matrix as `igraph_vector_int_t`. + - `igraph_get_shortest_path()`, `igraph_get_shortest_path_bellman_ford()` and `igraph_get_shortest_path_dijkstra()` now use `igraph_vector_int_t` vectors to return the list of vertex and edge IDs in the shortest path. + - `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_dijkstra()` and `igraph_get_shortest_paths_bellman_ford()` now use an `igraph_vector_int_t` to return the predecessors and inbound edges instead of an `igraph_vector_long_t`. + - The functions `igraph_get_all_shortest_paths()`, `igraph_get_all_shortest_paths_dijkstra()`, `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now return paths in an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - The vector of parents in `igraph_get_shortest_paths()`, `igraph_get_shortest_paths_bellman_ford()` and `igraph_get_shortest_paths_dijkstra()` now use -1 to represent the starting vertex, and -2 for unreachable vertices. + - The `maps` parameters in `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. + - `igraph_get_stochastic()` now has an additional `weights` argument for edge weights. + - `igraph_get_stochastic_sparse()` now returns the sparse adjacency matrix in an `igraph_sparsemat_t` structure, and it assumes that the input matrix is _initialized_ for sake of consistency with other igraph functions. It also received an additional `weights` argument for edge weights. + - `igraph_girth()` now uses an `igraph_vector_int_t` for its `circle` parameter. + - `igraph_girth()` now uses `igraph_real_t` as the return value so we can return infinity for graphs with no cycles (instead of zero). + - The `cliques` parameters of type `igraph_vector_ptr_t` in `igraph_graphlets()`, `igraph_graphlets_candidate_basis()` and `igraph_graphlets_project()` were changed to an `igraph_vector_int_list_t`. + - `igraph_hrg_init()` and `igraph_hrg_resize()` now takes an `igraph_integer_t` as their size arguments instead of an `int`. + - `igraph_hrg_consensus()` now returns the parent vector in an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_hrg_create()` now takes a vector of probabilities corresponding to the internal nodes of the dendogram. It used to also take probabilities for the leaf nodes and then ignore them. + - `igraph_hrg_predict()` now uses an `igraph_vector_int_t` for its `edges` parameter. + - `igraph_hrg_sample()` now always samples a single graph only. Use `igraph_hrg_sample_many()` if you need more than one sample, and call `igraph_hrg_fit()` beforehand if you do not have a HRG model but only a single input graph. + - `igraph_hrg_size()` now returns an `igraph_integer_t` instead of an `int`. + - `igraph_incidence()` does not accept negative incidence counts any more. + - `igraph_incident()` now uses an `igraph_vector_int_t` for its `eids` parameter. + - The `res` parameter in `igraph_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. + - `igraph_induced_subgraph_map()` now uses `igraph_vector_int_t` vectors to return the mapping and the inverse mapping of old vertex IDs to new ones. + - `igraph_intersection()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. + - The `edgemaps` parameter of `igraph_intersection_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. + - `igraph_is_chordal()` now uses an `igraph_vector_int_t` for its `alpha`, `alpham1` and `fill_in` parameters. + - `igraph_is_graphical()` and `igraph_is_bigraphical()` now take degree sequences represented as `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_is_matching()`, `igraph_is_maximal_matching()` and `igraph_maximum_bipartite_matching` now use an `igraph_vector_int_t` to return the matching instead of an `igraph_vector_long_t`. + - `igraph_is_mutual()` has an additional parameter which controls whether directed self-loops are considered mutual. + - The `vids` parameter for `igraph_isoclass_subgraph()` is now an `igraph_vector_int_t` instead of `igraph_vector_t`. + - `igraph_isomorphic_vf2()`, `igraph_get_isomorphisms_vf2_callback()` (which used to be called `igraph_isomorphic_function_vf2()`) and `igraph_isohandler_t` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. + - The `cliques` parameter of type `igraph_vector_ptr_t` in `igraph_largest_cliques()` was changed to an `igraph_vector_int_list_t`. + - The `res` parameters of type `igraph_vector_ptr_t` in `igraph_largest_independent_vertex_sets()` and `igraph_largest_weighted_cliques()` were changed to an `igraph_vector_int_list_t`. + - The dimension vector parameter for `igraph_square_lattice()` (used to be `igraph_lattice()`) is now an `igraph_vector_int_t` instead of `igraph_vector_t`. + - The maxiter parameter of `igraph_layout_bipartite()` is now an `igraph_integer_t` instead of `long int`. + - The fixed parameter of `igraph_layout_drl()` and `igraph_layout_drl_3d()` was removed as it has never been implemented properly. + - The width parameter of `igraph_layout_grid()` is now an `igraph_integer_t` instead of `long int`. + - The width and height parameters of `igraph_layout_grid_3d()` are now `igraph_integer_t` instead of `long int`. + - The dimension parameter of `igraph_layout_mds()` is now an `igraph_integer_t` instead of `long int`. + - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. + - The `roots` and `rootlevel` parameters of `igraph_layout_reingold_tilford_circular()` are now `igraph_vector_int_t` instead of `igraph_vector_t`. + - The order parameter of `igraph_layout_star()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - The maxiter parameter of `igraph_layout_sugiyama()` is now an `igraph_integer_t` instead of `long int`. Also, the function now uses an `igraph_vector_int_t` for its `extd_to_orig_eids` parameter. + - The shifts parameter of `igraph_lcf_vector()` is now an `igraph_vector_int_t` instead of an `igraph_vector_t`. + - `igraph_matrix_minmax()`, `igraph_matrix_which_minmax()`, `igraph_matrix_which_min()` and `igraph_matrix_which_max()` no longer return an error code. The return type is now `void`. These functions never fail. + - `igraph_maxflow()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - The `igraph_maxflow_stats_t` struct now contains `igraph_integer_t` values instead of `int` ones. + - The `res` parameters in `igraph_maximal_cliques()` and `igraph_maximal_cliques_subset()` are now of type `igraph_vector_int_list_t`. + - Callbacks used by `igraph_maximal_cliques_callback()` need to be updated to account for the fact that the callback does not own the clique passed to it any more; the callback needs to make a copy if it wants to hold on to the clique for a longer period of time. If the callback does not need to store the clique, it does not need to do anything any more, and it must not destroy or free the clique. + - The `res` parameter in `igraph_maximal_independent_vertex_sets()` is now an `igraph_vector_int_list_t`. + - `igraph_maximum_cardinality_search()` now uses an `igraph_vector_int_t` for its `alpha` and `alpham1` arguments. + - `igraph_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - `igraph_moran_process()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - Motif callbacks of type `igraph_motifs_handler_t` now take an `igraph_vector_int_t` with the vertex IDs instead of an `igraph_vector_t`, and use `igraph_integer_t` for the isoclass parameter. + - Motif functions now use `igraph_integer_t` instead of `int` for their `size` parameter. + - `igraph_neighborhood_size()` now uses an `igraph_vector_int_t` for its `res` parameter. + - The `res` parameter of `igraph_neighborhood()` is now an `igraph_vector_int_list_t`. + - `igraph_neighbors()` now uses an `igraph_vector_int_t` for its `neis` parameter. + - `igraph_permute_vertices()` now takes an `igraph_vector_int_t` as the permutation vector. + - `igraph_power_law_fit()` does not calculate the p-value automatically any more because the previous estimation method did not match the results from the original paper of Clauset, Shalizi and Newman (2009) and the implementation of the method outlined in the paper runs slower than the previous naive estimate. A separate function named `igraph_plfit_result_calculate_p_value()` is now provided for calculating the p-value. The automatic selection of the `x_min` cutoff also uses a different method than earlier versions. As a consequence, results might be slightly different if you used tests where the `x_min` cutoff was selected automatically. The new behaviour is now consistent with the defaults of the underlying `plfit` library. + - `igraph_preference_game()` now uses an `igraph_vector_int_t` to return the types of the nodes in the generated graph. + - `igraph_random_walk()` now uses an `igraph_vector_int_t` for its results. Also, the function now takes both vertices and edges as parameters. It can return IDs of vertices and/or edges on the walk. The function now takes weights as a parameter to support weighted graphs. + - `igraph_random_edge_walk()` now uses an `igraph_vector_int_t` for its `edgewalk` parameter. + - `igraph_read_graph_dimacs_flow()` now uses an `igraph_vector_int_t` for its label parameter. + - `igraph_read_graph_graphml()` now uses `igraph_integer_t` for its `index` argument. + - `igraph_read_graph_pajek()` now creates a Boolean `type` attribute for bipartite graphs. Previously it created a numeric attribute. + - `igraph_realize_degree_sequence()` now uses an `igraph_vector_int_t` for its `outdeg` and `indeg` parameters. + - `igraph_reindex_membership()` now uses an `igraph_vector_int_t` for its `new_to_old` parameter. + - `igraph_rng_seed()` now requires an `igraph_uint_t` as its seed arguments. RNG implementations are free to use only the lower bits of the seed if they do not support 64-bit seeds. + - `igraph_rngtype_rand` (i.e. the RNG that is based on BSD `rand()`) was removed due to poor statistical properties that sometimes resulted in weird artifacts like all-even "random" numbers when igraph's usage patterns happened to line up with the shortcomings of the `rand()` generator in a certain way. + - `igraph_roulette_wheel_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_similarity_dice_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. + - `igraph_similarity_jaccard_pairs()` now uses an `igraph_vector_int_t` for its `pairs` parameter. + - `igraph_simple_interconnected_islands_game()` does not generate multi-edges between islands any more. + - `igraph_sort_vertex_ids_by_degree()` and `igraph_topological_sorting()` now use an `igraph_vector_int_t` to return the vertex IDs instead of an `igraph_vector_t`. + - `igraph_spanning_tree()`, `igraph_minimum_spanning_tree()` and `igraph_random_spanning_tree()` now all use an `igraph_vector_int_t` to return the vector of edge IDs in the spanning tree instead of an `igraph_vector_t`. + - `igraph_sparsemat_cholsol()`, `igraph_sparsemat_lusol()`, `igraph_sparsemat_symbqr()` and `igraph_sparsemat_symblu()` now take an `igraph_integer_t` as their `order` parameter. + - `igraph_sparsemat_count_nonzero()` and `igraph_sparsemat_count_nonzerotol()` now return an `igraph_integer_t`. + - `igraph_sparsemat_is_symmetric()` now returns an error code and the result itself is provided in an output argument. + - The `values` argument of `igraph_sparsemat_transpose()` was removed; now the function always copies the values over to the transposed matrix. + - `igraph_spmatrix_t` and related functions were removed as they mostly duplicated functionality that was already present in `igraph_sparsemat_t`. Functions that used `igraph_spmatrix_t` in the library now use `igraph_sparsemat_t`. + - `igraph_stochastic_imitation()` now expects the list of strategies in an `igraph_vector_int_t` instead of an `igraph_int_t`. + - `igraph_st_mincut()` now uses an `igraph_vector_int_t` for its `cut`, `partition` and `partition2` parameters. + - `igraph_st_vertex_connectivity()` now ignores edges between source and target for `IGRAPH_VCONN_NEI_IGNORE` + - `igraph_strvector_get()` now returns strings in the return value, not in an output argument. + - `igraph_subcomponent()` now uses an `igraph_integer_t` for the seed vertex instead of an `igraph_real_t`. It also uses an `igraph_vector_int_t` to return the list of vertices in the same component as the seed vertex instead of an `igraph_vector_t`. + - `igraph_subisomorphic_vf2()`, `igraph_get_subisomorphisms_vf2_callback()` (which used to be called `igraph_subisomorphic_function_vf2()`) and `igraph_isomorphic_bliss()` now all use `igraph_vector_int_t` for their `map12` and `map21` parameters. + - The `maps` parameters in `igraph_subisomorphic_lad()`, `igraph_get_isomorphisms_vf2()` and `igraph_get_subisomorphisms_vf2()` are now of type `igraph_vector_int_list_t`. + - `igraph_subisomorphic_lad()` now uses an `igraph_vector_int_t` for its `map` parameter. Also, its `domains` parameter is now an `igraph_vector_int_list_t` instead of a pointer vector containing `igraph_vector_t` objects. + - `igraph_unfold_tree()` now uses an `igraph_vector_int_t` for its `vertex_index` and `roots` parameters. + - `igraph_union()` now uses an `igraph_vector_int_t` for its `edge_map1` and `edge_map2` parameters. + - The `edgemaps` parameter of `igraph_union_many()` is now an `igraph_vector_int_list_t` instead of a pointer vector. + - `igraph_vector_init_copy()` was refactored to take _another_ vector that the newly initialized vector should copy. The old array-based initialization function is now called `igraph_vector_init_array()`. + - `igraph_vector_ptr_init_copy()` was renamed to `igraph_vector_ptr_init_array()` for sake of consistency. + - `igraph_vs_vector()`, `igraph_vss_vector()` and `igraph_vs_vector_copy()` now all take an `igraph_vector_int_t` as the vector of vertex IDs, not an `igraph_vector_t`. Similarly, `igraph_vs_as_vector()` now returns the vector of matched vertex IDs in an `igraph_vector_int_t`, not an `igraph_vector_t`. + - The `res` parameter of `igraph_weighted_cliques()` is now an `igraph_vector_int_list_t`. + - `igraph_write_graph_dimacs_flow()` now uses `igraph_integer_t` for the source and target vertex index instead of a `long int`. + - `igraph_vector_*()`, `igraph_matrix_*()`, `igraph_stack_*()`, `igraph_array_*()` and several other generic igraph data types now use `igraph_integer_t` for indexing, _not_ `long int`. Please refer to the headers for the exact details; the list of affected functions is too large to include here. + - `igraph_vector_minmax()` and `igraph_vector_which_minmax()` no longer return an error code. The return type is now `void`. These functions never fail. + - `igraph_vector_order()` was removed; use `igraph_vector_int_pair_order()` instead. (The original function worked for vectors containing integers only). + - `igraph_vector_resize_min()` and `igraph_matrix_resize_min()` no longer return an error code (return type is now `void`). The vector or matrix is always left in a consistent state by these functions, with all data intact, even if releasing unused storage is not successful. + - `igraph_vector_qsort_ind()` and its variants now take an `igraph_order_t` enum instead of a boolean to denote whether the order should be ascending or descending. + - `igraph_weighted_adjacency()` now returns the weights in a separate vector instead of storing it in a vertex attribute. The reason is twofold: first, the previous solution worked only with the C attribute handler (not the ones from the higher-level interfaces), and second, it wasn't consistent with other igraph functions that use weights provided as separate arguments. + - The `loops` argument of `igraph_weighted_adjacency()` was converted to an `igraph_loops_t` for sake of consistency with `igraph_adjacency()` and `igraph_get_adjacency()`. + - `igraph_write_graph_gml()` takes an additional bitfield parameter controlling some aspects of writing the GML file. + - The `add_edges()` function in the attribute handler now takes an `igraph_vector_int_t` for its `edges` parameter instead of an `igraph_vector_t`. The `add_vertices()` function now takes an `igraph_integer_t` for the vertex count instead of a `long int`. The `combine_vertices()` and `combine_edges()` functions now take an `igraph_vector_ptr_t` containing vectors of type `igraph_vector_int_t` in their `merges` parameters. The `get_info()` function now uses `igraph_vector_int_t` to return the types of the graph, vertex and edge attribute types. The `permute_vertices()` and `permute_edges()` functions in the attribute handler tables now take an `igraph_vector_int_t` instead of an `igraph_vector_t` for the index vectors. These are relevant only to maintainers of higher level interfaces to igraph; they should update their attribute handlers accordingly. + - igraph functions that interface with external libraries such as BLAS or LAPACK may now fail if the underlying BLAS or LAPACK implementation cannot handle the size of input vectors or matrices (BLAS and LAPACK are usually limited to vectors whose size fits in an `int`). `igraph_blas_dgemv()` and `igraph_blas_dgemv_array()` thus now return an `igraph_error_t`, which may be set to `IGRAPH_EOVERFLOW` if the input vectors or matrices are too large. + - Functions that used an `igraph_vector_t` to represent cluster size and cluster membership now use an `igraph_vector_int_t` instead. These are: + - `igraph_connected_components()` (used to be `igraph_clusters()` in 0.9 and before) + - `igraph_community_eb_get_merges()` + - `igraph_community_edge_betweenness()` + - `igraph_community_fastgreedy()` + - `igraph_community_fluid_communities()` + - `igraph_community_infomap()` + - `igraph_community_label_propagation()` + - `igraph_community_leading_eigenvector()` + - `igraph_community_leiden()` + - `igraph_community_multilevel()` + - `igraph_community_optimal_modularity()` + - `igraph_community_spinglass()` + - `igraph_community_spinglass_single()` + - `igraph_community_to_membership()` + - `igraph_community_walktrap()` + - `igraph_compare_communities()` + - `igraph_le_community_to_membership()` + - `igraph_modularity()` + - `igraph_reindex_membership()` + - `igraph_split_join_distance()` + - `igraph_community_multilevel()` additionally uses a `igraph_matrix_int_t` instead of `igraph_matrix_t()` for its memberships parameter. + - `IGRAPH_TOTAL` was removed from the `igraph_neimode_t` enum; use the equivalent `IGRAPH_ALL` instead. + +### Added + + - A new integer type, `igraph_uint_t` has been added. This is the unsigned pair of `igraph_integer_t` and they are always consistent in size. + - A new container type, `igraph_vector_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of vectors. The type contains `igraph_vector_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). There is also a variant named `igraph_vector_int_list_t` for vectors of `igraph_vector_int_t` objects. + - A new container type, `igraph_matrix_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of matrices. The type contains `igraph_matrix_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). + - A new container type, `igraph_graph_list_t` has been added, replacing most uses of `igraph_vector_ptr_t` in the API where it was used to hold a variable-length list of graphs. The type contains `igraph_t` objects, and it is fully memory managed (i.e. its contents do not need to be allocated and destroyed manually). + - The vector container type, `igraph_vector_t`, has been extended with a new variant whose functions all start with `igraph_vector_fortran_int_...`. This vector container can be used for interfacing with Fortran code as it guarantees that the integers in the vector are compatible with Fortran integers. Note that `igraph_vector_int_t` is not suitable any more, as the elements of `igraph_vector_int_t` are of type `igraph_integer_t`, whose size may differ on 32-bit and 64-bit platforms, depending on how igraph was compiled. + - `igraph_adjlist_init_from_inclist()` to create an adjacency list from an already existing incidence list by resolving edge IDs to their corresponding endpoints. This function is useful for algorithms when both an adjacency and an incidence list is needed and they should be in the same order. + - `igraph_almost_equals()` and `igraph_cmp_epsilon()` to compare floating point numbers with a relative tolerance. + - `igraph_betweenness_subset()` and `igraph_edge_betweenness_subset()` calculates betweenness and edge betweenness scores using shortest paths between a subset of vertices only (#1711, thanks to @guyroznb) + - `igraph_blas_dgemm()` to multiply two matrices. + - `igraph_calloc()` and `igraph_realloc()` are now publicly exposed; these functions provide variants of `calloc()` and `realloc()` that can safely be deallocated within igraph functions. + - `igraph_circulant()` to create circulant graphs (#1856, thanks to @Gomango999). + - `igraph_complex_almost_equals()` to compare complex numbers with a relative tolerance. + - `igraph_eccentricity_dijkstra()` finds the longest weighted path length among all shortest paths between a set of vertices. + - `igraph_enter_safelocale()` and `igraph_exit_safelocale()` for temporarily setting the locale to C. Foreign format readers and writers require a locale which uses a decimal point instead of decimal comma. + - `igraph_es_all_between()` to create an edge selector that selects all edges between a pair of vertices. + - `igraph_full_multipartite()` generates full multipartite graphs (a generalization of bipartite graphs to multiple groups). + - `igraph_fundamental_cycles()` computes a fundamental cycle basis (experimental). + - `igraph_generalized_petersen()` to create generalized Petersen graphs (#1844, thanks to @alexsyou). + - `igraph_get_all_eids_between()` returns the IDs of all edges between a pair of vertices. + - `igraph_get_k_shortest_paths()` finds the k shortest paths between a source and a target vertex. + - `igraph_get_laplacian()` and `igraph_get_laplacian_sparse()` return the Laplacian matrix of the graph as a dense or sparse matrix, with various kinds of normalizations. They replace the now-deprecated `igraph_laplacian()` function. This makes the API consistent with `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()`. + - `igraph_get_widest_path()`, `igraph_get_widest_paths()`, `igraph_widest_path_widths_dijkstra()` and `igraph_widest_path_widths_floyd_warshall()` to find widest paths (#1893, thanks to @Gomango999). + - `igraph_graph_center()` finds the central vertices of the graph. The central vertices are the ones having a minimum eccentricity (PR #2084, thanks to @pradkrish). + - `igraph_graph_count()` returns the number of unlabelled graphs on a given number of vertices. It is meant to find the maximum isoclass value. + - `igraph_has_mutual()` checks if a directed graph has any mutual edges. + - `igraph_heap_clear()` and `igraph_heap_min_clear()` remove all elements from an `igraph_heap_t` or an `igraph_heap_min_t`, respectively. + - `igraph_invalidate_cache()` invalidates all cached graph properties, forcing their recomputation next time they are requested. This function should not be needed in everyday usage, but may be useful in debugging and benchmarking. + - `igraph_is_forest()` to check whether a graph is a forest (#1888, thanks to @rohitt28). + - `igraph_is_acyclic()` to check whether a graph is acyclic (#1945, thanks to @borsgeorgica). + - `igraph_is_perfect()` to check whether a graph is a perfect graph (#1730, thanks to @guyroznb). + - `igraph_hub_and_authority_scores()` calculates the hub and authority scores of a graph as a matching pair. + - `igraph_layout_umap()` and `igraph_layout_umap_3d()` to lay out a graph in 2D or 3D space using the UMAP dimensionality reduction algorithm. + - `igraph_local_scan_subset_ecount()` counts the number of edges in induced sugraphs from a subset of vertices. + - `igraph_matrix_view_from_vector()` allows interpreting the data stored in a vector as a matrix of the specified size. + - `igraph_minimum_cycle_basis()` computes an unweighted minimum cycle basis (experimental). + - `igraph_pseudo_diameter()` and `igraph_pseudo_diameter_dijkstra()` to determine a lower bound for the diameter of a graph (unweighted or weighted). + - `igraph_regular_tree()` creates a regular tree where all internal vertices have the same total degree. + - `igraph_rngtype_pcg32` and `igraph_rngtype_pcg64` implement 32-bit and 64-bit variants of the PCG random number generator. + - `igraph_rng_get_pois()` generates random variates from the Poisson distribution. + - `igraph_roots_for_tree_layout()` computes a set of roots suitable for a nice tree layout. + - `igraph_spanner()` calculates a spanner of a graph with a given stretch factor (#1752, thanks to @guyroznb) + - `igraph_sparse_adjacency()` and `igraph_sparse_weighted_adjacency()` constructs graphs from (weighted) sparse matrices. + - `igraph_sparsemat_get()` to retrieve a single element of a sparse matrix. + - `igraph_sparsemat_normalize_rows()` and `igraph_sparsemat_normalize_cols()` to normalize sparse matrices row-wise or column-wise. + - `igraph_stack_capacity()` to query the capacity of a stack. + - `igraph_strvector_capacity()` returns the maximum number of strings that can be stored in a string vector without reallocating the memory block holding the pointers to the individual strings. + - `igraph_strvector_merge()` moves all strings from one string vectors to the end of another without re-allocating them. + - `igraph_strvector_push_back_len()` adds a new string to the end of a string vector and allows the user to specify the length of the string being added. + - `igraph_strvector_reserve()` reserves space for a given number of string pointers in a string vector. + - `igraph_symmetric_tree()` to create a tree with the specified number of branches at each level (#1859, thanks to @YuliYudith and @DoruntinaM). + - `igraph_trussness()` calculates the trussness of each edge in the graph (#1034, thanks to @alexperrone) + - `igraph_turan()` generates Turán graphs (#2088, thanks to @pradkrish) + - `igraph_vector_all_almost_e()`, `igraph_vector_complex_all_almost_e()`, `igraph_matrix_all_almost_e()`, `igraph_matrix_complex_all_almost_e()` for elementwise comparisons of floating point vector and matrices with a relative tolerance. + - `igraph_vector_complex_zapsmall()` and `igraph_matrix_complex_zapsmall()` for replacing small components of complex vector or matrix elements with exact zeros. + - `igraph_vector_lex_cmp_untyped()` and `igraph_vector_colex_cmp_untyped()` for lexicographic and colexicographic comparison of vectors, similarly to `igraph_vector_lex_cmp()` and `igraph_vector_colex_cmp()`. The difference between the two variants is that the untyped versions declare the vectors as `const void*`, making the functions suitable as comparators for `qsort()`. + - `igraph_vector_permute()` functions to permute a vector based on an index vector. + - `igraph_vector_ptr_sort_ind()` to obtain an index vector that would sort a vector of pointers based on some comparison function. + - `igraph_vector_range()` to fill an existing vector with a range of increasing numbers. + - `igraph_vector_remove_fast()` functions to remove an item from a vector by swapping it with the last element and then popping it off. It allows one to remove an item from a vector in constant time if the order of items does not matter. + - `igraph_vertex_path_from_edge_path()` converts a sequence of edge IDs representing a path to an equivalent sequence of vertex IDs that represent the vertices the path travelled through. + - `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` creates vertex and edge sequences from C-style intervals (closed from the left, open from the right). + - `igraph_wheel()` to create a wheel graph (#1938, thanks to @kwofach). + +### Removed + + - `igraph_adjlist_remove_duplicate()`, `igraph_betweenness_estimate()`, `igraph_closeness_estimate()`, `igraph_edge_betweenness_estimate()`, `igraph_inclist_remove_duplicate()`, `igraph_is_degree_sequence()` and `igraph_is_graphical_degree_sequence()` were deprecated earlier in 0.9.0 and are now removed in this release. + - `igraph_dnorm()`, `igraph_strvector_move_interval()`, `igraph_strvector_permdelete()` and `igraph_strvector_remove_negidx()` were removed. These are not breaking changes as the functions were never documented, they were only exposed from one of the headers. + - `igraph_eigen_laplacian()`, `igraph_es_fromto()` and `igraph_maximum_matching()` were removed. These are not breaking changes either as the functions were never implemented, they returned an error code unconditionally. + +### Changed + + - `igraph_degree_sequence_game()` now supports an additional method, `IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE`, an edge-switching MCMC sampler. + - `igraph_get_adjacency()` and `igraph_get_adjacency_sparse()` now count loop edges _twice_ in undirected graphs when using `IGRAPH_GET_ADJACENCY_BOTH`. This is to ensure consistency with `IGRAPH_GET_ADJACENCY_UPPER` and `IGRAPH_GET_ADJACENCY_LOWER` such that the sum of the upper and the lower triangle matrix is equal to the full adjacency matrix even in the presence of loop edges. + - `igraph_matrix_print()` and `igraph_matrix_fprint()` functions now align columns when priting. + - `igraph_read_graph_gml()` now supports graph attributes (in addition to vertex and edge attributes). + - `igraph_read_graph_gml()` now uses NaN as the default numerical attribute values instead of 0. + - The Pajek parser in `igraph_read_graph_pajek()` is now less strict and accepts more files. + - `igraph_ring()` no longer simplifies its result when generating a one- or two-vertex graph. The one-cycle has a self-loop and the undirected two-cycle has parallel edges. + - `igraph_vector_view()` now allows `data` to be `NULL` in the special case when `length == 0`. + - `igraph_version()` no longer returns an error code. + - `igraph_write_graph_gml()` uses the `creator` parameter in a different way: the supplied string is now written into the Creator line as-is instead of being appended to a default value. + - `igraph_write_graph_gml()` skips writing NaN values. These two changes ensure consistent round-tripping. + - `igraph_write_graph_gml()` and `igraph_read_graph_gml()` now have limited support for entity encoding. + - `igraph_write_graph_ncol()` now preserves the edge ordering of the graph when writing an NCOL file. + - igraph functions that take an ARPACK options object now also accept `NULL` in place of an options object, and they will fall back to using a default object provided by `igraph_arpack_options_get_default()`. + - Foreign format readers now present more informative error messages. + - The default tolerance of the zapsmall functions is now `eps^(2/3)` instead of `eps^(1/2)` where eps is the machine epsilon of `igraph_real_t`. + - It is now possible to override the uniform integer and the Poisson samplers in the random number generator interface. + +### Fixed + + - When an error occurs during parsing DL, GML, NCOL, LGL or Pajek files, line numbers are now reported correctly. + - The GraphML parser does not print to stderr any more in case of encoding errors and other error conditions originating from the underlying `libxml2` library. + - The GraphML parser would omit some edges and vertices when reading files with custom attribute types, such as those produced by yEd. This is now corrected. + - The GML parser no longer mixes up Inf and NaN and -Inf now works. + - The GML parser now supports nodes with no id field. + - The GML parser now performs more stringent checks on the input file, such as verifying that `id`, `source`, `target` and `directed` fields are not duplicated. + - The core data structures (vector, etc.) have overflow checks now. + - Deterministic graph generators, as well as most random ones, have overflow checks now. + - Graphs no longer lose all their attributes after calling `igraph_contract_vertices()`. + - `igraph_hrg_init()` does not throw an assertion error anymore for zero vertices. + - `igraph_matrix_complex_create()` and `igraph_matrix_complex_create_polar()` now set their sizes correctly. + - `igraph_random_walk()` took one fewer steps than specified. + - `igraph_sparsemat_getelements_sorted()` did not sort the elements for triplet matrices correctly; this is fixed now. + - `igraph_write_graph_gml()` no longer produces corrupt output when some string attribute values contain `"` characters. + +### Deprecated + + - `igraph_clusters()` has been renamed to `igraph_connected_components()`; the old name is deprecated and will be removed in 0.11. + - `igraph_complex_eq_tol()` is now deprecated in favour of `igraph_complex_almost_equals()`. + - `igraph_get_sparsemat()` is deprecated in favour of `igraph_get_adjacency_sparse()`, and will be removed in 0.11. Note that `igraph_get_adjacency_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_sparsemat()` which takes an uninitialized one. + - `igraph_get_stochastic_sparsemat()` is deprecated in favour of `igraph_get_stochastic_sparse()`, and will be removed in 0.11. Note that `igraph_get_stochastic_sparse()` takes an _initialized_ sparse matrix as input, unlike `igraph_get_stochastic_sparsemat()`, which takes an uninitialized one. + - `igraph_isomorphic_34()` has been deprecated in favour of `igraph_isomorphic()`. Note that `igraph_isomorphic()` calls an optimized version for directed graphs of size 3 and 4, and undirected graphs with 3-6 vertices, so there is no need for a separate function. + - `igraph_laplacian()` is now deprecated; use `igraph_get_laplacian()` or `igraph_get_laplacian_sparse()` depending on whether you need a dense or a sparse matrix. + - `igraph_lattice()` has been renamed to `igraph_square_lattice()` to indicate that this function generates square lattices only. The old name is deprecated and will either be removed in 0.11 or will be changed to become a generic lattice generator that also supports other types of lattices. + - `igraph_local_scan_neighborhood_ecount()` is now deprecated in favour of `igraph_local_scan_subset_ecount()`. + - `igraph_matrix_all_e_tol()` is now deprecated in favour of `igraph_matrix_all_almost_e()`. + - `igraph_matrix_copy()` is now deprecated; use `igraph_matrix_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target matrix. The old name will be removed in 0.11. + - `igraph_matrix_e()` and `igraph_matrix_e_ptr()` have been renamed to `igraph_matrix_get()` and `igraph_matrix_get_ptr()`. The old names are deprecated and will be removed in 0.11. +- `igraph_random_edge_walk()` has been deprecated by `igraph_random_walk()` to support edges and/or vertices for the random walk in a single function. It will be removed in 0.11. + - `igraph_read_graph_dimacs()` has been renamed to `igraph_read_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS reader in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. + - `igraph_shortest_paths()` and related functions were renamed to `igraph_distances()`; the old name was unfortunate because these functions calculated _path lengths_ only and not the paths themselves. The old names are deprecated and will be removed in 0.11. + - `igraph_sparsemat_copy()`, `igraph_sparsemat_diag()` and `igraph_sparsemat_eye()` have been renamed to `igraph_sparsemat_init_copy()`, `igraph_sparsemat_init_diag()` and `igraph_sparsemat_init_eye()` to indicate that they _initialize_ a new sparse matrix. The old names are deprecated and will be removed in 0.11. + - `igraph_strvector_add()` has been renamed to `igraph_strvector_push_back()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. + - `igraph_strvector_copy()` has been renamed to `igraph_strvector_init_copy()` for sake of consistency with other vector-like data structures; the old name is deprecated and will be removed in 0.11. + - `igraph_strvector_get()` now returns a `const char*` and not a `char*` to indicate that you are not supposed to modify the string in the vector directly. If you do want to modify it and you are aware of the implications (i.e. the new string must not be longer than the original one), you can cast away the constness of the return value before modifying it. + - `igraph_strvector_set2()` has been renamed to `igraph_strvector_set_len()`; the old name is deprecated and will be removed in 0.11. + - `igraph_tree()` has been renamed to `igraph_kary_tree()`; the old name is deprecated and will be removed in 0.11. + - `igraph_vector_e()` and `igraph_vector_e_ptr()` have been renamed to `igraph_vector_get()` and `igraph_vector_get_ptr()`. The old names are deprecated and will be removed in 0.11. + - `igraph_vector_e_tol()` is now deprecated in favour of `igraph_vector_all_almost_e()`. + - `igraph_vector_copy()` is now deprecated; use `igraph_vector_init_copy()` instead. The new name emphasizes that the function _initializes_ the first argument instead of expecting an already-initialized target vector. The old name will be removed in 0.11. + - `igraph_vector_init_seq()` is now deprecated in favour of `igraph_vector_init_range()`, which uses C-style intervals (closed from the left and open from the right). + - `igraph_vs_seq()`, `igraph_vss_seq()`, `igraph_es_seq()` and `igraph_ess_seq()` are now deprecated in favour of `igraph_vs_range()`, `igraph_vss_range()`, `igraph_es_range()` and `igraph_ess_range()` because these use C-style intervals (closed from the left, open from the right). + - `igraph_write_graph_dimacs()` has been renamed to `igraph_write_graph_dimacs_flow()`; the old name is deprecated and might be re-used as a generic DIMACS writer in the future. Also, the function now uses `igraph_integer_t` as the source and target vertex IDs instead of a `long int`. + - `igraph_zeroin()` is deprecated and will be removed in 0.11, with no replacement. The function is not graph-related and was never part of the public API. + - The macros `igraph_Calloc`, `igraph_Realloc` and `igraph_Free` have been deprecated in favour of `IGRAPH_CALLOC`, `IGRAPH_REALLOC` and `IGRAPH_FREE` to simplify the API. The deprecated variants will be removed in 0.11. + +### Other + + - Documentation improvements. + - Support for Intel's LLVM-based compiler. + ## [0.9.10] - 2022-09-02 ### Added @@ -65,6 +601,7 @@ ### Other + - Greatly improved error reporting from foregin format parsers. - Documentation improvements. ## [0.9.8] - 2022-04-08 @@ -115,8 +652,6 @@ ## [0.9.6] - 2022-01-05 -### Changed - - Isomorphism class functions (`igraph_isoclass()`, `igraph_isoclass_subgraph()`, `igraph_isoclass_create`) and motif finder functions (`igraph_motifs_randesu()`, `igraph_motifs_randesu_estimate()`, `igraph_motifs_randesu_callback()`) now @@ -632,7 +1167,12 @@ - Provide proper support for Windows, using `__declspec(dllexport)` and `__declspec(dllimport)` for `DLL`s and static usage by using `#define IGRAPH_STATIC 1`. - Provided integer versions of `dqueue` and `stack` data types. -[Unreleased]: https://github.com/igraph/igraph/compare/0.9.10..HEAD +[master]: https://github.com/igraph/igraph/compare/0.10.4..master +[0.10.4]: https://github.com/igraph/igraph/compare/0.10.3..0.10.4 +[0.10.3]: https://github.com/igraph/igraph/compare/0.10.2..0.10.3 +[0.10.2]: https://github.com/igraph/igraph/compare/0.10.1..0.10.2 +[0.10.1]: https://github.com/igraph/igraph/compare/0.10.0..0.10.1 +[0.10.0]: https://github.com/igraph/igraph/compare/0.9.10..0.10.0 [0.9.10]: https://github.com/igraph/igraph/compare/0.9.9...0.9.10 [0.9.9]: https://github.com/igraph/igraph/compare/0.9.8...0.9.9 [0.9.8]: https://github.com/igraph/igraph/compare/0.9.7...0.9.8 diff --git a/src/vendor/cigraph/CMakeLists.txt b/src/vendor/cigraph/CMakeLists.txt index 26a03a1c29e..e84779f9f54 100644 --- a/src/vendor/cigraph/CMakeLists.txt +++ b/src/vendor/cigraph/CMakeLists.txt @@ -1,7 +1,11 @@ -# Minimum CMake that we require is 3.16 because we use --ignore-eol when -# comparing unit test results with expected outcomes (added in 3.14) and we -# also use SKIP_REGULAR_EXPRESSION to handle skipped tests properly -cmake_minimum_required(VERSION 3.16) +# Minimum CMake that we require is 3.18. +# Some of the recent features we use: +# * --ignore-eol when comparing unit test results with expected outcomes (3.14) +# * CROSSCOMPILING_EMULATOR can be a semicolon-separated list to pass arguments (3.15) +# * SKIP_REGULAR_EXPRESSION to handle skipped tests properly (3.16) +# * CheckLinkerFlag for HAVE_NEW_DTAGS test (3.18) +# * cmake -E cat (3.18) +cmake_minimum_required(VERSION 3.18...3.25) # Add etc/cmake to CMake's search path so we can put our private stuff there list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/etc/cmake) @@ -38,6 +42,9 @@ project( # Include some compiler-related helpers and set global compiler options include(compilers) +# Detect is certain attributes are supported by the compiler +include(attribute_support) + # Set default symbol visibility to hidden set(CMAKE_C_VISIBILITY_PRESET hidden) set(CMAKE_CXX_VISIBILITY_PRESET hidden) @@ -70,26 +77,41 @@ find_dependencies() # Run compile-time checks, generate config.h and igraph_threading.h include(CheckSymbolExists) +include(CheckIncludeFiles) +include(CMakePushCheckState) # First we check for some functions and symbols -set(CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) +cmake_push_check_state() if(NEED_LINKING_AGAINST_LIBM) list(APPEND CMAKE_REQUIRED_LIBRARIES m) endif() -check_symbol_exists(expm1 math.h HAVE_EXPM1) -check_symbol_exists(fmin math.h HAVE_FMIN) -check_symbol_exists(finite math.h HAVE_FINITE) -check_symbol_exists(isfinite math.h HAVE_ISFINITE) -check_symbol_exists(log2 math.h HAVE_LOG2) -check_symbol_exists(log1p math.h HAVE_LOG1P) -check_symbol_exists(rint math.h HAVE_RINT) -check_symbol_exists(rintf math.h HAVE_RINTF) -check_symbol_exists(round math.h HAVE_ROUND) -check_symbol_exists(stpcpy string.h HAVE_STPCPY) check_symbol_exists(strcasecmp strings.h HAVE_STRCASECMP) -check_symbol_exists(strdup string.h HAVE_STRDUP) +check_symbol_exists(strncasecmp strings.h HAVE_STRNCASECMP) check_symbol_exists(_stricmp string.h HAVE__STRICMP) -set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_SAVE}) +check_symbol_exists(_strnicmp string.h HAVE__STRNICMP) +check_symbol_exists(strdup string.h HAVE_STRDUP) +check_symbol_exists(strndup string.h HAVE_STRNDUP) +check_include_files(xlocale.h HAVE_XLOCALE) +if(HAVE_XLOCALE) + # On BSD, uselocale() is in xlocale.h instead of locale.h. + # Some systems provide xlocale.h, but uselocale() is still in locale.h, + # thus we try both. + check_symbol_exists(uselocale "xlocale.h;locale.h" HAVE_USELOCALE) +else() + check_symbol_exists(uselocale locale.h HAVE_USELOCALE) +endif() +check_symbol_exists(_configthreadlocale locale.h HAVE__CONFIGTHREADLOCALE) +cmake_pop_check_state() + +# Check for 128-bit integer multiplication support, floating-point endianness +# and support for built-in overflow detection. +include(ieee754_endianness) +include(uint128_support) +include(safe_math_support) + +if(NOT HAVE_USELOCALE AND NOT HAVE__CONFIGTHREADLOCALE) + message(WARNING "igraph cannot set per-thread locale on this platform. igraph_enter_safelocale() and igraph_exit_safelocale() will not be safe to use in multithreaded programs.") +endif() # Check for code coverage support option(IGRAPH_ENABLE_CODE_COVERAGE "Enable code coverage calculation" OFF) @@ -101,9 +123,9 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND IGRAPH_ENABLE_CODE_COVERAGE) EXECUTABLE "${CMAKE_COMMAND}" "--build" "${PROJECT_BINARY_DIR}" "--target" "check" # Generated files are excluded; apparently the CodeCoverage script has some # problems with them. Yes, the exclusion is correct, it refers to a nonexistent - # directory that somehow gets into the coverage results. /Applications is for - # macOS -- it excludes files from the macOS SDK. - EXCLUDE "io/*.l" "io/parsers/*" "/Applications/Xcode*" "examples/*" "tests/*" + # directory that somehow gets into the coverage results. /Applications and + # /Library/Developer are for macOS -- they exclude files from the macOS SDK. + EXCLUDE "io/*.l" "src/io/parsers/*" "io/parsers/*" "/Applications/Xcode*" "/Library/Developer/*" "examples/*" "interfaces/*" "tests/*" "vendor/pcg/*" ) endif() @@ -112,6 +134,10 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h ) +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_config.h.in + ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_config.h +) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/igraph_threading.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/igraph_threading.h @@ -121,10 +147,6 @@ configure_file( # included as a sub-project in another CMake project if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) include(CTest) - configure_file( - ${PROJECT_SOURCE_DIR}/etc/cmake/CTestCustom.cmake.in - ${PROJECT_BINARY_DIR}/CTestCustom.cmake - ) endif() # Traverse subdirectories. vendor/ should come first because code in diff --git a/src/vendor/cigraph/CONTRIBUTING.md b/src/vendor/cigraph/CONTRIBUTING.md index f3c61e5374e..1e5ed69a161 100644 --- a/src/vendor/cigraph/CONTRIBUTING.md +++ b/src/vendor/cigraph/CONTRIBUTING.md @@ -16,7 +16,7 @@ experienced with C, you can contribute in a number of ways: the issue stating that this is still a problem in version X. - Some [issues point out problems with the documentation](https://github.com/igraph/igraph/labels/documentation); perhaps you could help correct these? - - Some [issues require clarifying a mathematical problem, or some literature research](https://github.com/igraph/igraph/labels/theory), + - Some [issues require clarifying a mathematical problem, or some literature research](https://github.com/igraph/igraph/labels/theory), before any programming can begin. Can you contribute through your theoretical expertise? - Looking to contribute code? Take a look at some [good first issues](https://github.com/igraph/igraph/labels/good%20first%20issue). @@ -157,7 +157,7 @@ Follow the following steps if you would like to make a new pull request: ```bash git pull [--rebase] upstream ``` - + Rebasing is preferable over merging as you do not need to deal with merge conflicts; however, if you already have many commits, merging the upstream development branch may be faster. diff --git a/src/vendor/cigraph/CONTRIBUTORS.md b/src/vendor/cigraph/CONTRIBUTORS.md index abefae6a0a0..2e86e405fe5 100644 --- a/src/vendor/cigraph/CONTRIBUTORS.md +++ b/src/vendor/cigraph/CONTRIBUTORS.md @@ -58,6 +58,25 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Aman Verma

💻
guy rozenberg

💻
Artem V L

💻 +
Kateřina Č.

💻 + + +
valdaarhun

💻 +
YuliYudith

💻 +
alexsyou

💻 +
Rohit Tawde

💻 +
alexperrone

💻 +
Georgica Bors

💻 +
MEET PATEL

💻 + + +
kwofach

💻 +
Kevin Zhu

💻 +
Pradeep Krishnamurthy

💻 +
flange-ipb

💻 +
Juan Julián Merelo Guervós

💻 +
Radoslav Fulek

💻 +
professorcode1

💻 diff --git a/src/vendor/cigraph/ONEWS b/src/vendor/cigraph/ONEWS index c6bdcc42f74..4a262caf0c7 100644 --- a/src/vendor/cigraph/ONEWS +++ b/src/vendor/cigraph/ONEWS @@ -796,7 +796,7 @@ New in the C layer - Some stochastic test results are ignored (for spinglass community detection, some Erdos-Renyi generator tests) - Weighted shortest paths, Dijkstra's algorithm. -- The unweigthed shortest path routine returns 'Inf' for unreachable +- The unweighted shortest path routine returns 'Inf' for unreachable vertices. - New function, igraph_adjlist can create igraph graphs from adjacency lists. @@ -1326,7 +1326,7 @@ Changes in the R interface - graph.edgelist to create a graph from an edge list, can also handle edge lists with symbolic names - get.edgelist has now a 'names' argument and can return symbolic - vertex names instead of vertex ids, by default id uses the 'name' + vertex names instead of vertex IDs, by default id uses the 'name' vertex attribute is returned - printing graphs on screen also prints symbolic symbolic names (the 'name' attribute if present) diff --git a/src/vendor/cigraph/README.md b/src/vendor/cigraph/README.md index f9fa3f13076..ba5b88cec3e 100644 --- a/src/vendor/cigraph/README.md +++ b/src/vendor/cigraph/README.md @@ -6,12 +6,10 @@ The igraph library ------------------ -igraph is a C library for creating, manipulating and analysing graphs. -It is intended to be as powerful (i.e. fast) as possible to enable -working with large graphs. +igraph is a C library for complex network analysis and graph theory, with +emphasis on efficiency, portability and ease of use. -See https://igraph.org for installation instructions -and documentation. +See https://igraph.org for installation instructions and documentation. igraph can also be used from: diff --git a/src/vendor/cigraph/appveyor.yml b/src/vendor/cigraph/appveyor.yml index 28cc611c7af..d7d867a66d6 100644 --- a/src/vendor/cigraph/appveyor.yml +++ b/src/vendor/cigraph/appveyor.yml @@ -51,7 +51,7 @@ for: -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DIGRAPH_GRAPHML_SUPPORT=1 - -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 + -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 - matrix: @@ -67,7 +67,7 @@ for: -DVCPKG_TARGET_TRIPLET=x64-windows-static -DBUILD_SHARED_LIBS=1 -DIGRAPH_GRAPHML_SUPPORT=1 - -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 + -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 configuration: Release diff --git a/src/vendor/cigraph/azure-pipelines.yml b/src/vendor/cigraph/azure-pipelines.yml index 07c12de37da..165cfe5b05b 100644 --- a/src/vendor/cigraph/azure-pipelines.yml +++ b/src/vendor/cigraph/azure-pipelines.yml @@ -4,10 +4,31 @@ pool: variables: CMAKE_GENERATOR: Ninja CCACHE_DIR: $(Pipeline.Workspace)/ccache + CCACHE_MAXSIZE: 256M + ASAN_OPTIONS: detect_stack_use_after_return=1:color=always + UBSAN_OTIONS: print_stacktrace=1:color=always OMP_NUM_THREADS: 1 jobs: + # In this test we install and generate locales so that igraph_enter/exit_safelocale() can be tested - job: linux_static_vendored + steps: + - script: | + sudo apt-get update + sudo apt-get install ninja-build ccache language-pack-de -y + displayName: Install dependencies + + - script: | + sudo locale-gen de_DE + sudo update-locale + displayName: Generate locales + + - template: .azure/build.yml + parameters: + build_type: Debug + extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero"' + + - job: linux_static_vendored_32 steps: - script: sudo apt-get install ninja-build ccache -y displayName: Install dependencies @@ -15,7 +36,7 @@ jobs: - template: .azure/build.yml parameters: build_type: Debug - extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og" -DCMAKE_CXX_FLAGS="-Og"' + extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DIGRAPH_INTEGER_SIZE=32' - job: linux_static_external steps: @@ -27,7 +48,6 @@ jobs: int_blas: false int_lapack: false int_arpack: false - int_cxsparse: false int_gmp: false int_glpk: false extra_cmake_args: '-DBLA_VENDOR=OpenBLAS' @@ -51,12 +71,27 @@ jobs: int_blas: false int_lapack: false int_arpack: false - int_cxsparse: false int_gmp: false int_glpk: false extra_cmake_args: '-DBLA_VENDOR=OpenBLAS' build_shared: true + - job: linux_clang_17 + pool: + vmImage: 'ubuntu-22.04' + steps: + - script: | + sudo apt-get install ninja-build ccache -y + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 17 + displayName: Install dependencies + + - template: .azure/build.yml + parameters: + build_type: Debug + extra_cmake_args: '-DUSE_SANITIZER=Address\;Undefined -DCMAKE_C_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_CXX_FLAGS="-Og -fno-sanitize-recover=undefined -fno-sanitize=float-divide-by-zero" -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17' + - job: linux_x87 steps: - script: sudo apt-get install ninja-build ccache -y @@ -70,11 +105,12 @@ jobs: steps: # https://github.com/alpinelinux/alpine-chroot-install - bash: | - wget https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.13.2/alpine-chroot-install && echo '60c7e0b5d82e21d1a549fc9a46ba3b36688c09dc alpine-chroot-install' | sha1sum -c || exit 1 + set -e + wget https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.14.0/alpine-chroot-install && echo 'ccbf65f85cdc351851f8ad025bb3e65bae4d5b06 alpine-chroot-install' | sha1sum -c || exit 1 alpine() { /alpine/enter-chroot -u "$USER" "$@"; } sudo sh alpine-chroot-install -p 'build-base linux-headers git cmake ninja bison flex gmp-dev' mkdir build && cd build - alpine cmake .. -GNinja -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_CXSPARSE=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_ENABLE_TLS=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 + alpine cmake .. -GNinja -DIGRAPH_USE_INTERNAL_BLAS=1 -DIGRAPH_USE_INTERNAL_LAPACK=1 -DIGRAPH_USE_INTERNAL_ARPACK=1 -DIGRAPH_USE_INTERNAL_GLPK=1 -DIGRAPH_USE_INTERNAL_GMP=1 -DIGRAPH_ENABLE_TLS=1 -DIGRAPH_VERIFY_FINALLY_STACK=1 alpine cmake --build . --target build_tests alpine ctest -j `nproc` --output-on-failure @@ -83,7 +119,6 @@ jobs: vmImage: macos-latest steps: - script: | - brew update brew install ninja ccache displayName: Install dependencies diff --git a/src/vendor/cigraph/codecov.yml b/src/vendor/cigraph/codecov.yml index 0f21612efba..f4a2e6f637d 100644 --- a/src/vendor/cigraph/codecov.yml +++ b/src/vendor/cigraph/codecov.yml @@ -28,3 +28,4 @@ comment: ignore: - "tests" - "examples" + - "vendor/pcg" diff --git a/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in b/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in deleted file mode 100644 index 004e01545fc..00000000000 --- a/src/vendor/cigraph/etc/cmake/CTestCustom.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -# Ask CTest to run the build_tests target before running the tests. This can be -# turned off with an environment variable, which is useful in cases when you -# are working on unit tests for a long time and you are manually rebuilding the -# ones affected by your modifications, but you _don't_ want to wait for CTest -# to rebuild everything -if(NOT DEFINED ENV{IGRAPH_CTEST_MANUAL_BUILD}) - message(STATUS "Auto rebuild tests") - set(CTEST_CUSTOM_PRE_TEST "@CMAKE_COMMAND@ --build @PROJECT_BINARY_DIR@ --target build_tests") -endif() diff --git a/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake b/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake index 1a97d3c9bdc..5a72791091e 100644 --- a/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake +++ b/src/vendor/cigraph/etc/cmake/CheckTLSSupport.cmake @@ -1,7 +1,9 @@ include(CheckCSourceCompiles) +include(CMakePushCheckState) macro(check_tls_support VAR) if(NOT DEFINED "${VAR}") + cmake_push_check_state() set(CMAKE_REQUIRED_QUIET 1) check_c_source_compiles(" @@ -29,6 +31,6 @@ macro(check_tls_support VAR) set(${VAR} "" CACHE INTERNAL "Thread-local storage keyword in compiler") endif() endif() - set(CMAKE_REQUIRED_QUIET 0) + cmake_pop_check_state() endif() endmacro() diff --git a/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake b/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake deleted file mode 100644 index 13ee090c748..00000000000 --- a/src/vendor/cigraph/etc/cmake/FindCXSparse.cmake +++ /dev/null @@ -1,75 +0,0 @@ -# Retrieved from https://github.com/microsoft/vcpkg/blob/3b433e5081f35a32331492d98a8b0c1c2477048e/ports/suitesparse/FindCXSparse.cmake -# -# Distributed under the OSI-approved BSD 3-Clause License. -# -#.rst: -# FindCXSparse -# -------- -# -# Find the CXSparse library -# -# Result Variables -# ^^^^^^^^^^^^^^^^ -# -# The following variables will be defined: -# -# ``CXSparse_FOUND`` -# True if CXSparse found on the local system -# -# ``CXSPARSE_FOUND`` -# True if CXSparse found on the local system -# -# ``CXSparse_INCLUDE_DIRS`` -# Location of CXSparse header files -# -# ``CXSPARSE_INCLUDE_DIRS`` -# Location of CXSparse header files -# -# ``CXSparse_LIBRARIES`` -# List of the CXSparse libraries found -# -# ``CXSPARSE_LIBRARIES`` -# List of the CXSparse libraries found -# -# - -include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) -include(${CMAKE_ROOT}/Modules/SelectLibraryConfigurations.cmake) - -find_path(CXSPARSE_INCLUDE_DIR NAMES cs.h PATH_SUFFIXES suitesparse) - -find_library(CXSPARSE_LIBRARY_RELEASE NAMES cxsparse libcxsparse) -find_library(CXSPARSE_LIBRARY_DEBUG NAMES cxsparsed libcxsparsed) -select_library_configurations(CXSPARSE) - -if(CXSPARSE_INCLUDE_DIR) - set(CXSPARSE_VERSION_FILE ${CXSPARSE_INCLUDE_DIR}/cs.h) - file(READ ${CXSPARSE_INCLUDE_DIR}/cs.h CXSPARSE_VERSION_FILE_CONTENTS) - - string(REGEX MATCH "#define CS_VER [0-9]+" - CXSPARSE_MAIN_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") - string(REGEX REPLACE "#define CS_VER ([0-9]+)" "\\1" - CXSPARSE_MAIN_VERSION "${CXSPARSE_MAIN_VERSION}") - - string(REGEX MATCH "#define CS_SUBVER [0-9]+" - CXSPARSE_SUB_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") - string(REGEX REPLACE "#define CS_SUBVER ([0-9]+)" "\\1" - CXSPARSE_SUB_VERSION "${CXSPARSE_SUB_VERSION}") - - string(REGEX MATCH "#define CS_SUBSUB [0-9]+" - CXSPARSE_SUBSUB_VERSION "${CXSPARSE_VERSION_FILE_CONTENTS}") - string(REGEX REPLACE "#define CS_SUBSUB ([0-9]+)" "\\1" - CXSPARSE_SUBSUB_VERSION "${CXSPARSE_SUBSUB_VERSION}") - - set(CXSPARSE_VERSION "${CXSPARSE_MAIN_VERSION}.${CXSPARSE_SUB_VERSION}.${CXSPARSE_SUBSUB_VERSION}") -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CXSparse - REQUIRED_VARS CXSPARSE_INCLUDE_DIR CXSPARSE_LIBRARIES - VERSION_VAR CXSPARSE_VERSION) - -set(CXSPARSE_FOUND ${CXSparse_FOUND}) -set(CXSPARSE_INCLUDE_DIRS ${CXSPARSE_INCLUDE_DIR}) -set(CXSparse_INCLUDE_DIRS ${CXSPARSE_INCLUDE_DIR}) -set(CXSparse_LIBRARIES ${CXSPARSE_LIBRARIES}) diff --git a/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake b/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake index 503f15ea273..c157d7579cd 100644 --- a/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake +++ b/src/vendor/cigraph/etc/cmake/PreventInSourceBuilds.cmake @@ -14,16 +14,16 @@ function(AssureOutOfSourceBuilds) message("##########################################################################") message("# igraph should not be configured & built in the igraph source directory") message("# You must run cmake in a build directory.") - message("#") - message("# Example:") + message("#") + message("# Example:") message("# mkdir build; cd build; cmake ..; make") message("#") message("# NOTE: Given that you already tried to make an in-source build") message("# CMake have already created several files & directories") - message("# in your source tree. If you are using git, run 'git clean -dfx'") - message("# to start from scratch. If you don't have git, remove") - message("# CMakeCache.txt and the CMakeFiles/ folder from the top of") - message("# the source tree.") + message("# in your source tree. If you are using git, run 'git clean -dfx'") + message("# to start from scratch. If you don't have git, remove") + message("# CMakeCache.txt and the CMakeFiles/ folder from the top of") + message("# the source tree.") message("#") message("##########################################################################") message("") diff --git a/src/vendor/cigraph/etc/cmake/attribute_support.cmake b/src/vendor/cigraph/etc/cmake/attribute_support.cmake new file mode 100644 index 00000000000..4b1d392f683 --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/attribute_support.cmake @@ -0,0 +1,27 @@ + +# Detect if certain attributes are supported by the compiler +# The result will be used to set macros in include/igraph_config.h + +# GCC-style enum value deprecation + +include(CheckCSourceCompiles) +include(CMakePushCheckState) + +# Only check with Clang and GCC as we assume that the -Werror option is supported +# For other compilers, assume that the attribute is unsupported. +if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU") + cmake_push_check_state() + # Require compiling with no warning: + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror") + check_c_source_compiles( + "enum { A __attribute__ ((deprecated)) = 0 }; int main(void) { return 0; }" + COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR + ) + cmake_pop_check_state() +else() + set(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR FALSE) +endif() + +if(COMPILER_HAS_DEPRECATED_ENUMVAL_ATTR) + set(IGRAPH_DEPRECATED_ENUMVAL "__attribute__ ((deprecated))") +endif() diff --git a/src/vendor/cigraph/etc/cmake/compilers.cmake b/src/vendor/cigraph/etc/cmake/compilers.cmake index 336ee380d6b..1c2f9df8da2 100644 --- a/src/vendor/cigraph/etc/cmake/compilers.cmake +++ b/src/vendor/cigraph/etc/cmake/compilers.cmake @@ -5,9 +5,12 @@ if(MSVC) add_compile_definitions(_CRT_SECURE_NO_WARNINGS) # necessary to compile for UWP endif() -if (NOT MSVC) - check_c_compiler_flag("-Wno-varargs" COMPILER_SUPPORTS_NO_VARARGS_FLAG) - check_c_compiler_flag("-Wno-unknown-warning-option" COMPILER_SUPPORTS_NO_UNKNOWN_WARNING_OPTION_FLAG) +if(NOT MSVC) + # Even though we will later use 'no-unknown-warning-option', we perform the test for + # 'unknown-warning-option', without the 'no-' prefix. This is necessary because GCC + # will accept any warning option starting with 'no-', and will not error, yet it still + # prints a message about the unrecognized option. + check_c_compiler_flag("-Wunknown-warning-option" COMPILER_SUPPORTS_UNKNOWN_WARNING_OPTION_FLAG) endif() set( @@ -15,6 +18,17 @@ set( "Treat warnings as errors with GCC-like compilers" ) +option(FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." FALSE) +if(FORCE_COLORED_OUTPUT) + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-fdiagnostics-color=always) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_compile_options(-fcolor-diagnostics) + elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + add_compile_options(-fcolor-diagnostics) + endif() +endif() + macro(use_all_warnings TARGET_NAME) if(MSVC) target_compile_options(${TARGET_NAME} PRIVATE @@ -26,23 +40,33 @@ macro(use_all_warnings TARGET_NAME) /wd4800 # forcing value to 'true' or 'false' (performance warning) /wd4204 # nonstandard extension used: non-constant aggregate initializer /wd4701 # potentially uninitialized local variable + /wd4054 # 'type cast': from function pointer '...' to data pointer 'void *' + /wd4055 # from data pointer 'void *' to function pointer '...' + /wd4221 # nonstandard extension used: '...': cannot be initialized using address of automatic variable '...' ) else() + # Notes: + # GCC does not complain when encountering an unsupported "no"-prefixed wanring option such as -Wno-foo. + # Clang does complain, but these complaints can be silenced with -Wno-unknown-warning-option. + # Therefore it is generally safe to use -Wno-... options that are only supported by recent GCC/Clang. target_compile_options(${TARGET_NAME} PRIVATE # GCC-style compilers: - $<$: + $<$: $<$:-Werror> -Wall -Wextra -pedantic - -Wno-unused-function -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare + -Wstrict-prototypes + -Wno-unused-function -Wno-unused-parameter -Wno-unused-but-set-variable -Wno-sign-compare -Wno-constant-logical-operand > - $<$:-Wno-varargs> - $<$:-Wno-unknown-warning-option> + $<$:-Wno-unknown-warning-option> # Intel compiler: $<$: # disable #279: controlling expression is constant; affecting assert(condition && "message") - # disable #188: enumerated type mixed with another type; affecting IGRAPH_CHECK # disable #592: variable "var" is used before its value is set; affecting IGRAPH_UNUSED - -wd279 -wd188 -wd592 -diag-disable=remark + -wd279 -wd592 -diag-disable=remark + > + # Intel LLVM: + $<$: + -fp-model=precise # The default 'fast' mode is not compatible with igraph's extensive use of NaN/Inf > ) endif() diff --git a/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake b/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake index fa620c2d65e..308240be891 100644 --- a/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake +++ b/src/vendor/cigraph/etc/cmake/cpack_install_script.cmake @@ -59,18 +59,23 @@ if(CPACK_SOURCE_INSTALLED_DIRECTORIES) DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src" ) file( - INSTALL - "${CPACK_PACKAGE_DIRECTORY}/src/io/parsers" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src/io" - ) + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/src/io/parsers" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/src/io" + ) file( INSTALL "${SOURCE_DIR}/tools/removeexamples.py" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" ) file( - INSTALL - "${CPACK_PACKAGE_DIRECTORY}/doc/html" - DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/doc" - ) + INSTALL + "${SOURCE_DIR}/tools/strip_licenses_from_examples.py" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/tools" + ) + file( + INSTALL + "${CPACK_PACKAGE_DIRECTORY}/doc/html" + DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/doc" + ) endif() diff --git a/src/vendor/cigraph/etc/cmake/dependencies.cmake b/src/vendor/cigraph/etc/cmake/dependencies.cmake index 1a7048586c0..b803df2c0a0 100644 --- a/src/vendor/cigraph/etc/cmake/dependencies.cmake +++ b/src/vendor/cigraph/etc/cmake/dependencies.cmake @@ -1,6 +1,7 @@ include(helpers) include(CheckSymbolExists) +include(CMakePushCheckState) # The threading library is not needed for igraph itself, but might be needed # for tests @@ -8,7 +9,7 @@ include(FindThreads) macro(find_dependencies) # Declare the list of dependencies that _may_ be vendored - set(VENDORABLE_DEPENDENCIES BLAS CXSparse GLPK LAPACK ARPACK GMP PLFIT) + set(VENDORABLE_DEPENDENCIES BLAS GLPK LAPACK ARPACK GMP PLFIT) # Declare optional dependencies associated with IGRAPH_..._SUPPORT flags # Note that GLPK is both vendorable and optional @@ -18,7 +19,6 @@ macro(find_dependencies) tristate(IGRAPH_USE_INTERNAL_GMP "Compile igraph with internal Mini-GMP" AUTO) tristate(IGRAPH_USE_INTERNAL_ARPACK "Compile igraph with internal ARPACK" AUTO) tristate(IGRAPH_USE_INTERNAL_BLAS "Compile igraph with internal BLAS" AUTO) - tristate(IGRAPH_USE_INTERNAL_CXSPARSE "Compile igraph with internal CXSparse" AUTO) tristate(IGRAPH_USE_INTERNAL_GLPK "Compile igraph with internal GLPK" AUTO) tristate(IGRAPH_USE_INTERNAL_LAPACK "Compile igraph with internal LAPACK" AUTO) tristate(IGRAPH_USE_INTERNAL_PLFIT "Compile igraph with internal plfit" AUTO) @@ -136,12 +136,11 @@ macro(find_dependencies) # Check whether we need to link to the math library if(NOT DEFINED CACHE{NEED_LINKING_AGAINST_LIBM}) - set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET}) + cmake_push_check_state() set(CMAKE_REQUIRED_QUIET ON) check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) if(NOT SINH_FUNCTION_EXISTS) unset(SINH_FUNCTION_EXISTS CACHE) - set(CMAKE_REQUIRED_LIBRARIES_SAVE ${CMAKE_REQUIRED_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES m) check_symbol_exists(sinh "math.h" SINH_FUNCTION_EXISTS) if(SINH_FUNCTION_EXISTS) @@ -149,10 +148,9 @@ macro(find_dependencies) else() message(FATAL_ERROR "Failed to figure out how to link to the math library on this platform") endif() - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES_SAVE}) endif() unset(SINH_FUNCTION_EXISTS CACHE) - set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE}) + cmake_pop_check_state() endif() if(NEED_LINKING_AGAINST_LIBM) diff --git a/src/vendor/cigraph/etc/cmake/features.cmake b/src/vendor/cigraph/etc/cmake/features.cmake index f5c495c402f..acfa4a1c539 100644 --- a/src/vendor/cigraph/etc/cmake/features.cmake +++ b/src/vendor/cigraph/etc/cmake/features.cmake @@ -6,3 +6,17 @@ include(lto) option(IGRAPH_GLPK_SUPPORT "Compile igraph with GLPK support" ON) tristate(IGRAPH_GRAPHML_SUPPORT "Compile igraph with GraphML support" AUTO) tristate(IGRAPH_OPENMP_SUPPORT "Use OpenMP for parallelization" AUTO) + +set(IGRAPH_INTEGER_SIZE AUTO CACHE STRING "Set size of igraph integers") +set_property(CACHE IGRAPH_INTEGER_SIZE PROPERTY STRINGS AUTO 32 64) + +if(IGRAPH_INTEGER_SIZE STREQUAL AUTO) + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(IGRAPH_INTEGER_SIZE 64) + else() + set(IGRAPH_INTEGER_SIZE 32) + endif() +endif() + +option(FLEX_KEEP_LINE_NUMBERS "Keep references to the original line numbers in generated Flex/Bison parser files" OFF) +mark_as_advanced(FLEX_KEEP_LINE_NUMBERS) diff --git a/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake b/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake new file mode 100644 index 00000000000..fc66751ea67 --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/ieee754_endianness.cmake @@ -0,0 +1,54 @@ +include(CheckCSourceRuns) + +cmake_push_check_state(RESET) + +# Check whether IEEE754 doubles are laid out in little-endian order. We do this +# only when not cross-compiling; during cross-compilation, the host architecture +# might have different endianness conventions than the target, and we are running +# the test on the host here +if(CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) + # If we are cross-compiling and we have no emulator, let's just assume that + # IEEE754 doubles use the same endianness as uint64_t + set(IEEE754_DOUBLE_ENDIANNESS_MATCHES YES) + message(WARNING "\ +igraph is being cross-compiled, therefore we cannot validate whether the \ +endianness of IEEE754 doubles is the same as the endianness of uint64_t. \ +Most likely it is, unless you are compiling for some esoteric platform, \ +in which case you need make sure that this is the case on your own.\ +") +else() + if(NOT DEFINED CACHE{IEEE754_DOUBLE_ENDIANNESS_MATCHES}) + try_run( + IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE + IEEE754_DOUBLE_ENDIANNESS_TEST_COMPILES + ${CMAKE_BINARY_DIR} + ${PROJECT_SOURCE_DIR}/etc/cmake/ieee754_endianness_check.c + RUN_OUTPUT_VARIABLE IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT + ) + # Strip trailing newline, which is necessary on some platforms (such as node.js) + # to complete printing the output. + string(STRIP "${IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT}" IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT) + if(IEEE754_DOUBLE_ENDIANNESS_TEST_EXIT_CODE EQUAL 0) + if(IEEE754_DOUBLE_ENDIANNESS_TEST_RESULT STREQUAL "OK") + set(TEST_RESULT YES) + else() + set(TEST_RESULT NO) + endif() + else() + message(FATAL_ERROR "IEEE754 double endianness test terminated abnormally.") + endif() + + set( + IEEE754_DOUBLE_ENDIANNESS_MATCHES ${TEST_RESULT} CACHE BOOL + "Specifies whether the endianness of IEEE754 doubles is the same as the endianness of uint64_t." + FORCE + ) + mark_as_advanced(IEEE754_DOUBLE_ENDIANNESS_MATCHES) + endif() +endif() + +cmake_pop_check_state() + +if(NOT IEEE754_DOUBLE_ENDIANNESS_MATCHES) + message(FATAL_ERROR "igraph only supports platforms where IEEE754 doubles have the same endianness as uint64_t.") +endif() diff --git a/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c b/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c new file mode 100644 index 00000000000..6296825f620 --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/ieee754_endianness_check.c @@ -0,0 +1,24 @@ +/* Checks whether the endianness of IEEE754 doubles matches the endianness of + * uint64_t on the target system. This is needed to ensure that the trick we + * employ in igraph_rng_get_unif01() works. */ + +#include +#include + +union { + uint64_t as_uint64_t; + double as_double; +} value; + +int main(void) { + value.as_uint64_t = 4841376218035192321ULL; + if (value.as_double == 4510218239279617.0) { + /* endianness of uint64_t and double match */ + printf("OK\n"); + } + /* We always return 0, even for a negative result, this is because we + * need to tell on the CMake side whether a compiler misconfiguration + * aborted our program, which can then be detected from a nonzero exit + * code. */ + return 0; +} diff --git a/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake b/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake index e1562d86a68..d0d9ecb0ab4 100644 --- a/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake +++ b/src/vendor/cigraph/etc/cmake/pkgconfig_helpers.cmake @@ -26,6 +26,7 @@ if(MATH_LIBRARY) else() set(PKGCONFIG_LIBS_PRIVATE "") endif() +set(PKGCONFIG_REQUIRES_PRIVATE "") if(NOT MSVC) check_cxx_symbol_exists(_LIBCPP_VERSION "vector" USING_LIBCXX) @@ -38,7 +39,7 @@ if(NOT MSVC) endif() if(IGRAPH_GRAPHML_SUPPORT) - set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lxml2 -lz") + set(PKGCONFIG_REQUIRES_PRIVATE "${PKGCONFIG_REQUIRES_PRIVATE} libxml-2.0") endif() if(NOT IGRAPH_USE_INTERNAL_GMP) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lgmp") @@ -46,9 +47,6 @@ endif() if(NOT IGRAPH_USE_INTERNAL_BLAS) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lblas") endif() -if(NOT IGRAPH_USE_INTERNAL_CXSPARSE) - set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lcxsparse") -endif() if(IGRAPH_GLPK_SUPPORT AND NOT IGRAPH_USE_INTERNAL_GLPK) set(PKGCONFIG_LIBS_PRIVATE "${PKGCONFIG_LIBS_PRIVATE} -lglpk") endif() diff --git a/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake b/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake index f3cb216f647..ef09642f0ec 100644 --- a/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake +++ b/src/vendor/cigraph/etc/cmake/run_legacy_test.cmake @@ -13,23 +13,10 @@ # - IGRAPH_VERSION: version string of igraph that should be replaced in # expected outputs -function(print_file FILENAME) - # Replacement of "cmake -E cat" for older CMake versions. cat was added in - # CMake 3.18 - file(TO_NATIVE_PATH "${FILENAME}" FILENAME_NATIVE) - if(UNIX OR APPLE) - # Most likely Linux or macOS - execute_process(COMMAND "/bin/sh" "-c" "cat ${FILENAME_NATIVE}") - elseif(WIN32) - # Most likely Windows - execute_process(COMMAND "cmd" "/c" "type" "${FILENAME_NATIVE}") - endif() -endfunction() - get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) execute_process( - COMMAND ${TEST_EXECUTABLE} + COMMAND ${CROSSCOMPILING_EMULATOR} ${TEST_EXECUTABLE} WORKING_DIRECTORY ${WORK_DIR} RESULT_VARIABLE ERROR_CODE OUTPUT_VARIABLE OBSERVED_OUTPUT @@ -40,7 +27,7 @@ if(ERROR_CODE EQUAL 77) elseif(ERROR_CODE) set(MESSAGE "Test exited abnormally with error: ${ERROR_CODE}") file(WRITE ${OBSERVED_OUTPUT_FILE} "${MESSAGE}\n=========================================\n${OBSERVED_OUTPUT}") - print_file("${OBSERVED_OUTPUT_FILE}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${OBSERVED_OUTPUT_FILE}") file(REMOVE ${DIFF_FILE}) message(FATAL_ERROR "Exiting test.") else() @@ -72,7 +59,7 @@ else() if(EXISTS ${DIFF_FILE}) message(STATUS "See diff below:") message(STATUS "-------------------------------------------------------") - print_file("${DIFF_FILE}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E cat "${DIFF_FILE}") message(STATUS "-------------------------------------------------------") else() message(STATUS "Diff omitted; no diff tool was installed.") diff --git a/src/vendor/cigraph/etc/cmake/safe_math_support.cmake b/src/vendor/cigraph/etc/cmake/safe_math_support.cmake new file mode 100644 index 00000000000..a9e0ef49abb --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/safe_math_support.cmake @@ -0,0 +1,18 @@ +include(CheckCXXSourceCompiles) + +# Check whether the compiler supports the __builtin_add_overflow() and __builtin_mul_overflow() +# builtins. These are present in recent GCC-compatible compilers. +cmake_push_check_state(RESET) + +check_cxx_source_compiles(" + int main(void) { + long long a=1, b=2, c; + __builtin_add_overflow(a, b, &c); + __builtin_mul_overflow(a, b, &c); + return 0; + } + " + HAVE_BUILTIN_OVERFLOW +) + +cmake_pop_check_state() diff --git a/src/vendor/cigraph/etc/cmake/summary.cmake b/src/vendor/cigraph/etc/cmake/summary.cmake index 1cf132d7326..6d7f44f4979 100644 --- a/src/vendor/cigraph/etc/cmake/summary.cmake +++ b/src/vendor/cigraph/etc/cmake/summary.cmake @@ -34,6 +34,15 @@ if(BUILD_SHARED_LIBS) else() message(STATUS "Library type: static") endif() +if(${IGRAPH_INTEGER_SIZE} STREQUAL "AUTO") + print_str("igraph_integer_t size" "auto") +elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 64) + print_str("igraph_integer_t size" "64 bits") +elseif(${IGRAPH_INTEGER_SIZE} STREQUAL 32) + print_str("igraph_integer_t size" "32 bits") +else() + print_str("igraph_integer_t size" "INVALID") +endif() if(USE_CCACHE) if(CCACHE_PROGRAM) message(STATUS "Compiler cache: ccache") diff --git a/src/vendor/cigraph/etc/cmake/test_helpers.cmake b/src/vendor/cigraph/etc/cmake/test_helpers.cmake index a324cd10201..4d72877ba30 100644 --- a/src/vendor/cigraph/etc/cmake/test_helpers.cmake +++ b/src/vendor/cigraph/etc/cmake/test_helpers.cmake @@ -13,6 +13,9 @@ function(add_legacy_test FOLDER NAME NAMESPACE) use_all_warnings(${TARGET_NAME}) add_dependencies(build_tests ${TARGET_NAME}) target_link_libraries(${TARGET_NAME} PRIVATE igraph) + if (NAMESPACE STREQUAL "test") + target_link_libraries(${TARGET_NAME} PRIVATE test_utilities) + endif() if (NOT BUILD_SHARED_LIBS) # Add a compiler definition required to compile igraph in static mode @@ -25,12 +28,9 @@ function(add_legacy_test FOLDER NAME NAMESPACE) ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/src ${CMAKE_BINARY_DIR}/src ) - # Some tests include cs.h from CXSparse. The following ensures that the - # correct version is included, depending on whether CXSparse is vendored + # Some tests include cs.h from CXSparse target_include_directories( - ${TARGET_NAME} PRIVATE - $<$:$> - $<$:${CXSPARSE_INCLUDE_DIRS}> + ${TARGET_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/vendor/cs ) if (MSVC) @@ -44,6 +44,7 @@ function(add_legacy_test FOLDER NAME NAMESPACE) get_filename_component(WORK_DIR ${EXPECTED_OUTPUT_FILE} DIRECTORY) if(EXISTS ${EXPECTED_OUTPUT_FILE}) + get_property(CROSSCOMPILING_EMULATOR TARGET ${TARGET_NAME} PROPERTY CROSSCOMPILING_EMULATOR) add_test( NAME ${TEST_NAME} COMMAND ${CMAKE_COMMAND} @@ -54,6 +55,7 @@ function(add_legacy_test FOLDER NAME NAMESPACE) -DDIFF_TOOL=${DIFF_TOOL} -DFC_TOOL=${FC_TOOL} -DIGRAPH_VERSION=${PACKAGE_VERSION} + "-DCROSSCOMPILING_EMULATOR=${CROSSCOMPILING_EMULATOR}" -P ${CMAKE_SOURCE_DIR}/etc/cmake/run_legacy_test.cmake ) set_property(TEST ${TEST_NAME} PROPERTY SKIP_REGULAR_EXPRESSION "Test skipped") diff --git a/src/vendor/cigraph/etc/cmake/tls.cmake b/src/vendor/cigraph/etc/cmake/tls.cmake index 372eadb1b64..f0f4834b5b9 100644 --- a/src/vendor/cigraph/etc/cmake/tls.cmake +++ b/src/vendor/cigraph/etc/cmake/tls.cmake @@ -1,9 +1,17 @@ -option(IGRAPH_ENABLE_TLS "Enable thread-local storage for igraph global variables" OFF) +tristate(IGRAPH_ENABLE_TLS "Enable thread-local storage for igraph global variables" AUTO) -if(IGRAPH_ENABLE_TLS) - include(CheckTLSSupport) - check_tls_support(TLS_KEYWORD) +include(CheckTLSSupport) +check_tls_support(TLS_KEYWORD) +if(IGRAPH_ENABLE_TLS STREQUAL "AUTO") + if(TLS_KEYWORD) + set(IGRAPH_ENABLE_TLS ON) + else() + set(IGRAPH_ENABLE_TLS OFF) + endif() +endif() + +if(IGRAPH_ENABLE_TLS) if(NOT TLS_KEYWORD) message(FATAL_ERROR "Thread-local storage not supported on this compiler") endif() diff --git a/src/vendor/cigraph/etc/cmake/uint128_support.cmake b/src/vendor/cigraph/etc/cmake/uint128_support.cmake new file mode 100644 index 00000000000..968c50abe33 --- /dev/null +++ b/src/vendor/cigraph/etc/cmake/uint128_support.cmake @@ -0,0 +1,43 @@ +include(CheckCXXSourceCompiles) +include(CheckTypeSize) + +cmake_push_check_state(RESET) + +# Check whether the compiler supports the _umul128() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0, b = 0; + unsigned long long c; + volatile unsigned long long d; + d = _umul128(a, b, &c); + return 0; + } + " + HAVE__UMUL128 +) + +# Check whether the compiler supports the __umulh() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0, b = 0; + volatile unsigned long long c; + c = __umulh(a, b); + return 0; + } + " + HAVE___UMULH +) + +# Check whether the compiler has __uint128_t +check_type_size("__uint128_t" UINT128 LANGUAGE CXX) +if(UINT128 EQUAL 16) + set(HAVE___UINT128_T ON) +else() + set(HAVE___UINT128_T OFF) +endif() + +cmake_pop_check_state() diff --git a/src/vendor/cigraph/igraph.pc.in b/src/vendor/cigraph/igraph.pc.in index 4c036fbdc48..f869537d633 100644 --- a/src/vendor/cigraph/igraph.pc.in +++ b/src/vendor/cigraph/igraph.pc.in @@ -9,4 +9,5 @@ Version: @PROJECT_VERSION@ URL: @PROJECT_HOMEPAGE_URL@ Libs: -L${libdir} -ligraph Libs.private: @PKGCONFIG_LIBS_PRIVATE@ +Requires.private: @PKGCONFIG_REQUIRES_PRIVATE@ Cflags: -I${includedir}/igraph diff --git a/src/vendor/cigraph/include/igraph.h b/src/vendor/cigraph/include/igraph.h index 5b8c14736a1..d77e6261e27 100644 --- a/src/vendor/cigraph/include/igraph.h +++ b/src/vendor/cigraph/include/igraph.h @@ -45,13 +45,14 @@ #include "igraph_heap.h" #include "igraph_psumtree.h" #include "igraph_strvector.h" +#include "igraph_vector_list.h" #include "igraph_vector_ptr.h" -#include "igraph_spmatrix.h" #include "igraph_sparsemat.h" #include "igraph_qsort.h" #include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_graph_list.h" #include "igraph_iterators.h" #include "igraph_interface.h" #include "igraph_constructors.h" @@ -88,7 +89,6 @@ #include "igraph_hrg.h" #include "igraph_threading.h" #include "igraph_interrupt.h" -#include "igraph_scg.h" #include "igraph_matching.h" #include "igraph_embedding.h" #include "igraph_scan.h" @@ -98,5 +98,6 @@ #include "igraph_coloring.h" #include "igraph_eulerian.h" #include "igraph_graphicality.h" +#include "igraph_cycles.h" #endif diff --git a/src/vendor/cigraph/include/igraph_adjlist.h b/src/vendor/cigraph/include/igraph_adjlist.h index ee56bdb8016..a7aa69e22b7 100644 --- a/src/vendor/cigraph/include/igraph_adjlist.h +++ b/src/vendor/cigraph/include/igraph_adjlist.h @@ -26,6 +26,7 @@ #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -36,25 +37,30 @@ typedef struct igraph_adjlist_t { igraph_vector_int_t *adjs; } igraph_adjlist_t; -IGRAPH_EXPORT int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, +typedef struct igraph_inclist_t { + igraph_integer_t length; + igraph_vector_int_t *incs; +} igraph_inclist_t; + +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_loops_t loops, igraph_multiple_t multiple); -IGRAPH_EXPORT int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes); IGRAPH_EXPORT igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); -IGRAPH_EXPORT int igraph_adjlist_init_complementer(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, igraph_adjlist_t *al, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_init_from_inclist( + const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il); IGRAPH_EXPORT void igraph_adjlist_destroy(igraph_adjlist_t *al); IGRAPH_EXPORT void igraph_adjlist_clear(igraph_adjlist_t *al); IGRAPH_EXPORT void igraph_adjlist_sort(igraph_adjlist_t *al); -IGRAPH_EXPORT int igraph_adjlist_simplify(igraph_adjlist_t *al); -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_adjlist_remove_duplicate(const igraph_t *graph, - igraph_adjlist_t *al); -IGRAPH_EXPORT int igraph_adjlist_print(const igraph_adjlist_t *al); -IGRAPH_EXPORT int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_simplify(igraph_adjlist_t *al); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al); +IGRAPH_EXPORT igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile); IGRAPH_EXPORT igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed); -IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integer_t from, igraph_integer_t oldto, igraph_integer_t newto, igraph_bool_t directed); /** * \define igraph_adjlist_get @@ -68,35 +74,28 @@ IGRAPH_EXPORT int igraph_adjlist_replace_edge(igraph_adjlist_t* al, igraph_integ * * Time complexity: O(1). */ -#define igraph_adjlist_get(al,no) (&(al)->adjs[(long int)(no)]) +#define igraph_adjlist_get(al,no) (&(al)->adjs[(igraph_integer_t)(no)]) -IGRAPH_EXPORT int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, +IGRAPH_EXPORT igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, igraph_neimode_t mode, igraph_bool_t duplicate); -typedef struct igraph_inclist_t { - igraph_integer_t length; - igraph_vector_int_t *incs; -} igraph_inclist_t; - -IGRAPH_EXPORT int igraph_inclist_init(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_inclist_init(const igraph_t *graph, igraph_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops); -IGRAPH_EXPORT int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n); IGRAPH_EXPORT igraph_integer_t igraph_inclist_size(const igraph_inclist_t *al); IGRAPH_EXPORT void igraph_inclist_destroy(igraph_inclist_t *il); IGRAPH_EXPORT void igraph_inclist_clear(igraph_inclist_t *il); -IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_inclist_remove_duplicate(const igraph_t *graph, - igraph_inclist_t *il); -IGRAPH_EXPORT int igraph_inclist_print(const igraph_inclist_t *il); -IGRAPH_EXPORT int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); +IGRAPH_EXPORT igraph_error_t igraph_inclist_print(const igraph_inclist_t *il); +IGRAPH_EXPORT igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfile); /** * \define igraph_inclist_get * \brief Query a vector in an incidence list. * * Returns a pointer to an igraph_vector_int_t object from an - * incidence list containing edge ids. The vector can be modified, + * incidence list containing edge IDs. The vector can be modified, * resized, etc. as desired. * \param il Pointer to the incidence list. * \param no The vertex for which the incident edges are returned. @@ -104,7 +103,7 @@ IGRAPH_EXPORT int igraph_inclist_fprint(const igraph_inclist_t *il, FILE *outfil * * Time complexity: O(1). */ -#define igraph_inclist_get(il,no) (&(il)->incs[(long int)(no)]) +#define igraph_inclist_get(il,no) (&(il)->incs[(igraph_integer_t)(no)]) typedef struct igraph_lazy_adjlist_t { const igraph_t *graph; @@ -113,10 +112,9 @@ typedef struct igraph_lazy_adjlist_t { igraph_neimode_t mode; igraph_loops_t loops; igraph_multiple_t multiple; - igraph_vector_t dummy; } igraph_lazy_adjlist_t; -IGRAPH_EXPORT int igraph_lazy_adjlist_init(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, igraph_lazy_adjlist_t *al, igraph_neimode_t mode, igraph_loops_t loops, @@ -125,6 +123,19 @@ IGRAPH_EXPORT void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al); IGRAPH_EXPORT void igraph_lazy_adjlist_clear(igraph_lazy_adjlist_t *al); IGRAPH_EXPORT igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al); +/** + * \define igraph_lazy_adjlist_has + * \brief Are adjacenct vertices already stored in a lazy adjacency list? + * + * \param al The lazy adjacency list. + * \param no The vertex ID to query. + * \return True if the adjacent vertices of this vertex are already computed + * and stored, false otherwise. + * + * Time complexity: O(1). + */ +#define igraph_lazy_adjlist_has(al,no) ((igraph_bool_t) (al)->adjs[(igraph_integer_t)(no)]) + /** * \define igraph_lazy_adjlist_get * \brief Query neighbor vertices. @@ -133,17 +144,23 @@ IGRAPH_EXPORT igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlis * result is stored in the adjacency list and no further query * operations are needed when the neighbors of the same vertex are * queried again. + * * \param al The lazy adjacency list. * \param no The vertex ID to query. - * \return Pointer to a vector. It is allowed to modify it and + * \return Pointer to a vector, or \c NULL upon error. Errors can only + * occur the first time this function is called for a given vertex. + * It is safe to modify this vector, * modification does not affect the original graph. * + * \sa \ref igraph_lazy_adjlist_has() to check if this function has + * already been called for a vertex. + * * Time complexity: O(d), the number of neighbor vertices for the * first time, O(1) for subsequent calls. */ #define igraph_lazy_adjlist_get(al,no) \ - ((al)->adjs[(long int)(no)] != 0 ? ((al)->adjs[(long int)(no)]) : \ - (igraph_i_lazy_adjlist_get_real(al, no))) + (igraph_lazy_adjlist_has(al,no) ? ((al)->adjs[(igraph_integer_t)(no)]) \ + : (igraph_i_lazy_adjlist_get_real(al, no))) IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, igraph_integer_t no); typedef struct igraph_lazy_inclist_t { @@ -151,11 +168,10 @@ typedef struct igraph_lazy_inclist_t { igraph_integer_t length; igraph_vector_int_t **incs; igraph_neimode_t mode; - igraph_vector_t dummy; igraph_loops_t loops; } igraph_lazy_inclist_t; -IGRAPH_EXPORT int igraph_lazy_inclist_init(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops); @@ -163,6 +179,19 @@ IGRAPH_EXPORT void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il); IGRAPH_EXPORT void igraph_lazy_inclist_clear(igraph_lazy_inclist_t *il); IGRAPH_EXPORT igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il); +/** + * \define igraph_lazy_inclist_has + * \brief Are incident edges already stored in a lazy inclist? + * + * \param il The lazy incidence list. + * \param no The vertex ID to query. + * \return True if the incident edges of this vertex are already computed + * and stored, false otherwise. + * + * Time complexity: O(1). + */ +#define igraph_lazy_inclist_has(il,no) ((igraph_bool_t) (il)->incs[(igraph_integer_t)(no)]) + /** * \define igraph_lazy_inclist_get * \brief Query incident edges. @@ -171,18 +200,24 @@ IGRAPH_EXPORT igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclis * result is stored in the incidence list and no further query * operations are needed when the incident edges of the same vertex are * queried again. - * \param al The lazy incidence list object. - * \param no The vertex id to query. - * \return Pointer to a vector. It is allowed to modify it and + * + * \param il The lazy incidence list object. + * \param no The vertex ID to query. + * \return Pointer to a vector, or \c NULL upon error. Errors can only + * occur the first time this function is called for a given vertex. + * It is safe to modify this vector, * modification does not affect the original graph. * + * \sa \ref igraph_lazy_inclist_has() to check if this function has + * already been called for a vertex. + * * Time complexity: O(d), the number of incident edges for the first * time, O(1) for subsequent calls with the same \p no argument. */ -#define igraph_lazy_inclist_get(al,no) \ - ((al)->incs[(long int)(no)] != 0 ? ((al)->incs[(long int)(no)]) : \ - (igraph_i_lazy_inclist_get_real(al, no))) -IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *al, igraph_integer_t no); +#define igraph_lazy_inclist_get(il,no) \ + (igraph_lazy_inclist_has(il,no) ? ((il)->incs[(igraph_integer_t)(no)]) \ + : (igraph_i_lazy_inclist_get_real(il,no))) +IGRAPH_EXPORT igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, igraph_integer_t no); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_arpack.h b/src/vendor/cigraph/include/igraph_arpack.h index c28af921dbf..8999819acfe 100644 --- a/src/vendor/cigraph/include/igraph_arpack.h +++ b/src/vendor/cigraph/include/igraph_arpack.h @@ -25,9 +25,10 @@ #define IGRAPH_ARPACK_H #include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_matrix.h" __BEGIN_DECLS @@ -96,14 +97,15 @@ __BEGIN_DECLS /** * \struct igraph_arpack_options_t - * \brief Options for ARPACK + * \brief Options for ARPACK. * - * This data structure contains the options of thee ARPACK eigenvalue + * This data structure contains the options of the ARPACK eigenvalue * solver routines. It must be initialized by calling \ref * igraph_arpack_options_init() on it. Then it can be used for * multiple ARPACK calls, as the ARPACK solvers do not modify it. * * Input options: + * * \member bmat Character. Whether to solve a standard ('I') ot a * generalized problem ('B'). * \member n Dimension of the eigenproblem. @@ -186,6 +188,7 @@ __BEGIN_DECLS * \ref igraph_arpack_rssolve() of \ref igraph_arpack_rnsolve() call. * * Output options: + * * \member info Error flag of ARPACK. Possible values: * \clist \cli 0 * Normal exit. @@ -256,7 +259,7 @@ typedef struct igraph_arpack_options_t { /** * \struct igraph_arpack_storage_t - * \brief Storage for ARPACK + * \brief Storage for ARPACK. * * Public members, do not modify them directly, these are considered * to be read-only. @@ -284,19 +287,21 @@ typedef struct igraph_arpack_storage_t { igraph_real_t *resid; igraph_real_t *ax; int *select; - igraph_real_t *di; /* These two only for non-symmetric problems */ + /* The following two are only used for non-symmetric problems: */ + igraph_real_t *di; igraph_real_t *workev; } igraph_arpack_storage_t; IGRAPH_EXPORT void igraph_arpack_options_init(igraph_arpack_options_t *o); +IGRAPH_EXPORT igraph_arpack_options_t* igraph_arpack_options_get_default(void); -IGRAPH_EXPORT int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, - long int maxncv, long int maxldv, igraph_bool_t symm); +IGRAPH_EXPORT igraph_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, + igraph_integer_t maxncv, igraph_integer_t maxldv, igraph_bool_t symm); IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); /** * \typedef igraph_arpack_function_t - * Type of the ARPACK callback function + * \brief Type of the ARPACK callback function. * * \param to Pointer to an \c igraph_real_t, the result of the * matrix-vector product is expected to be stored here. @@ -307,25 +312,25 @@ IGRAPH_EXPORT void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s); * \param extra Extra argument to the matrix-vector calculation * function. This is coming from the \ref igraph_arpack_rssolve() * or \ref igraph_arpack_rnsolve() function. - * \return Error code, if not zero, then the ARPACK solver considers + * \return Error code. If not \c IGRAPH_SUCCESS, then the ARPACK solver considers * this as an error, stops and calls the igraph error handler. */ -typedef int igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, +typedef igraph_error_t igraph_arpack_function_t(igraph_real_t *to, const igraph_real_t *from, int n, void *extra); -IGRAPH_EXPORT int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, - long int nev); +IGRAPH_EXPORT igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + igraph_integer_t nev); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_array.h b/src/vendor/cigraph/include/igraph_array.h index 627433ad65e..1f098d0a3c3 100644 --- a/src/vendor/cigraph/include/igraph_array.h +++ b/src/vendor/cigraph/include/igraph_array.h @@ -25,6 +25,8 @@ #define IGRAPH_ARRAY_H #include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" __BEGIN_DECLS @@ -38,11 +40,11 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_LONG +#define BASE_INT #include "igraph_pmt.h" #include "igraph_array_pmt.h" #include "igraph_pmt_off.h" -#undef BASE_LONG +#undef BASE_INT #define BASE_CHAR #include "igraph_pmt.h" diff --git a/src/vendor/cigraph/include/igraph_array_pmt.h b/src/vendor/cigraph/include/igraph_array_pmt.h index 18c73ae8d0b..cd15fd7a7f5 100644 --- a/src/vendor/cigraph/include/igraph_array_pmt.h +++ b/src/vendor/cigraph/include/igraph_array_pmt.h @@ -23,7 +23,7 @@ typedef struct TYPE(igraph_array3) { TYPE(igraph_vector) data; - long int n1, n2, n3, n1n2; + igraph_integer_t n1, n2, n3, n1n2; } TYPE(igraph_array3); #ifndef IGRAPH_ARRAY3_INIT_FINALLY @@ -36,16 +36,19 @@ typedef struct TYPE(igraph_array3) { #define ARRAY3(m,i,j,k) ((m).data.stor_begin[(m).n1n2*(k)+(m).n1*(j)+(i)]) #endif -IGRAPH_EXPORT int FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, long int n1, long int n2, - long int n3); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, init)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3); IGRAPH_EXPORT void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a); -IGRAPH_EXPORT long int FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); -IGRAPH_EXPORT long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx); -IGRAPH_EXPORT int FUNCTION(igraph_array3, resize)(TYPE(igraph_array3) *a, long int n1, long int n2, - long int n3); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_array3, n)( + const TYPE(igraph_array3) *a, igraph_integer_t idx); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, resize)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3); IGRAPH_EXPORT void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a); IGRAPH_EXPORT BASE FUNCTION(igraph_array3, sum)(const TYPE(igraph_array3) *a); IGRAPH_EXPORT void FUNCTION(igraph_array3, scale)(TYPE(igraph_array3) *a, BASE by); IGRAPH_EXPORT void FUNCTION(igraph_array3, fill)(TYPE(igraph_array3) *a, BASE e); -IGRAPH_EXPORT int FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, update)(TYPE(igraph_array3) *to, const TYPE(igraph_array3) *from); diff --git a/src/vendor/cigraph/include/igraph_attributes.h b/src/vendor/cigraph/include/igraph_attributes.h index 37c87394711..c4d973fd4d8 100644 --- a/src/vendor/cigraph/include/igraph_attributes.h +++ b/src/vendor/cigraph/include/igraph_attributes.h @@ -24,11 +24,14 @@ #ifndef IGRAPH_ATTRIBUTES_H #define IGRAPH_ATTRIBUTES_H +#include "igraph_config.h" #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_datatype.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_strvector.h" +#include "igraph_vector_list.h" #include "igraph_vector_ptr.h" #include "igraph_iterators.h" @@ -41,10 +44,11 @@ __BEGIN_DECLS /** * \section about_attributes * - * Attributes are numbers or strings (or basically any kind - * of data) associated with the vertices or edges of a graph, or - * with the graph itself. Eg. you may label vertices with symbolic names - * or attach numeric weights to the edges of a graph. + * Attributes are numbers, boolean values or strings associated with + * the vertices or edges of a graph, or with the graph itself. E.g. you may + * label vertices with symbolic names or attach numeric weights to the edges + * of a graph. In addition to these three basic types, a custom object + * type is supported as well. * * igraph attributes are designed to be flexible and extensible. * In igraph attributes are implemented via an interface abstraction: @@ -52,7 +56,7 @@ __BEGIN_DECLS * for storing vertex, edge and graph attributes. This means that * different attribute implementations can be used together with * igraph. This is reasonable: if igraph is used from Python attributes can be - * of any Python type, from GNU R all R types are allowed. There is an + * of any Python type, from R all R types are allowed. There is also an * experimental attribute implementation to be used when programming * in C, but by default it is currently turned off. * @@ -71,10 +75,13 @@ __BEGIN_DECLS * notify the attribute handling code about the structural changes in * a graph. See the documentation of this type for details.
* - * By default there is no attribute interface attached to \a igraph, - * to attach one, call \ref igraph_set_attribute_table with your new - * table. - * + * By default there is no attribute interface attached to \a igraph. + * To attach one, call \ref igraph_set_attribute_table with your new + * table. This is normally done on program startup, and is kept untouched + * for the program's lifetime. It must be done before any graph object + * is created, as graphs created with a given attribute handler + * cannot be manipulated while a different attribute handler is + * active. */ /** @@ -101,27 +108,29 @@ __BEGIN_DECLS * * Note that this is only the * type communicated by the attribute interface towards igraph - * functions. Eg. in the GNU R attribute handler, it is safe to say + * functions. E.g. in the R attribute handler, it is safe to say * that all complex R object attributes are strings, as long as this * interface is able to serialize them into strings. See also \ref * igraph_attribute_table_t. - * \enumval IGRAPH_ATTRIBUTE_DEFAULT Currently not used for anything. + * \enumval IGRAPH_ATTRIBUTE_UNSPECIFIED Currently used internally + * as a "null value" or "placeholder value" in some algorithms. + * Attribute records with this type must not be passed to igraph + * functions. * \enumval IGRAPH_ATTRIBUTE_NUMERIC Numeric attribute. * \enumval IGRAPH_ATTRIBUTE_BOOLEAN Logical values, true or false. * \enumval IGRAPH_ATTRIBUTE_STRING Attribute that can be converted to * a string. - * \enumval IGRAPH_ATTRIBUTE_R_OBJECT An R object. This is usually - * ignored by the igraph functions. - * \enumval IGRAPH_ATTRIBUTE_PY_OBJECT A Python object. Usually - * ignored by the igraph functions. - * + * \enumval IGRAPH_ATTRIBUTE_OBJECT Custom attribute type, to be + * used for special data types by client applications. The R and + * Python interfaces use this for attributes that hold R or Python + * objects. Usually ignored by igraph functions. */ -typedef enum { IGRAPH_ATTRIBUTE_DEFAULT = 0, +typedef enum { IGRAPH_ATTRIBUTE_UNSPECIFIED = 0, + IGRAPH_ATTRIBUTE_DEFAULT IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_ATTRIBUTE_UNSPECIFIED, IGRAPH_ATTRIBUTE_NUMERIC = 1, - IGRAPH_ATTRIBUTE_BOOLEAN = 5, - IGRAPH_ATTRIBUTE_STRING = 2, - IGRAPH_ATTRIBUTE_R_OBJECT = 3, - IGRAPH_ATTRIBUTE_PY_OBJECT = 4 + IGRAPH_ATTRIBUTE_BOOLEAN = 2, + IGRAPH_ATTRIBUTE_STRING = 3, + IGRAPH_ATTRIBUTE_OBJECT = 127 } igraph_attribute_type_t; typedef struct igraph_attribute_record_t { @@ -184,26 +193,27 @@ typedef struct igraph_attribute_combination_t { #define IGRAPH_NO_MORE_ATTRIBUTES ((const char*)0) -IGRAPH_EXPORT int igraph_attribute_combination_init(igraph_attribute_combination_t *comb); -IGRAPH_EXPORT int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb); +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination(igraph_attribute_combination_t *comb, ...); IGRAPH_EXPORT void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb); -IGRAPH_EXPORT int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_add(igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t type, igraph_function_pointer_t func); -IGRAPH_EXPORT int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name); -IGRAPH_EXPORT int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, +IGRAPH_EXPORT igraph_error_t igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, const char *name, igraph_attribute_combination_type_t *type, igraph_function_pointer_t *func); /** * \struct igraph_attribute_table_t - * \brief Table of functions to perform operations on attributes + * \brief Table of functions to perform operations on attributes. * * This type collects the functions defining an attribute handler. * It has the following members: + * * \member init This function is called whenever a new graph object is * created, right after it is created but before any vertices or * edges are added. It is supposed to set the \c attr member of the \c @@ -277,58 +287,58 @@ IGRAPH_EXPORT int igraph_attribute_combination_query(const igraph_attribute_comb */ typedef struct igraph_attribute_table_t { - int (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); - void (*destroy)(igraph_t *graph); - int (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, - igraph_bool_t va, igraph_bool_t ea); - int (*add_vertices)(igraph_t *graph, long int nv, igraph_vector_ptr_t *attr); - int (*permute_vertices)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_t *idx); - int (*combine_vertices)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_ptr_t *merges, - const igraph_attribute_combination_t *comb); - int (*add_edges)(igraph_t *graph, const igraph_vector_t *edges, - igraph_vector_ptr_t *attr); - int (*permute_edges)(const igraph_t *graph, - igraph_t *newgraph, const igraph_vector_t *idx); - int (*combine_edges)(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_ptr_t *merges, - const igraph_attribute_combination_t *comb); - int (*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_error_t (*init)(igraph_t *graph, igraph_vector_ptr_t *attr); + void (*destroy)(igraph_t *graph); + igraph_error_t (*copy)(igraph_t *to, const igraph_t *from, igraph_bool_t ga, + igraph_bool_t va, igraph_bool_t ea); + igraph_error_t (*add_vertices)(igraph_t *graph, igraph_integer_t nv, igraph_vector_ptr_t *attr); + igraph_error_t (*permute_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx); + igraph_error_t (*combine_vertices)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); + igraph_error_t (*add_edges)(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_vector_ptr_t *attr); + igraph_error_t (*permute_edges)(const igraph_t *graph, + igraph_t *newgraph, const igraph_vector_int_t *idx); + igraph_error_t (*combine_edges)(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_list_t *merges, + const igraph_attribute_combination_t *comb); + igraph_error_t (*get_info)(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_t *etypes); igraph_bool_t (*has_attr)(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); - int (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, - igraph_attribute_elemtype_t elemtype, const char *name); - int (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, - igraph_vector_t *value); - int (*get_string_graph_attr)(const igraph_t *graph, const char *name, - igraph_strvector_t *value); - int (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, - igraph_vector_bool_t *value); - int (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_vector_t *value); - int (*get_string_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_strvector_t *value); - int (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, - igraph_vs_t vs, - igraph_vector_bool_t *value); - int (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_vector_t *value); - int (*get_string_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_strvector_t *value); - int (*get_bool_edge_attr)(const igraph_t *graph, const char *name, - igraph_es_t es, - igraph_vector_bool_t *value); + igraph_error_t (*gettype)(const igraph_t *graph, igraph_attribute_type_t *type, + igraph_attribute_elemtype_t elemtype, const char *name); + igraph_error_t (*get_numeric_graph_attr)(const igraph_t *graph, const char *name, + igraph_vector_t *value); + igraph_error_t (*get_string_graph_attr)(const igraph_t *graph, const char *name, + igraph_strvector_t *value); + igraph_error_t (*get_bool_graph_attr)(const igraph_t *igraph, const char *name, + igraph_vector_bool_t *value); + igraph_error_t (*get_numeric_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_t *value); + igraph_error_t (*get_string_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_strvector_t *value); + igraph_error_t (*get_bool_vertex_attr)(const igraph_t *graph, const char *name, + igraph_vs_t vs, + igraph_vector_bool_t *value); + igraph_error_t (*get_numeric_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_vector_t *value); + igraph_error_t (*get_string_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_strvector_t *value); + igraph_error_t (*get_bool_edge_attr)(const igraph_t *graph, const char *name, + igraph_es_t es, + igraph_vector_bool_t *value); } igraph_attribute_table_t; IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_attribute_table_t * igraph_i_set_attribute_table(const igraph_attribute_table_t * table); @@ -356,57 +366,57 @@ IGRAPH_EXPORT igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const c IGRAPH_EXPORT const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, igraph_integer_t eid); -IGRAPH_EXPORT int igraph_cattribute_VANV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VANV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_t *result); -IGRAPH_EXPORT int igraph_cattribute_EANV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EANV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_t *result); -IGRAPH_EXPORT int igraph_cattribute_VASV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VASV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_strvector_t *result); -IGRAPH_EXPORT int igraph_cattribute_EASV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EASV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_strvector_t *result); -IGRAPH_EXPORT int igraph_cattribute_VABV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VABV(const igraph_t *graph, const char *name, igraph_vs_t vids, igraph_vector_bool_t *result); -IGRAPH_EXPORT int igraph_cattribute_EABV(const igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EABV(const igraph_t *graph, const char *name, igraph_es_t eids, igraph_vector_bool_t *result); -IGRAPH_EXPORT 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); +IGRAPH_EXPORT igraph_error_t igraph_cattribute_list(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_t *etypes); IGRAPH_EXPORT igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, igraph_attribute_elemtype_t type, const char *name); -IGRAPH_EXPORT int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAN_set(igraph_t *graph, const char *name, igraph_real_t value); -IGRAPH_EXPORT int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAB_set(igraph_t *graph, const char *name, igraph_bool_t value); -IGRAPH_EXPORT int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_GAS_set(igraph_t *graph, const char *name, const char *value); -IGRAPH_EXPORT int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_real_t value); -IGRAPH_EXPORT int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_set(igraph_t *graph, const char *name, igraph_integer_t vid, igraph_bool_t value); -IGRAPH_EXPORT int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_set(igraph_t *graph, const char *name, igraph_integer_t vid, const char *value); -IGRAPH_EXPORT int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_real_t value); -IGRAPH_EXPORT int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_set(igraph_t *graph, const char *name, igraph_integer_t eid, igraph_bool_t value); -IGRAPH_EXPORT int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAS_set(igraph_t *graph, const char *name, igraph_integer_t eid, const char *value); -IGRAPH_EXPORT int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); -IGRAPH_EXPORT int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); -IGRAPH_EXPORT int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); -IGRAPH_EXPORT int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, const igraph_vector_t *v); -IGRAPH_EXPORT int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, const igraph_vector_bool_t *v); -IGRAPH_EXPORT int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, +IGRAPH_EXPORT igraph_error_t igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, const igraph_strvector_t *sv); IGRAPH_EXPORT void igraph_cattribute_remove_g(igraph_t *graph, const char *name); diff --git a/src/vendor/cigraph/include/igraph_bipartite.h b/src/vendor/cigraph/include/igraph_bipartite.h index 13771e7a5c6..0bed57a4069 100644 --- a/src/vendor/cigraph/include/igraph_bipartite.h +++ b/src/vendor/cigraph/include/igraph_bipartite.h @@ -25,11 +25,12 @@ #define IGRAPH_BIPARTITE_H #include "igraph_decls.h" +#include "igraph_datatype.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" -#include "igraph_datatype.h" __BEGIN_DECLS @@ -37,61 +38,73 @@ __BEGIN_DECLS /* Bipartite networks */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_full_bipartite(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, - const igraph_vector_t *edges, +IGRAPH_EXPORT igraph_error_t igraph_create_bipartite(igraph_t *g, const igraph_vector_bool_t *types, + const igraph_vector_int_t *edges, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_bipartite_projection_size(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_bipartite_projection(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *multiplicity1, + igraph_vector_int_t *multiplicity2, igraph_integer_t probe1); -IGRAPH_EXPORT int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, - const igraph_matrix_t *incidence, igraph_bool_t directed, +IGRAPH_EXPORT igraph_error_t igraph_biadjacency(igraph_t *graph, igraph_vector_bool_t *types, + const igraph_matrix_t *input, igraph_bool_t directed, igraph_neimode_t mode, igraph_bool_t multiple); -IGRAPH_EXPORT int igraph_get_incidence(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_get_biadjacency(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, - igraph_vector_t *row_ids, - igraph_vector_t *col_ids); + igraph_vector_int_t *row_ids, + igraph_vector_int_t *col_ids); -IGRAPH_EXPORT int igraph_is_bipartite(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *types); -IGRAPH_EXPORT int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, +IGRAPH_EXPORT igraph_error_t 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); +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_incidence( + const igraph_t *graph, const igraph_vector_bool_t *types, igraph_matrix_t *res, + igraph_vector_int_t *row_ids, igraph_vector_int_t *col_ids +); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_blas.h b/src/vendor/cigraph/include/igraph_blas.h index 4daf5df89d9..ae7daa47d24 100644 --- a/src/vendor/cigraph/include/igraph_blas.h +++ b/src/vendor/cigraph/include/igraph_blas.h @@ -25,6 +25,7 @@ #define IGRAPH_BLAS_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_matrix.h" @@ -51,16 +52,19 @@ __BEGIN_DECLS *
*/ -IGRAPH_EXPORT void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, +IGRAPH_EXPORT igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, + igraph_bool_t transpose_b, igraph_real_t alpha, const igraph_matrix_t* a, + const igraph_matrix_t* b, igraph_real_t beta, igraph_matrix_t* c); +IGRAPH_EXPORT igraph_error_t 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); IGRAPH_EXPORT igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v); -IGRAPH_EXPORT int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, +IGRAPH_EXPORT igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t *res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_centrality.h b/src/vendor/cigraph/include/igraph_centrality.h index c6f5abbc8f0..1e515a696dd 100644 --- a/src/vendor/cigraph/include/igraph_centrality.h +++ b/src/vendor/cigraph/include/igraph_centrality.h @@ -26,6 +26,7 @@ #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" #include "igraph_iterators.h" @@ -37,43 +38,51 @@ __BEGIN_DECLS /* Centrality */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_closeness(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, +IGRAPH_EXPORT igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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_EXPORT int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_t *reachable_count, igraph_bool_t *all_reachable, +IGRAPH_EXPORT igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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); -IGRAPH_EXPORT int igraph_harmonic_centrality(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_betweenness(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_bool_t directed, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, +IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, igraph_bool_t directed, - const igraph_vector_t *weigths); -IGRAPH_EXPORT int igraph_edge_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *result, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t 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); +IGRAPH_EXPORT igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights); /** * \typedef igraph_pagerank_algo_t - * \brief PageRank algorithm implementation + * \brief PageRank algorithm implementation. * * Algorithms to calculate PageRank. * \enumval IGRAPH_PAGERANK_ALGO_ARPACK Use the ARPACK library, this @@ -88,18 +97,18 @@ typedef enum { IGRAPH_PAGERANK_ALGO_PRPACK = 2 } igraph_pagerank_algo_t; -IGRAPH_EXPORT int igraph_pagerank(const igraph_t *graph, igraph_pagerank_algo_t algo, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_personalized_pagerank(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_personalized_pagerank_vs(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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, @@ -107,65 +116,62 @@ IGRAPH_EXPORT int igraph_personalized_pagerank_vs(const igraph_t *graph, igraph_vs_t reset_vids, const igraph_vector_t *weights, igraph_arpack_options_t *options); -IGRAPH_EXPORT int igraph_eigenvector_centrality(const igraph_t *graph, igraph_vector_t *vector, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_authority_score(const igraph_t *graph, igraph_vector_t *vector, +IGRAPH_EXPORT igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, igraph_vector_t *hub_vector, + igraph_vector_t *authority_vector, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options); -IGRAPH_EXPORT int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, +IGRAPH_EXPORT igraph_error_t igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_t *ins, igraph_vector_t *outs); IGRAPH_EXPORT igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized); -IGRAPH_EXPORT int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_centralization_degree_tmax(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_centralization_betweenness(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_centralization_betweenness_tmax(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res); -IGRAPH_EXPORT int igraph_centralization_closeness(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_centralization_closeness_tmax(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res); -IGRAPH_EXPORT int igraph_centralization_eigenvector_centrality( +IGRAPH_EXPORT igraph_error_t igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, @@ -175,29 +181,23 @@ IGRAPH_EXPORT int igraph_centralization_eigenvector_centrality( igraph_real_t *centralization, igraph_real_t *theoretical_max, igraph_bool_t normalized); -IGRAPH_EXPORT int igraph_centralization_eigenvector_centrality_tmax( +IGRAPH_EXPORT igraph_error_t 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); - /* Deprecated functions: */ -IGRAPH_EXPORT IGRAPH_DEPRECATED 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_EXPORT IGRAPH_DEPRECATED 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_EXPORT IGRAPH_DEPRECATED 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_DEPRECATED IGRAPH_EXPORT igraph_error_t 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); +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t 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); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_cliques.h b/src/vendor/cigraph/include/igraph_cliques.h index 179b3f788bc..141dd261f23 100644 --- a/src/vendor/cigraph/include/igraph_cliques.h +++ b/src/vendor/cigraph/include/igraph_cliques.h @@ -25,9 +25,10 @@ #define IGRAPH_CLIQUES_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS @@ -35,51 +36,51 @@ __BEGIN_DECLS /* Cliques, maximal independent vertex sets */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_maximal_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, - igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_maximal_cliques_file(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques( + const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size +); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_maximal_cliques_count(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_maximal_cliques_hist(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_subset( + const igraph_t *graph, const igraph_vector_int_t *subset, + igraph_vector_int_list_t *res, igraph_integer_t *no, + FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size +); +IGRAPH_EXPORT igraph_error_t igraph_maximal_cliques_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, +IGRAPH_EXPORT igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, +IGRAPH_EXPORT igraph_error_t igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_largest_cliques(const igraph_t *graph, - igraph_vector_ptr_t *cliques); -IGRAPH_EXPORT int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); -IGRAPH_EXPORT int igraph_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res, +IGRAPH_EXPORT igraph_error_t igraph_largest_cliques(const igraph_t *graph, + igraph_vector_int_list_t *cliques); +IGRAPH_EXPORT igraph_error_t igraph_clique_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT igraph_error_t igraph_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal); -IGRAPH_EXPORT int igraph_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res); -IGRAPH_EXPORT int igraph_weighted_clique_number(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t igraph_weighted_clique_number(const igraph_t *graph, const igraph_vector_t *vertex_weights, igraph_real_t *res); -IGRAPH_EXPORT int igraph_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res, +IGRAPH_EXPORT igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -IGRAPH_EXPORT int igraph_largest_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res); -IGRAPH_EXPORT int igraph_maximal_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res); -IGRAPH_EXPORT int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); +IGRAPH_EXPORT igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res); +IGRAPH_EXPORT igraph_error_t igraph_independence_number(const igraph_t *graph, igraph_integer_t *no); /** * \typedef igraph_clique_handler_t @@ -90,21 +91,22 @@ IGRAPH_EXPORT int igraph_independence_number(const igraph_t *graph, igraph_integ * See the details at the documentation of \ref * igraph_cliques_callback(). * - * \param clique The current clique. Destroying and freeing - * this vector is left to the user. - * Use \ref igraph_vector_destroy() and \ref igraph_free() - * to do this. + * \param clique The current clique. The clique is owned by the clique search + * routine. You do not need to destroy or free it if you do not want to store + * it; however, if you want to hold on to it for a longer period of time, you + * need to make a copy of it on your own and store the copy itself. * \param arg This extra argument was passed to \ref * igraph_cliques_callback() when it was called. - * \return Boolean, whether to continue with the clique search. + * \return Error code; \c IGRAPH_SUCCESS to continue the search or + * \c IGRAPH_STOP to stop the search without signaling an error. */ -typedef igraph_bool_t igraph_clique_handler_t(igraph_vector_t *clique, void *arg); +typedef igraph_error_t igraph_clique_handler_t(const igraph_vector_int_t *clique, void *arg); -IGRAPH_EXPORT int igraph_cliques_callback(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_maximal_cliques_callback(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); diff --git a/src/vendor/cigraph/include/igraph_cocitation.h b/src/vendor/cigraph/include/igraph_cocitation.h index 32699b10804..4dfd406ab94 100644 --- a/src/vendor/cigraph/include/igraph_cocitation.h +++ b/src/vendor/cigraph/include/igraph_cocitation.h @@ -25,6 +25,7 @@ #define IGRAPH_COCITATION_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" @@ -36,28 +37,28 @@ __BEGIN_DECLS /* Cocitation and other similarity measures */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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_EXPORT 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_EXPORT int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_similarity_inverse_log_weighted(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_similarity_inverse_log_weighted(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t vids, igraph_neimode_t mode); diff --git a/src/vendor/cigraph/include/igraph_cohesive_blocks.h b/src/vendor/cigraph/include/igraph_cohesive_blocks.h index b26a5f3d581..a52cf68f733 100644 --- a/src/vendor/cigraph/include/igraph_cohesive_blocks.h +++ b/src/vendor/cigraph/include/igraph_cohesive_blocks.h @@ -26,15 +26,16 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_vector.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_cohesive_blocks(const igraph_t *graph, - igraph_vector_ptr_t *blocks, - igraph_vector_t *cohesion, - igraph_vector_t *parent, +IGRAPH_EXPORT igraph_error_t igraph_cohesive_blocks(const igraph_t *graph, + igraph_vector_int_list_t *blocks, + igraph_vector_int_t *cohesion, + igraph_vector_int_t *parent, igraph_t *block_tree); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_coloring.h b/src/vendor/cigraph/include/igraph_coloring.h index 02876ebd94b..845cd77d359 100644 --- a/src/vendor/cigraph/include/igraph_coloring.h +++ b/src/vendor/cigraph/include/igraph_coloring.h @@ -23,6 +23,7 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" __BEGIN_DECLS @@ -32,14 +33,22 @@ __BEGIN_DECLS * * Ordering heuristics for \ref igraph_vertex_coloring_greedy(). * - * \enumval IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS Choose vertex with largest number of already colored neighbors. - * + * \enumval IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS + * Choose the vertex with largest number of already colored neighbors. + * \enumval IGRAPH_COLORING_GREEDY_DSATUR + * Choose the vertex with largest number of unique colors in its neighborhood, i.e. its + * "saturation degree". When multiple vertices have the same saturation degree, choose + * the one with the most not yet colored neighbors. Added in igraph 0.10.4. This heuristic + * is known as "DSatur", and was proposed in + * Daniel Brélaz: New methods to color the vertices of a graph, + * Commun. ACM 22, 4 (1979), 251–256. https://doi.org/10.1145/359094.359101 */ typedef enum { - IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0 + IGRAPH_COLORING_GREEDY_COLORED_NEIGHBORS = 0, + IGRAPH_COLORING_GREEDY_DSATUR = 1 } igraph_coloring_greedy_t; -IGRAPH_EXPORT int igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic); +IGRAPH_EXPORT igraph_error_t igraph_vertex_coloring_greedy(const igraph_t *graph, igraph_vector_int_t *colors, igraph_coloring_greedy_t heuristic); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_community.h b/src/vendor/cigraph/include/igraph_community.h index aaaa2461ebd..d81b8bd555e 100644 --- a/src/vendor/cigraph/include/igraph_community.h +++ b/src/vendor/cigraph/include/igraph_community.h @@ -25,20 +25,24 @@ #define IGRAPH_COMMUNITY_H #include "igraph_decls.h" + +#include "igraph_arpack.h" #include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_arpack.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS /* -------------------------------------------------- */ -/* K-Cores */ +/* K-Cores and K-Truss */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, - igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_coreness( + const igraph_t *graph, igraph_vector_int_t *cores, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_trussness( + const igraph_t* graph, igraph_vector_int_t* trussness); /* -------------------------------------------------- */ /* Community Structure */ @@ -48,17 +52,17 @@ IGRAPH_EXPORT int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, /* TODO: edge.type.matrix */ /* TODO: */ -IGRAPH_EXPORT int igraph_community_optimal_modularity(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, - igraph_vector_t *membership, + igraph_vector_int_t *membership, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_community_spinglass(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -66,17 +70,13 @@ IGRAPH_EXPORT int igraph_community_spinglass(const igraph_t *graph, 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 lambda); - -IGRAPH_EXPORT int igraph_community_spinglass_single(const igraph_t *graph, + igraph_spinglass_implementation_t implementation, + igraph_real_t gamma_minus); + +IGRAPH_EXPORT igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, - igraph_vector_t *community, + igraph_vector_int_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -85,69 +85,69 @@ IGRAPH_EXPORT int igraph_community_spinglass_single(const igraph_t *graph, igraph_spincomm_update_t update_rule, igraph_real_t gamma); -IGRAPH_EXPORT int igraph_community_walktrap(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, - int steps, - igraph_matrix_t *merges, + igraph_integer_t steps, + igraph_matrix_int_t *merges, igraph_vector_t *modularity, - igraph_vector_t *membership); + igraph_vector_int_t *membership); -IGRAPH_EXPORT int igraph_community_infomap(const igraph_t * graph, +IGRAPH_EXPORT igraph_error_t 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_integer_t nb_trials, + igraph_vector_int_t *membership, igraph_real_t *codelength); -IGRAPH_EXPORT int igraph_community_edge_betweenness(const igraph_t *graph, - igraph_vector_t *result, +IGRAPH_EXPORT igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_int_t *result, igraph_vector_t *edge_betweenness, - igraph_matrix_t *merges, - igraph_vector_t *bridges, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, igraph_vector_t *modularity, - igraph_vector_t *membership, + igraph_vector_int_t *membership, igraph_bool_t directed, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_community_eb_get_merges(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_t *edges, + const igraph_vector_int_t *edges, const igraph_vector_t *weights, - igraph_matrix_t *merges, - igraph_vector_t *bridges, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, igraph_vector_t *modularity, - igraph_vector_t *membership); + igraph_vector_int_t *membership); -IGRAPH_EXPORT int igraph_community_fastgreedy(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_t *merges, + igraph_matrix_int_t *merges, igraph_vector_t *modularity, - igraph_vector_t *membership); + igraph_vector_int_t *membership); -IGRAPH_EXPORT int igraph_community_to_membership(const igraph_matrix_t *merges, +IGRAPH_EXPORT igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, igraph_integer_t nodes, igraph_integer_t steps, - igraph_vector_t *membership, - igraph_vector_t *csize); -IGRAPH_EXPORT int igraph_le_community_to_membership(const igraph_matrix_t *merges, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize); +IGRAPH_EXPORT igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, igraph_integer_t steps, - igraph_vector_t *membership, - igraph_vector_t *csize); + igraph_vector_int_t *membership, + igraph_vector_int_t *csize); -IGRAPH_EXPORT int igraph_modularity(const igraph_t *graph, - const igraph_vector_t *membership, +IGRAPH_EXPORT igraph_error_t igraph_modularity(const igraph_t *graph, + const igraph_vector_int_t *membership, const igraph_vector_t *weights, const igraph_real_t resolution, const igraph_bool_t directed, igraph_real_t *modularity); -IGRAPH_EXPORT int igraph_modularity_matrix(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_reindex_membership(igraph_vector_t *membership, - igraph_vector_t *new_to_old, +IGRAPH_EXPORT igraph_error_t igraph_reindex_membership(igraph_vector_int_t *membership, + igraph_vector_int_t *new_to_old, igraph_integer_t *nb_clusters); typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, @@ -168,7 +168,7 @@ typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, * \param membership The actual membership vector, before recording * the potential change implied by the newly found eigenvalue. * \param comm The id of the community that the algorithm tried to - * split in the last iteration. The community ids are indexed from + * split in the last iteration. The community IDs are indexed from * zero here! * \param eigenvalue The eigenvalue the algorithm has just found. * \param eigenvector The eigenvector corresponding to the eigenvalue @@ -184,67 +184,67 @@ typedef enum { IGRAPH_LEVC_HIST_SPLIT = 1, * igraph_arpack_function_t, \ref igraph_arpack_rssolve(). */ -typedef int igraph_community_leading_eigenvector_callback_t( - const igraph_vector_t *membership, - long int comm, +typedef igraph_error_t igraph_community_leading_eigenvector_callback_t( + const igraph_vector_int_t *membership, + igraph_integer_t comm, igraph_real_t eigenvalue, const igraph_vector_t *eigenvector, igraph_arpack_function_t *arpack_multiplier, void *arpack_extra, void *extra); -IGRAPH_EXPORT int igraph_community_leading_eigenvector(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_leading_eigenvector(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_t *merges, - igraph_vector_t *membership, + igraph_matrix_int_t *merges, + igraph_vector_int_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_list_t *eigenvectors, igraph_vector_t *history, igraph_community_leading_eigenvector_callback_t *callback, void *callback_extra); -IGRAPH_EXPORT int igraph_community_fluid_communities(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, igraph_integer_t no_of_communities, - igraph_vector_t *membership, - igraph_real_t *modularity); + igraph_vector_int_t *membership); -IGRAPH_EXPORT int igraph_community_label_propagation(const igraph_t *graph, - igraph_vector_t *membership, +IGRAPH_EXPORT igraph_error_t igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_neimode_t mode, const igraph_vector_t *weights, - const igraph_vector_t *initial, - const igraph_vector_bool_t *fixed, - igraph_real_t *modularity); + const igraph_vector_int_t *initial, + const igraph_vector_bool_t *fixed); -IGRAPH_EXPORT int igraph_community_multilevel(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_int_t *membership, + igraph_matrix_int_t *memberships, igraph_vector_t *modularity); -IGRAPH_EXPORT int igraph_community_leiden(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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, + const igraph_integer_t n_iterations, + igraph_vector_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality); /* -------------------------------------------------- */ /* Community Structure Comparison */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_compare_communities(const igraph_vector_t *comm1, - const igraph_vector_t *comm2, +IGRAPH_EXPORT igraph_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_real_t* result, igraph_community_comparison_t method); -IGRAPH_EXPORT int igraph_split_join_distance(const igraph_vector_t *comm1, - const igraph_vector_t *comm2, +IGRAPH_EXPORT igraph_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_integer_t* distance12, igraph_integer_t* distance21); diff --git a/src/vendor/cigraph/include/igraph_complex.h b/src/vendor/cigraph/include/igraph_complex.h index 2ff0b45aa88..d17b854bd70 100644 --- a/src/vendor/cigraph/include/igraph_complex.h +++ b/src/vendor/cigraph/include/igraph_complex.h @@ -40,9 +40,13 @@ typedef struct igraph_complex_t { IGRAPH_EXPORT igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); IGRAPH_EXPORT igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta); -IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, - igraph_complex_t z2, - igraph_real_t tol); +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_bool_t igraph_complex_almost_equals(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t eps); IGRAPH_EXPORT igraph_real_t igraph_complex_mod(igraph_complex_t z); IGRAPH_EXPORT igraph_real_t igraph_complex_arg(igraph_complex_t z); @@ -99,6 +103,12 @@ IGRAPH_EXPORT igraph_complex_t igraph_complex_sec(igraph_complex_t z); IGRAPH_EXPORT igraph_complex_t igraph_complex_csc(igraph_complex_t z); IGRAPH_EXPORT igraph_complex_t igraph_complex_cot(igraph_complex_t z); +IGRAPH_EXPORT int igraph_complex_printf(igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_fprintf(FILE *file, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_printf_aligned(int width, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val); +IGRAPH_EXPORT int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_components.h b/src/vendor/cigraph/include/igraph_components.h index b9c47972860..b3d05e44d20 100644 --- a/src/vendor/cigraph/include/igraph_components.h +++ b/src/vendor/cigraph/include/igraph_components.h @@ -25,11 +25,15 @@ #define IGRAPH_COMPONENTS_H #include "igraph_decls.h" + #include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_graph_list.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_ptr.h" -#include "igraph_datatype.h" +#include "igraph_vector_list.h" +#include "igraph_vector_ptr.h" /* because of igraph_decompose_destroy() */ __BEGIN_DECLS @@ -37,24 +41,31 @@ __BEGIN_DECLS /* Components */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_clusters(const igraph_t *graph, igraph_vector_t *membership, - igraph_vector_t *csize, igraph_integer_t *no, +/* Deprecated alias to igraph_connected_components; will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, igraph_connectedness_t mode); -IGRAPH_EXPORT int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, +IGRAPH_EXPORT igraph_error_t igraph_connected_components(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, igraph_connectedness_t mode); -IGRAPH_EXPORT void igraph_decompose_destroy(igraph_vector_ptr_t *complist); -IGRAPH_EXPORT int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, +IGRAPH_EXPORT igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, igraph_connectedness_t mode, - long int maxcompno, long int minelements); -IGRAPH_EXPORT int igraph_articulation_points(const igraph_t *graph, - igraph_vector_t *res); -IGRAPH_EXPORT int igraph_biconnected_components(const igraph_t *graph, + igraph_integer_t maxcompno, igraph_integer_t minelements); +IGRAPH_EXPORT igraph_error_t igraph_articulation_points(const igraph_t *graph, + igraph_vector_int_t *res); +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges); + igraph_vector_int_list_t *tree_edges, + igraph_vector_int_list_t *component_edges, + igraph_vector_int_list_t *components, + igraph_vector_int_t *articulation_points); +IGRAPH_EXPORT igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges); + +/* Deprecated in igraph 0.10 when we switched to igraph_graph_list_t. Will be + * removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED void igraph_decompose_destroy(igraph_vector_ptr_t *complist); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_config.h.in b/src/vendor/cigraph/include/igraph_config.h.in new file mode 100644 index 00000000000..99e6feeecf9 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_config.h.in @@ -0,0 +1,55 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2011-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_CONFIG_H +#define IGRAPH_CONFIG_H + +#include "igraph_decls.h" + +__BEGIN_DECLS + +/** + * \define IGRAPH_INTEGER_SIZE + * + * Specifies the size of igraph's integer data type; must be one of 32 (for + * 32-bit integers) or 64 (for 64-bit integers). + */ +#define IGRAPH_INTEGER_SIZE @IGRAPH_INTEGER_SIZE@ + +#define IGRAPH_DEPRECATED_ENUMVAL @IGRAPH_DEPRECATED_ENUMVAL@ + +/** + * \define IGRAPH_BOOL_TYPE + * + * Specifies the C type to be used for igraph_bool_t. This is added here _only_ + * to support the R interface, where we want to be able to create views into + * R boolean vectors and treat them as an igraph_vector_bool_t, which requires + * us to align igraph_bool_t with R's boolean type. + * + * Any other use-case of overriding igraph's bool type is completely + * unsupported. + */ +#define IGRAPH_BOOL_TYPE bool + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_constants.h b/src/vendor/cigraph/include/igraph_constants.h index 3fcb305aba2..2452c8c5a5d 100644 --- a/src/vendor/cigraph/include/igraph_constants.h +++ b/src/vendor/cigraph/include/igraph_constants.h @@ -24,9 +24,8 @@ #ifndef IGRAPH_CONSTANTS_H #define IGRAPH_CONSTANTS_H +#include "igraph_config.h" #include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_datatype.h" __BEGIN_DECLS @@ -48,9 +47,9 @@ typedef enum { IGRAPH_ASCENDING = 0, IGRAPH_DESCENDING = 1 } igraph_order_t; typedef enum { IGRAPH_MINIMUM = 0, IGRAPH_MAXIMUM = 1 } igraph_optimal_t; -typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 3, - IGRAPH_TOTAL = 3 - } igraph_neimode_t; +/* Do not renumber the following values! Some internal code treats them as bitmasks + * and assumes that IGRAPH_ALL == IGRAPH_IN | IGRAPH_OUT and IGRAPH_IN & IGRAPH_OUT == 0. */ +typedef enum { IGRAPH_OUT = 1, IGRAPH_IN = 2, IGRAPH_ALL = 3 } igraph_neimode_t; /* Reverse IGRAPH_OUT to IGRAPH_IN and vice versa. Leave other values alone. */ #define IGRAPH_REVERSE_MODE(mode) \ @@ -63,9 +62,10 @@ typedef enum { IGRAPH_RECIPROCITY_DEFAULT = 0, } igraph_reciprocity_t; typedef enum { IGRAPH_ADJ_DIRECTED = 0, - IGRAPH_ADJ_UNDIRECTED = 1, IGRAPH_ADJ_MAX = 1, + IGRAPH_ADJ_UNDIRECTED, IGRAPH_ADJ_UPPER, IGRAPH_ADJ_LOWER, IGRAPH_ADJ_MIN, - IGRAPH_ADJ_PLUS + IGRAPH_ADJ_PLUS, + IGRAPH_ADJ_MAX, } igraph_adjacency_t; typedef enum { IGRAPH_STAR_OUT = 0, IGRAPH_STAR_IN, @@ -73,6 +73,11 @@ typedef enum { IGRAPH_STAR_OUT = 0, IGRAPH_STAR_IN, IGRAPH_STAR_MUTUAL } igraph_star_mode_t; +typedef enum { IGRAPH_WHEEL_OUT = 0, IGRAPH_WHEEL_IN, + IGRAPH_WHEEL_UNDIRECTED, + IGRAPH_WHEEL_MUTUAL + } igraph_wheel_mode_t; + typedef enum { IGRAPH_TREE_OUT = 0, IGRAPH_TREE_IN, IGRAPH_TREE_UNDIRECTED } igraph_tree_mode_t; @@ -86,10 +91,16 @@ typedef enum { IGRAPH_GET_ADJACENCY_UPPER = 0, IGRAPH_GET_ADJACENCY_BOTH } igraph_get_adjacency_t; -typedef enum { IGRAPH_DEGSEQ_SIMPLE = 0, - IGRAPH_DEGSEQ_VL, - IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE, - IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM +typedef enum { IGRAPH_DEGSEQ_CONFIGURATION = 0, /* Configuration model, allowing non-simple graphs */ + IGRAPH_DEGSEQ_VL, /* Viger-Latapy, generates simple connected graphs */ + IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, /* Fast heuristic, generates simple graphs */ + IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE, /* Configuration model, generates simple graphs */ + IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE, /* Edge-switching MCMC, generates simple graphs */ + + /* Deprecated, kept for backwards compatibility: */ + IGRAPH_DEGSEQ_SIMPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE, + IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM IGRAPH_DEPRECATED_ENUMVAL = IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE } igraph_degseq_t; typedef enum { IGRAPH_REALIZE_DEGSEQ_SMALLEST = 0, @@ -181,13 +192,6 @@ typedef enum { IGRAPH_IMITATE_AUGMENTED = 0, IGRAPH_IMITATE_CONTRACTED } igraph_imitate_algorithm_t; -typedef igraph_real_t igraph_scalar_function_t(const igraph_vector_t *var, - const igraph_vector_t *par, - void* extra); -typedef void igraph_vector_function_t(const igraph_vector_t *var, - const igraph_vector_t *par, - igraph_vector_t* res, void* extra); - typedef enum { IGRAPH_LAYOUT_GRID = 0, IGRAPH_LAYOUT_NOGRID, IGRAPH_LAYOUT_AUTOGRID @@ -197,6 +201,14 @@ typedef enum { IGRAPH_RANDOM_WALK_STUCK_ERROR = 0, IGRAPH_RANDOM_WALK_STUCK_RETURN } igraph_random_walk_stuck_t; +typedef enum { IGRAPH_VORONOI_FIRST = 0, + IGRAPH_VORONOI_LAST, + IGRAPH_VORONOI_RANDOM + } igraph_voronoi_tiebreaker_t; + +typedef enum { IGRAPH_ROW_MAJOR = 0, + IGRAPH_COLUMN_MAJOR = 1 + } igraph_matrix_storage_t; __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_constructors.h b/src/vendor/cigraph/include/igraph_constructors.h index d7b1987349e..bd64dcf2157 100644 --- a/src/vendor/cigraph/include/igraph_constructors.h +++ b/src/vendor/cigraph/include/igraph_constructors.h @@ -26,10 +26,12 @@ #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_datatype.h" #include "igraph_graphicality.h" +#include "igraph_sparsemat.h" __BEGIN_DECLS @@ -37,43 +39,65 @@ __BEGIN_DECLS /* Constructors, deterministic */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_create(igraph_t *graph, const igraph_vector_t *edges, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_t *edges, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - ...); -IGRAPH_EXPORT int igraph_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, - igraph_adjacency_t mode); -IGRAPH_EXPORT int igraph_weighted_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, - igraph_adjacency_t mode, const char* attr, - igraph_bool_t loops); -IGRAPH_EXPORT int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, +IGRAPH_EXPORT igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + int first, ...); +IGRAPH_EXPORT igraph_error_t igraph_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_weighted_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_sparse_weighted_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, igraph_vector_t *weights, igraph_loops_t loops); +IGRAPH_EXPORT igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, igraph_integer_t center); -IGRAPH_EXPORT int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, igraph_integer_t nei, +IGRAPH_EXPORT igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, + igraph_integer_t center); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); -IGRAPH_EXPORT int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +IGRAPH_EXPORT igraph_error_t igraph_square_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, + igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *circular); +IGRAPH_EXPORT igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t circular); -IGRAPH_EXPORT int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, igraph_tree_mode_t type); -IGRAPH_EXPORT int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); -IGRAPH_EXPORT int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_full_citation(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_kary_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, + igraph_tree_mode_t type); +IGRAPH_EXPORT igraph_error_t igraph_tree_from_parent_vector(igraph_t *graph, const igraph_vector_int_t *parents, + igraph_tree_mode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer); +IGRAPH_EXPORT igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_full_multipartite(igraph_t *graph, igraph_vector_int_t *types, const igraph_vector_int_t *n, + igraph_bool_t directed, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_turan(igraph_t *graph, igraph_vector_int_t *types, igraph_integer_t n, igraph_integer_t r); +IGRAPH_EXPORT igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_atlas(igraph_t *graph, int number); -IGRAPH_EXPORT int igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, - const igraph_matrix_t *W, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); +IGRAPH_EXPORT igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number); +IGRAPH_EXPORT igraph_error_t igraph_extended_chordal_ring(igraph_t *graph, igraph_integer_t nodes, + const igraph_matrix_int_t *W, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph); -IGRAPH_EXPORT int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -IGRAPH_EXPORT int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); -IGRAPH_EXPORT int igraph_famous(igraph_t *graph, const char *name); -IGRAPH_EXPORT int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, - const igraph_vector_t *shifts, +IGRAPH_EXPORT igraph_error_t igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *l, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k); +IGRAPH_EXPORT igraph_error_t igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_famous(igraph_t *graph, const char *name); +IGRAPH_EXPORT igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_t *shifts, igraph_integer_t repeats); -IGRAPH_EXPORT int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); -IGRAPH_EXPORT int igraph_realize_degree_sequence(igraph_t *graph, - const igraph_vector_t *outdeg, const igraph_vector_t *indeg, +IGRAPH_EXPORT igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...); +IGRAPH_EXPORT igraph_error_t igraph_realize_degree_sequence(igraph_t *graph, + const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_edge_type_sw_t allowed_edge_types, igraph_realize_degseq_t method); +IGRAPH_EXPORT igraph_error_t igraph_triangular_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); +IGRAPH_EXPORT igraph_error_t igraph_hexagonal_lattice(igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, igraph_bool_t mutual); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_conversion.h b/src/vendor/cigraph/include/igraph_conversion.h index d191dc28809..d47d7f677d9 100644 --- a/src/vendor/cigraph/include/igraph_conversion.h +++ b/src/vendor/cigraph/include/igraph_conversion.h @@ -26,9 +26,9 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_spmatrix.h" +#include "igraph_error.h" +#include "igraph_types.h" #include "igraph_matrix.h" #include "igraph_sparsemat.h" #include "igraph_attributes.h" @@ -39,27 +39,41 @@ __BEGIN_DECLS /* Conversion */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, - igraph_get_adjacency_t type, igraph_bool_t eids); -IGRAPH_EXPORT int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, - igraph_get_adjacency_t type); +IGRAPH_EXPORT igraph_error_t igraph_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +); +IGRAPH_EXPORT igraph_error_t igraph_get_adjacency_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +); + +IGRAPH_EXPORT igraph_error_t igraph_get_stochastic( + const igraph_t *graph, igraph_matrix_t *matrix, igraph_bool_t column_wise, + const igraph_vector_t *weights +); + +IGRAPH_EXPORT igraph_error_t igraph_get_stochastic_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise, + const igraph_vector_t *weights +); -IGRAPH_EXPORT int igraph_get_stochastic(const igraph_t *graph, - igraph_matrix_t *matrix, - igraph_bool_t column_wise); +/* Deprecated, will be removed in 0.11. Use igraph_get_adjacency_sparse() instead, paying attention to differences. */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); -IGRAPH_EXPORT int igraph_get_stochastic_sparsemat(const igraph_t *graph, - igraph_sparsemat_t *sparsemat, +/* Deprecated, will be removed in 0.11. Use igraph_get_stochastic_sparse() instead, paying attention to differences. */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *res, igraph_bool_t column_wise); -IGRAPH_EXPORT int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t bycol); +IGRAPH_EXPORT igraph_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_t *res, igraph_bool_t bycol); -IGRAPH_EXPORT int igraph_to_directed(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_to_directed(igraph_t *graph, igraph_to_directed_t flags); -IGRAPH_EXPORT int igraph_to_undirected(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_to_undirected(igraph_t *graph, igraph_to_undirected_t mode, const igraph_attribute_combination_t *edge_comb); -IGRAPH_EXPORT int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); +IGRAPH_EXPORT igraph_error_t igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t *prufer); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_cycles.h b/src/vendor/cigraph/include/igraph_cycles.h new file mode 100644 index 00000000000..dd5870003b3 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_cycles.h @@ -0,0 +1,30 @@ + +#ifndef IGRAPH_CYCLES_H +#define IGRAPH_CYCLES_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_fundamental_cycles( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_minimum_cycle_basis( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t bfs_cutoff, + igraph_bool_t complete, + igraph_bool_t use_cycle_order, + const igraph_vector_t *weights); + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_datatype.h b/src/vendor/cigraph/include/igraph_datatype.h index 5e2219255e0..c0ee080e864 100644 --- a/src/vendor/cigraph/include/igraph_datatype.h +++ b/src/vendor/cigraph/include/igraph_datatype.h @@ -30,6 +30,44 @@ __BEGIN_DECLS +struct igraph_i_property_cache_t; +typedef struct igraph_i_property_cache_t igraph_i_property_cache_t; + +typedef enum { + /* Stores whether the graph has at least one self-loop. */ + IGRAPH_PROP_HAS_LOOP = 0, + + /* Stores whether the graph has at least one multi-edge, taking into account + * edge directions in directed graphs. In other words, this property should + * be false for a directed graph with edges (a, b) and (b, a), and true + * for a directed graph with edges (a, b) and (a, b) again. */ + IGRAPH_PROP_HAS_MULTI, + + /* Stores whether the graph has at least one reciprocal edge pair. Ignored + * in undirected graphs. This property should be true for a directed graph + * with edges (a, b) and (b, a), and false for a directed graph with + * edges (a, b) and (a, b) again. Self-loops (a, a) are not considered + * reciprocal. */ + IGRAPH_PROP_HAS_MUTUAL, + + /* Stores whether the graph is weakly connected. */ + IGRAPH_PROP_IS_WEAKLY_CONNECTED, + + /* Stores whether the graph is strongly connected. Ignored in undirected graphs. */ + IGRAPH_PROP_IS_STRONGLY_CONNECTED, + + /* Stores whether the graph is a directed acyclic graph. Not used for + * undirected graphs. */ + IGRAPH_PROP_IS_DAG, + + /* Stores whether the graph is a forest, i.e. an undirected or directed + * graph that is cycle-free even if we ignore edge directions. */ + IGRAPH_PROP_IS_FOREST, + + /* Dummy value used to count enum values */ + IGRAPH_PROP_I_SIZE +} igraph_cached_property_t; + /** * \ingroup internal * \struct igraph_t @@ -72,15 +110,18 @@ __BEGIN_DECLS typedef struct igraph_s { igraph_integer_t n; igraph_bool_t directed; - igraph_vector_t from; - igraph_vector_t to; - igraph_vector_t oi; - igraph_vector_t ii; - igraph_vector_t os; - igraph_vector_t is; + igraph_vector_int_t from; + igraph_vector_int_t to; + igraph_vector_int_t oi; + igraph_vector_int_t ii; + igraph_vector_int_t os; + igraph_vector_int_t is; void *attr; + igraph_i_property_cache_t *cache; } igraph_t; +IGRAPH_EXPORT void igraph_invalidate_cache(const igraph_t* graph); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_dqueue.h b/src/vendor/cigraph/include/igraph_dqueue.h index 5aab71f3e71..53c8b0e4ba7 100644 --- a/src/vendor/cigraph/include/igraph_dqueue.h +++ b/src/vendor/cigraph/include/igraph_dqueue.h @@ -25,6 +25,7 @@ #define IGRAPH_DQUEUE_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -39,12 +40,6 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_LONG -#include "igraph_pmt.h" -#include "igraph_dqueue_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_LONG - #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_dqueue_pmt.h" @@ -64,9 +59,12 @@ __BEGIN_DECLS #undef BASE_INT #define IGRAPH_DQUEUE_NULL { 0,0,0,0 } -#define IGRAPH_DQUEUE_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_dqueue_init(v, size)); \ - IGRAPH_FINALLY(igraph_dqueue_destroy, v); } while (0) +#define IGRAPH_DQUEUE_INIT_FINALLY(q, capacity) \ + do { IGRAPH_CHECK(igraph_dqueue_init(q, capacity)); \ + IGRAPH_FINALLY(igraph_dqueue_destroy, q); } while (0) +#define IGRAPH_DQUEUE_INT_INIT_FINALLY(q, capacity) \ + do { IGRAPH_CHECK(igraph_dqueue_int_init(q, capacity)); \ + IGRAPH_FINALLY(igraph_dqueue_int_destroy, q); } while (0) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_dqueue_pmt.h b/src/vendor/cigraph/include/igraph_dqueue_pmt.h index d478bdce31f..e0320195075 100644 --- a/src/vendor/cigraph/include/igraph_dqueue_pmt.h +++ b/src/vendor/cigraph/include/igraph_dqueue_pmt.h @@ -33,17 +33,18 @@ typedef struct TYPE(igraph_dqueue) { BASE *stor_end; } TYPE(igraph_dqueue); -IGRAPH_EXPORT int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size); -IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT void FUNCTION(igraph_dqueue, clear) (TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, empty)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT void FUNCTION(igraph_dqueue, clear)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_dqueue, full)(TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop)(TYPE(igraph_dqueue)* q); IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem); -IGRAPH_EXPORT int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); -IGRAPH_EXPORT int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); -IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, head)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, back)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file); +IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); +IGRAPH_DEPRECATED IGRAPH_EXPORT BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx); diff --git a/src/vendor/cigraph/include/igraph_eigen.h b/src/vendor/cigraph/include/igraph_eigen.h index 67ba26d4ad1..122eeb8a3a8 100644 --- a/src/vendor/cigraph/include/igraph_eigen.h +++ b/src/vendor/cigraph/include/igraph_eigen.h @@ -26,6 +26,7 @@ #include "igraph_decls.h" #include "igraph_arpack.h" +#include "igraph_error.h" #include "igraph_lapack.h" #include "igraph_sparsemat.h" @@ -63,7 +64,7 @@ typedef struct igraph_eigen_which_t { igraph_lapack_dgeevx_balance_t balance; } igraph_eigen_which_t; -IGRAPH_EXPORT int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -74,7 +75,7 @@ IGRAPH_EXPORT int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, igraph_vector_t *values, igraph_matrix_t *vectors); -IGRAPH_EXPORT int igraph_eigen_matrix(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -85,7 +86,7 @@ IGRAPH_EXPORT int igraph_eigen_matrix(const igraph_matrix_t *A, igraph_vector_complex_t *values, igraph_matrix_complex_t *vectors); -IGRAPH_EXPORT int igraph_eigen_adjacency(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, @@ -95,17 +96,6 @@ IGRAPH_EXPORT int igraph_eigen_adjacency(const igraph_t *graph, igraph_vector_complex_t *cmplxvalues, igraph_matrix_complex_t *cmplxvectors); -IGRAPH_EXPORT 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); - - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_embedding.h b/src/vendor/cigraph/include/igraph_embedding.h index 3eb97c67d76..2462b010c88 100644 --- a/src/vendor/cigraph/include/igraph_embedding.h +++ b/src/vendor/cigraph/include/igraph_embedding.h @@ -26,13 +26,13 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_arpack.h" #include "igraph_eigen.h" -#include "igraph_constants.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_adjacency_spectral_embedding(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -50,7 +50,7 @@ typedef enum { IGRAPH_EMBEDDING_OAP } igraph_laplacian_spectral_embedding_type_t; -IGRAPH_EXPORT int igraph_laplacian_spectral_embedding(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -61,7 +61,7 @@ IGRAPH_EXPORT int igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_vector_t *D, igraph_arpack_options_t *options); -IGRAPH_EXPORT int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); +IGRAPH_EXPORT igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_epidemics.h b/src/vendor/cigraph/include/igraph_epidemics.h index 52c1122c387..a13f8cbcd0f 100644 --- a/src/vendor/cigraph/include/igraph_epidemics.h +++ b/src/vendor/cigraph/include/igraph_epidemics.h @@ -26,6 +26,7 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" @@ -55,10 +56,10 @@ typedef struct igraph_sir_t { igraph_vector_int_t no_s, no_i, no_r; } igraph_sir_t; -IGRAPH_EXPORT int igraph_sir_init(igraph_sir_t *sir); +IGRAPH_EXPORT igraph_error_t igraph_sir_init(igraph_sir_t *sir); IGRAPH_EXPORT void igraph_sir_destroy(igraph_sir_t *sir); -IGRAPH_EXPORT int igraph_sir(const igraph_t *graph, igraph_real_t beta, +IGRAPH_EXPORT igraph_error_t igraph_sir(const igraph_t *graph, igraph_real_t beta, igraph_real_t gamma, igraph_integer_t no_sim, igraph_vector_ptr_t *result); diff --git a/src/vendor/cigraph/include/igraph_error.h b/src/vendor/cigraph/include/igraph_error.h index c9d6e54a462..d0d6bf37224 100644 --- a/src/vendor/cigraph/include/igraph_error.h +++ b/src/vendor/cigraph/include/igraph_error.h @@ -25,6 +25,7 @@ #define IGRAPH_ERROR_H #include "igraph_decls.h" +#include "igraph_config.h" #include @@ -35,20 +36,32 @@ __BEGIN_DECLS * prefix renamed to IGRAPH_), as I couldn't find a better way to do * them. */ -/* IGRAPH_NORETURN indicates to the compiler that a function does not return. +/* With some compilers, we use function attributes to help diagnostics + * and optimizations. These are not part of the public API, do not use + * them outside of igraph itself. + * + * IGRAPH_FUNCATTR_NORETURN indicates to the compiler that a function does not return. * There are standard facilities for this, namely _Noreturn in C11 and [[noreturn]] in C++11. * However, since igraph is currently compiled with older standards, and since * the standard 'noreturn' specification would need to be diferent between C and C++, * we do not use these facilities. + * + * IGRAPH_FUNCATTR_PRINTFLIKE(string, first) marks a function as having a printf-like syntax, + * allowing the compiler to check that the format specifiers match argument types. + * 'string' is the index of the string-argument and 'first' is the index of the + * first argument to check against format specifiers. */ #if defined(__GNUC__) /* Compilers that support the GNU C syntax. Use __noreturn__ instead of 'noreturn' as the latter is a macro in C11. */ -#define IGRAPH_NORETURN __attribute__((__noreturn__)) +#define IGRAPH_FUNCATTR_NORETURN __attribute__((__noreturn__)) +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) __attribute__((format(printf, string, first))) #elif defined(_MSC_VER) /* Compilers that support the MSVC syntax. */ -#define IGRAPH_NORETURN __declspec(noreturn) +#define IGRAPH_FUNCATTR_NORETURN __declspec(noreturn) +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) #else -#define IGRAPH_NORETURN +#define IGRAPH_FUNCATTR_NORETURN +#define IGRAPH_FUNCATTR_PRINTFLIKE(string, first) #endif /** @@ -120,7 +133,8 @@ __BEGIN_DECLS * * The contents of the rest of this chapter might be useful only * for those who want to create an interface to \a igraph from another - * language. Most readers can safely skip to the next chapter. + * language, or use igraph from a GUI application. Most readers can + * safely skip to the next chapter. * * * @@ -128,17 +142,27 @@ __BEGIN_DECLS * function of type \ref igraph_error_handler_t and calling * \ref igraph_set_error_handler(). This feature is useful for interface * writers, as \a igraph will have the chance to - * signal errors the appropriate way, e.g. the R interface defines an - * error handler which calls the error() - * function, as required by R, while the Python interface has an error - * handler which raises an exception according to the Python way. + * signal errors the appropriate way. For example, the R interface uses + * R's native printing facilities to communicate errors, while the Python + * interface converts them into Python exceptions. + * + * + * + * The two main tasks of the error handler are to report the error + * (i.e. print the error message) and ensure proper resource cleanup. + * This is ensured by calling \ref IGRAPH_FINALLY_FREE(), which deallocates + * some of the temporary memory to avoid memory leaks. Note that this may + * invalidate the error message buffer \p reason passed to the error handler. + * Do not access it after having called \ref IGRAPH_FINALLY_FREE(). * + * * - * If you want to write an error handler, your error handler should - * call \ref IGRAPH_FINALLY_FREE() to deallocate all temporary memory to - * prevent memory leaks. Note that this may invalidate the error message - * buffer \p reason passed to the error handler. Do not access it after - * having called \ref IGRAPH_FINALLY_FREE(). + * As of \a igraph 0.10, temporary memory is dellocated in stages, through + * multiple calls to the error handler (and indirectly to \ref IGRAPH_FINALLY_FREE()). + * Therefore, error handlers that do not abort the program + * immediately are expected to return. The error handler should not perform + * a longjmp, as this may lead to some of the memory not + * getting freed. * */ @@ -200,78 +224,6 @@ __BEGIN_DECLS *
*/ -/** - * \section error_handling_threads Error handling and threads - * - * - * It is likely that the \a igraph error handling - * method is \em not thread-safe, mainly because of - * the static global stack which is used to store the address of the - * temporarily allocated objects. This issue might be addressed in a - * later version of \a igraph. - * - */ - -/** - * \typedef igraph_error_handler_t - * \brief The type of error handler functions. - * - * This is the type of the error handler functions. - * \param reason Textual description of the error. - * \param file The source file in which the error is noticed. - * \param line The number of the line in the source file which triggered - * the error - * \param igraph_errno The \a igraph error code. - */ - -typedef void igraph_error_handler_t (const char *reason, const char *file, - int line, int igraph_errno); - -/** - * \var igraph_error_handler_abort - * \brief Abort program in case of error. - * - * The default error handler, prints an error message and aborts the - * program. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_abort; - -/** - * \var igraph_error_handler_ignore - * \brief Ignore errors. - * - * This error handler frees the temporarily allocated memory and returns - * with the error code. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_ignore; - -/** - * \var igraph_error_handler_printignore - * \brief Print and ignore errors. - * - * Frees temporarily allocated memory, prints an error message to the - * standard error and returns with the error code. - */ - -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; - -/** - * \function igraph_set_error_handler - * \brief Sets a new error handler. - * - * Installs a new error handler. If called with 0, it installs the - * default error handler (which is currently - * \ref igraph_error_handler_abort). - * \param new_handler The error handler function to install. - * \return The old error handler function. This should be saved and - * restored if \p new_handler is not needed any - * more. - */ - -IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler); - /** * \typedef igraph_error_type_t * \brief Error code type. @@ -290,9 +242,9 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand * number was specified as the number of vertices. * \enumval IGRAPH_EXISTS A graph/vertex/edge attribute is already * installed with the given name. - * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex ids. A vertex id + * \enumval IGRAPH_EINVEVECTOR Invalid vector of vertex IDs. A vertex ID * is either negative or bigger than the number of vertices minus one. - * \enumval IGRAPH_EINVVID Invalid vertex id, negative or too big. + * \enumval IGRAPH_EINVVID Invalid vertex ID, negative or too big. * \enumval IGRAPH_NONSQUARE A non-square matrix was received while a * square matrix was expected. * \enumval IGRAPH_EINVMODE Invalid mode parameter. @@ -340,12 +292,15 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand * \enumval IGRAPH_EATTRCOMBINE Unimplemented attribute combination * method for the given attribute type. * \enumval IGRAPH_ELAPACK A LAPACK call resulted in an error. - * \enumval IGRAPH_EDRL Internal error in the DrL layout generator. + * \enumval IGRAPH_EDRL Internal error in the DrL layout generator; not used + * any more (replaced by IGRAPH_EINTERNAL). * \enumval IGRAPH_EOVERFLOW Integer or double overflow. * \enumval IGRAPH_EGLP Internal GLPK error. * \enumval IGRAPH_CPUTIME CPU time exceeded. * \enumval IGRAPH_EUNDERFLOW Integer or double underflow. * \enumval IGRAPH_ERWSTUCK Random walk got stuck. + * \enumval IGRAPH_ERANGE Maximum vertex or edge count exceeded. + * \enumval IGRAPH_ENOSOL Input problem has no solution. */ typedef enum { @@ -402,21 +357,107 @@ typedef enum { IGRAPH_EATTRIBUTES = 51, IGRAPH_EATTRCOMBINE = 52, IGRAPH_ELAPACK = 53, - IGRAPH_EDRL = 54, + IGRAPH_EDRL IGRAPH_DEPRECATED_ENUMVAL = 54, IGRAPH_EOVERFLOW = 55, IGRAPH_EGLP = 56, IGRAPH_CPUTIME = 57, IGRAPH_EUNDERFLOW = 58, IGRAPH_ERWSTUCK = 59, - IGRAPH_STOP = 60 /* undocumented, used internally */ + IGRAPH_STOP = 60, + IGRAPH_ERANGE = 61, + IGRAPH_ENOSOL = 62 } igraph_error_type_t; /* Each enum value above must have a corresponding error string in - * igraph_i_error_strings[] in igraph_error.c + * igraph_i_error_strings[] in core/error.c * * Information on undocumented codes: * - IGRAPH_STOP signals a request to stop in functions like igraph_i_maximal_cliques_bk() */ +/** + * \section error_handling_threads Error handling and threads + * + * + * It is likely that the \a igraph error handling + * method is \em not thread-safe, mainly because of + * the static global stack which is used to store the address of the + * temporarily allocated objects. This issue might be addressed in a + * later version of \a igraph. + * + */ + +/** + * \typedef igraph_error_t + * \brief Return type for functions returning an error code. + * + * This type is used as the return type of igraph functions that return an + * error code. It is a type alias because \type igraph_error_t used to be + * an \c int, and was used slightly differenly than \type igraph_error_type_t. + */ +typedef igraph_error_type_t igraph_error_t; + +/** + * \typedef igraph_error_handler_t + * \brief The type of error handler functions. + * + * This is the type of the error handler functions. + * + * \param reason Textual description of the error. + * \param file The source file in which the error is noticed. + * \param line The number of the line in the source file which triggered + * the error + * \param igraph_errno The \a igraph error code. + */ + +typedef void igraph_error_handler_t (const char *reason, const char *file, + int line, igraph_error_t igraph_errno); + +/** + * \var igraph_error_handler_abort + * \brief Abort program in case of error. + * + * The default error handler, prints an error message and aborts the + * program. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_abort; + +/** + * \var igraph_error_handler_ignore + * \brief Ignore errors. + * + * This error handler frees the temporarily allocated memory and returns + * with the error code. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_ignore; + +/** + * \var igraph_error_handler_printignore + * \brief Print and ignore errors. + * + * Frees temporarily allocated memory, prints an error message to the + * standard error and returns with the error code. + */ + +IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_printignore; + +/** + * \function igraph_set_error_handler + * \brief Sets a new error handler. + * + * Installs a new error handler. If called with \c NULL, it installs the + * default error handler (which is currently \ref igraph_error_handler_abort). + * + * \param new_handler The error handler function to install. + * \return The old error handler function. This should be saved and + * restored if \p new_handler is not needed any + * more. + */ + +IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_handler_t* new_handler); + + /* We use IGRAPH_FILE_BASENAME instead of __FILE__ to ensure that full * paths don't leak into the library code. IGRAPH_FILE_BASENAME is set up * by the build system when compiling the individual files. However, when @@ -429,7 +470,7 @@ typedef enum { /** * \define IGRAPH_ERROR - * \brief Trigger an error. + * \brief Triggers an error. * * \a igraph functions usually use this macro when they notice an error. * It calls @@ -438,6 +479,7 @@ typedef enum { * code. If for some (suspicious) reason you want to call the error * handler without returning from the current function, call * \ref igraph_error() directly. + * * \param reason Textual description of the error. This should be * something more descriptive than the text associated with the error * code. E.g. if the error code is \c IGRAPH_EINVAL, @@ -460,7 +502,7 @@ typedef enum { /** * \function igraph_error - * \brief Triggers an error. + * \brief Reports an error. * * \a igraph functions usually call this function (most often via the * \ref IGRAPH_ERROR macro) if they notice an error. @@ -477,8 +519,8 @@ typedef enum { * \sa igraph_errorf(). */ -IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, - int igraph_errno); +IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, int line, + igraph_error_t igraph_errno); /** * \define IGRAPH_ERRORF @@ -491,6 +533,7 @@ IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, * error code. If for some (suspicious) reason you want to call the * error handler without returning from the current function, call * \ref igraph_errorf() directly. + * * \param reason Textual description of the error, a template string * with the same syntax as the standard printf C library function. * This should be something more descriptive than the text associated @@ -512,7 +555,7 @@ IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, /** * \function igraph_errorf - * \brief Triggers an error, printf-like version. + * \brief Reports an error, printf-like version. * * \param reason Textual description of the error, interpreted as * a \c printf format string. @@ -525,11 +568,12 @@ IGRAPH_EXPORT int igraph_error(const char *reason, const char *file, int line, * \sa igraph_error(). */ -IGRAPH_EXPORT int igraph_errorf(const char *reason, const char *file, int line, - int igraph_errno, ...); +IGRAPH_FUNCATTR_PRINTFLIKE(1,5) +IGRAPH_EXPORT igraph_error_t igraph_errorf(const char *reason, const char *file, int line, + igraph_error_t igraph_errno, ...); -IGRAPH_EXPORT int igraph_errorvf(const char *reason, const char *file, int line, - int igraph_errno, va_list ap); +IGRAPH_EXPORT igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, + igraph_error_t igraph_errno, va_list ap); /** * \function igraph_strerror @@ -542,7 +586,7 @@ IGRAPH_EXPORT int igraph_errorvf(const char *reason, const char *file, int line, * \return pointer to the textual description of the error code. */ -IGRAPH_EXPORT const char* igraph_strerror(const int igraph_errno); +IGRAPH_EXPORT const char* igraph_strerror(const igraph_error_t igraph_errno); #define IGRAPH_ERROR_SELECT_2(a,b) ((a) != IGRAPH_SUCCESS ? (a) : ((b) != IGRAPH_SUCCESS ? (b) : IGRAPH_SUCCESS)) #define IGRAPH_ERROR_SELECT_3(a,b,c) ((a) != IGRAPH_SUCCESS ? (a) : IGRAPH_ERROR_SELECT_2(b,c)) @@ -555,7 +599,7 @@ IGRAPH_EXPORT const char* igraph_strerror(const int igraph_errno); * information. We don't use the exception handling code though. */ struct igraph_i_protectedPtr { - int all; + int level; void *ptr; void (*func)(void*); }; @@ -569,8 +613,18 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); * \brief Signals clean deallocation of objects. * * Removes the specified number of objects from the stack of - * temporarily allocated objects. Most often this is called just - * before returning from a function. + * temporarily allocated objects. It is typically called + * immediately after manually destroying the objects: + * + * + * igraph_vector_t vector; + * igraph_vector_init(&vector, 10); + * IGRAPH_FINALLY(igraph_vector_destroy, &vector); + * // use vector + * igraph_vector_destroy(&vector); + * IGRAPH_FINALLY_CLEAN(1); + * + * * \param num The number of objects to remove from the bookkeeping * stack. */ @@ -579,11 +633,13 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); /** * \function IGRAPH_FINALLY_FREE - * \brief Deallocates all registered objects. + * \brief Deallocates objects registered at the current level. * - * Calls the destroy function for all objects in the stack of - * temporarily allocated objects. This is usually called only from an - * error handler. It is \em not appropriate to use it + * Calls the destroy function for all objects in the current level + * of the stack of temporarily allocated objects, i.e. up to the + * nearest mark set by IGRAPH_FINALLY_ENTER(). + * This function must only be called from an error handler. + * It is \em not appropriate to use it * instead of destroying each unneeded object of a function, as it * destroys the temporary objects of the caller function (and so on) * as well. @@ -591,6 +647,9 @@ IGRAPH_EXPORT void IGRAPH_FINALLY_CLEAN(int num); IGRAPH_EXPORT void IGRAPH_FINALLY_FREE(void); +IGRAPH_EXPORT void IGRAPH_FINALLY_ENTER(void); +IGRAPH_EXPORT void IGRAPH_FINALLY_EXIT(void); + /** * \function IGRAPH_FINALLY_STACK_SIZE * \brief The number of registered objects. @@ -620,14 +679,16 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); /** * \define IGRAPH_FINALLY * \brief Registers an object for deallocation. - * \param func The address of the function which is normally called to - * destroy the object. - * \param ptr Pointer to the object itself. * * This macro places the address of an object, together with the * address of its destructor in a stack. This stack is used if an * error happens to deallocate temporarily allocated objects to - * prevent memory leaks. + * prevent memory leaks. After manual deallocation, objects are removed + * from the stack using \ref IGRAPH_FINALLY_CLEAN(). + * + * \param func The function which is normally called to + * destroy the object. + * \param ptr Pointer to the object itself. */ #define IGRAPH_FINALLY(func, ptr) \ @@ -655,12 +716,12 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); #define IGRAPH_CHECK(a) \ do { \ int enter_stack_size = IGRAPH_FINALLY_STACK_SIZE(); \ - int igraph_i_ret=(a); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ + igraph_error_t igraph_i_ret=(a); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ IGRAPH_ERROR("", igraph_i_ret); \ } \ if (IGRAPH_UNLIKELY(enter_stack_size != IGRAPH_FINALLY_STACK_SIZE())) { \ - IGRAPH_ERROR("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN", IGRAPH_FAILURE); \ + IGRAPH_FATAL("Non-matching number of IGRAPH_FINALLY and IGRAPH_FINALLY_CLEAN."); \ } \ } while (0) #else @@ -668,7 +729,8 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * \define IGRAPH_CHECK * \brief Checks the return value of a function call. * - * \param a An expression, usually a function call. + * \param expr An expression, usually a function call. It is guaranteed to + * be evaluated only once. * * Executes the expression and checks its value. If this is not * \c IGRAPH_SUCCESS, it calls \ref IGRAPH_ERROR with @@ -686,14 +748,67 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * by using IGRAPH_CHECK on every \a igraph * call which can return an error code. */ -#define IGRAPH_CHECK(a) do { \ - int igraph_i_ret=(a); \ - if (IGRAPH_UNLIKELY(igraph_i_ret != 0)) {\ +#define IGRAPH_CHECK(expr) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) {\ IGRAPH_ERROR("", igraph_i_ret); \ - } } while (0) + } \ + } while (0) #endif +/** + * \define IGRAPH_CHECK_CALLBACK + * \brief Checks the return value of a callback. + * + * Identical to \ref IGRAPH_CHECK, but treats \c IGRAPH_STOP as a normal + * (non-erroneous) return code. This macro is used in some igraph functions + * that allow the user to hook into a long-running calculation with a callback + * function. When the user-defined callback function returns \c IGRAPH_SUCCESS, + * the calculation will proceed normally. Returning \c IGRAPH_STOP from the + * callback will terminate the calculation without reporting an error. Returning + * any other value from the callback is treated as an error code, and igraph + * will trigger the necessary cleanup functions before exiting the function. + * + *
+ * Note that \c IGRAPH_CHECK_CALLBACK does not handle \c IGRAPH_STOP by any + * means except returning it in the variable pointed to by \c code. It is the + * responsibility of the caller to handle \c IGRAPH_STOP accordingly. + * + * \param expr An expression, usually a call to a user-defined callback function. + * It is guaranteed to be evaluated only once. + * \param code Pointer to an optional variable of type igraph_error_t; + * the value of this variable will be set to the error code if it is not a null + * pointer. + */ +#define IGRAPH_CHECK_CALLBACK(expr, code) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + if (code) { \ + *(code) = igraph_i_ret; \ + } \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS && igraph_i_ret != IGRAPH_STOP)) { \ + IGRAPH_ERROR("", igraph_i_ret); \ + } \ + } while (0) +/** + * \define IGRAPH_CHECK_OOM + * \brief Checks for out-of-memory conditions after a memory allocation. + * + * This function should be called on pointers after memory allocations. The + * function checks whether the returned pointer is NULL, and if so, sets an + * error message with the \c IGRAPH_ENOMEM error code. + * + * \param ptr The pointer to check. + * \param message The error message to use when the pointer is \c NULL. + */ +#define IGRAPH_CHECK_OOM(ptr, message) \ + do { \ + if (IGRAPH_UNLIKELY(!ptr)) { \ + IGRAPH_ERROR(message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + } while (0) /** * \section about_igraph_warnings Warning messages @@ -730,13 +845,14 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); * argument is not used. */ -typedef igraph_error_handler_t igraph_warning_handler_t; +typedef void igraph_warning_handler_t (const char *reason, const char *file, int line); /** * \function igraph_set_warning_handler * \brief Installs a warning handler. * * Install the supplied warning handler function. + * * \param new_handler The new warning handler function to install. * Supply a null pointer here to uninstall the current * warning handler, without installing a new one. @@ -750,10 +866,11 @@ IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; /** * \function igraph_warning - * \brief Triggers a warning. + * \brief Reports a warning. * * Call this function if you want to trigger a warning from within * a function that uses \a igraph. + * * \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 @@ -763,8 +880,7 @@ IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; * \return The supplied error code. */ -IGRAPH_EXPORT int igraph_warning(const char *reason, const char *file, int line, - int igraph_errno); +IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line); /** * \define IGRAPH_WARNINGF @@ -783,18 +899,19 @@ IGRAPH_EXPORT int igraph_warning(const char *reason, const char *file, int line, #define IGRAPH_WARNINGF(reason, ...) \ do { \ igraph_warningf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ - -1, __VA_ARGS__); \ + __VA_ARGS__); \ } while (0) /** * \function igraph_warningf - * \brief Triggers a warning, printf-like version. + * \brief Reports a warning, printf-like version. * * This function is similar to \ref igraph_warning(), but * uses a printf-like syntax. It substitutes the additional arguments * into the \p reason template string and calls \ref igraph_warning(). + * * \param reason Textual description of the warning, a template string * with the same syntax as the standard printf C library function. * \param file The source file in which the warning was noticed. @@ -804,11 +921,10 @@ IGRAPH_EXPORT int igraph_warning(const char *reason, const char *file, int line, * but this is currently not used in igraph. * \param ... The additional arguments to be substituted into the * template string. - * \return The supplied error code. */ -IGRAPH_EXPORT int igraph_warningf(const char *reason, const char *file, int line, - int igraph_errno, ...); +IGRAPH_FUNCATTR_PRINTFLIKE(1,4) +IGRAPH_EXPORT void igraph_warningf(const char *reason, const char *file, int line, ...); /** * \define IGRAPH_WARNING @@ -821,7 +937,7 @@ IGRAPH_EXPORT int igraph_warningf(const char *reason, const char *file, int line #define IGRAPH_WARNING(reason) \ do { \ - igraph_warning(reason, IGRAPH_FILE_BASENAME, __LINE__, -1); \ + igraph_warning(reason, IGRAPH_FILE_BASENAME, __LINE__); \ } while (0) @@ -861,7 +977,7 @@ IGRAPH_EXPORT int igraph_warningf(const char *reason, const char *file, int line * * \param reason Textual description of the error. * \param file The source file in which the error is noticed. - * \param line The number of the line in the source file which triggered the error + * \param line The number of the line in the source file which triggered the error. */ typedef void igraph_fatal_handler_t (const char *reason, const char *file, int line); @@ -905,7 +1021,7 @@ IGRAPH_EXPORT igraph_fatal_handler_t igraph_fatal_handler_abort; * \param line The number of line in the source file which triggered the error. */ -IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatal(const char *reason, const char *file, int line); +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatal(const char *reason, const char *file, int line); /** * \function igraph_fatalf @@ -921,7 +1037,8 @@ IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatal(const char *reason, const char * * \param ... The additional arguments to be substituted into the template string. */ -IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char *file, int line, ...); +IGRAPH_FUNCATTR_PRINTFLIKE(1,4) +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatalf(const char *reason, const char *file, int line, ...); /** * \define IGRAPH_FATALF @@ -930,6 +1047,7 @@ IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char * \a igraph functions can use this macro when a fatal error occurs and * want to pass on extra information to the user about what went wrong. * It calls \ref igraph_fatalf() with the proper parameters. + * * \param reason Textual description of the error, a template string * with the same syntax as the standard printf C library function. * \param ... The additional arguments to be substituted into the @@ -978,7 +1096,7 @@ IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char * This macro is meant for internal use by \a igraph. * * - * Since a typial fatal error handler does a longjmp(), avoid using this + * Since a typical fatal error handler does a longjmp(), avoid using this * macro in C++ code. With most compilers, destructor will not be called when * longjmp() leaves the current scope. * @@ -987,7 +1105,7 @@ IGRAPH_EXPORT IGRAPH_NORETURN void igraph_fatalf(const char *reason, const char #define IGRAPH_ASSERT(condition) \ do { \ - if (!(condition)) { \ + if (IGRAPH_UNLIKELY(!(condition))) { \ igraph_fatal("Assertion failed: " #condition, IGRAPH_FILE_BASENAME, __LINE__); \ } \ } while (0) diff --git a/src/vendor/cigraph/include/igraph_eulerian.h b/src/vendor/cigraph/include/igraph_eulerian.h index 79f669fd81f..bb9a089a7b4 100644 --- a/src/vendor/cigraph/include/igraph_eulerian.h +++ b/src/vendor/cigraph/include/igraph_eulerian.h @@ -26,12 +26,13 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); -IGRAPH_EXPORT int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); -IGRAPH_EXPORT int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res); +IGRAPH_EXPORT igraph_error_t igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bool_t *has_cycle); +IGRAPH_EXPORT igraph_error_t igraph_eulerian_path(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); +IGRAPH_EXPORT igraph_error_t igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_flow.h b/src/vendor/cigraph/include/igraph_flow.h index 37ee4179b64..354f29f7e7b 100644 --- a/src/vendor/cigraph/include/igraph_flow.h +++ b/src/vendor/cigraph/include/igraph_flow.h @@ -26,9 +26,10 @@ #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS @@ -38,8 +39,7 @@ __BEGIN_DECLS /** * \typedef igraph_maxflow_stats_t - * A simple data type to return some statistics from the - * push-relabel maximum flow solver. + * \brief Data structure holding statistics from the push-relabel maximum flow solver. * * \param nopush The number of push operations performed. * \param norelabel The number of relabel operarions performed. @@ -54,101 +54,101 @@ __BEGIN_DECLS */ typedef struct { - int nopush, norelabel, nogap, nogapnodes, nobfs; + igraph_integer_t nopush, norelabel, nogap, nogapnodes, nobfs; } igraph_maxflow_stats_t; -IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *flow, igraph_vector_int_t *cut, + igraph_vector_int_t *partition, igraph_vector_int_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity, igraph_maxflow_stats_t *stats); -IGRAPH_EXPORT int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_t *cut, igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *res, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_mincut(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_t *partition, - igraph_vector_t *partition2, - igraph_vector_t *cut, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_st_vertex_connectivity(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); -IGRAPH_EXPORT int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks); /* s-t cut listing related stuff */ -IGRAPH_EXPORT int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, +IGRAPH_EXPORT igraph_error_t igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_residual_graph(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_reverse_residual_graph(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_reverse_residual_graph(const igraph_t *graph, const igraph_vector_t *capacity, igraph_t *residual, const igraph_vector_t *flow); -IGRAPH_EXPORT int igraph_dominator_tree(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, - igraph_vector_t *dom, + igraph_vector_int_t *dom, igraph_t *domtree, - igraph_vector_t *leftout, + igraph_vector_int_t *leftout, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_all_st_cuts(const igraph_t *graph, - igraph_vector_ptr_t *cuts, - igraph_vector_ptr_t *partition1s, +IGRAPH_EXPORT igraph_error_t igraph_all_st_cuts(const igraph_t *graph, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, igraph_integer_t source, igraph_integer_t target); -IGRAPH_EXPORT int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, - igraph_vector_ptr_t *cuts, - igraph_vector_ptr_t *partition1s, +IGRAPH_EXPORT igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_gomory_hu_tree(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, igraph_vector_t *flows, const igraph_vector_t *capacity); diff --git a/src/vendor/cigraph/include/igraph_foreign.h b/src/vendor/cigraph/include/igraph_foreign.h index e4a94422277..a0625cba62b 100644 --- a/src/vendor/cigraph/include/igraph_foreign.h +++ b/src/vendor/cigraph/include/igraph_foreign.h @@ -27,6 +27,7 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_strvector.h" @@ -38,48 +39,75 @@ __BEGIN_DECLS /* Read and write foreign formats */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_bool_t names, igraph_add_weights_t weights, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_read_graph_pajek(igraph_t *graph, FILE *instream); -IGRAPH_EXPORT int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, - int index); -IGRAPH_EXPORT int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, + igraph_integer_t index); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, igraph_strvector_t *problem, - igraph_vector_t *label, + igraph_vector_int_t *label, igraph_integer_t *source, igraph_integer_t *target, igraph_vector_t *capacity, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t igraph_read_graph_dimacs_flow(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_read_graph_gml(igraph_t *graph, FILE *instream); -IGRAPH_EXPORT int igraph_read_graph_dl(igraph_t *graph, FILE *instream, +IGRAPH_EXPORT igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream); +IGRAPH_EXPORT igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, +typedef unsigned int igraph_write_gml_sw_t; + +enum { + IGRAPH_WRITE_GML_DEFAULT_SW = 0x0, /* default settings */ + IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW = 0x1 /* only encode " characters, nothing else */ +}; + +IGRAPH_EXPORT igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, const char *names, const char *weights); -IGRAPH_EXPORT int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT igraph_error_t igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, const char *names, const char *weights, igraph_bool_t isolates); -IGRAPH_EXPORT int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT igraph_error_t igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, igraph_bool_t prefixattr); -IGRAPH_EXPORT int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, - long int source, long int target, +IGRAPH_EXPORT igraph_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity); -IGRAPH_EXPORT int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, - const igraph_vector_t *id, const char *creator); -IGRAPH_EXPORT int igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); -IGRAPH_EXPORT int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, +IGRAPH_EXPORT igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + igraph_write_gml_sw_t options, + const igraph_vector_t *id, const char *creator); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE *outstream); +IGRAPH_EXPORT igraph_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, const char* vertex_attr_name, const char* edge_attr_name); +/* -------------------------------------------------- */ +/* Convenience functions for temporary locale setting */ +/* -------------------------------------------------- */ + +typedef struct igraph_safelocale_s *igraph_safelocale_t; + +IGRAPH_EXPORT igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc); +IGRAPH_EXPORT void igraph_exit_safelocale(igraph_safelocale_t *loc); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_games.h b/src/vendor/cigraph/include/igraph_games.h index df5f6a91edc..e9384fd59ca 100644 --- a/src/vendor/cigraph/include/igraph_games.h +++ b/src/vendor/cigraph/include/igraph_games.h @@ -26,11 +26,13 @@ #include "igraph_decls.h" #include "igraph_constants.h" -#include "igraph_types.h" +#include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_matrix.h" +#include "igraph_matrix_list.h" +#include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_datatype.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS @@ -38,31 +40,28 @@ __BEGIN_DECLS /* Constructors, games (=stochastic) */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t A, igraph_bool_t directed, igraph_barabasi_algorithm_t algo, const igraph_t *start_from); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p, +IGRAPH_EXPORT igraph_error_t igraph_erdos_renyi_game_gnp(igraph_t *graph, igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_real_t m, +IGRAPH_EXPORT igraph_error_t igraph_erdos_renyi_game_gnm(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_t *out_deg, - const igraph_vector_t *in_deg, +IGRAPH_EXPORT igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, + const igraph_vector_int_t *in_deg, igraph_degseq_t method); -IGRAPH_EXPORT int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t citation); -IGRAPH_EXPORT int igraph_barabasi_aging_game(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -72,18 +71,18 @@ IGRAPH_EXPORT int igraph_barabasi_aging_game(igraph_t *graph, igraph_real_t deg_coef, igraph_real_t age_coef, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_recent_degree_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t window, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t zero_appeal, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_recent_degree_aging_game(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -91,133 +90,140 @@ IGRAPH_EXPORT int igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t window, igraph_real_t zero_appeal, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, + igraph_vector_int_t *node_type_vec); +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, + igraph_vector_int_t *node_type_vec); +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *node_type_vec, igraph_bool_t directed, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *node_type_out_vec, + igraph_vector_int_t *node_type_in_vec, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, +IGRAPH_EXPORT igraph_error_t igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_bool_t multiple); -IGRAPH_EXPORT int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, +IGRAPH_EXPORT igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_lastcit_game(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_t *types, +IGRAPH_EXPORT igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, const igraph_vector_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_t *types, +IGRAPH_EXPORT igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, const igraph_matrix_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, +IGRAPH_EXPORT igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, igraph_real_t fw_prob, igraph_real_t bw_factor, igraph_integer_t ambs, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_simple_interconnected_islands_game( +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_static_power_law_game(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_k_regular_game(igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t 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, + const igraph_vector_list_t *rholist, + const igraph_matrix_list_t *Clist, igraph_real_t p); -IGRAPH_EXPORT int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, +IGRAPH_EXPORT igraph_error_t 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); + const igraph_vector_int_t *permutation); -IGRAPH_EXPORT int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t 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); + const igraph_vector_int_t *permutation); -IGRAPH_EXPORT int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +IGRAPH_EXPORT igraph_error_t igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method); -IGRAPH_EXPORT int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, +IGRAPH_EXPORT igraph_error_t igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, +IGRAPH_EXPORT igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, igraph_matrix_t *res); +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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 +); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_graph_list.h b/src/vendor/cigraph/include/igraph_graph_list.h new file mode 100644 index 00000000000..7f8232621a0 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_graph_list.h @@ -0,0 +1,60 @@ +/* -*- 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_GRAPH_LIST_H +#define IGRAPH_GRAPH_LIST_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* List of graphs */ +/* -------------------------------------------------- */ + +#define GRAPH_LIST +#define BASE_GRAPH +#define EXTRA_TYPE_FIELDS igraph_bool_t directed; +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef EXTRA_TYPE_FIELDS +#undef BASE_GRAPH +#undef GRAPH_LIST + +void igraph_graph_list_set_directed(igraph_graph_list_t* list, igraph_bool_t directed); + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_GRAPH_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_graph_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_graph_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_graphicality.h b/src/vendor/cigraph/include/igraph_graphicality.h index 02feabfbeaa..f639217b5b1 100644 --- a/src/vendor/cigraph/include/igraph_graphicality.h +++ b/src/vendor/cigraph/include/igraph_graphicality.h @@ -22,11 +22,12 @@ #define IGRAPH_GRAPHICALITY_H #include "igraph_decls.h" -#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_vector.h" __BEGIN_DECLS -typedef unsigned char igraph_edge_type_sw_t; +typedef unsigned int igraph_edge_type_sw_t; /* * bit 0: self-loops alowed? @@ -39,27 +40,16 @@ enum { IGRAPH_MULTI_SW = 0x06 /* 110 */ }; -IGRAPH_EXPORT int igraph_is_graphical(const igraph_vector_t *out_degrees, - const igraph_vector_t *in_degrees, +IGRAPH_EXPORT igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, + const igraph_vector_int_t *in_degrees, const igraph_edge_type_sw_t allowed_edge_types, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_is_bigraphical(const igraph_vector_t *degrees1, - const igraph_vector_t *degrees2, +IGRAPH_EXPORT igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, + const igraph_vector_int_t *degrees2, const igraph_edge_type_sw_t allowed_edge_types, igraph_bool_t *res); - -/* Legacy functions (deprecated): */ - -IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_degree_sequence(const igraph_vector_t *out_degrees, - const igraph_vector_t *in_degrees, - igraph_bool_t *res); - -IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_is_graphical_degree_sequence(const igraph_vector_t *out_degrees, - const igraph_vector_t *in_degrees, - igraph_bool_t *res); - __END_DECLS #endif // IGRAPH_GRAPHICALITY_H diff --git a/src/vendor/cigraph/include/igraph_graphlets.h b/src/vendor/cigraph/include/igraph_graphlets.h index 6b5285362c7..b80165a6966 100644 --- a/src/vendor/cigraph/include/igraph_graphlets.h +++ b/src/vendor/cigraph/include/igraph_graphlets.h @@ -25,27 +25,28 @@ #define IGRAPH_GRAPHLETS_H #include "igraph_decls.h" + #include "igraph_datatype.h" -#include "igraph_vector_ptr.h" -#include "igraph_interface.h" +#include "igraph_error.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_graphlets_candidate_basis(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_ptr_t *cliques, + igraph_vector_int_list_t *cliques, igraph_vector_t *thresholds); -IGRAPH_EXPORT int igraph_graphlets_project(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, - const igraph_vector_ptr_t *cliques, + const igraph_vector_int_list_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, - int niter); + igraph_integer_t niter); -IGRAPH_EXPORT int igraph_graphlets(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_ptr_t *cliques, - igraph_vector_t *Mu, int niter); + igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_integer_t niter); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_heap.h b/src/vendor/cigraph/include/igraph_heap.h index 37292260a3b..3eb81c2f972 100644 --- a/src/vendor/cigraph/include/igraph_heap.h +++ b/src/vendor/cigraph/include/igraph_heap.h @@ -25,6 +25,8 @@ #define IGRAPH_HEAP_H #include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" __BEGIN_DECLS @@ -50,7 +52,7 @@ __BEGIN_DECLS #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL -#define BASE_LONG +#define BASE_INT #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "igraph_heap_pmt.h" @@ -61,7 +63,7 @@ __BEGIN_DECLS #include "igraph_heap_pmt.h" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN -#undef BASE_LONG +#undef BASE_INT #define BASE_CHAR #define HEAP_TYPE_MAX diff --git a/src/vendor/cigraph/include/igraph_heap_pmt.h b/src/vendor/cigraph/include/igraph_heap_pmt.h index 437a1996521..f4b12bb6344 100644 --- a/src/vendor/cigraph/include/igraph_heap_pmt.h +++ b/src/vendor/cigraph/include/igraph_heap_pmt.h @@ -25,15 +25,16 @@ typedef struct TYPE(igraph_heap) { BASE* stor_begin; BASE* stor_end; BASE* end; - int destroy; + igraph_bool_t destroy; } TYPE(igraph_heap); -IGRAPH_EXPORT int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int size); -IGRAPH_EXPORT int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, BASE* data, long int len); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *t, const BASE *data, igraph_integer_t len); IGRAPH_EXPORT void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); -IGRAPH_EXPORT BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_heap, empty)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem); +IGRAPH_EXPORT BASE FUNCTION(igraph_heap, top)(const TYPE(igraph_heap)* h); IGRAPH_EXPORT BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h); -IGRAPH_EXPORT int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity); diff --git a/src/vendor/cigraph/include/igraph_hrg.h b/src/vendor/cigraph/include/igraph_hrg.h index e81efb24696..6ceeb4cac24 100644 --- a/src/vendor/cigraph/include/igraph_hrg.h +++ b/src/vendor/cigraph/include/igraph_hrg.h @@ -25,15 +25,17 @@ #define IGRAPH_HRG_H #include "igraph_decls.h" -#include "igraph_vector.h" -#include "igraph_vector_ptr.h" + #include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_graph_list.h" +#include "igraph_vector.h" __BEGIN_DECLS /** * \struct igraph_hrg_t - * Data structure to store a hierarchical random graph + * \brief Data structure to store a hierarchical random graph. * * A hierarchical random graph (HRG) can be given as a binary tree, * where the internal vertices are labeled with real numbers. @@ -45,6 +47,7 @@ __BEGIN_DECLS * * * It has the following members: + * * \member left Vector that contains the left children of the internal * tree vertices. The first vertex is always the root vertex, so * the first element of the vector is the left child of the root @@ -65,51 +68,65 @@ __BEGIN_DECLS */ typedef struct igraph_hrg_t { - igraph_vector_t left, right, prob, edges, vertices; + igraph_vector_int_t left; + igraph_vector_int_t right; + igraph_vector_t prob; + igraph_vector_int_t vertices; + igraph_vector_int_t edges; } igraph_hrg_t; -IGRAPH_EXPORT int igraph_hrg_init(igraph_hrg_t *hrg, int n); +IGRAPH_EXPORT igraph_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n); IGRAPH_EXPORT void igraph_hrg_destroy(igraph_hrg_t *hrg); -IGRAPH_EXPORT int igraph_hrg_size(const igraph_hrg_t *hrg); -IGRAPH_EXPORT int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize); +IGRAPH_EXPORT igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg); +IGRAPH_EXPORT igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize); -IGRAPH_EXPORT int igraph_hrg_fit(const igraph_t *graph, - igraph_hrg_t *hrg, - igraph_bool_t start, - int steps); +IGRAPH_EXPORT igraph_error_t igraph_hrg_fit( + const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, + igraph_integer_t steps +); -IGRAPH_EXPORT int igraph_hrg_sample(const igraph_t *graph, - igraph_t *sample, - igraph_vector_ptr_t *samples, - igraph_integer_t no_samples, - igraph_hrg_t *hrg, - igraph_bool_t start); +IGRAPH_EXPORT igraph_error_t igraph_hrg_sample( + const igraph_hrg_t *hrg, igraph_t *sample +); -IGRAPH_EXPORT int igraph_hrg_game(igraph_t *graph, - const igraph_hrg_t *hrg); +IGRAPH_EXPORT igraph_error_t igraph_hrg_sample_many( + const igraph_hrg_t *hrg, igraph_graph_list_t *samples, + igraph_integer_t num_samples +); -IGRAPH_EXPORT int igraph_hrg_dendrogram(igraph_t *graph, - const igraph_hrg_t *hrg); +IGRAPH_EXPORT igraph_error_t igraph_hrg_game( + igraph_t *graph, const igraph_hrg_t *hrg +); -IGRAPH_EXPORT int igraph_hrg_consensus(const igraph_t *graph, - igraph_vector_t *parents, +IGRAPH_EXPORT igraph_error_t igraph_from_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg, igraph_vector_t *prob +); + +IGRAPH_EXPORT igraph_error_t igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_int_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, - int num_samples); + igraph_integer_t num_samples); -IGRAPH_EXPORT int igraph_hrg_predict(const igraph_t *graph, - igraph_vector_t *edges, +IGRAPH_EXPORT igraph_error_t igraph_hrg_predict(const igraph_t *graph, + igraph_vector_int_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, - int num_samples, - int num_bins); + igraph_integer_t num_samples, + igraph_integer_t num_bins); -IGRAPH_EXPORT int igraph_hrg_create(igraph_hrg_t *hrg, +IGRAPH_EXPORT igraph_error_t igraph_hrg_create(igraph_hrg_t *hrg, const igraph_t *graph, const igraph_vector_t *prob); +/* Deprecated functions: */ + +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t igraph_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg +); + __END_DECLS #endif /* IGRAPH_HRG_H */ diff --git a/src/vendor/cigraph/include/igraph_interface.h b/src/vendor/cigraph/include/igraph_interface.h index d8ec5689020..ccab1b67f5f 100644 --- a/src/vendor/cigraph/include/igraph_interface.h +++ b/src/vendor/cigraph/include/igraph_interface.h @@ -27,6 +27,7 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS @@ -35,45 +36,58 @@ __BEGIN_DECLS /* Interface */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); +IGRAPH_EXPORT igraph_error_t igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr); IGRAPH_EXPORT void igraph_destroy(igraph_t *graph); -IGRAPH_EXPORT int igraph_copy(igraph_t *to, const igraph_t *from); -IGRAPH_EXPORT int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, +IGRAPH_EXPORT igraph_error_t igraph_copy(igraph_t *to, const igraph_t *from); +IGRAPH_EXPORT igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, void *attr); -IGRAPH_EXPORT int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, +IGRAPH_EXPORT igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); -IGRAPH_EXPORT int igraph_delete_edges(igraph_t *graph, igraph_es_t edges); -IGRAPH_EXPORT int igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); -IGRAPH_EXPORT int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, - igraph_vector_t *idx, - igraph_vector_t *invidx); +IGRAPH_EXPORT igraph_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges); +IGRAPH_EXPORT igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices); +IGRAPH_EXPORT igraph_error_t igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, + igraph_vector_int_t *idx, + igraph_vector_int_t *invidx); IGRAPH_EXPORT igraph_integer_t igraph_vcount(const igraph_t *graph); IGRAPH_EXPORT igraph_integer_t igraph_ecount(const igraph_t *graph); -IGRAPH_EXPORT int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t vid, +IGRAPH_EXPORT igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_t *neis, igraph_integer_t vid, igraph_neimode_t mode); IGRAPH_EXPORT igraph_bool_t igraph_is_directed(const igraph_t *graph); -IGRAPH_EXPORT int igraph_degree(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, + igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, const igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_edge(const igraph_t *graph, igraph_integer_t eid, +IGRAPH_EXPORT igraph_error_t igraph_edge(const igraph_t *graph, igraph_integer_t eid, igraph_integer_t *from, igraph_integer_t *to); -IGRAPH_EXPORT int igraph_edges(const igraph_t *graph, igraph_es_t eids, - igraph_vector_t *edges); -IGRAPH_EXPORT int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, +IGRAPH_EXPORT igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, + igraph_vector_int_t *edges); +IGRAPH_EXPORT igraph_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed, igraph_bool_t error); -IGRAPH_EXPORT int igraph_get_eids(const igraph_t *graph, igraph_vector_t *eids, - const igraph_vector_t *pairs, - const igraph_vector_t *path, +IGRAPH_EXPORT igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, + const igraph_vector_int_t *pairs, igraph_bool_t directed, igraph_bool_t error); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t vid, +IGRAPH_EXPORT igraph_error_t igraph_get_all_eids_between(const igraph_t *graph, igraph_vector_int_t *eids, + igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *igraph2, igraph_bool_t *res); + +IGRAPH_EXPORT igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT void igraph_i_property_cache_set_bool(const igraph_t *cache, igraph_cached_property_t prop, igraph_bool_t value); +IGRAPH_EXPORT void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop); +IGRAPH_EXPORT void igraph_i_property_cache_invalidate_all(const igraph_t *graph); + +#define IGRAPH_RETURN_IF_CACHED_BOOL(graphptr, prop, resptr) \ + do { \ + if (igraph_i_property_cache_has((graphptr), (prop))) { \ + *(resptr) = igraph_i_property_cache_get_bool((graphptr), (prop)); \ + return IGRAPH_SUCCESS; \ + } \ + } while (0) /** * \define IGRAPH_FROM @@ -86,7 +100,7 @@ IGRAPH_EXPORT int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *i * \return The source vertex of the edge. * \sa \ref igraph_edge() if error checking is desired. */ -#define IGRAPH_FROM(graph,eid) ((igraph_integer_t)(VECTOR((graph)->from)[(long int)(eid)])) +#define IGRAPH_FROM(graph,eid) ((igraph_integer_t)(VECTOR((graph)->from)[(igraph_integer_t)(eid)])) /** * \define IGRAPH_TO @@ -99,7 +113,7 @@ IGRAPH_EXPORT int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *i * \return The target vertex of the edge. * \sa \ref igraph_edge() if error checking is desired. */ -#define IGRAPH_TO(graph,eid) ((igraph_integer_t)(VECTOR((graph)->to) [(long int)(eid)])) +#define IGRAPH_TO(graph,eid) ((igraph_integer_t)(VECTOR((graph)->to) [(igraph_integer_t)(eid)])) /** * \define IGRAPH_OTHER diff --git a/src/vendor/cigraph/include/igraph_interrupt.h b/src/vendor/cigraph/include/igraph_interrupt.h index 1c32b69ba56..6d8fb85ca8a 100644 --- a/src/vendor/cigraph/include/igraph_interrupt.h +++ b/src/vendor/cigraph/include/igraph_interrupt.h @@ -106,7 +106,7 @@ __BEGIN_DECLS * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ -typedef int igraph_interruption_handler_t (void* data); +typedef igraph_error_t igraph_interruption_handler_t (void* data); /** * \function igraph_allow_interruption @@ -119,7 +119,7 @@ typedef int igraph_interruption_handler_t (void* data); * \return \c IGRAPH_SUCCESS if the calculation should go on, anything else otherwise. */ -IGRAPH_EXPORT int igraph_allow_interruption(void* data); +IGRAPH_EXPORT igraph_error_t igraph_allow_interruption(void* data); IGRAPH_EXPORT igraph_interruption_handler_t * igraph_set_interruption_handler (igraph_interruption_handler_t * new_handler); diff --git a/src/vendor/cigraph/include/igraph_iterators.h b/src/vendor/cigraph/include/igraph_iterators.h index 86d604f7b89..ec7589e010b 100644 --- a/src/vendor/cigraph/include/igraph_iterators.h +++ b/src/vendor/cigraph/include/igraph_iterators.h @@ -24,8 +24,12 @@ #ifndef IGRAPH_ITERATORS_H #define IGRAPH_ITERATORS_H +#include "igraph_datatype.h" #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" __BEGIN_DECLS @@ -33,84 +37,91 @@ __BEGIN_DECLS /* Vertex selectors */ /* -------------------------------------------------- */ -#define IGRAPH_VS_ALL 0 -#define IGRAPH_VS_ADJ 1 -#define IGRAPH_VS_NONE 2 -#define IGRAPH_VS_1 3 -#define IGRAPH_VS_VECTORPTR 4 -#define IGRAPH_VS_VECTOR 5 -#define IGRAPH_VS_SEQ 6 -#define IGRAPH_VS_NONADJ 7 +typedef enum { + IGRAPH_VS_ALL, + IGRAPH_VS_ADJ, + IGRAPH_VS_NONE, + IGRAPH_VS_1, + IGRAPH_VS_VECTORPTR, + IGRAPH_VS_VECTOR, + IGRAPH_VS_RANGE, + IGRAPH_VS_NONADJ, +} igraph_vs_type_t; typedef struct igraph_vs_t { - int type; + igraph_vs_type_t type; union { igraph_integer_t vid; /* single vertex */ - const igraph_vector_t *vecptr; /* vector of vertices */ + const igraph_vector_int_t *vecptr; /* vector of vertices */ struct { igraph_integer_t vid; igraph_neimode_t mode; - } adj; /* adjacent vertices */ + } adj; /* adjacent vertices */ struct { - igraph_integer_t from; /* first index */ - igraph_integer_t to; /* last index */ - } seq; /* sequence of vertices from:to */ + igraph_integer_t start; /* first index (inclusive) */ + igraph_integer_t end; /* last index (exclusive) */ + } range; /* range of vertices */ } data; } igraph_vs_t; -IGRAPH_EXPORT int igraph_vs_all(igraph_vs_t *vs); +IGRAPH_EXPORT igraph_error_t igraph_vs_all(igraph_vs_t *vs); IGRAPH_EXPORT igraph_vs_t igraph_vss_all(void); -IGRAPH_EXPORT int igraph_vs_adj(igraph_vs_t *vs, +IGRAPH_EXPORT igraph_error_t igraph_vs_adj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, +IGRAPH_EXPORT igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_vs_none(igraph_vs_t *vs); +IGRAPH_EXPORT igraph_error_t igraph_vs_none(igraph_vs_t *vs); IGRAPH_EXPORT igraph_vs_t igraph_vss_none(void); -IGRAPH_EXPORT int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); +IGRAPH_EXPORT igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid); IGRAPH_EXPORT igraph_vs_t igraph_vss_1(igraph_integer_t vid); -IGRAPH_EXPORT int igraph_vs_vector(igraph_vs_t *vs, - const igraph_vector_t *v); -IGRAPH_EXPORT igraph_vs_t igraph_vss_vector(const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v); -IGRAPH_EXPORT int igraph_vs_vector_small(igraph_vs_t *vs, ...); +IGRAPH_EXPORT igraph_error_t igraph_vs_vector_small(igraph_vs_t *vs, ...); -IGRAPH_EXPORT int igraph_vs_vector_copy(igraph_vs_t *vs, - const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, + const igraph_vector_int_t *v); -IGRAPH_EXPORT int igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to); + +IGRAPH_EXPORT igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end); IGRAPH_EXPORT void igraph_vs_destroy(igraph_vs_t *vs); IGRAPH_EXPORT igraph_bool_t igraph_vs_is_all(const igraph_vs_t *vs); -IGRAPH_EXPORT int igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); +IGRAPH_EXPORT igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src); -IGRAPH_EXPORT int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, - igraph_vector_t *v); -IGRAPH_EXPORT int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, +IGRAPH_EXPORT igraph_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result); -IGRAPH_EXPORT int igraph_vs_type(const igraph_vs_t *vs); +IGRAPH_EXPORT igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs); /* -------------------------------------------------- */ /* Vertex iterators */ /* -------------------------------------------------- */ -#define IGRAPH_VIT_SEQ 0 -#define IGRAPH_VIT_VECTOR 1 -#define IGRAPH_VIT_VECTORPTR 2 +typedef enum { + IGRAPH_VIT_RANGE, + IGRAPH_VIT_VECTOR, + IGRAPH_VIT_VECTORPTR, +} igraph_vit_type_t; typedef struct igraph_vit_t { - int type; - long int pos; - long int start; /* first index */ - long int end; /* one past last index */ - const igraph_vector_t *vec; + igraph_vit_type_t type; + igraph_integer_t pos; + igraph_integer_t start; /* first index */ + igraph_integer_t end; /* one past last index */ + const igraph_vector_int_t *vec; } igraph_vit_t; /** @@ -136,7 +147,7 @@ typedef struct igraph_vit_t { * igraph_vs_adj(&vs, 0, IGRAPH_ALL); * igraph_vit_create(&graph, vs, &vit); * while (!IGRAPH_VIT_END(vit)) { - * printf(" %li", (long int) IGRAPH_VIT_GET(vit)); + * printf(" %" IGRAPH_PRId, IGRAPH_VIT_GET(vit)); * IGRAPH_VIT_NEXT(vit); * } * printf("\n"); @@ -196,124 +207,136 @@ typedef struct igraph_vit_t { * \define IGRAPH_VIT_GET * \brief Query the current position. * - * Gives the vertex id of the current vertex pointed to by the + * Gives the vertex ID of the current vertex pointed to by the * iterator. * \param vit The vertex iterator. - * \return The vertex id of the current vertex. + * \return The vertex ID of the current vertex. * * Time complexity: O(1). */ #define IGRAPH_VIT_GET(vit) \ - ((igraph_integer_t)(((vit).type == IGRAPH_VIT_SEQ) ? (vit).pos : \ + ((igraph_integer_t)(((vit).type == IGRAPH_VIT_RANGE) ? (vit).pos : \ VECTOR(*(vit).vec)[(vit).pos])) -IGRAPH_EXPORT int igraph_vit_create(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit); IGRAPH_EXPORT void igraph_vit_destroy(const igraph_vit_t *vit); -IGRAPH_EXPORT int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v); /* -------------------------------------------------- */ /* Edge Selectors */ /* -------------------------------------------------- */ -#define IGRAPH_ES_ALL 0 -#define IGRAPH_ES_ALLFROM 1 -#define IGRAPH_ES_ALLTO 2 -#define IGRAPH_ES_INCIDENT 3 -#define IGRAPH_ES_NONE 4 -#define IGRAPH_ES_1 5 -#define IGRAPH_ES_VECTORPTR 6 -#define IGRAPH_ES_VECTOR 7 -#define IGRAPH_ES_SEQ 8 -#define IGRAPH_ES_PAIRS 9 -#define IGRAPH_ES_PATH 10 -#define IGRAPH_ES_MULTIPAIRS 11 +typedef enum { + IGRAPH_ES_ALL, + IGRAPH_ES_ALLFROM, + IGRAPH_ES_ALLTO, + IGRAPH_ES_INCIDENT, + IGRAPH_ES_NONE, + IGRAPH_ES_1, + IGRAPH_ES_VECTORPTR, + IGRAPH_ES_VECTOR, + IGRAPH_ES_RANGE, + IGRAPH_ES_PAIRS, + IGRAPH_ES_PATH, + IGRAPH_ES_UNUSED_WAS_MULTIPAIRS, /* placeholder for deprecated IGRAPH_ES_MULTIPAIRS from igraph 0.10 */ + IGRAPH_ES_ALL_BETWEEN, +} igraph_es_type_t; typedef struct igraph_es_t { - int type; + igraph_es_type_t type; union { igraph_integer_t vid; igraph_integer_t eid; - const igraph_vector_t *vecptr; + const igraph_vector_int_t *vecptr; struct { igraph_integer_t vid; igraph_neimode_t mode; } incident; struct { - igraph_integer_t from; /* first index */ - igraph_integer_t to; /* last index */ - } seq; + igraph_integer_t start; /* first index (inclusive) */ + igraph_integer_t end; /* last index (exclusive) */ + } range; struct { - const igraph_vector_t *ptr; + const igraph_vector_int_t *ptr; igraph_bool_t mode; } path; + struct { + igraph_integer_t from; + igraph_integer_t to; + igraph_bool_t directed; + } between; } data; } igraph_es_t; -IGRAPH_EXPORT int igraph_es_all(igraph_es_t *es, +IGRAPH_EXPORT igraph_error_t igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order); IGRAPH_EXPORT igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); -IGRAPH_EXPORT int igraph_es_incident(igraph_es_t *es, +IGRAPH_EXPORT igraph_error_t igraph_es_incident(igraph_es_t *es, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_es_none(igraph_es_t *es); +IGRAPH_EXPORT igraph_error_t igraph_es_none(igraph_es_t *es); IGRAPH_EXPORT igraph_es_t igraph_ess_none(void); -IGRAPH_EXPORT int igraph_es_1(igraph_es_t *es, igraph_integer_t eid); +IGRAPH_EXPORT igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid); IGRAPH_EXPORT igraph_es_t igraph_ess_1(igraph_integer_t eid); -IGRAPH_EXPORT int igraph_es_vector(igraph_es_t *es, - const igraph_vector_t *v); -IGRAPH_EXPORT igraph_es_t igraph_ess_vector(const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_es_vector(igraph_es_t *es, + const igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_es_t igraph_ess_vector(const igraph_vector_int_t *v); -IGRAPH_EXPORT int igraph_es_fromto(igraph_es_t *es, - igraph_vs_t from, igraph_vs_t to); +IGRAPH_EXPORT igraph_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_es_t igraph_ess_range(igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT int igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT int igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v); -IGRAPH_EXPORT int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, +IGRAPH_EXPORT igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, ...); +IGRAPH_EXPORT igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); -IGRAPH_EXPORT int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, - igraph_bool_t directed); - -IGRAPH_EXPORT int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, +IGRAPH_EXPORT igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...); +IGRAPH_EXPORT igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...); + +IGRAPH_EXPORT igraph_error_t igraph_es_all_between( + igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, + igraph_bool_t directed +); IGRAPH_EXPORT void igraph_es_destroy(igraph_es_t *es); IGRAPH_EXPORT igraph_bool_t igraph_es_is_all(const igraph_es_t *es); -IGRAPH_EXPORT int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); +IGRAPH_EXPORT igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src); -IGRAPH_EXPORT int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, - igraph_vector_t *v); -IGRAPH_EXPORT int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, +IGRAPH_EXPORT igraph_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_int_t *v); +IGRAPH_EXPORT igraph_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result); -IGRAPH_EXPORT int igraph_es_type(const igraph_es_t *es); +IGRAPH_EXPORT igraph_es_type_t igraph_es_type(const igraph_es_t *es); /* -------------------------------------------------- */ /* Edge Iterators */ /* -------------------------------------------------- */ -#define IGRAPH_EIT_SEQ 0 -#define IGRAPH_EIT_VECTOR 1 -#define IGRAPH_EIT_VECTORPTR 2 +typedef enum { + IGRAPH_EIT_RANGE, + IGRAPH_EIT_VECTOR, + IGRAPH_EIT_VECTORPTR, +} igraph_eit_type_t; typedef struct igraph_eit_t { - int type; - long int pos; - long int start; /* first index */ - long int end; /* one past last index */ - const igraph_vector_t *vec; + igraph_eit_type_t type; + igraph_integer_t pos; + igraph_integer_t start; /* first index */ + igraph_integer_t end; /* one past last index */ + const igraph_vector_int_t *vec; } igraph_eit_t; /** @@ -377,21 +400,21 @@ typedef struct igraph_eit_t { * \define IGRAPH_EIT_GET * \brief Query an edge iterator. * - * Gives the edge id of the current edge pointed to by an iterator. + * Gives the edge ID of the current edge pointed to by an iterator. * \param eit The edge iterator. * \return The id of the current edge. * * Time complexity: O(1). */ #define IGRAPH_EIT_GET(eit) \ - (igraph_integer_t)((((eit).type == IGRAPH_EIT_SEQ) ? (eit).pos : \ + (igraph_integer_t)((((eit).type == IGRAPH_EIT_RANGE) ? (eit).pos : \ VECTOR(*(eit).vec)[(eit).pos])) -IGRAPH_EXPORT int igraph_eit_create(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_eit_create(const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit); IGRAPH_EXPORT void igraph_eit_destroy(const igraph_eit_t *eit); -IGRAPH_EXPORT int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v); +IGRAPH_EXPORT igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_lapack.h b/src/vendor/cigraph/include/igraph_lapack.h index 79b3c026d5c..76b30f37dd0 100644 --- a/src/vendor/cigraph/include/igraph_lapack.h +++ b/src/vendor/cigraph/include/igraph_lapack.h @@ -25,6 +25,7 @@ #define IGRAPH_LAPACK_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_matrix.h" @@ -56,11 +57,11 @@ __BEGIN_DECLS * */ -IGRAPH_EXPORT int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, int *info); -IGRAPH_EXPORT int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, const igraph_vector_int_t *ipiv, igraph_matrix_t *b); -IGRAPH_EXPORT int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, igraph_matrix_t *b, int *info); typedef enum { IGRAPH_LAPACK_DSYEV_ALL, @@ -68,7 +69,7 @@ typedef enum { IGRAPH_LAPACK_DSYEV_ALL, IGRAPH_LAPACK_DSYEV_SELECT } igraph_lapack_dsyev_which_t; -IGRAPH_EXPORT int igraph_lapack_dsyevr(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t 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, @@ -77,7 +78,7 @@ IGRAPH_EXPORT int igraph_lapack_dsyevr(const igraph_matrix_t *A, /* TODO: should we use complex vectors/matrices? */ -IGRAPH_EXPORT int igraph_lapack_dgeev(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, @@ -90,7 +91,7 @@ typedef enum { IGRAPH_LAPACK_DGEEVX_BALANCE_NONE = 0, } igraph_lapack_dgeevx_balance_t; -IGRAPH_EXPORT int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, @@ -102,7 +103,7 @@ IGRAPH_EXPORT int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, igraph_vector_t *rcondv, int *info); -IGRAPH_EXPORT int igraph_lapack_dgehrd(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result); diff --git a/src/vendor/cigraph/include/igraph_layout.h b/src/vendor/cigraph/include/igraph_layout.h index 16c7b3c2bd1..1730f1a710d 100644 --- a/src/vendor/cigraph/include/igraph_layout.h +++ b/src/vendor/cigraph/include/igraph_layout.h @@ -25,14 +25,16 @@ #define IGRAPH_LAYOUT_H #include "igraph_decls.h" + #include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" +#include "igraph_matrix_list.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" #include "igraph_matrix.h" -#include "igraph_datatype.h" -#include "igraph_arpack.h" -#include "igraph_iterators.h" __BEGIN_DECLS @@ -52,59 +54,59 @@ __BEGIN_DECLS /* Layouts */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t order); -IGRAPH_EXPORT int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, - igraph_integer_t center, const igraph_vector_t *order); -IGRAPH_EXPORT int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int width); -IGRAPH_EXPORT int igraph_layout_fruchterman_reingold(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_int_t *order); +IGRAPH_EXPORT igraph_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width); +IGRAPH_EXPORT igraph_error_t 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 *weights, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy); -IGRAPH_EXPORT int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t maxiter, igraph_real_t maxdelta, igraph_real_t area, igraph_real_t coolexp, igraph_real_t repulserad, igraph_real_t cellsize, igraph_integer_t root); -IGRAPH_EXPORT int igraph_layout_reingold_tilford(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_layout_reingold_tilford_circular(const igraph_t *graph, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel); +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT 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); - -IGRAPH_EXPORT int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, - long int width, long int height); -IGRAPH_EXPORT int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel); +IGRAPH_EXPORT igraph_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, + igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, + const igraph_vector_int_t* layers, igraph_real_t hgap, + igraph_real_t vgap, igraph_integer_t maxiter, const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res); +IGRAPH_EXPORT igraph_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t width, igraph_integer_t height); +IGRAPH_EXPORT igraph_error_t 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 *weights, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -112,7 +114,7 @@ IGRAPH_EXPORT int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, const igraph_vector_t *minz, const igraph_vector_t *maxz); -IGRAPH_EXPORT int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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, @@ -120,7 +122,7 @@ IGRAPH_EXPORT int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_ma const igraph_vector_t *miny, const igraph_vector_t *maxy, const igraph_vector_t *minz, const igraph_vector_t *maxz); -IGRAPH_EXPORT int igraph_layout_graphopt(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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, @@ -128,13 +130,34 @@ IGRAPH_EXPORT int igraph_layout_graphopt(const igraph_t *graph, igraph_real_t max_sa_movement, igraph_bool_t use_seed); -IGRAPH_EXPORT int igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, - const igraph_matrix_t *dist, long int dim); +IGRAPH_EXPORT igraph_error_t igraph_layout_mds(const igraph_t *graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, igraph_integer_t dim); -IGRAPH_EXPORT int igraph_layout_bipartite(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); + igraph_real_t vgap, igraph_integer_t maxiter); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights); + +IGRAPH_EXPORT igraph_error_t igraph_layout_umap_compute_weights(const igraph_t *graph, + const igraph_vector_t *distances, + igraph_vector_t *weights); + /** * \struct igraph_layout_drl_options_t @@ -222,30 +245,28 @@ typedef enum { IGRAPH_LAYOUT_DRL_DEFAULT = 0, IGRAPH_LAYOUT_DRL_FINAL } igraph_layout_drl_default_t; -IGRAPH_EXPORT int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, +IGRAPH_EXPORT igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, igraph_layout_drl_default_t templ); -IGRAPH_EXPORT int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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 igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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); + const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, - const igraph_vector_ptr_t *coords, +IGRAPH_EXPORT igraph_error_t igraph_layout_merge_dla(const igraph_vector_ptr_t *graphs, + const igraph_matrix_list_t *coords, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t 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, @@ -253,6 +274,25 @@ IGRAPH_EXPORT int igraph_layout_davidson_harel(const igraph_t *graph, igraph_mat igraph_real_t weight_edge_crossings, igraph_real_t weight_node_edge_dist); +/** + * \typedef igraph_root_choice_t + * \brief Root choice heuristic for tree visualizations. + * + * Used with \ref igraph_roots_for_tree_layout(). + */ + +typedef enum { + IGRAPH_ROOT_CHOICE_DEGREE, + IGRAPH_ROOT_CHOICE_ECCENTRICITY +} igraph_root_choice_t; + +IGRAPH_EXPORT igraph_error_t igraph_roots_for_tree_layout( + const igraph_t *graph, + igraph_neimode_t mode, + igraph_vector_int_t *roots, + igraph_root_choice_t use_eccentricity); + + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_lsap.h b/src/vendor/cigraph/include/igraph_lsap.h index eeffedec006..61898d40ca5 100644 --- a/src/vendor/cigraph/include/igraph_lsap.h +++ b/src/vendor/cigraph/include/igraph_lsap.h @@ -3,13 +3,14 @@ #define IGRAPH_LSAP_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_matrix.h" #include "igraph_vector.h" #include "igraph_types.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_solve_lsap(igraph_matrix_t *c, igraph_integer_t n, +IGRAPH_EXPORT igraph_error_t igraph_solve_lsap(const igraph_matrix_t *c, igraph_integer_t n, igraph_vector_int_t *p); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_matching.h b/src/vendor/cigraph/include/igraph_matching.h index c9a154058a8..44b9082f488 100644 --- a/src/vendor/cigraph/include/igraph_matching.h +++ b/src/vendor/cigraph/include/igraph_matching.h @@ -24,8 +24,8 @@ #define IGRAPH_MATCHING_H #include "igraph_decls.h" -#include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -35,22 +35,18 @@ __BEGIN_DECLS /* Matchings in graphs */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_is_matching(const igraph_t* graph, - const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, +IGRAPH_EXPORT igraph_error_t igraph_is_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, igraph_bool_t* result); -IGRAPH_EXPORT int igraph_is_maximal_matching(const igraph_t* graph, - const igraph_vector_bool_t* types, const igraph_vector_long_t* matching, +IGRAPH_EXPORT igraph_error_t igraph_is_maximal_matching(const igraph_t* graph, + const igraph_vector_bool_t* types, const igraph_vector_int_t* matching, igraph_bool_t* result); -IGRAPH_EXPORT int igraph_maximum_bipartite_matching(const igraph_t* graph, +IGRAPH_EXPORT igraph_error_t 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, + igraph_real_t* matching_weight, igraph_vector_int_t* matching, const igraph_vector_t* weights, igraph_real_t eps); -IGRAPH_EXPORT 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); - __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_matrix.h b/src/vendor/cigraph/include/igraph_matrix.h index a8b8b5e0244..6a77444b492 100644 --- a/src/vendor/cigraph/include/igraph_matrix.h +++ b/src/vendor/cigraph/include/igraph_matrix.h @@ -25,6 +25,7 @@ #define IGRAPH_MATRIX_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -45,12 +46,6 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_INT -#define BASE_LONG -#include "igraph_pmt.h" -#include "igraph_matrix_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_LONG - #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_matrix_pmt.h" @@ -73,6 +68,9 @@ __BEGIN_DECLS #define IGRAPH_MATRIX_INIT_FINALLY(m, nr, nc) \ do { IGRAPH_CHECK(igraph_matrix_init(m, nr, nc)); \ IGRAPH_FINALLY(igraph_matrix_destroy, m); } while (0) +#define IGRAPH_MATRIX_INT_INIT_FINALLY(m, nr, nc) \ + do { IGRAPH_CHECK(igraph_matrix_int_init(m, nr, nc)); \ + IGRAPH_FINALLY(igraph_matrix_int_destroy, m); } while (0) /** * \ingroup matrix @@ -89,11 +87,16 @@ __BEGIN_DECLS */ #define MATRIX(m,i,j) ((m).data.stor_begin[(m).nrow*(j)+(i)]) -IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, - const igraph_matrix_t *rhs, - igraph_real_t tol); +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t eps); -IGRAPH_EXPORT int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_matrix_list.h b/src/vendor/cigraph/include/igraph_matrix_list.h new file mode 100644 index 00000000000..1e44cf8d405 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_matrix_list.h @@ -0,0 +1,59 @@ +/* -*- 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_MATRIX_LIST_H +#define IGRAPH_MATRIX_LIST_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_matrix.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Flexible list of matrices */ +/* -------------------------------------------------- */ + +/* Indicate to igraph_typed_list_pmt.h that we are going to work with _matrices_ + * of the base type, not the base type directly */ +#define MATRIX_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#undef MATRIX_LIST + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_MATRIX_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_matrix_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_matrix_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_matrix_pmt.h b/src/vendor/cigraph/include/igraph_matrix_pmt.h index 81a735303e8..472170ea47e 100644 --- a/src/vendor/cigraph/include/igraph_matrix_pmt.h +++ b/src/vendor/cigraph/include/igraph_matrix_pmt.h @@ -23,31 +23,40 @@ typedef struct TYPE(igraph_matrix) { TYPE(igraph_vector) data; - long int nrow, ncol; + igraph_integer_t nrow, ncol; } TYPE(igraph_matrix); /*---------------*/ /* Allocation */ /*---------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, init)(TYPE(igraph_matrix) *m, - long int nrow, long int ncol); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, - const TYPE(igraph_matrix) *from); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_array)( + TYPE(igraph_matrix)* m, const BASE* data, igraph_integer_t nrow, igraph_integer_t ncol, igraph_matrix_storage_t storage); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, init_copy)( + TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); IGRAPH_EXPORT void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m); -IGRAPH_EXPORT long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_matrix, copy)( + TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); /*--------------------*/ /* Accessing elements */ /*--------------------*/ /* MATRIX */ -IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, - long int row, long int col); -IGRAPH_EXPORT BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, - long int row, long int col); -IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, - BASE value); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_matrix, e)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_matrix, e_ptr)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, get)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT BASE* FUNCTION(igraph_matrix, get_ptr)( + const TYPE(igraph_matrix) *m, igraph_integer_t row, igraph_integer_t col); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, set)( + TYPE(igraph_matrix)* m, igraph_integer_t row, igraph_integer_t col, BASE value); /*------------------------------*/ /* Initializing matrix elements */ @@ -60,70 +69,70 @@ IGRAPH_EXPORT void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) /* Matrix views */ /*-----------------------*/ -IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)(const TYPE(igraph_matrix) *m, - const BASE *data, - long int nrow, - long int ncol); +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view)( + const TYPE(igraph_matrix) *m, const BASE *data, + igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( + const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, + igraph_integer_t ncol +); /*------------------*/ /* Copying matrices */ /*------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_matrix, copy_to)(const TYPE(igraph_matrix) *m, BASE *to); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2); /*--------------------------*/ /* Copying rows and columns */ /*--------------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res, long int index); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, - TYPE(igraph_vector) *res, long int index); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, long int index); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, - const TYPE(igraph_vector) *v, long int index); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, - TYPE(igraph_matrix) *res, - const igraph_vector_t *rows); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, - TYPE(igraph_matrix) *res, - const igraph_vector_t *cols); -IGRAPH_EXPORT 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); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_row)( + const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, get_col)( + const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_row)( + TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, set_col)( + TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *rows); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_cols)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, const igraph_vector_int_t *cols); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, select_rows_cols)( + const TYPE(igraph_matrix) *m, TYPE(igraph_matrix) *res, + const igraph_vector_int_t *rows, const igraph_vector_int_t *cols); /*-----------------------------*/ /* Exchanging rows and columns */ /*-----------------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, - long int i, long int j); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, - long int i, long int j); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, swap_rowcol)(TYPE(igraph_matrix) *m, - long int i, long int j); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_cols)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, swap_rowcol)( + TYPE(igraph_matrix) *m, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m); /*-----------------------------*/ /* Matrix operations */ /*-----------------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); IGRAPH_EXPORT void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by); IGRAPH_EXPORT void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus); @@ -135,15 +144,15 @@ IGRAPH_EXPORT void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, #ifndef NOTORDERED IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, max)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_min)(const TYPE(igraph_matrix) *m, - long int *i, long int *j); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m, - long int *i, long int *j); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, - BASE *min, BASE *max); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, - long int *imin, long int *jmin, - long int *imax, long int *jmax); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_min)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_max)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, minmax)( + const TYPE(igraph_matrix) *m, BASE *min, BASE *max); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, which_minmax)( + const TYPE(igraph_matrix) *m, igraph_integer_t *imin, igraph_integer_t *jmin, + igraph_integer_t *imax, igraph_integer_t *jmax); #endif /*------------------------------*/ @@ -169,15 +178,15 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_ma IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m); IGRAPH_EXPORT BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, TYPE(igraph_vector) *res); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, is_equal)(const TYPE(igraph_matrix) *m1, const TYPE(igraph_matrix) *m2); @@ -190,55 +199,65 @@ IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(ig /* Searching for elements */ /*------------------------*/ -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, - BASE e); -IGRAPH_EXPORT 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_EXPORT igraph_bool_t FUNCTION(igraph_matrix, contains)( + const TYPE(igraph_matrix) *m, BASE e); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_matrix, search)( + const TYPE(igraph_matrix) *m, igraph_integer_t from, BASE what, + igraph_integer_t *pos, igraph_integer_t *row, igraph_integer_t *col); /*------------------------*/ /* Resizing operations */ /*------------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, - long int nrow, long int ncol); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, resize)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol); +IGRAPH_EXPORT void FUNCTION(igraph_matrix, resize_min)( + TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_cols)( + TYPE(igraph_matrix) *m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, add_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_col)( + TYPE(igraph_matrix) *m, igraph_integer_t col); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, remove_row)( + TYPE(igraph_matrix) *m, igraph_integer_t row); /*------------------------*/ /* Print as text */ /*------------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, - const char *format); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, - FILE *file); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file); -#ifdef BASE_COMPLEX +#ifdef OUT_FORMAT +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, const char *format); +#endif /* OUT_FORMAT */ -IGRAPH_EXPORT int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, - igraph_matrix_t *real); -IGRAPH_EXPORT int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, - igraph_matrix_t *imag); -IGRAPH_EXPORT int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, - igraph_matrix_t *real, - igraph_matrix_t *imag); -IGRAPH_EXPORT int igraph_matrix_complex_create(igraph_matrix_complex_t *v, - const igraph_matrix_t *real, - const igraph_matrix_t *imag); -IGRAPH_EXPORT int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, - const igraph_matrix_t *r, - const igraph_matrix_t *theta); +/*-----------------------------------------*/ +/* Operations specific to complex matrices */ +/*-----------------------------------------*/ -#endif +#ifdef BASE_COMPLEX -IGRAPH_EXPORT int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, - long int *index, long int nremove); -IGRAPH_EXPORT int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m, - const igraph_vector_t *neg, - long int nremove); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *v, + igraph_matrix_t *real); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, + igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, + igraph_matrix_t *real, + igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *v, + const igraph_matrix_t *real, + const igraph_matrix_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, + const igraph_matrix_t *r, + const igraph_matrix_t *theta); + +IGRAPH_EXPORT igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, + igraph_matrix_complex_t *rhs, + igraph_real_t eps); + +#endif /* BASE_COMPLEX */ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove); diff --git a/src/vendor/cigraph/include/igraph_memory.h b/src/vendor/cigraph/include/igraph_memory.h index 98305722d05..643501c1174 100644 --- a/src/vendor/cigraph/include/igraph_memory.h +++ b/src/vendor/cigraph/include/igraph_memory.h @@ -29,16 +29,21 @@ __BEGIN_DECLS -#define IGRAPH_CALLOC(n,t) (t*) calloc( (n) > 0 ? (size_t)(n) : (size_t)1, sizeof(t) ) +#define IGRAPH_CALLOC(n,t) (t*) calloc( (n) > 0 ? (size_t)((n)*sizeof(t)) : (size_t)1, 1 ) +#define IGRAPH_MALLOC(n) malloc( (n) > 0 ? (size_t)((n)) : (size_t)1 ) #define IGRAPH_REALLOC(p,n,t) (t*) realloc((void*)(p), (n) > 0 ? (size_t)((n)*sizeof(t)) : (size_t)1) #define IGRAPH_FREE(p) (free( (void *)(p) ), (p) = NULL) +/* These are deprecated and scheduled for removal in 0.11 */ #define igraph_Calloc IGRAPH_CALLOC #define igraph_Realloc IGRAPH_REALLOC #define igraph_Free IGRAPH_FREE +/* Deprecated section ends here */ -IGRAPH_EXPORT void igraph_free(void *p); -IGRAPH_EXPORT void *igraph_malloc(size_t n); +IGRAPH_EXPORT void *igraph_calloc(size_t count, size_t size); +IGRAPH_EXPORT void *igraph_malloc(size_t size); +IGRAPH_EXPORT void *igraph_realloc(void* ptr, size_t size); +IGRAPH_EXPORT void igraph_free(void *ptr); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_microscopic_update.h b/src/vendor/cigraph/include/igraph_microscopic_update.h index 7432b71810d..bff0e03a1eb 100644 --- a/src/vendor/cigraph/include/igraph_microscopic_update.h +++ b/src/vendor/cigraph/include/igraph_microscopic_update.h @@ -25,34 +25,35 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_iterators.h" #include "igraph_types.h" #include "igraph_vector.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_deterministic_optimal_imitation(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_moran_process(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, - igraph_vector_t *strategies, + igraph_vector_int_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_roulette_wheel_imitation(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_stochastic_imitation(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_mixing.h b/src/vendor/cigraph/include/igraph_mixing.h index 5bc62eccd43..4bf2af82c4e 100644 --- a/src/vendor/cigraph/include/igraph_mixing.h +++ b/src/vendor/cigraph/include/igraph_mixing.h @@ -27,22 +27,25 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_assortativity_nominal(const igraph_t *graph, - const igraph_vector_t *types, +IGRAPH_EXPORT igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_int_t *types, igraph_real_t *res, - igraph_bool_t directed); + igraph_bool_t directed, + igraph_bool_t normalized); -IGRAPH_EXPORT int igraph_assortativity(const igraph_t *graph, - const igraph_vector_t *types1, - const igraph_vector_t *types2, +IGRAPH_EXPORT igraph_error_t igraph_assortativity(const igraph_t *graph, + const igraph_vector_t *values, + const igraph_vector_t *values_in, igraph_real_t *res, - igraph_bool_t directed); + igraph_bool_t directed, + igraph_bool_t normalized); -IGRAPH_EXPORT int igraph_assortativity_degree(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_assortativity_degree(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed); diff --git a/src/vendor/cigraph/include/igraph_motifs.h b/src/vendor/cigraph/include/igraph_motifs.h index 7768ee17d8d..6394b86266a 100644 --- a/src/vendor/cigraph/include/igraph_motifs.h +++ b/src/vendor/cigraph/include/igraph_motifs.h @@ -27,6 +27,7 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS @@ -37,59 +38,62 @@ __BEGIN_DECLS /** * \typedef igraph_motifs_handler_t - * Callback type for \c igraph_motifs_randesu_callback + * \brief Callback type for \c igraph_motifs_randesu_callback. * * \ref igraph_motifs_randesu_callback() calls a specified callback * function whenever a new motif is found during a motif search. This * callback function must be of type \c igraph_motifs_handler_t. It has * the following arguments: + * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vids The IDs of the vertices in the motif that has just been * found. This vector is owned by the motif search algorithm, so do not * modify or destroy it; make a copy of it if you need it later. * \param isoclass The isomorphism class of the motif that has just been - * found. Use \ref igraph_isoclass or \ref igraph_isoclass_subgraph to find - * out which isomorphism class belongs to a given motif. + * found. Use \ref igraph_graph_count() to find the maximum possible + * isoclass for graphs of a given size. See \ref igraph_isoclass and + * \ref igraph_isoclass_subgraph for more information. * \param extra The extra argument that was passed to \ref * igraph_motifs_randesu_callback(). - * \return A logical value, if TRUE (=non-zero), that is interpreted - * as a request to stop the motif search and return to the caller. + * \return \c IGRAPH_SUCCESS to continue the motif search, + * \c IGRAPH_STOP to stop the motif search and return to the caller + * normally. Any other return value is interpreted as an igraph error code, + * which will terminate the search and return the same error code to the + * caller. * * \sa \ref igraph_motifs_randesu_callback() */ -typedef igraph_bool_t igraph_motifs_handler_t(const igraph_t *graph, - igraph_vector_t *vids, - int isoclass, +typedef igraph_error_t igraph_motifs_handler_t(const igraph_t *graph, + igraph_vector_int_t *vids, + igraph_integer_t isoclass, void* extra); -IGRAPH_EXPORT int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, - int size, const igraph_vector_t *cut_prob); +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t size, const igraph_vector_t *cut_prob); -IGRAPH_EXPORT int igraph_motifs_randesu_callback(const igraph_t *graph, int size, +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra); -IGRAPH_EXPORT int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, - int size, const igraph_vector_t *cut_prob, +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, + igraph_integer_t size, const igraph_vector_t *cut_prob, igraph_integer_t sample_size, - const igraph_vector_t *sample); -IGRAPH_EXPORT int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, - int size, const igraph_vector_t *cut_prob); + const igraph_vector_int_t *sample); +IGRAPH_EXPORT igraph_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + igraph_integer_t size, const igraph_vector_t *cut_prob); -IGRAPH_EXPORT int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, - igraph_integer_t *asym, igraph_integer_t *null); -IGRAPH_EXPORT int igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, - igraph_real_t *res4); +IGRAPH_EXPORT igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, + igraph_real_t *asym, igraph_real_t *null); +IGRAPH_EXPORT igraph_error_t igraph_triad_census(const igraph_t *igraph, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_adjacent_triangles(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids); -IGRAPH_EXPORT int igraph_list_triangles(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_list_triangles(const igraph_t *graph, igraph_vector_int_t *res); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_neighborhood.h b/src/vendor/cigraph/include/igraph_neighborhood.h index 88a1c4ff190..d96d22c9cf9 100644 --- a/src/vendor/cigraph/include/igraph_neighborhood.h +++ b/src/vendor/cigraph/include/igraph_neighborhood.h @@ -26,18 +26,20 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_graph_list.h" #include "igraph_iterators.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode, igraph_integer_t mindist); -IGRAPH_EXPORT int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, +IGRAPH_EXPORT igraph_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode, igraph_integer_t mindist); -IGRAPH_EXPORT int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, +IGRAPH_EXPORT igraph_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_t *res, igraph_vs_t vids, igraph_integer_t order, igraph_neimode_t mode, igraph_integer_t mindist); diff --git a/src/vendor/cigraph/include/igraph_nongraph.h b/src/vendor/cigraph/include/igraph_nongraph.h index 47ebedb4a8f..43b6ec3c507 100644 --- a/src/vendor/cigraph/include/igraph_nongraph.h +++ b/src/vendor/cigraph/include/igraph_nongraph.h @@ -25,20 +25,35 @@ #define IGRAPH_NONGRAPH_H #include "igraph_decls.h" -#include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" __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 + +typedef igraph_real_t igraph_scalar_function_t(const igraph_vector_t *var, + const igraph_vector_t *par, + void* extra); +typedef void igraph_vector_function_t(const igraph_vector_t *var, + const igraph_vector_t *par, + igraph_vector_t* res, void* extra); + /* -------------------------------------------------- */ /* Other, not graph related */ /* -------------------------------------------------- */ /** * \struct igraph_plfit_result_t - * \brief Result of fitting a power-law distribution to a vector + * \brief Result of fitting a power-law distribution to a vector. * * This data structure contains the result of \ref igraph_power_law_fit(), * which tries to fit a power-law distribution to a vector of numbers. The @@ -56,36 +71,44 @@ __BEGIN_DECLS * \member D The test statistic of a Kolmogorov-Smirnov test that compares * the fitted distribution with the input vector. Smaller scores * denote better fit. - * \member p The p-value of the Kolmogorov-Smirnov test. Small p-values - * (less than 0.05) indicate that the test rejected the hypothesis - * that the original data could have been drawn from the fitted - * power-law distribution. + * \member p The p-value of the Kolmogorov-Smirnov test; \c NaN if it has + * not been calculated yet. Small p-values (less than 0.05) + * indicate that the test rejected the hypothesis that the + * original data could have been drawn from the fitted power-law + * distribution. + * \member data The vector containing the original input data. May not be valid + * any more if the caller already destroyed the vector. */ typedef struct igraph_plfit_result_t { igraph_bool_t continuous; - double alpha; - double xmin; - double L; - double D; - double p; + igraph_real_t alpha; + igraph_real_t xmin; + igraph_real_t L; + igraph_real_t D; + const igraph_vector_t* data; } igraph_plfit_result_t; -IGRAPH_EXPORT int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth); -IGRAPH_EXPORT int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, +IGRAPH_EXPORT igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, igraph_integer_t length); -IGRAPH_EXPORT int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, +IGRAPH_EXPORT igraph_error_t igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_int_t *resverts, igraph_matrix_t *rescoords); -IGRAPH_EXPORT int igraph_zeroin(igraph_real_t *ax, igraph_real_t *bx, - igraph_real_t (*f)(igraph_real_t x, void *info), - void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_power_law_fit(const igraph_vector_t* vector, igraph_plfit_result_t* result, - igraph_real_t xmin, igraph_bool_t force_continuous); +IGRAPH_EXPORT igraph_bool_t igraph_almost_equals(double a, double b, double eps); +IGRAPH_EXPORT int igraph_cmp_epsilon(double a, double b, double eps); + +IGRAPH_EXPORT igraph_error_t igraph_power_law_fit( + const igraph_vector_t* vector, igraph_plfit_result_t* result, + igraph_real_t xmin, igraph_bool_t force_continuous +); +IGRAPH_EXPORT igraph_error_t igraph_plfit_result_calculate_p_value( + const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision +); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_zeroin( + igraph_real_t *ax, igraph_real_t *bx, igraph_real_t (*f)(igraph_real_t x, void *info), + void *info, igraph_real_t *Tol, int *Maxit, igraph_real_t *res +); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_operators.h b/src/vendor/cigraph/include/igraph_operators.h index 7992bdfe38a..698dc9504c2 100644 --- a/src/vendor/cigraph/include/igraph_operators.h +++ b/src/vendor/cigraph/include/igraph_operators.h @@ -25,10 +25,13 @@ #define IGRAPH_OPERATORS_H #include "igraph_decls.h" + #include "igraph_attributes.h" #include "igraph_constants.h" -#include "igraph_types.h" #include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS @@ -37,52 +40,56 @@ __BEGIN_DECLS /* Graph operators */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); -IGRAPH_EXPORT int igraph_disjoint_union(igraph_t *res, +IGRAPH_EXPORT igraph_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT igraph_error_t igraph_disjoint_union(igraph_t *res, const igraph_t *left, const igraph_t *right); -IGRAPH_EXPORT int igraph_disjoint_union_many(igraph_t *res, +IGRAPH_EXPORT igraph_error_t igraph_disjoint_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs); -IGRAPH_EXPORT int igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); -IGRAPH_EXPORT int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_ptr_t *edgemaps); -IGRAPH_EXPORT int igraph_intersection(igraph_t *res, +IGRAPH_EXPORT igraph_error_t igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps); +IGRAPH_EXPORT igraph_error_t igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_t *edge_map1, - igraph_vector_t *edge_map2); -IGRAPH_EXPORT int igraph_intersection_many(igraph_t *res, + igraph_vector_int_t *edge_map1, + igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_intersection_many(igraph_t *res, const igraph_vector_ptr_t *graphs, - igraph_vector_ptr_t *edgemaps); -IGRAPH_EXPORT int igraph_difference(igraph_t *res, + igraph_vector_int_list_t *edgemaps); +IGRAPH_EXPORT igraph_error_t igraph_difference(igraph_t *res, const igraph_t *orig, const igraph_t *sub); -IGRAPH_EXPORT int igraph_complementer(igraph_t *res, const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_complementer(igraph_t *res, const igraph_t *graph, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, - igraph_vector_t *edge_map1, igraph_vector_t *edge_map2); -IGRAPH_EXPORT int igraph_contract_vertices(igraph_t *graph, - const igraph_vector_t *mapping, - const igraph_attribute_combination_t - *vertex_comb); -IGRAPH_EXPORT int igraph_permute_vertices(const igraph_t *graph, igraph_t *res, - const igraph_vector_t *permutation); -IGRAPH_EXPORT int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, +IGRAPH_EXPORT igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); +IGRAPH_EXPORT igraph_error_t igraph_contract_vertices(igraph_t *graph, + const igraph_vector_int_t *mapping, + const igraph_attribute_combination_t *vertex_comb); +IGRAPH_EXPORT igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_int_t *permutation); +IGRAPH_EXPORT igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_rewire(igraph_t *graph, - igraph_integer_t n, igraph_rewiring_t mode); -IGRAPH_EXPORT int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, +IGRAPH_EXPORT igraph_error_t igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode); +IGRAPH_EXPORT igraph_error_t igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_bool_t loops, const igraph_attribute_combination_t *edge_comb); -IGRAPH_EXPORT int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, + igraph_vector_int_t *map, + igraph_vector_int_t *invmap); +IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, igraph_subgraph_implementation_t impl); -IGRAPH_EXPORT int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, +IGRAPH_EXPORT igraph_error_t igraph_induced_subgraph_edges( + const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges); +IGRAPH_EXPORT igraph_error_t igraph_subgraph_from_edges(const igraph_t *graph, igraph_t *res, const igraph_es_t eids, igraph_bool_t delete_vertices); -IGRAPH_EXPORT int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids); +IGRAPH_EXPORT igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_subgraph_edges( + const igraph_t *graph, igraph_t *res, const igraph_es_t eids, + igraph_bool_t delete_vertices +); __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_paths.h b/src/vendor/cigraph/include/igraph_paths.h index 067449d151b..40256b4c243 100644 --- a/src/vendor/cigraph/include/igraph_paths.h +++ b/src/vendor/cigraph/include/igraph_paths.h @@ -22,154 +22,335 @@ #ifndef IGRAPH_PATHS_H #define IGRAPH_PATHS_H -#include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_iterators.h" +#include "igraph_matrix.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_ptr.h" -#include "igraph_matrix.h" -#include "igraph_iterators.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_diameter(const igraph_t *graph, igraph_real_t *res, +/** + * \typedef igraph_astar_heuristic_func_t + * \brief Distance estimator for A* algorithm. + * + * \ref igraph_get_shortest_path_astar() uses a heuristic based on a distance + * estimate to the target vertex to guide its search, and determine + * which vertex to try next. The heurstic function is expected to compute + * an estimate of the distance between \p from and \p to. In order for + * \ref igraph_get_shortest_path_astar() to find an exact shortest path, + * the distance must not be overestimated, i.e. the heuristic function + * must be \em admissible. + * + * \param result The result of the heuristic, i.e. the estimated distance. + * A lower value will mean this vertex will be a better candidate for + * exploration. + * \param from The vertex ID of the candidate vertex will be passed here. + * \param to The vertex ID of the endpoint of the path, i.e. the \c to parameter + * given to \ref igraph_get_shortest_path_astar(), will be passed here. + * \param extra The \c extra argument that was passed to + * \ref igraph_get_shortest_path_astar(). + * \return Error code. Must return \c IGRAPH_SUCCESS if there were no errors. + * This can be used to break off the algorithm if something unexpected happens, + * like a failed memory allocation (\c IGRAPH_ENOMEM). + * + * \sa \ref igraph_get_shortest_path_astar() + */ +typedef igraph_error_t igraph_astar_heuristic_func_t( + igraph_real_t *result, + igraph_integer_t from, igraph_integer_t to, + void *extra); + +typedef enum { + IGRAPH_FLOYD_WARSHALL_AUTOMATIC = 0, + IGRAPH_FLOYD_WARSHALL_ORIGINAL = 1, + IGRAPH_FLOYD_WARSHALL_TREE = 2 +} igraph_floyd_warshall_algorithm_t; + +IGRAPH_EXPORT igraph_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, igraph_integer_t *from, igraph_integer_t *to, - igraph_vector_t *path, + igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_path, igraph_bool_t directed, igraph_bool_t unconn); -IGRAPH_EXPORT int igraph_diameter_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_diameter_dijkstra(const igraph_t *graph, const igraph_vector_t *weights, - igraph_real_t *pres, - igraph_integer_t *pfrom, - igraph_integer_t *pto, - igraph_vector_t *path, + igraph_real_t *res, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_vector_int_t *vertex_path, + igraph_vector_int_t *edge_path, igraph_bool_t directed, igraph_bool_t unconn); -IGRAPH_EXPORT int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode, igraph_real_t cutoff); +IGRAPH_EXPORT igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_distances_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); +IGRAPH_EXPORT igraph_error_t igraph_distances_dijkstra_cutoff(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, + igraph_real_t cutoff); +IGRAPH_EXPORT igraph_error_t igraph_distances_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); +IGRAPH_EXPORT igraph_error_t igraph_distances_johnson(const igraph_t *graph, + igraph_matrix_t *res, + const igraph_vs_t from, + const igraph_vs_t to, + const igraph_vector_t *weights); +IGRAPH_EXPORT igraph_error_t igraph_distances_floyd_warshall(const igraph_t *graph, + igraph_matrix_t *res, + igraph_vs_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_floyd_warshall_algorithm_t method); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT 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_EXPORT 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); -IGRAPH_EXPORT int igraph_shortest_paths_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); -IGRAPH_EXPORT int igraph_shortest_paths_bellman_ford(const igraph_t *graph, - igraph_matrix_t *res, - const igraph_vs_t from, - const igraph_vs_t to, +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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); + +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, const igraph_vector_t *weights, - igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_get_shortest_path_bellman_ford(const igraph_t *graph, - igraph_vector_t *vertices, - igraph_vector_t *edges, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); + +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, igraph_integer_t from, igraph_integer_t to, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_get_shortest_path_dijkstra(const igraph_t *graph, - igraph_vector_t *vertices, - igraph_vector_t *edges, +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, igraph_integer_t from, igraph_integer_t to, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, - igraph_vector_ptr_t *res, - igraph_vector_t *nrgeo, + +IGRAPH_EXPORT igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_astar_heuristic_func_t *heuristic, + void *extra); + +IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, + igraph_integer_t from, const igraph_vs_t to, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_average_path_length(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_average_path_length_dijkstra(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, igraph_real_t *unconnected, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *weights, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_eccentricity(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_radius(const igraph_t *graph, igraph_real_t *radius, +IGRAPH_EXPORT igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_get_all_simple_paths(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_graph_center(const igraph_t *graph, + igraph_vector_int_t *res, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn); +IGRAPH_EXPORT igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn); + +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_random_walk(const igraph_t *graph, igraph_vector_t *walk, - igraph_integer_t start, igraph_neimode_t mode, +IGRAPH_EXPORT igraph_error_t igraph_random_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t start, + igraph_neimode_t mode, igraph_integer_t steps, igraph_random_walk_stuck_t stuck); -IGRAPH_EXPORT int igraph_random_edge_walk(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_get_k_shortest_paths(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_vector_int_list_t *vertex_paths, + igraph_vector_int_list_t *edge_paths, + igraph_integer_t k, + igraph_integer_t from, + igraph_integer_t to, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_spanner(const igraph_t *graph, + igraph_vector_int_t *spanner, + igraph_real_t stretch, + const igraph_vector_t *weights); + +IGRAPH_EXPORT igraph_error_t igraph_get_widest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges); +IGRAPH_EXPORT igraph_error_t igraph_get_widest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_widest_path_widths_floyd_warshall(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); +IGRAPH_EXPORT igraph_error_t igraph_widest_path_widths_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); +IGRAPH_EXPORT igraph_error_t igraph_voronoi(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *distances, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker); + +IGRAPH_EXPORT igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t *path); + +IGRAPH_EXPORT igraph_error_t igraph_vertex_path_from_edge_path( + const igraph_t *graph, igraph_integer_t start, + const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, + igraph_neimode_t mode); + +/* Deprecated functions: */ + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_random_edge_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *edgewalk, + igraph_integer_t start, + igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_pmt.h b/src/vendor/cigraph/include/igraph_pmt.h index ab810d6bcf8..fe8c350d3eb 100644 --- a/src/vendor/cigraph/include/igraph_pmt.h +++ b/src/vendor/cigraph/include/igraph_pmt.h @@ -27,35 +27,27 @@ #define CONCAT3(a,b,c) CONCAT3x(a,b,c) #define CONCAT4x(a,b,c,d) a ## _ ## b ## _ ## c ## _ ## d #define CONCAT4(a,b,c,d) CONCAT4x(a,b,c,d) +#define CONCAT5x(a,b,c,d,e) a ## _ ## b ## _ ## c ## _ ## d ## _ ## e +#define CONCAT5(a,b,c,d,e) CONCAT5x(a,b,c,d,e) #if defined(BASE_IGRAPH_REAL) #define BASE igraph_real_t + #define BASE_VECTOR igraph_vector_t + #define BASE_MATRIX igraph_matrix_t #define SHORT - #define OUT_FORMAT "%G" + #define OUT_FORMAT "%g" #define PRINTFUNC(val) igraph_real_printf(val) + #define SNPRINTFUNC(str, size, val) igraph_real_snprintf(str, size, val) + #define FPRINTFUNC_ALIGNED(file, width, val) igraph_real_fprintf_aligned(file, width, val) #define FPRINTFUNC(file, val) igraph_real_fprintf(file, val) #define ZERO 0.0 #define ONE 1.0 #define MULTIPLICITY 1 -#elif defined(BASE_FLOAT) - #define BASE float - #define SHORT float - #define OUT_FORMAT "%f" - #define ZERO 0.0F - #define ONE 1.0F - #define MULTIPLICITY 1 - -#elif defined(BASE_LONG) - #define BASE long - #define SHORT long - #define OUT_FORMAT "%ld" - #define ZERO 0L - #define ONE 1L - #define MULTIPLICITY 1 - #elif defined(BASE_CHAR) #define BASE char + #define BASE_VECTOR igraph_vector_char_t + #define BASE_MATRIX igraph_matrix_char_t #define SHORT char #define OUT_FORMAT "%d" #define ZERO 0 @@ -64,6 +56,8 @@ #elif defined(BASE_BOOL) #define BASE igraph_bool_t + #define BASE_VECTOR igraph_vector_bool_t + #define BASE_MATRIX igraph_matrix_bool_t #define SHORT bool #define OUT_FORMAT "%d" #define ZERO 0 @@ -75,12 +69,22 @@ #elif defined(BASE_INT) #define BASE igraph_integer_t + #define BASE_VECTOR igraph_vector_int_t + #define BASE_MATRIX igraph_matrix_int_t #define SHORT int #define OUT_FORMAT "%" IGRAPH_PRId #define ZERO 0 #define ONE 1 #define MULTIPLICITY 1 +#elif defined(BASE_FORTRAN_INT) + #define BASE int + #define SHORT fortran_int + #define OUT_FORMAT "%d" + #define ZERO 0 + #define ONE 1 + #define MULTIPLICITY 1 + #elif defined(BASE_PTR) #define BASE void* #define SHORT ptr @@ -90,9 +94,15 @@ #elif defined(BASE_COMPLEX) #undef complex #define BASE igraph_complex_t + #define BASE_VECTOR igraph_vector_complex_t + #define BASE_MATRIX igraph_matrix_complex_t #define SHORT complex - #define ZERO igraph_complex(0,0) - #define ONE {{1.0,0.0}} + #define PRINTFUNC(val) igraph_complex_printf(val) + #define SNPRINTFUNC(str, size, val) igraph_complex_snprintf(str, size, val) + #define FPRINTFUNC_ALIGNED(file, width, val) igraph_complex_fprintf_aligned(file, width, val) + #define FPRINTFUNC(file, val) igraph_complex_fprintf(file, val) + #define ZERO {{0.0, 0.0}} + #define ONE {{1.0, 0.0}} #define MULTIPLICITY 2 #define NOTORDERED 1 #define NOABS 1 @@ -103,21 +113,62 @@ #define EQ(a,b) IGRAPH_COMPLEX_EQ((a),(b)) #define SQ(a) IGRAPH_REAL(igraph_complex_mul((a),(a))) +#elif defined(BASE_GRAPH) + #define BASE igraph_t + #else #error unknown BASE_ directive #endif -#if defined(BASE_IGRAPH_REAL) - #define FUNCTION(dir,name) CONCAT2(dir,name) - #define TYPE(dir) CONCAT2(dir,t) -#elif defined(BASE_BOOL) - /* Special case because stdbool.h defines bool as a macro to _Bool which would - * screw things up */ - #define FUNCTION(a,c) CONCAT3x(a,bool,c) - #define TYPE(dir) CONCAT3x(dir,bool,t) +#if defined(VECTOR_LIST) + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(c) CONCAT2x(igraph_vector_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_list,c) + #define TYPE igraph_vector_list_t + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(c) CONCAT2x(igraph_vector_bool_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_vector_bool_list,c) + #define TYPE igraph_vector_bool_list_t + #else + #define FUNCTION(c) CONCAT4(igraph_vector,SHORT,list,c) + #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_vector,SHORT,list,c) + #define TYPE CONCAT3(igraph_vector,SHORT,list_t) + #endif +#elif defined(MATRIX_LIST) + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(c) CONCAT2x(igraph_matrix_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_list,c) + #define TYPE igraph_matrix_list_t + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(c) CONCAT2x(igraph_matrix_bool_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_matrix_bool_list,c) + #define TYPE igraph_matrix_bool_list_t + #else + #define FUNCTION(c) CONCAT4(igraph_matrix,SHORT,list,c) + #define INTERNAL_FUNCTION(c) CONCAT4(igraph_i_matrix,SHORT,list,c) + #define TYPE CONCAT3(igraph_matrix,SHORT,list_t) + #endif +#elif defined(GRAPH_LIST) + #define FUNCTION(c) CONCAT2x(igraph_graph_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_graph_list,c) + #define TYPE igraph_graph_list_t #else - #define FUNCTION(a,c) CONCAT3(a,SHORT,c) - #define TYPE(dir) CONCAT3(dir,SHORT,t) + #if defined(BASE_IGRAPH_REAL) + #define FUNCTION(a,c) CONCAT2(a,c) + #define TYPE(a) CONCAT2(a,t) + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define FUNCTION(a,c) CONCAT3x(a,bool,c) + #define TYPE(a) CONCAT3x(a,bool,t) + #else + #define FUNCTION(a,c) CONCAT3(a,SHORT,c) + #define TYPE(a) CONCAT3(a,SHORT,t) + #endif #endif #if defined(HEAP_TYPE_MIN) @@ -126,6 +177,7 @@ #define HEAPLESS > #define HEAPLESSEQ >= #undef FUNCTION + #undef INTERNAL_FUNCTION #undef TYPE #if defined(BASE_IGRAPH_REAL) #define FUNCTION(dir,name) CONCAT3(dir,min,name) diff --git a/src/vendor/cigraph/include/igraph_pmt_off.h b/src/vendor/cigraph/include/igraph_pmt_off.h index 95a957e7fe1..03ef43c7fd3 100644 --- a/src/vendor/cigraph/include/igraph_pmt_off.h +++ b/src/vendor/cigraph/include/igraph_pmt_off.h @@ -37,6 +37,14 @@ #undef BASE_EPSILON #endif +#ifdef BASE_MATRIX + #undef BASE_MATRIX +#endif + +#ifdef BASE_VECTOR + #undef BASE_VECTOR +#endif + #ifdef CONCAT2 #undef CONCAT2 #endif @@ -61,6 +69,14 @@ #undef CONCAT4x #endif +#ifdef CONCAT5 + #undef CONCAT5 +#endif + +#ifdef CONCAT5x + #undef CONCAT5x +#endif + #ifdef FP #undef FP #endif @@ -73,6 +89,10 @@ #undef IN_FORMAT #endif +#ifdef INTERNAL_FUNCTION + #undef INTERNAL_FUNCTION +#endif + #ifdef MULTIPLICITY #undef MULTIPLICITY #endif @@ -149,8 +169,16 @@ #undef PRINTFUNC #endif +#ifdef SNPRINTFUNC + #undef SNPRINTFUNC +#endif + +#ifdef FPRINTFUNC_ALIGNED + #undef FPRINTFUNC_ALIGNED +#endif + #ifdef FPRINTFUNC - #undef PRINTFUNC + #undef FPRINTFUNC #endif #ifdef UNSIGNED diff --git a/src/vendor/cigraph/include/igraph_progress.h b/src/vendor/cigraph/include/igraph_progress.h index f7a0f25e1d3..f85139a412d 100644 --- a/src/vendor/cigraph/include/igraph_progress.h +++ b/src/vendor/cigraph/include/igraph_progress.h @@ -25,6 +25,7 @@ #define IGRAPH_PROGRESS_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -142,16 +143,16 @@ __BEGIN_DECLS * error code \c IGRAPH_INTERRUPTED in this case. */ -typedef int igraph_progress_handler_t(const char *message, igraph_real_t percent, +typedef igraph_error_t igraph_progress_handler_t(const char *message, igraph_real_t percent, void *data); IGRAPH_EXPORT extern igraph_progress_handler_t igraph_progress_handler_stderr; IGRAPH_EXPORT igraph_progress_handler_t * igraph_set_progress_handler(igraph_progress_handler_t new_handler); -IGRAPH_EXPORT int igraph_progress(const char *message, igraph_real_t percent, void *data); +IGRAPH_EXPORT igraph_error_t igraph_progress(const char *message, igraph_real_t percent, void *data); -IGRAPH_EXPORT int igraph_progressf(const char *message, igraph_real_t percent, void *data, +IGRAPH_EXPORT igraph_error_t igraph_progressf(const char *message, igraph_real_t percent, void *data, ...); /** diff --git a/src/vendor/cigraph/include/igraph_psumtree.h b/src/vendor/cigraph/include/igraph_psumtree.h index c27d5764679..658d155a5c9 100644 --- a/src/vendor/cigraph/include/igraph_psumtree.h +++ b/src/vendor/cigraph/include/igraph_psumtree.h @@ -25,24 +25,25 @@ #define IGRAPH_PSUMTREE_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS typedef struct { igraph_vector_t v; - long int size; - long int offset; + igraph_integer_t size; + igraph_integer_t offset; } igraph_psumtree_t; -IGRAPH_EXPORT int igraph_psumtree_init(igraph_psumtree_t *t, long int size); +IGRAPH_EXPORT igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size); IGRAPH_EXPORT void igraph_psumtree_reset(igraph_psumtree_t *t); IGRAPH_EXPORT void igraph_psumtree_destroy(igraph_psumtree_t *t); -IGRAPH_EXPORT igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx); -IGRAPH_EXPORT long int igraph_psumtree_size(const igraph_psumtree_t *t); -IGRAPH_EXPORT int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, +IGRAPH_EXPORT igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t idx); +IGRAPH_EXPORT igraph_integer_t igraph_psumtree_size(const igraph_psumtree_t *t); +IGRAPH_EXPORT igraph_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, igraph_real_t elem); -IGRAPH_EXPORT int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, +IGRAPH_EXPORT igraph_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, igraph_real_t new_value); IGRAPH_EXPORT igraph_real_t igraph_psumtree_sum(const igraph_psumtree_t *t); diff --git a/src/vendor/cigraph/include/igraph_random.h b/src/vendor/cigraph/include/igraph_random.h index cbdb5c32368..71b4e2557d5 100644 --- a/src/vendor/cigraph/include/igraph_random.h +++ b/src/vendor/cigraph/include/igraph_random.h @@ -25,73 +25,131 @@ #define IGRAPH_RANDOM_H #include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" -__BEGIN_DECLS - +#include #include #include -#include "igraph_types.h" -#include "igraph_vector.h" - -/* The new RNG interface is (somewhat) modelled based on the GSL */ +__BEGIN_DECLS +/* The new RNG interface is (somewhat) modelled on the GSL */ + +/* When implementing your own RNG in igraph, the following methods must be + * supplied in the corresponding igraph_rng_type_t structure: + * + * - init() + * - destroy() + * - seed() + * - get() + * + * Optionally, you can provide specialized routines for several distributions + * in the following functions: + * + * - get_int() + * - get_real() + * - get_norm() + * - get_geom() + * - get_binom() + * - get_exp() + * - get_gamma() + * - get_pois() + * + * The best is probably to define get() leave the others as NULL; igraph will use + * default implementations for these. + * + * Note that if all that you would do in a get_real() implementation is to + * generate random bits with get() and divide by the maximum, don't do that; + * The default implementation takes care of calling get() a sufficient number of + * times to utilize most of the precision of the igraph_real_t type, and generate + * accurate variates. Inaccuracies in the output of get_real() can get magnified + * when using the default generators for non-uniform distributions. + * When implementing get_real(), the sampling range must be half-open, i.e. [0, 1). + * If unsure, leave get_real() unimplemented and igraph will provide an implementation + * in terms of get(). + * + * When implementing get_int(), you do not need to check whether lo < hi; + * the caller is responsible for ensuring that this is the case. You can always + * assume that hi > lo. Note that both endpoints are _inclusive_, and you must + * make sure that your generation scheme works for both 32-bit and 64-bit + * versions of igraph_integer_t as igraph can be compiled for both cases. If + * you are unsure, leave get_int() unimplemented and igraph will provide its + * own implementation based on get(). + */ typedef struct igraph_rng_type_t { const char *name; - unsigned long int min; /* 'min' must always be set to 0 */ - unsigned long int max; - int (*init)(void **state); + uint8_t bits; + + /* Initialization and destruction */ + igraph_error_t (*init)(void **state); void (*destroy)(void *state); - int (*seed)(void *state, unsigned long int seed); - unsigned long int (*get)(void *state); + + /* Seeding */ + igraph_error_t (*seed)(void *state, igraph_uint_t seed); + + /* Fundamental generator: return as many random bits as the RNG supports in + * a single round */ + igraph_uint_t (*get)(void *state); + + /* Optional generators; defaults are provided by igraph that rely solely + * on get() */ + igraph_integer_t (*get_int)(void *state, igraph_integer_t l, igraph_integer_t h); igraph_real_t (*get_real)(void *state); igraph_real_t (*get_norm)(void *state); igraph_real_t (*get_geom)(void *state, igraph_real_t p); - igraph_real_t (*get_binom)(void *state, long int n, igraph_real_t p); + igraph_real_t (*get_binom)(void *state, igraph_integer_t n, igraph_real_t p); igraph_real_t (*get_exp)(void *state, igraph_real_t rate); igraph_real_t (*get_gamma)(void *state, igraph_real_t shape, igraph_real_t scale); + igraph_real_t (*get_pois)(void *state, igraph_real_t mu); } igraph_rng_type_t; typedef struct igraph_rng_t { const igraph_rng_type_t *type; void *state; - int def; + igraph_bool_t is_seeded; } igraph_rng_t; /* --------------------------------- */ -IGRAPH_EXPORT int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); +IGRAPH_EXPORT igraph_error_t igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type); IGRAPH_EXPORT void igraph_rng_destroy(igraph_rng_t *rng); -IGRAPH_EXPORT int igraph_rng_seed(igraph_rng_t *rng, unsigned long int seed); -IGRAPH_EXPORT unsigned long int igraph_rng_max(igraph_rng_t *rng); -IGRAPH_EXPORT IGRAPH_DEPRECATED unsigned long int igraph_rng_min(igraph_rng_t *rng); -IGRAPH_EXPORT const char *igraph_rng_name(igraph_rng_t *rng); - -IGRAPH_EXPORT long int igraph_rng_get_integer(igraph_rng_t *rng, - long int l, long int h); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, - igraph_real_t m, igraph_real_t s); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif(igraph_rng_t *rng, - igraph_real_t l, igraph_real_t h); +IGRAPH_EXPORT igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed); +IGRAPH_EXPORT igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng); +IGRAPH_EXPORT igraph_uint_t igraph_rng_max(const igraph_rng_t *rng); +IGRAPH_EXPORT const char *igraph_rng_name(const igraph_rng_t *rng); + +IGRAPH_EXPORT igraph_integer_t igraph_rng_get_integer( + igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t h +); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_normal( + igraph_rng_t *rng, igraph_real_t m, igraph_real_t s +); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif( + igraph_rng_t *rng, igraph_real_t l, igraph_real_t h +); IGRAPH_EXPORT igraph_real_t igraph_rng_get_unif01(igraph_rng_t *rng); IGRAPH_EXPORT igraph_real_t igraph_rng_get_geom(igraph_rng_t *rng, igraph_real_t p); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, long int n, - igraph_real_t p); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_binom( + igraph_rng_t *rng, igraph_integer_t n, igraph_real_t p +); IGRAPH_EXPORT igraph_real_t igraph_rng_get_exp(igraph_rng_t *rng, igraph_real_t rate); -IGRAPH_EXPORT unsigned long int igraph_rng_get_int31(igraph_rng_t *rng); -IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma(igraph_rng_t *rng, igraph_real_t shape, - igraph_real_t scale); -IGRAPH_EXPORT int igraph_rng_get_dirichlet(igraph_rng_t *rng, +IGRAPH_EXPORT igraph_real_t igraph_rng_get_gamma( + igraph_rng_t *rng, igraph_real_t shape, igraph_real_t scale +); +IGRAPH_EXPORT igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate); +IGRAPH_EXPORT igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, const igraph_vector_t *alpha, igraph_vector_t *result); /* --------------------------------- */ IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_glibc2; -IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_rand; IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_mt19937; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg32; +IGRAPH_EXPORT extern const igraph_rng_type_t igraph_rngtype_pcg64; IGRAPH_EXPORT igraph_rng_t *igraph_rng_default(void); IGRAPH_EXPORT void igraph_rng_set_default(igraph_rng_t *rng); @@ -105,20 +163,15 @@ void PutRNGstate(void); #define RNG_BEGIN() GetRNGstate() #define RNG_END() PutRNGstate() -double Rf_dnorm4(double x, double mu, double sigma, int give_log); -#define igraph_dnorm Rf_dnorm4 - #else #define RNG_BEGIN() \ - if (igraph_rng_default()->def == 1) { \ + do { if (!igraph_rng_default()->is_seeded) { \ igraph_rng_seed(igraph_rng_default(), time(0)); \ - igraph_rng_default()->def=2; \ - } -#define RNG_END() /* do nothing */ - -IGRAPH_EXPORT double igraph_dnorm(double x, double mu, double sigma, int give_log); - + igraph_rng_default()->is_seeded = 1; \ + } } while (0) +#define RNG_END() \ + do { /* nothing */ } while (0) #endif #define RNG_INTEGER(l,h) (igraph_rng_get_integer(igraph_rng_default(),(l),(h))) @@ -127,7 +180,10 @@ IGRAPH_EXPORT double igraph_dnorm(double x, double mu, double sigma, int give_lo #define RNG_UNIF01() (igraph_rng_get_unif01(igraph_rng_default())) #define RNG_GEOM(p) (igraph_rng_get_geom(igraph_rng_default(),(p))) #define RNG_BINOM(n,p) (igraph_rng_get_binom(igraph_rng_default(),(n),(p))) -#define RNG_INT31() (igraph_rng_get_int31(igraph_rng_default())) +#define RNG_EXP(rate) (igraph_rng_get_exp(igraph_rng_default(),(rate))) +#define RNG_POIS(rate) (igraph_rng_get_pois(igraph_rng_default(),(rate))) +#define RNG_GAMMA(shape, scale) \ + (igraph_rng_get_gamma(igraph_rng_default(), (shape), (scale))) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_scan.h b/src/vendor/cigraph/include/igraph_scan.h index 0b1480f186e..bfc47c46626 100644 --- a/src/vendor/cigraph/include/igraph_scan.h +++ b/src/vendor/cigraph/include/igraph_scan.h @@ -26,44 +26,47 @@ #include "igraph_decls.h" #include "igraph_datatype.h" -#include "igraph_arpack.h" #include "igraph_constants.h" -#include "igraph_vector_ptr.h" +#include "igraph_error.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, igraph_vector_t *res, - const igraph_vector_t *weigths_them, + const igraph_vector_t *weights_them, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_k_ecount(const igraph_t *graph, int k, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, - int k, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, + igraph_integer_t k, igraph_vector_t *res, const igraph_vector_t *weights_them, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, - const igraph_vector_ptr_t *neighborhoods); - + const igraph_vector_int_list_t *neighborhoods); +IGRAPH_EXPORT igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *neighborhoods); __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_scg.h b/src/vendor/cigraph/include/igraph_scg.h deleted file mode 100644 index 7b466a42496..00000000000 --- a/src/vendor/cigraph/include/igraph_scg.h +++ /dev/null @@ -1,143 +0,0 @@ -/* -*- 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 - -*/ - -#ifndef IGRAPH_SCG_H -#define IGRAPH_SCG_H - -#include "igraph_decls.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_matrix.h" -#include "igraph_sparsemat.h" - -__BEGIN_DECLS - -typedef enum { IGRAPH_SCG_SYMMETRIC = 1, IGRAPH_SCG_LAPLACIAN = 2, - IGRAPH_SCG_STOCHASTIC = 3 - } igraph_scg_matrix_t; - -typedef enum { IGRAPH_SCG_OPTIMUM = 1, IGRAPH_SCG_INTERV_KM = 2, - IGRAPH_SCG_INTERV = 3, IGRAPH_SCG_EXACT = 4 - } -igraph_scg_algorithm_t; - -typedef enum { IGRAPH_SCG_NORM_ROW = 1, IGRAPH_SCG_NORM_COL = 2 } -igraph_scg_norm_t; - -typedef enum { IGRAPH_SCG_DIRECTION_DEFAULT = 1, - IGRAPH_SCG_DIRECTION_LEFT = 2, - IGRAPH_SCG_DIRECTION_RIGHT = 3 - } igraph_scg_direction_t; - -IGRAPH_EXPORT 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); - -IGRAPH_EXPORT 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); - -IGRAPH_EXPORT 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); - -IGRAPH_EXPORT 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_EXPORT 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_EXPORT 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); - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_separators.h b/src/vendor/cigraph/include/igraph_separators.h index aed1d5a6f9a..7008d9261b1 100644 --- a/src/vendor/cigraph/include/igraph_separators.h +++ b/src/vendor/cigraph/include/igraph_separators.h @@ -25,28 +25,28 @@ #define IGRAPH_SEPARATORS_H #include "igraph_decls.h" -#include "igraph_constants.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_vector_ptr.h" + #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_iterators.h" +#include "igraph_types.h" +#include "igraph_vector_list.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_is_separator(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_is_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_all_minimal_st_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators); +IGRAPH_EXPORT igraph_error_t igraph_all_minimal_st_separators(const igraph_t *graph, + igraph_vector_int_list_t *separators); -IGRAPH_EXPORT int igraph_is_minimal_separator(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_is_minimal_separator(const igraph_t *graph, const igraph_vs_t candidate, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_minimum_size_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators); +IGRAPH_EXPORT igraph_error_t igraph_minimum_size_separators(const igraph_t *graph, + igraph_vector_int_list_t *separators); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_sparsemat.h b/src/vendor/cigraph/include/igraph_sparsemat.h index 6070ca834dd..50ba93682cf 100644 --- a/src/vendor/cigraph/include/igraph_sparsemat.h +++ b/src/vendor/cigraph/include/igraph_sparsemat.h @@ -25,6 +25,7 @@ #define IGRAPH_SPARSEMAT_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_vector.h" #include "igraph_datatype.h" @@ -34,20 +35,25 @@ __BEGIN_DECLS -struct cs_di_sparse; -struct cs_di_symbolic; -struct cs_di_numeric; +/* + * These types are private to igraph, and customized to use igraph_integer_t. + * Do not attempt to access them using a separate copy of the CXSparse library. + * Use the public igraph_sparsemat_... types instead. + */ +struct cs_igraph_sparse; +struct cs_igraph_symbolic; +struct cs_igraph_numeric; typedef struct { - struct cs_di_sparse *cs; + struct cs_igraph_sparse *cs; } igraph_sparsemat_t; typedef struct { - struct cs_di_symbolic *symbolic; + struct cs_igraph_symbolic *symbolic; } igraph_sparsemat_symbolic_t; typedef struct { - struct cs_di_numeric *numeric; + struct cs_igraph_numeric *numeric; } igraph_sparsemat_numeric_t; typedef enum { IGRAPH_SPARSEMAT_TRIPLET, @@ -55,149 +61,154 @@ typedef enum { IGRAPH_SPARSEMAT_TRIPLET, } igraph_sparsemat_type_t; typedef struct { - igraph_sparsemat_t *mat; - int pos; - int col; + const igraph_sparsemat_t *mat; + igraph_integer_t pos; + igraph_integer_t col; } igraph_sparsemat_iterator_t; -IGRAPH_EXPORT int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax); -IGRAPH_EXPORT int igraph_sparsemat_copy(igraph_sparsemat_t *to, - const igraph_sparsemat_t *from); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init( + igraph_sparsemat_t *A, igraph_integer_t rows, igraph_integer_t cols, + igraph_integer_t nzmax +); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from); IGRAPH_EXPORT void igraph_sparsemat_destroy(igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_eye(igraph_sparsemat_t *A, + igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress); -IGRAPH_EXPORT long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A); -IGRAPH_EXPORT long int igraph_sparsemat_ncol(const igraph_sparsemat_t *B); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_init_diag(igraph_sparsemat_t *A, + igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress); + +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_ncol(const igraph_sparsemat_t *B); IGRAPH_EXPORT igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_triplet(const igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_permute(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_permute(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res); -IGRAPH_EXPORT int igraph_sparsemat_index(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_real_t igraph_sparsemat_get( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +); +IGRAPH_EXPORT igraph_error_t 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_EXPORT int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, - igraph_real_t elem); -IGRAPH_EXPORT int igraph_sparsemat_compress(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, + igraph_integer_t row, igraph_integer_t col, igraph_real_t elem); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res); -IGRAPH_EXPORT int igraph_sparsemat_transpose(const igraph_sparsemat_t *A, - igraph_sparsemat_t *res, int values); -IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_dupl(igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_fkeep(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_transpose( + const igraph_sparsemat_t *A, igraph_sparsemat_t *res +); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_fkeep(igraph_sparsemat_t *A, igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), void *other); -IGRAPH_EXPORT int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); -IGRAPH_EXPORT int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res); -IGRAPH_EXPORT int igraph_sparsemat_add(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_usolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - int order); + igraph_integer_t order); -IGRAPH_EXPORT int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - int order, + igraph_integer_t order, igraph_real_t tol); -IGRAPH_EXPORT int igraph_sparsemat_print(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, FILE *outstream); -IGRAPH_EXPORT int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, - igraph_real_t value, - igraph_bool_t compress); - -IGRAPH_EXPORT int igraph_sparsemat_diag(igraph_sparsemat_t *A, int nzmax, - const igraph_vector_t *values, - igraph_bool_t compress); - -IGRAPH_EXPORT int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed); -IGRAPH_EXPORT int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed, const char *attr, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res); - -IGRAPH_EXPORT int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, +IGRAPH_EXPORT igraph_error_t igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, const igraph_matrix_t *mat, igraph_real_t tol); -IGRAPH_EXPORT int igraph_sparsemat_as_matrix(igraph_matrix_t *res, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_as_matrix(igraph_matrix_t *res, const igraph_sparsemat_t *spmat); typedef enum { IGRAPH_SPARSEMAT_SOLVE_LU, IGRAPH_SPARSEMAT_SOLVE_QR } igraph_sparsemat_solve_t; -IGRAPH_EXPORT int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_sparsemat_lu(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_lu(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din, double tol); -IGRAPH_EXPORT int igraph_sparsemat_qr(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_qr(const igraph_sparsemat_t *A, const igraph_sparsemat_symbolic_t *dis, igraph_sparsemat_numeric_t *din); -IGRAPH_EXPORT int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, const igraph_sparsemat_numeric_t *din, const igraph_vector_t *b, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); -IGRAPH_EXPORT int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis); @@ -206,82 +217,95 @@ IGRAPH_EXPORT void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t * IGRAPH_EXPORT igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A); IGRAPH_EXPORT igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_minmax(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, igraph_real_t *min, igraph_real_t *max); -IGRAPH_EXPORT long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); -IGRAPH_EXPORT long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, igraph_real_t tol); -IGRAPH_EXPORT int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colsums(const igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmins(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_colmins(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmins(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos); -IGRAPH_EXPORT int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos); -IGRAPH_EXPORT int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by); -IGRAPH_EXPORT int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n); -IGRAPH_EXPORT int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n); -IGRAPH_EXPORT int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, - long int ncol, int nzmax); -IGRAPH_EXPORT int igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t n); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, + igraph_integer_t nrow, igraph_integer_t ncol, igraph_integer_t nzmax); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); -IGRAPH_EXPORT int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x); -IGRAPH_EXPORT int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, const igraph_vector_t *fact); -IGRAPH_EXPORT int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, const igraph_vector_t *fact); -IGRAPH_EXPORT int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, const igraph_matrix_t *B, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, const igraph_sparsemat_t *B, igraph_matrix_t *res); -IGRAPH_EXPORT int igraph_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, - int *p, int *i, double *x, int nz); -IGRAPH_EXPORT IGRAPH_DEPRECATED int igraph_i_sparsemat_view(igraph_sparsemat_t *A, int nzmax, int m, int n, - int *p, int *i, double *x, int nz); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, + igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz); -IGRAPH_EXPORT int igraph_sparsemat_sort(const igraph_sparsemat_t *A, +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, igraph_sparsemat_t *sorted); -IGRAPH_EXPORT int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_nzmax(const igraph_sparsemat_t *A); + +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A); -IGRAPH_EXPORT int igraph_sparsemat_neg(igraph_sparsemat_t *A); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_cols(igraph_sparsemat_t *sparsemat, + igraph_bool_t allow_zeros); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_normalize_rows(igraph_sparsemat_t *sparsemat, + igraph_bool_t allow_zeros); -IGRAPH_EXPORT int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, - igraph_sparsemat_t *sparsemat); -IGRAPH_EXPORT int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_iterator_init( + igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat +); +IGRAPH_EXPORT igraph_error_t igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it); IGRAPH_EXPORT igraph_bool_t igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it); IGRAPH_EXPORT igraph_real_t igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it); -IGRAPH_EXPORT int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); +IGRAPH_EXPORT igraph_integer_t igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_sparsemat_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_spmatrix.h b/src/vendor/cigraph/include/igraph_spmatrix.h deleted file mode 100644 index 141c39afb15..00000000000 --- a/src/vendor/cigraph/include/igraph_spmatrix.h +++ /dev/null @@ -1,109 +0,0 @@ -/* -*- 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_SPMATRIX_H -#define IGRAPH_SPMATRIX_H - -#include "igraph_decls.h" -#include "igraph_vector.h" - -__BEGIN_DECLS - -/* -------------------------------------------------- */ -/* Sparse matrix */ -/* -------------------------------------------------- */ - -/** - * \section about_igraph_spmatrix_t_objects About \type igraph_spmatrix_t objects - * - * The \type igraph_spmatrix_t type stores a sparse matrix with the - * assumption that the number of nonzero elements in the matrix scales - * linearly with the row or column count of the matrix (so most of the - * elements are zero). Of course it can store an arbitrary real matrix, - * but if most of the elements are nonzero, one should use \type igraph_matrix_t - * instead. - * - * The elements are stored in column compressed format, so the elements - * in the same column are stored adjacent in the computer's memory. The storage - * requirement for a sparse matrix is O(n) where n is the number of nonzero - * elements. Actually it can be a bit larger, see the documentation of - * the vector type for an explanation. - */ -typedef struct s_spmatrix { - igraph_vector_t ridx, cidx, data; - long int nrow, ncol; -} igraph_spmatrix_t; - -#define IGRAPH_SPMATRIX_INIT_FINALLY(m, nr, nc) \ - do { IGRAPH_CHECK(igraph_spmatrix_init(m, nr, nc)); \ - IGRAPH_FINALLY(igraph_spmatrix_destroy, m); } while (0) - -IGRAPH_EXPORT int igraph_spmatrix_init(igraph_spmatrix_t *m, long int nrow, long int ncol); -IGRAPH_EXPORT void igraph_spmatrix_destroy(igraph_spmatrix_t *m); -IGRAPH_EXPORT int igraph_spmatrix_resize(igraph_spmatrix_t *m, long int nrow, long int ncol); -IGRAPH_EXPORT igraph_real_t igraph_spmatrix_e(const igraph_spmatrix_t *m, long int row, long int col); -IGRAPH_EXPORT int igraph_spmatrix_set(igraph_spmatrix_t *m, long int row, long int col, - igraph_real_t value); -IGRAPH_EXPORT int igraph_spmatrix_add_e(igraph_spmatrix_t *m, long int row, long int col, - igraph_real_t value); -IGRAPH_EXPORT int igraph_spmatrix_add_col_values(igraph_spmatrix_t *m, long int to, long int from); -IGRAPH_EXPORT long int igraph_spmatrix_count_nonzero(const igraph_spmatrix_t *m); -IGRAPH_EXPORT long int igraph_spmatrix_size(const igraph_spmatrix_t *m); -IGRAPH_EXPORT long int igraph_spmatrix_nrow(const igraph_spmatrix_t *m); -IGRAPH_EXPORT long int igraph_spmatrix_ncol(const igraph_spmatrix_t *m); -IGRAPH_EXPORT int igraph_spmatrix_copy_to(const igraph_spmatrix_t *m, igraph_real_t *to); -IGRAPH_EXPORT int igraph_spmatrix_null(igraph_spmatrix_t *m); -IGRAPH_EXPORT int igraph_spmatrix_add_cols(igraph_spmatrix_t *m, long int n); -IGRAPH_EXPORT int igraph_spmatrix_add_rows(igraph_spmatrix_t *m, long int n); -IGRAPH_EXPORT int igraph_spmatrix_clear_col(igraph_spmatrix_t *m, long int col); -IGRAPH_EXPORT int igraph_spmatrix_clear_row(igraph_spmatrix_t *m, long int row); -IGRAPH_EXPORT int igraph_spmatrix_copy(igraph_spmatrix_t *to, const igraph_spmatrix_t *from); -IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max_nonzero(const igraph_spmatrix_t *m, - igraph_real_t *ridx, igraph_real_t *cidx); -IGRAPH_EXPORT igraph_real_t igraph_spmatrix_max(const igraph_spmatrix_t *m, - igraph_real_t *ridx, igraph_real_t *cidx); -IGRAPH_EXPORT void igraph_spmatrix_scale(igraph_spmatrix_t *m, igraph_real_t by); -IGRAPH_EXPORT int igraph_spmatrix_colsums(const igraph_spmatrix_t *m, igraph_vector_t *res); -IGRAPH_EXPORT int igraph_spmatrix_rowsums(const igraph_spmatrix_t *m, igraph_vector_t *res); - -IGRAPH_EXPORT int igraph_spmatrix_print(const igraph_spmatrix_t *matrix); -IGRAPH_EXPORT int igraph_spmatrix_fprint(const igraph_spmatrix_t *matrix, FILE* file); - - -typedef struct s_spmatrix_iter { - const igraph_spmatrix_t *m; /* pointer to the matrix we are iterating over */ - long int pos; /* internal index into the data vector */ - long int ri; /* row index */ - long int ci; /* column index */ - igraph_real_t value; /* value at the given cell */ -} igraph_spmatrix_iter_t; - -IGRAPH_EXPORT int igraph_spmatrix_iter_create(igraph_spmatrix_iter_t *mit, const igraph_spmatrix_t *m); -IGRAPH_EXPORT int igraph_spmatrix_iter_reset(igraph_spmatrix_iter_t *mit); -IGRAPH_EXPORT int igraph_spmatrix_iter_next(igraph_spmatrix_iter_t *mit); -IGRAPH_EXPORT igraph_bool_t igraph_spmatrix_iter_end(igraph_spmatrix_iter_t *mit); -IGRAPH_EXPORT void igraph_spmatrix_iter_destroy(igraph_spmatrix_iter_t *mit); - -__END_DECLS - -#endif diff --git a/src/vendor/cigraph/include/igraph_stack.h b/src/vendor/cigraph/include/igraph_stack.h index a186296ff4b..30f9b44ac28 100644 --- a/src/vendor/cigraph/include/igraph_stack.h +++ b/src/vendor/cigraph/include/igraph_stack.h @@ -25,6 +25,7 @@ #define IGRAPH_STACK_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -39,12 +40,6 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_LONG -#include "igraph_pmt.h" -#include "igraph_stack_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_LONG - #define BASE_INT #include "igraph_pmt.h" #include "igraph_stack_pmt.h" @@ -63,16 +58,13 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_BOOL -#define BASE_PTR -#include "igraph_pmt.h" -#include "igraph_stack_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_PTR - #define IGRAPH_STACK_NULL { 0,0,0 } - -IGRAPH_EXPORT void igraph_stack_ptr_free_all(igraph_stack_ptr_t* s); -IGRAPH_EXPORT void igraph_stack_ptr_destroy_all(igraph_stack_ptr_t* s); +#define IGRAPH_STACK_INIT_FINALLY(s, capacity) \ + do { IGRAPH_CHECK(igraph_stack_init(s, capacity)); \ + IGRAPH_FINALLY(igraph_stack_destroy, s); } while (0) +#define IGRAPH_STACK_INT_INIT_FINALLY(s, capacity) \ + do { IGRAPH_CHECK(igraph_stack_int_init(s, capacity)); \ + IGRAPH_FINALLY(igraph_stack_int_destroy, s); } while (0) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_stack_pmt.h b/src/vendor/cigraph/include/igraph_stack_pmt.h index 1a9459b690f..18055332027 100644 --- a/src/vendor/cigraph/include/igraph_stack_pmt.h +++ b/src/vendor/cigraph/include/igraph_stack_pmt.h @@ -34,14 +34,14 @@ typedef struct TYPE(igraph_stack) { BASE* end; } TYPE(igraph_stack); -IGRAPH_EXPORT int FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, long int size); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity); IGRAPH_EXPORT void FUNCTION(igraph_stack, destroy)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT int FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, long int size); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT long int FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); IGRAPH_EXPORT void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s); -IGRAPH_EXPORT int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem); IGRAPH_EXPORT BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s); IGRAPH_EXPORT BASE FUNCTION(igraph_stack, top)(const TYPE(igraph_stack)* s); -IGRAPH_EXPORT int FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); -IGRAPH_EXPORT int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack)* s, FILE *file); diff --git a/src/vendor/cigraph/include/igraph_statusbar.h b/src/vendor/cigraph/include/igraph_statusbar.h index c3cf4f33ba0..d3bc8995403 100644 --- a/src/vendor/cigraph/include/igraph_statusbar.h +++ b/src/vendor/cigraph/include/igraph_statusbar.h @@ -25,6 +25,7 @@ #define IGRAPH_STATUSBAR_H #include "igraph_decls.h" +#include "igraph_error.h" __BEGIN_DECLS @@ -60,15 +61,17 @@ __BEGIN_DECLS * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. + * \return Error code. The current calculation will abort if you return anything + * else than \c IGRAPH_SUCCESS here. */ -typedef int igraph_status_handler_t(const char *message, void *data); +typedef igraph_error_t igraph_status_handler_t(const char *message, void *data); IGRAPH_EXPORT extern igraph_status_handler_t igraph_status_handler_stderr; -IGRAPH_EXPORT igraph_status_handler_t * igraph_set_status_handler(igraph_status_handler_t new_handler); +IGRAPH_EXPORT igraph_status_handler_t *igraph_set_status_handler(igraph_status_handler_t new_handler); -IGRAPH_EXPORT int igraph_status(const char *message, void *data); +IGRAPH_EXPORT igraph_error_t igraph_status(const char *message, void *data); /** * \define IGRAPH_STATUS @@ -95,7 +98,7 @@ IGRAPH_EXPORT int igraph_status(const char *message, void *data); } \ } while (0) -IGRAPH_EXPORT int igraph_statusf(const char *message, void *data, ...); +IGRAPH_EXPORT igraph_error_t igraph_statusf(const char *message, void *data, ...); /** * \define IGRAPH_STATUSF diff --git a/src/vendor/cigraph/include/igraph_structural.h b/src/vendor/cigraph/include/igraph_structural.h index 100aa476e62..bdfce8cb90c 100644 --- a/src/vendor/cigraph/include/igraph_structural.h +++ b/src/vendor/cigraph/include/igraph_structural.h @@ -25,14 +25,14 @@ #define IGRAPH_STRUCTURAL_H #include "igraph_decls.h" -#include "igraph_constants.h" -#include "igraph_types.h" -#include "igraph_vector.h" -#include "igraph_matrix.h" #include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_constants.h" #include "igraph_iterators.h" -#include "igraph_attributes.h" +#include "igraph_matrix.h" #include "igraph_sparsemat.h" +#include "igraph_types.h" +#include "igraph_vector.h" __BEGIN_DECLS @@ -40,70 +40,76 @@ __BEGIN_DECLS /* Basic query functions */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es_t es); -IGRAPH_EXPORT int igraph_density(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t igraph_are_connected(const igraph_t *graph, igraph_integer_t v1, igraph_integer_t v2, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es); +IGRAPH_EXPORT igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid); +IGRAPH_EXPORT igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, +IGRAPH_EXPORT igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, const igraph_vs_t vs); -IGRAPH_EXPORT int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, - igraph_vector_t *circle); -IGRAPH_EXPORT int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, +IGRAPH_EXPORT igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, + igraph_vector_int_t *circle); +IGRAPH_EXPORT igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); -IGRAPH_EXPORT int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, +IGRAPH_EXPORT igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); -IGRAPH_EXPORT int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es); -IGRAPH_EXPORT int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, +IGRAPH_EXPORT igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops); +IGRAPH_EXPORT igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops); -IGRAPH_EXPORT int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, +IGRAPH_EXPORT igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, igraph_bool_t ignore_loops, igraph_reciprocity_t mode); -IGRAPH_EXPORT int igraph_strength(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t 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); -IGRAPH_EXPORT int igraph_sort_vertex_ids_by_degree(const igraph_t *graph, - igraph_vector_t *outvids, +IGRAPH_EXPORT igraph_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, + igraph_vector_int_t *outvids, igraph_vs_t vids, igraph_neimode_t mode, igraph_bool_t loops, igraph_order_t order, igraph_bool_t only_indices); +IGRAPH_EXPORT igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect); /* -------------------------------------------------- */ /* Structural properties */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst); -IGRAPH_EXPORT int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, +IGRAPH_EXPORT igraph_error_t igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, +IGRAPH_EXPORT igraph_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vid); -IGRAPH_EXPORT int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real_t vid, +IGRAPH_EXPORT igraph_error_t igraph_subcomponent(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vid, igraph_neimode_t mode); -IGRAPH_EXPORT 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); +IGRAPH_EXPORT igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, + igraph_neimode_t mode, const igraph_vector_int_t *roots, + igraph_vector_int_t *vertex_index); -IGRAPH_EXPORT int igraph_maximum_cardinality_search(const igraph_t *graph, - igraph_vector_t *alpha, - igraph_vector_t *alpham1); -IGRAPH_EXPORT int igraph_is_chordal(const igraph_t *graph, - const igraph_vector_t *alpha, - const igraph_vector_t *alpham1, +IGRAPH_EXPORT igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_int_t *alpha, + igraph_vector_int_t *alpham1); +IGRAPH_EXPORT igraph_error_t igraph_is_chordal(const igraph_t *graph, + const igraph_vector_int_t *alpha, + const igraph_vector_int_t *alpham1, igraph_bool_t *chordal, - igraph_vector_t *fill_in, + igraph_vector_int_t *fill_in, igraph_t *newgraph); -IGRAPH_EXPORT int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_neimode_t mode, igraph_neimode_t neighbor_degree_mode, @@ -111,17 +117,49 @@ IGRAPH_EXPORT int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vector_t *knnk, const igraph_vector_t *weights); -IGRAPH_EXPORT int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, +IGRAPH_EXPORT igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_t *result, const igraph_vector_t *weights, igraph_fas_algorithm_t algo); /* -------------------------------------------------- */ /* Spectral Properties */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_laplacian(const igraph_t *graph, igraph_matrix_t *res, - igraph_sparsemat_t *sparseres, - igraph_bool_t normalized, - const igraph_vector_t *weights); +/** + * \typedef igraph_laplacian_normalization_t + * \brief Normalization methods for a Laplacian matrix. + * + * Normalization methods for \ref igraph_get_laplacian() and + * \ref igraph_get_laplacian_sparse(). In the following, \c A refers to the + * (possibly weighted) adjacency matrix and \c D is a diagonal matrix containing + * degrees (unweighted case) or strengths (weighted case). Out-, in- or total degrees + * are used according to the \p mode parameter. + * + * \enumval IGRAPH_LAPLACIAN_UNNORMALIZED Unnormalized Laplacian, L = D - A. + * \enumval IGRAPH_LAPLACIAN_SYMMETRIC Symmetric normalized Laplacian, L = I - D^(-1/2) A D^(-1/2). + * \enumval IGRAPH_LAPLACIAN_LEFT Left-stochastic normalized Laplacian, L = I - D^-1 A. + * \enumval IGRAPH_LAPLACIAN_RIGHT Right-stochastic normalized Laplacian, L = I - A D^-1. + */ +typedef enum { + IGRAPH_LAPLACIAN_UNNORMALIZED = 0, + IGRAPH_LAPLACIAN_SYMMETRIC = 1, + IGRAPH_LAPLACIAN_LEFT = 2, + IGRAPH_LAPLACIAN_RIGHT = 3 +} igraph_laplacian_normalization_t; + +IGRAPH_EXPORT igraph_error_t igraph_get_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +); +IGRAPH_EXPORT igraph_error_t igraph_get_laplacian_sparse( + const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, const igraph_vector_t *weights +); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_strvector.h b/src/vendor/cigraph/include/igraph_strvector.h index 525fef8a837..f434ef894e5 100644 --- a/src/vendor/cigraph/include/igraph_strvector.h +++ b/src/vendor/cigraph/include/igraph_strvector.h @@ -25,6 +25,7 @@ #define IGRAPH_STRVECTOR_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -35,62 +36,75 @@ __BEGIN_DECLS */ typedef struct s_igraph_strvector { - char **data; - long int len; + char **stor_begin; + char **stor_end; + char **end; } igraph_strvector_t; /** * \define STR - * Indexing string vectors + * \brief Indexing string vectors. + * + * This is a macro that allows to query the elements of a string vector, just + * like \ref igraph_strvector_get(), but without the overhead of a function + * call. Note this macro cannot be used to set an element. Use + * \ref igraph_strvector_set() to set an element instead. * - * This is a macro which allows to query the elements of a string vector in - * simpler way than \ref igraph_strvector_get(). Note this macro cannot be - * used to set an element, for that use \ref igraph_strvector_set(). * \param sv The string vector * \param i The the index of the element. * \return The element at position \p i. * * Time complexity: O(1). */ -#define STR(sv,i) ((const char *)((sv).data[(i)])) +#define STR(sv,i) ((const char *)((sv).stor_begin[(i)])) -#define IGRAPH_STRVECTOR_NULL { 0,0 } -#define IGRAPH_STRVECTOR_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_strvector_init(v, size)); \ - IGRAPH_FINALLY( igraph_strvector_destroy, v); } while (0) +#define IGRAPH_STRVECTOR_NULL { 0,0,0 } +#define IGRAPH_STRVECTOR_INIT_FINALLY(sv, size) \ + do { IGRAPH_CHECK(igraph_strvector_init(sv, size)); \ + IGRAPH_FINALLY( igraph_strvector_destroy, sv); } while (0) -IGRAPH_EXPORT int igraph_strvector_init(igraph_strvector_t *sv, long int len); +IGRAPH_EXPORT igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t len); IGRAPH_EXPORT void igraph_strvector_destroy(igraph_strvector_t *sv); -IGRAPH_EXPORT long int igraph_strvector_size(const igraph_strvector_t *sv); -IGRAPH_EXPORT void igraph_strvector_get(const igraph_strvector_t *sv, - long int idx, char **value); -IGRAPH_EXPORT int igraph_strvector_set(igraph_strvector_t *sv, long int idx, - const char *value); -IGRAPH_EXPORT int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, - const char *value, int len); +IGRAPH_EXPORT igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv); +IGRAPH_EXPORT igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv); +IGRAPH_EXPORT const char* igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx); +IGRAPH_EXPORT igraph_error_t igraph_strvector_set( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value); +IGRAPH_EXPORT igraph_error_t igraph_strvector_set_len( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len); IGRAPH_EXPORT void igraph_strvector_clear(igraph_strvector_t *sv); -IGRAPH_EXPORT void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, - long int to); -IGRAPH_EXPORT void igraph_strvector_remove(igraph_strvector_t *v, long int elem); -IGRAPH_EXPORT void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, - long int end, long int to); -IGRAPH_EXPORT int igraph_strvector_copy(igraph_strvector_t *to, - const igraph_strvector_t *from); -IGRAPH_EXPORT int igraph_strvector_append(igraph_strvector_t *to, - const igraph_strvector_t *from); -IGRAPH_EXPORT int igraph_strvector_resize(igraph_strvector_t* v, long int newsize); -IGRAPH_EXPORT int igraph_strvector_add(igraph_strvector_t *v, const char *value); -IGRAPH_EXPORT void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, - long int nremove); -IGRAPH_EXPORT void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t *neg, - long int nremove); -IGRAPH_EXPORT int igraph_strvector_print(const igraph_strvector_t *v, FILE *file, +IGRAPH_EXPORT void igraph_strvector_remove_section( + igraph_strvector_t *v, igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT void igraph_strvector_remove( + igraph_strvector_t *v, igraph_integer_t elem); +IGRAPH_EXPORT igraph_error_t igraph_strvector_init_copy( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_append( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_merge( + igraph_strvector_t *to, igraph_strvector_t *from); +IGRAPH_EXPORT igraph_error_t igraph_strvector_resize( + igraph_strvector_t* v, igraph_integer_t newsize); +IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back(igraph_strvector_t *v, + const char *value); +IGRAPH_EXPORT igraph_error_t igraph_strvector_push_back_len(igraph_strvector_t *v, + const char *value, igraph_integer_t len); +IGRAPH_EXPORT igraph_error_t igraph_strvector_print(const igraph_strvector_t *v, FILE *file, const char *sep); -IGRAPH_EXPORT int igraph_strvector_index(const igraph_strvector_t *v, +IGRAPH_EXPORT igraph_error_t igraph_strvector_index(const igraph_strvector_t *v, igraph_strvector_t *newv, - const igraph_vector_t *idx); + const igraph_vector_int_t *idx); + +IGRAPH_EXPORT igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, + igraph_integer_t capacity); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_add(igraph_strvector_t *v, const char *value); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_copy( + igraph_strvector_t *to, const igraph_strvector_t *from); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_strvector_set2( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len +); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_topology.h b/src/vendor/cigraph/include/igraph_topology.h index 90e656b2d5d..3afd1e4f5cf 100644 --- a/src/vendor/cigraph/include/igraph_topology.h +++ b/src/vendor/cigraph/include/igraph_topology.h @@ -27,8 +27,9 @@ #include "igraph_decls.h" #include "igraph_constants.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS @@ -36,10 +37,10 @@ __BEGIN_DECLS /* Directed acyclic graphs */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_topological_sorting(const igraph_t *graph, igraph_vector_t *res, - igraph_neimode_t mode); -IGRAPH_EXPORT int igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); -IGRAPH_EXPORT int igraph_transitive_closure_dag(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_topological_sorting( + const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_is_dag(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure); /* -------------------------------------------------- */ @@ -47,22 +48,22 @@ IGRAPH_EXPORT int igraph_transitive_closure_dag(const igraph_t *graph, /* -------------------------------------------------- */ /* Common functions */ -IGRAPH_EXPORT int igraph_simplify_and_colorize( +IGRAPH_EXPORT igraph_error_t igraph_simplify_and_colorize( const igraph_t *graph, igraph_t *res, igraph_vector_int_t *vertex_color, igraph_vector_int_t *edge_color); /* Generic interface */ -IGRAPH_EXPORT int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); -IGRAPH_EXPORT int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); /* LAD */ -IGRAPH_EXPORT 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); +IGRAPH_EXPORT igraph_error_t igraph_subisomorphic_lad( + const igraph_t *pattern, const igraph_t *target, const igraph_vector_int_list_t *domains, + igraph_bool_t *iso, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, + igraph_bool_t induced, igraph_integer_t time_limit +); /* VF2 family*/ /** @@ -70,18 +71,21 @@ IGRAPH_EXPORT int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph * Callback type, called when an isomorphism was found * * See the details at the documentation of \ref - * igraph_isomorphic_function_vf2(). + * igraph_get_isomorphisms_vf2_callback(). * \param map12 The mapping from the first graph to the second. * \param map21 The mapping from the second graph to the first, the * inverse of \p map12 basically. * \param arg This extra argument was passed to \ref - * igraph_isomorphic_function_vf2() when it was called. - * \return Boolean, whether to continue with the isomorphism search. + * igraph_get_isomorphisms_vf2_callback() when it was called. + * \return \c IGRAPH_SUCCESS to continue the search, \c IGRAPH_STOP to + * terminate the search. Any other return value is interpreted as an + * igraph error code, which will then abort the search and return the + * same error code from the caller function. */ -typedef igraph_bool_t igraph_isohandler_t(const igraph_vector_t *map12, - const igraph_vector_t *map21, void *arg); +typedef igraph_error_t igraph_isohandler_t(const igraph_vector_int_t *map12, + const igraph_vector_int_t *map21, void *arg); /** * \typedef igraph_isocompat_t @@ -113,28 +117,18 @@ typedef igraph_bool_t igraph_isocompat_t(const igraph_t *graph1, const igraph_integer_t g2_num, void *arg); -IGRAPH_EXPORT int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *map12, + igraph_vector_int_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t 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, @@ -143,41 +137,47 @@ IGRAPH_EXPORT int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const ig igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT int igraph_get_isomorphisms_vf2(const igraph_t *graph1, +IGRAPH_EXPORT igraph_error_t 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_vector_int_list_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); - -IGRAPH_EXPORT int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t igraph_get_isomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +/* Deprecated alias to igraph_get_isomorphisms_vf2_callback(), will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *map12, + igraph_vector_int_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT 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); -IGRAPH_EXPORT int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t 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, @@ -186,25 +186,44 @@ IGRAPH_EXPORT int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); -IGRAPH_EXPORT int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, +IGRAPH_EXPORT igraph_error_t 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_vector_int_list_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg); +IGRAPH_EXPORT igraph_error_t igraph_get_subisomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); + +/* Deprecated alias to igraph_get_subisomorphisms_vf2_callback(), will be removed in 0.11 */ +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +); /* BLISS family */ /** * \struct igraph_bliss_info_t - * Information about a BLISS run + * \brief Information about a Bliss run. * - * Some secondary information found by the BLISS algorithm is stored + * Some secondary information found by the Bliss algorithm is stored * here. It is useful if you wany to study the internal working of the * algorithm. + * * \member nof_nodes The number of nodes in the search tree. * \member nof_leaf_nodes The number of leaf nodes in the search tree. * \member nof_bad_nodes Number of bad nodes. @@ -215,7 +234,7 @@ IGRAPH_EXPORT int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, * given as a string. It should be deallocated via * \ref igraph_free() if not needed any more. * - * See http://www.tcs.hut.fi/Software/bliss/index.html + * See https://users.aalto.fi/~tjunttil/bliss/ * for details about the algorithm and these parameters. */ typedef struct igraph_bliss_info_t { @@ -253,30 +272,43 @@ typedef enum { IGRAPH_BLISS_F = 0, IGRAPH_BLISS_FL, IGRAPH_BLISS_FLM, IGRAPH_BLISS_FSM } igraph_bliss_sh_t; -IGRAPH_EXPORT int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_t *labeling, +IGRAPH_EXPORT igraph_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_t *labeling, igraph_bliss_sh_t sh, igraph_bliss_info_t *info); -IGRAPH_EXPORT int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t 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_bool_t *iso, igraph_vector_int_t *map12, + igraph_vector_int_t *map21, igraph_bliss_sh_t sh, igraph_bliss_info_t *info1, igraph_bliss_info_t *info2); -IGRAPH_EXPORT int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, - igraph_bliss_sh_t sh, igraph_bliss_info_t *info); +IGRAPH_EXPORT igraph_error_t igraph_count_automorphisms( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); -IGRAPH_EXPORT 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_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_automorphisms( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info); -/* Functions for 3-4 graphs */ -IGRAPH_EXPORT int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, +IGRAPH_EXPORT igraph_error_t igraph_automorphism_group( + const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh, + igraph_bliss_info_t *info +); + +/* Functions for small graphs (<= 4 vertices for directed graphs, <= 6 for undirected graphs) */ +IGRAPH_EXPORT igraph_error_t igraph_isomorphic_small(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso); -IGRAPH_EXPORT int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); -IGRAPH_EXPORT int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, +IGRAPH_EXPORT igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass); +IGRAPH_EXPORT igraph_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, igraph_integer_t *isoclass); -IGRAPH_EXPORT int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, +IGRAPH_EXPORT igraph_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed); +IGRAPH_EXPORT igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count); + +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_isomorphic_34( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +); diff --git a/src/vendor/cigraph/include/igraph_transitivity.h b/src/vendor/cigraph/include/igraph_transitivity.h index 33aee5410ac..9566561f6a2 100644 --- a/src/vendor/cigraph/include/igraph_transitivity.h +++ b/src/vendor/cigraph/include/igraph_transitivity.h @@ -27,36 +27,32 @@ #include "igraph_decls.h" #include "igraph_datatype.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_iterators.h" __BEGIN_DECLS -IGRAPH_EXPORT int igraph_transitivity_undirected(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_local_undirected(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_local_undirected1(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_local_undirected2(const igraph_t *graph, - igraph_vector_t *res, - const igraph_vs_t vids, - igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_local_undirected4(const igraph_t *graph, - igraph_vector_t *res, - igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_avglocal_undirected(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode); -IGRAPH_EXPORT int igraph_transitivity_barrat(const igraph_t *graph, +IGRAPH_EXPORT igraph_error_t igraph_transitivity_barrat(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, const igraph_vector_t *weights, const igraph_transitivity_mode_t mode); +IGRAPH_EXPORT igraph_error_t igraph_ecc(const igraph_t *graph, + igraph_vector_t *res, + igraph_es_t eids, + igraph_integer_t k, + igraph_bool_t offset, + igraph_bool_t normalize); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_typed_list_pmt.h b/src/vendor/cigraph/include/igraph_typed_list_pmt.h new file mode 100644 index 00000000000..20ebba392ee --- /dev/null +++ b/src/vendor/cigraph/include/igraph_typed_list_pmt.h @@ -0,0 +1,119 @@ +/* -*- 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 + +*/ + +#if defined(VECTOR_LIST) + /* It was indicated that every item in a list is a vector of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_VECTOR +#elif defined(MATRIX_LIST) + /* It was indicated that every item in a list is a matrix of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_MATRIX +#else + #define ITEM_TYPE BASE +#endif + +/** + * Vector list, dealing with lists of typed vectors efficiently. + * \ingroup types + */ + +typedef struct { + ITEM_TYPE* stor_begin; + ITEM_TYPE* stor_end; + ITEM_TYPE* end; +#ifdef EXTRA_TYPE_FIELDS + EXTRA_TYPE_FIELDS +#endif +} TYPE; + +/*--------------------*/ +/* Allocation */ +/*--------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size); +IGRAPH_EXPORT void FUNCTION(destroy)(TYPE* v); + +/*--------------------*/ +/* Accessing elements */ +/*--------------------*/ + +IGRAPH_EXPORT ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos); +IGRAPH_EXPORT void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v); + +/*-----------------*/ +/* List properties */ +/*-----------------*/ + +IGRAPH_EXPORT igraph_integer_t FUNCTION(capacity)(const TYPE* v); +IGRAPH_EXPORT igraph_bool_t FUNCTION(empty)(const TYPE* v); +IGRAPH_EXPORT igraph_integer_t FUNCTION(size)(const TYPE* v); + +/*------------------------*/ +/* Resizing operations */ +/*------------------------*/ + +IGRAPH_EXPORT void FUNCTION(clear)(TYPE* v); +IGRAPH_EXPORT igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size); + +/*------------------------*/ +/* Adding/removing items */ +/*------------------------*/ + +IGRAPH_EXPORT void FUNCTION(discard)(TYPE* v, igraph_integer_t index); +IGRAPH_EXPORT void FUNCTION(discard_back)(TYPE* v); +IGRAPH_EXPORT void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** result); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** result); +IGRAPH_EXPORT ITEM_TYPE FUNCTION(pop_back)(TYPE* v); +IGRAPH_EXPORT igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); +IGRAPH_EXPORT igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* e); +IGRAPH_EXPORT void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e); +IGRAPH_EXPORT void FUNCTION(remove_consecutive_duplicates)(TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*)); + +/*------------------*/ +/* Exchanging items */ +/*------------------*/ + +IGRAPH_EXPORT igraph_error_t FUNCTION(permute)(TYPE *v, const igraph_vector_int_t *index); +IGRAPH_EXPORT igraph_error_t FUNCTION(reverse)(TYPE *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(swap_elements)(TYPE* v, igraph_integer_t i, igraph_integer_t j); + +/*-----------*/ +/* Sorting */ +/*-----------*/ + +IGRAPH_EXPORT void FUNCTION(sort)( + TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)); +IGRAPH_EXPORT igraph_error_t FUNCTION(sort_ind)( + TYPE *v, igraph_vector_int_t *ind, + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) +); + +#undef ITEM_TYPE diff --git a/src/vendor/cigraph/include/igraph_types.h b/src/vendor/cigraph/include/igraph_types.h index 07e4befaa67..18eb9b69d37 100644 --- a/src/vendor/cigraph/include/igraph_types.h +++ b/src/vendor/cigraph/include/igraph_types.h @@ -32,43 +32,125 @@ __BEGIN_DECLS #define _GNU_SOURCE 1 #endif -#include "igraph_error.h" -#include +#ifdef __cplusplus + #define __STDC_FORMAT_MACROS /* needed for PRId32 and PRId64 from inttypes.h on Linux */ +#endif + +#include "igraph_config.h" + +#include #include +#include +#include #include -typedef int igraph_integer_t; + +#if !defined(IGRAPH_INTEGER_SIZE) +# error "igraph integer size not defined; check the value of IGRAPH_INTEGER_SIZE when compiling" +#elif IGRAPH_INTEGER_SIZE == 64 +typedef int64_t igraph_integer_t; +typedef uint64_t igraph_uint_t; +#elif IGRAPH_INTEGER_SIZE == 32 +typedef int32_t igraph_integer_t; +typedef uint32_t igraph_uint_t; +#else +# error "Invalid igraph integer size; check the value of IGRAPH_INTEGER_SIZE when compiling" +#endif + typedef double igraph_real_t; -typedef int igraph_bool_t; + +/* IGRAPH_BOOL_TYPE is set to 'bool' by default, and it is not meant to be + * overridden, except for the R interface where we know what we are doing. + * See igraph_config.h for more info */ +typedef IGRAPH_BOOL_TYPE igraph_bool_t; /* printf format specifier for igraph_integer_t */ -#define IGRAPH_PRId "d" +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_PRId PRId64 +# define IGRAPH_PRIu PRIu64 +#else +# define IGRAPH_PRId PRId32 +# define IGRAPH_PRIu PRIu32 +#endif + +/* maximum and minimum allowed values for igraph_integer_t */ +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_INTEGER_MAX INT64_MAX +# define IGRAPH_INTEGER_MIN INT64_MIN +#else +# define IGRAPH_INTEGER_MAX INT32_MAX +# define IGRAPH_INTEGER_MIN INT32_MIN +#endif + +/* maximum and minimum allowed values for igraph_uint_t */ +#if IGRAPH_INTEGER_SIZE == 64 +# define IGRAPH_UINT_MAX UINT64_MAX +# define IGRAPH_UINT_MIN UINT64_MIN +#else +# define IGRAPH_UINT_MAX UINT32_MAX +# define IGRAPH_UINT_MIN UINT32_MIN +#endif + + +/** + * \define IGRAPH_VCOUNT_MAX + * \brief The maximum number of vertices supported in igraph graphs. + * + * The value of this constant is one less than \c IGRAPH_INTEGER_MAX . + * When igraph is compiled in 32-bit mode, this means that you are limited + * to 231 – 2 (about 2.1 billion) vertices. In + * 64-bit mode, the limit is 263 – 2 so you are much + * more likely to hit out-of-memory issues due to other reasons before reaching + * this limit. + */ +#define IGRAPH_VCOUNT_MAX (IGRAPH_INTEGER_MAX-1) +/* The 'os' and 'is' vectors in igraph_t have vcount+1 elements, + * thus this cannot currently be larger than IGRAPH_INTEGER_MAX-1 + */ + +/** + * \define IGRAPH_ECOUNT_MAX + * \brief The maximum number of edges supported in igraph graphs. + * + * The value of this constant is half of \c IGRAPH_INTEGER_MAX . + * When igraph is compiled in 32-bit mode, this means that you are limited + * to approximately 230 (about 1.07 billion) + * vertices. In 64-bit mode, the limit is approximately + * 262 so you are much more likely to hit + * out-of-memory issues due to other reasons before reaching this limit. + */ +#define IGRAPH_ECOUNT_MAX (IGRAPH_INTEGER_MAX/2) +/* The endpoints of edges are often stored in a vector twice the length + * of the edge count, thus this cannot be larger than IGRAPH_INTEGER_MAX/2. + * Some of the overflow checking code relies on this. */ /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) */ IGRAPH_EXPORT int igraph_real_printf(igraph_real_t val); IGRAPH_EXPORT int igraph_real_fprintf(FILE *file, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_snprintf(char* str, size_t size, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_printf_aligned(int width, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_snprintf(char *str, size_t size, igraph_real_t val); /* Replacements for printf that print doubles in the same way on all platforms * (even for NaN and infinities) with the largest possible precision */ IGRAPH_EXPORT int igraph_real_printf_precise(igraph_real_t val); IGRAPH_EXPORT int igraph_real_fprintf_precise(FILE *file, igraph_real_t val); -IGRAPH_EXPORT int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val); +IGRAPH_EXPORT int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val); -#define IGRAPH_INFINITY INFINITY -#define IGRAPH_POSINFINITY INFINITY -#define IGRAPH_NEGINFINITY (-INFINITY) +#define IGRAPH_INFINITY ((double)INFINITY) +#define IGRAPH_POSINFINITY IGRAPH_INFINITY +#define IGRAPH_NEGINFINITY (-IGRAPH_INFINITY) -IGRAPH_EXPORT int igraph_finite(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_finite(double x); #define IGRAPH_FINITE(x) igraph_finite(x) -IGRAPH_EXPORT int igraph_is_nan(double x); -IGRAPH_EXPORT int igraph_is_inf(double x); -IGRAPH_EXPORT int igraph_is_posinf(double x); -IGRAPH_EXPORT int igraph_is_neginf(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_nan(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_inf(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_posinf(double x); +IGRAPH_DEPRECATED IGRAPH_EXPORT int igraph_is_neginf(double x); -#define IGRAPH_NAN NAN +#define IGRAPH_NAN ((double)NAN) __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_vector.h b/src/vendor/cigraph/include/igraph_vector.h index 80773289643..567c835b882 100644 --- a/src/vendor/cigraph/include/igraph_vector.h +++ b/src/vendor/cigraph/include/igraph_vector.h @@ -24,9 +24,11 @@ #ifndef IGRAPH_VECTOR_H #define IGRAPH_VECTOR_H +#include "igraph_complex.h" +#include "igraph_constants.h" #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" -#include "igraph_complex.h" __BEGIN_DECLS @@ -40,18 +42,6 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_FLOAT -#include "igraph_pmt.h" -#include "igraph_vector_type.h" -#include "igraph_pmt_off.h" -#undef BASE_FLOAT - -#define BASE_LONG -#include "igraph_pmt.h" -#include "igraph_vector_type.h" -#include "igraph_pmt_off.h" -#undef BASE_LONG - #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_type.h" @@ -82,18 +72,6 @@ __BEGIN_DECLS #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_FLOAT -#include "igraph_pmt.h" -#include "igraph_vector_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_FLOAT - -#define BASE_LONG -#include "igraph_pmt.h" -#include "igraph_vector_pmt.h" -#include "igraph_pmt_off.h" -#undef BASE_LONG - #define BASE_CHAR #include "igraph_pmt.h" #include "igraph_vector_pmt.h" @@ -146,38 +124,41 @@ __BEGIN_DECLS do { IGRAPH_CHECK(igraph_vector_int_init(v, size)); \ IGRAPH_FINALLY(igraph_vector_int_destroy, v); } while (0) #endif -#ifndef IGRAPH_VECTOR_LONG_INIT_FINALLY -#define IGRAPH_VECTOR_LONG_INIT_FINALLY(v, size) \ - do { IGRAPH_CHECK(igraph_vector_long_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_long_destroy, v); } while (0) -#endif /* -------------------------------------------------- */ /* Type-specific vector functions */ /* -------------------------------------------------- */ -IGRAPH_EXPORT int igraph_vector_floor(const igraph_vector_t *from, igraph_vector_long_t *to); -IGRAPH_EXPORT int igraph_vector_round(const igraph_vector_t *from, igraph_vector_long_t *to); - -IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, - const igraph_vector_t *rhs, - igraph_real_t tol); - -IGRAPH_EXPORT int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); - -IGRAPH_EXPORT int igraph_vector_order(const igraph_vector_t* v, const igraph_vector_t *v2, - igraph_vector_t* res, igraph_real_t maxval); -IGRAPH_EXPORT int igraph_vector_order1(const igraph_vector_t* v, - igraph_vector_t* res, igraph_real_t maxval); -IGRAPH_EXPORT int igraph_vector_order1_int(const igraph_vector_t* v, - igraph_vector_int_t* res, igraph_real_t maxval); -IGRAPH_EXPORT int igraph_vector_order2(igraph_vector_t *v); -IGRAPH_EXPORT int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, - long int nodes); -IGRAPH_EXPORT int igraph_vector_is_nan(const igraph_vector_t *v, +IGRAPH_EXPORT igraph_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to); +IGRAPH_EXPORT igraph_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to); + +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t tol); + +IGRAPH_EXPORT igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t eps); + +IGRAPH_EXPORT igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) ; + +IGRAPH_EXPORT igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan); IGRAPH_EXPORT igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_order2(igraph_vector_t *v); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_rank(const igraph_vector_t *v, igraph_vector_int_t *res, + igraph_integer_t nodes); + +IGRAPH_EXPORT igraph_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, const igraph_vector_int_t *v2, + igraph_vector_int_t* res, igraph_integer_t maxval); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, + igraph_vector_int_t* res, igraph_integer_t maxval); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_vector_int_rank(const igraph_vector_int_t *v, igraph_vector_int_t *res, + igraph_integer_t nodes); + __END_DECLS #endif diff --git a/src/vendor/cigraph/include/igraph_vector_list.h b/src/vendor/cigraph/include/igraph_vector_list.h new file mode 100644 index 00000000000..f1d299dc948 --- /dev/null +++ b/src/vendor/cigraph/include/igraph_vector_list.h @@ -0,0 +1,73 @@ +/* -*- 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_VECTOR_LIST_H +#define IGRAPH_VECTOR_LIST_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* Flexible list of vectors */ +/* -------------------------------------------------- */ + +/* Indicate to igraph_typed_list_pmt.h that we are going to work with _vectors_ + * of the base type, not the base type directly */ +#define VECTOR_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#undef VECTOR_LIST + +/* -------------------------------------------------- */ +/* Helper macros */ +/* -------------------------------------------------- */ + +#define IGRAPH_VECTOR_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_BOOL_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_bool_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_bool_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_CHAR_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_char_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_char_list_destroy, v); } while (0) +#define IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_vector_int_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_vector_int_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/include/igraph_vector_pmt.h b/src/vendor/cigraph/include/igraph_vector_pmt.h index 1962d29256f..04e0707dc7e 100644 --- a/src/vendor/cigraph/include/igraph_vector_pmt.h +++ b/src/vendor/cigraph/include/igraph_vector_pmt.h @@ -25,19 +25,23 @@ /* Allocation */ /*--------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, long int size); -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_copy)(TYPE(igraph_vector)* v, - const BASE* data, long int length); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init)( + TYPE(igraph_vector)* v, igraph_integer_t size); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_array)( + TYPE(igraph_vector)* v, const BASE* data, igraph_integer_t length); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_copy)( + TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); #ifndef NOTORDERED -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector)*v, BASE start, BASE end); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector)*v, BASE from, BASE to); #endif -IGRAPH_EXPORT int FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, - const TYPE(igraph_vector) *from); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, copy)( + TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); IGRAPH_EXPORT void FUNCTION(igraph_vector, destroy)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v); /*--------------------*/ /* Accessing elements */ @@ -65,9 +69,11 @@ IGRAPH_EXPORT long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vecto #define VECTOR(v) ((v).stor_begin) #endif -IGRAPH_EXPORT BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, long int pos); -IGRAPH_EXPORT BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, long int pos); -IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, long int pos, BASE value); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT IGRAPH_DEPRECATED BASE* FUNCTION(igraph_vector, e_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT BASE* FUNCTION(igraph_vector, get_ptr)(const TYPE(igraph_vector)* v, igraph_integer_t pos); +IGRAPH_EXPORT void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, BASE value); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v); /*-----------------------*/ @@ -77,33 +83,39 @@ IGRAPH_EXPORT BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT void FUNCTION(igraph_vector, null)(TYPE(igraph_vector)* v); IGRAPH_EXPORT void FUNCTION(igraph_vector, fill)(TYPE(igraph_vector)* v, BASE e); +#ifndef NOTORDERED +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector)*v, BASE start, BASE end); +#endif + /*-----------------------*/ /* Vector views */ /*-----------------------*/ IGRAPH_EXPORT const TYPE(igraph_vector) *FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v, const BASE *data, - long int length); + igraph_integer_t length); /*-----------------------*/ /* Copying vectors */ /*-----------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE* to); -IGRAPH_EXPORT int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); -IGRAPH_EXPORT int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); -IGRAPH_EXPORT int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2); /*-----------------------*/ /* Exchanging elements */ /*-----------------------*/ -IGRAPH_EXPORT int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, - long int i, long int j); -IGRAPH_EXPORT int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, swap_elements)( + TYPE(igraph_vector) *v, igraph_integer_t i, igraph_integer_t j); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector) *v, + const igraph_vector_int_t *ind); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); /*-----------------------*/ /* Vector operations */ @@ -111,19 +123,19 @@ IGRAPH_EXPORT int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v); IGRAPH_EXPORT void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus); IGRAPH_EXPORT void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by); -IGRAPH_EXPORT int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2); -IGRAPH_EXPORT int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from); #ifndef NOABS - IGRAPH_EXPORT int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); + IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v); #endif /*------------------------------*/ @@ -141,10 +153,12 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_ve const TYPE(igraph_vector) *rhs); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, - const void *rhs); -IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, - const void *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs); +IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs); #endif /*------------------------------*/ @@ -154,20 +168,20 @@ IGRAPH_EXPORT int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, #ifndef NOTORDERED IGRAPH_EXPORT BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, - BASE *min, BASE *max); -IGRAPH_EXPORT int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, - long int *which_min, long int *which_max); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT void FUNCTION(igraph_vector, minmax)( + const TYPE(igraph_vector) *v, BASE *min, BASE *max); +IGRAPH_EXPORT void FUNCTION(igraph_vector, which_minmax)( + const TYPE(igraph_vector) *v, igraph_integer_t *which_min, igraph_integer_t *which_max); #endif /*-------------------*/ /* Vector properties */ /*-------------------*/ -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v); -IGRAPH_EXPORT long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, empty)(const TYPE(igraph_vector)* v); +IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_vector, size)(const TYPE(igraph_vector)* v); IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, sum)(const TYPE(igraph_vector) *v); IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, sumsq)(const TYPE(igraph_vector) *v); @@ -190,17 +204,16 @@ IGRAPH_EXPORT igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(ig /*------------------------*/ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, BASE e); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, - long int from, BASE what, - long int *pos); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, search)( + const TYPE(igraph_vector) *v, igraph_integer_t from, BASE what, igraph_integer_t *pos); #ifndef NOTORDERED -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) *v, - BASE what, long int *pos, - long int start, long int end); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, - BASE what, long int *pos); -IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, - BASE what); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)( + const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos, + igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch)( + const TYPE(igraph_vector) *v, BASE what, igraph_integer_t *pos); +IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)( + const TYPE(igraph_vector) *v, BASE what); #endif /*------------------------*/ @@ -208,15 +221,21 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igrap /*------------------------*/ IGRAPH_EXPORT void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize); -IGRAPH_EXPORT int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, long int size); -IGRAPH_EXPORT int FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, resize)( + TYPE(igraph_vector)* v, igraph_integer_t new_size); +IGRAPH_EXPORT void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, reserve)( + TYPE(igraph_vector)* v, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e); IGRAPH_EXPORT BASE FUNCTION(igraph_vector, pop_back)(TYPE(igraph_vector)* v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, BASE value); -IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem); -IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, - long int from, long int to); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, insert)( + TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove)( + TYPE(igraph_vector) *v, igraph_integer_t elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_fast)( + TYPE(igraph_vector) *v, igraph_integer_t elem); +IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)( + TYPE(igraph_vector) *v, igraph_integer_t from, igraph_integer_t to); /*-----------*/ /* Sorting */ @@ -226,8 +245,8 @@ IGRAPH_EXPORT void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) * IGRAPH_EXPORT void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v); IGRAPH_EXPORT void FUNCTION(igraph_vector, reverse_sort)(TYPE(igraph_vector) *v); -IGRAPH_EXPORT long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v, - igraph_vector_t *inds, igraph_bool_t descending); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, qsort_ind)( + const TYPE(igraph_vector) *v, igraph_vector_int_t *inds, igraph_order_t order); #endif @@ -235,56 +254,65 @@ IGRAPH_EXPORT long int FUNCTION(igraph_vector, qsort_ind)(TYPE(igraph_vector) *v /* Printing */ /*-----------*/ -IGRAPH_EXPORT int FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); -IGRAPH_EXPORT int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, - const char *format); -IGRAPH_EXPORT int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file); -#ifdef BASE_COMPLEX +#ifdef OUT_FORMAT +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format); +#endif /* OUT_FORMAT */ -IGRAPH_EXPORT int igraph_vector_complex_real(const igraph_vector_complex_t *v, - igraph_vector_t *real); -IGRAPH_EXPORT int igraph_vector_complex_imag(const igraph_vector_complex_t *v, - igraph_vector_t *imag); -IGRAPH_EXPORT int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, - igraph_vector_t *real, - igraph_vector_t *imag); -IGRAPH_EXPORT int igraph_vector_complex_create(igraph_vector_complex_t *v, - const igraph_vector_t *real, - const igraph_vector_t *imag); -IGRAPH_EXPORT int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, - const igraph_vector_t *r, - const igraph_vector_t *theta); +/*----------------------------------------*/ +/* Operations specific to complex vectors */ +/*----------------------------------------*/ -#endif +#ifdef BASE_COMPLEX -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); -IGRAPH_EXPORT int FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); - -IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, - long int begin, long int end, long int to); -IGRAPH_EXPORT int FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, - long int begin, long int end, long int to); -IGRAPH_EXPORT void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v, - const igraph_vector_t *index, - long int nremove); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, + igraph_vector_t *real); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, + igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_realimag(const igraph_vector_complex_t *v, + igraph_vector_t *real, + igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create(igraph_vector_complex_t *v, + const igraph_vector_t *real, + const igraph_vector_t *imag); +IGRAPH_EXPORT igraph_error_t igraph_vector_complex_create_polar(igraph_vector_complex_t *v, + const igraph_vector_t *r, + const igraph_vector_t *theta); + +IGRAPH_EXPORT igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, + const igraph_vector_complex_t *rhs, + igraph_real_t eps); + +#endif /* BASE_COMPLEX */ + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector)*v, int no, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector)*v, double endmark, ...); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector)*v, int endmark, ...); + +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, move_interval)( + TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, + igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t FUNCTION(igraph_vector, move_interval2)( + TYPE(igraph_vector) *v, igraph_integer_t begin, igraph_integer_t end, + igraph_integer_t to); #ifndef NOTORDERED -IGRAPH_EXPORT int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem); #endif -IGRAPH_EXPORT int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, - TYPE(igraph_vector) *res, - long int from, long int to); +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, get_interval)( + const TYPE(igraph_vector) *v, TYPE(igraph_vector) *res, + igraph_integer_t from, igraph_integer_t to); #ifndef NOTORDERED -IGRAPH_EXPORT int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); -IGRAPH_EXPORT int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); #endif -IGRAPH_EXPORT int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, - const igraph_vector_t *idx); + const igraph_vector_int_t *idx); -IGRAPH_EXPORT int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, +IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, const igraph_vector_int_t *idx); diff --git a/src/vendor/cigraph/include/igraph_vector_ptr.h b/src/vendor/cigraph/include/igraph_vector_ptr.h index c2667be29ee..ec498a4e5b0 100644 --- a/src/vendor/cigraph/include/igraph_vector_ptr.h +++ b/src/vendor/cigraph/include/igraph_vector_ptr.h @@ -25,6 +25,7 @@ #define IGRAPH_VECTOR_PTR_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_vector.h" __BEGIN_DECLS @@ -48,39 +49,43 @@ typedef struct s_vector_ptr { #define IGRAPH_VECTOR_PTR_NULL { 0,0,0,0 } #define IGRAPH_VECTOR_PTR_INIT_FINALLY(v, size) \ do { IGRAPH_CHECK(igraph_vector_ptr_init(v, size)); \ - IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0) + IGRAPH_FINALLY(igraph_vector_ptr_destroy, v); } while (0) -IGRAPH_EXPORT int igraph_vector_ptr_init (igraph_vector_ptr_t* v, long int size); -IGRAPH_EXPORT int igraph_vector_ptr_init_copy (igraph_vector_ptr_t* v, void** data, long int length); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t* v, void *const *data, igraph_integer_t length); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); IGRAPH_EXPORT const igraph_vector_ptr_t *igraph_vector_ptr_view (const igraph_vector_ptr_t *v, - void *const *data, long int length); -IGRAPH_EXPORT void igraph_vector_ptr_destroy (igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_free_all (igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_destroy_all (igraph_vector_ptr_t* v); -IGRAPH_EXPORT int igraph_vector_ptr_reserve (igraph_vector_ptr_t* v, long int size); -IGRAPH_EXPORT igraph_bool_t igraph_vector_ptr_empty (const igraph_vector_ptr_t* v); -IGRAPH_EXPORT long int igraph_vector_ptr_size (const igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_clear (igraph_vector_ptr_t* v); -IGRAPH_EXPORT void igraph_vector_ptr_null (igraph_vector_ptr_t* v); -IGRAPH_EXPORT int igraph_vector_ptr_push_back (igraph_vector_ptr_t* v, void* e); -IGRAPH_EXPORT int igraph_vector_ptr_append (igraph_vector_ptr_t *to, - const igraph_vector_ptr_t *from); -IGRAPH_EXPORT void *igraph_vector_ptr_pop_back (igraph_vector_ptr_t *v); -IGRAPH_EXPORT int igraph_vector_ptr_insert(igraph_vector_ptr_t *v, long int pos, void* e); -IGRAPH_EXPORT void* igraph_vector_ptr_e (const igraph_vector_ptr_t* v, long int pos); -IGRAPH_EXPORT void igraph_vector_ptr_set (igraph_vector_ptr_t* v, long int pos, void* value); -IGRAPH_EXPORT int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize); + void *const *data, igraph_integer_t length); +IGRAPH_EXPORT void igraph_vector_ptr_destroy(igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_free_all(igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_destroy_all(igraph_vector_ptr_t* v); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v); +IGRAPH_EXPORT igraph_integer_t igraph_vector_ptr_size(const igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_clear(igraph_vector_ptr_t* v); +IGRAPH_EXPORT void igraph_vector_ptr_null(igraph_vector_ptr_t* v); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, + const igraph_vector_ptr_t *from); +IGRAPH_EXPORT void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t *v, igraph_integer_t pos, void* e); +IGRAPH_EXPORT IGRAPH_DEPRECATED void* igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos); +IGRAPH_EXPORT void* igraph_vector_ptr_get(const igraph_vector_ptr_t* v, igraph_integer_t pos); +IGRAPH_EXPORT void igraph_vector_ptr_set(igraph_vector_ptr_t* v, igraph_integer_t pos, void* value); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize); IGRAPH_EXPORT void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to); -IGRAPH_EXPORT int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); -IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index); +IGRAPH_EXPORT void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t pos); IGRAPH_EXPORT void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(const void*, const void*)); -IGRAPH_EXPORT int igraph_vector_ptr_index_int(igraph_vector_ptr_t *v, - const igraph_vector_int_t *idx); +IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_sort_ind( + igraph_vector_ptr_t *v, igraph_vector_int_t *inds, int(*compar)(const void*, const void*)); IGRAPH_EXPORT igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector_ptr_t *v); IGRAPH_EXPORT igraph_finally_func_t* igraph_vector_ptr_set_item_destructor(igraph_vector_ptr_t *v, igraph_finally_func_t *func); +IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from); + /** * \define IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR * \brief Sets the item destructor for this pointer vector (macro version). diff --git a/src/vendor/cigraph/include/igraph_version.h.in b/src/vendor/cigraph/include/igraph_version.h.in index cae3cfa1148..fda42278d82 100644 --- a/src/vendor/cigraph/include/igraph_version.h.in +++ b/src/vendor/cigraph/include/igraph_version.h.in @@ -34,10 +34,10 @@ __BEGIN_DECLS #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); +IGRAPH_EXPORT void igraph_version(const char **version_string, + int *major, + int *minor, + int *subminor); __END_DECLS diff --git a/src/vendor/cigraph/include/igraph_visitor.h b/src/vendor/cigraph/include/igraph_visitor.h index 936ff654bf3..c57cb2bed06 100644 --- a/src/vendor/cigraph/include/igraph_visitor.h +++ b/src/vendor/cigraph/include/igraph_visitor.h @@ -26,6 +26,7 @@ #include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" #include "igraph_types.h" #include "igraph_datatype.h" @@ -37,12 +38,13 @@ __BEGIN_DECLS /** * \typedef igraph_bfshandler_t - * Callback type for BFS function + * \brief Callback type for BFS function. * * \ref igraph_bfs() is able to call a callback function, whenever a * new vertex is found, while doing the breadth-first search. This * callback function must be of type \c igraph_bfshandler_t. It has * the following arguments: + * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the breadth-first @@ -58,16 +60,18 @@ __BEGIN_DECLS * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_bfs(). - * \return A logical value, if TRUE (=non-zero), that is interpreted - * as a request to stop the BFS and return to the caller. If a BFS - * is terminated like this, then all elements of the result vectors + * \return \c IGRAPH_SUCCESS if the BFS should continue, \c IGRAPH_STOP + * if the BFS should stop and return to the caller normally. Any other + * value is treated as an igraph error code, terminating the search and + * returning to the caller with the same error code. If a BFS is + * is terminated prematurely, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain NaN. * * \sa \ref igraph_bfs() */ -typedef igraph_bool_t igraph_bfshandler_t(const igraph_t *graph, +typedef igraph_error_t igraph_bfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t pred, igraph_integer_t succ, @@ -75,28 +79,29 @@ typedef igraph_bool_t igraph_bfshandler_t(const igraph_t *graph, igraph_integer_t dist, void *extra); -IGRAPH_EXPORT int igraph_bfs(const igraph_t *graph, - igraph_integer_t root, const igraph_vector_t *roots, +IGRAPH_EXPORT igraph_error_t igraph_bfs(const igraph_t *graph, + igraph_integer_t root, const igraph_vector_int_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, + const igraph_vector_int_t *restricted, + igraph_vector_int_t *order, igraph_vector_int_t *rank, + igraph_vector_int_t *parents, + igraph_vector_int_t *pred, igraph_vector_int_t *succ, + igraph_vector_int_t *dist, igraph_bfshandler_t *callback, void *extra); -IGRAPH_EXPORT 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_EXPORT igraph_error_t igraph_bfs_simple(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_vector_int_t *order, igraph_vector_int_t *layers, + igraph_vector_int_t *parents); /** * \function igraph_dfshandler_t - * Callback type for the DFS function + * \brief Callback type for the DFS function. * * \ref igraph_dfs() is able to call a callback function, whenever a * new vertex is discovered, and/or whenever a subtree is * completed. These callbacks must be of type \c * igraph_dfshandler_t. They have the following arguments: + * * \param graph The graph that that algorithm is working on. Of course * this must not be modified. * \param vid The id of the vertex just found by the depth-first @@ -105,25 +110,27 @@ IGRAPH_EXPORT int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igrap * from the root of the current search tree. * \param extra The extra argument that was passed to \ref * igraph_dfs(). - * \return A logical value, if TRUE (=non-zero), that is interpreted - * as a request to stop the DFS and return to the caller. If a DFS - * is terminated like this, then all elements of the result vectors + * \return \c IGRAPH_SUCCESS if the DFS should continue, \c IGRAPH_STOP + * if the DFS should stop and return to the caller normally. Any other + * value is treated as an igraph error code, terminating the search and + * returning to the caller with the same error code. If a BFS is + * is terminated prematurely, then all elements of the result vectors * that were not yet calculated at the point of the termination * contain NaN. * * \sa \ref igraph_dfs() */ -typedef igraph_bool_t igraph_dfshandler_t(const igraph_t *graph, +typedef igraph_error_t igraph_dfshandler_t(const igraph_t *graph, igraph_integer_t vid, igraph_integer_t dist, void *extra); -IGRAPH_EXPORT int igraph_dfs(const igraph_t *graph, igraph_integer_t root, +IGRAPH_EXPORT igraph_error_t 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_vector_int_t *order, + igraph_vector_int_t *order_out, igraph_vector_int_t *parents, + igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, igraph_dfshandler_t *out_callback, void *extra); diff --git a/src/vendor/cigraph/interfaces/CMakeLists.txt b/src/vendor/cigraph/interfaces/CMakeLists.txt index 1cff0ec9616..57a89d582f6 100644 --- a/src/vendor/cigraph/interfaces/CMakeLists.txt +++ b/src/vendor/cigraph/interfaces/CMakeLists.txt @@ -21,7 +21,7 @@ if(STIMULUS_COMMAND) add_custom_target( check_stimulus - COMMAND test_stimulus + COMMAND test_stimulus DEPENDS test_stimulus COMMENT "Running C++ checker for Stimulus function and type specifications..." ) diff --git a/src/vendor/cigraph/interfaces/functions.yaml b/src/vendor/cigraph/interfaces/functions.yaml index 54623c61318..5110b77175c 100644 --- a/src/vendor/cigraph/interfaces/functions.yaml +++ b/src/vendor/cigraph/interfaces/functions.yaml @@ -14,17 +14,28 @@ igraph_empty: PARAMS: OUT GRAPH graph, INTEGER n=0, BOOLEAN directed=True igraph_add_edges: - PARAMS: INOUT GRAPH graph, VECTOR edges, ATTRIBUTES attr + PARAMS: INOUT GRAPH graph, VERTEX_INDEX_PAIRS edges, ATTRIBUTES attr + DEPS: edges ON graph igraph_add_vertices: PARAMS: INOUT GRAPH graph, INTEGER nv, ATTRIBUTES attr +igraph_copy: + PARAMS: OUT GRAPH to, IN GRAPH from + igraph_delete_edges: - PARAMS: INOUT GRAPH graph, EDGESET edges + PARAMS: INOUT GRAPH graph, EDGE_SELECTOR edges DEPS: edges ON graph igraph_delete_vertices: - PARAMS: INOUT GRAPH graph, VERTEXSET vertices + PARAMS: INOUT GRAPH graph, VERTEX_SELECTOR vertices + DEPS: vertices ON graph + +igraph_delete_vertices_idx: + PARAMS: |- + INOUT GRAPH graph, VERTEX_SELECTOR vertices, + OPTIONAL OUT VECTOR_INT idx, + OPTIONAL OUT VECTOR_INT invidx DEPS: vertices ON graph igraph_vcount: @@ -36,7 +47,7 @@ igraph_ecount: RETURN: INTEGER igraph_neighbors: - PARAMS: GRAPH graph, OUT VECTOR neis, INTEGER vid, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VERTEX_INDICES neis, VERTEX vid, NEIMODE mode=ALL igraph_is_directed: PARAMS: GRAPH graph @@ -44,7 +55,7 @@ igraph_is_directed: igraph_degree: PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, BOOLEAN loops DEPS: vids ON graph @@ -52,21 +63,30 @@ igraph_edge: PARAMS: GRAPH graph, INTEGER eid, OUT INTEGER from, OUT INTEGER to igraph_edges: - PARAMS: GRAPH graph, EDGESET eids, OUT VECTOR edges + PARAMS: GRAPH graph, EDGE_SELECTOR eids, OUT VECTOR_INT edges DEPS: eids ON graph +igraph_empty_attrs: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed, ATTRIBUTES attr + igraph_get_eid: PARAMS: |- - GRAPH graph, OUT INTEGER eid, INTEGER from, - INTEGER to, BOOLEAN directed=True, BOOLEAN error=True + GRAPH graph, OUT EDGE eid, VERTEX from, VERTEX to, + BOOLEAN directed=True, BOOLEAN error=True igraph_get_eids: PARAMS: |- - GRAPH graph, OUT VECTOR eids, OPTIONAL VECTOR pairs, - OPTIONAL VECTOR path, BOOLEAN directed=True, BOOLEAN error=True + GRAPH graph, OUT EDGE_INDICES eids, VERTEX_INDEX_PAIRS pairs, + BOOLEAN directed=True, BOOLEAN error=True + DEPS: pairs ON graph + +igraph_get_all_eids_between: + PARAMS: |- + GRAPH graph, OUT EDGE_INDICES eids, VERTEX from, VERTEX to, + BOOLEAN directed=True igraph_incident: - PARAMS: GRAPH graph, OUT VECTOR eids, INTEGER vid, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT EDGE_INDICES eids, VERTEX vid, NEIMODE mode=ALL igraph_is_same_graph: PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN res @@ -76,32 +96,58 @@ igraph_is_same_graph: ####################################### igraph_create: - PARAMS: OUT GRAPH graph, VECTOR edges, INTEGER n=0, BOOLEAN directed=True + PARAMS: OUT GRAPH graph, VECTOR_INT edges, INTEGER n=0, BOOLEAN directed=True igraph_adjacency: - PARAMS: OUT GRAPH graph, MATRIX adjmatrix, ADJACENCYMODE mode=DIRECTED + PARAMS: |- + OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE + +igraph_sparse_adjacency: + # adjmatrix is declared as INOUT because it might be modified during the + # construction to eliminate duplicate elements from the representation + PARAMS: |- + OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, LOOPS loops=ONCE + +igraph_sparse_weighted_adjacency: + # adjmatrix is declared as INOUT because it might be modified during the + # construction to eliminate duplicate elements from the representation + PARAMS: |- + OUT GRAPH graph, INOUT SPARSEMAT adjmatrix, ADJACENCY_MODE mode=DIRECTED, + OUT EDGEWEIGHTS weights, LOOPS loops=ONCE igraph_weighted_adjacency: PARAMS: |- - OUT GRAPH graph, MATRIX adjmatrix, - ADJACENCYMODE mode=DIRECTED, CSTRING attr="weight", - BOOLEAN loops + OUT GRAPH graph, MATRIX adjmatrix, ADJACENCY_MODE mode=DIRECTED, + OUT EDGEWEIGHTS weights, LOOPS loops=ONCE igraph_star: - PARAMS: OUT GRAPH graph, INTEGER n, STARMODE mode=OUT, INTEGER center=0 + PARAMS: OUT GRAPH graph, INTEGER n, STAR_MODE mode=OUT, INTEGER center=0 + +igraph_wheel: + PARAMS: OUT GRAPH graph, INTEGER n, WHEEL_MODE mode=OUT, INTEGER center=0 -igraph_lattice: +igraph_square_lattice: PARAMS: |- - OUT GRAPH graph, VECTOR dimvector, INTEGER nei=1, - BOOLEAN directed=False, BOOLEAN mutual=False, BOOLEAN circular=False + OUT GRAPH graph, VECTOR_INT dimvector, INTEGER nei=1, + BOOLEAN directed=False, BOOLEAN mutual=False, OPTIONAL VECTOR_BOOL periodic + +igraph_triangular_lattice: + PARAMS: |- + OUT GRAPH graph, VECTOR_INT dimvector, BOOLEAN directed=False, BOOLEAN mutual=False igraph_ring: PARAMS: |- OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN mutual=False, BOOLEAN circular=True -igraph_tree: - PARAMS: OUT GRAPH graph, INTEGER n, INTEGER children=2, TREEMODE type=OUT +igraph_kary_tree: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER children=2, TREE_MODE type=OUT + +igraph_symmetric_tree: + PARAMS: OUT GRAPH graph, VECTOR_INT branches, TREE_MODE type=OUT + +igraph_regular_tree: + PARAMS: OUT GRAPH graph, INTEGER h, INTEGER k=3, TREE_MODE type=UNDIRECTED igraph_full: PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False, BOOLEAN loops=False @@ -110,10 +156,10 @@ igraph_full_citation: PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=True igraph_atlas: - PARAMS: OUT GRAPH graph, INT number=0 + PARAMS: OUT GRAPH graph, INTEGER number=0 igraph_extended_chordal_ring: - PARAMS: OUT GRAPH graph, INTEGER nodes, MATRIX W, BOOLEAN directed=False + PARAMS: OUT GRAPH graph, INTEGER nodes, MATRIX_INT W, BOOLEAN directed=False igraph_connect_neighborhood: PARAMS: INOUT GRAPH graph, INTEGER order=2, NEIMODE mode=ALL @@ -128,10 +174,10 @@ igraph_kautz: PARAMS: OUT GRAPH graph, INTEGER m, INTEGER n igraph_famous: - PARAMS: OUT GRAPH graph, CSTRING name="" + PARAMS: OUT GRAPH graph, CSTRING name igraph_lcf_vector: - PARAMS: OUT GRAPH graph, INTEGER n, VECTOR shifts, INTEGER repeats=1 + PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, INTEGER repeats=1 igraph_adjlist: PARAMS: |- @@ -140,14 +186,33 @@ igraph_adjlist: igraph_full_bipartite: PARAMS: |- - OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, INTEGER n1, + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, INTEGER n1, INTEGER n2, BOOLEAN directed=False, NEIMODE mode=ALL +igraph_full_multipartite: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, VECTOR_INT n, + BOOLEAN directed=False, NEIMODE mode=ALL + igraph_realize_degree_sequence: PARAMS: |- - OUT GRAPH graph, VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, + OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg=NULL, EDGE_TYPE_SW allowed_edge_types=SIMPLE, REALIZE_DEGSEQ_METHOD method=SMALLEST +igraph_circulant: + PARAMS: OUT GRAPH graph, INTEGER n, VECTOR_INT shifts, BOOLEAN directed=False + +igraph_generalized_petersen: + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER k + +igraph_turan: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT INDEX_VECTOR types, INTEGER n, INTEGER r + +igraph_weighted_sparsemat: + PARAMS: + OUT GRAPH graph, SPARSEMAT A, BOOLEAN directed, CSTRING attr, BOOLEAN loops=False + ####################################### # Constructors, games ####################################### @@ -155,20 +220,20 @@ igraph_realize_degree_sequence: igraph_barabasi_game: PARAMS: |- OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER m=1, - VECTOR_OR_0 outseq, BOOLEAN outpref=False, REAL A=1.0, + OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL A=1.0, BOOLEAN directed=True, BARABASI_ALGORITHM algo=BAG, - GRAPH_OR_0 start_from=0 + OPTIONAL GRAPH start_from igraph_erdos_renyi_game_gnp: PARAMS: OUT GRAPH graph, INTEGER n, REAL p, BOOLEAN directed=False, BOOLEAN loops=False igraph_erdos_renyi_game_gnm: - PARAMS: OUT GRAPH graph, INTEGER n, REAL m, BOOLEAN directed=False, BOOLEAN loops=False + PARAMS: OUT GRAPH graph, INTEGER n, INTEGER m, BOOLEAN directed=False, BOOLEAN loops=False igraph_degree_sequence_game: PARAMS: |- - OUT GRAPH graph, VECTOR out_deg, VECTOR_OR_0 in_deg, - DEGSEQMODE method=SIMPLE + OUT GRAPH graph, VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, + DEGSEQ_MODE method=CONFIGURATION igraph_growing_random_game: PARAMS: |- @@ -177,7 +242,7 @@ igraph_growing_random_game: igraph_barabasi_aging_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INTEGER m=1, VECTOR_OR_0 outseq, + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, REAL zero_deg_appeal=1.0, REAL zero_age_appeal=0.0, REAL deg_coef=1.0, REAL age_coef=1.0, BOOLEAN directed=True @@ -185,12 +250,12 @@ igraph_barabasi_aging_game: igraph_recent_degree_game: PARAMS: |- OUT GRAPH graph, INTEGER n, REAL power=1.0, INTEGER window=1, - INTEGER m=1, VECTOR_OR_0 outseq, BOOLEAN outpref=False, + INTEGER m=1, OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL zero_appeal=1.0, BOOLEAN directed=True igraph_recent_degree_aging_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, INTEGER m=1, VECTOR_OR_0 outseq, + OUT GRAPH graph, INTEGER nodes, INTEGER m=1, OPTIONAL VECTOR_INT outseq, BOOLEAN outpref=False, REAL pa_exp=1.0, REAL aging_exp=0.0, INTEGER aging_bin=1, INTEGER window=1, REAL zero_appeal=1.0, BOOLEAN directed=True @@ -198,13 +263,13 @@ igraph_callaway_traits_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, INTEGER edges_per_step=1, VECTOR type_dist, MATRIX pref_matrix, - BOOLEAN directed=False, OPTIONAL OUT VECTOR node_type_vec + BOOLEAN directed=False, OPTIONAL OUT VECTOR_INT node_type_vec igraph_establishment_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, INTEGER k=1, VECTOR type_dist, MATRIX pref_matrix, BOOLEAN directed=True, - OPTIONAL OUT VECTOR node_type_vec + OPTIONAL OUT VECTOR_INT node_type_vec igraph_grg_game: PARAMS: |- @@ -215,14 +280,14 @@ igraph_preference_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER types, VECTOR type_dist, BOOLEAN fixed_sizes=False, - MATRIX pref_matrix, OUT VECTOR node_type_vec, + MATRIX pref_matrix, OUT VECTOR_INT node_type_vec, BOOLEAN directed=False, BOOLEAN loops=False igraph_asymmetric_preference_game: PARAMS: |- OUT GRAPH graph, INTEGER nodes, INTEGER out_types, INTEGER in_types, MATRIX type_dist_matrix, MATRIX pref_matrix, - OUT VECTOR node_type_in_vec, OUT VECTOR node_type_out_vec, + OUT VECTOR_INT node_type_out_vec, OUT VECTOR_INT node_type_in_vec, BOOLEAN loops=False igraph_rewire_edges: @@ -247,12 +312,12 @@ igraph_lastcit_game: igraph_cited_type_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, VECTOR types, VECTOR pref, + OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, VECTOR pref, INTEGER edges_per_step=1, BOOLEAN directed=True igraph_citing_cited_type_game: PARAMS: |- - OUT GRAPH graph, INTEGER nodes, VECTOR types, MATRIX pref, + OUT GRAPH graph, INTEGER nodes, INDEX_VECTOR types, MATRIX pref, INTEGER edges_per_step=1, BOOLEAN directed=True igraph_forest_fire_game: @@ -268,7 +333,7 @@ igraph_simple_interconnected_islands_game: igraph_static_fitness_game: PARAMS: |- OUT GRAPH graph, INTEGER no_of_edges, VECTOR fitness_out, - VECTOR_OR_0 fitness_in=NULL, BOOLEAN loops=False, + OPTIONAL VECTOR fitness_in, BOOLEAN loops=False, BOOLEAN multiple=False igraph_static_power_law_game: @@ -299,18 +364,18 @@ igraph_hsbm_list_game: INTERNAL: true PARAMS: |- OUT GRAPH graph, INTEGER n, VECTOR_INT mlist, - VECTORLIST rholist, MATRIXLIST Clist, REAL p + VECTOR_LIST rholist, MATRIX_LIST Clist, REAL p igraph_correlated_game: PARAMS: |- GRAPH old_graph, OUT GRAPH new_graph, - REAL corr, REAL p=edge_density(old.graph), VECTORM1_OR_0 permutation=NULL + REAL corr, REAL p=edge_density(old.graph), OPTIONAL INDEX_VECTOR permutation=NULL igraph_correlated_pair_game: PARAMS: |- OUT GRAPH graph1, OUT GRAPH graph2, INTEGER n, REAL corr, REAL p, BOOLEAN directed=False, - VECTORM1_OR_0 permutation=NULL + OPTIONAL INDEX_VECTOR permutation=NULL igraph_dot_product_game: PARAMS: OUT GRAPH graph, MATRIX vecs, BOOLEAN directed=False @@ -333,7 +398,7 @@ igraph_sample_dirichlet: ####################################### igraph_are_connected: - PARAMS: GRAPH graph, INTEGER v1, INTEGER v2, OUT BOOLEAN res + PARAMS: GRAPH graph, VERTEX v1, VERTEX v2, OUT BOOLEAN res ####################################### # Structural properties @@ -342,130 +407,240 @@ igraph_are_connected: igraph_diameter: PARAMS: |- GRAPH graph, OUT REAL res, OUT INTEGER from, - OUT INTEGER to, OUT VECTOR_OR_0 path, BOOLEAN directed=True, - BOOLEAN unconnected=True + OUT INTEGER to, OPTIONAL OUT VECTOR_INT vertex_path, + OPTIONAL OUT VECTOR_INT edge_path, + BOOLEAN directed=True, BOOLEAN unconnected=True igraph_diameter_dijkstra: PARAMS: |- GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT REAL res, OUT INTEGER from, - OUT INTEGER to, OUT VECTOR_OR_0 path, BOOLEAN directed=True, - BOOLEAN unconnected=True + OUT REAL res, OUT INTEGER from, OUT INTEGER to, + OPTIONAL OUT VECTOR_INT vertex_path, + OPTIONAL OUT VECTOR_INT edge_path, + BOOLEAN directed=True, BOOLEAN unconnected=True DEPS: weights ON graph igraph_closeness: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, - OUT VECTOR_OR_0 reachable_count, + GRAPH graph, OUT VERTEX_QTY res, + OPTIONAL OUT VECTOR_INT reachable_count, OPTIONAL OUT BOOLEAN all_reachable, - VERTEXSET vids=ALL, + VERTEX_SELECTOR vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False DEPS: vids ON graph, weights ON graph, res ON graph vids igraph_closeness_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, - OUT VECTOR_OR_0 reachable_count, + GRAPH graph, OUT VERTEX_QTY res, + OPTIONAL OUT VECTOR_INT reachable_count, OPTIONAL OUT BOOLEAN all_reachable, - VERTEXSET vids=ALL, + VERTEX_SELECTOR vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids -igraph_shortest_paths: +igraph_distances: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, - VERTEXSET to=ALL, NEIMODE mode=OUT + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT DEPS: from ON graph, to ON graph +igraph_distances_cutoff: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, REAL cutoff=-1 + DEPS: from ON graph, to ON graph + +igraph_get_shortest_path: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_bellman_ford: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_dijkstra: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_shortest_path_astar: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, OPTIONAL EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + OPTIONAL ASTAR_HEURISTIC_FUNC heuristic=NULL, EXTRA extra=NULL + DEPS: from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + igraph_get_shortest_paths: PARAMS: |- GRAPH graph, - OPTIONAL OUT VERTEXSETLIST vertices, OPTIONAL OUT EDGESETLIST edges, - VERTEX from, VERTEXSET to=ALL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_LONG predecessors, - OPTIONAL OUT VECTOR_LONG inbound_edges + OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, + VERTEX from, VERTEX_SELECTOR to=ALL, NEIMODE mode=OUT, + OPTIONAL OUT VECTOR_INT parents, + OPTIONAL OUT VECTOR_INT inbound_edges DEPS: edges ON graph, from ON graph, to ON graph igraph_get_all_shortest_paths: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST res, OUT VECTOR nrgeo, - VERTEX from, VERTEXSET to, NEIMODE mode=OUT - DEPS: res ON graph, from ON graph, to ON graph + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, + VERTEX from, VERTEX_SELECTOR to, NEIMODE mode=OUT + DEPS: edges ON graph, from ON graph, to ON graph + +igraph_distances_dijkstra: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph -igraph_shortest_paths_dijkstra: +igraph_distances_dijkstra_cutoff: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, - VERTEXSET to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT, REAL cutoff=-1 DEPS: from ON graph, to ON graph, weights ON graph igraph_get_shortest_paths_dijkstra: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSETLIST vertices, - OPTIONAL OUT EDGESETLIST edges, VERTEX from, VERTEXSET to=ALL, + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_LONG predecessors=0, - OPTIONAL OUT VECTOR_LONG inbound_edges=0 + OPTIONAL OUT VECTOR_INT parents=0, + OPTIONAL OUT VECTOR_INT inbound_edges=0 DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph igraph_get_shortest_paths_bellman_ford: PARAMS: |- - GRAPH graph, OPTIONAL OUT VERTEXSETLIST vertices, - OPTIONAL OUT EDGESETLIST edges, VERTEX from, VERTEXSET to=ALL, + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, - OPTIONAL OUT VECTOR_LONG predecessors=0, - OPTIONAL OUT VECTOR_LONG inbound_edges=0 + OPTIONAL OUT VECTOR_INT parents=0, + OPTIONAL OUT VECTOR_INT inbound_edges=0 DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph igraph_get_all_shortest_paths_dijkstra: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST res, OUT VECTOR nrgeo, - VERTEX from, VERTEXSET to=ALL, EDGEWEIGHTS weights, + GRAPH graph, OPTIONAL OUT VERTEXSET_LIST vertices, + OPTIONAL OUT EDGESET_LIST edges, OPTIONAL OUT VECTOR_INT nrgeo, + VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT - DEPS: weights ON graph, to ON graph, res ON graph, from ON graph + DEPS: |- + weights ON graph, from ON graph, to ON graph, vertices ON graph, edges ON graph -igraph_shortest_paths_bellman_ford: +igraph_distances_bellman_ford: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, - VERTEXSET to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT DEPS: from ON graph, to ON graph, weights ON graph -igraph_shortest_paths_johnson: +igraph_distances_johnson: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET from=ALL, - VERTEXSET to=ALL, EDGEWEIGHTS weights + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights DEPS: from ON graph, to ON graph, weights ON graph +igraph_distances_floyd_warshall: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, + FWALGORITHM method + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_voronoi: + PARAMS: |- + GRAPH graph, OUT VECTOR_INT membership, OUT VECTOR distances, + VERTEX_INDICES generators, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT, VORONOI_TIEBREAKER tiebreaker=RANDOM + DEPS: weights ON graph, generators ON graph + igraph_get_all_simple_paths: PARAMS: |- - GRAPH graph, OUT VERTEXSET_INT res, VERTEX from, - VERTEXSET to=ALL, INTEGER cutoff=-1, NEIMODE mode=OUT + GRAPH graph, OUT VERTEX_INDICES res, VERTEX from, + VERTEX_SELECTOR to=ALL, INTEGER cutoff=-1, NEIMODE mode=OUT DEPS: from ON graph, to ON graph, res ON graph +igraph_get_k_shortest_paths: + PARAMS: |- + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL OUT VERTEXSET_LIST vertex_paths, + OPTIONAL OUT EDGESET_LIST edge_paths, + INTEGER k, VERTEX from, VERTEX to, NEIMODE mode=OUT + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertex_paths ON graph, edge_paths ON graph + +igraph_get_widest_path: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEX_INDICES vertices, OPTIONAL OUT EDGE_INDICES edges, + VERTEX from, VERTEX to, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertices ON graph, edges ON graph + +igraph_get_widest_paths: + PARAMS: |- + GRAPH graph, + OPTIONAL OUT VERTEXSET_LIST vertices, OPTIONAL OUT EDGESET_LIST edges, + VERTEX from, VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights=NULL, + NEIMODE mode=OUT, OPTIONAL OUT VECTOR_INT parents, + OPTIONAL OUT VECTOR_INT inbound_edges + DEPS: |- + from ON graph, to ON graph, weights ON graph, vertices ON graph, + edges ON graph + +igraph_widest_path_widths_dijkstra: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_widest_path_widths_floyd_warshall: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR from=ALL, + VERTEX_SELECTOR to=ALL, EDGEWEIGHTS weights, NEIMODE mode=OUT + DEPS: from ON graph, to ON graph, weights ON graph + +igraph_spanner: + PARAMS: |- + GRAPH graph, OUT EDGE_INDICES spanner, REAL stretch, EDGEWEIGHTS weights + DEPS: weights ON graph + igraph_subcomponent: - # TODO(ntamas): vid is a double; this is correct but this is actually - # a mistake in 0.9.x. This should be fixed in 0.10. - PARAMS: GRAPH graph, OUT VERTEXSET res, REAL vid, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VERTEX_INDICES res, VERTEX vid, NEIMODE mode=ALL DEPS: vid ON graph, res ON graph igraph_betweenness: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, EDGEWEIGHTS weights=NULL DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_betweenness_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, EDGEWEIGHTS weights=NULL, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids +igraph_betweenness_subset: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, + EDGEWEIGHTS weights=NULL + DEPS: |- + vids ON graph, weights ON graph, res ON graph, sources ON graph, targets ON graph + igraph_edge_betweenness: PARAMS: |- GRAPH graph, OUT VECTOR res, BOOLEAN directed=True, @@ -478,15 +653,23 @@ igraph_edge_betweenness_cutoff: EDGEWEIGHTS weights=NULL, REAL cutoff=-1 DEPS: weights ON graph +igraph_edge_betweenness_subset: + PARAMS: |- + GRAPH graph, OUT VERTEX_QTY res, EDGE_SELECTOR eids=ALL, + BOOLEAN directed=True, VERTEX_SELECTOR sources=ALL, VERTEX_SELECTOR targets=ALL, + EDGEWEIGHTS weights=NULL + DEPS: |- + eids ON graph, weights ON graph, res ON graph, sources ON graph, targets ON graph + igraph_harmonic_centrality: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_harmonic_centrality_cutoff: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=OUT, EDGEWEIGHTS weights=NULL, BOOLEAN normalized=False, REAL cutoff=-1 DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -494,39 +677,52 @@ igraph_harmonic_centrality_cutoff: igraph_pagerank: PARAMS: |- GRAPH graph, PAGERANKALGO algo=PRPACK, - OUT VERTEXINDEX vector, OUT REAL value, - VERTEXSET vids=ALL, BOOLEAN directed=True, + OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, REAL damping=0.85, EDGEWEIGHTS weights=NULL, INOUT PAGERANKOPT options=NULL DEPS: |- - vids ON graph, weights ON graph, vector ON graph vids, + vids ON graph, weights ON graph, vector ON graph, options ON algo igraph_personalized_pagerank: PARAMS: |- GRAPH graph, PAGERANKALGO algo=PRPACK, - OUT VERTEXINDEX vector, OUT REAL value, - VERTEXSET vids=ALL, BOOLEAN directed=True, - REAL damping=0.85, VECTOR_OR_0 personalized=NULL, - EDGEWEIGHTS weights=NULL, + OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, OPTIONAL VECTOR personalized, + OPTIONAL EDGEWEIGHTS weights, + INOUT PAGERANKOPT options=NULL + DEPS: |- + vids ON graph, weights ON graph, vector ON graph vids, + options ON algo + +igraph_personalized_pagerank_vs: + PARAMS: |- + GRAPH graph, PAGERANKALGO algo=PRPACK, + PRIMARY OUT VERTEX_QTY vector, OUT REAL value, + VERTEX_SELECTOR vids=ALL, BOOLEAN directed=True, + REAL damping=0.85, + VERTEX_SELECTOR reset_vids, + OPTIONAL EDGEWEIGHTS weights=NULL, INOUT PAGERANKOPT options=NULL DEPS: |- vids ON graph, weights ON graph, vector ON graph vids, options ON algo igraph_rewire: - PARAMS: INOUT GRAPH rewire, INTEGER n, REWIRINGMODE mode=SIMPLE + PARAMS: INOUT GRAPH rewire, INTEGER n, REWIRING_MODE mode=SIMPLE igraph_induced_subgraph: - PARAMS: GRAPH graph, OUT GRAPH res, VERTEXSET vids, SUBGRAPH_IMPL impl=AUTO + PARAMS: GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl=AUTO DEPS: vids ON graph -igraph_subgraph_edges: - PARAMS: GRAPH graph, OUT GRAPH res, EDGESET eids, BOOLEAN delete_vertices=True +igraph_subgraph_from_edges: + PARAMS: GRAPH graph, OUT GRAPH res, EDGE_SELECTOR eids, BOOLEAN delete_vertices=True DEPS: eids ON graph igraph_reverse_edges: - PARAMS: INOUT GRAPH graph, EDGESET eids=ALL + PARAMS: INOUT GRAPH graph, EDGE_SELECTOR eids=ALL DEPS: eids ON graph igraph_average_path_length: @@ -550,32 +746,39 @@ igraph_simplify: EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default igraph_transitivity_undirected: - PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITYMODE mode=NAN + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN igraph_transitivity_local_undirected: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, TRANSITIVITYMODE mode=NAN + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, TRANSITIVITY_MODE mode=NAN + DEPS: vids ON graph igraph_transitivity_avglocal_undirected: - PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITYMODE mode=NAN + PARAMS: GRAPH graph, OUT REAL res, TRANSITIVITY_MODE mode=NAN igraph_transitivity_barrat: PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, - EDGEWEIGHTS weights=NULL, TRANSITIVITYMODE mode=NAN + GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, + EDGEWEIGHTS weights=NULL, TRANSITIVITY_MODE mode=NAN DEPS: res ON graph, vids ON graph, weights ON graph +igraph_ecc: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR eids=ALL, + INTEGER k=3, BOOLEAN offset=False, BOOLEAN normalize=True + DEPS: res ON graph, eids ON graph + igraph_reciprocity: PARAMS: |- GRAPH graph, OUT REAL res, BOOLEAN ignore_loops=True, - RECIP mode=Default + RECIP mode=DEFAULT igraph_constraint: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL, VECTOR_OR_0 weights - DEPS: vids ON graph + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL, OPTIONAL EDGEWEIGHTS weights + DEPS: vids ON graph, weights ON graph igraph_maxdegree: PARAMS: |- - GRAPH graph, OUT INTEGER res, VERTEXSET vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT INTEGER res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=True DEPS: vids ON graph @@ -584,45 +787,48 @@ igraph_density: igraph_neighborhood_size: PARAMS: |- - GRAPH graph, OUT VECTOR res, VERTEXSET vids, INTEGER order, + GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: vids ON graph igraph_neighborhood: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST res, - VERTEXSET vids, INTEGER order, + GRAPH graph, OUT VERTEXSET_LIST res, + VERTEX_SELECTOR vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: res ON graph, vids ON graph igraph_neighborhood_graphs: PARAMS: |- - GRAPH graph, OUT GRAPHLIST res, VERTEXSET vids, + GRAPH graph, OUT GRAPH_LIST res, VERTEX_SELECTOR vids, INTEGER order, NEIMODE mode=ALL, INTEGER mindist=0 DEPS: vids ON graph igraph_topological_sorting: - PARAMS: GRAPH graph, OUT VECTOR res, NEIMODE mode=OUT + PARAMS: GRAPH graph, OUT VECTOR_INT res, NEIMODE mode=OUT igraph_feedback_arc_set: # Default algorithm is the approximate method because it is faster and the # function is _not_ called igraph_minimum_feedback_arc_set - PARAMS: GRAPH graph, OUT EDGESET result, EDGEWEIGHTS weights=NULL, FAS_ALGORITHM algo=APPROX_EADES + PARAMS: GRAPH graph, OUT EDGE_INDICES result, EDGEWEIGHTS weights=NULL, FAS_ALGORITHM algo=APPROX_EADES DEPS: result ON graph, weights ON graph igraph_is_loop: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL DEPS: es ON graph igraph_is_dag: PARAMS: GRAPH graph, OUT BOOLEAN res +igraph_is_acyclic: + PARAMS: GRAPH graph, OUT BOOLEAN res + igraph_is_simple: PARAMS: GRAPH graph, OUT BOOLEAN res igraph_is_multiple: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL DEPS: es ON graph igraph_has_loop: @@ -632,19 +838,22 @@ igraph_has_multiple: PARAMS: GRAPH graph, OUT BOOLEAN res igraph_count_multiple: - PARAMS: GRAPH graph, OUT VECTOR res, EDGESET es=ALL + PARAMS: GRAPH graph, OUT VECTOR_INT res, EDGE_SELECTOR es=ALL DEPS: es ON graph igraph_girth: - PARAMS: GRAPH graph, OUT INTEGER girth, OUT VERTEXSET circle + PARAMS: GRAPH graph, OUT REAL girth, OUT VERTEX_INDICES circle DEPS: circle ON graph +igraph_is_perfect: + PARAMS: GRAPH graph, OUT BOOLEAN res + igraph_add_edge: PARAMS: INOUT GRAPH graph, INTEGER from, INTEGER to igraph_eigenvector_centrality: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, + GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, BOOLEAN directed=False, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults @@ -652,68 +861,58 @@ igraph_eigenvector_centrality: igraph_hub_score: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, + GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults DEPS: weights ON graph, vector ON graph igraph_authority_score: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX vector, OUT REAL value, + GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=arpack_defaults DEPS: weights ON graph, vector ON graph -igraph_arpack_rssolve: - # TODO(ntamas): this should probably not be exposed to higher-level - # interfaces; igraph's goal is not to provide an ARPACK wrapper +igraph_hub_and_authority_scores: PARAMS: |- - ARPACKFUNC fun, EXTRA extra, INOUT ARPACKOPT options=arpack_defaults, - INOUT ARPACKSTORAGE storage, OPTIONAL OUT VECTOR values, OPTIONAL OUT MATRIX vectors - -igraph_arpack_rnsolve: - # TODO(ntamas): this should probably not be exposed to higher-level - # interfaces; igraph's goal is not to provide an ARPACK wrapper - PARAMS: |- - ARPACKFUNC fun, EXTRA extra, INOUT ARPACKOPT options=arpack_defaults, - INOUT ARPACKSTORAGE storage, OPTIONAL OUT MATRIX values, OPTIONAL OUT MATRIX vectors - -igraph_arpack_unpack_complex: - # TODO(ntamas): this should probably not be exposed to higher-level - # interfaces; igraph's goal is not to provide an ARPACK wrapper - PARAMS: INOUT MATRIX vectors, INOUT MATRIX values, LONGINT nev + GRAPH graph, OUT VERTEX_QTY hub_vector, OUT VERTEX_QTY authority_vector, + OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, + INOUT ARPACKOPT options=arpack_defaults igraph_unfold_tree: PARAMS: |- - GRAPH graph, OUT GRAPH tree, NEIMODE mode=ALL, VECTOR roots, - OUT VECTORM1_OR_0 vertex_index + GRAPH graph, OUT GRAPH tree, NEIMODE mode=ALL, VECTOR_INT roots, + OPTIONAL OUT INDEX_VECTOR vertex_index igraph_is_mutual: - PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGESET es=ALL + PARAMS: GRAPH graph, OUT VECTOR_BOOL res, EDGE_SELECTOR es=ALL, BOOLEAN loops=True DEPS: es ON graph +igraph_has_mutual: + PARAMS: GRAPH graph, OUT BOOLEAN res, BOOLEAN loops=True + igraph_maximum_cardinality_search: - PARAMS: GRAPH graph, OPTIONAL OUT VECTORM1 alpha, OPTIONAL OUT VERTEXSET alpham1 + PARAMS: GRAPH graph, OPTIONAL OUT INDEX_VECTOR alpha, OPTIONAL OUT VERTEX_INDICES alpham1 DEPS: alpham1 ON graph igraph_is_chordal: PARAMS: |- - GRAPH graph, VECTORM1 alpha=NULL, OPTIONAL VECTORM1 alpham1=NULL, - OPTIONAL OUT BOOLEAN chordal, OUT VECTORM1_OR_0 fillin, - OUT GRAPH_OR_0 newgraph + GRAPH graph, OPTIONAL INDEX_VECTOR alpha=NULL, OPTIONAL VERTEX_INDICES alpham1=NULL, + OPTIONAL OUT BOOLEAN chordal, OPTIONAL OUT VECTOR_INT fillin, + OPTIONAL OUT GRAPH newgraph DEPS: alpham1 ON graph igraph_avg_nearest_neighbor_degree: PARAMS: |- - GRAPH graph, VERTEXSET vids=ALL, + GRAPH graph, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, NEIMODE neighbor_degree_mode=ALL, - OPTIONAL OUT VERTEXINDEX knn, OUT VECTOR_OR_0 knnk, + OPTIONAL OUT VERTEX_QTY knn, OPTIONAL OUT VECTOR knnk, EDGEWEIGHTS weights=NULL DEPS: vids ON graph, weights ON graph, knn ON graph vids igraph_strength: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=True, EDGEWEIGHTS weights=NULL DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -733,7 +932,7 @@ igraph_centralization_degree_tmax: # should not have a default value; see this comment from @torfason: # https://github.com/igraph/rigraph/issues/369#issuecomment-939893681 PARAMS: |- - GRAPH_OR_0 graph=NULL, INTEGER nodes=0, NEIMODE mode=ALL, + OPTIONAL GRAPH graph, INTEGER nodes=0, NEIMODE mode=ALL, BOOLEAN loops, OUT REAL res igraph_centralization_betweenness: @@ -746,7 +945,7 @@ igraph_centralization_betweenness: igraph_centralization_betweenness_tmax: PARAMS: |- - GRAPH_OR_0 graph=NULL, INTEGER nodes=0, + OPTIONAL GRAPH graph, INTEGER nodes=0, BOOLEAN directed=True, OUT REAL res igraph_centralization_closeness: @@ -758,7 +957,7 @@ igraph_centralization_closeness: igraph_centralization_closeness_tmax: PARAMS: |- - GRAPH_OR_0 graph=NULL, INTEGER nodes=0, + OPTIONAL GRAPH graph, INTEGER nodes=0, NEIMODE mode=OUT, OUT REAL res igraph_centralization_eigenvector_centrality: @@ -771,52 +970,80 @@ igraph_centralization_eigenvector_centrality: igraph_centralization_eigenvector_centrality_tmax: PARAMS: |- - GRAPH_OR_0 graph=NULL, INTEGER nodes=0, + OPTIONAL GRAPH graph, INTEGER nodes=0, BOOLEAN directed=False, BOOLEAN scale=True, OUT REAL res igraph_assortativity_nominal: PARAMS: |- - GRAPH graph, VECTORM1 types, OUT REAL res, - BOOLEAN directed=True + GRAPH graph, INDEX_VECTOR types, OUT REAL res, + BOOLEAN directed=True, BOOLEAN normalized=True igraph_assortativity: PARAMS: |- - GRAPH graph, VECTOR types1, VECTOR_OR_0 types2=NULL, - OUT REAL res, BOOLEAN directed=True + GRAPH graph, VECTOR values, OPTIONAL VECTOR values_in, + OUT REAL res, BOOLEAN directed=True, BOOLEAN normalized=True igraph_assortativity_degree: PARAMS: GRAPH graph, OUT REAL res, BOOLEAN directed=True igraph_contract_vertices: PARAMS: |- - INOUT GRAPH graph, VECTORM1 mapping, + INOUT GRAPH graph, INDEX_VECTOR mapping, VERTEX_ATTRIBUTE_COMBINATION vertex_attr_comb=Default igraph_eccentricity: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL DEPS: vids ON graph, res ON graph vids +igraph_eccentricity_dijkstra: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, + NEIMODE mode=ALL + DEPS: weights ON graph, vids ON graph, res ON graph vids + +igraph_graph_center: + PARAMS: |- + GRAPH graph, OUT VERTEX_INDICES res, NEIMODE mode=ALL + DEPS: res ON graph + igraph_radius: PARAMS: GRAPH graph, OUT REAL radius, NEIMODE mode=ALL +igraph_pseudo_diameter: + NAME-R: pseudo_diameter + PARAMS: |- + GRAPH graph, OUT REAL diameter, VERTEX start_vid, + OUT INTEGER from=NULL, OUT INTEGER to=NULL, + BOOLEAN directed=True, BOOLEAN unconnected=True + +igraph_pseudo_diameter_dijkstra: + NAME-R: pseudo_diameter + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT REAL diameter, VERTEX start_vid, + OUT INTEGER from=NULL, OUT INTEGER to=NULL, + BOOLEAN directed=True, BOOLEAN unconnected=True + DEPS: weights ON graph + igraph_diversity: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEXINDEX res, - VERTEXSET vids=ALL + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_QTY res, + VERTEX_SELECTOR vids=ALL DEPS: weights ON graph, vids ON graph, res ON graph vids igraph_random_walk: PARAMS: |- - GRAPH graph, OUT VERTEXSET walk, VERTEX start, NEIMODE mode=OUT, - INTEGER steps, RWSTUCK stuck=RETURN - DEPS: start ON graph, walk ON graph + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT VERTEX_INDICES vertices, OUT EDGE_INDICES edges, + VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN + DEPS: start ON graph, weights ON graph, vertices ON graph, edges ON graph igraph_random_edge_walk: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, OUT EDGESET edgewalk, + GRAPH graph, EDGEWEIGHTS weights=NULL, OUT EDGE_INDICES edgewalk, VERTEX start, NEIMODE mode=OUT, INTEGER steps, RWSTUCK stuck=RETURN DEPS: start ON graph, weights ON graph, edgewalk ON graph @@ -826,7 +1053,7 @@ igraph_global_efficiency: igraph_local_efficiency: PARAMS: |- - GRAPH graph, OUT VERTEXINDEX res, VERTEXSET vids=ALL, + GRAPH graph, OUT VERTEX_QTY res, VERTEX_SELECTOR vids=ALL, EDGEWEIGHTS weights=NULL, BOOLEAN directed=True, NEIMODE mode=ALL DEPS: vids ON graph, weights ON graph, res ON graph vids @@ -836,47 +1063,53 @@ igraph_average_local_efficiency: BOOLEAN directed=True, NEIMODE mode=ALL DEPS: weights ON graph +igraph_transitive_closure_dag: + PARAMS: GRAPH graph, OUT GRAPH closure + +igraph_trussness: + PARAMS: GRAPH graph, OUT VECTOR_INT trussness + ####################################### # Degree sequences ####################################### igraph_is_bigraphical: PARAMS: |- - VECTOR degrees1, VECTOR degrees2, + VECTOR_INT degrees1, VECTOR_INT degrees2, EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res -igraph_is_degree_sequence: - PARAMS: VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, OUT BOOLEAN res - FLAGS: DEPRECATED - igraph_is_graphical: PARAMS: |- - VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, + VECTOR_INT out_deg, OPTIONAL VECTOR_INT in_deg, EDGE_TYPE_SW allowed_edge_types=SIMPLE, OUT BOOLEAN res -igraph_is_graphical_degree_sequence: - PARAMS: VECTOR out_deg, VECTOR_OR_0 in_deg=NULL, OUT BOOLEAN res - FLAGS: DEPRECATED - ####################################### # Visitors ####################################### igraph_bfs: PARAMS: |- - GRAPH graph, INTEGER root, VECTOR_OR_0 roots, + GRAPH graph, VERTEX root, OPTIONAL VERTEX_INDICES roots, NEIMODE mode=OUT, BOOLEAN unreachable, - VECTOR_OR_0 restricted, - OUT VECTOR_OR_0 order, OUT VECTOR_OR_0 rank, - OUT VECTOR_OR_0 father, - OUT VECTOR_OR_0 pred, OUT VECTOR_OR_0 succ, - OUT VECTOR_OR_0 dist, BFS_FUNC callback, EXTRA extra + VERTEX_INDICES restricted, + OUT VERTEX_INDICES order, OUT VECTOR_INT rank, + OUT VECTOR_INT parents, + OUT VECTOR_INT pred, OUT VECTOR_INT succ, + OUT VECTOR_INT dist, BFS_FUNC callback, EXTRA extra + +igraph_bfs_simple: + PARAMS: |- + GRAPH graph, VERTEX root, + NEIMODE mode=OUT, + OUT VERTEX_INDICES order, + OUT VECTOR_INT layers, + OUT VECTOR_INT parents igraph_dfs: PARAMS: |- - GRAPH graph, INTEGER root, NEIMODE mode=OUT, BOOLEAN unreachable, - OUT VECTOR_OR_0 order, OUT VECTOR_OR_0 order_out, - OUT VECTOR_OR_0 father, OUT VECTOR_OR_0 dist, + GRAPH graph, VERTEX root, NEIMODE mode=OUT, BOOLEAN unreachable, + OUT VERTEX_INDICES order, OUT VERTEX_INDICES order_out, + OUT VECTOR_INT father, OUT VECTOR_INT dist, DFS_FUNC in_callback, DFS_FUNC out_callback, EXTRA extra ####################################### @@ -894,60 +1127,72 @@ igraph_bipartite_projection: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT GRAPH proj1, OUT GRAPH proj2, - OUT VECTOR_OR_0 multiplicity1, - OUT VECTOR_OR_0 multiplicity2, INTEGER probe1=-1 + OPTIONAL OUT VECTOR_INT multiplicity1, + OPTIONAL OUT VECTOR_INT multiplicity2, INTEGER probe1=-1 DEPS: types ON graph igraph_create_bipartite: PARAMS: |- - OUT GRAPH graph, IN VECTOR_BOOL types, - VECTORM1 edges, BOOLEAN directed=False + OUT GRAPH graph, IN BIPARTITE_TYPES types, + VECTOR_INT edges, BOOLEAN directed=False -igraph_incidence: +igraph_biadjacency: PARAMS: |- - OUT GRAPH graph, OUT VECTOR_BOOL types, MATRIX incidence, + OUT GRAPH graph, OUT BIPARTITE_TYPES types, MATRIX incidence, BOOLEAN directed=False, NEIMODE mode=ALL, BOOLEAN multiple=False -igraph_get_incidence: +igraph_get_biadjacency: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, - OUT VECTOR_OR_0 row_ids, OUT VECTOR_OR_0 col_ids + OPTIONAL OUT INDEX_VECTOR row_ids, OPTIONAL OUT INDEX_VECTOR col_ids DEPS: types ON graph igraph_is_bipartite: - PARAMS: GRAPH graph, OUT BOOLEAN res, OUT VECTOR_BOOL_OR_0 type + PARAMS: GRAPH graph, OUT BOOLEAN res, OPTIONAL OUT BIPARTITE_TYPES type igraph_bipartite_game_gnp: PARAMS: |- - OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, - INTEGER n1, INTEGER n2, REAL p, BOOLEAN directed, - NEIMODE mode + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + INTEGER n1, INTEGER n2, REAL p, BOOLEAN directed=False, + NEIMODE mode=ALL igraph_bipartite_game_gnm: PARAMS: |- - OUT GRAPH graph, OUT VECTOR_BOOL_OR_0 types, - INTEGER n1, INTEGER n2, INTEGER m, BOOLEAN directed, - NEIMODE mode + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + INTEGER n1, INTEGER n2, INTEGER m, BOOLEAN directed=False, + NEIMODE mode=ALL + +igraph_bipartite_game: + PARAMS: |- + OUT GRAPH graph, OPTIONAL OUT BIPARTITE_TYPES types, + ERDOS_RENYI_TYPE type, INTEGER n1, INTEGER n2, REAL p=0.0, + INTEGER m=0, BOOLEAN directed=False, NEIMODE mode=ALL + ####################################### # Spectral properties ####################################### -igraph_laplacian: +igraph_get_laplacian: PARAMS: |- - GRAPH graph, OUT MATRIX_OR_0 res, - OUT SPARSEMAT_OR_0 sparseres, BOOLEAN normalized=False, - EDGEWEIGHTS weights=NULL + GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, + LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph + +igraph_get_laplacian_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparseres, NEIMODE mode=OUT, + LAPLACIAN_NORMALIZATION normalization=UNNORMALIZED, EDGEWEIGHTS weights=NULL DEPS: weights ON graph ####################################### # Components ####################################### -igraph_clusters: +igraph_connected_components: PARAMS: |- - GRAPH graph, OUT VECTOR membership, OUT VECTOR csize, + GRAPH graph, PRIMARY OUT VECTOR_INT membership, OUT VECTOR_INT csize, OUT INTEGER no, CONNECTEDNESS mode=WEAK igraph_is_connected: @@ -955,26 +1200,26 @@ igraph_is_connected: igraph_decompose: PARAMS: |- - GRAPH graph, OUT GRAPHLIST components, CONNECTEDNESS mode=WEAK, - LONGINT maxcompno=-1, LONGINT minelements=1 + GRAPH graph, OUT GRAPH_LIST components, CONNECTEDNESS mode=WEAK, + INTEGER maxcompno=-1, INTEGER minelements=1 igraph_articulation_points: - PARAMS: GRAPH graph, OUT VERTEXSET res + PARAMS: GRAPH graph, OUT VERTEX_INDICES res DEPS: res ON graph igraph_biconnected_components: PARAMS: |- GRAPH graph, OUT INTEGER no, - OPTIONAL OUT EDGESETLIST tree_edges, - OPTIONAL OUT EDGESETLIST component_edges, - OPTIONAL OUT VERTEXSETLIST components, - OUT VERTEXSET articulation_points + OPTIONAL OUT EDGESET_LIST tree_edges, + OPTIONAL OUT EDGESET_LIST component_edges, + OPTIONAL OUT VERTEXSET_LIST components, + OUT VERTEX_INDICES articulation_points DEPS: |- tree_edges ON graph, component_edges ON graph, components ON graph, articulation_points ON graph igraph_bridges: - PARAMS: GRAPH graph, OUT EDGESET res + PARAMS: GRAPH graph, OUT EDGE_INDICES res DEPS: res ON graph ####################################### @@ -983,7 +1228,7 @@ igraph_bridges: igraph_cliques: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, + GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph @@ -997,13 +1242,19 @@ igraph_clique_size_hist: GRAPH graph, OUT VECTOR hist, INTEGER min_size=0, INTEGER max_size=0 igraph_largest_cliques: - PARAMS: GRAPH graph, OUT VERTEXSETLIST res + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res DEPS: res ON graph igraph_maximal_cliques: - PARAMS: GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, INTEGER max_size=0 + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph +igraph_maximal_cliques_subset: + PARAMS: |- + GRAPH graph, VERTEX_INDICES subset, PRIMARY OUT VERTEXSET_LIST res, + OUT INTEGER no, OUTFILE outfile=NULL, INTEGER min_size=0, INTEGER max_size=0 + DEPS: subset ON graph, res ON graph + igraph_maximal_cliques_callback: PARAMS: |- GRAPH graph, CLIQUE_FUNC cliquehandler_fn, EXTRA arg, @@ -1026,13 +1277,13 @@ igraph_clique_number: igraph_weighted_cliques: PARAMS: |- - GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSETLIST res, + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res, REAL min_weight=0, REAL max_weight=0, BOOLEAN maximal=False DEPS: vertex_weights ON graph, res ON graph igraph_largest_weighted_cliques: PARAMS: |- - GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSETLIST res + GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT VERTEXSET_LIST res DEPS: vertex_weights ON graph, res ON graph igraph_weighted_clique_number: @@ -1041,16 +1292,16 @@ igraph_weighted_clique_number: igraph_independent_vertex_sets: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST res, INTEGER min_size=0, + GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, INTEGER max_size=0 DEPS: res ON graph igraph_largest_independent_vertex_sets: - PARAMS: GRAPH graph, OUT VERTEXSETLIST res + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res DEPS: res ON graph igraph_maximal_independent_vertex_sets: - PARAMS: GRAPH graph, OUT VERTEXSETLIST res + PARAMS: GRAPH graph, OUT VERTEXSET_LIST res DEPS: res ON graph igraph_independence_number: @@ -1064,20 +1315,20 @@ igraph_layout_random: PARAMS: GRAPH graph, OUT MATRIX res igraph_layout_circle: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET order=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR order=ALL DEPS: order ON graph igraph_layout_star: PARAMS: |- GRAPH graph, OUT MATRIX res, VERTEX center=V(graph)[1], - VECTORM1_OR_0 order=NULL + OPTIONAL INDEX_VECTOR order=NULL DEPS: center ON graph igraph_layout_grid: - PARAMS: GRAPH graph, OUT MATRIX res, LONGINT width=0 + PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0 igraph_layout_grid_3d: - PARAMS: GRAPH graph, OUT MATRIX res, LONGINT width=0, LONGINT height=0 + PARAMS: GRAPH graph, OUT MATRIX res, INTEGER width=0, INTEGER height=0 igraph_layout_fruchterman_reingold: PARAMS: |- @@ -1085,8 +1336,8 @@ igraph_layout_fruchterman_reingold: BOOLEAN use_seed=False, INTEGER niter=500, REAL start_temp=sqrt(vcount(graph)), LAYOUT_GRID grid=AUTO, EDGEWEIGHTS weights=NULL, - VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, - VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, DEPRECATED repulserad DEPS: weights ON graph @@ -1096,8 +1347,8 @@ igraph_layout_kamada_kawai: GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, INTEGER maxiter=500, REAL epsilon=0.0, REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, - VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, - VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy DEPS: weights ON graph igraph_layout_lgl: @@ -1109,12 +1360,17 @@ igraph_layout_lgl: igraph_layout_reingold_tilford: PARAMS: |- GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, - OPTIONAL VECTOR roots, OPTIONAL VECTOR rootlevel + OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel igraph_layout_reingold_tilford_circular: PARAMS: |- GRAPH graph, OUT MATRIX res, NEIMODE mode=OUT, - OPTIONAL VECTOR roots, OPTIONAL VECTOR rootlevel + OPTIONAL VERTEX_INDICES roots, OPTIONAL VECTOR_INT rootlevel + +igraph_roots_for_tree_layout: + PARAMS: |- + GRAPH graph, NEIMODE mode=OUT, OUT VERTEX_INDICES roots, ROOTCHOICE heuristic + DEPS: roots ON graph igraph_layout_random_3d: PARAMS: GRAPH graph, OUT MATRIX res @@ -1128,9 +1384,9 @@ igraph_layout_fruchterman_reingold_3d: BOOLEAN use_seed=False, INTEGER niter=500, REAL start_temp=sqrt(vcount(graph)), EDGEWEIGHTS weights=NULL, - VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, - VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, - VECTOR_OR_0 minz=NULL, VECTOR_OR_0 maxz=NULL, + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz, DEPRECATED coolexp, DEPRECATED maxdelta, DEPRECATED area, DEPRECATED repulserad DEPS: weights ON graph @@ -1140,9 +1396,9 @@ igraph_layout_kamada_kawai_3d: GRAPH graph, INOUT MATRIX coords, BOOLEAN use_seed=False, INTEGER maxiter=500, REAL epsilon=0.0, REAL kkconst=vcount(graph), EDGEWEIGHTS weights=NULL, - VECTOR_OR_0 minx=NULL, VECTOR_OR_0 maxx=NULL, - VECTOR_OR_0 miny=NULL, VECTOR_OR_0 maxy=NULL, - VECTOR_OR_0 minz=NULL, VECTOR_OR_0 maxz=NULL + OPTIONAL VECTOR minx, OPTIONAL VECTOR maxx, + OPTIONAL VECTOR miny, OPTIONAL VECTOR maxy, + OPTIONAL VECTOR minz, OPTIONAL VECTOR maxz DEPS: weights ON graph igraph_layout_graphopt: @@ -1155,35 +1411,33 @@ igraph_layout_graphopt: igraph_layout_drl: PARAMS: |- GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - DRL_OPTIONS options=drl_defaults$default, VECTOR_OR_0 weights=NULL, - VECTOR_BOOL_OR_0 fixed=NULL + DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights igraph_layout_drl_3d: PARAMS: |- GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, - DRL_OPTIONS options=drl_defaults$default, VECTOR_OR_0 weights=NULL, - VECTOR_BOOL_OR_0 fixed=NULL + DRL_OPTIONS options=drl_defaults$default, OPTIONAL EDGEWEIGHTS weights igraph_layout_merge_dla: - PARAMS: GRAPHLIST graphs, MATRIXLIST coords, OUT MATRIX res + PARAMS: GRAPH_PTR_LIST graphs, MATRIX_LIST coords, OUT MATRIX res igraph_layout_sugiyama: PARAMS: |- - GRAPH graph, OUT MATRIX res, OUT GRAPH_OR_0 extd_graph, - OUT VECTORM1_OR_0 extd_to_orig_eids, - VECTORM1_OR_0 layers=NULL, - REAL hgap=1, REAL vgap=1, LONGINT maxiter=100, + GRAPH graph, OUT MATRIX res, OPTIONAL OUT GRAPH extd_graph, + OPTIONAL OUT INDEX_VECTOR extd_to_orig_eids, + OPTIONAL INDEX_VECTOR layers=NULL, + REAL hgap=1, REAL vgap=1, INTEGER maxiter=100, EDGEWEIGHTS weights=NULL DEPS: weights ON graph igraph_layout_mds: PARAMS: |- - GRAPH graph, OUT MATRIX res, MATRIX_OR_0 dist=NULL, LONGINT dim=2 + GRAPH graph, OUT MATRIX res, OPTIONAL MATRIX dist, INTEGER dim=2 igraph_layout_bipartite: PARAMS: |- GRAPH graph, BIPARTITE_TYPES types=NULL, OUT MATRIX res, - REAL hgap=1, REAL vgap=1, LONGINT maxiter=100 + REAL hgap=1, REAL vgap=1, INTEGER maxiter=100 DEPS: types ON graph igraph_layout_gem: @@ -1204,101 +1458,146 @@ igraph_layout_davidson_harel: REAL weight_edge_crossings=ECROSSW, REAL weight_node_edge_dist=NEDISTW +igraph_layout_umap: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, + BOOLEAN distances_are_weights=False + +igraph_layout_umap_3d: + PARAMS: |- + GRAPH graph, INOUT MATRIX res, BOOLEAN use_seed=False, + OPTIONAL VECTOR distances=NULL, REAL min_dist=0.0, INTEGER epochs=200, + BOOLEAN distances_are_weights=False + +igraph_layout_umap_compute_weights: + PARAMS: |- + GRAPH graph, VECTOR distances, INOUT VECTOR weights + ####################################### # Cocitation and other similarity measures ####################################### igraph_cocitation: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL DEPS: vids ON graph igraph_bibcoupling: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL DEPS: vids ON graph -igraph_similarity_jaccard: +igraph_similarity_dice: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, BOOLEAN loops=False - DEPS: vids ON graph res, mode ON vids + DEPS: vids ON graph -igraph_similarity_dice: +igraph_similarity_dice_es: PARAMS: |- - GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL, + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, BOOLEAN loops=False - DEPS: vids ON graph + DEPS: es ON graph + +igraph_similarity_dice_pairs: + PARAMS: |- + GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: pairs ON graph igraph_similarity_inverse_log_weighted: - PARAMS: GRAPH graph, OUT MATRIX res, VERTEXSET vids=ALL, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL DEPS: vids ON graph +igraph_similarity_jaccard: + PARAMS: |- + GRAPH graph, OUT MATRIX res, VERTEX_SELECTOR vids=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: vids ON graph res, mode ON vids + +igraph_similarity_jaccard_es: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGE_SELECTOR es=ALL, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: es ON graph + +igraph_similarity_jaccard_pairs: + PARAMS: |- + GRAPH graph, OUT VECTOR res, VERTEX_INDEX_PAIRS pairs, NEIMODE mode=ALL, + BOOLEAN loops=False + DEPS: pairs ON graph + ####################################### # Community structure ####################################### igraph_compare_communities: PARAMS: |- - VECTOR comm1, VECTOR comm2, OUT REAL res, COMMCMP method=VI + VECTOR_INT comm1, VECTOR_INT comm2, OUT REAL res, COMMCMP method=VI igraph_community_spinglass: PARAMS: |- - GRAPH graph, VECTOR_OR_0 weights, OUT REAL modularity, - OUT REAL temperature, OUT VECTOR membership, OUT VECTOR csize, + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT REAL modularity, + OUT REAL temperature, OUT VECTOR_INT membership, OUT VECTOR_INT csize, INTEGER spins=25, BOOLEAN parupdate=False, REAL starttemp=1, REAL stoptemp=0.01, REAL coolfact=0.99, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0, SPINGLASS_IMPLEMENTATION implementation=ORIG, REAL lambda=1.0 + DEPS: weights ON graph igraph_community_spinglass_single: PARAMS: |- - GRAPH graph, VECTOR_OR_0 weights, INTEGER vertex, OUT VECTOR community, - OUT REAL cohesion, OUT REAL adhesion, + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER vertex, + OUT VECTOR_INT community, OUT REAL cohesion, OUT REAL adhesion, OUT INTEGER inner_links, OUT INTEGER outer_links, INTEGER spins=25, SPINCOMMUPDATE update_rule=CONFIG, REAL gamma=1.0 + DEPS: weights ON graph igraph_community_walktrap: PARAMS: |- - GRAPH graph, VECTOR weights, INT steps=4, OUT MATRIX merges, - OUT VECTOR modularity, OUT VECTOR membership + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, INTEGER steps=4, + OUT MATRIX_INT merges, OUT VECTOR modularity, OUT VECTOR_INT membership + DEPS: weights ON graph igraph_community_edge_betweenness: PARAMS: |- - GRAPH graph, OUT VECTOR result, OUT VECTOR edge_betweenness, - OUT MATRIX merges, OUT VECTORM1 bridges, - OUT VECTOR_OR_0 modularity, OUT VECTOR_OR_0 membership, - BOOLEAN directed=True, EDGEWEIGHTS weights=NULL + GRAPH graph, OUT VECTOR_INT result, OPTIONAL OUT VECTOR edge_betweenness, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership, + BOOLEAN directed=True, OPTIONAL EDGEWEIGHTS weights=NULL DEPS: weights ON graph igraph_community_eb_get_merges: PARAMS: |- - GRAPH graph, BOOLEAN directed, VECTOR edges, EDGEWEIGHTS weights=NULL, - OUT MATRIX merges, OUT VECTORM1 bridges, - OUT VECTOR_OR_0 modularity, OUT VECTOR_OR_0 membership + GRAPH graph, BOOLEAN directed, EDGE_INDICES edges, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT INDEX_VECTOR bridges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership DEPS: weights ON graph igraph_community_fastgreedy: PARAMS: |- - GRAPH graph, VECTOR_OR_0 weights, OUT MATRIX merges, OUT VECTOR modularity, - OUT VECTOR_OR_0 membership + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, OUT MATRIX_INT merges, + OPTIONAL OUT VECTOR modularity, OPTIONAL OUT VECTOR_INT membership + DEPS: weights ON graph igraph_community_to_membership: PARAMS: |- - MATRIX merges, INTEGER nodes, INTEGER steps, - OUT VECTOR membership, OUT VECTOR csize + MATRIX_INT merges, INTEGER nodes, INTEGER steps, + OPTIONAL OUT VECTOR_INT membership, OPTIONAL OUT VECTOR_INT csize igraph_le_community_to_membership: PARAMS: |- - MATRIX merges, INTEGER steps, INOUT VECTOR membership, - OUT VECTOR_OR_0 csize + MATRIX_INT merges, INTEGER steps, INOUT VECTOR_INT membership, + OPTIONAL OUT VECTOR_INT csize igraph_modularity: PARAMS: |- - GRAPH graph, VECTOR membership, IN VECTOR_OR_0 weights=NULL, + GRAPH graph, VECTOR_INT membership, OPTIONAL EDGEWEIGHTS weights=NULL, REAL resolution=1.0, BOOLEAN directed=True, OUT REAL modularity + DEPS: weights ON graph igraph_modularity_matrix: PARAMS: |- GRAPH graph, - EDGEWEIGHTS weights=NULL, + OPTIONAL EDGEWEIGHTS weights, REAL resolution=1.0, OUT MATRIX modmat, BOOLEAN directed=True @@ -1306,115 +1605,133 @@ igraph_modularity_matrix: igraph_reindex_membership: PARAMS: |- - INOUT VECTOR membership, OUT VECTOR_OR_0 new_to_old, + INOUT VECTOR_INT membership, OUT INDEX_VECTOR new_to_old, OUT INTEGER nb_clusters igraph_community_leading_eigenvector: PARAMS: |- GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT MATRIX merges, OUT VECTOR membership, + OPTIONAL OUT MATRIX_INT merges, OPTIONAL OUT VECTOR_INT membership, INTEGER steps=-1, INOUT ARPACKOPT options=arpack_defaults, - OUT REAL modularity, BOOLEAN start=False, - OUT VECTOR_OR_0 eigenvalues, - OPTIONAL OUT VECTORLIST eigenvectors, - OUT VECTOR_OR_0 history, + OPTIONAL OUT REAL modularity, BOOLEAN start=False, + OPTIONAL OUT VECTOR eigenvalues, + OPTIONAL OUT VECTOR_LIST eigenvectors, + OPTIONAL OUT VECTOR history, LEVCFUNC callback, EXTRA callback_extra igraph_community_fluid_communities: PARAMS: |- - GRAPH graph, INTEGER no_of_communities, - OUT VECTOR membership, OUT REAL modularity + GRAPH graph, INTEGER no_of_communities, OUT VECTOR_INT membership igraph_community_label_propagation: PARAMS: |- - GRAPH graph, OUT VECTOR membership, EDGEWEIGHTS weights=NULL, - VECTOR_OR_0 initial=NULL, VECTOR_BOOL_OR_0 fixed=NULL, - OUT REAL modularity + GRAPH graph, OUT VECTOR_INT membership, NEIMODE mode=ALL, + OPTIONAL EDGEWEIGHTS weights, OPTIONAL INDEX_VECTOR initial, + OPTIONAL VECTOR_BOOL fixed DEPS: weights ON graph igraph_community_multilevel: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - REAL resolution=1.0, - OUT VECTOR membership, - OUT MATRIX_OR_0 memberships, OUT VECTOR_OR_0 modularity + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, REAL resolution=1.0, + OUT VECTOR_INT membership, OPTIONAL OUT MATRIX_INT memberships, + OPTIONAL OUT VECTOR modularity DEPS: weights ON graph igraph_community_optimal_modularity: PARAMS: |- - GRAPH graph, OUT REAL modularity, - OUT VECTOR_OR_0 membership, - EDGEWEIGHTS weights=NULL + GRAPH graph, OUT REAL modularity, OPTIONAL OUT VECTOR_INT membership, + OPTIONAL EDGEWEIGHTS weights DEPS: weights ON graph igraph_community_leiden: PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - VERTEXWEIGHTS vertex_weights=NULL, - REAL resolution_parameter, REAL beta, - BOOLEAN start, INOUT VECTOR_OR_0 membership, + GRAPH graph, OPTIONAL EDGEWEIGHTS weights, + OPTIONAL VERTEXWEIGHTS vertex_weights, + REAL resolution, REAL beta=0.01, BOOLEAN start, INTEGER n_iterations=2, + OPTIONAL INOUT VECTOR_INT membership, OUT INTEGER nb_clusters, OUT REAL quality DEPS: weights ON graph, vertex_weights ON graph igraph_split_join_distance: PARAMS: |- - VECTOR comm1, VECTOR comm2, OUT INTEGER distance12, + VECTOR_INT comm1, VECTOR_INT comm2, OUT INTEGER distance12, OUT INTEGER distance21 +igraph_community_infomap: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS e_weights=NULL, + VERTEXWEIGHTS v_weights=NULL, INTEGER nb_trials=10, + OUT VECTOR_INT membership, OUT REAL codelength + DEPS: e_weights ON graph, v_weights ON graph + +####################################### +# Graphlets +####################################### + +igraph_graphlets: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSET_LIST cliques, OUT VECTOR Mu, INTEGER niter=1000 + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_candidate_basis: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + OUT VERTEXSET_LIST cliques, OUT VECTOR thresholds + DEPS: weights ON graph, cliques ON graph + +igraph_graphlets_project: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, + VERTEXSET_LIST cliques, INOUT VECTOR Muc, + BOOLEAN startMu=False, INTEGER niter=1000 + DEPS: weights ON graph + +####################################### +# Hierarchical random graphs +####################################### + igraph_hrg_fit: PARAMS: |- GRAPH graph, INOUT HRG hrg=Default, BOOLEAN start=False, - INT steps=0 + INTEGER steps=0 -igraph_hrg_game: - PARAMS: OUT GRAPH graph, HRG hrg +igraph_hrg_sample: + PARAMS: HRG hrg, OUT GRAPH sample -igraph_hrg_dendrogram: +igraph_hrg_sample_many: + PARAMS: HRG hrg, OUT GRAPH_LIST samples, INTEGER num_samples + +igraph_hrg_game: PARAMS: OUT GRAPH graph, HRG hrg igraph_hrg_consensus: PARAMS: |- - GRAPH graph, OUT VECTOR parents, OUT VECTOR weights, + GRAPH graph, OUT VECTOR_INT parents, OUT VECTOR weights, INOUT HRG hrg=Default, BOOLEAN start=False, - INT num_samples=10000 + INTEGER num_samples=10000 igraph_hrg_predict: PARAMS: |- - GRAPH graph, OUT VERTEXSET edges, OUT VECTOR prob, + GRAPH graph, OUT VERTEX_INDICES edges, OUT VECTOR prob, INOUT HRG hrg=Default, BOOLEAN start=False, - INT num_samples=10000, INT num_bins=25 + INTEGER num_samples=10000, INTEGER num_bins=25 DEPS: edges ON graph igraph_hrg_create: PARAMS: OUT HRG hrg, GRAPH graph, VECTOR prob DEPS: prob ON graph -igraph_community_infomap: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS e_weights=NULL, - VERTEXWEIGHTS v_weights=NULL, INT nb_trials=10, - OUT VECTOR membership, OUT REAL codelength - DEPS: e_weights ON graph, v_weights ON graph - -igraph_graphlets: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT VERTEXSETLIST cliques, OUT VECTOR Mu, INT niter=1000 - DEPS: weights ON graph, cliques ON graph +igraph_hrg_resize: + PARAMS: INOUT HRG hrg, INTEGER newsize -igraph_graphlets_candidate_basis: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - OUT VERTEXSETLIST cliques, OUT VECTOR thresholds - DEPS: weights ON graph, cliques ON graph +igraph_hrg_size: + PARAMS: HRG hrg + RETURN: INTEGER -igraph_graphlets_project: - PARAMS: |- - GRAPH graph, EDGEWEIGHTS weights=NULL, - VERTEXSETLIST cliques, INOUT VECTOR Muc, - BOOLEAN startMu=False, INT niter=1000 - DEPS: weights ON graph +igraph_from_hrg_dendrogram: + PARAMS: OUT GRAPH graph, HRG hrg, OUT VECTOR prob ####################################### # Conversion @@ -1423,27 +1740,42 @@ igraph_graphlets_project: igraph_get_adjacency: PARAMS: |- GRAPH graph, OUT MATRIX res, GETADJACENCY type=BOTH, - BOOLEAN eids=False + EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE + DEPS: + weights ON graph + +igraph_get_adjacency_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparsemat, GETADJACENCY type=BOTH, + EDGEWEIGHTS weights=NULL, LOOPS loops=ONCE + DEPS: + weights ON graph igraph_get_edgelist: - PARAMS: GRAPH graph, OUT VECTOR res, BOOLEAN bycol=False + PARAMS: GRAPH graph, OUT VECTOR_INT res, BOOLEAN bycol=False + +igraph_get_stochastic: + PARAMS: |- + GRAPH graph, OUT MATRIX res, BOOLEAN column_wise=False, + EDGEWEIGHTS weights=NULL + DEPS: + weights ON graph + +igraph_get_stochastic_sparse: + PARAMS: |- + GRAPH graph, OUT SPARSEMAT sparsemat, BOOLEAN column_wise=False, + EDGEWEIGHTS weights=NULL + DEPS: + weights ON graph igraph_to_directed: - PARAMS: INOUT GRAPH graph, TODIRECTED flags=MUTUAL + PARAMS: INOUT GRAPH graph, TODIRECTED mode=MUTUAL igraph_to_undirected: PARAMS: |- INOUT GRAPH graph, TOUNDIRECTED mode=COLLAPSE, EDGE_ATTRIBUTE_COMBINATION edge_attr_comb=Default -igraph_get_stochastic: - PARAMS: GRAPH graph, OUT MATRIX res, BOOLEAN column_wise=False - -igraph_get_stochastic_sparsemat: - PARAMS: |- - GRAPH graph, OUT SPARSEMATPTR sparsemat, - BOOLEAN column_wise=False - ####################################### # Read and write foreign formats ####################################### @@ -1453,7 +1785,7 @@ igraph_read_graph_edgelist: igraph_read_graph_ncol: PARAMS: |- - OUT GRAPH graph, INFILE instream, OPTIONAL STRVECTOR predefnames, + OUT GRAPH graph, INFILE instream, OPTIONAL VECTOR_STR predefnames, BOOLEAN names=True, ADD_WEIGHTS weights=True, BOOLEAN directed=True igraph_read_graph_lgl: @@ -1465,12 +1797,12 @@ igraph_read_graph_pajek: PARAMS: OUT GRAPH graph, INFILE instream igraph_read_graph_graphml: - PARAMS: OUT GRAPH graph, INFILE instream, INT index=0 + PARAMS: OUT GRAPH graph, INFILE instream, INTEGER index=0 -igraph_read_graph_dimacs: +igraph_read_graph_dimacs_flow: PARAMS: |- OUT GRAPH graph, INFILE instream, - OPTIONAL OUT STRVECTOR problem, OPTIONAL OUT VECTOR label, + OPTIONAL OUT VECTOR_STR problem, OPTIONAL OUT VECTOR_INT label, OPTIONAL OUT INTEGER source, OPTIONAL OUT INTEGER target, OPTIONAL OUT VECTOR capacity, BOOLEAN directed=True @@ -1503,13 +1835,13 @@ igraph_write_graph_graphml: igraph_write_graph_pajek: PARAMS: GRAPH graph, OUTFILE outstream -igraph_write_graph_dimacs: +igraph_write_graph_dimacs_flow: PARAMS: |- - GRAPH graph, OUTFILE outstream, LONGINT source=0, LONGINT target=0, + GRAPH graph, OUTFILE outstream, VERTEX source=0, VERTEX target=0, VECTOR capacity igraph_write_graph_gml: - PARAMS: GRAPH graph, OUTFILE outstream, VECTOR id, CSTRING creator="igraph" + PARAMS: GRAPH graph, OUTFILE outstream, WRITE_GML_SW options=DEFAULT, VECTOR id, CSTRING creator=NULL igraph_write_graph_dot: PARAMS: GRAPH graph, OUTFILE outstream @@ -1519,18 +1851,18 @@ igraph_write_graph_dot: ####################################### igraph_motifs_randesu: - PARAMS: GRAPH graph, OUT VECTOR hist, INT size=3, VECTOR cut_prob + PARAMS: GRAPH graph, OUT VECTOR hist, INTEGER size=3, VECTOR cut_prob igraph_motifs_randesu_estimate: PARAMS: |- - GRAPH graph, OUT INTEGER est, INT size=3, VECTOR cut_prob, - INTEGER sample_size, VECTOR_OR_0 sample + GRAPH graph, OUT INTEGER est, INTEGER size=3, VECTOR cut_prob, + INTEGER sample_size, OPTIONAL VECTOR_INT sample igraph_motifs_randesu_no: - PARAMS: GRAPH graph, OUT INTEGER no, INT size=3, VECTOR cut_prob + PARAMS: GRAPH graph, OUT INTEGER no, INTEGER size=3, VECTOR cut_prob igraph_dyad_census: - PARAMS: GRAPH graph, OUT INTEGER mut, OUT INTEGER asym, OUT INTEGER null + PARAMS: GRAPH graph, OUT REAL mut, OUT REAL asym, OUT REAL null RETURN: ERROR igraph_triad_census: @@ -1538,7 +1870,7 @@ igraph_triad_census: RETURN: ERROR igraph_adjacent_triangles: - PARAMS: GRAPH graph, OUT VECTOR res, VERTEXSET vids=ALL + PARAMS: GRAPH graph, OUT VECTOR res, VERTEX_SELECTOR vids=ALL DEPS: vids ON graph igraph_local_scan_0: @@ -1567,24 +1899,30 @@ igraph_local_scan_1_ecount_them: igraph_local_scan_k_ecount: PARAMS: |- - GRAPH graph, INT k, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + GRAPH graph, INTEGER k, OUT VECTOR res, EDGEWEIGHTS weights=NULL, NEIMODE mode=OUT DEPS: weights ON graph igraph_local_scan_k_ecount_them: PARAMS: |- - GRAPH us, GRAPH them, INT k, OUT VECTOR res, + GRAPH us, GRAPH them, INTEGER k, OUT VECTOR res, EDGEWEIGHTS weights_them=NULL, NEIMODE mode=OUT DEPS: weights_them ON them igraph_local_scan_neighborhood_ecount: PARAMS: |- GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, - VERTEXSETLIST_INT neighborhoods + VERTEXSET_LIST neighborhoods + DEPS: weights ON graph + +igraph_local_scan_subset_ecount: + PARAMS: |- + GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL, + VERTEXSET_LIST subsets DEPS: weights ON graph igraph_list_triangles: - PARAMS: GRAPH graph, OUT VERTEXSET_INT res + PARAMS: GRAPH graph, OUT VERTEX_INDICES res DEPS: res ON graph ####################################### @@ -1595,25 +1933,25 @@ igraph_disjoint_union: PARAMS: OUT GRAPH res, GRAPH left, GRAPH right igraph_disjoint_union_many: - PARAMS: OUT GRAPH res, GRAPHLIST graphs + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs igraph_union: PARAMS: |- OUT GRAPH res, GRAPH left, GRAPH right, - OUT VECTORM1 edge_map_left, OUT VECTORM1 edge_map_right + OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right DEPS: edge_map_left ON left, edge_map_right ON right igraph_union_many: - PARAMS: OUT GRAPH res, GRAPHLIST graphs, OUT VECTORLIST edgemaps + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps igraph_intersection: PARAMS: |- OUT GRAPH res, GRAPH left, GRAPH right, - OUT VECTORM1 edge_map_left, OUT VECTORM1 edge_map_right + OUT INDEX_VECTOR edge_map_left, OUT INDEX_VECTOR edge_map_right DEPS: edge_map_left ON left, edge_map_right ON right igraph_intersection_many: - PARAMS: OUT GRAPH res, GRAPHLIST graphs, OUT VECTORLIST edgemaps + PARAMS: OUT GRAPH res, GRAPH_PTR_LIST graphs, OUT VECTOR_INT_LIST edgemaps igraph_difference: PARAMS: OUT GRAPH res, GRAPH orig, GRAPH sub @@ -1624,19 +1962,29 @@ igraph_complementer: igraph_compose: PARAMS: |- OUT GRAPH res, GRAPH g1, GRAPH g2, - OUT VECTORM1 edge_map1, OUT VECTORM1 edge_map2 + OUT INDEX_VECTOR edge_map1, OUT INDEX_VECTOR edge_map2 DEPS: edge_map1 ON g1, edge_map2 ON g2 +igraph_induced_subgraph_map: + PARAMS: |- + GRAPH graph, OUT GRAPH res, VERTEX_SELECTOR vids, SUBGRAPH_IMPL impl, + OPTIONAL OUT INDEX_VECTOR map, OPTIONAL OUT INDEX_VECTOR invmap + DEPS: vids ON graph + ####################################### # Maximum flows, minimum cuts ####################################### +igraph_gomory_hu_tree: + PARAMS: GRAPH graph, OUT GRAPH tree, OPTIONAL OUT VECTOR flows, OPTIONAL EDGE_CAPACITY capacity + DEPS: capacity ON graph + igraph_maxflow: PARAMS: |- - GRAPH graph, OUT REAL value, OUT VECTOR_OR_0 flow, - OUT VECTORM1_OR_0 cut, OPTIONAL OUT VERTEXSET partition1, - OPTIONAL OUT VERTEXSET partition2, VERTEX source, VERTEX target, - EDGECAPACITY capacity=NULL, OUT MAXFLOW_STATS stats + GRAPH graph, OUT REAL value, OPTIONAL OUT VECTOR flow, + OUT EDGE_INDICES cut, OPTIONAL OUT VERTEX_INDICES partition1, + OPTIONAL OUT VERTEX_INDICES partition2, VERTEX source, VERTEX target, + OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats DEPS: |- capacity ON graph, source ON graph, target ON graph, partition1 ON graph, partition2 ON graph, flow ON graph, @@ -1645,28 +1993,55 @@ igraph_maxflow: igraph_maxflow_value: PARAMS: |- GRAPH graph, OUT REAL value, VERTEX source, VERTEX target, - VECTOR_OR_0 capacity, OUT MAXFLOW_STATS stats - DEPS: source ON graph, target ON graph + OPTIONAL EDGE_CAPACITY capacity, OPTIONAL OUT MAXFLOW_STATS stats + DEPS: source ON graph, target ON graph, capacity ON graph igraph_mincut_value: - PARAMS: GRAPH graph, OUT REAL res, VECTOR_OR_0 capacity + PARAMS: GRAPH graph, OUT REAL res, OPTIONAL EDGE_CAPACITY capacity + DEPS: + capacity ON graph + +igraph_st_mincut: + PARAMS: |- + GRAPH graph, OUT REAL value, OUT EDGE_INDICES cut, + OUT VERTEX_INDICES partition1, OUT VERTEX_INDICES partition2, + VERTEX source, VERTEX target, + OPTIONAL EDGE_CAPACITY capacity + DEPS: |- + source ON graph, target ON graph, capacity ON graph, + partition1 ON graph, partition2 ON graph, cut ON graph igraph_st_mincut_value: PARAMS: |- GRAPH graph, OUT REAL res, VERTEX source, VERTEX target, - VECTOR_OR_0 capacity - DEPS: source ON graph, target ON graph + OPTIONAL EDGE_CAPACITY capacity + DEPS: source ON graph, target ON graph, capacity ON graph igraph_mincut: PARAMS: |- - GRAPH graph, OUT REAL value, OUT VECTORM1 partition1, - OUT VECTORM1 partition2, OUT VECTORM1 cut, VECTOR_OR_0 capacity + GRAPH graph, OUT REAL value, OUT VERTEX_INDICES partition1, + OUT VERTEX_INDICES partition2, OUT EDGE_INDICES cut, + OPTIONAL EDGE_CAPACITY capacity + DEPS: capacity ON graph, partition1 ON graph, partition2 ON graph, cut ON graph + +igraph_residual_graph: + PARAMS: |- + GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, + OUT EDGE_CAPACITY residual_capacity, VECTOR flow + DEPS: capacity ON graph, flow ON graph, residual_capacity ON residual + +igraph_reverse_residual_graph: + PARAMS: |- + GRAPH graph, EDGE_CAPACITY capacity, OUT GRAPH residual, + VECTOR flow + DEPS: capacity ON graph, flow ON graph igraph_st_mincut: PARAMS: |- - GRAPH graph, OUT REAL value, OUT VECTORM1_OR_0 cut, - OPTIONAL OUT VERTEXSET partition1, OPTIONAL OUT VERTEXSET partition2, - VERTEX source, VERTEX target, EDGECAPACITY capacity=NULL + GRAPH graph, OUT REAL value, OUT EDGE_INDICES cut, + OPTIONAL OUT VERTEX_INDICES partition1, + OPTIONAL OUT VERTEX_INDICES partition2, + VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity DEPS: |- capacity ON graph, source ON graph, target ON graph, partition1 ON graph, partition2 ON graph, cut ON graph @@ -1707,15 +2082,15 @@ igraph_cohesion: igraph_dominator_tree: PARAMS: |- - GRAPH graph, VERTEX root, OUT VECTORM1 dom, - OUT GRAPH_OR_0 domtree, OUT VERTEXSET leftout, + GRAPH graph, VERTEX root, OUT INDEX_VECTOR dom, + OPTIONAL OUT GRAPH domtree, OUT VERTEX_INDICES leftout, NEIMODE mode=OUT DEPS: root ON graph, leftout ON graph igraph_all_st_cuts: PARAMS: |- - GRAPH graph, OPTIONAL OUT EDGESETLIST cuts, - OPTIONAL OUT VERTEXSETLIST partition1s, + GRAPH graph, OPTIONAL OUT EDGESET_LIST cuts, + OPTIONAL OUT VERTEXSET_LIST partition1s, VERTEX source, VERTEX target DEPS: |- source ON graph, target ON graph, cuts ON graph, @@ -1724,33 +2099,38 @@ igraph_all_st_cuts: igraph_all_st_mincuts: PARAMS: |- GRAPH graph, OUT REAL value, - OPTIONAL OUT EDGESETLIST cuts, - OPTIONAL OUT VERTEXSETLIST partition1s, - VERTEX source, VERTEX target, EDGEWEIGHTS capacity=NULL + OPTIONAL OUT EDGESET_LIST cuts, + OPTIONAL OUT VERTEXSET_LIST partition1s, + VERTEX source, VERTEX target, OPTIONAL EDGE_CAPACITY capacity DEPS: |- capacity ON graph, source ON graph, target ON graph, cuts ON graph, partition1s ON graph +igraph_even_tarjan_reduction: + PARAMS: GRAPH graph, OUT GRAPH graphbar, OPTIONAL OUT EDGE_CAPACITY capacity + DEPS: |- + capacity ON graphbar + igraph_is_separator: - PARAMS: GRAPH graph, VERTEXSET candidate, OUT BOOLEAN res + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res DEPS: candidate ON graph igraph_is_minimal_separator: - PARAMS: GRAPH graph, VERTEXSET candidate, OUT BOOLEAN res + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res DEPS: candidate ON graph igraph_all_minimal_st_separators: - PARAMS: GRAPH graph, OUT VERTEXSETLIST separators + PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators DEPS: separators ON graph igraph_minimum_size_separators: - PARAMS: GRAPH graph, OUT VERTEXSETLIST separators + PARAMS: GRAPH graph, OUT VERTEXSET_LIST separators DEPS: separators ON graph igraph_cohesive_blocks: PARAMS: |- - GRAPH graph, OUT VERTEXSETLIST blocks, - OUT VECTOR cohesion, OUT VECTORM1 parent, + GRAPH graph, OUT VERTEXSET_LIST blocks, + OUT VECTOR_INT cohesion, OUT INDEX_VECTOR parent, OUT GRAPH blockTree DEPS: blocks ON graph @@ -1759,7 +2139,7 @@ igraph_cohesive_blocks: ####################################### igraph_coreness: - PARAMS: GRAPH graph, OUT VECTOR cores, NEIMODE mode=ALL + PARAMS: GRAPH graph, OUT VECTOR_INT cores, NEIMODE mode=ALL ####################################### # Graph isomorphism @@ -1772,7 +2152,7 @@ igraph_isomorphic: PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso igraph_isoclass_subgraph: - PARAMS: GRAPH graph, VECTOR vids, OUT INTEGER isoclass + PARAMS: GRAPH graph, VECTOR_INT vids, OUT INTEGER isoclass DEPS: vids ON graph igraph_isoclass_create: @@ -1786,7 +2166,7 @@ igraph_isomorphic_vf2: OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, OUT BOOLEAN iso, - OUT VECTORM1_OR_0 map12, OUT VECTORM1_OR_0 map21, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, OPTIONAL ISOCOMPAT_FUNC node_compat_fn, OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra @@ -1810,25 +2190,40 @@ igraph_get_isomorphisms_vf2: GRAPH graph1, GRAPH graph2, VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, - OUT VECTORLIST maps, ISOCOMPAT_FUNC node_compat_fn, + OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 +igraph_subisomorphic: + PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso + igraph_subisomorphic_vf2: PARAMS: |- GRAPH graph1, GRAPH graph2, - VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, - EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, + OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, + OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, OUT BOOLEAN iso, - OUT VECTORM1_OR_0 map12, OUT VECTORM1_OR_0 map21, - ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + OPTIONAL ISOCOMPAT_FUNC node_compat_fn, OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 +igraph_subisomorphic_function_vf2: + PARAMS: |- + GRAPH graph1, GRAPH graph2, + OPTIONAL VERTEX_COLOR vertex_color1, OPTIONAL VERTEX_COLOR vertex_color2, + OPTIONAL EDGE_COLOR edge_color1, OPTIONAL EDGE_COLOR edge_color2, + OPTIONAL OUT INDEX_VECTOR map12, OPTIONAL OUT INDEX_VECTOR map21, + ISOMORPHISM_FUNC ishohandler_fn, OPTIONAL ISOCOMPAT_FUNC node_compat_fn, + OPTIONAL ISOCOMPAT_FUNC edge_compat_fn, EXTRA arg + DEPS: |- + vertex_color1 ON graph1, vertex_color2 ON graph2, + edge_color1 ON graph1, edge_color2 ON graph2 + igraph_count_subisomorphisms_vf2: PARAMS: |- GRAPH graph1, GRAPH graph2, @@ -1845,31 +2240,28 @@ igraph_get_subisomorphisms_vf2: GRAPH graph1, GRAPH graph2, VERTEX_COLOR vertex_color1, VERTEX_COLOR vertex_color2, EDGE_COLOR edge_color1, EDGE_COLOR edge_color2, - OUT VECTORLIST maps, ISOCOMPAT_FUNC node_compat_fn, + OUT VECTOR_INT_LIST maps, ISOCOMPAT_FUNC node_compat_fn, ISOCOMPAT_FUNC edge_compat_fn, EXTRA extra DEPS: |- vertex_color1 ON graph1, vertex_color2 ON graph2, edge_color1 ON graph1, edge_color2 ON graph2 -igraph_isomorphic_34: - PARAMS: GRAPH graph1, GRAPH graph2, OUT BOOLEAN iso - igraph_canonical_permutation: PARAMS: |- GRAPH graph, OPTIONAL VERTEX_COLOR colors, - OUT VECTORM1 labeling, BLISSSH sh="fm", OUT BLISSINFO info + OUT INDEX_VECTOR labeling, BLISSSH sh="fm", OUT BLISSINFO info DEPS: colors ON graph igraph_permute_vertices: - PARAMS: GRAPH graph, OUT GRAPH res, VECTORM1 permutation + PARAMS: GRAPH graph, OUT GRAPH res, INDEX_VECTOR permutation igraph_isomorphic_bliss: PARAMS: |- GRAPH graph1, GRAPH graph2, OPTIONAL VERTEX_COLOR colors1, OPTIONAL VERTEX_COLOR colors2, - OUT BOOLEAN iso, OUT VECTORM1_OR_0 map12, - OUT VECTORM1_OR_0 map21, BLISSSH sh="fm", - OUT BLISSINFO info1, OUT BLISSINFO info2 + OUT BOOLEAN iso, OPTIONAL OUT INDEX_VECTOR map12, + OPTIONAL OUT INDEX_VECTOR map21, BLISSSH sh="fm", + OPTIONAL OUT BLISSINFO info1, OPTIONAL OUT BLISSINFO info2 DEPS: colors1 ON graph1, colors2 ON graph2 igraph_automorphisms: @@ -1879,15 +2271,15 @@ igraph_automorphisms: igraph_automorphism_group: PARAMS: |- - GRAPH graph, OPTIONAL VERTEX_COLOR colors, PRIMARY OUT VERTEXSETLIST generators, + GRAPH graph, OPTIONAL VERTEX_COLOR colors, PRIMARY OUT VERTEXSET_LIST generators, BLISSSH sh="fm", OUT BLISSINFO info DEPS: colors ON graph, generators ON graph igraph_subisomorphic_lad: PARAMS: |- - GRAPH pattern, GRAPH target, OPTIONAL VERTEXSETLIST domains, - OPTIONAL OUT BOOLEAN iso, OUT VECTOR_OR_0 map, - OPTIONAL OUT VECTORLIST maps, BOOLEAN induced, INT time_limit + GRAPH pattern, GRAPH target, OPTIONAL VERTEXSET_LIST domains, + OPTIONAL OUT BOOLEAN iso, OUT INDEX_VECTOR map, + OPTIONAL OUT VECTOR_INT_LIST maps, BOOLEAN induced, INTEGER time_limit igraph_simplify_and_colorize: # Despite their names, vertex_color and edge_color are not really colors @@ -1896,73 +2288,8 @@ igraph_simplify_and_colorize: GRAPH graph, OUT GRAPH res, OUT VECTOR_INT vertex_color, OUT VECTOR_INT edge_color DEPS: vertex_color ON graph, edge_color ON graph -####################################### -# SCG -####################################### - -igraph_scg_grouping: - PARAMS: |- - MATRIX V, OUT VECTORM1 groups, INTEGER nt, - VECTOR_OR_0 nt_vec, SCGMAT mtype=Default, - SCGALGO algo=Default, VECTOR_OR_0 p=NULL, - INTEGER maxiter=100 - -igraph_scg_semiprojectors: - PARAMS: |- - VECTORM1 groups, SCGMAT mtype=Default, - OUT MATRIX_OR_0 L, OUT MATRIX_OR_0 R, - OPTIONAL OUT SPARSEMATPTR Lsparse, OPTIONAL OUT SPARSEMATPTR Rsparse, - VECTOR_OR_0 p=NULL, SCGNORM norm=Default - -igraph_scg_norm_eps: - PARAMS: |- - MATRIX V, VECTORM1 groups, OUT VECTOR eps, - SCGMAT mtype=Default, VECTOR_OR_0 p=NULL, - SCGNORM norm=Default - -igraph_scg_adjacency: - PARAMS: |- - GRAPH_OR_0 graph, MATRIX_OR_0 matrix, - SPARSEMAT_OR_0 sparsemat, VECTOR ev, - INTEGER nt, VECTOR_OR_0 ntvec, - SCGALGO algo, INOUT VECTOR_OR_0 values, - INOUT MATRIX_OR_0 vectors, INOUT VECTORM1_OR_0 groups, - BOOLEAN use_arpack=False, INTEGER maxiter, - OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, - OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, - OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, - OPTIONAL OUT SPARSEMATPTR Rsparse - -igraph_scg_stochastic: - PARAMS: |- - GRAPH_OR_0 graph, MATRIX_OR_0 matrix, - SPARSEMAT_OR_0 sparsemat, VECTOR ev, - INTEGER nt, VECTOR_OR_0 nt_vec, - SCGALGO algo, SCGNORM norm=Default, - OPTIONAL INOUT VECTOR_COMPLEX values, - OPTIONAL INOUT MATRIX_COMPLEX vectors, - INOUT VECTORM1_OR_0 groups, INOUT VECTOR_OR_0 p, - BOOLEAN use_arpack=False, INTEGER maxiter, - OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, - OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, - OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, - OPTIONAL OUT SPARSEMATPTR Rsparse - -igraph_scg_laplacian: - PARAMS: |- - GRAPH_OR_0 graph, MATRIX_OR_0 matrix, - SPARSEMAT_OR_0 sparsemat, VECTOR ev, - INTEGER nt, VECTOR_OR_0 nt_vec, - SCGALGO algo, SCGNORM norm=Default, - SCGDIR direction=Default, - OPTIONAL INOUT VECTOR_COMPLEX values, - OPTIONAL INOUT MATRIX_COMPLEX vectors, - INOUT VECTORM1_OR_0 groups, - BOOLEAN use_arpack=False, INTEGER maxiter, - OUT GRAPH_OR_0 scg_graph, OUT MATRIX_OR_0 scg_matrix, - OUT SPARSEMAT_OR_0 scg_sparsemat, OUT MATRIX_OR_0 L, - OUT MATRIX_OR_0 R, OPTIONAL OUT SPARSEMATPTR Lsparse, - OPTIONAL OUT SPARSEMATPTR Rsparse +igraph_graph_count: + PARAMS: INTEGER n, BOOLEAN directed=False, OUT INTEGER count ####################################### # Matching @@ -1970,23 +2297,23 @@ igraph_scg_laplacian: igraph_is_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, - VECTOR_LONG_M1 matching, OUT BOOLEAN res + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + INDEX_VECTOR matching, OUT BOOLEAN res DEPS: types ON graph, matching ON graph igraph_is_maximal_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, - VECTOR_LONG_M1 matching, OUT BOOLEAN res + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, + INDEX_VECTOR matching, OUT BOOLEAN res DEPS: types ON graph igraph_maximum_bipartite_matching: PARAMS: |- - GRAPH graph, OPTIONAL BIPARTITE_TYPES types=NULL, + GRAPH graph, OPTIONAL BIPARTITE_TYPES types, OPTIONAL OUT INTEGER matching_size, OPTIONAL OUT REAL matching_weight, - OUT VECTOR_LONG_M1 matching, - EDGEWEIGHTS weights=NULL, REAL eps=.Machine$double.eps + OUT INDEX_VECTOR matching, + OPTIONAL EDGEWEIGHTS weights, REAL eps=.Machine$double.eps DEPS: types ON graph, weights ON graph ####################################### @@ -1997,7 +2324,7 @@ igraph_adjacency_spectral_embedding: PARAMS: |- GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, EIGENWHICHPOS which=ASE, BOOLEAN scaled=True, OUT MATRIX X, - OUT MATRIX_OR_0 Y, OUT VECTOR_OR_0 D, + OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, VECTOR cvec=AsmDefaultCvec, INOUT ARPACKOPT options=igraph.arpack.default DEPS: weights ON graph, cvec ON graph @@ -2007,7 +2334,7 @@ igraph_laplacian_spectral_embedding: GRAPH graph, INTEGER no, EDGEWEIGHTS weights=NULL, EIGENWHICHPOS which=ASE, LSETYPE type=Default, BOOLEAN scaled=True, OUT MATRIX X, - OUT MATRIX_OR_0 Y, OUT VECTOR_OR_0 D, + OPTIONAL OUT MATRIX Y, OPTIONAL OUT VECTOR D, INOUT ARPACKOPT options=igraph.arpack.default DEPS: weights ON graph, type ON graph @@ -2039,7 +2366,7 @@ igraph_power_law_fit: igraph_sir: PARAMS: |- GRAPH graph, REAL beta, REAL gamma, INTEGER no_sim=100, - OUT SIRLIST res + OUT SIR_LIST res ####################################### # Other, not graph related @@ -2049,14 +2376,37 @@ igraph_running_mean: PARAMS: VECTOR data, OUT VECTOR res, INTEGER binwidth igraph_random_sample: - PARAMS: OUT VECTOR res, REAL l, REAL h, INTEGER length + PARAMS: OUT VECTOR_INT res, INTEGER l, INTEGER h, INTEGER length igraph_convex_hull: - PARAMS: MATRIX data, OUT VECTOR resverts, OUT MATRIX rescoords + PARAMS: MATRIX data, OUT INDEX_VECTOR resverts, OUT MATRIX rescoords igraph_dim_select: PARAMS: VECTOR sv, OUT INTEGER dim +igraph_almost_equals: + PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps + RETURN: BOOLEAN + +igraph_cmp_epsilon: + PARAMS: DOUBLE a, DOUBLE b, DOUBLE eps + RETURN: INT + +igraph_eigen_matrix: + PARAMS: |- + MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, + EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=igraph.arpack.default, + INOUT ARPACKSTORAGE storage, OUT VECTOR_COMPLEX values, OUT MATRIX_COMPLEX vectors + +igraph_eigen_matrix_symmetric: + PARAMS: |- + MATRIX A, SPARSEMAT sA, ARPACKFUNC fun, INT n, EXTRA extra, + EIGENALGO algorithm, EIGENWHICH which, INOUT ARPACKOPT options=igraph.arpack.default, + INOUT ARPACKSTORAGE storage, OUT VECTOR values, OUT MATRIX vectors + +igraph_solve_lsap: + PARAMS: MATRIX c, INTEGER n, OUT VECTOR_INT p + ####################################### # Eulerian functions ####################################### @@ -2065,13 +2415,25 @@ igraph_is_eulerian: PARAMS: GRAPH graph, OUT BOOLEAN has_path, OUT BOOLEAN has_cycle igraph_eulerian_path: - PARAMS: GRAPH graph, OPTIONAL OUT EDGESET edge_res, OPTIONAL OUT VERTEXSET vertex_res + PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res DEPS: edge_res ON graph, vertex_res ON graph igraph_eulerian_cycle: - PARAMS: GRAPH graph, OPTIONAL OUT EDGESET edge_res, OPTIONAL OUT VERTEXSET vertex_res + PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res DEPS: edge_res ON graph, vertex_res ON graph +####################################### +# Cycle bases +####################################### + +igraph_fundamental_cycles: + PARAMS: GRAPH graph, OUT EDGESET_LIST basis, OPTIONAL VERTEX start, INTEGER bfs_cutoff, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph, basis ON graph, start ON graph + +igraph_minimum_cycle_basis: + PARAMS: GRAPH graph, OUT EDGESET_LIST basis, INTEGER bfs_cutoff, BOOLEAN complete, BOOLEAN use_cycle_order, EDGEWEIGHTS weights=NULL + DEPS: weights ON graph, basis ON graph + ####################################### # Trees ####################################### @@ -2080,24 +2442,32 @@ igraph_is_tree: PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX root, NEIMODE mode=OUT DEPS: root ON graph +igraph_is_forest: + PARAMS: GRAPH graph, PRIMARY OUT BOOLEAN res, OPTIONAL OUT VERTEX_INDICES roots, NEIMODE mode=OUT + DEPS: roots ON graph + igraph_from_prufer: PARAMS: OUT GRAPH graph, INDEX_VECTOR prufer igraph_to_prufer: PARAMS: GRAPH graph, OUT INDEX_VECTOR prufer +igraph_tree_from_parent_vector: + PARAMS: OUT GRAPH graph, INDEX_VECTOR parents, TREE_MODE type=OUT + igraph_minimum_spanning_tree: - PARAMS: GRAPH graph, OUT VECTOR res, EDGEWEIGHTS weights=NULL - DEPS: weights ON graph + PARAMS: GRAPH graph, OUT EDGE_INDICES res, EDGEWEIGHTS weights=NULL + DEPS: res ON graph, weights ON graph igraph_minimum_spanning_tree_unweighted: PARAMS: GRAPH graph, OUT GRAPH mst igraph_minimum_spanning_tree_prim: - PARAMS: GRAPH graph, OUT GRAPH mst, VECTOR weights + PARAMS: GRAPH graph, OUT GRAPH mst, EDGEWEIGHTS weights + DEPS: weights ON graph igraph_random_spanning_tree: - PARAMS: GRAPH graph, OUT EDGESET res, OPTIONAL VERTEX vid=-1 + PARAMS: GRAPH graph, OUT EDGE_INDICES res, OPTIONAL VERTEX vid DEPS: res ON graph, vid ON graph igraph_tree_game: @@ -2111,9 +2481,86 @@ igraph_vertex_coloring_greedy: PARAMS: GRAPH graph, OUT VERTEX_COLOR colors, GREEDY_COLORING_HEURISTIC heuristic=NEIGHBORS DEPS: colors ON graph +####################################### +# Microscopic update +####################################### + +igraph_deterministic_optimal_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, OPTIMALITY optimality=MAXIMUM, VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +igraph_stochastic_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +igraph_moran_process: + PARAMS: |- + GRAPH graph, EDGEWEIGHTS weights=NULL, INOUT VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: weights ON graph, quantities ON graph, strategies ON graph + +igraph_roulette_wheel_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, BOOLEAN is_local, VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + +igraph_stochastic_imitation: + PARAMS: |- + GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, VERTEX_QTY quantities, + INOUT VECTOR_INT strategies, NEIMODE mode=OUT + DEPS: vid ON graph, quantities ON graph, strategies ON graph + ####################################### # Other, (yet) undocumented functions ####################################### igraph_convergence_degree: PARAMS: GRAPH graph, OUT VECTOR result, OUT VECTOR in, OUT VECTOR out + +igraph_has_attribute_table: + RETURN: BOOLEAN + +####################################### +# Progress, status handling +####################################### + +igraph_progress: + PARAMS: CSTRING message, REAL percent, EXTRA data + +igraph_status: + PARAMS: CSTRING message, EXTRA data + +igraph_strerror: + PARAMS: ERROR igraph_errno + RETURN: CSTRING + +####################################### +# Other functions, documented, graph related +####################################### + +igraph_expand_path_to_pairs: + PARAMS: INOUT VERTEX_INDICES path + +igraph_invalidate_cache: + PARAMS: GRAPH graph + RETURN: VOID + +igraph_vertex_path_from_edge_path: + PARAMS: |- + GRAPH graph, VERTEX start, EDGE_INDICES edge_path, + OUT VERTEX_INDICES vertex_path, NEIMODE mode=OUT + +####################################### +# Meta info +####################################### + +igraph_version: + PARAMS: |- + OPTIONAL OUT CSTRING version_string, OPTIONAL OUT INT major, + OPTIONAL OUT INT minor, OPTIONAL OUT INT subminor + RETURN: VOID diff --git a/src/vendor/cigraph/interfaces/types.yaml b/src/vendor/cigraph/interfaces/types.yaml index 65634af4f81..aeb62480e57 100644 --- a/src/vendor/cigraph/interfaces/types.yaml +++ b/src/vendor/cigraph/interfaces/types.yaml @@ -28,7 +28,7 @@ COMPLEX: ERROR: # An igraph error code - CTYPE: int + CTYPE: igraph_error_t ############################################################################### ## C data types @@ -54,6 +54,14 @@ OUTFILE: # A file, already open for writing CTYPE: FILE* +DOUBLE: + # A C double + CTYPE: double + +VOID: + # C void + CTYPE: void + ############################################################################### # Vectors, matrices and other template types ############################################################################### @@ -75,11 +83,6 @@ VECTOR_INT: CTYPE: igraph_vector_int_t FLAGS: BY_REF -VECTOR_LONG: - # A vector of C long integers. Deprecated, will be removed in 0.10. - CTYPE: igraph_vector_long_t - FLAGS: BY_REF - VECTOR_BOOL: # A vector of Boolean values CTYPE: igraph_vector_bool_t @@ -89,24 +92,19 @@ VECTOR_COMPLEX: # A vector of igraph complex numbers CTYPE: igraph_vector_complex_t -STRVECTOR: +VECTOR_STR: # A vector of strings - # TODO(ntamas): maybe rename this to igraph_vector_str_t and VECTOR_STR - # for consistency? CTYPE: igraph_strvector_t FLAGS: BY_REF -VECTORLIST: - # A vector containing pointers to vectors of floating-point numbers - CTYPE: igraph_vector_ptr_t +VECTOR_LIST: + # A list containing vectors of floating-point numbers + CTYPE: igraph_vector_list_t FLAGS: BY_REF -VECTORM1: - # A vector of integer indices that should adapt to the conventions of the - # host language (i.e. 1-based for R, Mathematica, Octave etc, 0-based for - # Python and similar). - # TODO(ntamas): should be replaced with INDEX_VECTOR - CTYPE: igraph_vector_t +VECTOR_INT_LIST: + # A list containing vectors of integers + CTYPE: igraph_vector_int_list_t FLAGS: BY_REF MATRIX: @@ -114,13 +112,18 @@ MATRIX: CTYPE: igraph_matrix_t FLAGS: BY_REF +MATRIX_INT: + # A matrix of igraph integers + CTYPE: igraph_matrix_int_t + FLAGS: BY_REF + MATRIX_COMPLEX: # A matrix of igraph complex numbers CTYPE: igraph_matrix_complex_t -MATRIXLIST: - # A vector containing pointers to matrices of floating-point numbers - CTYPE: igraph_vector_ptr_t +MATRIX_LIST: + # A list containing matrices of floating-point numbers + CTYPE: igraph_matrix_list_t FLAGS: BY_REF SPARSEMAT: @@ -128,86 +131,41 @@ SPARSEMAT: CTYPE: igraph_sparsemat_t FLAGS: BY_REF -SPARSEMATPTR: - # A sparse matrix of floating-point numbers. The specialty of this type - # is that it is uninitialized upon calling the function that uses it; the - # function will initialize it instead. - # TODO(ntamas): check whether we could merge this with SPARSEMAT in 0.10 - CTYPE: igraph_sparsemat_t - FLAGS: BY_REF - -# SOMETHING_OR_0 variants -- these will be phased out in favour of the -# OPTIONAL modifier - -VECTOR_OR_0: - # A vector of floating-point numbers values where a null pointer is also a valid value - CTYPE: igraph_vector_t - FLAGS: BY_REF - -VECTOR_BOOL_OR_0: - # A vector of Boolean values where a null pointer is also a valid value - CTYPE: igraph_vector_bool_t - FLAGS: BY_REF - -VECTORM1_OR_0: - # A vector of integer indices that should adapt to the conventions of the - # host language (i.e. 1-based for R, Mathematica, Octave etc, 0-based for - # Python and similar). A null pointer is also a valid value here. - # TODO(ntamas): should be replaced with INDEX_VECTOR - CTYPE: igraph_vector_t - FLAGS: BY_REF - -VECTOR_LONG_M1: - # A vector of integer indices (as C long ints) that should adapt to the - # conventions of the host language (i.e. 1-based for R, Mathematica, Octave - # etc, 0-based for Python and similar). Deprecated, will be removed in 0.10. - # - # TODO(ntamas): should be replaced with INDEX_VECTOR - CTYPE: igraph_vector_long_t - FLAGS: BY_REF - -MATRIX_OR_0: - # A matrix of floating-point numbers values where a null pointer is also a valid value - CTYPE: igraph_matrix_t - FLAGS: BY_REF - -SPARSEMAT_OR_0: - # A sparse matrix of floating-point numbers where a null pointer is also a valid value - CTYPE: igraph_sparsemat_t - FLAGS: BY_REF - ############################################################################### -# Vertices, edges, vertex and edge sequences +# Vertices, edges, vertex and edge selectors ############################################################################### EDGE: # A single edge index CTYPE: igraph_integer_t -EDGESET: - # An igraph edge sequence. This is an ugly hybrid type; when it is an - # IN argument in generated code, it is an igraph_es_t, but when it is an - # OUT argument, it is an igraph_vector_t. This should be fixed for 0.10. - CTYPE: - IN: igraph_es_t - OUT: igraph_vector_t +EDGE_INDICES: + # An integer vector containing edge indices. + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +EDGE_SELECTOR: + # An igraph edge selector. Typically used only as an input argument type. + CTYPE: igraph_es_t VERTEX: # A single vertex index CTYPE: igraph_integer_t -VERTEXSET: - # An igraph vertex sequence. This is an ugly hybrid type; when it is an - # IN argument in generated code, it is an igraph_vs_t, but when it is an - # OUT argument, it is an igraph_vector_t. This should be fixed for 0.10. - CTYPE: - IN: igraph_vs_t - OUT: igraph_vector_t - -VERTEXSET_INT: - # An igraph vertex sequence where each vertex is represented as an integer, - # hence the entire vector is an igraph_vector_int_t. +VERTEX_INDICES: + # An integer vector containing vertex indices. CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VERTEX_INDEX_PAIRS: + # An integer vector containing pairs of vertex indices, in a flattened + # representation + CTYPE: igraph_vector_int_t + FLAGS: BY_REF + +VERTEX_SELECTOR: + # An igraph vertex selector. Typically used only as an input argument type. + CTYPE: igraph_vs_t ############################################################################### # Specialized vectors with semantic meaning @@ -219,7 +177,7 @@ BIPARTITE_TYPES: CTYPE: igraph_vector_bool_t FLAGS: BY_REF -EDGECAPACITY: +EDGE_CAPACITY: # A vector containing edge capacities (typically for max-flow algorithms) CTYPE: igraph_vector_t FLAGS: BY_REF @@ -234,55 +192,40 @@ EDGEWEIGHTS: CTYPE: igraph_vector_t FLAGS: BY_REF -EDGESETLIST: - # A vector containing vectors of floating-point numbers where each such +EDGESET_LIST: + # A list containing vectors of igraph integers where each such # vector represents a sequence of edge indices. - # - # TODO(ntamas): the name is slightly inconsistent because EDGESET is - # the abstract type for igraph_es_t, but an EDGESETLIST is _not_ a - # vector of igraph_es_t objects - CTYPE: igraph_vector_ptr_t + CTYPE: igraph_vector_int_list_t + FLAGS: BY_REF + +GRAPH_LIST: + # A list containing graphs (owned by the list itself) + CTYPE: igraph_graph_list_t FLAGS: BY_REF -GRAPHLIST: - # A vector containing pointers to graph objects +GRAPH_PTR_LIST: + # A vector containing pointers to graph objects (not owned by the vector) CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VERTEXINDEX: +VERTEX_QTY: # A vector of floating-point numbers where each entry corresponds to - # one of the vertices in a graph. Higher-level interfaces may use this - # type to provide a "named vector" such that each entry can be indexed - # either by the vertex index or by the vertex name. - # - # TODO(ntamas): this is a misleading name; we should find a better name - # for this type + # one of the vertices in a graph and its value represents some quantity + # associated to the vertex with the same index. Higher-level interfaces may + # use this type to provide a "named vector" such that each entry can be + # indexed either by the vertex index or by the vertex name. CTYPE: igraph_vector_t FLAGS: BY_REF -SIRLIST: +SIR_LIST: # A vector containing pointers to igraph_sir_t objects CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VERTEXSETLIST: - # A vector containing vectors of floating-point numbers where each such +VERTEXSET_LIST: + # A list containing vectors of igraph integers where each such # vector represents a sequence of vertex indices. - # - # TODO(ntamas): the name is slightly inconsistent because VERTEXSET is - # the abstract type for igraph_vs_t, but a VERTEXSETLIST is _not_ a - # vector of igraph_vs_t objects - CTYPE: igraph_vector_ptr_t - FLAGS: BY_REF - -VERTEXSETLIST_INT: - # A vector containing vectors of igraph integers where each such vector - # represents a sequence of vertex indices. - # - # TODO(ntamas): the name is slightly inconsistent because VERTEXSET is - # the abstract type for igraph_vs_t, but a VERTEXSETLIST is _not_ a - # vector of igraph_vs_t objects - CTYPE: igraph_vector_ptr_t + CTYPE: igraph_vector_int_list_t FLAGS: BY_REF VERTEX_COLOR: @@ -314,14 +257,6 @@ INCLIST: CTYPE: igraph_inclist_t FLAGS: BY_REF -# SOMETHING_OR_0 variants -- these will be phased out in favour of the -# OPTIONAL modifier - -GRAPH_OR_0: - # An igraph graph where a null pointer is also a valid value - CTYPE: igraph_t - FLAGS: BY_REF - ############################################################################### # Enums ############################################################################### @@ -332,7 +267,7 @@ ADD_WEIGHTS: CTYPE: igraph_add_weights_t FLAGS: ENUM -ADJACENCYMODE: +ADJACENCY_MODE: # Enum that describes how an adjacency matrix should be constructed CTYPE: igraph_adjacency_t FLAGS: ENUM @@ -358,7 +293,7 @@ CONNECTEDNESS: CTYPE: igraph_connectedness_t FLAGS: ENUM -DEGSEQMODE: +DEGSEQ_MODE: # Enum that describes the various implementations of generating a graph # with an arbitrary degree sequence CTYPE: igraph_degseq_t @@ -376,11 +311,24 @@ EIGENWHICHPOS: CTYPE: igraph_eigen_which_position_t FLAGS: ENUM +ERDOS_RENYI_TYPE: + # Enum that says wheter a GNM (n vertices, m edges) or + # GNP (n vertices, every edge exists with probability p) + # graph is created + CTYPE: igraph_erdos_renyi_t + FLAGS: ENUM + FAS_ALGORITHM: # Enum representing feedback arc set algorithms CTYPE: igraph_fas_algorithm_t FLAGS: ENUM +FWALGORITHM: + # Enum that describes the variant of the Floyd-Warshall algorithm to use in + # Floyd-Warshall graph distances computing function + CTYPE: igraph_floyd_warshall_algorithm_t + FLAGS: ENUM + GETADJACENCY: # Enum storing how to retrieve the adjacency matrix from a graph CTYPE: igraph_get_adjacency_t @@ -391,6 +339,17 @@ GREEDY_COLORING_HEURISTIC: CTYPE: igraph_coloring_greedy_t FLAGS: ENUM +IMITATE_ALGORITHM: + # This enum controls which algorithm to use in stochastic imitation + CTYPE: igraph_imitate_algorithm_t + FLAGS: ENUM + +LAPLACIAN_NORMALIZATION: + # Enum representing the possible normalization methods of a Laplacian + # matrix + CTYPE: igraph_laplacian_normalization_t + FLAGS: ENUM + LAYOUT_GRID: # Whether to use the fast (but less accurate) grid-based version of a # layout algorithm that supports it (typically the Fruchterman-Reingold @@ -398,6 +357,13 @@ LAYOUT_GRID: CTYPE: igraph_layout_grid_t FLAGS: ENUM +LOOPS: + # Enum that describes how loop edges should be handled in undirected graphs + # in functions that support it. Possible options are: no loops, loops + # counted once, loops counted twice + CTYPE: igraph_loops_t + FLAGS: ENUM + LSETYPE: # Enum storing the possible types (definitions) of the Laplacian matrix # to use in the Laplacian spectral embedding algorithms @@ -410,6 +376,16 @@ NEIMODE: CTYPE: igraph_neimode_t FLAGS: ENUM +OPTIMALITY: + # This enum controls which algorithm to use in deterministic optimal imitation + CTYPE: igraph_optimal_t + FLAGS: ENUM + +ORDER: + # Whether ordering should be ascending or descending + CTYPE: igraph_order_t + FLAGS: ENUM + PAGERANKALGO: # Enum that describes the various implementations of the PageRank algorithm CTYPE: igraph_pagerank_algo_t @@ -432,39 +408,22 @@ RECIP: CTYPE: igraph_reciprocity_t FLAGS: ENUM -REWIRINGMODE: +REWIRING_MODE: # Enum for the rewiring modes of igraph_rewire() CTYPE: igraph_rewiring_t FLAGS: ENUM +ROOTCHOICE: + # Enum for the heuristic of igraph_roots_for_tree_layout() + CTYPE: igraph_root_choice_t + FLAGS: ENUM + RWSTUCK: # Enum that describes what igraph should do when a random walk gets stuck # in a sink vertex CTYPE: igraph_random_walk_stuck_t FLAGS: ENUM -SCGALGO: - # Enum representing the algorithms that may be used for spectral coarse - # graining of graphs - CTYPE: igraph_scg_algorithm_t - FLAGS: ENUM - -SCGDIR: - # Enum storing whether the spectral coarse graining algorithm should work - # with left or right eigenvectors - CTYPE: igraph_scg_direction_t - FLAGS: ENUM - -SCGMAT: - # Enum representing the possible types of semiprojections used in the - # spectral coarse graining algorithm - CTYPE: igraph_scg_matrix_t - FLAGS: ENUM - -SCGNORM: - CTYPE: igraph_scg_norm_t - FLAGS: ENUM - SPINCOMMUPDATE: # Enum containing update modes for the spinglass community detection # algorithm @@ -477,7 +436,7 @@ SPINGLASS_IMPLEMENTATION: CTYPE: igraph_spinglass_implementation_t FLAGS: ENUM -STARMODE: +STAR_MODE: # Enum that describes how a star graph should be constructed CTYPE: igraph_star_mode_t FLAGS: ENUM @@ -500,21 +459,34 @@ TOUNDIRECTED: CTYPE: igraph_to_undirected_t FLAGS: ENUM -TRANSITIVITYMODE: +TRANSITIVITY_MODE: # Enum that specifies how isolated vertices should be handled in transitivity # calcuations CTYPE: igraph_transitivity_mode_t FLAGS: ENUM -TREEMODE: +TREE_MODE: # Enum that describes how a tree graph should be constructed CTYPE: igraph_tree_mode_t FLAGS: ENUM VCONNNEI: + # Enum specifying what to do in vertex connectivity tests when the two + # vertices being tested are already connected CTYPE: igraph_vconn_nei_t FLAGS: ENUM +VORONOI_TIEBREAKER: + # Enum specifying what to do when two vertices are at equal distance from + # multiple generators while computing Voronoi partitionings + CTYPE: igraph_voronoi_tiebreaker_t + FLAGS: ENUM + +WHEEL_MODE: + # Enum that describes how a star graph should be constructed + CTYPE: igraph_wheel_mode_t + FLAGS: ENUM + ############################################################################### # Switches / flags / bits ############################################################################### @@ -525,6 +497,11 @@ EDGE_TYPE_SW: CTYPE: igraph_edge_type_sw_t FLAGS: BITS +WRITE_GML_SW: + # Flag bitfield that specifies how to write GML files. + CTYPE: igraph_write_gml_sw_t + FLAGS: BITS + ############################################################################### # Callbacks ############################################################################### @@ -582,6 +559,10 @@ ARPACKSTORAGE: CTYPE: igraph_arpack_storage_t FLAGS: BY_REF +ASTAR_HEURISTIC_FUNC: + # A* heuristic function + CTYPE: igraph_astar_heuristic_func_t + ATTRIBUTES: # An opaque data structure that a high-level interface may use to pass # information about graph/vertex/edge attributes to a low-level igraph diff --git a/src/vendor/cigraph/src/CMakeLists.txt b/src/vendor/cigraph/src/CMakeLists.txt index d2353841113..fca49d6e5d9 100644 --- a/src/vendor/cigraph/src/CMakeLists.txt +++ b/src/vendor/cigraph/src/CMakeLists.txt @@ -17,13 +17,17 @@ foreach(FORMAT dl gml lgl ncol pajek) if (BISON_VERSION VERSION_GREATER_EQUAL 3) set(bison_no_deprecated -Wno-deprecated) endif() + if (NOT FLEX_KEEP_LINE_NUMBERS) + set(bison_hide_line_numbers --no-lines) + set(flex_hide_line_numbers --noline) + endif() bison_target( ${FORMAT}_parser io/${FORMAT}-parser.y ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-parser.c - COMPILE_FLAGS "--no-lines ${bison_no_deprecated}" + COMPILE_FLAGS "${bison_hide_line_numbers} ${bison_no_deprecated}" ) flex_target( ${FORMAT}_lexer io/${FORMAT}-lexer.l ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.c - COMPILE_FLAGS "--noline" + COMPILE_FLAGS "${flex_hide_line_numbers}" DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/io/parsers/${FORMAT}-lexer.h ) add_flex_bison_dependency(${FORMAT}_lexer ${FORMAT}_parser) @@ -42,28 +46,30 @@ add_library( core/error.c core/estack.c core/fixed_vectorlist.c + core/genheap.c core/grid.c core/heap.c core/indheap.c core/interruption.c core/marked_queue.c core/matrix.c + core/matrix_list.c core/memory.c core/printing.c core/progress.c core/psumtree.c core/set.c core/sparsemat.c - core/spmatrix.c core/stack.c core/statusbar.c core/strvector.c core/trie.c - core/vector_ptr.c core/vector.c + core/vector_list.c + core/vector_ptr.c - math/bfgs.c math/complex.c + math/safe_intop.c math/utils.c linalg/arpack.c @@ -72,37 +78,47 @@ add_library( linalg/lapack.c random/random.c + random/rng_glibc2.c + random/rng_mt19937.c + random/rng_pcg32.c + random/rng_pcg64.c graph/adjlist.c graph/attributes.c graph/basic_query.c + graph/caching.c graph/cattributes.c + graph/graph_list.c graph/iterators.c + graph/type_common.c graph/type_indexededgelist.c graph/visitors.c constructors/adjacency.c constructors/atlas.c constructors/basic_constructors.c + constructors/circulant.c constructors/de_bruijn.c constructors/famous.c constructors/full.c + constructors/generalized_petersen.c constructors/kautz.c + constructors/lattices.c constructors/lcf.c constructors/linegraph.c constructors/prufer.c constructors/regular.c + constructors/trees.c + games/barabasi.c games/callaway_traits.c games/citations.c games/correlated.c - games/degree_sequence_vl/gengraph_box_list.cpp games/degree_sequence_vl/gengraph_degree_sequence.cpp games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp games/degree_sequence_vl/gengraph_mr-connected.cpp - games/degree_sequence_vl/gengraph_powerlaw.cpp games/degree_sequence.c games/dotproduct.c games/erdos_renyi.c @@ -124,6 +140,10 @@ add_library( centrality/centralization.c centrality/closeness.c centrality/coreness.c + centrality/eigenvector.c + centrality/hub_authority.c + centrality/pagerank.c + centrality/truss.cpp centrality/prpack.cpp cliques/cliquer_wrapper.c @@ -137,7 +157,6 @@ add_library( community/fluid.c community/infomap/infomap_FlowGraph.cc community/infomap/infomap_Greedy.cc - community/infomap/infomap_Node.cc community/infomap/infomap.cc community/label_propagation.c community/leading_eigenvector.c @@ -159,6 +178,7 @@ add_library( connectivity/separators.c flow/flow.c + flow/flow_conversion.c flow/st-cuts.c hrg/hrg_types.cc @@ -176,6 +196,7 @@ add_library( io/lgl.c io/ncol.c io/pajek.c + io/parse_utils.c ${PARSER_SOURCES} layout/circular.c @@ -199,6 +220,7 @@ add_library( layout/merge_grid.c layout/reingold_tilford.c layout/sugiyama.c + layout/umap.c operators/add_edge.c operators/complementer.c @@ -218,37 +240,37 @@ add_library( operators/union.c paths/all_shortest_paths.c + paths/astar.c paths/bellman_ford.c paths/dijkstra.c paths/distances.c paths/eulerian.c + paths/floyd_warshall.c paths/histogram.c paths/johnson.c paths/random_walk.c paths/shortest_paths.c paths/simple_paths.c + paths/sparsifier.c paths/unweighted.c + paths/voronoi.c + paths/widest_paths.c properties/basic_properties.c properties/constraint.c properties/convergence_degree.c properties/dag.c properties/degrees.c + properties/ecc.c properties/girth.c properties/loops.c properties/multiplicity.c properties/neighborhood.c + properties/perfect.c properties/spectral.c properties/trees.c properties/triangles.c - scg/scg_approximate_methods.c - scg/scg_exact_scg.c - scg/scg_kmeans.c - scg/scg_optimal_method.c - scg/scg_utils.c - scg/scg.c - isomorphism/bliss.cc isomorphism/isoclasses.c isomorphism/lad.c @@ -261,6 +283,7 @@ add_library( misc/cocitation.c misc/coloring.c misc/conversion.c + misc/cycle_bases.c misc/degree_sequence.cpp misc/embedding.c misc/feedback_arc_set.c @@ -269,7 +292,9 @@ add_library( misc/microscopic_update.c misc/mixing.c misc/motifs.c + misc/order_cycle.cpp misc/other.c + misc/power_law_fit.c misc/scan.c misc/sir.c misc/spanning_trees.c @@ -279,6 +304,7 @@ add_library( internal/lsap.c internal/qsort_r.c internal/qsort.c + internal/utils.c internal/zeroin.c version.c @@ -287,16 +313,18 @@ add_library( $,$,$>,$,> $,$,> $,$,> - $,$,> $,$,> $,$,> $,$,> $,$,> ) +# Required by Xcode new build system +add_dependencies(igraph parsersources) + # Set soname for the library -set_target_properties(igraph PROPERTIES VERSION "0.0.0") -set_target_properties(igraph PROPERTIES SOVERSION 0) +set_target_properties(igraph PROPERTIES VERSION "3.0.0") +set_target_properties(igraph PROPERTIES SOVERSION 3) # Add extra compiler definitions if needed target_compile_definitions( @@ -304,6 +332,10 @@ target_compile_definitions( PRIVATE IGRAPH_VERIFY_FINALLY_STACK=$,1,0> ) +# target_compile_options( +# # -Wconversion could be useful? +# igraph PRIVATE -Wshorten-64-to-32 +# ) # Make sure that a macro named IGRAPH_FILE_BASENAME is provided in every # compiler call so we can use these in debug messages without revealing the @@ -326,12 +358,10 @@ target_include_directories( ${PROJECT_SOURCE_DIR}/vendor # Vendored library include paths - "$<$:$>" "$<$:$>" "$<$:$>" # Include paths for dependencies - "$<$:${CXSPARSE_INCLUDE_DIRS}>" "$<$:${GLPK_INCLUDE_DIR}>" "$<$:${GMP_INCLUDE_DIR}>" "$<$:${LIBXML2_INCLUDE_DIRS}>" @@ -343,51 +373,40 @@ if(MATH_LIBRARY) endif() if(ARPACK_LIBRARIES) - target_link_libraries(igraph PUBLIC ${ARPACK_LIBRARIES}) -endif() - -if(BLAS_LIBRARIES) - target_link_libraries(igraph PUBLIC ${BLAS_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${ARPACK_LIBRARIES}) endif() -if(CXSPARSE_LIBRARIES) - target_link_libraries(igraph PUBLIC ${CXSPARSE_LIBRARIES}) +if(BLAS_FOUND) + target_link_libraries(igraph PRIVATE ${BLAS_LIBRARIES}) endif() if(GLPK_LIBRARIES) - target_link_libraries(igraph PUBLIC ${GLPK_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${GLPK_LIBRARIES}) endif() if(GMP_LIBRARIES) - target_link_libraries(igraph PUBLIC ${GMP_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${GMP_LIBRARIES}) endif() if(LAPACK_LIBRARIES) - target_link_libraries(igraph PUBLIC ${LAPACK_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${LAPACK_LIBRARIES}) endif() if(LIBXML2_LIBRARIES) - target_link_libraries(igraph PUBLIC ${LIBXML2_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${LIBXML2_LIBRARIES}) endif() if(PLFIT_LIBRARIES) - target_link_libraries(igraph PUBLIC ${PLFIT_LIBRARIES}) + target_link_libraries(igraph PRIVATE ${PLFIT_LIBRARIES}) endif() # Link igraph statically to some of the libraries from the subdirectories target_link_libraries( igraph PRIVATE - bliss cliquer prpack + bliss cliquer cxsparse_vendored pcg prpack ) -# Disable complex number support for CXSparse because: -# - It is necessary to compile with MSVC -# - igraph does not need complex number support from CXSparse on any platform -# This is needed here (in addition to the cxsparse_vendored target) because -# igraph may be compiled with an external CXSparse. -target_compile_definitions(igraph PRIVATE NCOMPLEX) - if (NOT BUILD_SHARED_LIBS) target_compile_definitions(igraph PRIVATE IGRAPH_STATIC) else() @@ -434,7 +453,7 @@ write_basic_package_version_file( # Define how to install the library install( - TARGETS igraph bliss cliquer prpack + TARGETS igraph bliss cliquer cxsparse_vendored pcg prpack EXPORT igraph_targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/vendor/cigraph/src/centrality/betweenness.c b/src/vendor/cigraph/src/centrality/betweenness.c index 8c9f9d80cb1..9adff201e2b 100644 --- a/src/vendor/cigraph/src/centrality/betweenness.c +++ b/src/vendor/cigraph/src/centrality/betweenness.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ -/* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2007-2020 The igraph development team + 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 @@ -14,22 +12,442 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 . + 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_dqueue.h" #include "igraph_interface.h" +#include "igraph_memory.h" +#include "igraph_nongraph.h" #include "igraph_progress.h" #include "igraph_stack.h" -#include "igraph_dqueue.h" #include "core/indheap.h" #include "core/interruption.h" -#include "core/math.h" + +/* + * We provide separate implementations of single-source shortest path searches, + * one with incidence lists and one with adjacency lists. We use the implementation + * based on adjacency lists when possible (i.e. when weights are not needed) to + * avoid an expensive IGRAPH_OTHER() lookup on edge IDs. The cost of this macro + * comes from the inability of the branch predictor to predict accurately whether + * the condition in the macro will be true or not. + * + * The following four functions are very similar in their structure. If you make + * a modification to one of them, consider whether the same modification makes + * sense in the context of the remaining three functions as well. + */ + +/** + * Internal function to calculate the single source shortest paths for the + * vertex unweighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents adjacent list that starts empty and that stores the IDs + * of the vertices that lead to a given node during the traversal + * \param adjlist the adjacency list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf( + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + igraph_stack_int_t *stack, + igraph_adjlist_t *parents, + const igraph_adjlist_t *adjlist, + igraph_real_t cutoff) { + + igraph_dqueue_int_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); + + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_dqueue_int_empty(&queue)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[actnode] = 0; + nrgeo[actnode] = 0; + igraph_vector_int_clear(igraph_adjlist_get(parents, actnode)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); + + /* Examine the neighbors of this node */ + neis = igraph_adjlist_get(adjlist, actnode); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + + if (VECTOR(*dist)[neighbor] == 0) { + /* We have found 'neighbor' for the first time */ + VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); + } + + if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && + (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + v = igraph_adjlist_get(parents, neighbor); + IGRAPH_CHECK(igraph_vector_int_push_back(v, actnode)); + nrgeo[neighbor] += nrgeo[actnode]; + } + } + } + + igraph_dqueue_int_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the + * edge unweighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents incidence list that starts empty and that stores the IDs + * of the edges that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_edge( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + igraph_stack_int_t *stack, + igraph_inclist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + igraph_dqueue_int_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&queue, 100); + + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, source)); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_dqueue_int_empty(&queue)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && VECTOR(*dist)[actnode] > cutoff + 1) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[actnode] = 0; + nrgeo[actnode] = 0; + igraph_vector_int_clear(igraph_inclist_get(parents, actnode)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, actnode)); + + /* Examine the neighbors of this node */ + neis = igraph_inclist_get(inclist, actnode); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); + + if (VECTOR(*dist)[neighbor] == 0) { + /* We have found 'neighbor' for the first time */ + VECTOR(*dist)[neighbor] = VECTOR(*dist)[actnode] + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&queue, neighbor)); + } + + if (VECTOR(*dist)[neighbor] == VECTOR(*dist)[actnode] + 1 && + (VECTOR(*dist)[neighbor] <= cutoff + 1 || cutoff < 0)) { + /* Only add if the node is not more distant than the cutoff */ + v = igraph_inclist_get(parents, neighbor); + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + nrgeo[neighbor] += nrgeo[actnode]; + } + } + } + + igraph_dqueue_int_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the vertex + * weighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param weights the weights of the edges + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents adjacency list that starts empty and that stores the IDs + * of the vertices that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_weighted( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + const igraph_vector_t *weights, + igraph_stack_int_t *stack, + igraph_adjlist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + int cmp_result; + igraph_2wheap_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + /* TODO: this is an O|V| step here. We could save some time by pre-allocating + * the two-way heap in the caller and re-using it here */ + IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); + + igraph_2wheap_push_with_index(&queue, source, -1.0); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&queue)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&queue); + igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[minnei] = 0; + nrgeo[minnei] = 0; + igraph_vector_int_clear(igraph_adjlist_get(parents, minnei)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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 */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = minnei; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = minnei; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + igraph_2wheap_modify(&queue, 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 */ + v = igraph_adjlist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, minnei)); + nrgeo[to] += nrgeo[minnei]; + } + } + } + + igraph_2wheap_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * Internal function to calculate the single source shortest paths for the edge + * weighted case. + * + * \param graph the graph to calculate the single source shortest paths on + * \param weights the weights of the edges + * \param source the source node + * \param dist distance of each node from the source node \em plus one; + * must be filled with zeros initially + * \param nrgeo vector storing the number of geodesics from the source node + * to each node; must be filled with zeros initially + * \param stack stack in which the nodes are pushed in the order they are + * discovered during the traversal + * \param parents incidence list that starts empty and that stores the IDs + * of the edges that lead to a given node during the traversal + * \param inclist the incidence list of the graph + * \param cutoff cutoff length of shortest paths + */ +static igraph_error_t igraph_i_sspf_weighted_edge( + const igraph_t *graph, + igraph_integer_t source, + igraph_vector_t *dist, + igraph_real_t *nrgeo, + const igraph_vector_t *weights, + igraph_stack_int_t *stack, + igraph_inclist_t *parents, + const igraph_inclist_t *inclist, + igraph_real_t cutoff) { + + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + + int cmp_result; + igraph_2wheap_t queue; + const igraph_vector_int_t *neis; + igraph_vector_int_t *v; + igraph_integer_t nlen; + + /* TODO: this is an O|V| step here. We could save some time by pre-allocating + * the two-way heap in the caller and re-using it here */ + IGRAPH_CHECK(igraph_2wheap_init(&queue, igraph_vcount(graph))); + IGRAPH_FINALLY(igraph_2wheap_destroy, &queue); + + igraph_2wheap_push_with_index(&queue, source, -1.0); + VECTOR(*dist)[source] = 1.0; + nrgeo[source] = 1; + + while (!igraph_2wheap_empty(&queue)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&queue); + igraph_real_t mindist = -igraph_2wheap_delete_max(&queue); + + /* Ignore vertices that are more distant than the cutoff */ + if (cutoff >= 0 && mindist > cutoff + 1.0) { + /* Reset variables if node is too distant */ + VECTOR(*dist)[minnei] = 0; + nrgeo[minnei] = 0; + igraph_vector_int_clear(igraph_inclist_get(parents, minnei)); + continue; + } + + /* Record that we have visited this node */ + IGRAPH_CHECK(igraph_stack_int_push(stack, minnei)); + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_inclist_get(inclist, minnei); + nlen = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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 */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&queue, to, -altdist)); + } else if (cmp_result < 0) { + /* This is a shorter path */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_resize(v, 1)); + VECTOR(*v)[0] = edge; + nrgeo[to] = nrgeo[minnei]; + VECTOR(*dist)[to] = altdist; + igraph_2wheap_modify(&queue, 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 */ + v = igraph_inclist_get(parents, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, edge)); + nrgeo[to] += nrgeo[minnei]; + } + } + } + + igraph_2wheap_destroy(&queue); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_betweenness_check_weights( + const igraph_vector_t* weights, igraph_integer_t no_of_edges +) { + igraph_real_t minweight; + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Weight vector length must match the number of edges.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + minweight = igraph_vector_min(weights); + if (minweight <= 0) { + IGRAPH_ERROR("Weight vector must be positive.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } else if (minweight <= IGRAPH_SHORTEST_PATH_EPSILON) { + IGRAPH_WARNING( + "Some weights are smaller than epsilon, calculations may " + "suffer from numerical precision issues." + ); + } + } + } + + return IGRAPH_SUCCESS; +} /***** Vertex betweenness *****/ @@ -42,6 +460,7 @@ * 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. @@ -55,7 +474,7 @@ * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex id passed in + * \c IGRAPH_EINVVID, invalid vertex ID passed in * \p vids. * * Time complexity: O(|V||E|), @@ -70,200 +489,12 @@ * 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, +igraph_error_t 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 @@ -289,7 +520,7 @@ static int igraph_i_betweenness_cutoff_weighted( * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex id passed in + * \c IGRAPH_EINVVID, invalid vertex ID passed in * \p vids. * * Time complexity: O(|V||E|), @@ -303,176 +534,126 @@ static int igraph_i_betweenness_cutoff_weighted( * \ref igraph_edge_betweenness_cutoff() to calculate the range-limited * edge betweenness. */ -int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_adjlist_t adjlist, parents; + igraph_inclist_t inclist; + igraph_integer_t source, j, neighbor; + igraph_stack_int_t S; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; /* 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; + * Therefore, we use a 'igraph_real_t' instead. While a 'igraph_real_t' 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 'igraph_real_t', even + * for a 101*101 grid graph. */ + igraph_real_t *nrgeo = 0; + igraph_real_t *tmpscore; 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; + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); if (weights) { - return igraph_i_betweenness_cutoff_weighted(graph, res, vids, directed, - cutoff, weights); + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + } else { + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); } - if (!igraph_vs_is_all(&vids)) { - /* subset */ - IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); - } else { - /* only */ + IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + if (igraph_vs_is_all(&vids)) { + /* result covers all vertices */ 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); + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); } - IGRAPH_FINALLY(igraph_free, tmpscore); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); - igraph_stack_init(&stack, no_of_nodes); - IGRAPH_FINALLY(igraph_stack_destroy, &stack); + for (source = 0; source < no_of_nodes; source++) { - /* here we go */ + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' adjacency list contains empty vectors only + */ - 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; - } + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); + } else { + IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, cutoff)); + } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *neis = igraph_adjlist_get(&parents, actnode); + igraph_integer_t nneis = igraph_vector_int_size(neis); + igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; - 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]; + neighbor = VECTOR(*neis)[j]; + tmpscore[neighbor] += nrgeo[neighbor] * coeff; } if (actnode != source) { VECTOR(*tmpres)[actnode] += tmpscore[actnode]; } - /* Reset variables */ - distance[actnode] = 0; + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; nrgeo[actnode] = 0; tmpscore[actnode] = 0; - igraph_vector_int_clear(igraph_adjlist_get(adjlist_in_p, actnode)); + igraph_vector_int_clear(neis); } } /* 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]; + for (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[j] = VECTOR(*tmpres)[node]; } igraph_vit_destroy(&vit); @@ -480,254 +661,29 @@ int igraph_betweenness_cutoff(const igraph_t *graph, igraph_vector_t *res, 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; - } + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); } - igraph_adjlist_destroy(&adjlist_out); - igraph_adjlist_destroy(&adjlist_in); - IGRAPH_FINALLY_CLEAN(2); - - return 0; -} - + IGRAPH_PROGRESS("Betweenness centrality: ", 100.0, 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. - */ + IGRAPH_FREE(nrgeo); + IGRAPH_FREE(tmpscore); + igraph_vector_destroy(&dist); + igraph_stack_int_destroy(&S); + igraph_adjlist_destroy(&parents); + if (weights) { + igraph_inclist_destroy(&inclist); + } else { + igraph_adjlist_destroy(&adjlist); + } + IGRAPH_FINALLY_CLEAN(6); -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); + return IGRAPH_SUCCESS; } /***** 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 @@ -761,7 +717,7 @@ static int igraph_i_edge_betweenness_cutoff_weighted( * 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_error_t 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, @@ -800,217 +756,530 @@ int igraph_edge_betweenness(const igraph_t *graph, igraph_vector_t *result, * \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_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_inclist_t inclist, parents; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_integer_t source, j; + igraph_stack_int_t S; - if (weights) { - return igraph_i_edge_betweenness_cutoff_weighted(graph, result, - directed, cutoff, weights); - } + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); - 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; - } + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); - 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_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness calculation."); IGRAPH_FINALLY(igraph_free, nrgeo); - tmpscore = IGRAPH_CALLOC(no_of_nodes, double); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); if (tmpscore == 0) { - IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for edge betweenness calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); - igraph_vector_null(result); - /* here we go */ - for (source = 0; source < no_of_nodes; source++) { + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' incidence list contains empty vectors only + */ + 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; + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, cutoff)); + } else { + IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, cutoff)); + } - while (!igraph_dqueue_empty(&q)) { - long int actnode = (long int) igraph_dqueue_pop(&q); + /* Aggregate betweenness scores for the edges we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; - if (cutoff >= 0 && distance[actnode] > cutoff ) { - /* Reset variables if node is too distant */ - distance[actnode] = 0; - tmpscore[actnode] = 0; - nrgeo[actnode] = 0; - continue; + for (j = 0; j < fatv_len; j++) { + igraph_integer_t fedge = VECTOR(*fatv)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, fedge, actnode); + tmpscore[neighbor] += nrgeo[neighbor] * coeff; + VECTOR(*result)[fedge] += nrgeo[neighbor] * coeff; } - 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; + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); } - /* Ok, we've the scores for this source */ - } /* for source <= no_of_nodes */ + } /* source < no_of_nodes */ + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(result, 0.5); + } + IGRAPH_PROGRESS("Edge betweenness centrality: ", 100.0, 0); - /* clean and return */ - IGRAPH_FREE(distance); - IGRAPH_FREE(nrgeo); + igraph_stack_int_destroy(&S); + igraph_inclist_destroy(&inclist); + igraph_inclist_destroy(&parents); + igraph_vector_destroy(&dist); IGRAPH_FREE(tmpscore); - igraph_dqueue_destroy(&q); - igraph_stack_destroy(&stack); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FREE(nrgeo); + IGRAPH_FINALLY_CLEAN(6); - if (directed) { - igraph_inclist_destroy(&elist_out); - igraph_inclist_destroy(&elist_in); - IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_betweenness_subset + * \brief Betweenness centrality for a subset of source and target vertices. + * + * This function computes the subset-limited version of betweenness centrality + * by considering only those shortest paths that lie between vertices in a given + * source and target subset. + * + * \param graph The graph object. + * \param res The result of the computation, a vector containing the + * betweenness score for the subset of vertices. + * \param vids The vertices for which the subset-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 sources A vertex selector for the sources of the shortest paths taken + * into considuration in the betweenness calculation. + * \param targets A vertex selector for the targets of the shortest paths taken + * into considuration in the betweenness calculation. + * \return Error code: + * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_EINVVID, invalid vertex ID passed in \p vids, + * \p sources or \p targets + * + * Time complexity: O(|S||E|), where + * |S| is the number of vertices in the subset and + * |E| is the number of edges in the graph. + * + * \sa \ref igraph_betweenness() to calculate the exact vertex betweenness and + * \ref igraph_betweenness_cutoff() to calculate the range-limited vertex + * betweenness. + */ +igraph_error_t igraph_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_vs_t vids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_sources; + igraph_integer_t no_of_processed_sources; + igraph_adjlist_t adjlist, parents; + igraph_inclist_t inclist; + igraph_integer_t source, j; + igraph_stack_int_t S; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_integer_t father; + igraph_vector_t dist; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_vit_t vit; + bool *is_target; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); + + if (weights) { + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); } else { - igraph_inclist_destroy(&elist_out); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); } - /* 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; + IGRAPH_CHECK(igraph_adjlist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &parents); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + is_target = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset betweenness calculation."); + IGRAPH_FINALLY(igraph_free, is_target); + + IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + is_target[IGRAPH_VIT_GET(vit)] = true; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + if (!igraph_vs_is_all(&vids)) { + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_nodes); + } else { + /* result covers all vertices */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + tmpres = res; + } + + IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + for ( + no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ + ) { + source = IGRAPH_VIT_GET(vit); + + IGRAPH_PROGRESS( + "Betweenness centrality (subset): ", + 100.0 * no_of_processed_sources / no_of_sources, 0 + ); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' adjacency list contains empty vectors only + */ + + /* TODO: there is more room for optimization here; the single-source + * shortest path search runs until it reaches all the nodes in the + * component of the source node even if we are only interested in a + * smaller target subset. We could stop the search when all target + * nodes were reached. + */ + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); + } else { + IGRAPH_CHECK(igraph_i_sspf(source, &dist, nrgeo, &S, &parents, &adjlist, -1)); } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_adjlist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff; + + if (is_target[actnode]) { + coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + } else { + coeff = tmpscore[actnode] / nrgeo[actnode]; + } + + for (j = 0; j < fatv_len; j++) { + father = VECTOR(*fatv)[j]; + tmpscore[father] += nrgeo[father] * coeff; + } + + if (actnode != source) { + VECTOR(*tmpres)[actnode] += tmpscore[actnode]; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); + } + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + /* 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 (j = 0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), j++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + VECTOR(*res)[j] = VECTOR(*tmpres)[node]; + } + + igraph_vit_destroy(&vit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); + } + + IGRAPH_FREE(is_target); + IGRAPH_FREE(tmpscore); + IGRAPH_FREE(nrgeo); + igraph_vector_destroy(&dist); + igraph_stack_int_destroy(&S); + igraph_adjlist_destroy(&parents); + if (weights) { + igraph_inclist_destroy(&inclist); + } else { + igraph_adjlist_destroy(&adjlist); } + IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup structural - * \function igraph_edge_betweenness_estimate - * \brief Estimated betweenness centrality of the edges. + * \function igraph_edge_betweenness_subset + * \brief Edge betweenness centrality for a subset of source and target vertices. * - * \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. + * This function computes the subset-limited version of edge betweenness centrality + * by considering only those shortest paths that lie between vertices in a given + * source and target subset. * * \param graph The graph object. - * \param result The result of the computation, vector containing the + * \param res The result of the computation, vector containing the * betweenness scores for the edges. + * \param eids The edges for which the subset-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 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. + * \param weights An optional weight vector for weighted + * betweenness. No edge weight may be NaN. Supply a null + * pointer here for unweighted betweenness. + * \param sources A vertex selector for the sources of the shortest paths taken + * into considuration in the betweenness calculation. + * \param targets A vertex selector for the targets of the shortest paths taken + * into considuration in the betweenness calculation. * \return Error code: - * \c IGRAPH_ENOMEM, not enough memory for - * temporary data. + * \c IGRAPH_ENOMEM, not enough memory for temporary data. + * \c IGRAPH_EINVVID, invalid vertex ID passed in \p sources or \p targets * - * Time complexity: O(|V||E|), - * |V| and - * |E| are the number of vertices and - * edges in the graph. + * Time complexity: O(|S||E|), where + * |S| is the number of vertices in the subset and + * |E| is the number of 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. + * \sa \ref igraph_edge_betweenness() to compute the exact edge betweenness and + * \ref igraph_edge_betweenness_cutoff() to compute the range-limited edge betweenness. */ -int igraph_edge_betweenness_estimate(const igraph_t *graph, igraph_vector_t *result, - igraph_bool_t directed, igraph_real_t cutoff, +igraph_error_t igraph_edge_betweenness_subset(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t directed, + const igraph_vs_t sources, const igraph_vs_t targets, 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); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_sources; + igraph_integer_t no_of_processed_sources; + igraph_inclist_t inclist, parents; + igraph_vit_t vit; + igraph_eit_t eit; + igraph_neimode_t mode = directed ? IGRAPH_OUT : IGRAPH_ALL; + igraph_vector_t dist; + igraph_vector_t v_tmpres, *tmpres = &v_tmpres; + igraph_real_t *nrgeo; + igraph_real_t *tmpscore; + igraph_integer_t source, j; + bool *is_target; + igraph_stack_int_t S; + + IGRAPH_CHECK(igraph_i_betweenness_check_weights(weights, no_of_edges)); + + IGRAPH_CHECK(igraph_vs_size(graph, &sources, &no_of_sources)); + + is_target = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, is_target); + + IGRAPH_CHECK(igraph_vit_create(graph, targets, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + is_target[IGRAPH_VIT_GET(vit)] = true; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, mode, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_inclist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); + + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); + + nrgeo = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(nrgeo, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, nrgeo); + + tmpscore = IGRAPH_CALLOC(no_of_nodes, igraph_real_t); + IGRAPH_CHECK_OOM(tmpscore, "Insufficient memory for subset edge betweenness calculation."); + IGRAPH_FINALLY(igraph_free, tmpscore); + + IGRAPH_CHECK(igraph_stack_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &S); + + if (!igraph_es_is_all(&eids)) { + /* result needed only for a subset of the vertices */ + IGRAPH_VECTOR_INIT_FINALLY(tmpres, no_of_edges); + } else { + /* result covers all vertices */ + IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges)); + igraph_vector_null(res); + tmpres = res; + } + + IGRAPH_CHECK(igraph_vit_create(graph, sources, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + for ( + no_of_processed_sources = 0, IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit), no_of_processed_sources++ + ) { + source = IGRAPH_VIT_GET(vit); + + IGRAPH_PROGRESS( + "Edge betweenness centrality (subset): ", + 100.0 * no_of_processed_sources / no_of_sources, 0 + ); + IGRAPH_ALLOW_INTERRUPTION(); + + /* Loop invariant that is valid at this point: + * + * - the stack S is empty + * - the 'dist' vector contains zeros only + * - the 'nrgeo' array contains zeros only + * - the 'tmpscore' array contains zeros only + * - the 'parents' incidence list contains empty vectors only + */ + + /* TODO: there is more room for optimization here; the single-source + * shortest path search runs until it reaches all the nodes in the + * component of the source node even if we are only interested in a + * smaller target subset. We could stop the search when all target + * nodes were reached. + */ + + /* Conduct a single-source shortest path search from the source node */ + if (weights) { + IGRAPH_CHECK(igraph_i_sspf_weighted_edge(graph, source, &dist, nrgeo, weights, &S, &parents, &inclist, -1)); + } else { + IGRAPH_CHECK(igraph_i_sspf_edge(graph, source, &dist, nrgeo, &S, &parents, &inclist, -1)); + } + + /* Aggregate betweenness scores for the nodes we have reached in this + * traversal */ + while (!igraph_stack_int_empty(&S)) { + igraph_integer_t actnode = igraph_stack_int_pop(&S); + igraph_vector_int_t *fatv = igraph_inclist_get(&parents, actnode); + igraph_integer_t fatv_len = igraph_vector_int_size(fatv); + igraph_real_t coeff; + + if (is_target[actnode]) { + coeff = (1 + tmpscore[actnode]) / nrgeo[actnode]; + } else { + coeff = tmpscore[actnode] / nrgeo[actnode]; + } + + for (j = 0; j < fatv_len; j++) { + igraph_integer_t father_edge = VECTOR(*fatv)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, father_edge, actnode); + tmpscore[neighbor] += nrgeo[neighbor] * coeff; + VECTOR(*tmpres)[father_edge] += nrgeo[neighbor] * coeff; + } + + /* Reset variables to ensure that the 'for' loop invariant will + * still be valid in the next iteration */ + + VECTOR(dist)[actnode] = 0; + nrgeo[actnode] = 0; + tmpscore[actnode] = 0; + igraph_vector_int_clear(fatv); + } + } + + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); + + /* Keep only the requested edges */ + if (!igraph_es_is_all(&eids)) { + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (j = 0, IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), j++) { + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + VECTOR(*res)[j] = VECTOR(*tmpres)[edge]; + } + + igraph_eit_destroy(&eit); + igraph_vector_destroy(tmpres); + IGRAPH_FINALLY_CLEAN(2); + } + + + if (!directed || !igraph_is_directed(graph)) { + igraph_vector_scale(res, 0.5); + } + + igraph_stack_int_destroy(&S); + IGRAPH_FREE(tmpscore); + IGRAPH_FREE(nrgeo); + igraph_vector_destroy(&dist); + igraph_inclist_destroy(&parents); + igraph_inclist_destroy(&inclist); + IGRAPH_FREE(is_target); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/centrality/centrality_internal.h b/src/vendor/cigraph/src/centrality/centrality_internal.h new file mode 100644 index 00000000000..4479a3466ae --- /dev/null +++ b/src/vendor/cigraph/src/centrality/centrality_internal.h @@ -0,0 +1,37 @@ +/* -*- 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_CENTRALITY_INTERNAL_H +#define IGRAPH_CENTRALITY_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vector); + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/src/centrality/centrality_other.c b/src/vendor/cigraph/src/centrality/centrality_other.c index 55d16978426..6130b253b0c 100644 --- a/src/vendor/cigraph/src/centrality/centrality_other.c +++ b/src/vendor/cigraph/src/centrality/centrality_other.c @@ -18,37 +18,9 @@ along with this program. If not, see . */ -#include "igraph_centrality.h" +#include "centrality/centrality_internal.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) { +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 @@ -59,1530 +31,22 @@ static igraph_bool_t igraph_i_vector_mostly_negative(const igraph_vector_t *vect * the values are relatively large negative numbers, in which case we should * negate the eigenvector. */ - long int n = igraph_vector_size(vector); + igraph_integer_t n = igraph_vector_size(vector); igraph_real_t mi, ma; if (n == 0) { - return 0; + return false; } igraph_vector_minmax(vector, &mi, &ma); if (mi >= 0) { - return 0; + return false; } if (ma <= 0) { - return 1; + return true; } /* 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; - igraph_bool_t negative_weights = 0; - 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; - } - - if (min < 0) { - /* When there are negative weights, the eigenvalue and the eigenvector are no - * longer guaranteed to be non-negative. */ - negative_weights = 1; - IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " - "will be selected, but it may not be the largest in magnitude."); - } - } - - IGRAPH_VECTOR_INIT_FINALLY(&values, 0); - IGRAPH_MATRIX_INIT_FINALLY(&vectors, options->n, 1); - - IGRAPH_VECTOR_INIT_FINALLY(°ree, options->n); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), - IGRAPH_ALL, IGRAPH_LOOPS, weights)); - 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 (vector) { - igraph_real_t amax = 0; - long int which = 0; - long int i; - IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); - - if (!negative_weights && 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); - VECTOR(values)[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) */ - if (! negative_weights) { - for (i = 0; i < options->n; i++) { - if (VECTOR(*vector)[i] < 0) { - VECTOR(*vector)[i] = 0; - } - } - } - } - } - - if (value) { - *value = VECTOR(values)[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; -} - - -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; - igraph_bool_t negative_weights = 0; - 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("The 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); - } - - /* Safe to call minmax, ecount == 0 case was caught earlier */ - IGRAPH_CHECK(igraph_vector_minmax(weights, &min, &max)); - - if (min < 0.0) { - /* When there are negative weights, the eigenvalue and the eigenvector are no - * longer guaranteed to be non-negative, or even real-valued. */ - negative_weights = 1; - IGRAPH_WARNING("Negative weights in directed graph, eigenpair may 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, IGRAPH_LOOPS, 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 (vector) { - igraph_real_t amax = 0; - long int which = 0; - long int i; - IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); - - if (!negative_weights && MATRIX(values, 0, 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) */ - if (! negative_weights) { - for (i = 0; i < options->n; i++) { - if (VECTOR(*vector)[i] < 0) { - VECTOR(*vector)[i] = 0; - } - } - } - } - - if (value) { - *value = MATRIX(values, 0, 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 IGRAPH_SUCCESS; -} - -/** - * \function igraph_eigenvector_centrality - * \brief 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 (strongly) connected graphs. - * Undirected 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 (indicating no edge weights), or a vector - * giving the weights of the edges. Weights should be positive to guarantee - * a meaningful result. The algorithm might produce complex numbers when some - * weights are negative and the graph is directed. 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_TWICE, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &myinadjlist); - IGRAPH_CHECK(igraph_adjlist_init(graph, &myoutadjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); - IGRAPH_FINALLY(igraph_adjlist_destroy, &myoutadjlist); - } else { - IGRAPH_CHECK(igraph_inclist_init(graph, &myininclist, IGRAPH_IN, IGRAPH_LOOPS_TWICE)); - IGRAPH_FINALLY(igraph_inclist_destroy, &myininclist); - IGRAPH_CHECK(igraph_inclist_init(graph, &myoutinclist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); - 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] = 'A'; - - 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] = 'R'; - 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/vendor/cigraph/src/centrality/centralization.c b/src/vendor/cigraph/src/centrality/centralization.c index 308e9fd1ad3..544dfca97a0 100644 --- a/src/vendor/cigraph/src/centrality/centralization.c +++ b/src/vendor/cigraph/src/centrality/centralization.c @@ -21,13 +21,14 @@ #include "igraph_centrality.h" #include "igraph_interface.h" +#include "igraph_structural.h" #include "igraph_vector.h" #include "core/math.h" /** * \function igraph_centralization - * Calculate the centralization score from the node level scores + * \brief 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 @@ -35,12 +36,14 @@ * 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, + * + * 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 + * + * 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. @@ -50,8 +53,7 @@ * 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 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. @@ -74,7 +76,7 @@ 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_integer_t no_of_nodes = igraph_vector_size(scores); igraph_real_t maxscore = 0.0; igraph_real_t cent = 0.0; @@ -93,12 +95,13 @@ igraph_real_t igraph_centralization(const igraph_vector_t *scores, /** * \function igraph_centralization_degree - * Calculate vertex degree and graph centralization + * \brief 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. @@ -125,7 +128,7 @@ igraph_real_t igraph_centralization(const igraph_vector_t *scores, * score. */ -int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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, @@ -144,10 +147,8 @@ int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, 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)); + IGRAPH_CHECK(igraph_strength(graph, scores, igraph_vss_all(), mode, loops, 0)); + IGRAPH_CHECK(igraph_centralization_degree_tmax(graph, 0, mode, loops, tmax)); *centralization = igraph_centralization(scores, *tmax, normalized); @@ -156,12 +157,12 @@ int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_degree_tmax - * Theoretical maximum for graph centralization based on degree + * \brief Theoretical maximum for graph centralization based on degree. * * This function returns the theoretical maximum graph centrality * based on vertex degree. @@ -183,6 +184,7 @@ int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, * 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 @@ -203,7 +205,7 @@ int igraph_centralization_degree(const igraph_t *graph, igraph_vector_t *res, * igraph_centralization(). */ -int igraph_centralization_degree_tmax(const igraph_t *graph, +igraph_error_t igraph_centralization_degree_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_bool_t loops, @@ -245,17 +247,18 @@ int igraph_centralization_degree_tmax(const igraph_t *graph, } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_betweenness - * Calculate vertex betweenness and graph centralization + * \brief 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. @@ -279,7 +282,7 @@ int igraph_centralization_degree_tmax(const igraph_t *graph, * centralization score. */ -int igraph_centralization_betweenness(const igraph_t *graph, +igraph_error_t igraph_centralization_betweenness(const igraph_t *graph, igraph_vector_t *res, igraph_bool_t directed, igraph_real_t *centralization, @@ -310,12 +313,12 @@ int igraph_centralization_betweenness(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_betweenness_tmax - * Theoretical maximum for graph centralization based on betweenness + * \brief Theoretical maximum for graph centralization based on betweenness. * * This function returns the theoretical maximum graph centrality * based on vertex betweenness. @@ -335,6 +338,7 @@ int igraph_centralization_betweenness(const igraph_t *graph, * * * 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 @@ -351,7 +355,7 @@ int igraph_centralization_betweenness(const igraph_t *graph, * igraph_centralization(). */ -int igraph_centralization_betweenness_tmax(const igraph_t *graph, +igraph_error_t igraph_centralization_betweenness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, igraph_real_t *res) { @@ -370,17 +374,18 @@ int igraph_centralization_betweenness_tmax(const igraph_t *graph, *res = (real_nodes - 1) * (real_nodes - 1) * (real_nodes - 2) / 2.0; } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_closeness - * Calculate vertex closeness and graph centralization + * \brief 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. @@ -406,7 +411,7 @@ int igraph_centralization_betweenness_tmax(const igraph_t *graph, * centralization score. */ -int igraph_centralization_closeness(const igraph_t *graph, +igraph_error_t igraph_centralization_closeness(const igraph_t *graph, igraph_vector_t *res, igraph_neimode_t mode, igraph_real_t *centralization, @@ -427,7 +432,7 @@ int igraph_centralization_closeness(const igraph_t *graph, } IGRAPH_CHECK(igraph_closeness(graph, scores, NULL, NULL, igraph_vss_all(), mode, - /*weights=*/ 0, /*normalize=*/ 1)); + /*weights=*/ 0, /*normalized=*/ 1)); IGRAPH_CHECK(igraph_centralization_closeness_tmax(graph, 0, mode, tmax)); @@ -439,12 +444,12 @@ int igraph_centralization_closeness(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_closeness_tmax - * Theoretical maximum for graph centralization based on closeness + * \brief Theoretical maximum for graph centralization based on closeness. * * This function returns the theoretical maximum graph centrality * based on vertex closeness. @@ -482,7 +487,7 @@ int igraph_centralization_closeness(const igraph_t *graph, * igraph_centralization(). */ -int igraph_centralization_closeness_tmax(const igraph_t *graph, +igraph_error_t igraph_centralization_closeness_tmax(const igraph_t *graph, igraph_integer_t nodes, igraph_neimode_t mode, igraph_real_t *res) { @@ -503,12 +508,12 @@ int igraph_centralization_closeness_tmax(const igraph_t *graph, *res = (real_nodes - 1) * (real_nodes - 2) / (2.0 * real_nodes - 3); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_eigenvector_centrality - * Calculate eigenvector centrality scores and graph centralization + * \brief 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); @@ -544,7 +549,7 @@ int igraph_centralization_closeness_tmax(const igraph_t *graph, * for the calculating the centralization. */ -int igraph_centralization_eigenvector_centrality( +igraph_error_t igraph_centralization_eigenvector_centrality( const igraph_t *graph, igraph_vector_t *vector, igraph_real_t *value, @@ -588,12 +593,12 @@ int igraph_centralization_eigenvector_centrality( IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_centralization_eigenvector_centrality_tmax - * Theoretical maximum centralization for eigenvector centrality + * \brief Theoretical maximum centralization for eigenvector centrality. * * This function returns the theoretical maximum graph centrality * based on vertex eigenvector centrality. @@ -632,7 +637,7 @@ int igraph_centralization_eigenvector_centrality( * igraph_centralization(). */ -int igraph_centralization_eigenvector_centrality_tmax( +igraph_error_t igraph_centralization_eigenvector_centrality_tmax( const igraph_t *graph, igraph_integer_t nodes, igraph_bool_t directed, @@ -654,5 +659,5 @@ int igraph_centralization_eigenvector_centrality_tmax( } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/centrality/closeness.c b/src/vendor/cigraph/src/centrality/closeness.c index 643532121f9..408cac4536c 100644 --- a/src/vendor/cigraph/src/centrality/closeness.c +++ b/src/vendor/cigraph/src/centrality/closeness.c @@ -27,7 +27,6 @@ #include "core/indheap.h" #include "core/interruption.h" -#include "core/math.h" /***** Closeness centrality *****/ @@ -101,7 +100,7 @@ * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -116,17 +115,17 @@ * \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, +igraph_error_t igraph_closeness(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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, +static igraph_error_t igraph_i_closeness_cutoff_weighted(const igraph_t *graph, igraph_vector_t *res, - igraph_vector_t *reachable_count, + igraph_vector_int_t *reachable_count, igraph_bool_t *all_reachable, const igraph_vs_t vids, igraph_neimode_t mode, @@ -134,22 +133,22 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, const igraph_vector_t *weights, igraph_bool_t normalized) { - /* See igraph_shortest_paths_dijkstra() for the implementation + /* See igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t vit; - long int nodes_to_calc; + igraph_integer_t nodes_to_calc; igraph_lazy_inclist_t inclist; - long int i, j; + igraph_integer_t i, j; igraph_vector_t dist; - igraph_vector_long_t which; - long int nodes_reached; + igraph_vector_int_t which; + igraph_integer_t nodes_reached; igraph_real_t mindist = 0; @@ -161,7 +160,7 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, 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)) { + } else if (isnan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -172,7 +171,7 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, nodes_to_calc = IGRAPH_VIT_SIZE(vit); if (reachable_count) { - igraph_vector_resize(reachable_count, nodes_to_calc); + IGRAPH_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); } if (all_reachable) { @@ -185,15 +184,15 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, 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_int_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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_integer_t source = IGRAPH_VIT_GET(vit); igraph_2wheap_clear(&Q); igraph_2wheap_push_with_index(&Q, source, -1.0); VECTOR(which)[source] = i + 1; @@ -201,11 +200,14 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, nodes_reached = 0; while (!igraph_2wheap_empty(&Q)) { - igraph_integer_t minnei = (igraph_integer_t) igraph_2wheap_max_index(&Q); + igraph_integer_t minnei = 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); + igraph_integer_t nlen; + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + nlen = igraph_vector_int_size(neis); mindist = -igraph_2wheap_delete_max(&Q); if (cutoff >= 0 && (mindist - 1.0) > cutoff) { @@ -216,8 +218,8 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, 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_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t to = IGRAPH_OTHER(graph, edge, minnei); igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; igraph_real_t curdist = VECTOR(dist)[to]; @@ -229,7 +231,7 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, } 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_modify(&Q, to, -altdist); } } @@ -254,94 +256,16 @@ static int igraph_i_closeness_cutoff_weighted(const igraph_t *graph, } } /* !IGRAPH_VIT_END(vit) */ - igraph_vector_long_destroy(&which); + igraph_vector_int_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); + return IGRAPH_SUCCESS; } - /** * \ingroup structural * \function igraph_closeness_cutoff @@ -387,7 +311,7 @@ int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -401,25 +325,25 @@ int igraph_closeness_estimate(const igraph_t *graph, igraph_vector_t *res, * \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, +igraph_error_t igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t already_counted; igraph_vector_int_t *neis; - long int i, j; - long int nodes_reached; + igraph_integer_t i, j; + igraph_integer_t nodes_reached; igraph_adjlist_t allneis; - long int actdist = 0; + igraph_integer_t actdist = 0; - igraph_dqueue_t q; + igraph_dqueue_int_t q; - long int nodes_to_calc; + igraph_integer_t nodes_to_calc; igraph_vit_t vit; if (weights) { @@ -433,7 +357,7 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, nodes_to_calc = IGRAPH_VIT_SIZE(vit); if (reachable_count) { - igraph_vector_resize(reachable_count, nodes_to_calc); + IGRAPH_CHECK(igraph_vector_int_resize(reachable_count, nodes_to_calc)); } if (all_reachable) { @@ -444,8 +368,8 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, IGRAPH_ERROR("Invalid mode for closeness.", IGRAPH_EINVMODE); } - IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); @@ -458,17 +382,17 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, 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_dqueue_int_clear(&q); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(vit))); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + VECTOR(already_counted)[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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + actdist = igraph_dqueue_int_pop(&q); if (cutoff >= 0 && actdist > cutoff) { continue; /* NOT break!!! */ @@ -479,14 +403,15 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, /* 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]; + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } } @@ -512,8 +437,8 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, IGRAPH_PROGRESS("Closeness: ", 100.0, NULL); /* Clean */ - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&already_counted); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&already_counted); igraph_vit_destroy(&vit); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(4); @@ -524,22 +449,22 @@ int igraph_closeness_cutoff(const igraph_t *graph, igraph_vector_t *res, /***** Harmonic centrality *****/ -static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph_vector_t *res, +static igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_vector_t already_counted; igraph_vector_int_t *neis; - long int i, j; + igraph_integer_t i, j; igraph_adjlist_t allneis; - long int actdist = 0; + igraph_integer_t actdist = 0; - igraph_dqueue_t q; + igraph_dqueue_int_t q; - long int nodes_to_calc; + igraph_integer_t nodes_to_calc; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); @@ -553,7 +478,7 @@ static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph } IGRAPH_VECTOR_INIT_FINALLY(&already_counted, no_of_nodes); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); @@ -565,19 +490,19 @@ static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int source = IGRAPH_VIT_GET(vit); + igraph_integer_t source = IGRAPH_VIT_GET(vit); - igraph_dqueue_clear(&q); - IGRAPH_CHECK(igraph_dqueue_push(&q, source)); - IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + igraph_dqueue_int_clear(&q); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + actdist = igraph_dqueue_int_pop(&q); if (cutoff >= 0 && actdist > cutoff) { continue; /* NOT break!!! */ @@ -590,14 +515,15 @@ static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph /* 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]; + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } } } @@ -609,7 +535,7 @@ static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph IGRAPH_PROGRESS("Harmonic centrality: ", 100.0, NULL); /* Clean */ - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); igraph_vector_destroy(&already_counted); igraph_vit_destroy(&vit); igraph_adjlist_destroy(&allneis); @@ -619,7 +545,7 @@ static int igraph_i_harmonic_centrality_unweighted(const igraph_t *graph, igraph } -static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, +static igraph_error_t igraph_i_harmonic_centrality_weighted(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_neimode_t mode, @@ -627,21 +553,21 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, igraph_bool_t normalized, igraph_real_t cutoff) { - /* See igraph_shortest_paths_dijkstra() for the implementation + /* See igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t vit; - long int nodes_to_calc; + igraph_integer_t nodes_to_calc; igraph_lazy_inclist_t inclist; - long int i, j; + igraph_integer_t i, j; igraph_vector_t dist; - igraph_vector_long_t which; + igraph_vector_int_t which; igraph_real_t mindist = 0; @@ -653,7 +579,7 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, 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)) { + } else if (isnan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -669,26 +595,29 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, 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_int_init(&which, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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_integer_t 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); + igraph_integer_t minnei = 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); + igraph_integer_t nlen; + + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); mindist = -igraph_2wheap_delete_max(&Q); if (cutoff >= 0 && (mindist - 1.0) > cutoff) { @@ -701,8 +630,8 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, } for (j = 0; j < nlen; j++) { - long int edge = (long int) VECTOR(*neis)[j]; - long int to = IGRAPH_OTHER(graph, edge, minnei); + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t to = IGRAPH_OTHER(graph, edge, minnei); igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; igraph_real_t curdist = VECTOR(dist)[to]; @@ -714,7 +643,7 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, } 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_modify(&Q, to, -altdist); } } @@ -726,7 +655,7 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, igraph_vector_scale(res, 1.0 / (no_of_nodes - 1)); } - igraph_vector_long_destroy(&which); + igraph_vector_int_destroy(&which); igraph_vector_destroy(&dist); igraph_lazy_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); @@ -780,7 +709,7 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -792,7 +721,7 @@ static int igraph_i_harmonic_centrality_weighted(const igraph_t *graph, * \sa Other centrality types: \ref igraph_closeness(), \ref igraph_betweenness(). */ -int igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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, @@ -857,7 +786,7 @@ int igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *re * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -869,7 +798,7 @@ int igraph_harmonic_centrality_cutoff(const igraph_t *graph, igraph_vector_t *re * \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, +igraph_error_t 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) { diff --git a/src/vendor/cigraph/src/centrality/coreness.c b/src/vendor/cigraph/src/centrality/coreness.c index 28ac5042b47..34b3fc42a6b 100644 --- a/src/vendor/cigraph/src/centrality/coreness.c +++ b/src/vendor/cigraph/src/centrality/coreness.c @@ -40,6 +40,8 @@ * This function implements the algorithm presented in Vladimir * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores * Decomposition of Networks. + * https://arxiv.org/abs/cs/0310049 + * * \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 @@ -55,14 +57,14 @@ * Time complexity: O(|E|), the number of edges. */ -int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, - igraph_neimode_t mode) { +igraph_error_t igraph_coreness(const igraph_t *graph, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *bin, *vert, *pos; + igraph_integer_t maxdeg; + igraph_integer_t i, j = 0; + igraph_vector_int_t neis; igraph_neimode_t omode; if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { @@ -77,50 +79,50 @@ int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, } if (no_of_nodes == 0) { - igraph_vector_clear(cores); + igraph_vector_int_clear(cores); return IGRAPH_SUCCESS; } - vert = IGRAPH_CALLOC(no_of_nodes, long int); + vert = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (vert == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, vert); - pos = IGRAPH_CALLOC(no_of_nodes, long int); + pos = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (pos == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, /* loops= */ true)); + + maxdeg = igraph_vector_int_max(cores); - bin = IGRAPH_CALLOC(maxdeg + 1, long int); + bin = IGRAPH_CALLOC(maxdeg + 1, igraph_integer_t); if (bin == 0) { - IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot calculate k-cores", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, bin); /* degree histogram */ for (i = 0; i < no_of_nodes; i++) { - bin[ (long int)VECTOR(*cores)[i] ] += 1; + bin[VECTOR(*cores)[i] ] += 1; } /* start pointers */ j = 0; for (i = 0; i <= maxdeg; i++) { - long int k = bin[i]; + igraph_integer_t 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]]; + pos[i] = bin[VECTOR(*cores)[i]]; vert[pos[i]] = i; - bin[(long int)VECTOR(*cores)[i]] += 1; + bin[VECTOR(*cores)[i]] += 1; } /* correct bin */ @@ -130,17 +132,18 @@ int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, bin[0] = 0; /* this is the main algorithm */ - IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t v = vert[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, omode)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (j = 0; j < nei_count; j++) { + igraph_integer_t u = 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]; + igraph_integer_t du = VECTOR(*cores)[u]; + igraph_integer_t pu = pos[u]; + igraph_integer_t pw = bin[du]; + igraph_integer_t w = vert[pw]; if (u != w) { pos[u] = pw; pos[w] = pu; @@ -153,12 +156,13 @@ int igraph_coreness(const igraph_t *graph, igraph_vector_t *cores, } } - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); igraph_free(bin); igraph_free(pos); igraph_free(vert); IGRAPH_FINALLY_CLEAN(3); - return 0; + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/centrality/eigenvector.c b/src/vendor/cigraph/src/centrality/eigenvector.c new file mode 100644 index 00000000000..b4514e718ad --- /dev/null +++ b/src/vendor/cigraph/src/centrality/eigenvector.c @@ -0,0 +1,549 @@ +/* -*- 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" +#include "igraph_topology.h" + +#include "centrality/centrality_internal.h" + +#include + +/* Multiplies vector 'from' by the unweighted adjacency matrix and stores the result in 'to'. */ +static igraph_error_t adjmat_mul_unweighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + igraph_adjlist_t *adjlist = extra; + igraph_vector_int_t *neis; + igraph_integer_t 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +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; + +/* Multiplies vector 'from' by the weighted adjacency matrix and stores the result in 'to'. */ +static igraph_error_t adjmat_mul_weighted(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; + igraph_integer_t 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++) { + igraph_integer_t edge = VECTOR(*edges)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + igraph_real_t w = VECTOR(*weights)[edge]; + to[i] += w * from[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t 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; + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_bool_t negative_weights = false; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(weights), igraph_ecount(graph)); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + + if (min < 0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative. */ + negative_weights = 1; + IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " + "will be selected, but it may not be the largest in magnitude."); + } + } + + IGRAPH_VECTOR_INIT_FINALLY(&values, 0); + IGRAPH_MATRIX_INIT_FINALLY(&vectors, no_of_nodes, 1); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), + IGRAPH_ALL, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); + for (i = 0; i < no_of_nodes; 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 = (int) no_of_nodes; + 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(adjmat_mul_unweighted, + &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(adjmat_mul_weighted, + &data, options, 0, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (vector) { + igraph_real_t amax = 0; + igraph_integer_t which = 0; + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + + if (!negative_weights && 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); + VECTOR(values)[0] = 0; + } else { + for (i = 0; i < no_of_nodes; 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) */ + if (! negative_weights) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + } + + if (value) { + *value = VECTOR(values)[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; +} + +static igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_bool_t negative_weights = false; + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(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_CHECK(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_ERRORF("Weights vector length (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(weights), igraph_ecount(graph)); + } + if (igraph_is_directed(graph)) { + IGRAPH_WARNING("Weighted directed graph in eigenvector centrality"); + } + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + + if (min < 0.0) { + /* When there are negative weights, the eigenvalue and the eigenvector are no + * longer guaranteed to be non-negative, or even real-valued. */ + negative_weights = true; + IGRAPH_WARNING("Negative weights in directed graph, eigenpair may be complex."); + } + if (min == 0.0 && max == 0.0) { + /* special case: all weights are zeros */ + if (value) { + *value = 0; + } + if (vector) { + IGRAPH_CHECK(igraph_vector_resize(vector, igraph_vcount(graph))); + igraph_vector_fill(vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + options->n = (int) no_of_nodes; + 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, no_of_nodes, 1); + + IGRAPH_VECTOR_INIT_FINALLY(&indegree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + IGRAPH_IN, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); + for (i = 0; i < no_of_nodes; 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(adjmat_mul_unweighted, + &adjlist, options, NULL, &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(adjmat_mul_weighted, + &data, options, NULL, &values, &vectors)); + + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } + + if (vector) { + igraph_real_t amax = 0; + igraph_integer_t which = 0; + + IGRAPH_CHECK(igraph_vector_resize(vector, options->n)); + + if (!negative_weights && 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 < no_of_nodes; 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) */ + if (! negative_weights) { + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(*vector)[i] < 0) { + VECTOR(*vector)[i] = 0; + } + } + } + } + + if (value) { + *value = MATRIX(values, 0, 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 IGRAPH_SUCCESS; +} + +/** + * \function igraph_eigenvector_centrality + * \brief 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 (strongly) connected graphs. + * Undirected 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. + * + * + * When working with directed graphs, consider using hub and authority + * scores instead, see \ref igraph_hub_and_authority_scores(). + * + * \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 (indicating no edge weights), or a vector + * giving the weights of the edges. Weights should be positive to guarantee + * a meaningful result. The algorithm might produce complex numbers when some + * weights are negative and the graph is directed. In this case only + * the real part is reported. + * \param options Options to ARPACK. See \ref igraph_arpack_options_t + * for details. Supply \c NULL here to use the defaults. 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. + * \ref igraph_hub_and_authority_scores() for a similar pair of measures + * intended for directed graphs. + * + * \example examples/simple/eigenvector_centrality.c + */ + +igraph_error_t 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 (!options) { + options = igraph_arpack_options_get_default(); + } + + 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); + } +} diff --git a/src/vendor/cigraph/src/centrality/hub_authority.c b/src/vendor/cigraph/src/centrality/hub_authority.c new file mode 100644 index 00000000000..896e848127d --- /dev/null +++ b/src/vendor/cigraph/src/centrality/hub_authority.c @@ -0,0 +1,495 @@ +/* -*- 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_structural.h" +#include "igraph_blas.h" + +#include "centrality/centrality_internal.h" + +#include + +/* 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; + +static igraph_error_t igraph_i_kleinberg_unweighted_hub_to_auth( + igraph_integer_t n, igraph_vector_t *to, const igraph_real_t *from, + igraph_adjlist_t *in) { + igraph_vector_int_t *neis; + igraph_integer_t i, j, nlen; + + for (i = 0; i < n; i++) { + neis = igraph_adjlist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*to)[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + VECTOR(*to)[i] += from[nei]; + } + } + return IGRAPH_SUCCESS; +} + +/* ARPACK auxiliary routine for the unweighted HITS algorithm */ +static igraph_error_t 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 *out = data->out; + igraph_vector_t *tmp = data->tmp; + igraph_vector_int_t *neis; + igraph_integer_t i, j, nlen; + + igraph_i_kleinberg_unweighted_hub_to_auth(n, tmp, from, data->in); + + 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++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + to[i] += VECTOR(*tmp)[nei]; + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_kleinberg_weighted_hub_to_auth(igraph_integer_t n, + igraph_vector_t *to, const igraph_real_t *from, igraph_inclist_t *in, + const igraph_t *g, const igraph_vector_t *weights) { + igraph_vector_int_t *neis; + igraph_integer_t nlen, i, j; + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(in, i); + nlen = igraph_vector_int_size(neis); + VECTOR(*to)[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t nei_edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); + VECTOR(*to)[i] += from[nei] * VECTOR(*weights)[nei_edge]; + } + } + return IGRAPH_SUCCESS; +} + +/* ARPACK auxiliary routine for the weighted HITS algorithm */ +static igraph_error_t 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 *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; + igraph_integer_t i, j, nlen; + + igraph_i_kleinberg_weighted_hub_to_auth(n, tmp, from, data->in, g, weights); + + 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++) { + igraph_integer_t nei_edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(g, nei_edge, i); + to[i] += VECTOR(*tmp)[nei] * VECTOR(*weights)[nei_edge]; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hub_and_authority_scores + * \brief Kleinberg's hub and authority scores. + * + * Hub and authority scores are a generalization of the ideas behind + * eigenvector centrality to directed graphs. The authority score of + * a vertex is proportional to the sum of the hub scores of vertices + * that point to it. Conversely, the hub score of a vertex is proportional + * to the sum of authority scores of vertices that it points to. + * + * + * The hub and authority scores of the vertices are defined as the principal + * eigenvectors of A A^T and A^T A, respectively, + * where A is the adjacency matrix of the graph and A^T + * is its transposed. + * + * + * The concept of hub and authority scores were developed for \em directed graphs. + * In undirected graphs, both the hub and authority scores are equal to the + * eigenvector centrality, which can be computed using + * \ref igraph_eigenvector_centrality(). + * + * + * 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). + * https://doi.org/10.1145/324133.324140 + * Also appears as IBM Research Report RJ 10076, May + * 1997. + * + * \param graph The input graph. Can be directed and undirected. + * \param hub_vector Pointer to an initialized vector, the hub scores are + * stored here. If a null pointer then it is ignored. + * \param authority_vector Pointer to an initialized vector, the authority scores are + * stored here. If a null pointer then it is ignored. + * \param value If not a null pointer then the eigenvalue + * corresponding to the calculated eigenvectors 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 (meaning 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. Supply \c NULL here to use the defaults. 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(), \ref igraph_authority_score() + * for the separate calculations, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for a similar measure intended + * for undirected graphs. + */ +igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, + igraph_vector_t *hub_vector, igraph_vector_t *authority_vector, + igraph_real_t *value, igraph_bool_t scale, + const igraph_vector_t *weights, igraph_arpack_options_t *options) { + + igraph_adjlist_t inadjlist, outadjlist; + igraph_inclist_t ininclist, outinclist; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t tmp; + igraph_vector_t values; + igraph_matrix_t vectors; + igraph_i_kleinberg_data_t extra; + igraph_i_kleinberg_data2_t extra2; + igraph_vector_t *my_hub_vector_p; + igraph_vector_t my_hub_vector; + + + if (igraph_ecount(graph) == 0) { + /* special case: empty graph */ + if (value) { + *value = igraph_ecount(graph) ? 1.0 : IGRAPH_NAN; + } + if (hub_vector) { + IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); + igraph_vector_fill(hub_vector, 1.0); + } + if (authority_vector) { + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_fill(authority_vector, 1.0); + } + return IGRAPH_SUCCESS; + } + + if (weights) { + igraph_real_t min, max; + + if (igraph_vector_size(weights) != igraph_ecount(graph)) { + IGRAPH_ERRORF( + "Weights vector length (%" IGRAPH_PRId ") should match number of " + "edges (%" IGRAPH_PRId ") when calculating " + "hub or authority scores.", + IGRAPH_EINVAL, + igraph_vector_size(weights), + igraph_ecount(graph)); + } + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + if (min == 0 && max == 0) { + /* special case: all weights are zeros */ + if (value) { + *value = IGRAPH_NAN; + } + if (hub_vector) { + IGRAPH_CHECK(igraph_vector_resize(hub_vector, no_of_nodes)); + igraph_vector_fill(hub_vector, 1); + } + if (authority_vector) { + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_fill(authority_vector, 1); + } + return IGRAPH_SUCCESS; + } + } + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK", IGRAPH_EOVERFLOW); + } + + if (!options) { + options = igraph_arpack_options_get_default(); + } + + options->n = no_of_nodes; + 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 (weights == NULL) { + IGRAPH_CHECK(igraph_adjlist_init(graph, &inadjlist, IGRAPH_IN, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &inadjlist); + IGRAPH_CHECK(igraph_adjlist_init(graph, &outadjlist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &outadjlist); + } else { + IGRAPH_CHECK(igraph_inclist_init(graph, &ininclist, IGRAPH_IN, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &ininclist); + IGRAPH_CHECK(igraph_inclist_init(graph, &outinclist, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &outinclist); + } + + IGRAPH_CHECK(igraph_strength(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0, 0)); + for (igraph_integer_t 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] = 'A'; + + if (weights == NULL) { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_unweighted, &extra, + options, 0, &values, &vectors)); + } else { + IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_kleinberg_weighted, &extra2, + options, 0, &values, &vectors)); + } + + + if (value) { + *value = VECTOR(values)[0]; + } + + if (hub_vector || authority_vector) { + if (!hub_vector) { + IGRAPH_VECTOR_INIT_FINALLY(&my_hub_vector, options->n); + my_hub_vector_p = &my_hub_vector; + } else { + my_hub_vector_p = hub_vector; + } + igraph_real_t amax = 0; + igraph_integer_t which = 0; + + IGRAPH_CHECK(igraph_vector_resize(my_hub_vector_p, options->n)); + for (igraph_integer_t i = 0; i < options->n; i++) { + igraph_real_t tmp; + VECTOR(*my_hub_vector_p)[i] = MATRIX(vectors, i, 0); + tmp = fabs(VECTOR(*my_hub_vector_p)[i]); + if (tmp > amax) { + amax = tmp; + which = i; + } + } + if (scale && amax != 0) { + igraph_vector_scale(my_hub_vector_p, 1 / VECTOR(*my_hub_vector_p)[which]); + } else if (igraph_i_vector_mostly_negative(my_hub_vector_p)) { + igraph_vector_scale(my_hub_vector_p, -1.0); + } + + /* Correction for numeric inaccuracies (eliminating -0.0) */ + for (igraph_integer_t i = 0; i < options->n; i++) { + if (VECTOR(*my_hub_vector_p)[i] < 0) { + VECTOR(*my_hub_vector_p)[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); + + if (authority_vector) { + igraph_real_t norm; + IGRAPH_CHECK(igraph_vector_resize(authority_vector, no_of_nodes)); + igraph_vector_null(authority_vector); + if (weights == NULL) { + igraph_i_kleinberg_unweighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &inadjlist); + } else { + igraph_i_kleinberg_weighted_hub_to_auth(no_of_nodes, authority_vector, &VECTOR(*my_hub_vector_p)[0], &ininclist, graph, weights); + } + if (!scale) { + norm = 1.0 / igraph_blas_dnrm2(authority_vector); + } else { + norm = 1.0 / igraph_vector_max(authority_vector); + } + igraph_vector_scale(authority_vector, norm); + } + + if (!hub_vector && authority_vector) { + igraph_vector_destroy(&my_hub_vector); + IGRAPH_FINALLY_CLEAN(1); + } + if (weights == NULL) { + igraph_adjlist_destroy(&outadjlist); + igraph_adjlist_destroy(&inadjlist); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_inclist_destroy(&outinclist); + igraph_inclist_destroy(&ininclist); + IGRAPH_FINALLY_CLEAN(2); + } + igraph_vector_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hub_score + * \brief Kleinberg's hub scores. + * + * \deprecated-by igraph_hub_and_authority_scores 0.10.5 + * + * 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_hub_and_authority_scores() to compute + * hub and authrotity scores efficiently at the same time, + * \ref igraph_authority_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +igraph_error_t 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_hub_and_authority_scores(graph, vector, NULL, value, scale, weights, options); +} + +/** + * \function igraph_authority_score + * \brief Kleinberg's authority scores. + * + * \deprecated-by igraph_hub_and_authority_scores 0.10.5 + * + * 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_and_authority_scores() to compute + * hub and authrotity scores efficiently at the same time, + * \ref igraph_hub_score() for the companion measure, + * \ref igraph_pagerank(), \ref igraph_personalized_pagerank(), + * \ref igraph_eigenvector_centrality() for similar measures. + */ + +igraph_error_t 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_hub_and_authority_scores(graph, NULL, vector, value, scale, weights, options); +} diff --git a/src/vendor/cigraph/src/centrality/pagerank.c b/src/vendor/cigraph/src/centrality/pagerank.c new file mode 100644 index 00000000000..a0615bbd374 --- /dev/null +++ b/src/vendor/cigraph/src/centrality/pagerank.c @@ -0,0 +1,708 @@ +/* -*- 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_adjlist.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_structural.h" + +#include "centrality/prpack_internal.h" + +#include + +static igraph_error_t 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); + +typedef struct { + const igraph_t *graph; + igraph_adjlist_t *adjlist; + igraph_real_t damping; + igraph_vector_t *outdegree; + igraph_vector_t *tmp; + igraph_vector_t *reset; +} pagerank_data_t; + +typedef struct { + 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; +} pagerank_data_weighted_t; + +/* The two pagerank_operator functions below update the probabilities of a random walker + * being in each of the vertices after one step of the walk. */ + +static igraph_error_t pagerank_operator_unweighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + 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; + igraph_integer_t 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++) { + igraph_integer_t nei = 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 IGRAPH_SUCCESS; +} + +static igraph_error_t pagerank_operator_weighted(igraph_real_t *to, const igraph_real_t *from, + int n, void *extra) { + + pagerank_data_weighted_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; + igraph_integer_t 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; i 0) { + sumfrom += from[i] * fact; + VECTOR(*tmp)[i] = from[i] / VECTOR(*outdegree)[i]; + } else { + sumfrom += from[i]; + /* The following value is used only when all outgoing edges have + * weight zero (as opposed to there being no outgoing edges at all). + * We set it to zero to avoid a 0.0*inf situation when computing + * to[i] below. */ + VECTOR(*tmp)[i] = 0; + } + } + + for (i = 0; i < n; i++) { + neis = igraph_inclist_get(inclist, i); + nlen = igraph_vector_int_size(neis); + to[i] = 0.0; + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); + to[i] += VECTOR(*weights)[edge] * VECTOR(*tmp)[nei]; + } + to[i] *= data->damping; + } + + /* 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, which phrases the PageRank calculation + * as an eigenvalue problem, which is then solved using 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. PRPACK uses an + * algebraic method, i.e. solves a linear system to obtain the PageRank + * scores. + * + * + * 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. + * https://doi.org/10.1016/S0169-7552(98)00110-X + * + * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \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. Supply \c NULL here to use the defaults. 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 + */ + +igraph_error_t 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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \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. Supply \c NULL here to use the defaults. 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. + */ + +igraph_error_t 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)[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 IGRAPH_SUCCESS; +} + +/** + * \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. When using \c IGRAPH_PAGERANK_ALGO_ARPACK, + * the eigenvalue corresponding to the PageRank vector is stored here. It is + * expected to be exactly one. Checking this value can be used to diagnose cases + * when ARPACK failed to converge to the leading eigenvector. + * When using \c IGRAPH_PAGERANK_ALGO_PRPACK, this is always set to 1.0. + * \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. Supply \c NULL here to use the defaults. 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. + */ +igraph_error_t 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 ? options : igraph_arpack_options_get_default() + ); + } 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 igraph_error_t 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; + + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_real_t reset_sum; /* used only when reset != NULL */ + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid length of weights vector when calculating PageRank scores.", IGRAPH_EINVAL); + } + + if (reset && igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); + } + + if (reset) { + reset_sum = igraph_vector_sum(reset); + if (no_of_nodes > 0 && reset_sum == 0) { + IGRAPH_ERROR("The sum of the elements in the reset vector must not be zero.", IGRAPH_EINVAL); + } + + igraph_real_t reset_min = igraph_vector_min(reset); + if (reset_min < 0) { + IGRAPH_ERROR("The reset vector must not contain negative elements.", IGRAPH_EINVAL); + } + if (isnan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", 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) { + if (reset && no_of_nodes > 0) { + IGRAPH_CHECK(igraph_vector_update(vector, reset)); + igraph_vector_scale(vector, 1.0 / reset_sum); + } else { + IGRAPH_CHECK(igraph_vector_resize(vector, no_of_nodes)); + 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] = 'R'; + options->start = 1; /* no random start vector */ + + directed = directed && igraph_is_directed(graph); + + if (weights) { + igraph_real_t min, max; + + /* Safe to call minmax, ecount == 0 case was caught earlier */ + igraph_vector_minmax(weights, &min, &max); + if (min < 0) { + IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); + } + if (isnan(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 */ + IGRAPH_CHECK(igraph_vector_init_copy(&normalized_reset, reset)); + IGRAPH_FINALLY(igraph_vector_destroy, &normalized_reset); + + igraph_vector_scale(&normalized_reset, 1.0 / reset_sum); + } + + IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), + directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS, weights)); + IGRAPH_CHECK(igraph_strength(graph, &indegree, igraph_vss_all(), + directed ? IGRAPH_IN : IGRAPH_ALL, IGRAPH_LOOPS, weights)); + + /* Set up an appropriate starting vector. We start from the (possibly weight) in-degrees + * plus some small random noise to avoid convergence problems. */ + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(indegree)[i] > 0) { + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(-1e-4, 1e-4); + } else { + MATRIX(vectors, i, 0) = 1; + } + } + + if (!weights) { + + igraph_adjlist_t adjlist; + 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_adjlist_init(graph, &adjlist, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_unweighted, + &data, options, NULL, &values, &vectors)); + + igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + } else { + + igraph_inclist_t inclist; + pagerank_data_weighted_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); + + IGRAPH_CHECK(igraph_arpack_rnsolve(pagerank_operator_weighted, + &data, options, NULL, &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) { + igraph_vit_t vit; + igraph_integer_t 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, 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/vendor/cigraph/src/centrality/prpack.cpp b/src/vendor/cigraph/src/centrality/prpack.cpp index ca109cac24e..5c9b32f34a0 100644 --- a/src/vendor/cigraph/src/centrality/prpack.cpp +++ b/src/vendor/cigraph/src/centrality/prpack.cpp @@ -24,6 +24,8 @@ #include "centrality/prpack/prpack_solver.h" #include "core/exceptions.h" +#include + using namespace prpack; using namespace std; @@ -32,67 +34,67 @@ using namespace std; * * 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_error_t 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; + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_integer_t i, no_of_nodes = igraph_vcount(graph); + 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; + std::unique_ptr v; + + if (reset) { + if (igraph_vector_size(reset) != no_of_nodes) { + IGRAPH_ERROR("Invalid length of reset vector when calculating personalized PageRank scores.", IGRAPH_EINVAL); } - // 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); + /* 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 (isnan(reset_min)) { + IGRAPH_ERROR("The reset vector must not contain NaN values.", IGRAPH_EINVAL); } - // 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, ""); + 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); + } - // Delete the personalization vector - delete [] v; - ); + // Construct the personalization vector + v.reset(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.get(); + } + + // 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; + IGRAPH_CHECK(prpack_graph.convert_from_igraph(graph, weights, directed)); + prpack_solver solver(&prpack_graph, false); + std::unique_ptr res( solver.solve(damping, 1e-10, u, v.get(), "") ); + + // Delete the personalization vector + v.reset(); // Check whether the solver converged // TODO: this is commented out because some of the solvers do not implement it yet @@ -103,22 +105,30 @@ int igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t */ // 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)]; + { + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + + igraph_vit_t vit; + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + igraph_integer_t 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[IGRAPH_VIT_GET(vit)]; + } + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(1); } - igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(1); - // TODO: can we get the eigenvalue? We'll just fake it until we can. + // PRPACK calculates PageRank scores by solving a linear system, + // so there is no eigenvalue. We return an exact 1.0 in all cases. if (value) { *value = 1.0; } - delete res; return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; } diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp index b78c6fed4f4..603567db0a7 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.cpp @@ -64,7 +64,7 @@ prpack_base_graph::prpack_base_graph(const prpack_csc* g) { 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()); + assert(g->num_vs <= std::numeric_limits::max()); num_vs = (int)g->num_vs; num_es = (int)g->num_es; // fill in heads and tails @@ -138,6 +138,7 @@ prpack_base_graph::prpack_base_graph(const prpack_edge_list* g) { delete[] osets; } +#if 0 prpack_base_graph::prpack_base_graph(const char* filename, const char* format, const bool weighted) { initialize(); FILE* f = fopen(filename, "r"); @@ -159,6 +160,7 @@ prpack_base_graph::prpack_base_graph(const char* filename, const char* format, c } fclose(f); } +#endif prpack_base_graph::~prpack_base_graph() { delete[] heads; @@ -166,6 +168,7 @@ prpack_base_graph::~prpack_base_graph() { delete[] vals; } +#if 0 void prpack_base_graph::read_smat(FILE* f, const bool weighted) { // read in header double ignore = 0.0; @@ -275,6 +278,7 @@ void prpack_base_graph::read_ascii(FILE* f) { } delete[] al; } +#endif prpack_base_graph::prpack_base_graph(int nverts, int nedges, std::pair* edges) { diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h index d51df887c4c..411d57d305f 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_base_graph.h @@ -12,9 +12,11 @@ namespace prpack { private: // helper methods void initialize(); +#if 0 void read_smat(std::FILE* f, const bool weighted); void read_edges(std::FILE* f); void read_ascii(std::FILE* f); +#endif public: // instance variables int num_vs; @@ -29,7 +31,9 @@ namespace prpack { prpack_base_graph(const prpack_int64_csc* g); prpack_base_graph(const prpack_csr* g); prpack_base_graph(const prpack_edge_list* g); +#if 0 prpack_base_graph(const char* filename, const char* format, const bool weighted); +#endif prpack_base_graph(int nverts, int nedges, std::pair* edges); // destructor ~prpack_base_graph(); diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp index 65549367ec5..b09c063ece4 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.cpp @@ -1,6 +1,9 @@ #include "prpack_igraph_graph.h" +#include +#include #include #include +#include #include "igraph_interface.h" @@ -9,53 +12,69 @@ 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; +igraph_error_t prpack_igraph_graph::convert_from_igraph( + const igraph_t *g, const igraph_vector_t *weights, bool directed) { + + const bool treat_as_directed = igraph_is_directed(g) && directed; + igraph_integer_t vcount = igraph_vcount(g), ecount = igraph_ecount(g); + double *p_weight = nullptr; + + if (vcount > INT_MAX) { + IGRAPH_ERROR("Too many vertices for PRPACK.", IGRAPH_EINVAL); + } + if (ecount > (treat_as_directed ? INT_MAX : INT_MAX/2)) { + IGRAPH_ERROR("Too many edges for PRPACK.", IGRAPH_EINVAL); + } + + if (weights && igraph_vector_size(weights) != ecount) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } // 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_vs = (int) vcount; + num_es = (int) ecount; num_self_es = 0; if (!treat_as_directed) { num_es *= 2; } // Allocate memory for heads and tails - p_head = heads = new int[num_es]; + int *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) { + if (weights) { p_weight = vals = new double[num_es]; } // Count the number of ignored edges (those with negative or zero weight) - num_ignored_es = 0; + int 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); + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + // Select all the edges and iterate over them by the source vertices // Add the edges - igraph_eit_create(g, es, &eit); + igraph_eit_t eit; + IGRAPH_CHECK(igraph_eit_create(g, igraph_ess_all(IGRAPH_EDGEORDER_TO), &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); while (!IGRAPH_EIT_END(eit)) { - eid = IGRAPH_EIT_GET(eit); + igraph_integer_t 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. + if (VECTOR(*weights)[eid] < 0) { + // Negative weights are disallowed. + IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); + } else if (isnan(VECTOR(*weights)[eid])) { + IGRAPH_ERROR("Edge weights must not be NaN.", IGRAPH_EINVAL); + } else if (VECTOR(*weights)[eid] == 0) { + // Edges with zero weight are ignored. num_ignored_es++; continue; } @@ -73,25 +92,32 @@ prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_ } } igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); } else { + // Use of igraph "finally" stack is safe in this block + // since no exceptions can be thrown from here. + // Select all the edges and iterate over them by the target vertices - igraph_vector_init(&neis, 0); + igraph_vector_int_t neis; + IGRAPH_CHECK(igraph_vector_int_init(&neis, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); - for (i = 0; i < num_vs; i++) { - igraph_incident(g, &neis, i, IGRAPH_ALL); - temp = igraph_vector_size(&neis); + for (int i = 0; i < num_vs; i++) { + IGRAPH_CHECK(igraph_incident(g, &neis, i, IGRAPH_ALL)); + + int temp = igraph_vector_int_size(&neis); // TODO: should loop edges be added in both directions? - p_head_copy = p_head; - for (j = 0; j < temp; j++) { + int *p_head_copy = p_head; + for (int j = 0; j < temp; j++) { if (weights != 0) { - if (VECTOR(*weights)[(long int)VECTOR(neis)[j]] <= 0) { + if (VECTOR(*weights)[VECTOR(neis)[j]] <= 0) { // Ignore num_ignored_es++; continue; } - *p_weight = VECTOR(*weights)[(long int)VECTOR(neis)[j]]; + *p_weight = VECTOR(*weights)[VECTOR(neis)[j]]; ++p_weight; } @@ -104,15 +130,16 @@ prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_ tails[i] = p_head - p_head_copy; } - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); } // 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; + for (int i = 0, sum = 0; i < num_vs; ++i) { + int temp = sum; sum += tails[i]; tails[i] = temp; } @@ -141,6 +168,8 @@ prpack_igraph_graph::prpack_igraph_graph(const igraph_t* g, const igraph_vector_ } printf("===========================\n"); */ + + return IGRAPH_SUCCESS; } // PRPACK_IGRAPH_SUPPORT diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h index 1bab84d4de6..b245f1ad59e 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_igraph_graph.h @@ -5,18 +5,23 @@ #include "prpack_base_graph.h" -struct igraph_s; -struct igraph_vector_t; +#include "igraph_datatype.h" +#include "igraph_vector.h" 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); + class prpack_igraph_graph : public prpack_base_graph { + public: + // constructors + prpack_igraph_graph() { } + + // We use a separate function to carry out the actual construction of the graph. + // The base class constructor sets the heads/tails/vals arrays to NULL, + // so these can safely be delete'ed by the destructor when + // convert_from_igraph() fails. + igraph_error_t convert_from_igraph(const igraph_t *g, + const igraph_vector_t *weights, + bool directed = true); }; } diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_result.h b/src/vendor/cigraph/src/centrality/prpack/prpack_result.h index 87da0f7bfbb..00d96b948a6 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_result.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_result.h @@ -2,6 +2,7 @@ #define PRPACK_RESULT #include +#include namespace prpack { @@ -15,7 +16,7 @@ namespace prpack { double read_time; double preprocess_time; double compute_time; - long num_es_touched; + int64_t num_es_touched; std::string method; int converged; // constructor diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp index b1de4afa2ba..4d2d1f544cc 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.cpp @@ -13,7 +13,7 @@ void prpack_solver::initialize() { gsg = NULL; sg = NULL; sccg = NULL; - owns_bg = true; + owns_bg = true; } prpack_solver::prpack_solver(const prpack_csc* g) { @@ -38,19 +38,21 @@ prpack_solver::prpack_solver(const prpack_edge_list* g) { prpack_solver::prpack_solver(prpack_base_graph* g, bool owns_bg) { initialize(); - this->owns_bg = owns_bg; + this->owns_bg = owns_bg; TIME(read_time, bg = g); } +#if 0 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)); } +#endif prpack_solver::~prpack_solver() { - if (owns_bg) { - delete bg; - } + if (owns_bg) { + delete bg; + } delete geg; delete gsg; delete sg; diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h index c89d4f301a1..91a3cef7c6b 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_solver.h @@ -159,7 +159,9 @@ namespace prpack { prpack_solver(const prpack_csr* g); prpack_solver(const prpack_edge_list* g); prpack_solver(prpack_base_graph* g, bool owns_bg=true); +#if 0 prpack_solver(const char* filename, const char* format, const bool weighted); +#endif // destructor ~prpack_solver(); // methods diff --git a/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp b/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp index 1d425b160f7..15981eed41f 100644 --- a/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp +++ b/src/vendor/cigraph/src/centrality/prpack/prpack_utils.cpp @@ -41,8 +41,7 @@ double prpack_utils::get_time() { 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); + IGRAPH_FATAL("Internal error in PRPACK."); #else cerr << msg << endl; exit(-1); diff --git a/src/vendor/cigraph/src/centrality/prpack_internal.h b/src/vendor/cigraph/src/centrality/prpack_internal.h index 01a85f68a9c..938a41e63ed 100644 --- a/src/vendor/cigraph/src/centrality/prpack_internal.h +++ b/src/vendor/cigraph/src/centrality/prpack_internal.h @@ -21,8 +21,8 @@ */ -#ifndef IGRAPH_PRPACK -#define IGRAPH_PRPACK +#ifndef IGRAPH_PRPACK_H +#define IGRAPH_PRPACK_H #include "igraph_decls.h" #include "igraph_types.h" @@ -33,7 +33,7 @@ __BEGIN_DECLS -int igraph_i_personalized_pagerank_prpack(const igraph_t *graph, igraph_vector_t *vector, +igraph_error_t 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, diff --git a/src/vendor/cigraph/src/centrality/truss.cpp b/src/vendor/cigraph/src/centrality/truss.cpp new file mode 100644 index 00000000000..d1f817b7936 --- /dev/null +++ b/src/vendor/cigraph/src/centrality/truss.cpp @@ -0,0 +1,286 @@ +/* + Copyright 2017 The Johns Hopkins University Applied Physics Laboratory LLC. All Rights Reserved. + Copyright 2021 The igraph team. + + Truss algorithm for cohesive subgroups. + + Author: Alex Perrone + Date: 2017-08-03 + Minor edits: The igraph team, 2021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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_community.h" + +#include "igraph_adjlist.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_motifs.h" +#include "igraph_structural.h" + +#include "core/exceptions.h" +#include "core/interruption.h" + +using std::vector; +using std::unordered_set; + + +// Unpack the triangles as a vector of vertices to be a vector of edges. +// So, instead of the triangle specified as vertices [1, 2, 3], return the +// edges as [1, 2, 1, 3, 2, 3] so that the support can be computed. +static igraph_error_t igraph_truss_i_unpack(const igraph_vector_int_t *tri, igraph_vector_int_t *unpacked_tri) { + igraph_integer_t num_triangles = igraph_vector_int_size(tri); + + IGRAPH_CHECK(igraph_vector_int_resize(unpacked_tri, 2 * num_triangles)); + + for (igraph_integer_t i = 0, j = 0; i < num_triangles; i += 3, j += 6) { + VECTOR(*unpacked_tri)[j] = VECTOR(*unpacked_tri)[j+2] = VECTOR(*tri)[i]; + VECTOR(*unpacked_tri)[j+1] = VECTOR(*unpacked_tri)[j+4] = VECTOR(*tri)[i+1]; + VECTOR(*unpacked_tri)[j+3] = VECTOR(*unpacked_tri)[j+5] = VECTOR(*tri)[i+2]; + } + + return IGRAPH_SUCCESS; +} + + +// Compute the edge support, i.e. number of triangles each edge occurs in. +// Time complexity: O(m), where m is the number of edges listed in eid. +static void igraph_truss_i_compute_support(const igraph_vector_int_t *eid, igraph_vector_int_t *support) { + igraph_integer_t m = igraph_vector_int_size(eid); + for (igraph_integer_t i = 0; i < m; ++i) { + VECTOR(*support)[VECTOR(*eid)[i]] += 1; + } +} + + +/* internal function doing the computations once the support is defined */ +static igraph_error_t igraph_i_trussness(const igraph_t *graph, igraph_vector_int_t *support, + igraph_vector_int_t *trussness) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_adjlist_t adjlist; + igraph_vector_int_t commonNeighbors; + igraph_vector_bool_t completed; + + // C++ data structures + vector< unordered_set > vec; + + // Allocate memory for result + igraph_integer_t no_of_edges = igraph_vector_int_size(support); + IGRAPH_CHECK(igraph_vector_int_resize(trussness, no_of_edges)); + if (no_of_edges == 0) { + return IGRAPH_SUCCESS; + } + + // Get max possible value = max entry in support. + // This cannot be computed if there are no edges, hence the above check + igraph_integer_t max = igraph_vector_int_max(support); + + // Initialize completed edges. + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&completed, no_of_edges); + + // The vector of levels. Each level of the vector is a set of edges initially + // at that level of support, where support is # of triangles the edge is in. + vec.resize(max + 1); + + // Add each edge to its appropriate level of support. + for (igraph_integer_t i = 0; i < no_of_edges; ++i) { + vec[VECTOR(*support)[i]].insert(i); // insert edge i into its support level + } + + // Record the trussness of edges at level 0. These edges are not part + // of any triangles, so there's not much to do and we "complete" them + for (auto edge : vec[0]) { + VECTOR(*trussness)[edge] = 2; + VECTOR(completed)[edge] = true; + } + + // Initialize variables needed below. + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + IGRAPH_VECTOR_INT_INIT_FINALLY(&commonNeighbors, 0); + + // Move through the levels, one level at a time, starting at first level. + for (igraph_integer_t level = 1; level <= max; ++level) { + + /* Track down edges one at a time */ + while (!vec[level].empty()) { + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_integer_t seed = *vec[level].begin(); // pull out the first edge + vec[level].erase(seed); // remove the first element + + /* Find the vertices of this edge */ + igraph_integer_t fromVertex = IGRAPH_FROM(graph, seed); + igraph_integer_t toVertex = IGRAPH_TO(graph, seed); + + /* Find neighbors of both vertices. If they run into each other, + * there is a triangle. We rely on the neighbor lists being sorted, + * as guaranteed by igraph_adjlist_init(), when computing intersections. */ + igraph_vector_int_t *fromNeighbors = igraph_adjlist_get(&adjlist, fromVertex); + igraph_vector_int_t *toNeighbors = igraph_adjlist_get(&adjlist, toVertex); + igraph_vector_int_t *q1 = fromNeighbors; + igraph_vector_int_t *q2 = toNeighbors; + + if (igraph_vector_int_size(q1) > igraph_vector_int_size(q2)) { + // case: #fromNeighbors > #toNeigbors, so make q1 the smaller set. + q1 = toNeighbors; + q2 = fromNeighbors; + } + + // Intersect the neighbors. + IGRAPH_CHECK(igraph_vector_int_intersect_sorted(q1, q2, &commonNeighbors)); + + /* Go over the overlapping neighbors and check each */ + igraph_integer_t ncommon = igraph_vector_int_size(&commonNeighbors); + for (igraph_integer_t j = 0; j < ncommon; j++) { + igraph_integer_t n = VECTOR(commonNeighbors)[j]; // the common neighbor + igraph_integer_t e1, e2; + IGRAPH_CHECK(igraph_get_eid(graph, &e1, fromVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); + IGRAPH_CHECK(igraph_get_eid(graph, &e2, toVertex, n, IGRAPH_UNDIRECTED, /* error= */ true)); + + bool e1_complete = VECTOR(completed)[e1]; + bool e2_complete = VECTOR(completed)[e2]; + + if (!e1_complete && !e2_complete) { + igraph_integer_t newLevel; + + // Demote this edge, if higher than current level. + if (VECTOR(*support)[e1] > level) { + VECTOR(*support)[e1] -= 1; // decrement the level + newLevel = VECTOR(*support)[e1]; + vec[newLevel].insert(e1); + vec[newLevel + 1].erase(e1); // the old level + } + // Demote this edge, if higher than current level. + if (VECTOR(*support)[e2] > level) { + VECTOR(*support)[e2] -= 1; // decrement the level + newLevel = VECTOR(*support)[e2]; + vec[newLevel].insert(e2); + vec[newLevel + 1].erase(e2); // the old level + } + } + } + // Record this edge; its level is its trussness. + VECTOR(*trussness)[seed] = level + 2; + VECTOR(completed)[seed] = true; // mark as complete + igraph_vector_int_clear(&commonNeighbors); + } // end while + } // end for-loop over levels + + // Clean up. + igraph_vector_int_destroy(&commonNeighbors); + igraph_adjlist_destroy(&adjlist); + igraph_vector_bool_destroy(&completed); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; +} + + +/** + * \function igraph_trussness + * \brief Finding the "trussness" of the edges in a network. + * + * A k-truss is a subgraph in which every edge occurs in at least k-2 triangles + * in the subgraph. The trussness of an edge indicates the highest k-truss that + * the edge occurs in. + * + * + * This function returns the highest \c k for each edge. If you are interested in + * a particular k-truss subgraph, you can subset the graph to those edges + * which are >= k because each k-truss is a subgraph of a (k–1)-truss + * Thus, to get all 4-trusses, take k >= 4 because the 5-trusses, 6-trusses, + * etc. need to be included. + * + * + * The current implementation of this function iteratively decrements support + * of each edge using O(|E|) space and O(|E|^1.5) time. The implementation does + * not support multigraphs; use \ref igraph_simplify() to collapse edges before + * calling this function. + * + * + * Reference: + * + * + * See Algorithm 2 in: + * Wang, Jia, and James Cheng. "Truss decomposition in massive networks." + * Proceedings of the VLDB Endowment 5.9 (2012): 812-823. + * https://doi.org/10.14778/2311906.2311909 + * + * \param graph The input graph. Loop edges are allowed; multigraphs are not. + * \param truss Pointer to initialized vector of truss values that will + * indicate the highest k-truss each edge occurs in. It will be resized as + * needed. + * \return Error code. + * + * Time complexity: It should be O(|E|^1.5) according to the reference. + */ +igraph_error_t igraph_trussness(const igraph_t* graph, igraph_vector_int_t* trussness) { + igraph_vector_int_t triangles, support, unpacked_triangles, eid; + igraph_bool_t is_multigraph; + + /* Check whether the graph is a multigraph; trussness will not work for these */ + IGRAPH_CHECK(igraph_has_multiple(graph, &is_multigraph)); + if (! is_multigraph && igraph_is_directed(graph)) { + /* Directed graphs with mutual edges are effectively multigraphs + * when edge directions are ignored. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &is_multigraph, /* loops */ false)); + } + if (is_multigraph) { + IGRAPH_ERROR("Trussness is not implemented for graph with multi-edges.", IGRAPH_UNIMPLEMENTED); + } + + /* Manage the stack to make it memory safe: do not change the order of + * initialization of the following four vectors */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&support, igraph_ecount(graph)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eid, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&unpacked_triangles, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&triangles, 0); + + // List the triangles as vertex triplets. + IGRAPH_CHECK(igraph_list_triangles(graph, &triangles)); + + // Unpack the triangles from vertex list to edge list. + IGRAPH_CHECK(igraph_truss_i_unpack(&triangles, &unpacked_triangles)); + igraph_vector_int_destroy(&triangles); + IGRAPH_FINALLY_CLEAN(1); + + // Get the edge IDs of the unpacked triangles. Note: a given eid can occur + // multiple times in this list if it is in multiple triangles. + IGRAPH_CHECK(igraph_get_eids(graph, &eid, &unpacked_triangles, /* directed = */ false, /* error = */ true)); + igraph_vector_int_destroy(&unpacked_triangles); + IGRAPH_FINALLY_CLEAN(1); + + // Compute the support of the edges. + igraph_truss_i_compute_support(&eid, &support); + igraph_vector_int_destroy(&eid); + IGRAPH_FINALLY_CLEAN(1); + + // Compute the trussness of the edges. + IGRAPH_CHECK(igraph_i_trussness(graph, &support, trussness)); + igraph_vector_int_destroy(&support); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt b/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt index e4871a24e98..7926f4efcd6 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt +++ b/src/vendor/cigraph/src/cliques/cliquer/CMakeLists.txt @@ -24,5 +24,4 @@ endif() # function as is (without visibility specification) target_compile_definitions(cliquer PRIVATE IGRAPH_STATIC) -# TODO(ntamas): make sure that this works for Cliquer -# use_all_warnings(cliquer) +use_all_warnings(cliquer) diff --git a/src/vendor/cigraph/src/cliques/cliquer/cliquer.c b/src/vendor/cigraph/src/cliques/cliquer/cliquer.c index 2dd08b934f3..218e16a1d91 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/cliquer.c +++ b/src/vendor/cigraph/src/cliques/cliquer/cliquer.c @@ -21,7 +21,7 @@ /* Default cliquer options */ IGRAPH_THREAD_LOCAL clique_options clique_default_options = { - reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 + reorder_by_default, NULL, /*clique_print_time*/ NULL, NULL, NULL, NULL, NULL, 0 }; @@ -88,18 +88,18 @@ memcpy(&realtimer,&old_realtimer,sizeof(struct timeval));*/ /* 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, +static igraph_error_t 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, + clique_options *opts, CLIQUER_LARGE_INT *num_found); +static igraph_error_t 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); + graph_t *g, clique_options *opts, int *weight_found); -static boolean store_clique(set_t clique, graph_t *g, clique_options *opts); +static igraph_error_t 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); +static igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opts); @@ -140,10 +140,10 @@ static boolean false_function(set_t clique,graph_t *g,clique_options *opts); */ 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; @@ -181,7 +181,7 @@ static int unweighted_clique_search_single(int *table, int min_size, clique_size[v]=clique_size[w]; } - /* + /* if (opts && opts->time_function) { gettimeofday(&timeval,NULL); times(&tms); @@ -200,7 +200,7 @@ static int unweighted_clique_search_single(int *table, int min_size, return 0; } } - */ + */ if (min_size) { if (clique_size[v]>=min_size) { @@ -289,7 +289,7 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, } } - /* Avoid unneccessary loops (next size == p1-newtable) */ + /* Avoid unnecessary 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 @@ -325,6 +325,7 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, * maximal - requires cliques to be maximal * g - the graph * opts - time printing and clique storage options + * num_found - number of cliques found * * Cliques found are stored as defined by opts->user_function and * opts->clique_list. opts->time_function is called after each @@ -332,24 +333,22 @@ static boolean sub_unweighted_single(int *table, int size, int min_size, * * 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) { - /* +static igraph_error_t unweighted_clique_search_all( + int *table, int start, int min_size, int max_size, boolean maximal, + graph_t *g, clique_options *opts, CLIQUER_LARGE_INT *num_found +) { + /* struct timeval timeval; struct tms tms; - */ - int i, j; + */ + int i, j; int v; int *newtable; int newsize; - CLIQUER_LARGE_INT r; - CLIQUER_LARGE_INT count=0; + CLIQUER_LARGE_INT r; + CLIQUER_LARGE_INT count=0; + igraph_error_t retval = IGRAPH_SUCCESS; if (temp_count) { temp_count--; @@ -373,15 +372,14 @@ static CLIQUER_LARGE_INT unweighted_clique_search_all(int *table, int start, } SET_ADD_ELEMENT(current_clique,v); - r=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1, - maximal,g,opts); + retval=sub_unweighted_all(newtable,newsize,min_size-1,max_size-1, + maximal,g,opts,&r); SET_DEL_ELEMENT(current_clique,v); - if (r<0) { + count+=r; + if (retval) { /* Abort. */ - count-=r; break; } - count+=r; #if 0 if (opts->time_function) { @@ -405,7 +403,12 @@ static CLIQUER_LARGE_INT unweighted_clique_search_all(int *table, int start, #endif } temp_list[temp_count++]=newtable; - return count; + + if (num_found) { + *num_found = count; + } + + return retval; } /* @@ -423,6 +426,7 @@ static CLIQUER_LARGE_INT unweighted_clique_search_all(int *table, int start, * maximal - require cliques to be maximal (passed through) * g - the graph * opts - storage options + * num_found - number of cliques found * * All cliques of suitable size found are stored according to opts. * @@ -433,32 +437,37 @@ static CLIQUER_LARGE_INT unweighted_clique_search_all(int *table, int start, * 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, +static igraph_error_t sub_unweighted_all(int *table, int size, int min_size, int max_size, boolean maximal, graph_t *g, - clique_options *opts) { + clique_options *opts, CLIQUER_LARGE_INT *num_found) { + igraph_error_t retval = IGRAPH_SUCCESS; int i; int v; int *newtable; int *p1, *p2; - CLIQUER_LARGE_INT n; - CLIQUER_LARGE_INT count=0; /* Amount of cliques found */ + 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; + retval = store_clique(current_clique, g, opts); + if (retval) { + *num_found = count; + return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; } } if (max_size <= 0) { /* If we add another element, size will be too big. */ - return count; + *num_found = count; + return IGRAPH_SUCCESS; } } if (size < min_size) { - return count; + *num_found = count; + return IGRAPH_SUCCESS; } /* Dynamic memory allocation with cache */ @@ -488,25 +497,25 @@ static CLIQUER_LARGE_INT sub_unweighted_all(int *table, int size, int min_size, } } - /* Avoid unneccessary loops (next size == p1-newtable) */ + /* Avoid unnecessary 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); + retval = sub_unweighted_all(newtable,p1-newtable, + min_size-1,max_size-1,maximal,g,opts,&n); SET_DEL_ELEMENT(current_clique,v); - if (n < 0) { - /* Abort. */ - count -= n; - count = -count; + count += n; + if (retval || n < 0) { break; } count+=n; } temp_list[temp_count++]=newtable; - return count; + + *num_found = count; + return retval; } @@ -546,13 +555,13 @@ static CLIQUER_LARGE_INT sub_unweighted_all(int *table, int size, int min_size, * * Note: Does NOT use opts->user_function of opts->clique_list. */ -static int weighted_clique_search_single(int *table, int min_weight, +static igraph_error_t weighted_clique_search_single(int *table, int min_weight, int max_weight, graph_t *g, - clique_options *opts) { - /* + clique_options *opts, int *result) { + /* struct timeval timeval; struct tms tms; - */ + */ int i,j; int v; int *newtable; @@ -561,6 +570,9 @@ static int weighted_clique_search_single(int *table, int min_weight, int search_weight; int min_w; clique_options localopts; + igraph_error_t retval = IGRAPH_SUCCESS; + + ASSERT(result != NULL); if (min_weight==0) min_w=INT_MAX; @@ -576,10 +588,13 @@ static int weighted_clique_search_single(int *table, int min_weight, if (g->weights[table[i]] <= max_weight) { set_empty(best_clique); SET_ADD_ELEMENT(best_clique,table[i]); - return g->weights[table[i]]; + *result = g->weights[table[i]]; + return IGRAPH_SUCCESS; } } - return 0; + + *result = 0; + return IGRAPH_SUCCESS; } localopts.time_function=NULL; @@ -598,7 +613,8 @@ static int weighted_clique_search_single(int *table, int min_weight, if (min_weight && (search_weight >= min_weight)) { if (search_weight <= max_weight) { /* Found suitable clique. */ - return search_weight; + *result = search_weight; + return IGRAPH_SUCCESS; } search_weight=min_weight-1; } @@ -627,14 +643,14 @@ static int weighted_clique_search_single(int *table, int min_weight, SET_ADD_ELEMENT(current_clique,v); - search_weight=sub_weighted_all(newtable,newsize,newweight, + retval=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); + g,&localopts, &search_weight); SET_DEL_ELEMENT(current_clique,v); - if (search_weight < 0) { + if (retval || search_weight < 0) { break; } @@ -665,9 +681,12 @@ static int weighted_clique_search_single(int *table, int min_weight, temp_list[temp_count++]=newtable; if (min_weight && (search_weight > 0)) { /* Requested clique has not been found. */ - return 0; + *result = 0; + } else { + *result = clique_size[table[i-1]]; } - return clique_size[table[i-1]]; + + return retval; } @@ -686,6 +705,7 @@ static int weighted_clique_search_single(int *table, int min_weight, * maximal - search only for maximal cliques * g - the graph * opts - time printing and clique storage options + * num_found - number of cliques found * * Cliques found are stored as defined by opts->user_function and * opts->clique_list. opts->time_function is called after each @@ -694,13 +714,13 @@ static int weighted_clique_search_single(int *table, int min_weight, * 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 + * Returns the number of cliques stored (not necessarily number of cliques * in graph, if user/time_function aborts). */ -static int weighted_clique_search_all(int *table, int start, +static igraph_error_t weighted_clique_search_all(int *table, int start, int min_weight, int max_weight, boolean maximal, graph_t *g, - clique_options *opts) { + clique_options *opts, int* num_found) { /* struct timeval timeval; struct tms tms; @@ -710,6 +730,7 @@ static int weighted_clique_search_all(int *table, int start, int *newtable; int newsize; int newweight; + igraph_error_t retval = IGRAPH_SUCCESS; if (temp_count) { temp_count--; @@ -735,12 +756,12 @@ static int weighted_clique_search_all(int *table, int start, } SET_ADD_ELEMENT(current_clique,v); - j=sub_weighted_all(newtable,newsize,newweight, + retval=sub_weighted_all(newtable,newsize,newweight, g->weights[v],min_weight-1,INT_MAX, - min_weight,max_weight,maximal,g,opts); + min_weight,max_weight,maximal,g,opts,&j); SET_DEL_ELEMENT(current_clique,v); - if (j<0) { + if (retval || j < 0) { /* Abort. */ break; } @@ -769,7 +790,11 @@ static int weighted_clique_search_all(int *table, int start, } temp_list[temp_count++]=newtable; - return clique_list_count; + if (num_found) { + *num_found = clique_list_count; + } + + return retval; } /* @@ -792,13 +817,12 @@ static int weighted_clique_search_all(int *table, int start, * maximal - search only for maximal cliques * g - the graph * opts - storage options + * weight_found - weight of the 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. * * 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. * @@ -812,10 +836,11 @@ static int weighted_clique_search_all(int *table, int start, * searching for all cliques, min_weight should be given the minimum weight * desired. */ -static int sub_weighted_all(int *table, int size, int weight, +static igraph_error_t 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) { + graph_t *g, clique_options *opts, int* weight_found) { + igraph_error_t retval = IGRAPH_SUCCESS; int i; int v,w; int *newtable; @@ -826,13 +851,16 @@ static int sub_weighted_all(int *table, int size, int 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; + retval = store_clique(current_clique,g,opts); + if (retval) { + *weight_found = -1; + return retval == IGRAPH_STOP ? IGRAPH_SUCCESS : retval; } } if (current_weight >= max_weight) { /* Clique too heavy. */ - return min_weight-1; + *weight_found = min_weight-1; + return IGRAPH_SUCCESS; } } if (size <= 0) { @@ -842,12 +870,16 @@ static int sub_weighted_all(int *table, int size, int weight, if (best_clique) { best_clique = set_copy(best_clique,current_clique); } - if (current_weight < min_weight) - return current_weight; - else - return min_weight-1; + if (current_weight < min_weight) { + *weight_found = current_weight; + return IGRAPH_SUCCESS; + } else { + *weight_found = min_weight-1; + return IGRAPH_SUCCESS; + } } else { - return prune_low; + *weight_found = prune_low; + return IGRAPH_SUCCESS; } } @@ -884,26 +916,28 @@ static int sub_weighted_all(int *table, int size, int weight, w=g->weights[v]; weight-=w; - /* Avoid a few unneccessary loops */ + /* Avoid a few unnecessary loops */ if (current_weight+w+newweight <= prune_low) { continue; } SET_ADD_ELEMENT(current_clique,v); - prune_low=sub_weighted_all(newtable,p1-newtable, + retval=sub_weighted_all(newtable,p1-newtable, newweight, current_weight+w, prune_low,prune_high, min_weight,max_weight,maximal, - g,opts); + g,opts, &prune_low); SET_DEL_ELEMENT(current_clique,v); - if ((prune_low<0) || (prune_low>=prune_high)) { + if (retval || (prune_low<0) || (prune_low>=prune_high)) { /* Impossible to find larger clique. */ break; } } temp_list[temp_count++]=newtable; - return prune_low; + + *weight_found = prune_low; + return IGRAPH_SUCCESS; } @@ -920,10 +954,10 @@ static int sub_weighted_all(int *table, int size, int weight, * clique - the clique to store * opts - storage options * - * Returns FALSE if opts->user_function() returned FALSE; otherwise - * returns TRUE. + * Returns the same igraph error code as the one returned by + * opts->user_function(). Returns IGRAPH_SUCCESS if no callback is defined. */ -static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) { +static igraph_error_t store_clique(set_t clique, graph_t *g, clique_options *opts) { clique_list_count++; @@ -944,13 +978,10 @@ static boolean store_clique(set_t clique, graph_t *g, clique_options *opts) { /* user_function() */ if (opts->user_function) { - if (!opts->user_function(clique,g,opts)) { - /* User function requested abort. */ - return FALSE; - } + return opts->user_function(clique, g, opts); } - return TRUE; + return IGRAPH_SUCCESS; } /* @@ -1034,10 +1065,10 @@ static boolean is_maximal(set_t clique, graph_t *g) { /* * false_function() * - * Returns FALSE. Can be used as user_function. + * Returns IGRAPH_STOP. Can be used as user_function. */ -static boolean false_function(set_t clique,graph_t *g,clique_options *opts) { - return FALSE; +static igraph_error_t false_function(set_t clique,graph_t *g,clique_options *opts) { + return IGRAPH_STOP; } @@ -1048,31 +1079,31 @@ static boolean false_function(set_t clique,graph_t *g,clique_options *opts) { /* * clique_unweighted_max_weight() * - * Returns the size of the maximum (sized) clique in g (or 0 if search - * was aborted). - * * g - the graph * opts - time printing options + * size - the size of the maximum (sized) clique in g * * Note: As we don't have an algorithm faster than actually finding * a maximum clique, we use clique_unweighted_find_single(). * This incurs only very small overhead. */ -int clique_unweighted_max_weight(graph_t *g, clique_options *opts) { - set_t s; - int size; +igraph_error_t clique_unweighted_max_weight(graph_t *g, clique_options *opts, int* size) { + set_t clique; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); - s=clique_unweighted_find_single(g,0,0,FALSE,opts); - if (s==NULL) { - /* Search was aborted. */ - return 0; + IGRAPH_CHECK(clique_unweighted_find_single(g, 0, 0, FALSE,opts, &clique)); + + if (size != NULL) { + *size = clique ? set_size(clique) : 0; } - size=set_size(s); - set_free(s); - return size; + + if (clique) { + set_free(clique); + } + + return IGRAPH_SUCCESS; } @@ -1097,11 +1128,13 @@ int clique_unweighted_max_weight(graph_t *g, clique_options *opts) { * * 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) { +igraph_error_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, + boolean maximal, clique_options *opts, set_t *clique) { int i; int *table; set_t s; + igraph_error_t retval = IGRAPH_SUCCESS; + CLIQUER_LARGE_INT found; ENTRANCE_SAVE(); entrance_level++; @@ -1109,6 +1142,7 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, if (opts==NULL) opts=&clique_default_options; + ASSERT(clique!=NULL); ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_size>=0); @@ -1120,7 +1154,8 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, if ((max_size>0) && (min_size>max_size)) { /* state was not changed */ entrance_level--; - return NULL; + *clique = NULL; + return IGRAPH_SUCCESS; } /* @@ -1152,8 +1187,7 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, } ASSERT(reorder_is_bijection(table,g->n)); - - if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + if (unweighted_clique_search_single(table,min_size,g,opts) == 0) { set_free(current_clique); current_clique=NULL; goto cleanreturn; @@ -1174,20 +1208,20 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, 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); + retval = unweighted_clique_search_all( + table, i, min_size, max_size, maximal, g, &localopts, &found + ); + set_free(current_clique); + if (retval || !found) { current_clique=NULL; + } else { + current_clique=s; } } } cleanreturn: - s=current_clique; + *clique = current_clique; /* Free resources */ for (i=0; i < temp_count; i++) @@ -1199,7 +1233,7 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, ENTRANCE_RESTORE(); entrance_level--; - return s; + return retval; } @@ -1215,21 +1249,24 @@ set_t clique_unweighted_find_single(graph_t *g,int min_size,int max_size, * 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). + * num_found - the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() returns + * FALSE (request abort) or opts->user_function() returns an + * igraph error code * * 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) { +igraph_error_t clique_unweighted_find_all( + graph_t *g, int min_size, int max_size, boolean maximal, clique_options *opts, + CLIQUER_LARGE_INT *num_found +) { int i; int *table; - CLIQUER_LARGE_INT count; + CLIQUER_LARGE_INT count; + igraph_error_t retval = IGRAPH_SUCCESS; ENTRANCE_SAVE(); entrance_level++; @@ -1248,7 +1285,10 @@ CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_s if ((max_size>0) && (min_size>max_size)) { /* state was not changed */ entrance_level--; - return 0; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; } /* @@ -1286,7 +1326,7 @@ CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_s /* Search as normal until there is a chance to find a suitable * clique. */ - if (unweighted_clique_search_single(table,min_size,g,opts)==0) { + if (unweighted_clique_search_single(table,min_size,g,opts) == 0) { count=0; goto cleanreturn; } @@ -1303,8 +1343,8 @@ CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_s 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); + + retval = unweighted_clique_search_all(table, i, min_size, max_size, maximal, g, opts, &count); cleanreturn: /* Free resources */ @@ -1318,7 +1358,11 @@ CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_s ENTRANCE_RESTORE(); entrance_level--; - return count; + if (num_found) { + *num_found = count; + } + + return retval; } @@ -1337,21 +1381,32 @@ CLIQUER_LARGE_INT clique_unweighted_find_all(graph_t *g, int min_size, int max_s * a maximum weight clique, we use clique_find_single(). * This incurs only very small overhead. */ -int clique_max_weight(graph_t *g,clique_options *opts) { +igraph_error_t clique_max_weight(graph_t *g, clique_options *opts, int *weight_found) { set_t s; - int weight; + int weight = 0; + igraph_error_t retval; ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); - s=clique_find_single(g,0,0,FALSE,opts); - if (s==NULL) { + retval = clique_find_single(g, 0, 0, FALSE, opts, &s); + + if (retval || s == NULL) { /* Search was aborted. */ - return 0; + weight = 0; + } else { + weight = graph_subgraph_weight(g,s); + } + + if (s != NULL) { + set_free(s); + } + + if (weight_found) { + *weight_found = weight; } - weight=graph_subgraph_weight(g,s); - set_free(s); - return weight; + + return retval; } @@ -1379,11 +1434,16 @@ int clique_max_weight(graph_t *g,clique_options *opts) { * 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) { +igraph_error_t clique_find_single( + graph_t *g, int min_weight, int max_weight, boolean maximal, + clique_options *opts, set_t *clique +) { int i; int *table; set_t s; + igraph_error_t retval = IGRAPH_SUCCESS; + int weight_found; + int num_found; ENTRANCE_SAVE(); entrance_level++; @@ -1391,6 +1451,7 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, if (opts==NULL) opts=&clique_default_options; + ASSERT(clique!=NULL); ASSERT((sizeof(setelement)*8)==ELEMENTSIZE); ASSERT(g!=NULL); ASSERT(min_weight>=0); @@ -1402,7 +1463,8 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; - return NULL; + *clique = NULL; + return IGRAPH_SUCCESS; } /* @@ -1419,16 +1481,17 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, if (max_weight < min_weight) { /* state was not changed */ entrance_level--; - return NULL; + *clique = NULL; + return IGRAPH_SUCCESS; } } weight_multiplier = g->weights[0]; entrance_level--; - s=clique_unweighted_find_single(g,min_weight,max_weight, - maximal,opts); + retval = clique_unweighted_find_single(g, min_weight, max_weight, maximal, opts, &s); ENTRANCE_RESTORE(); - return s; + *clique = s; + return retval; } /* Dynamic allocation */ @@ -1461,13 +1524,15 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, if (max_weight==0) max_weight=INT_MAX; - if (weighted_clique_search_single(table,min_weight,max_weight, - g,opts)==0) { + retval = weighted_clique_search_single(table, min_weight, max_weight, g, opts, &weight_found); + + if (retval || weight_found == 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) { @@ -1483,9 +1548,12 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, 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)) { + + retval = weighted_clique_search_all( + table, i, min_weight, max_weight, maximal, g, &localopts, &num_found + ); + + if (retval || !weight_found) { set_free(best_clique); best_clique=NULL; } @@ -1510,7 +1578,9 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, ENTRANCE_RESTORE(); entrance_level--; - return s; + *clique = s; + + return retval; } @@ -1530,10 +1600,10 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, * 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). + * num_found - the number of cliques found. This can be less than the number + * of cliques in the graph iff opts->time_function() returns + * FALSE (request abort) or opts->user_function() returns an + * igraph error code * * The cliques found are stored in opts->clique_list[] and * opts->user_function() is called with them (if non-NULL). The cliques @@ -1543,11 +1613,12 @@ set_t clique_find_single(graph_t *g,int min_weight,int max_weight, * 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; +igraph_error_t clique_find_all(graph_t *g, int min_weight, int max_weight, + boolean maximal, clique_options *opts, int *num_found) { + int i,n; int *table; - CLIQUER_LARGE_INT r; + CLIQUER_LARGE_INT r; + igraph_error_t retval = IGRAPH_SUCCESS; ENTRANCE_SAVE(); entrance_level++; @@ -1566,7 +1637,10 @@ int clique_find_all(graph_t *g, int min_weight, int max_weight, if ((max_weight>0) && (min_weight>max_weight)) { /* state was not changed */ entrance_level--; - return 0; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; } /* @@ -1582,16 +1656,21 @@ int clique_find_all(graph_t *g, int min_weight, int max_weight, if (max_weight < min_weight) { /* state was not changed */ entrance_level--; - return 0; + if (num_found) { + *num_found = 0; + } + return IGRAPH_SUCCESS; } } weight_multiplier = g->weights[0]; entrance_level--; - r=clique_unweighted_find_all(g,min_weight,max_weight,maximal, - opts); + retval = clique_unweighted_find_all(g, min_weight, max_weight, maximal, opts, &r); ENTRANCE_RESTORE(); - return r; + if (num_found) { + *num_found = r; + } + return retval; } /* Dynamic allocation */ @@ -1620,8 +1699,8 @@ int clique_find_all(graph_t *g, int min_weight, int max_weight, ASSERT(reorder_is_bijection(table,g->n)); /* First phase */ - n=weighted_clique_search_single(table,min_weight,INT_MAX,g,opts); - if (n==0) { + retval = weighted_clique_search_single(table, min_weight, INT_MAX, g, opts, &n); + if (retval || n == 0) { /* Requested clique has not been found. */ goto cleanreturn; } @@ -1640,10 +1719,9 @@ int clique_find_all(graph_t *g, int min_weight, int max_weight, break; /* Second phase */ - n=weighted_clique_search_all(table,i,min_weight,max_weight,maximal, - g,opts); + retval = weighted_clique_search_all(table, i, min_weight, max_weight, maximal, g, opts, &n); - cleanreturn: +cleanreturn: /* Free resources */ for (i=0; i < temp_count; i++) free(temp_list[i]); @@ -1656,7 +1734,11 @@ int clique_find_all(graph_t *g, int min_weight, int max_weight, ENTRANCE_RESTORE(); entrance_level--; - return n; + if (num_found) { + *num_found = n; + } + + return retval; } diff --git a/src/vendor/cigraph/src/cliques/cliquer/cliquer.h b/src/vendor/cigraph/src/cliques/cliquer/cliquer.h index e49636c0784..ec7f9ee37b2 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/cliquer.h +++ b/src/vendor/cigraph/src/cliques/cliquer/cliquer.h @@ -28,27 +28,38 @@ struct _clique_options { clique_options *); FILE *output; - boolean (*user_function)(set_t,graph_t *,clique_options *); + igraph_error_t (*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); +extern igraph_error_t clique_max_weight( + graph_t *g, clique_options *opts, int *weight_found +); +extern igraph_error_t clique_find_single( + graph_t *g, int min_weight, int max_weight, boolean maximal, + clique_options *opts, set_t *clique +); +extern igraph_error_t clique_find_all( + graph_t *g, int req_weight, boolean exact, boolean maximal, + clique_options *opts, int *num_found +); /* 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); +extern igraph_error_t clique_unweighted_max_weight( + graph_t *g, clique_options *opts, int *weight_found +); +extern igraph_error_t clique_unweighted_find_single( + graph_t *g, int min_size, int max_size, boolean maximal, + clique_options *opts, set_t *clique +); +extern igraph_error_t clique_unweighted_find_all( + graph_t *g, int min_size, int max_size, boolean maximal, + clique_options *opts, CLIQUER_LARGE_INT *num_found +); /* Time printing functions */ /* diff --git a/src/vendor/cigraph/src/cliques/cliquer/set.h b/src/vendor/cigraph/src/cliques/cliquer/set.h index 9727d3024d8..bca7aa018ce 100644 --- a/src/vendor/cigraph/src/cliques/cliquer/set.h +++ b/src/vendor/cigraph/src/cliques/cliquer/set.h @@ -142,8 +142,8 @@ static int set_bit_count[256] = { * Create a new set that can hold values in the range 0,...,size-1. */ UNUSED_FUNCTION -static set_t set_new(int size) { - int n; +static set_t set_new(size_t size) { + size_t n; set_t s; ASSERT(size>0); @@ -270,7 +270,7 @@ static void set_empty(set_t s) { */ UNUSED_FUNCTION INLINE static set_t set_intersection(set_t res,set_t a,set_t b) { - int i,max; + size_t i, max; if (res==NULL) { res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); @@ -303,7 +303,7 @@ static set_t set_intersection(set_t res,set_t a,set_t b) { */ UNUSED_FUNCTION INLINE static set_t set_union(set_t res,set_t a,set_t b) { - int i,max; + size_t i, max; if (res==NULL) { res = set_new(MAX(SET_MAX_SIZE(a),SET_MAX_SIZE(b))); diff --git a/src/vendor/cigraph/src/cliques/cliquer_internal.h b/src/vendor/cigraph/src/cliques/cliquer_internal.h index f90b64a218b..93e9897978e 100644 --- a/src/vendor/cigraph/src/cliques/cliquer_internal.h +++ b/src/vendor/cigraph/src/cliques/cliquer_internal.h @@ -1,27 +1,49 @@ +/* + IGraph library. + Copyright (C) 2016-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, see . + */ + #ifndef IGRAPH_CLIQUER_H #define IGRAPH_CLIQUER_H -#include "igraph_interface.h" +#include "igraph_decls.h" #include "igraph_cliques.h" -int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, +__BEGIN_DECLS + +igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size); -int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, +igraph_error_t igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); -int igraph_i_cliquer_callback(const igraph_t *graph, +igraph_error_t 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); -int igraph_i_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res, +igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res, igraph_real_t min_weight, igraph_real_t max_weight, igraph_bool_t maximal); -int igraph_i_largest_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res); +igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res); -int igraph_i_weighted_clique_number(const igraph_t *graph, +igraph_error_t igraph_i_weighted_clique_number(const igraph_t *graph, const igraph_vector_t *vertex_weights, igraph_real_t *res); +__END_DECLS + #endif // IGRAPH_CLIQUER_H diff --git a/src/vendor/cigraph/src/cliques/cliquer_wrapper.c b/src/vendor/cigraph/src/cliques/cliquer_wrapper.c index 7b4fa2cd9ce..445faa37425 100644 --- a/src/vendor/cigraph/src/cliques/cliquer_wrapper.c +++ b/src/vendor/cigraph/src/cliques/cliquer_wrapper.c @@ -1,7 +1,23 @@ +/* + IGraph library. + Copyright (C) 2016-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, see . + */ #include "igraph_error.h" -#include "igraph_memory.h" -#include "igraph_constants.h" +#include "igraph_interface.h" #include "core/interruption.h" #include "cliques/cliquer_internal.h" @@ -9,38 +25,7 @@ #include "config.h" -/* Call this to allow for interruption in Cliquer callback functions */ -#define CLIQUER_ALLOW_INTERRUPTION() \ - { \ - if (igraph_i_interruption_handler) \ - if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \ - cliquer_interrupted = 1; \ - return FALSE; \ - } \ - } - -/* Interruptable Cliquer functions must be wrapped in CLIQUER_INTERRUPTABLE when called */ -#define CLIQUER_INTERRUPTABLE(x) \ - { \ - cliquer_interrupted = 0; \ - x; \ - if (cliquer_interrupted) return IGRAPH_INTERRUPTED; \ - } - - -/* Nonzero value signals interuption from Cliquer callback function */ -static IGRAPH_THREAD_LOCAL int cliquer_interrupted; - - -/* For use with IGRAPH_FINALLY */ -static void free_clique_list(igraph_vector_ptr_t *vp) { - igraph_integer_t i, len; - len = igraph_vector_ptr_size(vp); - for (i = 0; i < len; ++i) { - igraph_vector_destroy((igraph_vector_t *) VECTOR(*vp)[i]); - } - igraph_vector_ptr_free_all(vp); -} +#include /* We shall use this option struct for all calls to Cliquer */ static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { @@ -49,9 +34,9 @@ static IGRAPH_THREAD_LOCAL clique_options igraph_cliquer_opt = { /* Convert an igraph graph to a Cliquer graph */ -static void igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { +static igraph_error_t igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { igraph_integer_t vcount, ecount; - int i; + igraph_integer_t i; if (igraph_is_directed(ig)) { IGRAPH_WARNING("Edge directions are ignored for clique calculations"); @@ -60,22 +45,28 @@ static void igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { vcount = igraph_vcount(ig); ecount = igraph_ecount(ig); - *cg = graph_new(vcount); + if (vcount > INT_MAX) { + IGRAPH_ERROR("Graph too large for Cliquer", IGRAPH_EOVERFLOW); + } + + *cg = graph_new((int) vcount); for (i = 0; i < ecount; ++i) { - long s, t; + igraph_integer_t s, t; s = IGRAPH_FROM(ig, i); t = IGRAPH_TO(ig, i); if (s != t) { GRAPH_ADD_EDGE(*cg, s, t); } } + + return IGRAPH_SUCCESS; } /* Copy weights to a Cliquer graph */ -static int set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { - int i; +static igraph_error_t set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { + igraph_integer_t i; IGRAPH_ASSERT(vertex_weights != NULL); @@ -99,36 +90,56 @@ static int set_weights(const igraph_vector_t *vertex_weights, graph_t *g) { /* 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; +typedef struct { + igraph_vector_int_t clique; + igraph_vector_int_list_t* result; +} igraph_i_cliquer_cliques_user_data_t; + +static igraph_error_t igraph_i_cliquer_cliques_user_data_init( + igraph_i_cliquer_cliques_user_data_t* data, + igraph_vector_int_list_t* result +) { + data->result = result; + igraph_vector_int_list_clear(result); + return igraph_vector_int_init(&data->clique, 0); +} + +static void igraph_i_cliquer_cliques_user_data_destroy( + igraph_i_cliquer_cliques_user_data_t* data +) { + igraph_vector_int_destroy(&data->clique); + data->result = 0; +} + +static igraph_error_t collect_cliques_callback(set_t s, graph_t *g, clique_options *opt) { + int i; + igraph_integer_t j; + igraph_i_cliquer_cliques_user_data_t* data = (igraph_i_cliquer_cliques_user_data_t *) opt->user_data; IGRAPH_UNUSED(g); - CLIQUER_ALLOW_INTERRUPTION(); + IGRAPH_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)); + IGRAPH_CHECK(igraph_vector_int_resize(&data->clique, set_size(s))); i = -1; j = 0; while ((i = set_return_next(s, i)) >= 0) { - VECTOR(*clique)[j++] = i; + VECTOR(data->clique)[j++] = i; } - igraph_vector_ptr_push_back(list, clique); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(data->result, &data->clique)); - return TRUE; + return IGRAPH_SUCCESS; } -int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, +igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { graph_t *g; igraph_integer_t vcount = igraph_vcount(graph); + igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_ptr_clear(res); + igraph_vector_int_list_clear(res); return IGRAPH_SUCCESS; } @@ -139,23 +150,28 @@ int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, max_size = 0; } + if (max_size > INT_MAX) { + max_size = INT_MAX; + } + 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_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(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_data = &data; 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); + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); graph_free(g); - IGRAPH_FINALLY_CLEAN(1); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } @@ -163,23 +179,23 @@ int igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, /* Count cliques of each size. */ -static boolean count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { +static igraph_error_t count_cliques_callback(set_t s, graph_t *g, clique_options *opt) { igraph_vector_t *hist; IGRAPH_UNUSED(g); - CLIQUER_ALLOW_INTERRUPTION(); + IGRAPH_ALLOW_INTERRUPTION(); hist = (igraph_vector_t *) opt->user_data; VECTOR(*hist)[set_size(s) - 1] += 1; - return TRUE; + return IGRAPH_SUCCESS; } -int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, +igraph_error_t 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 i; igraph_integer_t vcount = igraph_vcount(graph); if (vcount == 0) { @@ -194,27 +210,32 @@ int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, max_size = vcount; /* also used for initial hist vector size, do not set to zero */ } + if (max_size > INT_MAX) { + max_size = INT_MAX; + } + 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_CHECK(igraph_to_cliquer(graph, &g)); IGRAPH_FINALLY(graph_free, g); - igraph_vector_resize(hist, max_size); + IGRAPH_CHECK(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)); + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); - for (i = max_size; i > 0; --i) + for (i = max_size; i > 0; --i) { if (VECTOR(*hist)[i - 1] > 0) { break; } - igraph_vector_resize(hist, i); + } + IGRAPH_CHECK(igraph_vector_resize(hist, i)); igraph_vector_resize_min(hist); graph_free(g); @@ -227,36 +248,40 @@ int igraph_i_cliquer_histogram(const igraph_t *graph, igraph_vector_t *hist, /* Call function for each clique. */ struct callback_data { + igraph_vector_int_t *clique; igraph_clique_handler_t *handler; void *arg; }; -static boolean callback_callback(set_t s, graph_t *g, clique_options *opt) { - igraph_vector_t *clique; +static igraph_error_t callback_callback(set_t s, graph_t *g, clique_options *opt) { struct callback_data *cd; - int i, j; + int i; + igraph_integer_t j; + igraph_error_t retval; IGRAPH_UNUSED(g); - CLIQUER_ALLOW_INTERRUPTION(); + IGRAPH_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)); + IGRAPH_CHECK(igraph_vector_int_resize(cd->clique, set_size(s))); i = -1; j = 0; while ((i = set_return_next(s, i)) >= 0) { - VECTOR(*clique)[j++] = i; + VECTOR(*cd->clique)[j++] = i; } - return (*(cd->handler))(clique, cd->arg); + retval = (*(cd->handler))(cd->clique, cd->arg); + + return retval; } -int igraph_i_cliquer_callback(const igraph_t *graph, +igraph_error_t 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; + igraph_vector_int_t current_clique; struct callback_data cd; igraph_integer_t vcount = igraph_vcount(graph); @@ -271,22 +296,30 @@ int igraph_i_cliquer_callback(const igraph_t *graph, max_size = 0; } + if (max_size > INT_MAX) { + max_size = INT_MAX; + } + 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_CHECK(igraph_to_cliquer(graph, &g)); IGRAPH_FINALLY(graph_free, g); + IGRAPH_VECTOR_INT_INIT_FINALLY(¤t_clique, min_size); + + cd.clique = ¤t_clique; 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)); + IGRAPH_CHECK(clique_unweighted_find_all(g, (int) min_size, (int) max_size, /* maximal= */ FALSE, &igraph_cliquer_opt, NULL)); + igraph_vector_int_destroy(¤t_clique); graph_free(g); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } @@ -294,14 +327,15 @@ int igraph_i_cliquer_callback(const igraph_t *graph, /* 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_error_t igraph_i_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_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); + igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_ptr_clear(res); + igraph_vector_int_list_clear(res); return IGRAPH_SUCCESS; } @@ -326,21 +360,22 @@ int igraph_i_weighted_cliques(const igraph_t *graph, IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL); } - igraph_to_cliquer(graph, &g); + IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(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_data = &data; 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); + IGRAPH_CHECK(clique_find_all(g, (int) min_weight, (int) max_weight, maximal, &igraph_cliquer_opt, NULL)); graph_free(g); - IGRAPH_FINALLY_CLEAN(1); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } @@ -348,31 +383,33 @@ int igraph_i_weighted_cliques(const igraph_t *graph, /* 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) { +igraph_error_t igraph_i_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res) { graph_t *g; igraph_integer_t vcount = igraph_vcount(graph); + igraph_i_cliquer_cliques_user_data_t data; if (vcount == 0) { - igraph_vector_ptr_clear(res); + igraph_vector_int_list_clear(res); return IGRAPH_SUCCESS; } - igraph_to_cliquer(graph, &g); + IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); + IGRAPH_FINALLY(igraph_i_cliquer_cliques_user_data_destroy, &data); + + IGRAPH_CHECK(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_data = &data; 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); + IGRAPH_CHECK(clique_find_all(g, 0, 0, FALSE, &igraph_cliquer_opt, NULL)); graph_free(g); - IGRAPH_FINALLY_CLEAN(1); + igraph_i_cliquer_cliques_user_data_destroy(&data); + IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } @@ -380,28 +417,40 @@ int igraph_i_largest_weighted_cliques(const igraph_t *graph, /* Find weight of largest weight clique. */ -int igraph_i_weighted_clique_number(const igraph_t *graph, +static igraph_error_t check_interruption_callback(set_t s, graph_t *g, clique_options *opt) { + IGRAPH_UNUSED(s); IGRAPH_UNUSED(g); IGRAPH_UNUSED(opt); + IGRAPH_ALLOW_INTERRUPTION(); + return IGRAPH_SUCCESS; +} + +igraph_error_t 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); + int res_int; if (vcount == 0) { - *res = 0; + if (res) { + *res = 0; + } return IGRAPH_SUCCESS; } - igraph_to_cliquer(graph, &g); + IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); IGRAPH_FINALLY(graph_free, g); IGRAPH_CHECK(set_weights(vertex_weights, g)); - igraph_cliquer_opt.user_function = NULL; + igraph_cliquer_opt.user_function = check_interruption_callback; - /* we are not using a callback function, thus this is not interruptable */ - *res = clique_max_weight(g, &igraph_cliquer_opt); + IGRAPH_CHECK(clique_max_weight(g, &igraph_cliquer_opt, &res_int)); graph_free(g); IGRAPH_FINALLY_CLEAN(1); + if (res) { + *res = res_int; + } + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/cliques/cliques.c b/src/vendor/cigraph/src/cliques/cliques.c index 23ce9ae7f4a..0e7d07f2b39 100644 --- a/src/vendor/cigraph/src/cliques/cliques.c +++ b/src/vendor/cigraph/src/cliques/cliques.c @@ -28,8 +28,6 @@ #include "igraph_constants.h" #include "igraph_adjlist.h" #include "igraph_interface.h" -#include "igraph_progress.h" -#include "igraph_stack.h" #include "cliques/cliquer_internal.h" #include "core/interruption.h" @@ -37,54 +35,39 @@ #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( +static igraph_error_t igraph_i_find_k_indsets( 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_integer_t size, + const igraph_integer_t *member_storage, + igraph_integer_t **new_member_storage, + igraph_integer_t old_count, + igraph_integer_t *new_count, + igraph_vector_int_t *neis) { + + igraph_integer_t j, k, l, m, n, new_member_storage_size; + const igraph_integer_t *c1, *c2; + igraph_integer_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; + (size_t) (size * old_count), + igraph_integer_t); + IGRAPH_CHECK_OOM(*new_member_storage, "igraph_independent_vertex_sets failed"); + + new_member_storage_size = size * old_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++) { + /* Now consider all pairs of i-1-indsets and see if they can be merged */ + for (j = 0; j < old_count; j++) { + for (k = j + 1; k < old_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 + /* Since indsets are represented by their vertex indices in increasing + * order, two indsets can be merged iff they have exactly the same + * indices excluding one AND there is no edge between the two different * vertices */ c1 = member_storage + j * (size - 1); c2 = member_storage + k * (size - 1); @@ -92,214 +75,88 @@ static int igraph_i_find_k_cliques( 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; + if (ok && c2[l] == v1) { + (*new_member_storage)[m++] = c2[l]; + v2 = c1[l]; } else { - m = n; + break; } - } else { - m = n; } + } else { + break; } - /* 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); + } + /* Now, if l != size-1, the two vectors had a difference in more than + * one place, so the whole independent vertex set is invalid. */ + if (l != size - 1) { + /* Step back in new_member_storage */ + m = n; + } else { + /* v1 and v2 are the two different vertices. Check for the + * absence of an edge since we are looking for independent + * vertex sets */ + IGRAPH_CHECK(igraph_neighbors(graph, neis, v1, IGRAPH_ALL)); + if (!igraph_vector_int_search(neis, 0, v2, 0)) { + /* Found a new independent vertex set, step forward in new_member_storage */ + if (m == n || v2 > (*new_member_storage)[m - 1]) { + (*new_member_storage)[m++] = v2; + n = m; + } else { + m = n; } - new_member_storage_size *= 2; - IGRAPH_FINALLY(igraph_free, *new_member_storage); + } else { + m = n; } } - } - } - - /* 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); + /* 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_integer_t); + IGRAPH_CHECK_OOM(*new_member_storage, "igraph_independent_vertex_sets failed"); + new_member_storage_size *= 2; + IGRAPH_FINALLY(igraph_free, *new_member_storage); } - 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; + /* Calculate how many independent vertex sets we have found */ + *new_count = n / size; - 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; + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } /** @@ -318,14 +175,11 @@ static int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, * 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 + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. + * \param min_size Integer specifying 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 + * \param max_size Integer specifying the maximum size of the cliques to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * @@ -335,7 +189,7 @@ static int igraph_i_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, * * \example examples/simple/igraph_cliques.c */ -int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, +igraph_error_t igraph_cliques(const igraph_t *graph, igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { return igraph_i_cliquer_cliques(graph, res, min_size, max_size); } @@ -357,9 +211,9 @@ int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, * 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 + * \param min_size Integer specifying 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 + * \param max_size Integer specifying the maximum size of the cliques to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * @@ -368,7 +222,7 @@ int igraph_cliques(const igraph_t *graph, igraph_vector_ptr_t *res, * Time complexity: Exponential * */ -int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, +igraph_error_t 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); } @@ -382,8 +236,8 @@ int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, * 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() + * callback function as a pointer to an \ref igraph_vector_int_t. Destroying and + * freeing this vector is left up to the user. Use \ref igraph_vector_int_destroy() * to destroy it first, then free it using \ref igraph_free(). * * The current implementation of this function @@ -391,12 +245,12 @@ int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, * 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 + * \param min_size Integer specifying 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 + * \param max_size Integer specifying 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. + * See also \ref igraph_clique_handler_t. * \param arg Extra argument to supply to \p cliquehandler_fn. * \return Error code. * @@ -405,7 +259,7 @@ int igraph_clique_size_hist(const igraph_t *graph, igraph_vector_t *hist, * Time complexity: Exponential * */ -int igraph_cliques_callback(const igraph_t *graph, +igraph_error_t 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); @@ -431,14 +285,11 @@ int igraph_cliques_callback(const igraph_t *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 + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. + * \param min_weight Integer specifying 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 + * \param max_weight Integer specifying 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. @@ -448,8 +299,8 @@ int igraph_cliques_callback(const igraph_t *graph, * Time complexity: Exponential * */ -int igraph_weighted_cliques(const igraph_t *graph, - const igraph_vector_t *vertex_weights, igraph_vector_ptr_t *res, +igraph_error_t igraph_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_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); @@ -478,19 +329,16 @@ int igraph_weighted_cliques(const igraph_t *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 res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. * \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) { +igraph_error_t igraph_largest_weighted_cliques(const igraph_t *graph, + const igraph_vector_t *vertex_weights, igraph_vector_int_list_t *res) { if (vertex_weights) { return igraph_i_largest_weighted_cliques(graph, vertex_weights, res); } else { @@ -522,7 +370,7 @@ int igraph_largest_weighted_cliques(const igraph_t *graph, * Time complexity: TODO * */ -int igraph_weighted_clique_number(const igraph_t *graph, +igraph_error_t 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); @@ -536,16 +384,16 @@ int igraph_weighted_clique_number(const igraph_t *graph, } } -typedef int(*igraph_i_maximal_clique_func_t)(const igraph_vector_t*, void*, igraph_bool_t*); +typedef igraph_error_t(*igraph_i_maximal_clique_func_t)(const igraph_vector_int_t*, void*, igraph_bool_t*); typedef struct { - igraph_vector_ptr_t* result; + igraph_vector_int_list_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( +static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets( const igraph_t *graph, - igraph_vector_ptr_t *res, + igraph_vector_int_list_t *res, igraph_integer_t *clique_number, igraph_bool_t keep_only_largest, igraph_bool_t complementer); @@ -570,14 +418,11 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets( * 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 + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. + * \param min_size Integer specifying 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 + * \param max_size Integer specifying the maximum size of the sets to be * returned. If negative or zero, no upper bound will be used. * \return Error code. * @@ -588,11 +433,101 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets( * * \example examples/simple/igraph_independent_sets.c */ -int igraph_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res, +igraph_error_t igraph_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res, igraph_integer_t min_size, igraph_integer_t max_size) { - return igraph_i_cliques(graph, res, min_size, max_size, 1); + igraph_integer_t no_of_nodes; + igraph_vector_int_t neis, *indset; + igraph_integer_t *member_storage = 0, *new_member_storage, *c1; + igraph_vector_int_t new_member_storage_view; + igraph_integer_t i, j, k, indset_count, old_indset_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_int_list_clear(res); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* Will be resized later, if needed. */ + member_storage = IGRAPH_CALLOC(1, igraph_integer_t); + if (member_storage == 0) { + IGRAPH_ERROR("igraph_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + 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_integer_t); + if (new_member_storage == 0) { + IGRAPH_ERROR("igraph_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, new_member_storage); + + for (i = 0; i < no_of_nodes; i++) { + new_member_storage[i] = i; + } + indset_count = no_of_nodes; + old_indset_count = 0; + + /* Add size 1 indsets if requested */ + if (min_size <= 1) { + IGRAPH_CHECK(igraph_vector_int_list_resize(res, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + indset = igraph_vector_int_list_get_ptr(res, i); + IGRAPH_CHECK(igraph_vector_int_push_back(indset, i)); + } + } + + for (i = 2; i <= max_size && indset_count > 1; i++) { + + /* Here new_member_storage contains the independent vertex sets 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_indset_count = indset_count; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Calculate the independent vertex sets */ + + IGRAPH_FINALLY_CLEAN(2); + IGRAPH_CHECK(igraph_i_find_k_indsets(graph, i, member_storage, + &new_member_storage, + old_indset_count, + &indset_count, + &neis)); + 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 < indset_count; j++, k += i) { + igraph_vector_int_view(&new_member_storage_view, new_member_storage + k, i); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(res, &new_member_storage_view)); + } + } + + } /* i <= max_size && clique_count != 0 */ + + igraph_free(member_storage); + igraph_free(new_member_storage); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; } /** @@ -611,8 +546,8 @@ int igraph_independent_vertex_sets(const igraph_t *graph, * 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. + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. * \return Error code. * * \sa \ref igraph_independent_vertex_sets(), \ref @@ -621,15 +556,15 @@ int igraph_independent_vertex_sets(const igraph_t *graph, * Time complexity: TODO */ -int igraph_largest_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res) { +igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_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_vector_int_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; @@ -637,59 +572,51 @@ typedef struct igraph_i_max_ind_vsets_data_t { 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( +static igraph_error_t igraph_i_maximal_independent_vertex_sets_backtrack( const igraph_t *graph, - igraph_vector_ptr_t *res, + igraph_vector_int_list_t *res, igraph_i_max_ind_vsets_data_t *clqdata, igraph_integer_t level) { - long int v1, v2, v3, c, j, k; + igraph_integer_t 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_integer_t it_state; + igraph_vector_int_t vec; IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + 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++) + igraph_vector_int_clear(&vec); + + for (v1 = 0; v1 < clqdata->matrix_size; v1++) { if (clqdata->IS[v1] == 0) { - IGRAPH_CHECK(igraph_vector_push_back(vec, v1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vec, v1)); } - size = (igraph_integer_t) igraph_vector_size(vec); + } + + size = igraph_vector_int_size(&vec); if (!clqdata->keep_only_largest) { - IGRAPH_CHECK(igraph_vector_ptr_push_back(res, vec)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(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)); + igraph_vector_int_list_clear(res); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(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_CHECK(igraph_vector_int_list_push_back_copy(res, &vec)); } } - IGRAPH_FINALLY_CLEAN(1); } else { - for (v1 = 0, size = 0; v1 < clqdata->matrix_size; v1++) + 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; @@ -702,7 +629,7 @@ static int igraph_i_maximal_independent_vertex_sets_backtrack( c = 0; j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + (v2 = VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { c++; } @@ -713,34 +640,33 @@ static int igraph_i_maximal_independent_vertex_sets_backtrack( /* If there are no such nodes... */ j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + (v2 = VECTOR(*neis1)[j]) <= level) { clqdata->IS[v2]++; j++; } - IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1)); + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + (v2 = 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] = c; + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); clqdata->IS[v1] = 0; f = 1; j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + (v2 = VECTOR(*neis1)[j]) <= level) { if (clqdata->IS[v2] == 0) { - IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], - (igraph_integer_t) j)); + IGRAPH_CHECK(igraph_set_add(&clqdata->buckets[v1], j)); neis2 = igraph_adjlist_get(&clqdata->adj_list, v2); k = 0; while (k < VECTOR(clqdata->deg)[v2] && - (v3 = (long int) VECTOR(*neis2)[k]) <= level) { + (v3 = VECTOR(*neis2)[k]) <= level) { clqdata->IS[v3]--; if (clqdata->IS[v3] == 0) { f = 0; @@ -753,24 +679,23 @@ static int igraph_i_maximal_independent_vertex_sets_backtrack( } if (f) { - IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, (igraph_integer_t) v1)); + IGRAPH_CHECK(igraph_i_maximal_independent_vertex_sets_backtrack(graph, res, clqdata, v1)); } j = 0; while (j < VECTOR(clqdata->deg)[v1] && - (v2 = (long int) VECTOR(*neis1)[j]) <= level) { + (v2 = 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]; + while (igraph_set_iterate(&clqdata->buckets[v1], &it_state, &j)) { + v2 = 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) { + (v3 = VECTOR(*neis2)[k]) <= level) { clqdata->IS[v3]++; k++; } @@ -779,11 +704,14 @@ static int igraph_i_maximal_independent_vertex_sets_backtrack( } } - return 0; + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } static void igraph_i_free_set_array(igraph_set_t* array) { - long int i = 0; + igraph_integer_t i = 0; while (igraph_set_inited(array + i)) { igraph_set_destroy(array + i); i++; @@ -815,11 +743,8 @@ static void igraph_i_free_set_array(igraph_set_t* array) { * 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. + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. * \return Error code. * * \sa \ref igraph_maximal_cliques(), \ref @@ -827,10 +752,10 @@ static void igraph_i_free_set_array(igraph_set_t* array) { * * Time complexity: TODO. */ -int igraph_maximal_independent_vertex_sets(const igraph_t *graph, - igraph_vector_ptr_t *res) { +igraph_error_t igraph_maximal_independent_vertex_sets(const igraph_t *graph, + igraph_vector_int_list_t *res) { igraph_i_max_ind_vsets_data_t clqdata; - igraph_integer_t no_of_nodes = (igraph_integer_t) igraph_vcount(graph), i; + igraph_integer_t no_of_nodes = igraph_vcount(graph), i; if (igraph_is_directed(graph)) { IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); @@ -846,18 +771,18 @@ int igraph_maximal_independent_vertex_sets(const igraph_t *graph, 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_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + IGRAPH_VECTOR_INT_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_ERROR("igraph_maximal_independent_vertex_sets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -865,7 +790,7 @@ int igraph_maximal_independent_vertex_sets(const igraph_t *graph, IGRAPH_CHECK(igraph_set_init(&clqdata.buckets[i], 0)); } - igraph_vector_ptr_clear(res); + igraph_vector_int_list_clear(res); /* Do the show */ clqdata.largest_set_size = 0; @@ -876,11 +801,11 @@ int igraph_maximal_independent_vertex_sets(const igraph_t *graph, igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_destroy(&clqdata.deg); + igraph_vector_int_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /** @@ -907,9 +832,9 @@ int igraph_maximal_independent_vertex_sets(const igraph_t *graph, * * Time complexity: TODO. */ -int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { +igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph), i; if (igraph_is_directed(graph)) { IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); @@ -925,18 +850,18 @@ int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { clqdata.IS = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (clqdata.IS == 0) { - IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + IGRAPH_VECTOR_INT_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_ERROR("igraph_independence_number failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -954,53 +879,48 @@ int igraph_independence_number(const igraph_t *graph, igraph_integer_t *no) { igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_destroy(&clqdata.deg); + igraph_vector_int_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /*************************************************************************/ /* MAXIMAL CLIQUES, LARGEST CLIQUES */ /*************************************************************************/ -static igraph_bool_t igraph_i_maximal_cliques_store_max_size(igraph_vector_t* clique, void* data) { +static igraph_error_t igraph_i_maximal_cliques_store_max_size(const igraph_vector_int_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); + if (*result < igraph_vector_int_size(clique)) { + *result = igraph_vector_int_size(clique); } - igraph_vector_destroy(clique); - igraph_Free(clique); - return 1; + return IGRAPH_SUCCESS; } -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; +static igraph_error_t igraph_i_largest_cliques_store(const igraph_vector_int_t* clique, void* data) { + igraph_vector_int_list_t* result = (igraph_vector_int_list_t*)data; + igraph_integer_t 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 (!igraph_vector_int_list_empty(result)) { + igraph_vector_int_t* first; + + n = igraph_vector_int_size(clique); + first = igraph_vector_int_list_get_ptr(result, 0); + if (n < igraph_vector_int_size(first)) { + return IGRAPH_SUCCESS; } - 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); + if (n > igraph_vector_int_size(first)) { + igraph_vector_int_list_clear(result); } } - IGRAPH_CHECK(igraph_vector_ptr_push_back(result, clique)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(result, clique)); - return 1; + return IGRAPH_SUCCESS; } /** @@ -1026,9 +946,8 @@ static igraph_bool_t igraph_i_largest_cliques_store(igraph_vector_t* clique, voi * 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. + * \param res Pointer to a list of integer vectors, the result will be stored + * here. The pointer vector will be resized if needed. * \return Error code. * * \sa \ref igraph_cliques(), \ref igraph_maximal_cliques() @@ -1036,11 +955,9 @@ static igraph_bool_t igraph_i_largest_cliques_store(igraph_vector_t* clique, voi * 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_error_t igraph_largest_cliques(const igraph_t *graph, igraph_vector_int_list_t *res) { + igraph_vector_int_list_clear(res); IGRAPH_CHECK(igraph_maximal_cliques_callback(graph, &igraph_i_largest_cliques_store, (void*)res, 0, 0)); - IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } @@ -1064,18 +981,18 @@ int igraph_largest_cliques(const igraph_t *graph, igraph_vector_ptr_t *res) { * * Time complexity: O(3^(|V|/3)) worst case. */ -int igraph_clique_number(const igraph_t *graph, igraph_integer_t *no) { +igraph_error_t 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, +static igraph_error_t igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, + igraph_vector_int_list_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; + igraph_integer_t no_of_nodes = igraph_vcount(graph), i; if (igraph_is_directed(graph)) { IGRAPH_WARNING("directionality of edges is ignored for directed graphs"); @@ -1095,18 +1012,18 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, 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_ERROR("igraph_i_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, clqdata.IS); - IGRAPH_VECTOR_INIT_FINALLY(&clqdata.deg, no_of_nodes); + IGRAPH_VECTOR_INT_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_ERROR("igraph_maximal_or_largest_cliques_or_indsets failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_i_free_set_array, clqdata.buckets); @@ -1115,7 +1032,7 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, } if (res) { - igraph_vector_ptr_clear(res); + igraph_vector_int_list_clear(res); } /* Do the show */ @@ -1127,7 +1044,7 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, igraph_set_destroy(&clqdata.buckets[i]); } igraph_adjlist_destroy(&clqdata.adj_list); - igraph_vector_destroy(&clqdata.deg); + igraph_vector_int_destroy(&clqdata.deg); igraph_free(clqdata.IS); igraph_free(clqdata.buckets); IGRAPH_FINALLY_CLEAN(4); @@ -1135,5 +1052,5 @@ static int igraph_i_maximal_or_largest_cliques_or_indsets(const igraph_t *graph, if (clique_number) { *clique_number = clqdata.largest_set_size; } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/cliques/glet.c b/src/vendor/cigraph/src/cliques/glet.c index 74d14add203..8f0f4b02ceb 100644 --- a/src/vendor/cigraph/src/cliques/glet.c +++ b/src/vendor/cigraph/src/cliques/glet.c @@ -26,6 +26,7 @@ #include "igraph_conversion.h" #include "igraph_constructors.h" #include "igraph_cliques.h" +#include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_operators.h" #include "igraph_qsort.h" @@ -74,12 +75,12 @@ typedef struct { igraph_vector_int_t *resultids; igraph_t *result; igraph_vector_t *resultweights; - int nc; + igraph_integer_t 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; + igraph_integer_t i; if (data->resultids) { for (i = 0; i < data->nc; i++) { igraph_vector_int_destroy(&data->resultids[i]); @@ -106,11 +107,11 @@ static void igraph_i_subclique_next_free(void *ptr) { * * \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 ids The IDs of the vertices in the input graph. + * \param cliques A list of \ref igraph_vector_int_t, 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 + * \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. @@ -119,10 +120,10 @@ static void igraph_i_subclique_next_free(void *ptr) { * */ -static int igraph_i_subclique_next(const igraph_t *graph, +static igraph_error_t 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, + const igraph_vector_int_list_t *cliques, igraph_t **result, igraph_vector_t **resultweights, igraph_vector_int_t **resultids, @@ -135,11 +136,11 @@ static int igraph_i_subclique_next(const igraph_t *graph, 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_vector_int_t neis, newedges; + igraph_integer_t c, nc = igraph_vector_int_list_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 }; + igraph_i_subclique_next_free_t freedata = { NULL, NULL, NULL, nc }; if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid length of weight vector", IGRAPH_EINVAL); @@ -150,53 +151,46 @@ static int igraph_i_subclique_next(const igraph_t *graph, } 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); - } + IGRAPH_CHECK_OOM(*resultids, "Cannot calculate next cliques."); freedata.resultids = *resultids; + *resultweights = IGRAPH_CALLOC(nc, igraph_vector_t); - if (!*resultweights) { - IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(*resultweights, "Cannot calculate next cliques."); freedata.resultweights = *resultweights; + *result = IGRAPH_CALLOC(nc, igraph_t); - if (!*result) { - IGRAPH_ERROR("Cannot calculate next cliques", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(*result, "Cannot calculate next cliques."); 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newedges, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&map, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); if (clique_thr) { - igraph_vector_resize(clique_thr, nc); + IGRAPH_CHECK(igraph_vector_resize(clique_thr, nc)); } - if (next_thr) { - igraph_vector_resize(next_thr, nc); + if (next_thr) { + IGRAPH_CHECK(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_vector_int_t *clique = igraph_vector_int_list_get_ptr(cliques, c); igraph_real_t minweight = IGRAPH_INFINITY, nextweight = IGRAPH_INFINITY; - igraph_integer_t e, v, clsize = igraph_vector_size(clique); + igraph_integer_t e, v, clsize = igraph_vector_int_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); + igraph_vector_int_clear(&newedges); /* --------------------------------------------------- */ @@ -207,15 +201,15 @@ static int igraph_i_subclique_next(const igraph_t *graph, 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); + IGRAPH_CHECK(igraph_incident(graph, &neis, node, IGRAPH_ALL)); + neilen = igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, edge)); if (w < minweight) { nextweight = minweight; minweight = w; @@ -243,8 +237,8 @@ static int igraph_i_subclique_next(const igraph_t *graph, /* 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); + IGRAPH_CHECK(igraph_vector_int_init(newids, 0)); + IGRAPH_CHECK(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 @@ -256,52 +250,53 @@ static int igraph_i_subclique_next(const igraph_t *graph, 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); + IGRAPH_CHECK(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]); + IGRAPH_CHECK(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_CHECK(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_CHECK(igraph_vector_push_back(neww, w)); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[from])); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(map)[to])); } } - igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED); + IGRAPH_CHECK(igraph_create(newgraph, &newedges, nov, IGRAPH_UNDIRECTED)); /* --------------------------------------------------- */ } /* c < nc */ - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); igraph_vector_int_destroy(&edges); igraph_vector_int_destroy(&mark); igraph_vector_int_destroy(&map); - igraph_vector_destroy(&newedges); + igraph_vector_int_destroy(&newedges); IGRAPH_FINALLY_CLEAN(6); /* + freedata */ - return 0; + return IGRAPH_SUCCESS; } -static void igraph_i_graphlets_destroy_vectorlist(igraph_vector_ptr_t *vl) { - int i, n = igraph_vector_ptr_size(vl); +static void igraph_i_graphlets_destroy_clique_list(igraph_vector_ptr_t *vl) { + igraph_integer_t i, n = igraph_vector_ptr_size(vl); for (i = 0; i < n; i++) { - igraph_vector_t *v = (igraph_vector_t*) VECTOR(*vl)[i]; + igraph_vector_int_t *v = (igraph_vector_int_t*) VECTOR(*vl)[i]; if (v) { - igraph_vector_destroy(v); + igraph_vector_int_destroy(v); + IGRAPH_FREE(v); } } igraph_vector_ptr_destroy(vl); } -static int igraph_i_graphlets(const igraph_t *graph, +static igraph_error_t igraph_i_graphlets(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds, @@ -313,45 +308,43 @@ static int igraph_i_graphlets(const igraph_t *graph, 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_vector_int_list_t mycliques; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_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_integer_t i, j, nocliques; + igraph_t *newgraphs = NULL; + igraph_vector_t *newweights = NULL; + igraph_vector_int_t *newids = NULL; igraph_vector_t clique_thr, next_thr; - igraph_i_subclique_next_free_t freedata = { 0, 0, 0, 0 }; + igraph_i_subclique_next_free_t freedata = { NULL, NULL, NULL, 0 }; - IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0)); - IGRAPH_FINALLY(igraph_i_graphlets_destroy_vectorlist, &mycliques); - IGRAPH_VECTOR_INIT_FINALLY(&subv, 0); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&mycliques, 0); + IGRAPH_VECTOR_INT_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_CHECK(igraph_vector_int_push_back(&subv, i)); } } - igraph_subgraph_edges(graph, &subg, igraph_ess_vector(&subv), - /*delete_vertices=*/ 0); + IGRAPH_CHECK(igraph_subgraph_from_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_CHECK(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_int_destroy(&subv); + IGRAPH_FINALLY_CLEAN(2); - igraph_vector_destroy(&subv); - IGRAPH_FINALLY_CLEAN(1); + nocliques = igraph_vector_int_list_size(&mycliques); /* 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); + IGRAPH_CHECK(igraph_i_subclique_next( + graph, weights, ids, &mycliques, &newgraphs, &newweights, &newids, + &clique_thr, &next_thr + )); freedata.result = newgraphs; freedata.resultids = newids; @@ -360,36 +353,45 @@ static int igraph_i_graphlets(const igraph_t *graph, 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_CHECK(igraph_vector_append(thresholds, &clique_thr)); + IGRAPH_CHECK(igraph_vector_ptr_resize(cliques, igraph_vector_ptr_size(cliques) + nocliques)); + for (i = 0, j = igraph_vector_ptr_size(cliques) - 1; i < nocliques; i++, j--) { + igraph_vector_int_t *cl = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(cl, "Cannot find graphlets."); + IGRAPH_FINALLY(igraph_free, cl); + + *cl = igraph_vector_int_list_pop_back(&mycliques); + + /* From this point onwards, _we_ own the clique and not `mycliques'. + * We pass on the ownership to `cliques' */ + VECTOR(*cliques)[j] = cl; + IGRAPH_FINALLY_CLEAN(1); + + igraph_integer_t k, n = igraph_vector_int_size(cl); + for (k = 0; k < n; k++) { + igraph_integer_t node = VECTOR(*cl)[k]; + VECTOR(*cl)[k] = VECTOR(*ids)[node]; } - igraph_vector_sort(cl); + igraph_vector_int_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++) { + for (i = 0; i < nocliques; 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_CHECK(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_vector_int_list_destroy(&mycliques); /* contents was copied over to `cliques' */ IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } typedef struct { @@ -399,12 +401,12 @@ typedef struct { 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) 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; + igraph_vector_int_t *v_a, *v_b; + igraph_integer_t s_a, s_b; if (t_a < t_b) { return -1; @@ -412,10 +414,10 @@ static int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void * 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); + v_a = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*aa]; + v_b = (igraph_vector_int_t*) VECTOR(*ddata->cliques)[*bb]; + s_a = igraph_vector_int_size(v_a); + s_b = igraph_vector_int_size(v_b); if (s_a < s_b) { return -1; @@ -426,7 +428,7 @@ static int igraph_i_graphlets_filter_cmp(void *data, const void *a, const void * } } -static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, +static igraph_error_t igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, igraph_vector_t *thresholds) { /* Filter out non-maximal cliques. Every non-maximal clique is @@ -437,31 +439,27 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, 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_integer_t 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_CHECK(igraph_vector_int_init_range(&order, 0, 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_qsort_r(VECTOR(order), nocliques, sizeof(VECTOR(order)[0]), &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_integer_t ri = VECTOR(order)[i]; + igraph_vector_int_t *needle = VECTOR(*cliques)[ri]; igraph_real_t thr_i = VECTOR(*thresholds)[ri]; - int n_i = igraph_vector_size(needle); - int j = i + 1; + igraph_integer_t n_i = igraph_vector_int_size(needle); - for (j = i + 1; j < nocliques; j++) { - int rj = VECTOR(order)[j]; + for (igraph_integer_t j = i + 1; j < nocliques; j++) { + igraph_integer_t rj = VECTOR(order)[j]; igraph_real_t thr_j = VECTOR(*thresholds)[rj]; - igraph_vector_t *hay; - int n_j, pi = 0, pj = 0; + igraph_vector_int_t *hay; + igraph_integer_t n_j, pi = 0, pj = 0; /* Done, not found */ if (thr_j != thr_i) { @@ -470,15 +468,15 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, /* Check size of hay */ hay = VECTOR(*cliques)[rj]; - n_j = igraph_vector_size(hay); + n_j = igraph_vector_int_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]; + igraph_integer_t ei = VECTOR(*needle)[pi]; + igraph_integer_t ej = VECTOR(*hay)[pj]; if (ei < ej) { break; } else if (ei > ej) { @@ -489,7 +487,7 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, } if (pi == n_i) { /* Found, delete immediately */ - igraph_vector_destroy(needle); + igraph_vector_int_destroy(needle); igraph_free(needle); VECTOR(*cliques)[ri] = 0; break; @@ -499,20 +497,20 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, /* Remove null pointers from the list of cliques */ for (i = 0, iptr = 0; i < nocliques; i++) { - igraph_vector_t *v = VECTOR(*cliques)[i]; + igraph_vector_int_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_CHECK(igraph_vector_ptr_resize(cliques, iptr)); + IGRAPH_CHECK(igraph_vector_resize(thresholds, iptr)); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -522,10 +520,9 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, * \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 cliques An initialized list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. * \param thresholds An initialized vector, the (highest possible) * weight thresholds for finding the basis subgraphs are stored * here. @@ -534,17 +531,18 @@ static int igraph_i_graphlets_filter(igraph_vector_ptr_t *cliques, * See also: \ref igraph_graphlets() and \ref igraph_graphlets_project(). */ -int igraph_graphlets_candidate_basis(const igraph_t *graph, +igraph_error_t igraph_graphlets_candidate_basis(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_ptr_t *cliques, + igraph_vector_int_list_t *cliques, igraph_vector_t *thresholds) { - int no_of_nodes = igraph_vcount(graph); - int no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_real_t minthr; igraph_vector_int_t ids; igraph_bool_t simple; - int i; + igraph_integer_t i, no_of_cliques; + igraph_vector_ptr_t mycliques; /* Some checks */ if (weights == NULL) { @@ -559,39 +557,70 @@ int igraph_graphlets_candidate_basis(const igraph_t *graph, if (!simple) { IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); } + if (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + } - minthr = igraph_vector_min(weights); - igraph_vector_ptr_clear(cliques); + /* Internally, we will still use igraph_vector_ptr_t instead of + * igraph_vector_int_list_t to manage the list of cliques; this is because + * we are going to append & filter the list and it's more complicated to + * do with an igraph_vector_int_list_t */ + IGRAPH_CHECK(igraph_vector_ptr_init(&mycliques, 0)); + IGRAPH_FINALLY(igraph_i_graphlets_destroy_clique_list, &mycliques); + + igraph_vector_int_list_clear(cliques); igraph_vector_clear(thresholds); - igraph_vector_int_init(&ids, no_of_nodes); + + minthr = igraph_vector_min(weights); + + IGRAPH_CHECK(igraph_vector_int_init_range(&ids, 0, 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_CHECK(igraph_i_graphlets(graph, weights, &mycliques, thresholds, &ids, minthr)); igraph_vector_int_destroy(&ids); IGRAPH_FINALLY_CLEAN(1); - igraph_i_graphlets_filter(cliques, thresholds); + IGRAPH_CHECK(igraph_i_graphlets_filter(&mycliques, thresholds)); - return 0; + /* Pass ownership of cliques in `mycliques' to `cliques' so the user does + * not have to work with igraph_vector_ptr_t */ + no_of_cliques = igraph_vector_ptr_size(&mycliques); + for (i = 0; i < no_of_cliques; i++) { + IGRAPH_CHECK(igraph_vector_int_list_push_back( + cliques, VECTOR(mycliques)[i] + )); + IGRAPH_FREE(VECTOR(mycliques)[i]); + } + + /* `mycliques' is now empty so we can clear and destroy */ + igraph_vector_ptr_clear(&mycliques); + igraph_vector_ptr_destroy(&mycliques); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /* 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_error_t igraph_i_graphlets_project( + const igraph_t *graph, const igraph_vector_t *weights, + const igraph_vector_int_list_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, + igraph_integer_t niter, igraph_integer_t vid1 +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_cliques = igraph_vector_int_list_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_vector_int_t edgelist; + igraph_vector_t newweights, normfact; + igraph_integer_t i, total_vertices, e, ptr, total_edges; igraph_bool_t simple; /* Check arguments */ @@ -611,26 +640,33 @@ int igraph_i_graphlets_project(const igraph_t *graph, if (!simple) { IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); } + if (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Graphlets work on simple graphs only", IGRAPH_EINVAL); + } + } if (!startMu) { - igraph_vector_resize(Mu, no_cliques); + IGRAPH_CHECK(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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vclidx, no_of_nodes + 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&celidx, no_cliques + 3); 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); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t j, n = igraph_vector_int_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; + igraph_integer_t vv = VECTOR(*v)[j] - vid1; VECTOR(vclidx)[vv + 2] += 1; } } @@ -642,38 +678,34 @@ int igraph_i_graphlets_project(const igraph_t *graph, } /* 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vcl, total_vertices); for (i = 0; i < no_cliques; i++) { - igraph_vector_t *v = VECTOR(*cliques)[i]; - int j, n = igraph_vector_size(v); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t j, n = igraph_vector_int_size(v); for (j = 0; j < n; j++) { - int vv = VECTOR(*v)[j] - vid1; - int p = VECTOR(vclidx)[vv + 1]; + igraph_integer_t vv = VECTOR(*v)[j] - vid1; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&ecl, total_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eclidx, no_of_edges + 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, no_of_edges * 2); 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]; + igraph_integer_t from = VECTOR(edgelist)[i++]; + igraph_integer_t to = VECTOR(edgelist)[i++]; + igraph_integer_t from_s = VECTOR(vclidx)[from]; + igraph_integer_t from_e = VECTOR(vclidx)[from + 1]; + igraph_integer_t to_s = VECTOR(vclidx)[to]; + igraph_integer_t 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]; + igraph_integer_t from_v = VECTOR(vcl)[from_s]; + igraph_integer_t to_v = VECTOR(vcl)[to_s]; if (from_v == to_v) { VECTOR(ecl)[ptr++] = from_v; from_s++; to_s++; @@ -686,50 +718,47 @@ int igraph_i_graphlets_project(const igraph_t *graph, } VECTOR(eclidx)[e] = ptr; - igraph_vector_destroy(&edgelist); + igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cel, total_edges); for (i = 0; i < no_of_edges; i++) { - int ecl_s = VECTOR(eclidx)[i], ecl_e = VECTOR(eclidx)[i + 1], j; + igraph_integer_t 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]; + igraph_integer_t cl = VECTOR(ecl)[j]; + igraph_integer_t 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); + IGRAPH_VECTOR_INIT_FINALLY(&normfact, no_cliques); for (i = 0; i < no_cliques; i++) { - igraph_vector_t *v = VECTOR(*cliques)[i]; - int n = igraph_vector_size(v); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(cliques, i); + igraph_integer_t n = igraph_vector_int_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); + IGRAPH_VECTOR_INIT_FINALLY(&newweights, no_of_edges); 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]; + igraph_integer_t start = VECTOR(eclidx)[e]; + igraph_integer_t end = VECTOR(eclidx)[e + 1]; VECTOR(newweights)[e] = 0.0001; while (start < end) { - int clique = VECTOR(ecl)[start++]; + igraph_integer_t 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]; + igraph_integer_t start = VECTOR(celidx)[e]; + igraph_integer_t end = VECTOR(celidx)[e + 1]; while (start < end) { - int edge = VECTOR(cel)[start++]; + igraph_integer_t edge = VECTOR(cel)[start++]; sumratio += VECTOR(*weights)[edge] / VECTOR(newweights)[edge]; } VECTOR(*Mu)[e] *= sumratio / VECTOR(normfact)[e]; @@ -746,7 +775,7 @@ int igraph_i_graphlets_project(const igraph_t *graph, igraph_vector_int_destroy(&vclidx); IGRAPH_FINALLY_CLEAN(8); - return 0; + return IGRAPH_SUCCESS; } /** @@ -755,13 +784,14 @@ int igraph_i_graphlets_project(const igraph_t *graph, * * 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 + * 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 cliques An initialized list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. * \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 @@ -776,25 +806,25 @@ int igraph_i_graphlets_project(const igraph_t *graph, * \ref igraph_graphlets_candidate_basis(). */ -int igraph_graphlets_project(const igraph_t *graph, +igraph_error_t igraph_graphlets_project(const igraph_t *graph, const igraph_vector_t *weights, - const igraph_vector_ptr_t *cliques, + const igraph_vector_int_list_t *cliques, igraph_vector_t *Mu, igraph_bool_t startMu, - int niter) { + igraph_integer_t 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_int_list_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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t Mu_a = VECTOR(*ddata->Mu)[*aa]; igraph_real_t Mu_b = VECTOR(*ddata->Mu)[*bb]; @@ -817,9 +847,9 @@ static int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b * \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 cliques An initialized list of integer vectors. The graphlet basis is + * stored here. Each element of the list is an integer vector of + * vertex IDs, encoding a single basis subgraph. * \param Mu An initialized vector, the weights of the graphlets will * be stored here. * \param niter Integer scalar, the number of iterations to perform @@ -830,38 +860,35 @@ static int igraph_i_graphlets_order_cmp(void *data, const void *a, const void *b * \ref igraph_graphlets_project(). */ -int igraph_graphlets(const igraph_t *graph, +igraph_error_t igraph_graphlets(const igraph_t *graph, const igraph_vector_t *weights, - igraph_vector_ptr_t *cliques, - igraph_vector_t *Mu, int niter) { + igraph_vector_int_list_t *cliques, + igraph_vector_t *Mu, igraph_integer_t niter) { - int i, nocliques; + igraph_integer_t 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_INIT_FINALLY(&thresholds, 0); + IGRAPH_CHECK(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); + IGRAPH_CHECK(igraph_graphlets_project(graph, weights, cliques, Mu, /*startMu=*/ false, niter)); - nocliques = igraph_vector_ptr_size(cliques); - igraph_vector_int_init(&order, nocliques); + nocliques = igraph_vector_int_list_size(cliques); + IGRAPH_CHECK(igraph_vector_int_init_range(&order, 0, 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_qsort_r(VECTOR(order), nocliques, sizeof(VECTOR(order)[0]), &sortdata, igraph_i_graphlets_order_cmp); - igraph_vector_ptr_index_int(cliques, &order); - igraph_vector_index_int(Mu, &order); + IGRAPH_CHECK(igraph_vector_int_list_permute(cliques, &order)); + IGRAPH_CHECK(igraph_vector_index_int(Mu, &order)); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/cliques/maximal_cliques.c b/src/vendor/cigraph/src/cliques/maximal_cliques.c index d4a3dd36038..3e39528a6dc 100644 --- a/src/vendor/cigraph/src/cliques/maximal_cliques.c +++ b/src/vendor/cigraph/src/cliques/maximal_cliques.c @@ -36,107 +36,108 @@ #define CONCAT2(a,b) CONCAT2x(a,b) #define FUNCTION(name,sfx) CONCAT2(name,sfx) -static int igraph_i_maximal_cliques_reorder_adjlists( +static igraph_error_t igraph_i_maximal_cliques_reorder_adjlists( const igraph_vector_int_t *PX, - int PS, int PE, int XS, int XE, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, const igraph_vector_int_t *pos, igraph_adjlist_t *adjlist); -static int igraph_i_maximal_cliques_select_pivot( +static igraph_error_t igraph_i_maximal_cliques_select_pivot( const igraph_vector_int_t *PX, - int PS, int PE, int XS, int XE, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, - int *pivot, + igraph_integer_t *pivot, igraph_vector_int_t *nextv, - int oldPS, int oldXE); + igraph_integer_t oldPS, igraph_integer_t oldXE); -static int igraph_i_maximal_cliques_down( +static igraph_error_t igraph_i_maximal_cliques_down( igraph_vector_int_t *PX, - int PS, int PE, int XS, int XE, + igraph_integer_t PS, igraph_integer_t PE, igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, - igraph_adjlist_t *adjlist, int mynextv, + igraph_adjlist_t *adjlist, igraph_integer_t mynextv, igraph_vector_int_t *R, - int *newPS, int *newXE); + igraph_integer_t *newPS, igraph_integer_t *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, +static igraph_error_t igraph_i_maximal_cliques_PX( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t *PE, + igraph_integer_t *XS, igraph_integer_t XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, igraph_integer_t 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, +static igraph_error_t igraph_i_maximal_cliques_up( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t 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; + igraph_integer_t tmp = *avnei; *avnei = *pp; *pp = tmp; } @@ -148,37 +149,39 @@ static int igraph_i_maximal_cliques_reorder_adjlists( return IGRAPH_SUCCESS; } -static int igraph_i_maximal_cliques_select_pivot( +static igraph_error_t igraph_i_maximal_cliques_select_pivot( const igraph_vector_int_t *PX, - int PS, int PE, int XS, int XE, + igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, const igraph_vector_int_t *pos, const igraph_adjlist_t *adjlist, - int *pivot, + igraph_integer_t *pivot, igraph_vector_int_t *nextv, - int oldPS, int oldXE) { + igraph_integer_t oldPS, igraph_integer_t 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_integer_t j, pivotvectlen; + igraph_integer_t i, usize = -1; + igraph_integer_t 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_integer_t 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; + igraph_integer_t *avp = VECTOR(*avneis); + igraph_integer_t avlen = igraph_vector_int_size(avneis); + igraph_integer_t *ave = avp + avlen; + igraph_integer_t *avnei = avp, *pp = avp; for (; avnei < ave; avnei++) { - int avneipos = VECTOR(*pos)[(int)(*avnei)]; + igraph_integer_t avneipos = VECTOR(*pos)[(*avnei)]; if (avneipos < soldPS || avneipos > soldXE) { break; } if (avneipos >= sPS && avneipos <= sPE) { if (pp != avnei) { - int tmp = *avnei; + igraph_integer_t tmp = *avnei; *avnei = *pp; *pp = tmp; } @@ -196,12 +199,12 @@ static int igraph_i_maximal_cliques_select_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; + igraph_integer_t vcand = VECTOR(*PX)[j]; + igraph_bool_t nei = false; + igraph_integer_t k = 0; for (k = 0; k < pivotvectlen; k++) { - int unv = VECTOR(*pivotvectneis)[k]; - int unvpos = VECTOR(*pos)[unv]; + igraph_integer_t unv = VECTOR(*pivotvectneis)[k]; + igraph_integer_t unvpos = VECTOR(*pos)[unv]; if (unvpos < sPS || unvpos > sPE) { break; } @@ -218,30 +221,31 @@ static int igraph_i_maximal_cliques_select_pivot( 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; \ +#define SWAP(p1,p2) do { \ + igraph_integer_t v1=VECTOR(*PX)[p1]; \ + igraph_integer_t 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, +static igraph_error_t igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, - igraph_adjlist_t *adjlist, int mynextv, + igraph_adjlist_t *adjlist, igraph_integer_t mynextv, igraph_vector_int_t *R, - int *newPS, int *newXE) { + igraph_integer_t *newPS, igraph_integer_t *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; + igraph_integer_t j, vneislen = igraph_vector_int_size(vneis); + igraph_integer_t 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]; + igraph_integer_t vnei = VECTOR(*vneis)[j]; + igraph_integer_t vneipos = VECTOR(*pos)[vnei]; if (vneipos >= sPS && vneipos <= sPE) { (*newPS)--; SWAP(vneipos - 1, *newPS); @@ -258,16 +262,16 @@ static int igraph_i_maximal_cliques_down(igraph_vector_int_t *PX, #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) { +static igraph_error_t igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, + igraph_integer_t PS, igraph_integer_t *PE, igraph_integer_t *XS, igraph_integer_t XE, + igraph_vector_int_t *pos, igraph_adjlist_t *adjlist, igraph_integer_t v, + igraph_vector_int_t *H +) { - int vpos = VECTOR(*pos)[v] - 1; - int tmp = VECTOR(*PX)[*PE]; + igraph_integer_t vpos = VECTOR(*pos)[v] - 1; + igraph_integer_t tmp = VECTOR(*PX)[*PE]; IGRAPH_UNUSED(PS); - IGRAPH_UNUSED(PE); IGRAPH_UNUSED(XE); IGRAPH_UNUSED(adjlist); @@ -281,12 +285,14 @@ static int igraph_i_maximal_cliques_PX(igraph_vector_int_t *PX, int PS, int *PE, 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; +static igraph_error_t igraph_i_maximal_cliques_up( + igraph_vector_int_t *PX, igraph_integer_t PS, igraph_integer_t PE, + igraph_integer_t XS, igraph_integer_t XE, igraph_vector_int_t *pos, + igraph_adjlist_t *adjlist, + igraph_vector_int_t *R, + igraph_vector_int_t *H +) { + igraph_integer_t vv; IGRAPH_UNUSED(PS); IGRAPH_UNUSED(PE); @@ -296,8 +302,8 @@ static int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, 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]; + igraph_integer_t vvpos = VECTOR(*pos)[vv]; + igraph_integer_t tmp = VECTOR(*PX)[XS]; VECTOR(*PX)[XS] = vv; VECTOR(*PX)[vvpos - 1] = tmp; VECTOR(*pos)[vv] = XS + 1; @@ -305,7 +311,7 @@ static int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, PE++; XS++; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -334,7 +340,7 @@ static int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, * * \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 + * here, i.e. \p res will contain pointers to \ref igraph_vector_int_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 @@ -354,10 +360,10 @@ static int igraph_i_maximal_cliques_up(igraph_vector_int_t *PX, int PS, int PE, * \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); +igraph_error_t igraph_maximal_cliques( + const igraph_t *graph, igraph_vector_int_list_t *res, + igraph_integer_t min_size, igraph_integer_t max_size +); #define IGRAPH_MC_ORIG #include "maximal_cliques_template.h" @@ -365,9 +371,8 @@ int igraph_maximal_cliques(const igraph_t *graph, /** * \function igraph_maximal_cliques_count - * Count the number of maximal cliques in a graph + * \brief 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 @@ -391,7 +396,7 @@ int igraph_maximal_cliques(const igraph_t *graph, * \example examples/simple/igraph_maximal_cliques.c */ -int igraph_maximal_cliques_count(const igraph_t *graph, +igraph_error_t igraph_maximal_cliques_count(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t min_size, igraph_integer_t max_size); @@ -427,7 +432,7 @@ int igraph_maximal_cliques_count(const igraph_t *graph, * */ -int igraph_maximal_cliques_file(const igraph_t *graph, +igraph_error_t igraph_maximal_cliques_file(const igraph_t *graph, FILE *outfile, igraph_integer_t min_size, igraph_integer_t max_size); @@ -438,21 +443,18 @@ int igraph_maximal_cliques_file(const igraph_t *graph, /** * \function igraph_maximal_cliques_subset - * Maximal cliques for a subset of initial vertices + * \brief 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 + * \param res Pointer to a list of integer vectors; the cliques will be * stored here * \param no Pointer to an \c igraph_integer_t; the number of maximal * cliques will be stored here. @@ -471,13 +473,11 @@ int igraph_maximal_cliques_file(const igraph_t *graph, * */ -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); +igraph_error_t igraph_maximal_cliques_subset( + const igraph_t *graph, const igraph_vector_int_t *subset, + igraph_vector_int_list_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" @@ -490,16 +490,14 @@ int igraph_maximal_cliques_subset(const igraph_t *graph, * * 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(). + * callback function as a pointer to an \ref igraph_vector_int_t. The vector is + * owned by the maximal clique search routine so users are expected to make a + * copy of the vector using \ref igraph_vector_int_init_copy() if they want to + * hold on to it. * * - * * 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. @@ -517,7 +515,7 @@ int igraph_maximal_cliques_subset(const igraph_t *graph, * */ -int igraph_maximal_cliques_callback(const igraph_t *graph, +igraph_error_t 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); @@ -557,7 +555,7 @@ int igraph_maximal_cliques_callback(const igraph_t *graph, * */ -int igraph_maximal_cliques_hist(const igraph_t *graph, +igraph_error_t igraph_maximal_cliques_hist(const igraph_t *graph, igraph_vector_t *hist, igraph_integer_t min_size, igraph_integer_t max_size); diff --git a/src/vendor/cigraph/src/cliques/maximal_cliques_template.h b/src/vendor/cigraph/src/cliques/maximal_cliques_template.h index e3f3da7b53c..be0824bc258 100644 --- a/src/vendor/cigraph/src/cliques/maximal_cliques_template.h +++ b/src/vendor/cigraph/src/cliques/maximal_cliques_template.h @@ -22,24 +22,16 @@ */ #ifdef IGRAPH_MC_ORIG -#define RESTYPE igraph_vector_ptr_t *res +#define RESTYPE igraph_vector_int_list_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; \ + igraph_integer_t hcapacity = igraph_vector_capacity(hist); \ + igraph_integer_t j; \ + igraph_error_t 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); \ + IGRAPH_ERROR("Cannot count maximal cliques", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ for (j=hsize; j < clsize; j++) \ VECTOR(*hist)[j] = 0; \ } \ VECTOR(*hist)[clsize-1] += 1; \ } while (0) -#define FINALLY \ +#define PREPARE \ igraph_vector_clear(hist); \ - igraph_vector_reserve(hist, 50); /* initially reserve space for 50 elements */ + IGRAPH_CHECK(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); + igraph_integer_t 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_integer_t 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; + igraph_integer_t newPS, newXE; /* Going down, prepare */ IGRAPH_CHECK(igraph_i_maximal_cliques_down( @@ -244,10 +194,10 @@ static int FUNCTION(igraph_i_maximal_cliques_bk, SUFFIX)( /* 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; + return IGRAPH_SUCCESS; } -int FUNCTION(igraph_maximal_cliques, SUFFIX)( +igraph_error_t FUNCTION(igraph_maximal_cliques, SUFFIX)( const igraph_t *graph, RESTYPE, igraph_integer_t min_size, @@ -256,12 +206,13 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( /* Implementation details. TODO */ igraph_vector_int_t PX, R, H, pos, nextv; - igraph_vector_t coreness, order; + igraph_vector_int_t coreness; + igraph_vector_int_t order; igraph_vector_int_t rank; /* TODO: this is not needed */ - int i, ii, nn, no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_error_t err; IGRAPH_UNUSED(nn); if (igraph_is_directed(graph)) { @@ -269,40 +220,40 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( "calculation"); } - IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&coreness, no_of_nodes); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&coreness, &order, IGRAPH_ASCENDING)); for (ii = 0; ii < no_of_nodes; ii++) { - int v = VECTOR(order)[ii]; + igraph_integer_t v = VECTOR(order)[ii]; VECTOR(rank)[v] = ii; } - igraph_vector_destroy(&coreness); + igraph_vector_int_destroy(&coreness); IGRAPH_FINALLY_CLEAN(1); - igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + IGRAPH_CHECK(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_CHECK(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(&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; + PREPARE; FOR_LOOP_OVER_VERTICES { - int v; - int vrank; + igraph_integer_t v; + igraph_integer_t vrank; igraph_vector_int_t *vneis; - int vdeg; - int Pptr, Xptr, PS, PE, XS, XE; - int j; + igraph_integer_t vdeg; + igraph_integer_t Pptr, Xptr, PS, PE, XS, XE; + igraph_integer_t j; FOR_LOOP_OVER_VERTICES_PREPARE; @@ -335,7 +286,7 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( VECTOR(R)[0] = v; for (j = 0; j < vdeg; j++) { - int vx = VECTOR(*vneis)[j]; + igraph_integer_t vx = VECTOR(*vneis)[j]; if (VECTOR(rank)[vx] > vrank) { VECTOR(PX)[Pptr] = vx; VECTOR(pos)[vx] = Pptr + 1; @@ -357,14 +308,14 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( igraph_adjlist_get(&fulladjlist, v) )); for (j = 0; j <= vdeg - 1; j++) { - int vv = VECTOR(PX)[j]; + igraph_integer_t 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_integer_t 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; + igraph_integer_t nei = VECTOR(*fadj)[k]; + igraph_integer_t neipos = VECTOR(pos)[nei] - 1; if (neipos >= PS && neipos <= XE) { IGRAPH_CHECK(igraph_vector_int_push_back(radj, nei)); } @@ -399,7 +350,7 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( igraph_adjlist_destroy(&fulladjlist); igraph_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&rank); - igraph_vector_destroy(&order); + igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; @@ -409,7 +360,7 @@ int FUNCTION(igraph_maximal_cliques, SUFFIX)( #undef RESNAME #undef SUFFIX #undef RECORD -#undef FINALLY +#undef PREPARE #undef CLEANUP #undef FOR_LOOP_OVER_VERTICES #undef FOR_LOOP_OVER_VERTICES_PREPARE diff --git a/src/vendor/cigraph/src/community/community_misc.c b/src/vendor/cigraph/src/community/community_misc.c index a7fb2e7ef4c..77850b7fcb0 100644 --- a/src/vendor/cigraph/src/community/community_misc.c +++ b/src/vendor/cigraph/src/community/community_misc.c @@ -22,39 +22,21 @@ */ #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 "igraph_sparsemat.h" #include #include /** * \function igraph_community_to_membership - * \brief Create membership vector from community structure dendrogram + * \brief Creates a membership vector from a 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. + * from zero, see the same argument of \ref igraph_connected_components() + * for an example of a membership vector. * * * Many community detection algorithms return with a \em merges @@ -74,6 +56,7 @@ * 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. @@ -93,28 +76,28 @@ * * Time complexity: O(|V|), the number of vertices in the graph. */ -int igraph_community_to_membership(const igraph_matrix_t *merges, +igraph_error_t igraph_community_to_membership(const igraph_matrix_int_t *merges, igraph_integer_t nodes, igraph_integer_t steps, - igraph_vector_t *membership, - igraph_vector_t *csize) { + igraph_vector_int_t *membership, + igraph_vector_int_t *csize) { - long int no_of_nodes = nodes; - long int components = no_of_nodes - steps; - long int i, found = 0; + igraph_integer_t no_of_nodes = nodes; + igraph_integer_t components = no_of_nodes - steps; + igraph_integer_t 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; + igraph_vector_int_t own_membership; + igraph_bool_t using_own_membership = false; - if (steps > igraph_matrix_nrow(merges)) { + if (steps > igraph_matrix_int_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)); + IGRAPH_PRId " steps, %" IGRAPH_PRId " rows.", IGRAPH_EINVAL, steps, igraph_matrix_int_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 (igraph_matrix_int_ncol(merges) != 2) { + IGRAPH_ERRORF("The merges matrix should have two columns, but has %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_matrix_int_ncol(merges)); } if (steps < 0) { IGRAPH_ERRORF("Number of steps should be non-negative, found %" IGRAPH_PRId ".", IGRAPH_EINVAL, steps); @@ -123,36 +106,36 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, 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); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); } if (csize) { - IGRAPH_CHECK(igraph_vector_resize(csize, components)); - igraph_vector_null(csize); + IGRAPH_CHECK(igraph_vector_int_resize(csize, components)); + igraph_vector_int_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); + igraph_integer_t c1 = MATRIX(*merges, i, 0); + igraph_integer_t c2 = 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); + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %" IGRAPH_PRId ".", 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); + IGRAPH_ERRORF("Merges matrix contains multiple merges of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c2); } /* new component? */ @@ -162,7 +145,7 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, } if (c1 < no_of_nodes) { - long int cid = (long int) VECTOR(tmp)[i] - 1; + igraph_integer_t cid = VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c1] = cid + 1; } @@ -174,7 +157,7 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, } if (c2 < no_of_nodes) { - long int cid = (long int) VECTOR(tmp)[i] - 1; + igraph_integer_t cid = VECTOR(tmp)[i] - 1; if (membership) { VECTOR(*membership)[c2] = cid + 1; } @@ -191,7 +174,7 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, /* 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]; + igraph_integer_t tmp = VECTOR(*membership)[i]; if (tmp != 0) { if (membership) { VECTOR(*membership)[i] = tmp - 1; @@ -213,16 +196,16 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, IGRAPH_FINALLY_CLEAN(2); if (using_own_membership) { - igraph_vector_destroy(&own_membership); + igraph_vector_int_destroy(&own_membership); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_reindex_membership - * \brief Makes the IDs in a membership vector continuous + * \brief Makes the IDs in a membership vector contiguous. * * 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, @@ -234,21 +217,21 @@ int igraph_community_to_membership(const igraph_matrix_t *merges, * 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, + * old component ID for each new one, or \c 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 + * distinct clusters. If not \c 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_error_t igraph_reindex_membership(igraph_vector_int_t *membership, + igraph_vector_int_t *new_to_old, igraph_integer_t *nb_clusters) { - long int i, n = igraph_vector_size(membership); + igraph_integer_t i, n = igraph_vector_int_size(membership); igraph_vector_t new_cluster; igraph_integer_t i_nb_clusters; @@ -257,37 +240,42 @@ int igraph_reindex_membership(igraph_vector_t *membership, IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster); if (new_to_old) { - igraph_vector_clear(new_to_old); + igraph_vector_int_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]; + igraph_integer_t c = VECTOR(*membership)[i]; + + if (c < 0) { + IGRAPH_ERRORF("Membership indices should non-negative. " + "Found member of cluster %" IGRAPH_PRId ".", IGRAPH_EINVAL, c); + } if (c < 0) { IGRAPH_ERRORF("Membership indices should be non-negative. " - "Found member of cluster %ld.", IGRAPH_EINVAL, c); + "Found member of cluster %" IGRAPH_PRId ".", 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); + "Found member of cluster %" IGRAPH_PRId ", but only %" IGRAPH_PRId " 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(new_to_old, c)); } } } /* Assign new membership */ for (i = 0; i < n; i++) { - long int c = (long int)VECTOR(*membership)[i]; + igraph_integer_t c = VECTOR(*membership)[i]; VECTOR(*membership)[i] = VECTOR(new_cluster)[c] - 1; } if (nb_clusters) { @@ -302,14 +290,14 @@ int igraph_reindex_membership(igraph_vector_t *membership, 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, +static igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result); +static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result); +static igraph_error_t igraph_i_compare_communities_rand(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_real_t* result, igraph_bool_t adjust); +static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, + const igraph_vector_int_t *v2, igraph_integer_t* distance12, igraph_integer_t* distance21); /** @@ -435,24 +423,24 @@ static int igraph_i_split_join_distance(const igraph_vector_t *v1, * * 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_error_t igraph_compare_communities(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_real_t* result, igraph_community_comparison_t method) { - igraph_vector_t c1, c2; + igraph_vector_int_t c1, c2; - if (igraph_vector_size(comm1) != igraph_vector_size(comm2)) { + if (igraph_vector_int_size(comm1) != igraph_vector_int_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_int_init_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); - IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); - IGRAPH_FINALLY(igraph_vector_destroy, &c2); + IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); - IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL)); - IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, NULL)); switch (method) { case IGRAPH_COMMCMP_VI: @@ -481,17 +469,17 @@ int igraph_compare_communities(const igraph_vector_t *comm1, } /* Clean up everything */ - igraph_vector_destroy(&c1); - igraph_vector_destroy(&c2); + igraph_vector_int_destroy(&c1); + igraph_vector_int_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup communities * \function igraph_split_join_distance - * \brief Calculates the split-join distance of two community structures + * \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 @@ -538,34 +526,34 @@ int igraph_compare_communities(const igraph_vector_t *comm1, * * 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_error_t igraph_split_join_distance(const igraph_vector_int_t *comm1, + const igraph_vector_int_t *comm2, igraph_integer_t *distance12, igraph_integer_t *distance21) { - igraph_vector_t c1, c2; + igraph_vector_int_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)); + if (igraph_vector_int_size(comm1) != igraph_vector_int_size(comm2)) { + IGRAPH_ERRORF("Community membership vectors have different lengths: %" IGRAPH_PRId " and %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_size(comm1), igraph_vector_int_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_int_init_copy(&c1, comm1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c1); - IGRAPH_CHECK(igraph_vector_copy(&c2, comm2)); - IGRAPH_FINALLY(igraph_vector_destroy, &c2); + IGRAPH_CHECK(igraph_vector_int_init_copy(&c2, comm2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c2); - IGRAPH_CHECK(igraph_reindex_membership(&c1, 0, NULL)); - IGRAPH_CHECK(igraph_reindex_membership(&c2, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c1, NULL, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(&c2, NULL, NULL)); IGRAPH_CHECK(igraph_i_split_join_distance(&c1, &c2, distance12, distance21)); /* Clean up everything */ - igraph_vector_destroy(&c1); - igraph_vector_destroy(&c2); + igraph_vector_int_destroy(&c1); + igraph_vector_int_destroy(&c2); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** @@ -573,24 +561,25 @@ int igraph_split_join_distance(const igraph_vector_t *comm1, * 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; +static igraph_error_t igraph_i_entropy_and_mutual_information(const igraph_vector_int_t* v1, + const igraph_vector_int_t* v2, double* h1, double* h2, double* mut_inf) { + igraph_integer_t i, n; + igraph_integer_t k1; + igraph_integer_t k2; double *p1, *p2; - igraph_spmatrix_t m; - igraph_spmatrix_iter_t mit; + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; - n = igraph_vector_size(v1); + n = igraph_vector_int_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; + k1 = igraph_vector_int_max(v1) + 1; + k2 = igraph_vector_int_max(v2) + 1; p1 = IGRAPH_CALLOC(k1, double); if (p1 == 0) { IGRAPH_ERROR("Insufficient memory for computing community entropy.", IGRAPH_ENOMEM); @@ -605,7 +594,7 @@ static int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, /* Calculate the entropy of v1 */ *h1 = 0.0; for (i = 0; i < n; i++) { - p1[(long int)VECTOR(*v1)[i]]++; + p1[VECTOR(*v1)[i]]++; } for (i = 0; i < k1; i++) { p1[i] /= n; @@ -615,7 +604,7 @@ static int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, /* Calculate the entropy of v2 */ *h2 = 0.0; for (i = 0; i < n; i++) { - p2[(long int)VECTOR(*v2)[i]]++; + p2[VECTOR(*v2)[i]]++; } for (i = 0; i < k2; i++) { p2[i] /= n; @@ -632,27 +621,32 @@ static int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, /* 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); + IGRAPH_CHECK(igraph_sparsemat_init(&mu, k1, k2, n)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); 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_CHECK(igraph_sparsemat_entry( + &mu, VECTOR(*v1)[i], + VECTOR(*v2)[i], 1 + )); } - igraph_spmatrix_iter_destroy(&mit); - igraph_spmatrix_destroy(&m); + IGRAPH_CHECK(igraph_sparsemat_compress(&mu, &m)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + double p = igraph_sparsemat_iterator_get(&mit)/ n; + *mut_inf += p * (log(p) - p1[igraph_sparsemat_iterator_row(&mit)] - p2[igraph_sparsemat_iterator_col(&mit)]); + igraph_sparsemat_iterator_next(&mit); + } + igraph_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); IGRAPH_FREE(p1); IGRAPH_FREE(p2); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /** @@ -667,7 +661,7 @@ static int igraph_i_entropy_and_mutual_information(const igraph_vector_t* v1, * * Time complexity: O(n log(n)) */ -static int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, const igraph_vector_t *v2, +static igraph_error_t igraph_i_compare_communities_nmi(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, igraph_real_t* result) { double h1, h2, mut_inf; @@ -697,7 +691,7 @@ static int igraph_i_compare_communities_nmi(const igraph_vector_t *v1, const igr * * Time complexity: O(n log(n)) */ -static int igraph_i_compare_communities_vi(const igraph_vector_t *v1, const igraph_vector_t *v2, +static igraph_error_t igraph_i_compare_communities_vi(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, igraph_real_t* result) { double h1, h2, mut_inf; @@ -718,23 +712,23 @@ static int igraph_i_compare_communities_vi(const igraph_vector_t *v1, const igra * 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)); +static igraph_error_t igraph_i_confusion_matrix(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_sparsemat_t *m) { + igraph_integer_t k1, k2, i, n; + + n = igraph_vector_int_size(v1); + if (n == 0) { + IGRAPH_CHECK(igraph_sparsemat_resize(m, 0, 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)); + + k1 = igraph_vector_int_max(v1) + 1; + k2 = igraph_vector_int_max(v2) + 1; + IGRAPH_CHECK(igraph_sparsemat_resize(m, k1, k2, n)); 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_sparsemat_entry( + m, VECTOR(*v1)[i], VECTOR(*v2)[i], 1 + )); } return IGRAPH_SUCCESS; @@ -756,12 +750,13 @@ static int igraph_i_confusion_matrix(const igraph_vector_t *v1, const igraph_vec * 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, +static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, igraph_integer_t* distance12, igraph_integer_t* distance21) { - long int n = igraph_vector_size(v1); + igraph_integer_t n = igraph_vector_int_size(v1); igraph_vector_t rowmax, colmax; - igraph_spmatrix_t m; - igraph_spmatrix_iter_t mit; + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; if (n == 0) { *distance12 = 0; @@ -769,28 +764,31 @@ static int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_ 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)); + IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); /* 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)); + IGRAPH_VECTOR_INIT_FINALLY(&rowmax, igraph_sparsemat_nrow(&mu)); + IGRAPH_VECTOR_INIT_FINALLY(&colmax, igraph_sparsemat_ncol(&mu)); /* 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; + igraph_sparsemat_compress(&mu, &m); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + igraph_real_t value = igraph_sparsemat_iterator_get(&mit); + igraph_integer_t row = igraph_sparsemat_iterator_row(&mit); + igraph_integer_t col = igraph_sparsemat_iterator_col(&mit); + if (value > VECTOR(rowmax)[row]) { + VECTOR(rowmax)[row] = value; } - if (mit.value > VECTOR(colmax)[mit.ci]) { - VECTOR(colmax)[mit.ci] = mit.value; + if (value > VECTOR(colmax)[col]) { + VECTOR(colmax)[col] = value; } - igraph_spmatrix_iter_next(&mit); + igraph_sparsemat_iterator_next(&mit); } - igraph_spmatrix_iter_destroy(&mit); - IGRAPH_FINALLY_CLEAN(1); /* Calculate the distances */ *distance12 = (igraph_integer_t) (n - igraph_vector_sum(&rowmax)); @@ -798,8 +796,9 @@ static int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_ igraph_vector_destroy(&rowmax); igraph_vector_destroy(&colmax); - igraph_spmatrix_destroy(&m); - IGRAPH_FINALLY_CLEAN(3); + igraph_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); + IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; } @@ -826,25 +825,26 @@ static int igraph_i_split_join_distance(const igraph_vector_t *v1, const igraph_ * 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, +static igraph_error_t igraph_i_compare_communities_rand( + const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, igraph_real_t *result, igraph_bool_t adjust) { - igraph_spmatrix_t m; - igraph_spmatrix_iter_t mit; + igraph_sparsemat_t m; + igraph_sparsemat_t mu; /* uncompressed */ + igraph_sparsemat_iterator_t mit; igraph_vector_t rowsums, colsums; - long int i, nrow, ncol; + igraph_integer_t i, nrow, ncol; double rand, n; double frac_pairs_in_1, frac_pairs_in_2; - if (igraph_vector_size(v1) <= 1) { + if (igraph_vector_int_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)); + "Found membership vector of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, igraph_vector_int_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)); + IGRAPH_CHECK(igraph_sparsemat_init(&mu, 1, 1, 0)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &mu); + IGRAPH_CHECK(igraph_i_confusion_matrix(v1, v2, &mu)); /* The unadjusted Rand index is defined as (a+d) / (a+b+c+d), where: * @@ -876,24 +876,26 @@ static int igraph_i_compare_communities_rand( */ /* Calculate row and column sums */ - nrow = igraph_spmatrix_nrow(&m); - ncol = igraph_spmatrix_ncol(&m); - n = igraph_vector_size(v1) + 0.0; + nrow = igraph_sparsemat_nrow(&mu); + ncol = igraph_sparsemat_ncol(&mu); + n = igraph_vector_int_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)); + IGRAPH_CHECK(igraph_sparsemat_rowsums(&mu, &rowsums)); + IGRAPH_CHECK(igraph_sparsemat_colsums(&mu, &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_sparsemat_compress(&mu, &m); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &m); + IGRAPH_CHECK(igraph_sparsemat_dupl(&m)); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&mit, &m)); + while (!igraph_sparsemat_iterator_end(&mit)) { + igraph_real_t value = igraph_sparsemat_iterator_get(&mit); + rand += (value / n) * (value - 1) / (n - 1); + igraph_sparsemat_iterator_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++) { @@ -913,8 +915,9 @@ static int igraph_i_compare_communities_rand( igraph_vector_destroy(&rowsums); igraph_vector_destroy(&colsums); - igraph_spmatrix_destroy(&m); - IGRAPH_FINALLY_CLEAN(3); + igraph_sparsemat_destroy(&m); + igraph_sparsemat_destroy(&mu); + IGRAPH_FINALLY_CLEAN(4); *result = rand; diff --git a/src/vendor/cigraph/src/community/edge_betweenness.c b/src/vendor/cigraph/src/community/edge_betweenness.c index 750c183975a..a69bd9e77da 100644 --- a/src/vendor/cigraph/src/community/edge_betweenness.c +++ b/src/vendor/cigraph/src/community/edge_betweenness.c @@ -28,6 +28,7 @@ #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" +#include "igraph_nongraph.h" #include "igraph_progress.h" #include "igraph_stack.h" @@ -36,16 +37,15 @@ #include -static int igraph_i_rewrite_membership_vector(igraph_vector_t *membership) { - long int no = (long int) igraph_vector_max(membership) + 1; +static igraph_error_t igraph_i_rewrite_membership_vector(igraph_vector_int_t *membership) { + const igraph_integer_t no = igraph_vector_int_max(membership) + 1; igraph_vector_t idx; - long int realno = 0; - long int i; - long int len = igraph_vector_size(membership); + igraph_integer_t realno = 0; + const igraph_integer_t len = igraph_vector_int_size(membership); IGRAPH_VECTOR_INIT_FINALLY(&idx, no); - for (i = 0; i < len; i++) { - long int t = (long int) VECTOR(*membership)[i]; + for (igraph_integer_t i = 0; i < len; i++) { + const igraph_integer_t t = VECTOR(*membership)[i]; if (VECTOR(idx)[t]) { VECTOR(*membership)[i] = VECTOR(idx)[t] - 1; } else { @@ -56,55 +56,51 @@ static int igraph_i_rewrite_membership_vector(igraph_vector_t *membership) { igraph_vector_destroy(&idx); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_community_eb_get_merges2(const igraph_t *graph, +static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_t *edges, + const igraph_vector_int_t *edges, const igraph_vector_t *weights, - igraph_matrix_t *res, - igraph_vector_t *bridges, + igraph_matrix_int_t *res, + igraph_vector_int_t *bridges, igraph_vector_t *modularity, - igraph_vector_t *membership) { + igraph_vector_int_t *membership) { - igraph_vector_t mymembership; - long int no_of_nodes = igraph_vcount(graph); - long int i; + igraph_vector_int_t mymembership; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t maxmod = -1; - long int midx = 0; + igraph_integer_t 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); + const igraph_bool_t use_directed = directed && igraph_is_directed(graph); + igraph_integer_t max_merges; if (membership) { - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); } - if (modularity || res || bridges) { - IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, - IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); + max_merges = no_of_nodes - no_comps; if (modularity) { IGRAPH_CHECK(igraph_vector_resize(modularity, - no_of_nodes - no_comps + 1)); + max_merges + 1)); } if (res) { - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, + IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, 2)); } if (bridges) { - IGRAPH_CHECK(igraph_vector_resize(bridges, - no_of_nodes - no_comps)); + IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); } } - for (i = 0; i < no_of_nodes; i++) { - VECTOR(mymembership)[i] = i; - } + IGRAPH_CHECK(igraph_vector_int_init_range(&mymembership, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &mymembership); + if (membership) { - igraph_vector_update(membership, &mymembership); + IGRAPH_CHECK(igraph_vector_int_update(membership, &mymembership)); } IGRAPH_CHECK(igraph_modularity(graph, &mymembership, weights, @@ -114,25 +110,25 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, 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]; + for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_integer_t c1 = VECTOR(mymembership)[from]; + igraph_integer_t c2 = 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; + VECTOR(*bridges)[midx] = i; } /* The new cluster has id no_of_nodes+midx+1 */ - for (j = 0; j < no_of_nodes; j++) { + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { if (VECTOR(mymembership)[j] == c1 || VECTOR(mymembership)[j] == c2) { VECTOR(mymembership)[j] = no_of_nodes + midx; @@ -147,7 +143,7 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, if (actmod > maxmod) { maxmod = actmod; if (membership) { - igraph_vector_update(membership, &mymembership); + IGRAPH_CHECK(igraph_vector_int_update(membership, &mymembership)); } } } @@ -160,10 +156,10 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, IGRAPH_CHECK(igraph_i_rewrite_membership_vector(membership)); } - igraph_vector_destroy(&mymembership); + igraph_vector_int_destroy(&mymembership); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } @@ -171,14 +167,15 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, * \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. + * dendrogram, via its \p merges argument. Merges happen when the + * edge removal process is run backwards and two components become + * connected. * * \param graph The input graph. * \param edges Vector containing the edges to be removed from the @@ -191,20 +188,21 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, * 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 + * 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. + * component n+1, etc. The matrix will be resized as needed. + * \param bridges Pointer to an initialized vector of \c NULL. If not + * \c NULL then the indices into \p edges of all edges which caused + * one of the merges will be put here. This is equal to all edge removals + * which separated the network into more components, in reverse order. * \param modularity If not a null pointer, then the modularity values * for the different divisions, corresponding to the merges matrix, * will be stored here. @@ -218,34 +216,46 @@ static int igraph_i_community_eb_get_merges2(const igraph_t *graph, * 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, +igraph_error_t igraph_community_eb_get_merges(const igraph_t *graph, const igraph_bool_t directed, - const igraph_vector_t *edges, + const igraph_vector_int_t *edges, const igraph_vector_t *weights, - igraph_matrix_t *res, - igraph_vector_t *bridges, + igraph_matrix_int_t *res, + igraph_vector_int_t *bridges, igraph_vector_t *modularity, - igraph_vector_t *membership) { + igraph_vector_int_t *membership) { - long int no_of_nodes = igraph_vcount(graph); - igraph_vector_t ptr; - long int i, midx = 0; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t ptr; + igraph_integer_t midx = 0; igraph_integer_t no_comps; + const igraph_integer_t no_removed_edges = igraph_vector_int_size(edges); + igraph_integer_t max_merges; + + if (! igraph_vector_int_isininterval(edges, 0, no_of_edges-1)) { + IGRAPH_ERROR("Invalid edge ID.", IGRAPH_EINVAL); + } + if (no_removed_edges < no_of_edges) { + IGRAPH_ERRORF("Number of removed edges (%" IGRAPH_PRId ") should be equal to " + "number of edges in graph (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + no_removed_edges, no_of_edges); + } /* catch null graph early */ if (no_of_nodes == 0) { if (res) { - igraph_matrix_resize(res, 0, 2); + IGRAPH_CHECK(igraph_matrix_int_resize(res, 0, 2)); } if (bridges) { - igraph_vector_clear(bridges); + igraph_vector_int_clear(bridges); } if (modularity) { - igraph_vector_resize(modularity, 1); + IGRAPH_CHECK(igraph_vector_resize(modularity, 1)); VECTOR(*modularity)[0] = IGRAPH_NAN; } if (membership) { - igraph_vector_clear(membership); + igraph_vector_int_clear(membership); } return IGRAPH_SUCCESS; } @@ -257,28 +267,29 @@ int igraph_community_eb_get_merges(const igraph_t *graph, res, bridges, modularity, membership); } - IGRAPH_CHECK(igraph_clusters(graph, 0, 0, &no_comps, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_comps, IGRAPH_WEAK)); - IGRAPH_VECTOR_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); + max_merges = no_of_nodes - no_comps; + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, no_of_nodes * 2 - 1); if (res) { - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes - no_comps, 2)); + IGRAPH_CHECK(igraph_matrix_int_resize(res, max_merges, 2)); } if (bridges) { - IGRAPH_CHECK(igraph_vector_resize(bridges, no_of_nodes - no_comps)); + IGRAPH_CHECK(igraph_vector_int_resize(bridges, max_merges)); } - for (i = igraph_vector_size(edges) - 1; i >= 0; i--) { - igraph_integer_t edge = (igraph_integer_t) VECTOR(*edges)[i]; + for (igraph_integer_t i = igraph_vector_int_size(edges) - 1; i >= 0; i--) { + igraph_integer_t edge = VECTOR(*edges)[i]; igraph_integer_t from, to, c1, c2, idx; - igraph_edge(graph, edge, &from, &to); + IGRAPH_CHECK(igraph_edge(graph, edge, &from, &to)); idx = from + 1; while (VECTOR(ptr)[idx - 1] != 0) { - idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; + idx = VECTOR(ptr)[idx - 1]; } c1 = idx - 1; idx = to + 1; while (VECTOR(ptr)[idx - 1] != 0) { - idx = (igraph_integer_t) VECTOR(ptr)[idx - 1]; + idx = VECTOR(ptr)[idx - 1]; } c2 = idx - 1; if (c1 != c2) { /* this is a merge */ @@ -287,7 +298,7 @@ int igraph_community_eb_get_merges(const igraph_t *graph, MATRIX(*res, midx, 1) = c2; } if (bridges) { - VECTOR(*bridges)[midx] = i + 1; + VECTOR(*bridges)[midx] = i; } VECTOR(ptr)[c1] = no_of_nodes + midx + 1; @@ -299,16 +310,16 @@ int igraph_community_eb_get_merges(const igraph_t *graph, } } - igraph_vector_destroy(&ptr); + igraph_vector_int_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); +static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t *v, + const bool *passive) { + igraph_integer_t which, i = 0, size = igraph_vector_size(v); igraph_real_t max; while (passive[i]) { i++; @@ -354,7 +365,7 @@ static long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v, * * \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 + * 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 @@ -364,12 +375,12 @@ static long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v, * 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. + * 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 - * \c NULL then all edge removals which separated the network into - * more components are marked here. + * \c NULL then the indices into \p result of all edges which caused + * one of the \p merges will be put here. This is equivalent to all edge removals + * which separated the network into more components, in reverse order. * \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 @@ -394,49 +405,47 @@ static long int igraph_i_vector_which_max_not_null(const igraph_vector_t *v, * * \example examples/simple/igraph_community_edge_betweenness.c */ -int igraph_community_edge_betweenness(const igraph_t *graph, - igraph_vector_t *result, +igraph_error_t igraph_community_edge_betweenness(const igraph_t *graph, + igraph_vector_int_t *result, igraph_vector_t *edge_betweenness, - igraph_matrix_t *merges, - igraph_vector_t *bridges, + igraph_matrix_int_t *merges, + igraph_vector_int_t *bridges, igraph_vector_t *modularity, - igraph_vector_t *membership, + igraph_vector_int_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); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t 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, elist_in, parents; igraph_inclist_t *elist_out_p, *elist_in_p; igraph_vector_int_t *neip; - long int neino; + igraph_integer_t neino; igraph_vector_t eb; - long int maxedge, pos; + igraph_integer_t maxedge, pos; igraph_integer_t from, to; - igraph_bool_t result_owned = 0; - igraph_stack_t stack = IGRAPH_STACK_NULL; + igraph_bool_t result_owned = false; + igraph_stack_int_t stack; igraph_real_t steps, steps_done; - char *passive; + bool *passive; /* Needed only for the unweighted case */ - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_t q; /* 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); - } + if (result == NULL) { + result = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(result, "Insufficient memory for edge betweenness-based community detection."); IGRAPH_FINALLY(igraph_free, result); - IGRAPH_VECTOR_INIT_FINALLY(result, 0); - result_owned = 1; + + IGRAPH_VECTOR_INT_INIT_FINALLY(result, 0); + result_owned = true; } directed = directed && igraph_is_directed(graph); @@ -454,23 +463,19 @@ int igraph_community_edge_betweenness(const igraph_t *graph, } distance = IGRAPH_CALLOC(no_of_nodes, double); - if (distance == 0) { - IGRAPH_ERROR("Edge betweenness community structure failed.", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(distance, "Insufficient memory for edge betweenness-based community detection."); 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_CHECK_OOM(nrgeo, "Insufficient memory for edge betweenness-based community detection."); 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_CHECK_OOM(tmpscore, "Insufficient memory for edge betweenness-based community detection."); IGRAPH_FINALLY(igraph_free, tmpscore); - if (weights == 0) { - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + if (weights == NULL) { + IGRAPH_DQUEUE_INT_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); @@ -483,17 +488,17 @@ int igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_ERROR("Weights must be strictly positive.", IGRAPH_EINVAL); } - if (igraph_is_nan(minweight)) { + if (isnan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } - if (membership != 0) { + if (membership != NULL) { IGRAPH_WARNING("Membership vector will be selected based on the highest " "modularity score."); } - if (modularity != 0 || membership != 0) { + if (modularity != NULL || membership != NULL) { 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 " @@ -502,15 +507,13 @@ int igraph_community_edge_betweenness(const igraph_t *graph, 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_inclist_init_empty(&parents, no_of_nodes)); + IGRAPH_FINALLY(igraph_inclist_destroy, &parents); } - IGRAPH_CHECK(igraph_stack_init(&stack, no_of_nodes)); - IGRAPH_FINALLY(igraph_stack_destroy, &stack); + IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); - IGRAPH_CHECK(igraph_vector_resize(result, no_of_edges)); + IGRAPH_CHECK(igraph_vector_int_resize(result, no_of_edges)); if (edge_betweenness) { IGRAPH_CHECK(igraph_vector_resize(edge_betweenness, no_of_edges)); if (no_of_edges > 0) { @@ -520,10 +523,8 @@ int igraph_community_edge_betweenness(const igraph_t *graph, 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); - } + passive = IGRAPH_CALLOC(no_of_edges, bool); + IGRAPH_CHECK_OOM(passive, "Insufficient memory for edge betweenness-based community detection."); IGRAPH_FINALLY(igraph_free, passive); /* Estimate the number of steps to be taken. @@ -535,39 +536,39 @@ int igraph_community_edge_betweenness(const igraph_t *graph, 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++) { + for (igraph_integer_t 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) { + if (weights == NULL) { /* Unweighted variant follows */ /* The following for loop is copied almost intact from * igraph_edge_betweenness_cutoff */ - for (source = 0; source < no_of_nodes; source++) { + for (igraph_integer_t 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_stack_int_clear(&stack); /* it should be empty anyway... */ - IGRAPH_CHECK(igraph_dqueue_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); nrgeo[source] = 1; distance[source] = 0; - while (!igraph_dqueue_empty(&q)) { - long int actnode = (long int) igraph_dqueue_pop(&q); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_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); + for (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); if (nrgeo[neighbor] != 0) { /* we've already seen this node, another shortest path? */ if (distance[neighbor] == distance[actnode] + 1) { @@ -577,17 +578,17 @@ int igraph_community_edge_betweenness(const igraph_t *graph, /* 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, neighbor)); } } - } /* while !igraph_dqueue_empty */ + } /* while !igraph_dqueue_int_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); + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t actnode = igraph_stack_int_pop(&stack); if (distance[actnode] < 1) { continue; /* skip source node */ } @@ -595,9 +596,9 @@ int igraph_community_edge_betweenness(const igraph_t *graph, /* 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); + for (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, actnode); if (distance[neighbor] == distance[actnode] - 1 && nrgeo[neighbor] != 0) { tmpscore[neighbor] += @@ -612,9 +613,12 @@ int igraph_community_edge_betweenness(const igraph_t *graph, } else { /* Weighted variant follows */ + const igraph_real_t eps = IGRAPH_SHORTEST_PATH_EPSILON; + int cmp_result; + /* The following for loop is copied almost intact from * igraph_i_edge_betweenness_cutoff_weighted */ - for (source = 0; source < no_of_nodes; source++) { + for (igraph_integer_t source = 0; source < no_of_nodes; source++) { /* This will contain the edge betweenness in the current step */ IGRAPH_ALLOW_INTERRUPTION(); @@ -622,59 +626,64 @@ int igraph_community_edge_betweenness(const igraph_t *graph, 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); + IGRAPH_CHECK(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_integer_t minnei = igraph_2wheap_max_index(&heap); igraph_real_t mindist = -igraph_2wheap_delete_max(&heap); - igraph_stack_push(&stack, minnei); + IGRAPH_CHECK(igraph_stack_int_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); + for (igraph_integer_t i = 0; i < neino; i++) { + igraph_integer_t edge = VECTOR(*neip)[i]; + igraph_integer_t 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; + /* Note: curdist == 0 means infinity, and for this case + * cmp_result should be -1. However, this case is handled + * specially below, without referring to cmp_result. */ + cmp_result = igraph_cmp_epsilon(altdist, curdist - 1, eps); + if (curdist == 0) { /* This is the first finite distance to 'to' */ - v = igraph_inclist_get(&fathers, to); + v = igraph_inclist_get(&parents, 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) { + } else if (cmp_result < 0) { /* This is a shorter path */ - v = igraph_inclist_get(&fathers, to); + v = igraph_inclist_get(&parents, 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) { + igraph_2wheap_modify(&heap, to, -altdist); + } else if (cmp_result == 0) { /* Another path with the same length */ - v = igraph_inclist_get(&fathers, to); - igraph_vector_int_push_back(v, edge); + v = igraph_inclist_get(&parents, to); + IGRAPH_CHECK(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); + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t w = igraph_stack_int_pop(&stack); + igraph_vector_int_t *parv = igraph_inclist_get(&parents, w); + igraph_integer_t parv_len = igraph_vector_int_size(parv); - for (i = 0; i < fatv_len; i++) { - long int fedge = (long int) VECTOR(*fatv)[i]; - long int neighbor = IGRAPH_OTHER(graph, fedge, w); + for (igraph_integer_t i = 0; i < parv_len; i++) { + igraph_integer_t fedge = VECTOR(*parv)[i]; + igraph_integer_t 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]; } @@ -682,7 +691,7 @@ int igraph_community_edge_betweenness(const igraph_t *graph, tmpscore[w] = 0; distance[w] = 0; nrgeo[w] = 0; - igraph_vector_int_clear(fatv); + igraph_vector_int_clear(parv); } } /* source < no_of_nodes */ } @@ -697,8 +706,8 @@ int igraph_community_edge_betweenness(const igraph_t *graph, VECTOR(*edge_betweenness)[e] /= 2.0; } } - passive[maxedge] = 1; - igraph_edge(graph, (igraph_integer_t) maxedge, &from, &to); + passive[maxedge] = true; + IGRAPH_CHECK(igraph_edge(graph, maxedge, &from, &to)); neip = igraph_inclist_get(elist_in_p, to); neino = igraph_vector_int_size(neip); @@ -715,17 +724,17 @@ int igraph_community_edge_betweenness(const igraph_t *graph, IGRAPH_PROGRESS("Edge betweenness community detection: ", 100.0, NULL); - igraph_free(passive); + IGRAPH_FREE(passive); igraph_vector_destroy(&eb); - igraph_stack_destroy(&stack); + igraph_stack_int_destroy(&stack); IGRAPH_FINALLY_CLEAN(3); - if (weights == 0) { - igraph_dqueue_destroy(&q); + if (weights == NULL) { + igraph_dqueue_int_destroy(&q); IGRAPH_FINALLY_CLEAN(1); } else { igraph_2wheap_destroy(&heap); - igraph_inclist_destroy(&fathers); + igraph_inclist_destroy(&parents); IGRAPH_FINALLY_CLEAN(2); } igraph_free(tmpscore); @@ -749,10 +758,10 @@ int igraph_community_edge_betweenness(const igraph_t *graph, } if (result_owned) { - igraph_vector_destroy(result); + igraph_vector_int_destroy(result); IGRAPH_FREE(result); IGRAPH_FINALLY_CLEAN(2); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/fast_modularity.c b/src/vendor/cigraph/src/community/fast_modularity.c index ff790ccac03..d3632d05fa6 100644 --- a/src/vendor/cigraph/src/community/fast_modularity.c +++ b/src/vendor/cigraph/src/community/fast_modularity.c @@ -37,7 +37,7 @@ #ifdef _MSC_VER /* MSVC does not support variadic macros */ #include -void debug(const char* fmt, ...) { +void debug(const char *fmt, ...) { va_list args; va_start(args, fmt); #ifdef IGRAPH_FASTCOMM_DEBUG @@ -84,8 +84,8 @@ void debug(const char* fmt, ...) { /* 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_integer_t first; /* first member of the community pair */ + igraph_integer_t 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; @@ -96,30 +96,29 @@ 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_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 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; + * Returns true if the maximum is different from the previous one, + * false otherwise. */ +static igraph_bool_t igraph_i_fastgreedy_community_rescan_max(igraph_i_fastgreedy_community *comm) { + igraph_integer_t 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; + comm->maxdq = NULL; + return true; } best = (igraph_i_fastgreedy_commpair*)VECTOR(comm->neis)[0]; @@ -135,36 +134,36 @@ static int igraph_i_fastgreedy_community_rescan_max( if (best != comm->maxdq) { comm->maxdq = best; - return 1; + return true; } else { - return 0; + return false; } } /* Destroys the global community list object */ static void igraph_i_fastgreedy_community_list_destroy( - igraph_i_fastgreedy_community_list* list) { - long int i; + igraph_i_fastgreedy_community_list *list) { + igraph_integer_t i; for (i = 0; i < list->n; i++) { igraph_vector_ptr_destroy(&list->e[i].neis); } IGRAPH_FREE(list->e); - if (list->heapindex != 0) { + if (list->heapindex != NULL) { IGRAPH_FREE(list->heapindex); } - if (list->heap != 0) { + if (list->heap != NULL) { 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_i_fastgreedy_community_list *list, igraph_integer_t idx) { + igraph_integer_t 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; + igraph_integer_t *heapindex = list->heapindex; root = idx; while (root * 2 + 1 < list->no_of_communities) { @@ -194,12 +193,12 @@ static void igraph_i_fastgreedy_community_list_sift_down( /* 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_i_fastgreedy_community_list *list, igraph_integer_t idx) { + igraph_integer_t 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; + igraph_integer_t *heapindex = list->heapindex; root = idx; while (root > 0) { @@ -225,8 +224,8 @@ static void igraph_i_fastgreedy_community_list_sift_up( /* 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; + igraph_i_fastgreedy_community_list *list) { + igraph_integer_t i; for (i = list->no_of_communities / 2 - 1; i >= 0; i--) { igraph_i_fastgreedy_community_list_sift_down(list, i); } @@ -239,21 +238,21 @@ static void igraph_i_fastgreedy_community_list_build_heap( /* Dumps the heap - for debugging purposes */ /* static void igraph_i_fastgreedy_community_list_dump_heap( - igraph_i_fastgreedy_community_list* list) { - long int i; + igraph_i_fastgreedy_community_list *list) { + igraph_integer_t 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, + debug(" (%" IGRAPH_PRId ", %" IGRAPH_PRId ", %.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("%" IGRAPH_PRId " ", list->heapindex[i]); } debug("\nEND\n"); } @@ -263,13 +262,13 @@ static void igraph_i_fastgreedy_community_list_dump_heap( * Only useful for debugging. */ /* static void igraph_i_fastgreedy_community_list_check_heap( - igraph_i_fastgreedy_community_list* list) { - long int i; + igraph_i_fastgreedy_community_list *list) { + igraph_integer_t 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); + debug("Position: %" IGRAPH_PRId ", %" IGRAPH_PRId " and %" IGRAPH_PRId "\n", i, 2 * i + 1, 2 * i + 2); igraph_i_fastgreedy_community_list_dump_heap(list); } } @@ -278,13 +277,13 @@ static void igraph_i_fastgreedy_community_list_check_heap( /* 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_i_fastgreedy_community_list *list, igraph_integer_t idx) { igraph_real_t old; - long int commidx; + igraph_integer_t commidx; /* First adjust the index */ commidx = list->heap[list->no_of_communities - 1]->maxdq->first; - list->heapindex[commidx] = (igraph_integer_t) idx; + list->heapindex[commidx] = idx; commidx = list->heap[idx]->maxdq->first; list->heapindex[commidx] = -1; @@ -304,8 +303,8 @@ static void igraph_i_fastgreedy_community_list_remove( /* 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; + igraph_i_fastgreedy_community_list *list, igraph_integer_t idx, igraph_integer_t comm) { + igraph_integer_t i; if (idx == list->no_of_communities - 1) { /* We removed the rightmost element on the bottom level, no problem, @@ -317,7 +316,7 @@ static void igraph_i_fastgreedy_community_list_remove2( /* First adjust the index */ i = list->heap[list->no_of_communities - 1]->maxdq->first; - list->heapindex[i] = (igraph_integer_t) idx; + list->heapindex[i] = idx; list->heapindex[comm] = -1; /* Now remove the element */ @@ -333,9 +332,9 @@ static void igraph_i_fastgreedy_community_list_remove2( /* 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_community_list *list, igraph_integer_t c, igraph_integer_t k) { + igraph_integer_t i, n; + igraph_bool_t rescan = false; igraph_i_fastgreedy_commpair *p; igraph_i_fastgreedy_community *comm; igraph_real_t olddq; @@ -347,7 +346,7 @@ static void igraph_i_fastgreedy_community_remove_nei( if (p->second == k) { /* Check current maxdq */ if (comm->maxdq == p) { - rescan = 1; + rescan = true; } break; } @@ -367,7 +366,7 @@ static void igraph_i_fastgreedy_community_remove_nei( } 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); + debug("REMOVING (NO MORE NEIS): %" IGRAPH_PRId "\n", i); igraph_i_fastgreedy_community_list_remove2(list, i, c); } } @@ -376,26 +375,28 @@ static void igraph_i_fastgreedy_community_remove_nei( /* 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) { +static int igraph_i_fastgreedy_commpair_cmp(const void *p1, const void *p2) { igraph_i_fastgreedy_commpair *cp1, *cp2; + igraph_integer_t diff; cp1 = *(igraph_i_fastgreedy_commpair**)p1; cp2 = *(igraph_i_fastgreedy_commpair**)p2; - return (int) (cp1->second - cp2->second); + diff = cp1->second - cp2->second; + return (diff < 0) ? -1 : (diff > 0) ? 1 : 0; } /* 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_community_list *list, igraph_integer_t index, + igraph_i_fastgreedy_commpair *changed_pair) { + igraph_vector_ptr_t *vec; + igraph_integer_t i, n; + igraph_bool_t can_skip_sort = false; igraph_i_fastgreedy_commpair *other_pair; vec = &list->e[index].neis; - if (changed_pair != 0) { + if (changed_pair != NULL) { /* Optimized sorting */ /* First we look for changed_pair in vec */ @@ -407,45 +408,40 @@ static void igraph_i_fastgreedy_community_sort_neighbors_of( } /* 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; - } + IGRAPH_ASSERT(i < n); + + /* 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; + } + VECTOR(*vec)[i] = changed_pair; - /* Mark that we don't need a full sort */ - can_skip_sort = 1; + /* 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 = true; } if (!can_skip_sort) { @@ -458,10 +454,11 @@ static void igraph_i_fastgreedy_community_sort_neighbors_of( * 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; +static igraph_bool_t igraph_i_fastgreedy_community_update_dq( + igraph_i_fastgreedy_community_list *list, + igraph_i_fastgreedy_commpair *p, igraph_real_t newdq) { + + igraph_integer_t i, j, to, from; igraph_real_t olddq; igraph_i_fastgreedy_community *comm_to, *comm_from; to = p->first; from = p->second; @@ -492,7 +489,7 @@ static int igraph_i_fastgreedy_community_update_dq( j = igraph_i_fastgreedy_community_list_find_in_heap(list, from); igraph_i_fastgreedy_community_list_sift_up(list, j); } - return 1; + return true; } 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 @@ -515,7 +512,7 @@ static int igraph_i_fastgreedy_community_update_dq( igraph_i_fastgreedy_community_list_sift_up(list, j); } } - return 0; + return false; } else { /* We got here in two cases: (1) the pair we are modifying right now is the maximum in the given @@ -567,7 +564,7 @@ static int igraph_i_fastgreedy_community_update_dq( } } } - return 1; + return true; } /** @@ -593,8 +590,8 @@ static int igraph_i_fastgreedy_community_update_dq( * 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 + * 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. @@ -620,24 +617,24 @@ static int igraph_i_fastgreedy_community_update_dq( * * \example examples/simple/igraph_community_fastgreedy.c */ -int igraph_community_fastgreedy(const igraph_t *graph, +igraph_error_t igraph_community_fastgreedy(const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_t *merges, + igraph_matrix_int_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_vector_int_t *membership) { + igraph_integer_t no_of_edges, no_of_nodes, no_of_joins, total_joins; + igraph_integer_t i, j, k, n, m, from, to, dummy, best_no_of_joins; igraph_eit_t edgeit; igraph_i_fastgreedy_commpair *pairs, *p1, *p2; igraph_i_fastgreedy_community_list communities; igraph_vector_t a; + igraph_vector_int_t degrees; igraph_real_t q, *dq, bestq, weight_sum, loop_weight_sum; igraph_bool_t has_multiple; - igraph_matrix_t merges_local; + igraph_matrix_int_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 };*/ + /*igraph_integer_t 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 };*/ + /*igraph_integer_t 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); @@ -648,7 +645,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, total_joins = no_of_nodes > 0 ? no_of_nodes - 1 : 0; - if (weights != 0) { + if (weights) { if (igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Length of weight vector must agree with number of edges.", IGRAPH_EINVAL); } @@ -657,7 +654,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); } - if (igraph_is_nan(minweight)) { + if (isnan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -671,20 +668,20 @@ int igraph_community_fastgreedy(const igraph_t *graph, IGRAPH_ERROR("Fast greedy community detection works only on graphs without multi-edges.", IGRAPH_EINVAL); } - if (membership != 0 && merges == 0) { + if (membership != NULL && merges == NULL) { /* 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); + IGRAPH_CHECK(igraph_matrix_int_init(&merges_local, total_joins, 2)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &merges_local); merges = &merges_local; } - if (merges != 0) { - IGRAPH_CHECK(igraph_matrix_resize(merges, total_joins, 2)); - igraph_matrix_null(merges); + if (merges != NULL) { + IGRAPH_CHECK(igraph_matrix_int_resize(merges, total_joins, 2)); + igraph_matrix_int_null(merges); } - if (modularity != 0) { + if (modularity != NULL) { IGRAPH_CHECK(igraph_vector_resize(modularity, total_joins + 1)); } @@ -693,62 +690,63 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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]; + VECTOR(a)[IGRAPH_FROM(graph, i)] += VECTOR(*weights)[i]; + VECTOR(a)[IGRAPH_TO(graph, i)] += VECTOR(*weights)[i]; } } else { debug("Calculating degrees\n"); - IGRAPH_CHECK(igraph_degree(graph, &a, igraph_vss_all(), IGRAPH_ALL, 1)); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, 1)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(a)[i] = VECTOR(degrees)[i]; + } + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(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); - } + communities.e = IGRAPH_CALLOC(no_of_nodes, igraph_i_fastgreedy_community); + IGRAPH_CHECK_OOM(communities.e, "Insufficient memory for fast greedy community detection."); 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); - } + + communities.heap = IGRAPH_CALLOC(no_of_nodes, igraph_i_fastgreedy_community*); + IGRAPH_CHECK_OOM(communities.heap, "Insufficient memory for fast greedy community detection."); 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); - } + + communities.heapindex = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(communities.heapindex, "Insufficient memory for fast greedy community detection."); + 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; + IGRAPH_CHECK(igraph_vector_ptr_init(&communities.e[i].neis, 0)); + communities.e[i].id = 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); - } + dq = IGRAPH_CALLOC(no_of_edges, igraph_real_t); + IGRAPH_CHECK_OOM(dq, "Insufficient memory for fast greedy community detection."); IGRAPH_FINALLY(igraph_free, dq); + debug("Creating community pair list\n"); - IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &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); - } + pairs = IGRAPH_CALLOC(2 * no_of_edges, igraph_i_fastgreedy_commpair); + IGRAPH_CHECK_OOM(pairs, "Insufficient memory for fast greedy community detection."); 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); + igraph_integer_t eidx = IGRAPH_EIT_GET(edgeit); /* Create the pairs themselves */ - from = (long int)ffrom; to = (long int)fto; + from = IGRAPH_FROM(graph, eidx); to = IGRAPH_TO(graph, eidx); if (from == to) { loop_weight_sum += weights ? 2 * VECTOR(*weights)[eidx] : 2; continue; @@ -771,13 +769,13 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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]); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&communities.e[from].neis, &pairs[i])); + IGRAPH_CHECK(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) { + if (communities.e[from].maxdq == NULL || *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) { + if (communities.e[to].maxdq == NULL || *communities.e[to].maxdq->dq < *pairs[i + 1].dq) { communities.e[to].maxdq = &pairs[i + 1]; } } @@ -787,12 +785,12 @@ int igraph_community_fastgreedy(const igraph_t *graph, /* 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); + igraph_i_fastgreedy_community_sort_neighbors_of(&communities, i, NULL); /* 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) { + * the heap (to avoid maxdq == NULL) */ + if (communities.e[i].maxdq != NULL) { communities.heap[j] = &communities.e[i]; - communities.heapindex[i] = (igraph_integer_t) j; + communities.heapindex[i] = j; j++; } else { communities.heapindex[i] = -1; @@ -863,10 +861,10 @@ int igraph_community_fastgreedy(const igraph_t *graph, } debug("\n"); #endif - if (communities.heap[0] == 0) { + if (communities.heap[0] == NULL) { break; /* no more communities */ } - if (communities.heap[0]->maxdq == 0) { + if (communities.heap[0]->maxdq == NULL) { break; /* there are only isolated comms */ } to = communities.heap[0]->maxdq->second; @@ -897,13 +895,13 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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, + debug("Pairs: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\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); + debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, p1->second); if (p1->second == from) { - debug(" WILL REMOVE: %ld-%ld\n", to, from); + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", to, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", @@ -913,7 +911,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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, + debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId " and %" IGRAPH_PRId "-%" IGRAPH_PRId "\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", @@ -923,9 +921,9 @@ int igraph_community_fastgreedy(const igraph_t *graph, i++; j++; } else { - debug(" Considering: %ld-%ld\n", p2->first, p2->second); + debug(" Considering: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p2->first, p2->second); if (p2->second == to) { - debug(" WILL REMOVE: %ld-%ld\n", p2->second, p2->first); + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p2->second, p2->first); } else { /* chain, case 2 */ debug(" CHAIN(2): %ld %ld-%ld, newdq(%ld,%ld)=%.7f\n", @@ -933,7 +931,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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 + * not sorted anymore. 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 @@ -956,11 +954,11 @@ int igraph_community_fastgreedy(const igraph_t *graph, } } - p1 = 0; + p1 = NULL; 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); + debug(" WILL REMOVE: %" IGRAPH_PRId "-%" IGRAPH_PRId "\n", p1->first, from); } else { /* chain, case 1 */ debug(" CHAIN(1): %ld-%ld %ld, now=%.7f, adding=%.7f, newdq(%ld,%ld)=%.7f\n", @@ -996,12 +994,12 @@ int igraph_community_fastgreedy(const igraph_t *graph, /* Now, remove community `from` from the neighbors of community `to` */ if (communities.no_of_communities > 2) { - debug(" REMOVING: %ld-%ld\n", to, from); + debug(" REMOVING: %" IGRAPH_PRId "-%" IGRAPH_PRId "\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; + communities.e[from].maxdq = NULL; /* Update community sizes */ communities.e[to].size += communities.e[from].size; @@ -1015,7 +1013,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, 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); + communities.e[to].id = no_of_nodes + no_of_joins; } /* Update vector a */ @@ -1029,17 +1027,19 @@ int igraph_community_fastgreedy(const igraph_t *graph, * 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_integer_t *ivec; + igraph_integer_t merges_nrow = igraph_matrix_int_nrow(merges); + + ivec = IGRAPH_CALLOC(merges_nrow, igraph_integer_t); + IGRAPH_CHECK_OOM(ivec, "Insufficient memory for fast greedy community detection."); 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_matrix_int_permdelete_rows(merges, ivec, total_joins - no_of_joins); + IGRAPH_FREE(ivec); IGRAPH_FINALLY_CLEAN(1); } @@ -1047,7 +1047,7 @@ int igraph_community_fastgreedy(const igraph_t *graph, if (modularity) { VECTOR(*modularity)[no_of_joins] = q; - igraph_vector_resize(modularity, no_of_joins + 1); + IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_joins + 1)); } /* Internally, the algorithm does not create NaN values. @@ -1067,18 +1067,18 @@ int igraph_community_fastgreedy(const igraph_t *graph, if (membership) { IGRAPH_CHECK(igraph_community_to_membership(merges, - (igraph_integer_t) no_of_nodes, - /*steps=*/ (igraph_integer_t) best_no_of_joins, + no_of_nodes, + /*steps=*/ best_no_of_joins, membership, /*csize=*/ 0)); } if (merges == &merges_local) { - igraph_matrix_destroy(&merges_local); + igraph_matrix_int_destroy(&merges_local); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } #ifdef IGRAPH_FASTCOMM_DEBUG diff --git a/src/vendor/cigraph/src/community/fluid.c b/src/vendor/cigraph/src/community/fluid.c index e272a06d6a3..d419f7ac12f 100644 --- a/src/vendor/cigraph/src/community/fluid.c +++ b/src/vendor/cigraph/src/community/fluid.c @@ -37,18 +37,18 @@ * 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. + * interaction and density. Weighted graphs are not supported. * + * * 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. + * Springer, vol 689, p 229. https://doi.org/10.1007/978-3-319-72150-7_19 * * \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. + * Edge directions will be ignored. * \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 @@ -60,17 +60,16 @@ * * Time complexity: O(|E|) */ -int igraph_community_fluid_communities(const igraph_t *graph, +igraph_error_t igraph_community_fluid_communities(const igraph_t *graph, igraph_integer_t no_of_communities, - igraph_vector_t *membership, - igraph_real_t *modularity) { + igraph_vector_int_t *membership) { /* Declaration of variables */ - long int no_of_nodes, i, j, k, kv1; + igraph_integer_t 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; + igraph_real_t max_density; + igraph_bool_t is_simple, is_connected, running; + igraph_vector_t density, label_counters; + igraph_vector_int_t dominant_labels, node_order, com_to_numvertices; /* Initialization of variables needed for initial checking */ no_of_nodes = igraph_vcount(graph); @@ -78,27 +77,33 @@ int igraph_community_fluid_communities(const igraph_t *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)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, 0); } return IGRAPH_SUCCESS; } - if ((long int) no_of_communities < 1) { + if (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) { + if (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_CHECK(igraph_is_simple(graph, &is_simple)); + if (!is_simple) { IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_is_connected(graph, &res, IGRAPH_WEAK)); - if (!res) { + if (igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + igraph_bool_t has_mutual; + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + IGRAPH_ERROR("Fluid community detection supports only simple graphs.", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_is_connected(graph, &is_connected, IGRAPH_WEAK)); + if (!is_connected) { IGRAPH_ERROR("Fluid community detection supports only connected graphs.", IGRAPH_EINVAL); } if (igraph_is_directed(graph)) { @@ -109,29 +114,29 @@ int igraph_community_fluid_communities(const igraph_t *graph, max_density = 1.0; /* Resize membership vector (number of nodes) */ - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); /* Initialize density and com_to_numvertices vectors */ - IGRAPH_CHECK(igraph_vector_init(&density, (long int) no_of_communities)); + IGRAPH_CHECK(igraph_vector_init(&density, no_of_communities)); IGRAPH_FINALLY(igraph_vector_destroy, &density); - IGRAPH_CHECK(igraph_vector_int_init(&com_to_numvertices, (long int) no_of_communities)); + IGRAPH_CHECK(igraph_vector_int_init(&com_to_numvertices, 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); + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); /* Initialize the membership vector with 0 values */ - igraph_vector_null(membership); + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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; + VECTOR(*membership)[VECTOR(node_order)[i]] = i + 1; /* Initialize com_to_numvertices list: Number of vertices for each community */ VECTOR(com_to_numvertices)[i] = 1; } @@ -141,42 +146,43 @@ int igraph_community_fluid_communities(const igraph_t *graph, 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_VECTOR_INT_INIT_FINALLY(&dominant_labels, no_of_communities); - IGRAPH_CHECK(igraph_vector_init(&label_counters, (long int) no_of_communities)); + IGRAPH_CHECK(igraph_vector_init(&label_counters, no_of_communities)); IGRAPH_FINALLY(igraph_vector_destroy, &label_counters); + RNG_BEGIN(); + /* running is the convergence boolean variable */ - running = 1; + running = true; while (running) { - /* Declarations of varibales used inside main loop */ - long int v1, size, rand_idx; + /* Declarations of variables used inside main loop */ + igraph_integer_t 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; + running = false; /* Shuffle the node ordering vector */ - IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + IGRAPH_CHECK(igraph_vector_int_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_int_clear(&dominant_labels); igraph_vector_null(&label_counters); /* Obtain actual node index */ - v1 = (long int) VECTOR(node_order)[i]; + v1 = VECTOR(node_order)[i]; /* Take into account same label in updating rule */ - kv1 = (long int) VECTOR(*membership)[v1]; + kv1 = 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)); + IGRAPH_CHECK(igraph_vector_int_resize(&dominant_labels, 1)); VECTOR(dominant_labels)[0] = kv1; } @@ -184,7 +190,7 @@ int igraph_community_fluid_communities(const igraph_t *graph, 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]]; + k = VECTOR(*membership)[VECTOR(*neis)[j]]; /* skip if it has no label yet */ if (k == 0) { continue; @@ -195,25 +201,24 @@ int igraph_community_fluid_communities(const igraph_t *graph, /* 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)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&dominant_labels, k)); } } - RNG_BEGIN(); - if (!igraph_vector_empty(&dominant_labels)) { + if (!igraph_vector_int_empty(&dominant_labels)) { /* Maintain same label if it exists in dominant_labels */ - same_label_in_dominant = igraph_vector_contains(&dominant_labels, kv1); + same_label_in_dominant = igraph_vector_int_contains(&dominant_labels, kv1); if (!same_label_in_dominant) { /* We need at least one more iteration */ - running = 1; + running = true; /* 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]; + rand_idx = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); + k = VECTOR(dominant_labels)[rand_idx]; if (kv1 != 0) { /* Subtract 1 vertex from corresponding community in com_to_numvertices */ @@ -231,38 +236,27 @@ int igraph_community_fluid_communities(const igraph_t *graph, VECTOR(density)[k - 1] = max_density / VECTOR(com_to_numvertices)[k - 1]; } } - RNG_END(); } } + 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_ASSERT(VECTOR(*membership)[i] >= 0); /* all vertices must have a community assigned */ } 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_int_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); + igraph_vector_int_destroy(&dominant_labels); + IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap.cc b/src/vendor/cigraph/src/community/infomap/infomap.cc index 4985f1ade64..fe690948e17 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap.cc @@ -29,35 +29,43 @@ homePage: http://www.irit.fr/~Emmanuel.Navarro/ */ - -#include "igraph_interface.h" #include "igraph_community.h" + +#include "core/exceptions.h" #include "core/interruption.h" #include "infomap_Node.h" +#include "infomap_FlowGraph.h" #include "infomap_Greedy.h" -#include +#include +#include + +// This is necessary for GCC 5 and earlier, where including +// makes isnan() unusable without the std:: prefix, even if +// was included as well. +using std::isnan; /****************************************************************************/ -int infomap_partition(FlowGraph * fgraph, bool rcall) { - Greedy * greedy; +static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { // save the original graph - FlowGraph * cpy_fgraph = new FlowGraph(fgraph); - IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); + FlowGraph cpy_fgraph(fgraph); - int Nnode = cpy_fgraph->Nnode; + igraph_integer_t Nnode = cpy_fgraph.Nnode; // "real" number of vertex, ie. number of vertex of the graph - int iteration = 0; + igraph_integer_t iteration = 0; double outer_oldCodeLength, newCodeLength; - int *initial_move = NULL; + std::vector initial_move; bool initial_move_done = true; + // re-use vector in loop for better performance + std::vector subMoveTo; + do { // Main loop - outer_oldCodeLength = fgraph->codeLength; + outer_oldCodeLength = fgraph.codeLength; if (iteration > 0) { /**********************************************************************/ @@ -65,62 +73,45 @@ int infomap_partition(FlowGraph * fgraph, bool rcall) { // =========================================== // intial_move indicate current clustering - initial_move = new int[Nnode]; + initial_move.resize(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 + subMoveTo.clear(); // enventual new partitionment of original graph - if ((iteration % 2 == 0) && (fgraph->Nnode > 1)) { + if ((iteration % 2 == 0) && (fgraph.Nnode > 1)) { // 0/ Submodule movements : partition each module of the // current partition (rec. call) - subMoveTo = new int[Nnode]; + subMoveTo.resize(Nnode); // vid_cpy_fgraph --> new_cluster_id (new partition) - IGRAPH_FINALLY(operator delete [], subMoveTo); - - int subModIndex = 0; + igraph_integer_t subModIndex = 0; - for (int i = 0 ; i < fgraph->Nnode ; i++) { + for (igraph_integer_t i = 0 ; i < fgraph.Nnode ; i++) { // partition each non trivial module - int sub_Nnode = fgraph->node[i]->members.size(); + size_t 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]; - } + const std::vector &sub_members = fgraph.node[i].members; // extraction of the subgraph - FlowGraph *sub_fgraph = new FlowGraph(cpy_fgraph, sub_Nnode, - sub_members); - IGRAPH_FINALLY(delete_FlowGraph, sub_fgraph); - sub_fgraph->initiate(); + FlowGraph sub_fgraph(cpy_fgraph, sub_members); + 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; + for (igraph_integer_t j = 0; j < sub_fgraph.Nnode; j++) { + for (const auto &v : sub_fgraph.node[j].members) { + subMoveTo[sub_members[v]] = 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; + subMoveTo[fgraph.node[i].members[0]] = subModIndex; initial_move[subModIndex] = i; subModIndex++; } @@ -128,26 +119,19 @@ int infomap_partition(FlowGraph * fgraph, bool rcall) { } 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; + for (igraph_integer_t i = 0; i < fgraph.Nnode; i++) { // for each module + for (const auto &v : fgraph.node[i].members) { // for each vertex (of the module) + initial_move[v] = i; } } } - fgraph->back_to(cpy_fgraph); - if (subMoveTo) { - Greedy *cpy_greedy = new Greedy(fgraph); - IGRAPH_FINALLY(delete_Greedy, cpy_greedy); + fgraph.back_to(cpy_fgraph); + if (! subMoveTo.empty()) { + Greedy cpy_greedy(&fgraph); - cpy_greedy->setMove(subMoveTo); - cpy_greedy->apply(false); - - delete_Greedy(cpy_greedy); - IGRAPH_FINALLY_CLEAN(1); - delete [] subMoveTo; - IGRAPH_FINALLY_CLEAN(1); + cpy_greedy.setMove(subMoveTo); + cpy_greedy.apply(false); } } /**********************************************************************/ @@ -157,29 +141,26 @@ int infomap_partition(FlowGraph * fgraph, bool rcall) { do { // greedy optimizing object creation - greedy = new Greedy(fgraph); - IGRAPH_FINALLY(delete_Greedy, greedy); + Greedy greedy(&fgraph); // Initial move to apply ? - if (!initial_move_done && initial_move) { + if (!initial_move_done && ! initial_move.empty()) { initial_move_done = true; - greedy->setMove(initial_move); + greedy.setMove(initial_move); } - oldCodeLength = greedy->codeLength; + oldCodeLength = greedy.codeLength; bool moved = true; - //int Nloops = 0; - //int count = 0; + //igraph_integer_t count = 0; double inner_oldCodeLength = 1000; while (moved) { // main greedy optimizing loop - inner_oldCodeLength = greedy->codeLength; - moved = greedy->optimize(); + inner_oldCodeLength = greedy.codeLength; + moved = greedy.optimize(); - //Nloops++; //count++; - if (fabs(greedy->codeLength - inner_oldCodeLength) < 1.0e-10) + if (fabs(greedy.codeLength - inner_oldCodeLength) < 1.0e-10) // if the move does'n reduce the codelenght -> exit ! { moved = false; @@ -192,72 +173,66 @@ int infomap_partition(FlowGraph * fgraph, bool rcall) { } // transform the network to network of modules: - greedy->apply(true); - newCodeLength = greedy->codeLength; - - // destroy greedy object - delete greedy; - IGRAPH_FINALLY_CLEAN(1); - + greedy.apply(true); + newCodeLength = greedy.codeLength; } 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. + * \brief Find community structure that minimizes the expected description length of a random walker trajectory. * - * Implementation of the InfoMap community detection algorithm.of + * 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 ] + * + * For more details, see the visualization of the math and the map generator + * at https://www.mapequation.org . The original paper describing the algorithm + * is: 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). + * A more detailed paper about the algorithm is: 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.) + * Integration in igraph was 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 + * If you want to specify a random seed (as in the 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 + * The random walker will favour edges with high weights over + * edges with low weights; the probability of picking a particular + * outbound edge from a node is directly proportional to its weight. + * If it is \c NULL then all edges will have equal * weights. The weights are expected to be non-negative. * \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. + * Vertices with higher weights are favoured by the random walker + * when it needs to "teleport" to a new node after getting stuck in + * a sink node (i.e. a node with no outbound edges). The probability + * of picking a vertex when the random walker teleports is directly + * proportional to the weight of the vertex. If this argument is \c NULL + * then all vertices will have equal weights. 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 @@ -271,23 +246,27 @@ int infomap_partition(FlowGraph * fgraph, bool rcall) { * * Time complexity: TODO. */ -int igraph_community_infomap(const igraph_t * graph, +igraph_error_t 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_integer_t nb_trials, + igraph_vector_int_t *membership, igraph_real_t *codelength) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + if (e_weights) { const igraph_integer_t ecount = igraph_ecount(graph); if (igraph_vector_size(e_weights) != ecount) { IGRAPH_ERROR("Invalid edge weight vector length.", IGRAPH_EINVAL); } if (ecount > 0) { + /* Allow both positive and zero weights. + * The conversion to Infomap format will simply skip zero-weight edges/ */ igraph_real_t minweight = igraph_vector_min(e_weights); if (minweight < 0) { IGRAPH_ERROR("Edge weights must not be negative.", IGRAPH_EINVAL); - } else if (igraph_is_nan(minweight)) { + } else if (isnan(minweight)) { IGRAPH_ERROR("Edge weights must not be NaN values.", IGRAPH_EINVAL); } } @@ -299,58 +278,53 @@ int igraph_community_infomap(const igraph_t * graph, IGRAPH_ERROR("Invalid vertex weight vector length.", IGRAPH_EINVAL); } if (vcount > 0) { + /* TODO: Currently we require strictly positive. Can this be + * relaxed to non-negative values? */ igraph_real_t minweight = igraph_vector_min(v_weights); if (minweight <= 0) { IGRAPH_ERROR("Vertex weights must be positive.", IGRAPH_EINVAL); - } else if (igraph_is_nan(minweight)) { + } else if (isnan(minweight)) { IGRAPH_ERROR("Vertex weights must not be NaN values.", IGRAPH_EINVAL); } } } - FlowGraph * fgraph = new FlowGraph(graph, e_weights, v_weights); - IGRAPH_FINALLY(delete_FlowGraph, fgraph); + FlowGraph fgraph(graph, e_weights, v_weights); // compute stationary distribution - fgraph->initiate(); + fgraph.initiate(); - FlowGraph * cpy_fgraph ; double shortestCodeLength = 1000.0; // create membership vector - int Nnode = fgraph->Nnode; - IGRAPH_CHECK(igraph_vector_resize(membership, Nnode)); + igraph_integer_t Nnode = fgraph.Nnode; + IGRAPH_CHECK(igraph_vector_int_resize(membership, Nnode)); - for (int trial = 0; trial < nb_trials; trial++) { - cpy_fgraph = new FlowGraph(fgraph); - IGRAPH_FINALLY(delete_FlowGraph, cpy_fgraph); + for (igraph_integer_t trial = 0; trial < nb_trials; trial++) { + FlowGraph cpy_fgraph(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; + 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; + for (igraph_integer_t i = 0 ; i < cpy_fgraph.Nnode ; i++) { + size_t Nmembers = cpy_fgraph.node[i].members.size(); + for (size_t 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)); + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, NULL)); return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc index 9af7eb3a404..8b77d124e9c 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.cc @@ -24,133 +24,127 @@ #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) { +void FlowGraph::init(igraph_integer_t n, const igraph_vector_t *v_weights) { alpha = 0.15; beta = 1.0 - alpha; Nnode = n; - node = new Node*[Nnode]; + node.reserve(Nnode); if (v_weights) { - for (int i = 0; i < Nnode; i++) { - node[i] = new Node(i, (double)VECTOR(*v_weights)[i]); + for (igraph_integer_t i = 0; i < Nnode; i++) { + node.emplace_back(i, VECTOR(*v_weights)[i]); } } else { - for (int i = 0; i < Nnode; i++) { - node[i] = new Node(i, 1.0); + for (igraph_integer_t i = 0; i < Nnode; i++) { + node.emplace_back(i, 1.0); } } } -FlowGraph::FlowGraph(int n) { +FlowGraph::FlowGraph(igraph_integer_t n) { init(n, NULL); } -FlowGraph::FlowGraph(int n, const igraph_vector_t *v_weights) { +FlowGraph::FlowGraph(igraph_integer_t n, const igraph_vector_t *v_weights) { init(n, v_weights); } -/* Build the graph from igraph_t object - */ -FlowGraph::FlowGraph(const igraph_t * graph, +/* 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); + igraph_integer_t n = igraph_vcount(graph); init(n, v_weights); - int directed = (int) igraph_is_directed(graph); + bool directed = igraph_is_directed(graph); double linkWeight = 1.0; igraph_integer_t from, to; - long int Nlinks = (long int) igraph_ecount(graph); + igraph_integer_t Nlinks = igraph_ecount(graph); if (!directed) { Nlinks = Nlinks * 2 ; } - for (int i = 0; i < Nlinks; i++) { + for (igraph_integer_t i = 0; i < Nlinks; i++) { if (!directed) { // not directed if (i % 2 == 0) { - linkWeight = e_weights ? (double)VECTOR(*e_weights)[i / 2] : 1.0; + linkWeight = e_weights ? 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; + linkWeight = e_weights ? VECTOR(*e_weights)[i] : 1.0; igraph_edge(graph, i, &from, &to); } // Populate node from igraph_graph + // Negative edge weights were checked for already. + // We skip adding zero-weight edges. 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)); + node[from].outLinks.push_back(make_pair(to, linkWeight)); + node[to].inLinks.push_back(make_pair(from, linkWeight)); } } } } -FlowGraph::FlowGraph(FlowGraph * fgraph) { - int n = fgraph->Nnode; +FlowGraph::FlowGraph(const FlowGraph &fgraph) { + igraph_integer_t n = fgraph.Nnode; init(n, NULL); - for (int i = 0; i < n; i++) { - cpyNode(node[i], fgraph->node[i]); + for (igraph_integer_t i = 0; i < n; i++) { + node[i] = fgraph.node[i]; } //XXX: quid de danglings et Ndanglings? - alpha = fgraph->alpha ; - beta = fgraph->beta ; + 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; + 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; + codeLength = fgraph.codeLength; } /** construct a graph by extracting a subgraph from the given graph */ -FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) { +FlowGraph::FlowGraph(const FlowGraph &fgraph, const vector &sub_members) { + igraph_integer_t sub_Nnode = sub_members.size(); + 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 + set sub_mem(sub_members.begin(), sub_members.end()); - for (int j = 0; j < fgraph->Nnode; j++) { - sub_renumber[j] = -1; - } + set::iterator it_mem = sub_mem.begin(); + vector sub_renumber(fgraph.Nnode, -1); + // id --> sub_id - for (int j = 0; j < sub_Nnode; j++) { + for (igraph_integer_t j = 0; j < sub_Nnode; j++) { //int orig_nr = sub_members[j]; - int orig_nr = (*it_mem); + igraph_integer_t orig_nr = (*it_mem); - node[j]->teleportWeight = fgraph->node[orig_nr]->teleportWeight; - node[j]->selfLink = fgraph->node[orig_nr]->selfLink; + 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(); + size_t orig_NoutLinks = fgraph.node[orig_nr].outLinks.size(); + size_t 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; + for (size_t k = 0; k < orig_NoutLinks; k++) { + igraph_integer_t to = fgraph.node[orig_nr].outLinks[k].first; + igraph_integer_t 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 @@ -159,21 +153,21 @@ FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) { 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)); + // 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; + for (size_t k = 0; k < orig_NinLinks; k++) { + igraph_integer_t to = fgraph.node[orig_nr].inLinks[k].first; + igraph_integer_t 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)); + node[j].inLinks.push_back(make_pair(to_newnr, link_weight)); + node[to_newnr].outLinks.push_back(make_pair(j, link_weight)); } } } @@ -182,31 +176,15 @@ FlowGraph::FlowGraph(FlowGraph * fgraph, int sub_Nnode, int * sub_members) { } -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; +void FlowGraph::swap(FlowGraph &fgraph) { + node.swap(fgraph.node); - node = node_tmp; + igraph_integer_t Nnode_tmp = fgraph.Nnode; + fgraph.Nnode = Nnode; Nnode = Nnode_tmp; calibrate(); @@ -223,26 +201,26 @@ void FlowGraph::initiate() { // total teleport weight Ndanglings = 0; double totTeleportWeight = 0.0; - for (int i = 0; i < Nnode; i++) { - totTeleportWeight += node[i]->teleportWeight; + for (igraph_integer_t i = 0; i < Nnode; i++) { + totTeleportWeight += node[i].teleportWeight; } - for (int i = 0; i < Nnode; i++) { - node[i]->teleportWeight /= totTeleportWeight; + for (igraph_integer_t i = 0; i < Nnode; i++) { + node[i].teleportWeight /= totTeleportWeight; // normalize teleportation weight - if (node[i]->outLinks.empty() && (node[i]->selfLink <= 0.0)) { + 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; + size_t NoutLinks = node[i].outLinks.size(); + double sum = node[i].selfLink; // Take care of self-links + for (size_t 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; + node[i].selfLink /= sum; + for (size_t j = 0; j < NoutLinks; j++) { + node[i].outLinks[j].second /= sum; } } } @@ -251,25 +229,25 @@ void FlowGraph::initiate() { eigenvector(); // Update links to represent flow - for (int i = 0; i < Nnode; i++) { - node[i]->selfLink = beta * node[i]->size * node[i]->selfLink; + for (igraph_integer_t 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; + if (!node[i].outLinks.empty()) { + size_t NoutLinks = node[i].outLinks.size(); + for (size_t 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; + for (size_t j = 0; j < NoutLinks; j++) { + size_t NinLinks = node[node[i].outLinks[j].first].inLinks.size(); + for (size_t 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; } } @@ -278,23 +256,23 @@ void FlowGraph::initiate() { } // 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; + for (igraph_integer_t 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; + 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); + for (igraph_integer_t 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(); @@ -302,10 +280,10 @@ void FlowGraph::initiate() { /* Compute steady state distribution (ie. PageRank) over the network - * (for all i update node[i]->size) + * (for all i update node[i].size) */ void FlowGraph::eigenvector() { - vector size_tmp = vector(Nnode, 1.0 / Nnode); + vector size_tmp(Nnode, 1.0 / Nnode); int Niterations = 0; double danglingSize; @@ -316,35 +294,35 @@ void FlowGraph::eigenvector() { do { // Calculate dangling size danglingSize = 0.0; - for (int i = 0; i < Ndanglings; i++) { + for (igraph_integer_t 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; + for (igraph_integer_t 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]; + for (igraph_integer_t i = 0; i < Nnode; i++) { + node[i].size += beta * node[i].selfLink * size_tmp[i]; + size_t Nlinks = node[i].outLinks.size(); + for (size_t 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; + for (igraph_integer_t 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; + for (igraph_integer_t i = 0; i < Nnode; i++) { + node[i].size /= sum; + sqdiff += fabs(node[i].size - size_tmp[i]); + size_tmp[i] = node[i].size; } Niterations++; @@ -356,7 +334,7 @@ void FlowGraph::eigenvector() { } while ((Niterations < 200) && (sqdiff > 1.0e-15 || Niterations < 50)); danglingSize = 0.0; - for (int i = 0; i < Ndanglings; i++) { + for (igraph_integer_t i = 0; i < Ndanglings; i++) { danglingSize += size_tmp[danglings[i]]; } // cout << "done! (the error is " << sqdiff << " after " << Niterations @@ -372,13 +350,13 @@ void FlowGraph::calibrate() { exitFlow = 0.0; size_log_size = 0.0; - for (int i = 0; i < Nnode; i++) { // For each module + for (igraph_integer_t i = 0; i < Nnode; i++) { // For each module // own node/module codebook - size_log_size += plogp(node[i]->exit + node[i]->size); + 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); + exitFlow += node[i].exit; + exit_log_exit += plogp(node[i].exit); } exit = plogp(exitFlow); @@ -390,31 +368,20 @@ void FlowGraph::calibrate() { /* 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]); - } +void FlowGraph::back_to(const FlowGraph &fgraph) { + // delete current nodes and copy original ones + Nnode = fgraph.Nnode; + node = fgraph.node; // restore atributs - alpha = fgraph->alpha ; - beta = fgraph->beta ; + 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; + 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; + codeLength = fgraph.codeLength; } diff --git a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h index 937347cdc4a..c4c866dee56 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_FlowGraph.h @@ -22,47 +22,52 @@ */ -#ifndef FLOWGRAPH_H -#define FLOWGRAPH_H +#ifndef INFOMAP_FLOWGRAPH_H +#define INFOMAP_FLOWGRAPH_H + +#include "infomap_Node.h" + +#include "igraph_datatype.h" +#include "igraph_types.h" +#include "igraph_vector.h" #include #include +#include -#include "igraph_interface.h" - -#include "infomap_Node.h" +inline double plogp(double x) { + return x > 0.0 ? x*std::log(x) : 0.0; +} class FlowGraph { private: - void init(int n, const igraph_vector_t *nodeWeights); + void init(igraph_integer_t 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(igraph_integer_t n); + FlowGraph(igraph_integer_t n, const igraph_vector_t *nodeWeights); + FlowGraph(const FlowGraph &fgraph); + FlowGraph(const FlowGraph &fgraph, const std::vector &sub_members); - FlowGraph(const igraph_t * graph, const igraph_vector_t *e_weights, + FlowGraph(const igraph_t *graph, const igraph_vector_t *e_weights, const igraph_vector_t *v_weights); - ~FlowGraph(); - - void swap(FlowGraph * fgraph); + void swap(FlowGraph &fgraph); void initiate(); void eigenvector(); void calibrate(); - void back_to(FlowGraph * fgraph); + void back_to(const FlowGraph &fgraph); /*************************************************************************/ - Node **node; - int Nnode; + std::vector node; + igraph_integer_t Nnode; double alpha, beta; - int Ndanglings; - std::vector danglings; // id of dangling nodes + igraph_integer_t Ndanglings; + std::vector danglings; // id of dangling nodes double exit; // double exitFlow; // @@ -73,6 +78,4 @@ class FlowGraph { double codeLength; }; -void delete_FlowGraph(FlowGraph *fgraph); - -#endif +#endif // INFOMAP_FLOWGRAPH_H diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc index 62e2945c1a8..2714d2e3847 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc +++ b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.cc @@ -23,55 +23,49 @@ */ #include "infomap_Greedy.h" + +#include #include -#define plogp( x ) ( (x) > 0.0 ? (x)*log(x) : 0.0 ) +#include 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 +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); + node_index(Nnode), - 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); + Nempty(0), + mod_empty(Nnode), + mod_exit(Nnode), + mod_size(Nnode), + mod_danglingSize(Nnode), + mod_teleportWeight(Nnode), + mod_members(Nnode) +{ 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 + const std::vector &node = graph->node; + for (igraph_integer_t 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_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(); + 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; + codeLength = exit - 2.0 * exit_log_exit + size_log_size - nodeSize_log_nodeSize; } @@ -80,46 +74,46 @@ void delete_Greedy(Greedy *greedy) { */ bool Greedy::optimize() { bool moved = false; - Node ** node = graph->node; + const std::vector &node = graph->node; RNG_BEGIN(); // Generate random enumeration of nodes - vector randomOrder(Nnode); - for (int i = 0; i < Nnode; i++) { + vector randomOrder(Nnode); + for (igraph_integer_t i = 0; i < Nnode; i++) { randomOrder[i] = i; } - for (int i = 0; i < Nnode - 1; i++) { + for (igraph_integer_t i = 0; i < Nnode - 1; i++) { //int randPos = i ; //XXX - int randPos = RNG_INTEGER(i, Nnode - 1); + igraph_integer_t randPos = static_cast(RNG_INTEGER(i, Nnode - 1)); // swap i & randPos - int tmp = randomOrder[i]; + igraph_integer_t tmp = randomOrder[i]; randomOrder[i] = randomOrder[randPos]; randomOrder[randPos] = tmp; } - unsigned int offset = 1; - vector redirect(Nnode, 0); - vector > > flowNtoM(Nnode); + igraph_integer_t offset = 1; + vector redirect(Nnode, 0); + vector > > flowNtoM(Nnode); - for (int k = 0; k < Nnode; k++) { + for (igraph_integer_t k = 0; k < Nnode; k++) { // Pick nodes in random order - int flip = randomOrder[k]; - int oldM = node_index[flip]; + igraph_integer_t flip = randomOrder[k]; + igraph_integer_t oldM = node_index[flip]; - // Reset offset when int overflows - if (offset > INT_MAX) { - for (int j = 0; j < Nnode; j++) { + // Reset offset when igraph_integer_t overflows + if (offset > IGRAPH_INTEGER_MAX) { + for (igraph_integer_t j = 0; j < Nnode; j++) { redirect[j] = 0; } offset = 1; } // Size of vector with module links - int NmodLinks = 0; + igraph_integer_t NmodLinks = 0; // For all outLinks - int NoutLinks = node[flip]->outLinks.size(); + size_t NoutLinks = node[flip].outLinks.size(); if (NoutLinks == 0) { //dangling node, add node to calculate flow below redirect[oldM] = offset + NmodLinks; flowNtoM[NmodLinks].first = oldM; @@ -127,10 +121,10 @@ bool Greedy::optimize() { 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]; + for (size_t j = 0; j < NoutLinks; j++) { + igraph_integer_t nb_M = node_index[node[flip].outLinks[j].first]; // index destination du lien - double nb_flow = node[flip]->outLinks[j].second; + 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; @@ -144,10 +138,10 @@ bool Greedy::optimize() { } } // 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; + size_t NinLinks = node[flip].inLinks.size(); + for (size_t j = 0; j < NinLinks; j++) { + igraph_integer_t 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; @@ -161,42 +155,42 @@ bool Greedy::optimize() { } // For teleportation and dangling nodes - for (int j = 0; j < NmodLinks; j++) { - int newM = flowNtoM[j].first; + for (igraph_integer_t j = 0; j < NmodLinks; j++) { + igraph_integer_t 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); + (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; + (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) * + (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; + 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) ; + (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; + (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 (mod_members[oldM] > node[flip].members.size()) { if (Nempty > 0) { flowNtoM[NmodLinks].first = mod_empty[Nempty - 1]; flowNtoM[NmodLinks].second.first = 0.0; @@ -206,10 +200,10 @@ bool Greedy::optimize() { } // Randomize link order for optimized search - for (int j = 0; j < NmodLinks - 1; j++) { + for (igraph_integer_t j = 0; j < NmodLinks - 1; j++) { //int randPos = j ; // XXX - int randPos = RNG_INTEGER(j, NmodLinks - 1); - int tmp_M = flowNtoM[j].first; + igraph_integer_t randPos = static_cast(RNG_INTEGER(j, NmodLinks - 1)); + igraph_integer_t 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; @@ -220,15 +214,15 @@ bool Greedy::optimize() { flowNtoM[randPos].second.second = tmp_inFlow; } - int bestM = oldM; + igraph_integer_t 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++) { + for (igraph_integer_t j = 0; j < NmodLinks; j++) { - int newM = flowNtoM[j].first; + igraph_integer_t newM = flowNtoM[j].first; double outFlowNewM = flowNtoM[j].second.first; double inFlowNewM = flowNtoM[j].second.second; @@ -239,16 +233,16 @@ bool Greedy::optimize() { 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 - + 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); + + 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; @@ -268,7 +262,7 @@ bool Greedy::optimize() { if (mod_members[bestM] == 0) { Nempty--; } - if (mod_members[oldM] == static_cast(node[flip]->members.size())) { + if (mod_members[oldM] == node[flip].members.size()) { mod_empty[Nempty] = oldM; Nempty++; } @@ -279,19 +273,19 @@ bool Greedy::optimize() { size_log_size -= plogp(mod_exit[oldM] + mod_size[oldM]) + plogp(mod_exit[bestM] + mod_size[bestM]); - mod_exit[oldM] -= node[flip]->exit - outFlowOldM - + 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_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 - + 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(); + 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]; @@ -324,127 +318,106 @@ 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); - } + vector modSnode; // will give IDs of no-empty modules (nodes) + modSnode.reserve(Nnode); + + igraph_integer_t Nmod = 0; + for (igraph_integer_t i = 0; i < Nnode; i++) { + if (mod_members[i] > 0) { + Nmod++; + modSnode.push_back(i); } } + + if (sort) { + // sort by mod_size + std::sort(modSnode.begin(), modSnode.end(), + [&](double a, double b) { return mod_size[a] > mod_size[b]; } ); + } //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 ; + FlowGraph tmp_fgraph(Nmod); + vector &node_tmp = tmp_fgraph.node ; - Node ** node = graph->node; + const vector &node = graph->node; - vector nodeInMod = vector(Nnode); + vector nodeInMod(Nnode); // creation of new nodes - for (int i = 0; i < Nmod; i++) { + for (igraph_integer_t 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]]; + node_tmp[i].members.clear(); // 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; + vector > outFlowNtoM(Nmod); - for (int i = 0; i < Nnode; i++) { - int i_M = nodeInMod[node_index[i]]; //final id of the module of the node i + for (igraph_integer_t i = 0; i < Nnode; i++) { + igraph_integer_t 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; + copy( node[i].members.begin(), node[i].members.end(), + back_inserter( node_tmp[i_M].members ) ); + + for (const auto &link : node[i].outLinks) { + igraph_integer_t nb = link.first; + igraph_integer_t nb_M = nodeInMod[node_index[nb]]; + double nb_flow = link.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)); - } + // inserts key nb_M if it does not exist + outFlowNtoM[i_M][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)); + for (igraph_integer_t i = 0; i < Nmod; i++) { + for (const auto &item : outFlowNtoM[i]) { + if (item.first != i) { + node_tmp[i].outLinks.push_back(item); } } } // 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; + vector > inFlowNtoM(Nmod); + + for (igraph_integer_t i = 0; i < Nnode; i++) { + igraph_integer_t i_M = nodeInMod[node_index[i]]; + for (const auto &inLink : node[i].inLinks) { + igraph_integer_t nb = inLink.first; + igraph_integer_t nb_M = nodeInMod[node_index[nb]]; + double nb_flow = inLink.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)); - } + // inserts key nb_M if it does not exist + inFlowNtoM[i_M][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)); + for (igraph_integer_t i = 0; i < Nmod; i++) { + for (const auto &item : inFlowNtoM[i]) { + if (item.first != i) { + node_tmp[i].inLinks.push_back(item); } } } // Option to move to empty module - vector().swap(mod_empty); + mod_empty.clear(); 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); } @@ -463,13 +436,15 @@ void Greedy::apply(bool sort) { * - codeLength * according to **node / node[i]->index */ +/* unused */ +/* void Greedy::tune(void) { exit_log_exit = 0.0; size_log_size = 0.0; exitFlow = 0.0; - for (int i = 0; i < Nnode; i++) { + for (igraph_integer_t i = 0; i < Nnode; i++) { mod_exit[i] = 0.0; mod_size[i] = 0.0; mod_danglingSize[i] = 0.0; @@ -477,21 +452,20 @@ void Greedy::tune(void) { mod_members[i] = 0; } - Node ** node = graph->node; + const std::vector &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(); + for (igraph_integer_t i = 0; i < Nnode; i++) { + igraph_integer_t i_M = node_index[i]; // module id of node i - mod_size[i_M] += node[i]->size; - mod_danglingSize[i_M] += node[i]->danglingSize; - mod_teleportWeight[i_M] += node[i]->teleportWeight; + 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]; + for (const auto &link : node[i].outLinks) { + igraph_integer_t neighbor = link.first; + double neighbor_w = link.second; + igraph_integer_t neighbor_M = node_index[neighbor]; if (i_M != neighbor_M) { // neighbor in an other module mod_exit[i_M] += neighbor_w; } @@ -499,12 +473,12 @@ void Greedy::tune(void) { } // Update contribution from teleportation - for (int i = 0; i < Nnode; i++) { + for (igraph_integer_t 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++) { + for (igraph_integer_t 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]; @@ -514,40 +488,40 @@ void Greedy::tune(void) { 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::setMove(const std::vector &moveTo) { //void Greedy::determMove(int *moveTo) { - Node ** node = graph->node; + const std::vector &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]; + for (igraph_integer_t i = 0 ; i < Nnode ; i++) { // pour chaque module + igraph_integer_t oldM = i; + igraph_integer_t 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) + + 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) + 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; + 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; + for (const auto &outLink : node[i].outLinks) { + igraph_integer_t nb_M = node_index[outLink.first]; + double nb_flow = outLink.second; if (nb_M == oldM) { outFlowOldM += nb_flow; } else if (nb_M == newM) { @@ -556,10 +530,9 @@ void Greedy::setMove(int *moveTo) { } // 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; + for (const auto &inLink : node[i].inLinks) { + igraph_integer_t nb_M = node_index[inLink.first]; + double nb_flow = inLink.second; if (nb_M == oldM) { inFlowOldM += nb_flow; } else if (nb_M == newM) { @@ -573,7 +546,7 @@ void Greedy::setMove(int *moveTo) { // si le nouveau etait vide, on a un vide de moins... Nempty--; } - if (mod_members[oldM] == static_cast(node[i]->members.size())) { + if (mod_members[oldM] == node[i].members.size()) { // si l'ancien avait la taille de celui qui bouge, un vide de plus mod_empty[Nempty] = oldM; Nempty++; @@ -584,16 +557,16 @@ void Greedy::setMove(int *moveTo) { 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(); + 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]); diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h index 9769d1d4ca0..6c0594e33b9 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_Greedy.h @@ -22,27 +22,22 @@ */ -#ifndef GREEDY_H -#define GREEDY_H +#ifndef INFOMAP_GREEDY_H +#define INFOMAP_GREEDY_H -#include -#include -#include -#include +#include "infomap_Node.h" +#include "infomap_FlowGraph.h" #include "igraph_random.h" -#include "infomap_Node.h" -#include "infomap_FlowGraph.h" +#include class Greedy { public: Greedy(FlowGraph * fgraph); // initialise les attributs par rapport au graph - ~Greedy(); - - void setMove(int *moveTo); + void setMove(const std::vector &moveTo); //virtual void determMove(int *moveTo); bool optimize(); @@ -51,12 +46,16 @@ class Greedy { void apply(bool sort); //virtual void level(Node ***, bool sort); - void tune(void); + /* void tune(void); */ /* unused */ /**************************************************************************/ +public: + double codeLength; + +private: FlowGraph * graph; - int Nnode; + igraph_integer_t Nnode; double exit; double exitFlow; @@ -64,22 +63,19 @@ class Greedy { 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 + std::vector node_index; // module number of each node - int Nempty; - std::vector mod_empty; + igraph_integer_t 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; + std::vector mod_members; }; -void delete_Greedy(Greedy *greedy); -#endif +#endif // INFOMAP_GREEDY_H diff --git a/src/vendor/cigraph/src/community/infomap/infomap_Node.cc b/src/vendor/cigraph/src/community/infomap/infomap_Node.cc deleted file mode 100644 index 21f1b3160dd..00000000000 --- a/src/vendor/cigraph/src/community/infomap/infomap_Node.cc +++ /dev/null @@ -1,71 +0,0 @@ -/* -*- 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/vendor/cigraph/src/community/infomap/infomap_Node.h b/src/vendor/cigraph/src/community/infomap/infomap_Node.h index 5cd0407caa2..ee3d128f85c 100644 --- a/src/vendor/cigraph/src/community/infomap/infomap_Node.h +++ b/src/vendor/cigraph/src/community/infomap/infomap_Node.h @@ -22,23 +22,24 @@ */ -#ifndef NODE_H -#define NODE_H - -#include -#include +#ifndef INFOMAP_NODE_H +#define INFOMAP_NODE_H #include "igraph_interface.h" -class Node { -public: +#include - Node(); - Node(int modulenr, double tpweight); +struct Node { - std::vector members; - std::vector< std::pair > inLinks; - std::vector< std::pair > outLinks; + Node() : selfLink(0.0), exit(0.0), size(0.0) {} + Node(igraph_integer_t modulenr, double tpweight) : Node() { + teleportWeight = tpweight; + members.push_back(modulenr); // members = [nodenr] + } + + std::vector members; + std::vector< std::pair > inLinks; + std::vector< std::pair > outLinks; double selfLink; double teleportWeight; @@ -47,6 +48,4 @@ class Node { double size; }; -void cpyNode(Node *newNode, Node *oldNode); - -#endif +#endif // INFOMAP_NODE_H diff --git a/src/vendor/cigraph/src/community/label_propagation.c b/src/vendor/cigraph/src/community/label_propagation.c index 4fc9e4c5d97..5c9f3696826 100644 --- a/src/vendor/cigraph/src/community/label_propagation.c +++ b/src/vendor/cigraph/src/community/label_propagation.c @@ -48,17 +48,42 @@ * (among the ones incident on node \c i) have the highest total weight. * * - * Reference: + * For directed graphs, it is important to know that labels can circulate + * freely only within the strongly connected components of the graph and + * may propagate in only one direction (or not at all) \em between strongly + * connected components. You should treat directed edges as directed only + * if you are aware of the consequences. + * + * + * References: * * * 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). + * 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. + * + * Šubelj, L.: Label propagation for clustering. Chapter in "Advances in + * Network Clustering and Blockmodeling" edited by P. Doreian, V. Batagelj + * & A. Ferligoj (Wiley, New York, 2018). + * https://doi.org/10.1002/9781119483298.ch5 + * https://arxiv.org/abs/1709.05634 + * + * \param graph The input graph. Note that the algorithm wsa originally + * defined for undirected graphs. You are advised to set \p mode to + * \c IGRAPH_ALL if you pass a directed graph here to treat it as + * undirected. * \param membership The membership vector, the result is returned here. * For each vertex it gives the ID of its community (label). + * \param mode Whether to consider edge directions for the label propagation, + * and if so, which direction the labels should propagate. Ignored for + * undirected graphs. \c IGRAPH_ALL means to ignore edge directions (even + * in directed graphs). \c IGRAPH_OUT means to propagate labels along the + * natural direction of the edges. \c IGRAPH_IN means to propagate labels + * \em backwards (i.e. from head to tail). It is advised to set this to + * \c IGRAPH_ALL unless you are specifically interested in the effect of + * edge directions. * \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 @@ -76,36 +101,40 @@ * \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. Note that vertices without labels - * cannot be fixed. The fixed status will be ignord for these with a + * cannot be fixed. The fixed status will be ignored for these with a * warning. Also note that label numbers by themselves have no meaning, * and igraph may renumber labels. However, co-membership constraints * will be respected: two vertices can be fixed to be in the same or in * different communities. * \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. + * structure is stored here. Note that igraph will calculate the + * \em directed modularity if the input graph is directed, even if + * you set \p mode to \c IGRAPH_ALL * \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, +igraph_error_t igraph_community_label_propagation(const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_neimode_t mode, 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; + const igraph_vector_int_t *initial, + const igraph_vector_bool_t *fixed) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_not_fixed_nodes = no_of_nodes; + igraph_integer_t i, j, k; igraph_adjlist_t al; igraph_inclist_t il; igraph_bool_t running, control_iteration; igraph_bool_t unlabelled_left; + igraph_neimode_t reversed_mode; - igraph_vector_t label_counters, dominant_labels, nonzero_labels, node_order; + igraph_vector_t label_counters; + igraph_vector_int_t 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 @@ -131,7 +160,7 @@ int igraph_community_label_propagation(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Weights must not be negative.", IGRAPH_EINVAL); } - if (igraph_is_nan(minweight)) { + if (isnan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -140,10 +169,10 @@ int igraph_community_label_propagation(const igraph_t *graph, IGRAPH_WARNING("Ignoring fixed vertices as no initial labeling given."); } - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); if (initial) { - if (igraph_vector_size(initial) != no_of_nodes) { + if (igraph_vector_int_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 */ @@ -151,7 +180,7 @@ int igraph_community_label_propagation(const igraph_t *graph, if (VECTOR(*initial)[i] < 0) { VECTOR(*membership)[i] = 0; } else { - VECTOR(*membership)[i] = floor(VECTOR(*initial)[i]) + 1; + VECTOR(*membership)[i] = VECTOR(*initial)[i] + 1; } } if (fixed) { @@ -163,13 +192,13 @@ int igraph_community_label_propagation(const igraph_t *graph, /* 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); + fixed_copy = IGRAPH_CALLOC(1, igraph_vector_bool_t); if (fixed_copy == 0) { - IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Failed to copy 'fixed' vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, fixed_copy); - IGRAPH_CHECK(igraph_vector_bool_copy(fixed_copy, fixed)); + IGRAPH_CHECK(igraph_vector_bool_init_copy(fixed_copy, fixed)); IGRAPH_FINALLY(igraph_vector_bool_destroy, fixed_copy); } @@ -181,7 +210,7 @@ int igraph_community_label_propagation(const igraph_t *graph, } } - i = (long int) igraph_vector_max(membership); + i = igraph_vector_int_max(membership); if (i > no_of_nodes) { IGRAPH_ERROR("Elements of the initial labeling vector must be between 0 and |V|-1.", IGRAPH_EINVAL); } @@ -191,28 +220,30 @@ int igraph_community_label_propagation(const igraph_t *graph, } } + reversed_mode = IGRAPH_REVERSE_MODE(mode); + /* 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_CHECK(igraph_inclist_init(graph, &il, reversed_mode, 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_CHECK(igraph_adjlist_init(graph, &al, reversed_mode, 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&dominant_labels, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nonzero_labels, 0); + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_VECTOR_INT_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; @@ -220,107 +251,106 @@ int igraph_community_label_propagation(const igraph_t *graph, } } } else { - IGRAPH_CHECK(igraph_vector_init_seq(&node_order, 0, no_of_nodes - 1)); - IGRAPH_FINALLY(igraph_vector_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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; + control_iteration = true; + running = true; while (running) { - long int v1, num_neis; + igraph_integer_t 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; + /* If we are in the control iteration, we expect in the beginning of + the iteration that 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 = false; } else { /* Shuffle the node ordering vector if we are in the label updating iteration */ - IGRAPH_CHECK(igraph_vector_shuffle(&node_order)); + IGRAPH_CHECK(igraph_vector_int_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]; + v1 = VECTOR(node_order)[i]; /* Count the weights corresponding to different labels */ - igraph_vector_clear(&dominant_labels); - igraph_vector_clear(&nonzero_labels); + igraph_vector_int_clear(&dominant_labels); + igraph_vector_int_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) ]; + k = VECTOR(*membership)[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]]; + VECTOR(label_counters)[k] += VECTOR(*weights)[VECTOR(*ineis)[j]]; if (was_zero && VECTOR(label_counters)[k] != 0) { /* counter just became nonzero */ - IGRAPH_CHECK(igraph_vector_push_back(&nonzero_labels, k)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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]]; + k = VECTOR(*membership)[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)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&dominant_labels, k)); } } } - if (igraph_vector_size(&dominant_labels) > 0) { + if (igraph_vector_int_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) { + if (VECTOR(label_counters)[VECTOR(*membership)[v1]] != max_count) { /* Nope, we need at least one more iteration */ - running = 1; + running = true; } } 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]; + k = RNG_INTEGER(0, igraph_vector_int_size(&dominant_labels) - 1); + VECTOR(*membership)[v1] = VECTOR(dominant_labels)[k]; } } /* Clear the nonzero elements in label_counters */ - num_neis = igraph_vector_size(&nonzero_labels); + num_neis = igraph_vector_int_size(&nonzero_labels); for (j = 0; j < num_neis; j++) { - VECTOR(label_counters)[(long int)VECTOR(nonzero_labels)[j]] = 0; + VECTOR(label_counters)[VECTOR(nonzero_labels)[j]] = 0; } } RNG_END(); @@ -337,12 +367,12 @@ int igraph_community_label_propagation(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); /* Shift back the membership vector, permute labels in increasing order */ - /* We recycle label_counters here :) */ + /* We recycle label_counters here :) and use it as an integer vector from now on */ igraph_vector_fill(&label_counters, -1); j = 0; - unlabelled_left = 0; + unlabelled_left = false; for (i = 0; i < no_of_nodes; i++) { - k = (long)VECTOR(*membership)[i] - 1; + k = VECTOR(*membership)[i] - 1; if (k >= 0) { if (VECTOR(label_counters)[k] == -1) { /* We have seen this label for the first time */ @@ -350,11 +380,11 @@ int igraph_community_label_propagation(const igraph_t *graph, k = j; j++; } else { - k = (long int) VECTOR(label_counters)[k]; + k = (igraph_integer_t) VECTOR(label_counters)[k]; } } else { /* This is an unlabeled vertex */ - unlabelled_left = 1; + unlabelled_left = true; } VECTOR(*membership)[i] = k; } @@ -371,39 +401,39 @@ int igraph_community_label_propagation(const igraph_t *graph, * with the same label. */ if (unlabelled_left) { - igraph_dqueue_t q; - igraph_vector_t neis; + igraph_dqueue_int_t q; + igraph_vector_int_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_CHECK(igraph_vector_int_shuffle(&node_order)); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_init(&q, 0)); - IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); for (i=0; i < no_of_nodes; ++i) { - long int v = VECTOR(node_order)[i]; + igraph_integer_t 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); + igraph_dqueue_int_push(&q, v); VECTOR(*membership)[v] = j; - while (!igraph_dqueue_empty(&q)) { - long int ni, num_neis; - long int actnode = igraph_dqueue_pop(&q); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t ni, num_neis; + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_OUT)); - num_neis = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + num_neis = igraph_vector_int_size(&neis); for (ni = 0; ni < num_neis; ++ni) { - long int neighbor = VECTOR(neis)[ni]; + igraph_integer_t neighbor = VECTOR(neis)[ni]; if (IS_UNLABELLED(neighbor)) { VECTOR(*membership)[neighbor] = j; - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); } } } @@ -411,26 +441,20 @@ int igraph_community_label_propagation(const igraph_t *graph, } } - igraph_vector_destroy(&neis); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_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_int_destroy(&node_order); igraph_vector_destroy(&label_counters); - igraph_vector_destroy(&dominant_labels); - igraph_vector_destroy(&nonzero_labels); + igraph_vector_int_destroy(&dominant_labels); + igraph_vector_int_destroy(&nonzero_labels); IGRAPH_FINALLY_CLEAN(4); if (fixed != fixed_copy) { igraph_vector_bool_destroy(fixed_copy); - igraph_Free(fixed_copy); + IGRAPH_FREE(fixed_copy); IGRAPH_FINALLY_CLEAN(2); } diff --git a/src/vendor/cigraph/src/community/leading_eigenvector.c b/src/vendor/cigraph/src/community/leading_eigenvector.c index ca7c2932295..19863746cc7 100644 --- a/src/vendor/cigraph/src/community/leading_eigenvector.c +++ b/src/vendor/cigraph/src/community/leading_eigenvector.c @@ -28,13 +28,14 @@ #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_iterators.h" -#include "igraph_memory.h" #include "igraph_random.h" #include "igraph_statusbar.h" #include "igraph_structural.h" #include "core/interruption.h" +#include + /** * \section about_leading_eigenvector_methods * @@ -78,47 +79,48 @@ */ typedef struct igraph_i_community_leading_eigenvector_data_t { - igraph_vector_t *idx; - igraph_vector_t *idx2; + igraph_vector_int_t *idx; + igraph_vector_int_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; + igraph_integer_t no_of_edges; + igraph_vector_int_t *mymembership; + igraph_integer_t 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) { +static igraph_error_t 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_integer_t j, k, nlen, size = n; + igraph_vector_int_t *idx = data->idx; + igraph_vector_int_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; + igraph_integer_t no_of_edges = data->no_of_edges; + igraph_vector_int_t *mymembership = data->mymembership; + igraph_integer_t comm = data->comm; /* Ax */ for (j = 0; j < size; j++) { - long int oldid = (long int) VECTOR(*idx)[j]; + igraph_integer_t oldid = 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]; + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; if (neimemb == comm) { - to[j] += from[ (long int) VECTOR(*idx2)[nei] ]; + to[j] += from[ VECTOR(*idx2)[nei] ]; VECTOR(*tmp)[j] += 1; } } @@ -127,9 +129,9 @@ static int igraph_i_community_leading_eigenvector(igraph_real_t *to, /* 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_integer_t oldid = VECTOR(*idx)[j]; igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, oldid); - long int degree = igraph_vector_int_size(neis); + igraph_integer_t degree = igraph_vector_int_size(neis); ktx += from[j] * degree; ktx2 += degree; } @@ -138,7 +140,7 @@ static int igraph_i_community_leading_eigenvector(igraph_real_t *to, /* Now calculate Bx */ for (j = 0; j < size; j++) { - long int oldid = (long int) VECTOR(*idx)[j]; + igraph_integer_t oldid = 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; @@ -150,22 +152,23 @@ static int igraph_i_community_leading_eigenvector(igraph_real_t *to, to[j] -= VECTOR(*tmp)[j] * from[j]; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, - const igraph_real_t *from, - int n, void *extra) { +static igraph_error_t 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_integer_t j, k, nlen, size = n; + igraph_vector_int_t *idx = data->idx; + igraph_vector_int_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; + igraph_vector_int_t *mymembership = data->mymembership; + igraph_integer_t comm = data->comm; const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_t *strength = data->strength; @@ -173,18 +176,18 @@ static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, /* Ax */ for (j = 0; j < size; j++) { - long int oldid = (long int) VECTOR(*idx)[j]; + igraph_integer_t oldid = 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_integer_t edge = 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]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, oldid); + igraph_integer_t neimemb = VECTOR(*mymembership)[nei]; if (neimemb == comm) { - to[j] += from[ (long int) VECTOR(*idx2)[nei] ] * w; + to[j] += from[ VECTOR(*idx2)[nei] ] * w; VECTOR(*tmp)[j] += w; } } @@ -193,7 +196,7 @@ static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, /* k^Tx/2m */ ktx = 0.0; ktx2 = 0.0; for (j = 0; j < size; j++) { - long int oldid = (long int) VECTOR(*idx)[j]; + igraph_integer_t oldid = VECTOR(*idx)[j]; igraph_real_t str = VECTOR(*strength)[oldid]; ktx += from[j] * str; ktx2 += str; @@ -203,7 +206,7 @@ static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, /* Bx */ for (j = 0; j < size; j++) { - long int oldid = (long int) VECTOR(*idx)[j]; + igraph_integer_t oldid = VECTOR(*idx)[j]; igraph_real_t str = VECTOR(*strength)[oldid]; to[j] = to[j] - ktx * str; VECTOR(*tmp)[j] = VECTOR(*tmp)[j] - ktx2 * str; @@ -214,22 +217,11 @@ static int igraph_i_community_leading_eigenvector_weighted(igraph_real_t *to, 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(VECTOR(*ptr)[i]); - } - } + return IGRAPH_SUCCESS; } static void igraph_i_error_handler_none(const char *reason, const char *file, - int line, int igraph_errno) { + int line, igraph_error_t igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); @@ -277,8 +269,8 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * 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 options The options for ARPACK. Supply \c NULL here to use the + * defaults. \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. @@ -290,12 +282,9 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * 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. + * that are calculated in each step of the algorithm are stored here, + * in a list of vectors. Each eigenvector is stored in an + * \ref igraph_vector_t object. * \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: @@ -321,10 +310,12 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * \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. + * calculation. If the callback returns \c IGRAPH_STOP, then the + * community finding algorithm stops. If it returns \c IGRAPH_SUCCESS, + * the algorithm continues normally. Any other return value is considered + * an igraph error code and will terminete the algorithm with the same + * error code. 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. @@ -337,40 +328,46 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * |E| the number of edges, steps the number of splits * performed. */ -int igraph_community_leading_eigenvector(const igraph_t *graph, +igraph_error_t igraph_community_leading_eigenvector( + const igraph_t *graph, const igraph_vector_t *weights, - igraph_matrix_t *merges, - igraph_vector_t *membership, + igraph_matrix_int_t *merges, + igraph_vector_int_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_list_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_dqueue_int_t tosplit; + igraph_vector_int_t idx, idx2; + igraph_vector_t mymerges; igraph_vector_t strength, tmp; igraph_vector_t start_vec; - long int staken = 0; + igraph_integer_t 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_integer_t i, j, k, l; + igraph_integer_t communities; + igraph_vector_int_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; + weights ? igraph_i_community_leading_eigenvector_weighted : + igraph_i_community_leading_eigenvector; igraph_real_t sumweights = 0.0; + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for ARPACK", IGRAPH_EOVERFLOW); + } + if (weights && no_of_edges != igraph_vector_size(weights)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } @@ -381,12 +378,12 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } if (start && membership && - igraph_vector_size(membership) != no_of_nodes) { + igraph_vector_int_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) { + if (start && membership && igraph_vector_int_max(membership) >= no_of_nodes) { IGRAPH_WARNING("Too many communities in membership start vector"); } @@ -395,59 +392,53 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } if (steps < 0 || steps > no_of_nodes - 1) { - steps = (igraph_integer_t) no_of_nodes - 1; + steps = no_of_nodes > 0 ? no_of_nodes - 1 : 0; } if (!membership) { mymembership = &vmembership; - IGRAPH_VECTOR_INIT_FINALLY(mymembership, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(mymembership, 0); } IGRAPH_VECTOR_INIT_FINALLY(&mymerges, 0); IGRAPH_CHECK(igraph_vector_reserve(&mymerges, steps * 2)); - IGRAPH_VECTOR_INIT_FINALLY(&idx, 0); + IGRAPH_VECTOR_INT_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_vector_list_clear(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)); + IGRAPH_CHECK(igraph_connected_components(graph, mymembership, &idx, 0, IGRAPH_WEAK)); + communities = igraph_vector_int_size(&idx); 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)); + communities = igraph_vector_int_max(mymembership) + 1; 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); + IGRAPH_CHECK(igraph_vector_int_resize(&idx, communities)); + igraph_vector_int_null(&idx); for (i = 0; i < no_of_nodes; i++) { - int t = (int) VECTOR(*mymembership)[i]; + igraph_integer_t t = VECTOR(*mymembership)[i]; VECTOR(idx)[t] += 1; } } - IGRAPH_DQUEUE_INIT_FINALLY(&tosplit, 100); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&tosplit, 100); for (i = 0; i < communities; i++) { if (VECTOR(idx)[i] > 2) { - igraph_dqueue_push(&tosplit, i); + IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, i)); } } for (i = 1; i < communities; i++) { @@ -458,15 +449,10 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, 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); + /* There are no eigenvectors associated to these steps because the + * splits were given by the user (or by the components of the graph) + * so we push empty vectors */ + IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, NULL)); } if (history) { IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_SPLIT)); @@ -476,9 +462,9 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, 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); + IGRAPH_CHECK(igraph_vector_int_resize(&idx, no_of_nodes)); + igraph_vector_int_null(&idx); + IGRAPH_VECTOR_INT_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); @@ -491,6 +477,10 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, sumweights = igraph_vector_sum(weights); } + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rssolve */ options->which[0] = 'L'; options->which[1] = 'A'; @@ -512,12 +502,11 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, 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); + while (!igraph_dqueue_int_empty(&tosplit) && staken < steps) { + igraph_integer_t comm = igraph_dqueue_int_pop_back(&tosplit); /* depth first search */ - long int size = 0; + igraph_integer_t size = 0; - IGRAPH_STATUSF(("Trying to split community %li... ", 0, comm)); IGRAPH_ALLOW_INTERRUPTION(); for (i = 0; i < no_of_nodes; i++) { @@ -559,9 +548,9 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_shuffle(&start_vec)); { - int retval; + igraph_error_t retval; igraph_error_handler_t *errh = - igraph_set_error_handler(igraph_i_error_handler_none); + igraph_set_error_handler(igraph_i_error_handler_none); retval = igraph_arpack_rssolve(arpcb1, &extra, options, &storage, /*values=*/ 0, /*vectors=*/ 0); igraph_set_error_handler(errh); if (retval != IGRAPH_SUCCESS && retval != IGRAPH_ARPACK_MAXIT && retval != IGRAPH_ARPACK_NOSHIFT) { @@ -606,11 +595,17 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, if (callback) { igraph_vector_t vv; - int ret; + igraph_error_t ret; + igraph_vector_view(&vv, storage.v, size); - ret = callback(mymembership, comm, storage.d[0], &vv, - arpcb1, &extra, callback_extra); - if (ret) { + IGRAPH_CHECK_CALLBACK( + callback( + mymembership, comm, storage.d[0], &vv, arpcb1, + &extra, callback_extra + ), &ret + ); + + if (ret == IGRAPH_STOP) { break; } } @@ -620,22 +615,16 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } 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); + igraph_vector_t *v; + /* TODO: this would be faster if we had an igraph_vector_list_push_back_new_with_size_hint */ + IGRAPH_CHECK(igraph_vector_list_push_back_new(eigenvectors, &v)); + IGRAPH_CHECK(igraph_vector_resize(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)); @@ -655,7 +644,6 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } } if (l == 0 || l == size) { - IGRAPH_STATUS("no split.\n", 0); if (history) { IGRAPH_CHECK(igraph_vector_push_back(history, IGRAPH_LEVC_HIST_FAILED)); @@ -671,7 +659,6 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, 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)); @@ -681,12 +668,11 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } 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]; + igraph_integer_t oldid = VECTOR(idx)[j]; VECTOR(*mymembership)[oldid] = communities - 1; } } @@ -701,10 +687,10 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, /* Store the resulting communities in the queue if needed */ if (l > 1) { - IGRAPH_CHECK(igraph_dqueue_push(&tosplit, communities - 1)); + IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, communities - 1)); } if (size - l > 1) { - IGRAPH_CHECK(igraph_dqueue_push(&tosplit, comm)); + IGRAPH_CHECK(igraph_dqueue_int_push(&tosplit, comm)); } } @@ -719,23 +705,21 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, igraph_vector_destroy(&strength); IGRAPH_FINALLY_CLEAN(2); } - igraph_dqueue_destroy(&tosplit); + igraph_dqueue_int_destroy(&tosplit); igraph_vector_destroy(&tmp); - igraph_vector_destroy(&idx2); + igraph_vector_int_destroy(&idx2); IGRAPH_FINALLY_CLEAN(3); - IGRAPH_STATUS("Done.\n", 0); - /* reform the mymerges vector */ if (merges) { - igraph_vector_null(&idx); + igraph_vector_int_null(&idx); l = igraph_vector_size(&mymerges); k = communities; j = 0; - IGRAPH_CHECK(igraph_matrix_resize(merges, l / 2, 2)); + IGRAPH_CHECK(igraph_matrix_int_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]; + igraph_integer_t from = VECTOR(mymerges)[i - 1]; + igraph_integer_t to = 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) { @@ -749,25 +733,22 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, } } - if (eigenvectors) { - IGRAPH_FINALLY_CLEAN(1); - } - igraph_vector_destroy(&idx); + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_modularity(graph, mymembership, weights, + /* resolution */ 1, + /* only undirected */ 0, modularity)); } if (!membership) { - igraph_vector_destroy(mymembership); + igraph_vector_int_destroy(mymembership); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -792,37 +773,38 @@ int igraph_community_leading_eigenvector(const igraph_t *graph, * * 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) { +igraph_error_t igraph_le_community_to_membership(const igraph_matrix_int_t *merges, + igraph_integer_t steps, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize) { - long int no_of_nodes = igraph_vector_size(membership); - igraph_vector_t fake_memb; - long int components, i; + igraph_integer_t no_of_nodes = igraph_vector_int_size(membership); + igraph_vector_int_t fake_memb; + igraph_integer_t components, i; if (no_of_nodes > 0) { - components = (long int) igraph_vector_max(membership) + 1; + components = igraph_vector_int_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); + IGRAPH_ERRORF("Invalid membership vector: number of components (%" IGRAPH_PRId ") must " + "not be greater than the number of nodes (%" IGRAPH_PRId ").", + 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_ERRORF("Number of steps (%" IGRAPH_PRId ") must be smaller than number of components (%" IGRAPH_PRId ").", IGRAPH_EINVAL, steps, components); } - IGRAPH_VECTOR_INIT_FINALLY(&fake_memb, components); + IGRAPH_VECTOR_INT_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]); + IGRAPH_ERRORF("Invalid membership vector, negative ID found: %" IGRAPH_PRId ".", IGRAPH_EINVAL, VECTOR(*membership)[i]); } - VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ] += 1; + VECTOR(fake_memb)[ VECTOR(*membership)[i] ] += 1; } for (i = 0; i < components; i++) { if (VECTOR(fake_memb)[i] == 0) { @@ -833,26 +815,24 @@ int igraph_le_community_to_membership(const igraph_matrix_t *merges, } } - IGRAPH_CHECK(igraph_community_to_membership(merges, (igraph_integer_t) - components, steps, - &fake_memb, 0)); + IGRAPH_CHECK(igraph_community_to_membership(merges, 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); + IGRAPH_CHECK(igraph_vector_int_resize(csize, components - steps)); + igraph_vector_int_null(csize); } for (i = 0; i < no_of_nodes; i++) { - VECTOR(*membership)[i] = VECTOR(fake_memb)[ (long int) VECTOR(*membership)[i] ]; + VECTOR(*membership)[i] = VECTOR(fake_memb)[ VECTOR(*membership)[i] ]; if (csize) { - VECTOR(*csize)[ (long int) VECTOR(*membership)[i] ] += 1; + VECTOR(*csize)[ VECTOR(*membership)[i] ] += 1; } } - igraph_vector_destroy(&fake_memb); + igraph_vector_int_destroy(&fake_memb); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/leiden.c b/src/vendor/cigraph/src/community/leiden.c index 229b6d9e7de..e9aa9ada241 100644 --- a/src/vendor/cigraph/src/community/leiden.c +++ b/src/vendor/cigraph/src/community/leiden.c @@ -25,13 +25,14 @@ #include "igraph_community.h" #include "igraph_adjlist.h" +#include "igraph_constructors.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 "igraph_vector_list.h" #include "core/interruption.h" @@ -39,6 +40,7 @@ * * This function considers each node and greedily moves it to a neighboring * community that maximizes the improvement in the quality of a partition. + * Only moves that strictly improve the quality are considered. * * 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 @@ -49,101 +51,96 @@ * and is updated in-place. * */ -static int igraph_i_community_leiden_fastmovenodes( +static igraph_error_t 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_vector_int_t *membership, + igraph_bool_t *changed) { - igraph_dqueue_t unstable_nodes; + igraph_dqueue_int_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_t cluster_weights, edge_weights_per_cluster; + igraph_vector_int_t neighbor_clusters; + igraph_vector_int_t node_order; igraph_vector_int_t nb_nodes_per_cluster; - igraph_stack_t empty_clusters; - long int i, j, c, nb_neigh_clusters; + igraph_stack_int_t empty_clusters; + igraph_integer_t 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_VECTOR_BOOL_INIT_FINALLY(&node_is_stable, n); - IGRAPH_CHECK(igraph_dqueue_init(&unstable_nodes, n)); - IGRAPH_FINALLY(igraph_dqueue_destroy, &unstable_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&unstable_nodes, n); /* 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)); + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, n)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); /* Add to the queue */ for (i = 0; i < n; i++) { - igraph_dqueue_push(&unstable_nodes, (long int)VECTOR(node_order)[i]); + IGRAPH_CHECK(igraph_dqueue_int_push(&unstable_nodes, 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); + IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); for (i = 0; i < n; i++) { - c = (long int)VECTOR(*membership)[i]; + c = 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); + IGRAPH_STACK_INT_INIT_FINALLY(&empty_clusters, n); for (c = 0; c < n; c++) if (VECTOR(nb_nodes_per_cluster)[c] == 0) { - igraph_stack_push(&empty_clusters, c); + IGRAPH_CHECK(igraph_stack_int_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); + IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); /* 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); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); /* 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; + while (!igraph_dqueue_int_empty(&unstable_nodes)) { + igraph_integer_t v = igraph_dqueue_int_pop(&unstable_nodes); + igraph_integer_t best_cluster, current_cluster = VECTOR(*membership)[v]; + igraph_integer_t degree; 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)); + IGRAPH_CHECK(igraph_stack_int_push(&empty_clusters, current_cluster)); } /* Find out neighboring clusters */ - c = (long int) igraph_stack_top(&empty_clusters); + c = igraph_stack_int_top(&empty_clusters); VECTOR(neighbor_clusters)[0] = c; - VECTOR(neighbor_cluster_added)[c] = 1; + VECTOR(neighbor_cluster_added)[c] = true; 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); + igraph_integer_t e = VECTOR(*edges)[i]; + igraph_integer_t u = 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_cluster_added)[c] = true; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -156,34 +153,38 @@ static int igraph_i_community_leiden_fastmovenodes( 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; + /* Only consider strictly improving moves. + * Note that this is important in considering convergence. + */ if (diff > max_diff) { best_cluster = c; max_diff = diff; } VECTOR(edge_weights_per_cluster)[c] = 0.0; - VECTOR(neighbor_cluster_added)[c] = 0; + VECTOR(neighbor_cluster_added)[c] = false; } /* 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); + if (best_cluster == igraph_stack_int_top(&empty_clusters)) { + igraph_stack_int_pop(&empty_clusters); } /* Mark node as stable */ - VECTOR(node_is_stable)[v] = 1; + VECTOR(node_is_stable)[v] = true; /* Add stable neighbours that are not part of the new cluster to the queue */ if (best_cluster != current_cluster) { + *changed = true; 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); + igraph_integer_t e = VECTOR(*edges)[i]; + igraph_integer_t u = 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; + IGRAPH_CHECK(igraph_dqueue_int_push(&unstable_nodes, u)); + VECTOR(node_is_stable)[u] = false; } } } @@ -197,16 +198,15 @@ static int igraph_i_community_leiden_fastmovenodes( IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters)); - igraph_vector_destroy(&neighbor_clusters); + igraph_vector_int_destroy(&neighbor_clusters); igraph_vector_bool_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weights_per_cluster); - igraph_stack_destroy(&empty_clusters); + igraph_stack_int_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_int_destroy(&node_order); + igraph_dqueue_int_destroy(&unstable_nodes); igraph_vector_bool_destroy(&node_is_stable); - IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; @@ -221,39 +221,37 @@ static int igraph_i_community_leiden_fastmovenodes( * 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, +static igraph_error_t igraph_i_community_leiden_clean_refined_membership( + const igraph_vector_int_t* node_subset, + igraph_vector_int_t *refined_membership, igraph_integer_t* nb_refined_clusters) { - long int i, n = igraph_vector_size(node_subset); - igraph_vector_t new_cluster; + igraph_integer_t i, n = igraph_vector_int_size(node_subset); + igraph_vector_int_t new_cluster; - IGRAPH_CHECK(igraph_vector_init(&new_cluster, n)); - IGRAPH_FINALLY(igraph_vector_destroy, &new_cluster); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_cluster, n); /* 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]; + igraph_integer_t v = VECTOR(*node_subset)[i]; + igraph_integer_t c = VECTOR(*refined_membership)[v]; if (VECTOR(new_cluster)[c] == 0) { - VECTOR(new_cluster)[c] = (igraph_real_t)(*nb_refined_clusters); + VECTOR(new_cluster)[c] = *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]; + igraph_integer_t v = VECTOR(*node_subset)[i]; + igraph_integer_t c = 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_vector_int_destroy(&new_cluster); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -289,40 +287,38 @@ static int igraph_i_community_leiden_clean_refined_membership( * igraph_i_community_leiden_clean_refined_membership for more information about * this aspect. */ -static int igraph_i_community_leiden_mergenodes( +static igraph_error_t 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_vector_int_t *node_subset, + const igraph_vector_int_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_int_t *refined_membership) { + igraph_vector_int_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_integer_t n = igraph_vector_int_size(node_subset); + igraph_vector_t cluster_weights, cum_trans_diff, edge_weights_per_cluster, external_edge_weight_per_cluster_in_subset; + igraph_vector_int_t neighbor_clusters; igraph_vector_int_t *edges, nb_nodes_per_cluster; - long int i, j, degree, nb_neigh_clusters; + igraph_integer_t i, j, degree, nb_neigh_clusters; /* Initialize cluster weights */ - IGRAPH_CHECK(igraph_vector_init(&cluster_weights, n)); - IGRAPH_FINALLY(igraph_vector_destroy, &cluster_weights); + IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); /* 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nb_nodes_per_cluster, n); /* 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); + IGRAPH_VECTOR_INIT_FINALLY(&external_edge_weight_per_cluster_in_subset, n); /* Initialize administration for a singleton partition */ for (i = 0; i < n; i++) { - long int v = (long int) VECTOR(*node_subset)[i]; + igraph_integer_t v = VECTOR(*node_subset)[i]; VECTOR(*refined_membership)[v] = i; VECTOR(cluster_weights)[i] += VECTOR(*node_weights)[v]; VECTOR(nb_nodes_per_cluster)[i] += 1; @@ -332,8 +328,8 @@ static int igraph_i_community_leiden_mergenodes( 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); + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = 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]; } @@ -341,33 +337,28 @@ static int igraph_i_community_leiden_mergenodes( } /* 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)); + IGRAPH_CHECK(igraph_vector_int_init_copy(&node_order, node_subset)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&non_singleton_cluster, n); /* 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); + IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); /* 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); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); /* Initialize cumulative transformed difference */ - IGRAPH_CHECK(igraph_vector_init(&cum_trans_diff, n)); - IGRAPH_FINALLY(igraph_vector_destroy, &cum_trans_diff); + IGRAPH_VECTOR_INIT_FINALLY(&cum_trans_diff, n); 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]; + igraph_integer_t v = VECTOR(node_order)[i]; + igraph_integer_t chosen_cluster, best_cluster, current_cluster = VECTOR(*refined_membership)[v]; if (!VECTOR(non_singleton_cluster)[current_cluster] && (VECTOR(external_edge_weight_per_cluster_in_subset)[current_cluster] >= @@ -383,15 +374,15 @@ static int igraph_i_community_leiden_mergenodes( /* Also add current cluster to ensure it can be chosen. */ VECTOR(neighbor_clusters)[0] = current_cluster; - VECTOR(neighbor_cluster_added)[current_cluster] = 1; + VECTOR(neighbor_cluster_added)[current_cluster] = true; 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); + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); if (u != v && VECTOR(*membership)[u] == cluster_subset) { - long int c = VECTOR(*refined_membership)[u]; + igraph_integer_t c = VECTOR(*refined_membership)[u]; if (!VECTOR(neighbor_cluster_added)[c]) { - VECTOR(neighbor_cluster_added)[c] = 1; + VECTOR(neighbor_cluster_added)[c] = true; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -403,7 +394,7 @@ static int igraph_i_community_leiden_mergenodes( 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]; + igraph_integer_t c = 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; @@ -421,7 +412,7 @@ static int igraph_i_community_leiden_mergenodes( VECTOR(cum_trans_diff)[j] = total_cum_trans_diff; VECTOR(edge_weights_per_cluster)[c] = 0.0; - VECTOR(neighbor_cluster_added)[c] = 0; + VECTOR(neighbor_cluster_added)[c] = false; } /* Determine the neighboring cluster to which the currently selected node @@ -429,7 +420,7 @@ static int igraph_i_community_leiden_mergenodes( */ if (total_cum_trans_diff < IGRAPH_INFINITY) { igraph_real_t r = RNG_UNIF(0, total_cum_trans_diff); - long int chosen_idx; + igraph_integer_t chosen_idx; igraph_vector_binsearch_slice(&cum_trans_diff, r, &chosen_idx, 0, nb_neigh_clusters); chosen_cluster = VECTOR(neighbor_clusters)[chosen_idx]; } else { @@ -441,8 +432,8 @@ static int igraph_i_community_leiden_mergenodes( 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); + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t u = 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]; @@ -456,7 +447,7 @@ static int igraph_i_community_leiden_mergenodes( if (chosen_cluster != current_cluster) { VECTOR(*refined_membership)[v] = chosen_cluster; - VECTOR(non_singleton_cluster)[chosen_cluster] = 1; + VECTOR(non_singleton_cluster)[chosen_cluster] = true; } } /* end if singleton and may be merged */ } @@ -466,15 +457,14 @@ static int igraph_i_community_leiden_mergenodes( 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_int_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_int_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; @@ -482,33 +472,21 @@ static int igraph_i_community_leiden_mergenodes( /* 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. + * It is assumed that the incoming list of integer vectors is already sized + * appropriately (i.e. it has at least as many items as the number of clusters + * in the membership vector), and that each item in the list of integer vectors + * is empty. */ -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]; +static igraph_error_t igraph_i_community_get_clusters(const igraph_vector_int_t *membership, igraph_vector_int_list_t *clusters) { + igraph_integer_t n = igraph_vector_int_size(membership); + igraph_vector_int_t *cluster; - /* 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; - } + for (igraph_integer_t i = 0; i < n; i++) { + /* Get cluster for node i */ + cluster = igraph_vector_int_list_get_ptr(clusters, VECTOR(*membership)[i]); /* Add node i to cluster vector */ - IGRAPH_CHECK(igraph_vector_push_back(cluster, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(cluster, i)); } return IGRAPH_SUCCESS; @@ -528,65 +506,59 @@ static int igraph_i_community_get_clusters(const igraph_vector_t *membership, ig * aggregated_membership are all expected to be initialized. * */ -static int igraph_i_community_leiden_aggregate( +static igraph_error_t 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; + const igraph_vector_int_t *membership, const igraph_vector_int_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_int_t *aggregated_membership) { + igraph_vector_int_t aggregated_edges; + igraph_vector_t edge_weight_to_cluster; + igraph_vector_int_list_t refined_clusters; igraph_vector_int_t *incident_edges; - igraph_vector_t neighbor_clusters; + igraph_vector_int_t neighbor_clusters; igraph_vector_bool_t neighbor_cluster_added; - long int i, j, c, degree, nb_neigh_clusters; + igraph_integer_t 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_VECTOR_INT_LIST_INIT_FINALLY(&refined_clusters, nb_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&aggregated_edges, 0); /* 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 */ + /* 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_int_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); + IGRAPH_VECTOR_INIT_FINALLY(&edge_weight_to_cluster, nb_refined_clusters); /* 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); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, nb_refined_clusters); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, nb_refined_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; + igraph_vector_int_t* refined_cluster = igraph_vector_int_list_get_ptr(&refined_clusters, c); + igraph_integer_t n_c = igraph_vector_int_size(refined_cluster); + igraph_integer_t 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]; + v = 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]; + igraph_integer_t e = VECTOR(*incident_edges)[j]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + igraph_integer_t c2 = VECTOR(*refined_membership)[u]; if (c2 > c) { if (!VECTOR(neighbor_cluster_added)[c2]) { - VECTOR(neighbor_cluster_added)[c2] = 1; + VECTOR(neighbor_cluster_added)[c2] = true; VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c2; } VECTOR(edge_weight_to_cluster)[c2] += VECTOR(*edge_weights)[e]; @@ -598,36 +570,34 @@ static int igraph_i_community_leiden_aggregate( /* 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]; + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&aggregated_edges, c)); + IGRAPH_CHECK(igraph_vector_int_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(neighbor_cluster_added)[c2] = false; } VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v]; } - igraph_vector_destroy(&neighbor_clusters); + igraph_vector_int_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_vector_int_list_destroy(&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_vector_int_destroy(&aggregated_edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -656,14 +626,14 @@ static int igraph_i_community_leiden_aggregate( * weights inside cluster c. This is how the quality is calculated in practice. * */ -static int igraph_i_community_leiden_quality( +static igraph_error_t 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, + const igraph_vector_int_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);; + igraph_integer_t i, c, n = igraph_vcount(graph); *quality = 0.0; @@ -672,11 +642,11 @@ static int igraph_i_community_leiden_quality( 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)); + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, e), to = IGRAPH_TO(graph, e); total_edge_weight += VECTOR(*edge_weights)[e]; /* We add the internal edge weights */ - if (VECTOR(*membership)[(long int) from] == VECTOR(*membership)[(long int) to]) { + if (VECTOR(*membership)[from] == VECTOR(*membership)[to]) { *quality += 2 * VECTOR(*edge_weights)[e]; } IGRAPH_EIT_NEXT(eit); @@ -685,8 +655,7 @@ static int igraph_i_community_leiden_quality( 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); + IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); for (i = 0; i < n; i++) { c = VECTOR(*membership)[i]; VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i]; @@ -712,64 +681,56 @@ static int igraph_i_community_leiden_quality( * refined partition, using the non-refined partition to create an initial * partition for the aggregate network. */ -static int igraph_i_community_leiden( +static igraph_error_t 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_vector_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality, + igraph_bool_t *changed) { igraph_integer_t nb_refined_clusters; - long int i, c, n = igraph_vcount(graph); + igraph_integer_t 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_t aggregated_edge_weights, aggregated_node_weights; + igraph_vector_int_t aggregated_membership; + igraph_vector_t *i_edge_weights, *i_node_weights; + igraph_vector_int_t *i_membership; + igraph_vector_t tmp_edge_weights, tmp_node_weights; + igraph_vector_int_t tmp_membership; + igraph_vector_int_t refined_membership; igraph_vector_int_t aggregate_node; - igraph_vector_ptr_t clusters; + igraph_vector_int_list_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); + IGRAPH_VECTOR_INIT_FINALLY(&tmp_edge_weights, 0); + IGRAPH_VECTOR_INIT_FINALLY(&tmp_node_weights, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp_membership, 0); /* 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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&clusters, n); /* Initialize aggregate nodes, which initially is identical to simply the * nodes in the graph. */ - IGRAPH_CHECK(igraph_vector_int_init(&aggregate_node, n)); + IGRAPH_CHECK(igraph_vector_int_init_range(&aggregate_node, 0, 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&refined_membership, 0); /* 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); + IGRAPH_VECTOR_INIT_FINALLY(&aggregated_edge_weights, 0); /* Initialize aggregated node weights */ - IGRAPH_CHECK(igraph_vector_init(&aggregated_node_weights, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_node_weights); + IGRAPH_VECTOR_INIT_FINALLY(&aggregated_node_weights, 0); /* Initialize aggregated membership */ - IGRAPH_CHECK(igraph_vector_init(&aggregated_membership, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, &aggregated_membership); + IGRAPH_VECTOR_INT_INIT_FINALLY(&aggregated_membership, 0); /* Set actual graph, weights and membership to be used. */ i_graph = (igraph_t*)graph; @@ -782,9 +743,11 @@ static int igraph_i_community_leiden( IGRAPH_CHECK(igraph_reindex_membership(i_membership, NULL, nb_clusters)); if (*nb_clusters > n) { - IGRAPH_ERROR("Too many communities in membership vector", IGRAPH_EINVAL); + IGRAPH_ERROR("Too many communities in membership vector.", IGRAPH_EINVAL); } + /* We start out with no changes, whenever a node is moved, this will be set to true. */ + *changed = false; do { /* Get incidence list for fast iteration */ @@ -797,7 +760,8 @@ static int igraph_i_community_leiden( i_edge_weights, i_node_weights, resolution_parameter, nb_clusters, - i_membership)); + i_membership, + changed)); /* We only continue clustering if not all clusters are represented by a * single node yet @@ -808,7 +772,7 @@ static int igraph_i_community_leiden( /* Set original membership */ if (level > 0) { for (i = 0; i < n; i++) { - long int v_aggregate = VECTOR(aggregate_node)[i]; + igraph_integer_t v_aggregate = VECTOR(aggregate_node)[i]; VECTOR(*membership)[i] = VECTOR(*i_membership)[v_aggregate]; } } @@ -817,12 +781,12 @@ static int igraph_i_community_leiden( 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))); + IGRAPH_CHECK(igraph_vector_int_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_vector_int_t* cluster = igraph_vector_int_list_get_ptr(&clusters, c); IGRAPH_CHECK(igraph_i_community_leiden_mergenodes(i_graph, &edges_per_node, i_edge_weights, i_node_weights, @@ -830,13 +794,13 @@ static int igraph_i_community_leiden( resolution_parameter, beta, &nb_refined_clusters, &refined_membership)); /* Empty cluster */ - igraph_vector_clear(cluster); + igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_update(&refined_membership, i_membership)); nb_refined_clusters = *nb_clusters; } @@ -845,7 +809,7 @@ static int igraph_i_community_leiden( /* 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]; + VECTOR(aggregate_node)[i] = VECTOR(refined_membership)[v_aggregate]; } IGRAPH_CHECK(igraph_i_community_leiden_aggregate( @@ -868,7 +832,7 @@ static int igraph_i_community_leiden( /* 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)); + IGRAPH_CHECK(igraph_vector_int_update(i_membership, &tmp_membership)); level += 1; } @@ -879,17 +843,17 @@ static int igraph_i_community_leiden( } while (continue_clustering); /* Free aggregated graph and associated vectors */ - igraph_vector_destroy(&aggregated_membership); + igraph_vector_int_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(&refined_membership); igraph_vector_int_destroy(&aggregate_node); - igraph_vector_ptr_destroy_all(&clusters); - igraph_vector_destroy(&tmp_membership); + igraph_vector_int_list_destroy(&clusters); + igraph_vector_int_destroy(&tmp_membership); igraph_vector_destroy(&tmp_node_weights); igraph_vector_destroy(&tmp_edge_weights); IGRAPH_FINALLY_CLEAN(6); @@ -919,15 +883,16 @@ static int igraph_i_community_leiden( * 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 + * 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). + * Leiden algorithm, only nodes whose neighborhood has changed are visited. Only + * moves that strictly improve the quality function are made. 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 @@ -935,9 +900,12 @@ static int igraph_i_community_leiden( * 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). + * assigned. Note that even if a single iteration did not result in any change, + * it is still possible that a subsequent iteration might find some + * improvement. Each iteration explores different subsets of nodes to consider + * for moving from one cluster to another. 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 @@ -968,11 +936,14 @@ static int igraph_i_community_leiden( * \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 n_iterations Iterate the core Leiden algorithm for the indicated number + * of times. If this is a negative number, it will continue iterating until + * an iteration did not change the clustering. * \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. + * using \ref igraph_vector_int_init_range(). * \param nb_clusters The number of clusters contained in \c membership. * If \c NULL, the number of clusters will not be returned. * \param quality The quality of the partition, in terms of the objective @@ -984,10 +955,11 @@ static int igraph_i_community_leiden( * * \example examples/simple/igraph_community_leiden.c */ -int igraph_community_leiden(const igraph_t *graph, +igraph_error_t 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) { + const igraph_integer_t n_iterations, + igraph_vector_int_t *membership, igraph_integer_t *nb_clusters, igraph_real_t *quality) { igraph_vector_t *i_edge_weights, *i_node_weights; igraph_integer_t i_nb_clusters; igraph_integer_t n = igraph_vcount(graph); @@ -998,35 +970,29 @@ int igraph_community_leiden(const igraph_t *graph, if (start) { if (!membership) { - IGRAPH_ERROR("Cannot start optimization if membership is missing", IGRAPH_EINVAL); + 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); + if (igraph_vector_int_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); + "even when not starting optimization from it.", IGRAPH_EINVAL); - igraph_vector_resize(membership, n); - for (i = 0; i < n; i++) { - VECTOR(*membership)[i] = i; - } + IGRAPH_CHECK(igraph_vector_int_range(membership, 0, n)); } if (igraph_is_directed(graph)) { - IGRAPH_ERROR("Leiden algorithm is only implemented for undirected graphs", IGRAPH_EINVAL); + 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_CHECK_OOM(i_edge_weights, "Leiden algorithm failed, could not allocate memory for edge weights."); 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); @@ -1038,9 +1004,7 @@ int igraph_community_leiden(const igraph_t *graph, /* 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_CHECK_OOM(i_node_weights, "Leiden algorithm failed, could not allocate memory for node weights."); IGRAPH_FINALLY(igraph_free, i_node_weights); IGRAPH_CHECK(igraph_vector_init(i_node_weights, n)); IGRAPH_FINALLY(igraph_vector_destroy, i_node_weights); @@ -1049,10 +1013,21 @@ int igraph_community_leiden(const igraph_t *graph, 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)); + /* Perform actual Leiden algorithm iteratively. We either + * perform a fixed number of iterations, or we perform + * iterations until the quality remains unchanged. Even if + * a single iteration did not change anything, a subsequent + * iteration may still find some improvement. This is because + * each iteration explores different subsets of nodes. + */ + igraph_bool_t changed = false; + for (igraph_integer_t itr = 0; + n_iterations >= 0 ? itr < n_iterations : !changed; + itr++) { + IGRAPH_CHECK(igraph_i_community_leiden(graph, i_edge_weights, i_node_weights, + resolution_parameter, beta, + membership, nb_clusters, quality, &changed)); + } if (!edge_weights) { igraph_vector_destroy(i_edge_weights); diff --git a/src/vendor/cigraph/src/community/louvain.c b/src/vendor/cigraph/src/community/louvain.c index 5ffa184bad1..a6a84c56ef8 100644 --- a/src/vendor/cigraph/src/community/louvain.c +++ b/src/vendor/cigraph/src/community/louvain.c @@ -24,10 +24,10 @@ #include "igraph_community.h" #include "igraph_constructors.h" +#include "igraph_conversion.h" #include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_qsort.h" -#include "igraph_random.h" #include "core/interruption.h" @@ -35,28 +35,26 @@ 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_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_integer_t 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_vector_int_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; + const igraph_i_multilevel_community_list *communities, + const igraph_real_t resolution) { + igraph_real_t result = 0.0; igraph_real_t m = communities->weight_sum; - for (i = 0; i < communities->vertices_no; i++) { + for (igraph_integer_t 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; } @@ -66,52 +64,59 @@ static igraph_real_t igraph_i_multilevel_community_modularity( } typedef struct { - long int from; - long int to; - long int id; + igraph_integer_t from; + igraph_integer_t to; + igraph_integer_t 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; + igraph_integer_t diff; + + diff = ((igraph_i_multilevel_link*)a)->from - ((igraph_i_multilevel_link*)b)->from; + + if (diff < 0) { + return -1; + } else if (diff > 0) { + return 1; } - return (int) (((igraph_i_multilevel_link*)a)->to - - ((igraph_i_multilevel_link*)b)->to); + diff = ((igraph_i_multilevel_link*)a)->to - ((igraph_i_multilevel_link*)b)->to; + + if (diff < 0) { + return -1; + } else if (diff > 0) { + return 1; + } else { + return 0; + } } -/* 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; +/* removes multiple edges and returns new edge IDs for each edge in |E|log|E| */ +static igraph_error_t igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_int_t *eids) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_integer_t l = -1, last_from = -1, last_to = -1; igraph_bool_t directed = igraph_is_directed(graph); - igraph_vector_t edges; + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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_CHECK_OOM(links, "Multi-level community structure detection failed."); 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; + for (igraph_integer_t i = 0; i < ecount; i++) { + links[i].from = IGRAPH_FROM(graph, i); + links[i].to = IGRAPH_TO(graph, i); links[i].id = i; } - igraph_qsort((void*)links, (size_t) ecount, sizeof(igraph_i_multilevel_link), + igraph_qsort(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++) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + for (igraph_integer_t i = 0; i < ecount; i++) { if (links[i].from == last_from && links[i].to == last_to) { VECTOR(*eids)[links[i].id] = l; continue; @@ -120,8 +125,8 @@ static int igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_ last_from = links[i].from; last_to = links[i].to; - igraph_vector_push_back(&edges, last_from); - igraph_vector_push_back(&edges, last_to); + igraph_vector_int_push_back(&edges, last_from); + igraph_vector_int_push_back(&edges, last_to); l++; @@ -134,20 +139,23 @@ static int igraph_i_multilevel_simplify_multiple(igraph_t *graph, igraph_vector_ igraph_destroy(graph); IGRAPH_CHECK(igraph_create(graph, &edges, igraph_vcount(graph), directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } typedef struct { - long int community; + igraph_integer_t 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); + igraph_integer_t diff = ( + ((igraph_i_multilevel_community_link*)a)->community - + ((igraph_i_multilevel_community_link*)b)->community + ); + return diff < 0 ? -1 : diff > 0 ? 1 : 0; } /** @@ -163,36 +171,34 @@ static int igraph_i_multilevel_community_link_cmp(const void *a, const void *b) * communities incident on this vertex and the total weight of edges * pointing to these communities */ -static int igraph_i_multilevel_community_links( +static igraph_error_t 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_integer_t vertex, igraph_vector_int_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) { + igraph_vector_int_t *links_community, igraph_vector_t *links_weight) { - long int i, n, last = -1, c = -1; + igraph_integer_t 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_integer_t to, to_community; + igraph_integer_t community = VECTOR(*(communities->membership))[vertex]; igraph_i_multilevel_community_link *links; *weight_all = *weight_inside = *weight_loop = 0; - igraph_vector_clear(links_community); + igraph_vector_int_clear(links_community); igraph_vector_clear(links_weight); /* Get the list of incident edges */ - igraph_incident(graph, edges, vertex, IGRAPH_ALL); + IGRAPH_CHECK(igraph_incident(graph, edges, vertex, IGRAPH_ALL)); - n = igraph_vector_size(edges); + n = igraph_vector_int_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_CHECK_OOM(links, "Multi-level community structure detection failed."); IGRAPH_FINALLY(igraph_free, links); - for (i = 0; i < n; i++) { - long int eidx = (long int) VECTOR(*edges)[i]; + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t eidx = VECTOR(*edges)[i]; weight = VECTOR(*communities->weights)[eidx]; to = IGRAPH_OTHER(graph, eidx, vertex); @@ -206,7 +212,7 @@ static int igraph_i_multilevel_community_links( continue; } - to_community = (long int)VECTOR(*(communities->membership))[to]; + to_community = VECTOR(*(communities->membership))[to]; if (community == to_community) { *weight_inside += weight; } @@ -220,11 +226,11 @@ static int igraph_i_multilevel_community_links( /* 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++) { + for (igraph_integer_t 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); + IGRAPH_CHECK(igraph_vector_int_push_back(links_community, to_community)); + IGRAPH_CHECK(igraph_vector_push_back(links_weight, links[i].weight)); last = to_community; c++; } else { @@ -235,17 +241,17 @@ static int igraph_i_multilevel_community_links( igraph_free(links); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } 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) { + 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; + resolution * communities->item[community].weight_all * weight_all / communities->weight_sum; } /* Shrinks communities into single vertices, keeping all the edges. @@ -254,58 +260,43 @@ static igraph_real_t igraph_i_multilevel_community_modularity_gain( * 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); +static igraph_error_t igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_int_t *membership) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); - long int i; - igraph_eit_t eit; + IGRAPH_ASSERT(igraph_vector_int_size(membership) == no_of_nodes); 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); + return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); - IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, 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_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ false)); + for (igraph_integer_t i=0; i < 2*no_of_edges; i++) { + VECTOR(edges)[i] = VECTOR(*membership)[ VECTOR(edges)[i] ]; } - 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)); + no_of_nodes = igraph_vector_int_max(membership) + 1; + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup communities * \function igraph_i_community_multilevel_step - * \brief Performs a single step of the multi-level modularity optimization method + * \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, @@ -329,66 +320,48 @@ static int igraph_i_multilevel_shrink(igraph_t *graph, igraph_vector_t *membersh * * Time complexity: in average near linear on sparse graphs. */ -static int igraph_i_community_multilevel_step( +static igraph_error_t igraph_i_community_multilevel_step( igraph_t *graph, igraph_vector_t *weights, - igraph_vector_t *membership, + igraph_vector_int_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_integer_t vcount = igraph_vcount(graph); + igraph_integer_t ecount = igraph_ecount(graph); igraph_real_t q, pass_q; - /* int pass; */ - igraph_bool_t changed = 0; - igraph_vector_t links_community; + /* int pass; // used only for debugging */ + igraph_bool_t changed; + igraph_vector_int_t links_community; igraph_vector_t links_weight; - igraph_vector_t edges; - igraph_vector_t temp_membership; + igraph_vector_int_t edges; + igraph_vector_int_t temp_membership; igraph_i_multilevel_community_list communities; - igraph_vector_t node_order; + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_init_range(&node_order, 0, vcount)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &node_order); + igraph_vector_int_shuffle(&node_order); /* Initialize data structures */ - IGRAPH_VECTOR_INIT_FINALLY(&links_community, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&temp_membership, vcount); + IGRAPH_CHECK(igraph_vector_int_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.weight_sum = 2.0 * 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_CHECK_OOM(communities.item, "Multi-level community structure detection failed."); IGRAPH_FINALLY(igraph_free, communities.item); /* Still initializing the communities data structure */ - for (i = 0; i < vcount; i++) { + for (igraph_integer_t i = 0; i < vcount; i++) { VECTOR(*communities.membership)[i] = i; communities.item[i].size = 1; communities.item[i].weight_inside = 0; @@ -396,16 +369,15 @@ static int igraph_i_community_multilevel_step( } /* Some more initialization :) */ - for (i = 0; i < ecount; i++) { - igraph_integer_t ffrom, fto; + for (igraph_integer_t i = 0; i < ecount; i++) { + igraph_integer_t ffrom = IGRAPH_FROM(graph, i), fto = IGRAPH_TO(graph, i); 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; + communities.item[ffrom].weight_all += weight; + communities.item[fto].weight_all += weight; if (ffrom == fto) { - communities.item[(long int) ffrom].weight_inside += 2 * weight; + communities.item[ffrom].weight_inside += 2 * weight; } } @@ -413,36 +385,36 @@ static int igraph_i_community_multilevel_step( /* pass = 1; */ do { /* Pass begin */ - long int temp_communities_no = communities.communities_no; + igraph_integer_t temp_communities_no = communities.communities_no; pass_q = q; - changed = 0; + changed = false; /* Save the current membership, it will be restored in case of worse result */ - IGRAPH_CHECK(igraph_vector_update(&temp_membership, communities.membership)); + IGRAPH_CHECK(igraph_vector_int_update(&temp_membership, communities.membership)); - for (i = 0; i < vcount; i++) { + for (igraph_integer_t 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; + igraph_integer_t old_id, new_id, n, ni; ni = VECTOR(node_order)[i]; igraph_i_multilevel_community_links(graph, &communities, - (igraph_integer_t) ni, &edges, + ni, &edges, &weight_all, &weight_inside, &weight_loop, &links_community, &links_weight); - old_id = (long int)VECTOR(*(communities.membership))[ni]; + old_id = VECTOR(*(communities.membership))[ni]; new_id = old_id; /* Update old community */ - igraph_vector_set(communities.membership, ni, -1); + VECTOR(*communities.membership)[ni] = -1; communities.item[old_id].size--; if (communities.item[old_id].size == 0) { communities.communities_no--; @@ -455,16 +427,14 @@ static int igraph_i_community_multilevel_step( /* Find new community to join with the best modification gain */ max_q_gain = 0; max_weight = weight_inside; - n = igraph_vector_size(&links_community); + n = igraph_vector_int_size(&links_community); - for (j = 0; j < n; j++) { - long int c = (long int) VECTOR(links_community)[j]; + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t c = 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, + igraph_i_multilevel_community_modularity_gain(&communities, c, 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) { @@ -477,7 +447,7 @@ static int igraph_i_community_multilevel_step( /* 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); + VECTOR(*communities.membership)[ni] = new_id; if (communities.item[new_id].size == 0) { communities.communities_no++; } @@ -486,7 +456,7 @@ static int igraph_i_community_multilevel_step( communities.item[new_id].weight_inside += 2 * max_weight + weight_loop; if (new_id != old_id) { - changed++; + changed = true; } } @@ -498,7 +468,7 @@ static int igraph_i_community_multilevel_step( /* pass++; */ } else { /* No changes or the modularity became worse, restore last membership */ - IGRAPH_CHECK(igraph_vector_update(communities.membership, &temp_membership)); + IGRAPH_CHECK(igraph_vector_int_update(communities.membership, &temp_membership)); communities.communities_no = temp_communities_no; break; } @@ -513,15 +483,15 @@ static int igraph_i_community_multilevel_step( /* debug("Result Communities: %ld Modularity: %lf\n", communities.communities_no, (double) q); */ - IGRAPH_CHECK(igraph_reindex_membership(membership, 0, NULL)); + IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, 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_vector_int_update(&temp_membership, membership)); IGRAPH_CHECK(igraph_i_multilevel_shrink(graph, &temp_membership)); - igraph_vector_destroy(&temp_membership); + igraph_vector_int_destroy(&temp_membership); IGRAPH_FINALLY_CLEAN(1); /* Update edge weights after shrinking and simplification */ @@ -533,18 +503,18 @@ static int igraph_i_community_multilevel_step( 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]; + for (igraph_integer_t i = 0; i < ecount; i++) { + VECTOR(*weights)[VECTOR(edges)[i]] += VECTOR(links_weight)[i]; } igraph_free(communities.item); - igraph_vector_destroy(&links_community); + igraph_vector_int_destroy(&links_community); igraph_vector_destroy(&links_weight); - igraph_vector_destroy(&edges); - igraph_vector_destroy(&node_order); + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&node_order); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** @@ -579,7 +549,8 @@ static int igraph_i_community_multilevel_step( * 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. + * + * The original version of 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 @@ -604,49 +575,75 @@ static int igraph_i_community_multilevel_step( * \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_error_t igraph_community_multilevel(const igraph_t *graph, + const igraph_vector_t *weights, + const igraph_real_t resolution, + igraph_vector_int_t *membership, + igraph_matrix_int_t *memberships, + igraph_vector_t *modularity) { igraph_t g; - igraph_vector_t w, m, level_membership; + igraph_vector_t w; + igraph_vector_int_t m; + igraph_vector_int_t level_membership; igraph_real_t prev_q = -1, q = -1; - int i, level = 1; - long int vcount = igraph_vcount(graph); + igraph_integer_t level = 1; + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t ecount = igraph_ecount(graph); + + /* 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 (weights) { + if (igraph_vector_size(weights) != ecount) { + IGRAPH_ERROR("Weight vector length must agree with number of edges.", IGRAPH_EINVAL); + } + if (ecount > 0) { + igraph_real_t minweight = igraph_vector_min(weights); + if (minweight < 0) { + IGRAPH_ERROR("Weight vector must not be negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + if (resolution < 0.0) { + IGRAPH_ERROR("The resolution parameter must be non-negative.", IGRAPH_EINVAL); + } /* 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_CHECK(igraph_vector_init_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&m, vcount); + IGRAPH_VECTOR_INT_INIT_FINALLY(&level_membership, vcount); if (memberships || membership) { /* Put each vertex in its own community */ - for (i = 0; i < vcount; i++) { + for (igraph_integer_t 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)); + IGRAPH_CHECK(igraph_matrix_int_resize(memberships, 0, vcount)); } if (modularity) { /* Clear the modularity vector */ igraph_vector_clear(modularity); } - while (1) { + while (true) { /* Remember the previous modularity and vertex count, do a single step */ igraph_integer_t step_vcount = igraph_vcount(&g); @@ -659,9 +656,9 @@ int igraph_community_multilevel(const igraph_t *graph, } if (memberships || membership) { - for (i = 0; i < vcount; i++) { + for (igraph_integer_t i = 0; i < vcount; i++) { /* Readjust the membership vector */ - VECTOR(level_membership)[i] = VECTOR(m)[(long int) VECTOR(level_membership)[i]]; + VECTOR(level_membership)[i] = VECTOR(m)[ VECTOR(level_membership)[i] ]; } } @@ -673,11 +670,11 @@ int igraph_community_multilevel(const igraph_t *graph, 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)); + IGRAPH_CHECK(igraph_matrix_int_add_rows(memberships, 1)); + IGRAPH_CHECK(igraph_matrix_int_set_row(memberships, &level_membership, level - 1)); } - /* debug("Level: %d Communities: %ld Modularity: %f\n", level, (long int) igraph_vcount(&g), + /* debug("Level: %d Communities: %ld Modularity: %f\n", level, igraph_vcount(&g), (double) q); */ /* Increase the level counter */ @@ -687,25 +684,26 @@ int igraph_community_multilevel(const igraph_t *graph, /* 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_vector_int_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_vector_int_init_range(&tmp, 0, vcount)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp); + IGRAPH_CHECK(igraph_modularity(graph, &tmp, weights, resolution, - /* only undirected */ 0, &mod)); - igraph_vector_destroy(&tmp); + /* only undirected */ false, &mod)); + + igraph_vector_int_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++) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, vcount)); + for (igraph_integer_t i = 0; i < vcount; i++) { VECTOR(*membership)[i] = VECTOR(level_membership)[i]; } } @@ -714,9 +712,9 @@ int igraph_community_multilevel(const igraph_t *graph, igraph_destroy(&g); /* Destroy the temporary vectors */ - igraph_vector_destroy(&m); + igraph_vector_int_destroy(&m); igraph_vector_destroy(&w); - igraph_vector_destroy(&level_membership); + igraph_vector_int_destroy(&level_membership); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/community/modularity.c b/src/vendor/cigraph/src/community/modularity.c index 010d0841c67..57a855dd7f8 100644 --- a/src/vendor/cigraph/src/community/modularity.c +++ b/src/vendor/cigraph/src/community/modularity.c @@ -97,7 +97,7 @@ * 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 The resolutin parameter \c γ. Must not be negative. + * \param resolution The resolution parameter \c γ. Must not be negative. * 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. @@ -110,24 +110,24 @@ * 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, +igraph_error_t igraph_modularity(const igraph_t *graph, + const igraph_vector_int_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_integer_t types; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i; igraph_real_t m; - long int c1, c2; + igraph_integer_t 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)) { + if (igraph_vector_int_size(membership) != igraph_vcount(graph)) { IGRAPH_ERROR("Membership vector size differs from number of vertices.", IGRAPH_EINVAL); } @@ -147,9 +147,9 @@ int igraph_modularity(const igraph_t *graph, /* 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; + types = igraph_vector_int_max(membership) + 1; - if (igraph_vector_min(membership) < 0) { + if (igraph_vector_int_min(membership) < 0) { IGRAPH_ERROR("Invalid membership vector: negative entry.", IGRAPH_EINVAL); } @@ -159,7 +159,7 @@ int igraph_modularity(const igraph_t *graph, if (weights) { if (igraph_vector_size(weights) != no_of_edges) - IGRAPH_ERROR("Vector size differs from number of edges.", + IGRAPH_ERROR("Weight vector size differs from number of edges.", IGRAPH_EINVAL); m = 0.0; for (i = 0; i < no_of_edges; i++) { @@ -167,8 +167,8 @@ int igraph_modularity(const igraph_t *graph, 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) ]; + c1 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; if (c1 == c2) { VECTOR(e)[c1] += directed_multiplier * w; } @@ -179,8 +179,8 @@ int igraph_modularity(const igraph_t *graph, } 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) ]; + c1 = VECTOR(*membership)[ IGRAPH_FROM(graph, i) ]; + c2 = VECTOR(*membership)[ IGRAPH_TO(graph, i) ]; if (c1 == c2) { VECTOR(e)[c1] += directed_multiplier; } @@ -216,12 +216,12 @@ int igraph_modularity(const igraph_t *graph, return IGRAPH_SUCCESS; } -static int igraph_i_modularity_matrix_get_adjacency( +static igraph_error_t 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 no_of_nodes = igraph_vcount(graph); igraph_integer_t from, to; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); @@ -242,7 +242,8 @@ static int igraph_i_modularity_matrix_get_adjacency( } else { for (; !IGRAPH_EIT_END(edgeit); IGRAPH_EIT_NEXT(edgeit)) { igraph_integer_t edge = IGRAPH_EIT_GET(edgeit); - igraph_edge(graph, edge, &from, &to); + from = IGRAPH_FROM(graph, edge); + to = IGRAPH_TO(graph, edge); MATRIX(*res, from, to) += 1; if (!directed) { MATRIX(*res, to, from) += 1; @@ -298,17 +299,18 @@ static int igraph_i_modularity_matrix_get_adjacency( * * \sa \ref igraph_modularity() */ -int igraph_modularity_matrix(const igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_vector_int_t deg_int, in_deg_int, out_deg_int; + igraph_integer_t i, j; igraph_real_t scaling_factor; if (weights && igraph_vector_size(weights) != no_of_edges) { IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); @@ -327,10 +329,19 @@ int igraph_modularity_matrix(const igraph_t *graph, 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_VECTOR_INT_INIT_FINALLY(&in_deg_int, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_deg_int, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, &in_deg_int, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS)); - IGRAPH_CHECK(igraph_degree(graph, &out_deg, igraph_vss_all(), IGRAPH_OUT, + IGRAPH_CHECK(igraph_degree(graph, &out_deg_int, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(in_deg)[i] = VECTOR(in_deg_int)[i]; + VECTOR(out_deg)[i] = VECTOR(out_deg_int)[i]; + } + igraph_vector_int_destroy(&in_deg_int); + igraph_vector_int_destroy(&out_deg_int); + IGRAPH_FINALLY_CLEAN(2); } else { IGRAPH_CHECK(igraph_strength(graph, &in_deg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, weights)); @@ -352,15 +363,21 @@ int igraph_modularity_matrix(const igraph_t *graph, } else { IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); if (!weights) { - IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_VECTOR_INT_INIT_FINALLY(°_int, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °_int, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(deg)[i] = VECTOR(deg_int)[i]; + } + igraph_vector_int_destroy(°_int); + IGRAPH_FINALLY_CLEAN(1); } 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_vector_init_copy(°_unscaled, °); IGRAPH_FINALLY(igraph_vector_destroy, °_unscaled); scaling_factor = resolution / 2.0 / sw; igraph_vector_scale(°, scaling_factor); diff --git a/src/vendor/cigraph/src/community/optimal_modularity.c b/src/vendor/cigraph/src/community/optimal_modularity.c index cdb932891e1..ce3eb2d9be7 100644 --- a/src/vendor/cigraph/src/community/optimal_modularity.c +++ b/src/vendor/cigraph/src/community/optimal_modularity.c @@ -30,6 +30,7 @@ #include "core/interruption.h" #include "internal/glpk_support.h" +#include "math/safe_intop.h" #include "config.h" @@ -37,9 +38,11 @@ #include #endif +#include + /** * \function igraph_community_optimal_modularity - * Calculate the community structure with the highest modularity value + * \brief 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. @@ -49,10 +52,11 @@ * 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. + * Engineering 20(2):172-188, 2008 + * https://doi.org/10.1109/TKDE.2007.190689. * * - * Note that modularity optimization is an NP-complete problem, and + * Note that exact 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 @@ -68,6 +72,7 @@ * \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. + * When GLPK is not available, \c IGRAPH_UNIMPLEMENTED is returned. * * \sa \ref igraph_modularity(), \ref igraph_community_fastgreedy() * for an algorithm that finds a local optimum in a greedy way. @@ -77,21 +82,21 @@ * \example examples/simple/igraph_community_optimal_modularity.c */ -int igraph_community_optimal_modularity(const igraph_t *graph, +igraph_error_t igraph_community_optimal_modularity(const igraph_t *graph, igraph_real_t *modularity, - igraph_vector_t *membership, + igraph_vector_int_t *membership, const igraph_vector_t *weights) { #ifndef HAVE_GLPK - IGRAPH_ERROR("GLPK is not available", - IGRAPH_UNIMPLEMENTED); + 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = 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; + igraph_integer_t no_of_variables; + igraph_integer_t i, j, k, l; + int st; int idx[] = { 0, 0, 0, 0 }; double coef[] = { 0.0, 1.0, 1.0, -2.0 }; igraph_real_t total_weight; @@ -111,7 +116,7 @@ int igraph_community_optimal_modularity(const igraph_t *graph, if (minweight < 0) { IGRAPH_ERROR("Negative weights are not allowed in weight vector.", IGRAPH_EINVAL); } - if (igraph_is_nan(minweight)) { + if (isnan(minweight)) { IGRAPH_ERROR("Weights must not be NaN.", IGRAPH_EINVAL); } } @@ -120,8 +125,8 @@ int igraph_community_optimal_modularity(const igraph_t *graph, /* 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); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, 0); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); @@ -129,6 +134,18 @@ int igraph_community_optimal_modularity(const igraph_t *graph, return IGRAPH_SUCCESS; } + /* no_of_variables = no_of_nodes * (no_of_nodes + 1) / 2; + * + * Here we do not use IGRAPH_SAFE_N_CHOOSE_2 because later we rely on + * (no_of_nodes + 1) * no_of_nodes not overflowing even before the + * division by 2. See IDX() macro. + */ + IGRAPH_SAFE_MULT(no_of_nodes + 1, no_of_nodes, &no_of_variables); + no_of_variables /= 2; + if (no_of_variables > INT_MAX) { + IGRAPH_ERROR("Problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + if (weights) { total_weight = igraph_vector_sum(weights); } else { @@ -144,8 +161,8 @@ int igraph_community_optimal_modularity(const igraph_t *graph, *modularity = IGRAPH_NAN; } if (membership) { - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); - igraph_vector_null(membership); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_null(membership); } } @@ -162,14 +179,14 @@ int igraph_community_optimal_modularity(const igraph_t *graph, IGRAPH_FINALLY(igraph_i_glp_delete_prob, ip); glp_set_obj_dir(ip, GLP_MAX); - st = glp_add_cols(ip, no_of_variables); + st = glp_add_cols(ip, (int) no_of_variables); /* variables are binary */ for (i = 0; i < no_of_variables; i++) { - glp_set_col_kind(ip, (st + i), GLP_BV); + glp_set_col_kind(ip, (int)(st + i), GLP_BV); } -#define IDX(a,b) ((b)*((b)+1)/2+(a)) +#define IDX(a,b) (int)((b)*((b)+1)/2+(a)) /* reflexivity */ for (i = 0; i < no_of_nodes; i++) { @@ -248,8 +265,8 @@ int igraph_community_optimal_modularity(const igraph_t *graph, } if (membership) { - long int comm = 0; /* id of the last community that was found */ - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + igraph_integer_t comm = 0; /* id of the last community that was found */ + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); diff --git a/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp b/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp index 29014be0587..aeba0d00f80 100644 --- a/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp +++ b/src/vendor/cigraph/src/community/spinglass/NetRoutines.cpp @@ -48,7 +48,7 @@ #include "igraph_interface.h" #include "igraph_conversion.h" -int igraph_i_read_network(const igraph_t *graph, +igraph_error_t igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states) { @@ -58,13 +58,13 @@ int igraph_i_read_network(const igraph_t *graph, 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; + igraph_vector_int_t edgelist; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t ii; const char *empty = ""; - IGRAPH_VECTOR_INIT_FINALLY(&edgelist, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, no_of_edges * 2); IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0 /* rowwise */)); for (ii = 0; ii < no_of_nodes; ii++) { @@ -72,8 +72,8 @@ int igraph_i_read_network(const igraph_t *graph, } 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_integer_t i1 = VECTOR(edgelist)[2 * ii]; + igraph_integer_t i2 = VECTOR(edgelist)[2 * ii + 1]; igraph_real_t Links; if (use_weights) { Links = VECTOR(*weights)[ii]; @@ -82,11 +82,11 @@ int igraph_i_read_network(const igraph_t *graph, } node1 = net->node_list->Get(i1); - snprintf(name, sizeof(name) / sizeof(name[0]), "%li", i1+1); + snprintf(name, sizeof(name) / sizeof(name[0]), "%" IGRAPH_PRId "", i1+1); node1->Set_Name(name); node2 = net->node_list->Get(i2); - snprintf(name, sizeof(name) / sizeof(name[0]), "%li", i2+1); + snprintf(name, sizeof(name) / sizeof(name[0]), "%" IGRAPH_PRId "", i2+1); node2->Set_Name(name); node1->Connect_To(node2, Links); @@ -101,7 +101,7 @@ int igraph_i_read_network(const igraph_t *graph, } IGRAPH_FINALLY_CLEAN(1); - igraph_vector_destroy(&edgelist); + igraph_vector_int_destroy(&edgelist); node1 = iter.First(net->node_list); while (!iter.End()) { diff --git a/src/vendor/cigraph/src/community/spinglass/NetRoutines.h b/src/vendor/cigraph/src/community/spinglass/NetRoutines.h index 02030fbcdef..53a4e51aa34 100644 --- a/src/vendor/cigraph/src/community/spinglass/NetRoutines.h +++ b/src/vendor/cigraph/src/community/spinglass/NetRoutines.h @@ -48,7 +48,7 @@ #include "igraph_types.h" #include "igraph_datatype.h" -int igraph_i_read_network(const igraph_t *graph, +igraph_error_t igraph_i_read_network(const igraph_t *graph, const igraph_vector_t *weights, network *net, igraph_bool_t use_weights, unsigned int states); diff --git a/src/vendor/cigraph/src/community/spinglass/clustertool.cpp b/src/vendor/cigraph/src/community/spinglass/clustertool.cpp index c99173a885f..0a13afc9dc1 100644 --- a/src/vendor/cigraph/src/community/spinglass/clustertool.cpp +++ b/src/vendor/cigraph/src/community/spinglass/clustertool.cpp @@ -46,21 +46,21 @@ #include "pottsmodel_2.h" #include "igraph_community.h" +#include "igraph_components.h" #include "igraph_error.h" -#include "igraph_random.h" -#include "core/math.h" #include "igraph_interface.h" -#include "igraph_components.h" +#include "igraph_random.h" + #include "core/interruption.h" #include "core/exceptions.h" -static int igraph_i_community_spinglass_orig( +static igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -69,13 +69,13 @@ static int igraph_i_community_spinglass_orig( igraph_spincomm_update_t update_rule, igraph_real_t gamma); -static int igraph_i_community_spinglass_negative( +static igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -90,7 +90,7 @@ static int igraph_i_community_spinglass_negative( /** * \function igraph_community_spinglass - * \brief Community detection based on statistical mechanics + * \brief Community detection based on statistical mechanics. * * This function implements the community structure detection * algorithm proposed by Joerg Reichardt and Stefan Bornholdt. @@ -104,11 +104,11 @@ static int igraph_i_community_spinglass_negative( * 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. + * of the edges is ignored by 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. + * implementation. * \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 @@ -126,20 +126,17 @@ static int igraph_i_community_spinglass_negative( * 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. + * number of clusters. 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. + * parallel. It is not implemented in the \c IGRAPH_SPINCOMM_INP_NEG + * implementation. + * \param starttemp Real number, the temperature at the start. A reasonable + * default is 1.0. + * \param stoptemp Real number, the algorithm stops at this temperature. A + * reasonable default is 0.01. * \param coolfact Real number, the cooling factor for the simulated - * annealing. The default was 0.99 in the original code. + * annealing. A reasonable default is 0.99. * \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 @@ -149,29 +146,20 @@ static int igraph_i_community_spinglass_negative( * 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 gamma Real number. The gamma parameter of the algorithm, + * acting as a resolution parameter. Smaller values typically lead to + * larger clusters, larger values typically lead to smaller clusters. * \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. + * an implementation that allows negative edge weights. + * \param gamma_minus Real number. Parameter for the \c IGRAPH_SPINCOMM_IMP_NEG + * implementation. This acts as a resolution parameter for the negative part + * of the network. Smaller values of \p gamma_minus leads to fewer negative + * edges within clusters. If this argument is set to zero, the algorithm + * reduces to a graph coloring algorithm when all edges have negative + * weights, using the number of spins as the number of colors. * \return Error code. * * \sa igraph_community_spinglass_single() for calculating the community @@ -181,12 +169,12 @@ static int igraph_i_community_spinglass_negative( * */ -int igraph_community_spinglass(const igraph_t *graph, +igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -194,11 +182,7 @@ int igraph_community_spinglass(const igraph_t *graph, 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( @@ -216,8 +200,6 @@ int igraph_community_spinglass(const igraph_t *graph, spins, parupdate, starttemp, stoptemp, coolfact, update_rule, gamma, - /* adhesion, normalised_adhesion, */ - /* polarization, */ gamma_minus); break; default: @@ -227,13 +209,13 @@ int igraph_community_spinglass(const igraph_t *graph, ); } -static int igraph_i_community_spinglass_orig( +static igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -242,9 +224,9 @@ static int igraph_i_community_spinglass_orig( igraph_spincomm_update_t update_rule, igraph_real_t gamma) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); unsigned long changes, runs; - igraph_bool_t use_weights = 0; + igraph_bool_t use_weights = false; bool zeroT; double kT, acc, prob; @@ -262,6 +244,9 @@ static int igraph_i_community_spinglass_orig( IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } use_weights = 1; + if (igraph_vector_min(weights) < 0) { + IGRAPH_ERROR("Weights must not be negative when using the original implementation of spinglass communities. Select the implementation meant for negative weights.", IGRAPH_EINVAL); + } } if (coolfact < 0 || coolfact >= 1.0) { IGRAPH_ERROR("Invalid cooling factor", IGRAPH_EINVAL); @@ -278,8 +263,8 @@ static int igraph_i_community_spinglass_orig( 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); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, 0); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); @@ -289,8 +274,8 @@ static int igraph_i_community_spinglass_orig( } if (csize) { /* 0 clusters for 0 nodes, 1 cluster for 1 node */ - IGRAPH_CHECK(igraph_vector_resize(csize, no_of_nodes)); - igraph_vector_fill(csize, 1); + IGRAPH_CHECK(igraph_vector_int_resize(csize, no_of_nodes)); + igraph_vector_int_fill(csize, 1); } return IGRAPH_SUCCESS; } @@ -367,12 +352,12 @@ static int igraph_i_community_spinglass_orig( RNG_END(); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_community_spinglass_single - * \brief Community of a single node based on statistical mechanics + * \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 @@ -388,10 +373,10 @@ static int igraph_i_community_spinglass_orig( * \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 + * \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 + * 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. @@ -430,10 +415,10 @@ static int igraph_i_community_spinglass_orig( * Time complexity: TODO. */ -int igraph_community_spinglass_single(const igraph_t *graph, +igraph_error_t igraph_community_spinglass_single(const igraph_t *graph, const igraph_vector_t *weights, igraph_integer_t vertex, - igraph_vector_t *community, + igraph_vector_int_t *community, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -442,7 +427,7 @@ int igraph_community_spinglass_single(const igraph_t *graph, igraph_spincomm_update_t update_rule, igraph_real_t gamma) { IGRAPH_HANDLE_EXCEPTIONS( - igraph_bool_t use_weights = 0; + igraph_bool_t use_weights = false; double prob; char startnode[255]; @@ -465,7 +450,7 @@ int igraph_community_spinglass_single(const igraph_t *graph, IGRAPH_ERROR("Invalid gamme value", IGRAPH_EINVAL); } if (vertex < 0 || vertex > igraph_vcount(graph)) { - IGRAPH_ERROR("Invalid vertex id", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid vertex ID", IGRAPH_EINVAL); } /* Check whether we have a single component */ @@ -493,23 +478,23 @@ int igraph_community_spinglass_single(const igraph_t *graph, /* 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); + snprintf(startnode, 255, "%" IGRAPH_PRId "", vertex + 1); pm.FindCommunityFromStart(gamma, prob, startnode, community, cohesion, adhesion, inner_links, outer_links); RNG_END(); ); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_community_spinglass_negative( +static igraph_error_t 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_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t spins, igraph_bool_t parupdate, igraph_real_t starttemp, @@ -522,9 +507,9 @@ static int igraph_i_community_spinglass_negative( /* igraph_real_t *polarization, */ igraph_real_t gamma_minus) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); unsigned long changes, runs; - igraph_bool_t use_weights = 0; + igraph_bool_t use_weights = false; bool zeroT; double kT, acc; igraph_real_t d_n; @@ -565,8 +550,8 @@ static int igraph_i_community_spinglass_negative( 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); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, 0); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); @@ -576,8 +561,8 @@ static int igraph_i_community_spinglass_negative( } if (csize) { /* 0 clusters for 0 nodes, 1 cluster for 1 node */ - IGRAPH_CHECK(igraph_vector_resize(csize, no_of_nodes)); - igraph_vector_fill(csize, 1); + IGRAPH_CHECK(igraph_vector_int_resize(csize, no_of_nodes)); + igraph_vector_int_fill(csize, 1); } return IGRAPH_SUCCESS; } @@ -589,7 +574,7 @@ static int igraph_i_community_spinglass_negative( IGRAPH_ERROR("Cannot work with unconnected graph", IGRAPH_EINVAL); } - if (weights) { + if (weights && igraph_vector_size(weights) > 0) { igraph_vector_minmax(weights, &d_n, &d_p); } else { d_n = d_p = 1; @@ -660,5 +645,5 @@ static int igraph_i_community_spinglass_negative( RNG_END(); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp index a26b6730e25..347d83b15f8 100644 --- a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp +++ b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.cpp @@ -43,7 +43,6 @@ ***************************************************************************/ #include "pottsmodel_2.h" -#include "NetRoutines.h" #include "igraph_random.h" #include "core/interruption.h" @@ -54,11 +53,11 @@ using namespace std; //################################################################################################# -PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) +PottsModel::PottsModel(network *n, unsigned long qvalue, int m) : Qmatrix(qvalue+1), acceptance(0) { DLList_Iter iter; NNode *n_cur; - unsigned int *i_ptr; + unsigned long *i_ptr; net = n; q = qvalue; operation_mode = m; @@ -76,16 +75,16 @@ PottsModel::PottsModel(network *n, unsigned int qvalue, int m) : Qmatrix(qvalue+ 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(); + 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 = new unsigned long; *i_ptr = 0; new_spins->Push(i_ptr); - i_ptr = new unsigned int; + i_ptr = new unsigned long; *i_ptr = 0; previous_spins->Push(i_ptr); n_cur = iter.Next(); @@ -113,8 +112,8 @@ PottsModel::~PottsModel() { //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; +unsigned long PottsModel::assign_initial_conf(igraph_integer_t spin) { + igraph_integer_t s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; @@ -127,7 +126,7 @@ unsigned long PottsModel::assign_initial_conf(int spin) { // printf("Assigning initial configuration...\n"); // initialize colorfield - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long i = 0; i <= q; i++) { color_field[i] = 0.0; } // @@ -208,7 +207,7 @@ unsigned long PottsModel::initialize_lookup(double kT, double gamma) { double PottsModel::initialize_Qmatrix() { DLList_Iter l_iter; NLink *l_cur; - unsigned int i, j; + unsigned long i, j; //initialize with zeros num_of_links = net->link_list->Size(); for (i = 0; i <= q; i++) { @@ -244,7 +243,7 @@ double PottsModel::initialize_Qmatrix() { //#################################################################### double PottsModel::calculate_Q() { double Q = 0.0; - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long 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"); @@ -257,7 +256,7 @@ double PottsModel::calculate_Q() { } double PottsModel::calculate_genQ(double gamma) { double Q = 0.0; - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long 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"); @@ -285,7 +284,7 @@ double PottsModel::calculate_energy(double gamma) { l_cur = l_iter.Next(); } //and the penalty term contributes according to cluster sizes - for (unsigned int i = 1; i <= q; i++) { + for (unsigned long i = 1; i <= q; i++) { e += gamma * 0.5 * double(color_field[i]) * double((color_field[i] - 1)); } energy = e; @@ -327,10 +326,11 @@ double PottsModel::FindStartTemp(double gamma, double prob, double ts) { 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; + 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; + unsigned int sweep; + unsigned long *SPIN, *P_SPIN, old_spin, spin, new_spin, spin_opt; // long h; // degree; unsigned long changes; double h, delta = 0, deltaE, deltaEmin, w, degree; @@ -347,9 +347,9 @@ long PottsModel::HeatBathParallelLookupZeroTemp(double gamma, double prob, unsig node = net_iter.First(net->node_list); SPIN = i_iter.First(new_spins); while (!net_iter.End()) { - // How many neigbors of each type? + // How many neighbors of each type? // set them all zero - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long i = 0; i <= q; i++) { neighbours[i] = 0; } degree = node->Get_Weight(); @@ -467,7 +467,8 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned int new_spin, spin_opt, old_spin, spin, sweep; + unsigned long new_spin, spin_opt, old_spin, spin; + unsigned int sweep; long r;// degree; unsigned long changes; double delta = 0, h, deltaE, deltaEmin, w, degree; @@ -487,7 +488,7 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in 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++) { + for (unsigned long i = 0; i <= q; i++) { neighbours[i] = 0; } degree = node->Get_Weight(); @@ -573,17 +574,17 @@ double PottsModel::HeatBathLookupZeroTemp(double gamma, double prob, unsigned in 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; + 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 long new_spin, spin_opt, old_spin; + unsigned long *SPIN, *P_SPIN; unsigned int sweep; long max_q; - unsigned long changes /*, degree, problemcount*/; + unsigned long changes/*, degree, problemcount */; //HugeArray neighbours; double h, delta = 0, norm, r, beta, minweight, prefac = 0, w, degree; - bool cyclic = false, found; + bool cyclic = false/*, found*/; unsigned long number_of_nodes; sweep = 0; @@ -598,8 +599,8 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un SPIN = i_iter.First(new_spins); while (!net_iter.End()) { // Initialize neighbours and weights - //problemcount = 0; - for (unsigned int i = 0; i <= q; i++) { + // problemcount = 0; + for (unsigned long i = 0; i <= q; i++) { neighbours[i] = 0; weights[i] = 0; } @@ -658,11 +659,11 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un r = RNG_UNIF(0, norm); /* norm*double(rand())/double(RAND_MAX + 1.0); */ new_spin = 1; - found = false; - while (!found && new_spin <= q) { + //found = false; + while (/*!found &&*/ new_spin <= q) { if (r <= weights[new_spin]) { spin_opt = new_spin; - found = true; + // found = true; break; } else { r -= weights[new_spin]; @@ -726,7 +727,7 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un } // while markov max_q = 0; - for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { + for (unsigned long i = 1; i <= q; i++) if (color_field[i] > max_q) { max_q = long(color_field[i]); } @@ -747,25 +748,25 @@ long PottsModel::HeatBathParallelLookup(double gamma, double prob, double kT, un 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; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; - unsigned int new_spin, spin_opt, old_spin; + unsigned long new_spin, spin_opt, old_spin; unsigned int sweep; long max_q, rn; - unsigned long changes /*, degree, problemcount*/; + 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; + //bool found; + igraph_integer_t 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++) { + for (long 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); @@ -775,7 +776,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned node = net->node_list->Get(rn); // initialize the neighbours and the weights // problemcount = 0; - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long i = 0; i <= q; i++) { neighbours[i] = 0.0; weights[i] = 0.0; } @@ -837,11 +838,11 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned /* r = norm*double(rand())/double(RAND_MAX + 1.0); */ r = RNG_UNIF(0, norm); new_spin = 1; - found = false; - while (!found && new_spin <= q) { + //found = false; + while (/*!found &&*/ new_spin <= q) { if (r <= weights[new_spin]) { spin_opt = new_spin; - found = true; + //found = true; break; } else { r -= weights[new_spin]; @@ -886,7 +887,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned } // while markov max_q = 0; - for (unsigned int i = 1; i <= q; i++) if (color_field[i] > max_q) { + for (unsigned long i = 1; i <= q; i++) if (color_field[i] > max_q) { max_q = long(color_field[i] + 0.5); } @@ -899,7 +900,7 @@ double PottsModel::HeatBathLookup(double gamma, double prob, double kT, unsigned //############################################################################################### double PottsModel::FindCommunityFromStart(double gamma, double prob, char *nodename, - igraph_vector_t *result, + igraph_vector_int_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *my_inner_links, @@ -1131,17 +1132,17 @@ double PottsModel::FindCommunityFromStart(double gamma, double prob, } if (result) { node = iter.First(community); - igraph_vector_resize(result, 0); + igraph_vector_int_clear(result); 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())); + IGRAPH_CHECK(igraph_vector_int_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(); + unsigned long size = community->Size(); delete to_do; delete community; return size; @@ -1152,8 +1153,8 @@ double PottsModel::FindCommunityFromStart(double gamma, double prob, //################################################################################################ long PottsModel::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_t *csize, - igraph_vector_t *membership, + igraph_vector_int_t *csize, + igraph_vector_int_t *membership, double kT, double gamma) { NNode *n_cur, *n_cur2; /* @@ -1181,7 +1182,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, if (csize || membership || modularity) { // TODO: count the number of clusters - for (unsigned int spin = 1; spin <= q; spin++) { + for (unsigned long spin = 1; spin <= q; spin++) { inner_links[spin] = 0; outer_links[spin] = 0; nodes[spin] = 0; @@ -1205,7 +1206,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, } if (modularity) { *modularity = 0.0; - for (unsigned int spin = 1; spin <= q; spin++) { + for (unsigned long 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]) / @@ -1216,8 +1217,8 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, } } if (csize) { - igraph_vector_resize(csize, 0); - for (unsigned int spin = 1; spin <= q; spin++) { + igraph_vector_int_clear(csize); + for (unsigned long spin = 1; spin <= q; spin++) { if (nodes[spin] > 0) { inner_links[spin] /= 2; // fprintf(file,"Cluster\tNodes\tInnerLinks\tOuterLinks\tp_in\tp_out\n"); @@ -1245,7 +1246,7 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, 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])); + IGRAPH_CHECK(igraph_vector_int_push_back(csize, nodes[spin])); } } // fprintf(file,"\n"); @@ -1253,9 +1254,9 @@ long PottsModel::WriteClusters(igraph_real_t *modularity, //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++) { + igraph_integer_t no = -1; + IGRAPH_CHECK(igraph_vector_int_resize(membership, num_of_nodes)); + for (unsigned long spin = 1; spin <= q; spin++) { if (nodes[spin] > 0) { no++; } @@ -1552,7 +1553,7 @@ double PottsModel::GammaSweepZeroTemp(double gamma_start, double gamma_stop, dou //############################################################################## //################################################################################################# -PottsModelN::PottsModelN(network *n, unsigned int num_communities, bool directed) : +PottsModelN::PottsModelN(network *n, unsigned long 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), @@ -1594,7 +1595,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { #ifdef SPINGLASS_DEBUG printf("Start assigning.\n"); #endif - unsigned int s; + unsigned long s; DLList_Iter iter; DLList_Iter l_iter; NNode *n_cur; @@ -1620,7 +1621,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { 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 + spin = new unsigned long[num_nodes]; //The spin state of each node } if (is_init) { @@ -1645,11 +1646,11 @@ void PottsModelN::assign_initial_conf(bool init_spins) { //...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 + csize = new unsigned long[q + 1]; //The number of nodes in each community //Initialize communities - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long 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; @@ -1660,7 +1661,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { //Initialize vectors if (init_spins) { - for (unsigned int i = 0; i < num_nodes; i++) { + for (unsigned long i = 0; i < num_nodes; i++) { degree_pos_in[i] = 0.0; degree_neg_in[i] = 0.0; degree_pos_out[i] = 0.0; @@ -1683,7 +1684,7 @@ void PottsModelN::assign_initial_conf(bool init_spins) { #ifdef SPINGLASS_DEBUG printf("Visiting each node.\n"); #endif - for (unsigned int v = 0; v < num_nodes; v++) { + for (unsigned long v = 0; v < num_nodes; v++) { if (init_spins) { s = RNG_INTEGER(1, q); //The new spin s spin[v] = (unsigned int)s; @@ -1769,7 +1770,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign #endif DLList_Iter iter; DLList_Iter l_iter; - DLList_Iter i_iter, i_iter2; + DLList_Iter i_iter, i_iter2; NNode *node, *n_cur; NLink *l_cur; /* The new_spin contains the spin to which we will update, @@ -1777,13 +1778,13 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign * the old_spin is the spin of the node we are currently * changing. */ - unsigned int new_spin, spin_opt, old_spin; + unsigned long 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 + long 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; @@ -1814,7 +1815,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign while (sweep < max_sweeps) { sweep++; //loop over all nodes in network - for (unsigned int n = 0; n < num_nodes; n++) { + for (unsigned long n = 0; n < num_nodes; n++) { //Look for a random node v = RNG_INTEGER(0, num_nodes - 1); //We will be investigating node v @@ -1824,7 +1825,7 @@ double PottsModelN::HeatBathLookup(double gamma, double lambda, double t, unsign /*******************************************/ // initialize the neighbours and the weights // problemcount = 0; - for (unsigned int i = 0; i <= q; i++) { + for (unsigned long i = 0; i <= q; i++) { neighbours[i] = 0.0; weights[i] = 0.0; } @@ -1994,8 +1995,8 @@ double PottsModelN::FindStartTemp(double gamma, double lambda, double ts) { long PottsModelN::WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_t *community_size, - igraph_vector_t *membership, + igraph_vector_int_t *community_size, + igraph_vector_int_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, @@ -2010,16 +2011,16 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, 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++) { + unsigned long *cluster_assign = new unsigned long[q + 1]; + for (unsigned long i = 0; i <= q; i++) { cluster_assign[i] = 0; } - int num_clusters = 0; + long 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]; + for (unsigned long i = 0; i < num_nodes; i++) { + unsigned long s = spin[i]; if (cluster_assign[s] == 0) { num_clusters++; cluster_assign[s] = num_clusters; @@ -2038,11 +2039,11 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //And now assign each node to its new community q = num_clusters; - for (unsigned int i = 0; i < num_nodes; i++) { + for (unsigned long 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]]; + unsigned long s = cluster_assign[spin[i]]; spin[i] = s; #ifdef SPINGLASS_DEBUG printf("Have set node %d to %d.\n", i, s); @@ -2058,8 +2059,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, 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++) { + IGRAPH_CHECK(igraph_vector_int_resize(community_size, q)); + for (unsigned long spin_opt = 1; spin_opt <= q; spin_opt++) { //Set the community size VECTOR(*community_size)[spin_opt - 1] = csize[spin_opt]; } @@ -2067,8 +2068,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //Set the membership if (membership) { - IGRAPH_CHECK(igraph_vector_resize(membership, num_nodes)); - for (unsigned int i = 0; i < num_nodes; i++) { + IGRAPH_CHECK(igraph_vector_int_resize(membership, num_nodes)); + for (unsigned long i = 0; i < num_nodes; i++) { VECTOR(*membership)[ i ] = spin[i] - 1; } } @@ -2085,7 +2086,7 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, num_links_neg = new double *[q + 1] ; //memory allocated for elements of each column. - for ( unsigned int i = 0 ; i < q + 1 ; i++) { + for ( unsigned long i = 0 ; i < q + 1 ; i++) { num_links_pos[i] = new double[q + 1]; num_links_neg[i] = new double[q + 1]; } @@ -2093,8 +2094,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //Init num_links - for (unsigned int i = 0; i <= q; i++) { - for (unsigned int j = 0; j <= q; j++) { + for (unsigned long i = 0; i <= q; i++) { + for (unsigned long j = 0; j <= q; j++) { num_links_pos[i][j] = 0.0; num_links_neg[i][j] = 0.0; } @@ -2107,8 +2108,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, 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()]; + unsigned long a = spin[l_cur->Get_Start()->Get_Index()]; + unsigned long 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 @@ -2139,8 +2140,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, //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++) { + for (unsigned long i = 1; i <= q; i++) { + for (unsigned long 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); @@ -2207,7 +2208,7 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, } //for i //free the allocated memory - for ( unsigned int i = 0 ; i < q + 1 ; i++ ) { + for ( unsigned long i = 0 ; i < q + 1 ; i++ ) { delete [] num_links_pos[i] ; delete [] num_links_neg[i]; } @@ -2226,8 +2227,8 @@ long PottsModelN::WriteClusters(igraph_real_t *modularity, if (polarization) { double sum_ad = 0.0; - for (unsigned int i = 0; i < q; i++) { - for (unsigned int j = 0; j < q; j++) { + for (unsigned long i = 0; i < q; i++) { + for (unsigned long j = 0; j < q; j++) { if (i != j) { sum_ad -= MATRIX(*normalised_adhesion, i, j); } diff --git a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h index 7d0ee28385c..27addecfedf 100644 --- a/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h +++ b/src/vendor/cigraph/src/community/spinglass/pottsmodel_2.h @@ -69,11 +69,11 @@ class PottsModel { private: // HugeArray neg_gammalookup; // HugeArray pos_gammalookup; - DL_Indexed_List *new_spins; - DL_Indexed_List *previous_spins; + DL_Indexed_List *new_spins; + DL_Indexed_List *previous_spins; HugeArray*> correlation; network *net; - unsigned int q; + unsigned long q; unsigned int operation_mode; // FILE *Qfile, *Magfile; SimpleMatrix Qmatrix; @@ -87,10 +87,10 @@ class PottsModel { double acceptance; double *neighbours; public: - PottsModel(network *net, unsigned int q, int norm_by_degree); + PottsModel(network *net, unsigned long q, int norm_by_degree); ~PottsModel(); double* color_field; - unsigned long assign_initial_conf(int spin); + unsigned long assign_initial_conf(igraph_integer_t spin); unsigned long initialize_lookup(double kT, double gamma); double initialize_Qmatrix(); double calculate_Q(); @@ -106,14 +106,14 @@ class PottsModel { double calculate_energy(double gamma); long WriteClusters(igraph_real_t *modularity, igraph_real_t *temperature, - igraph_vector_t *csize, igraph_vector_t *membership, + igraph_vector_int_t *csize, igraph_vector_int_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_vector_int_t *result, igraph_real_t *cohesion, igraph_real_t *adhesion, igraph_integer_t *inner_links, @@ -130,10 +130,10 @@ class PottsModelN { HugeArray*> correlation; network *net; - unsigned int q; //number of communities + unsigned long 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 + unsigned long num_nodes; //number of nodes bool is_directed; bool is_init; @@ -148,14 +148,14 @@ class PottsModelN { 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 + unsigned long *csize; //The number of nodes in each community + unsigned long *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(network *n, unsigned long num_communities, bool directed); ~PottsModelN(); void assign_initial_conf(bool init_spins); double FindStartTemp(double gamma, double lambda, double ts); @@ -164,8 +164,8 @@ class PottsModelN { // 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_vector_int_t *community_size, + igraph_vector_int_t *membership, igraph_matrix_t *adhesion, igraph_matrix_t *normalised_adhesion, igraph_real_t *polarization, diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap.cpp index d35bcb28072..1fb9d09ef02 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap.cpp @@ -63,10 +63,19 @@ #include "core/exceptions.h" #include "core/interruption.h" +#include +#include + +// This is necessary for GCC 5 and earlier, where including +// makes isnan() unusable without the std:: prefix, even if +// was included as well. +using std::isnan; + using namespace igraph::walktrap; /** * \function igraph_community_walktrap + * \brief Community finding using a random walk based similarity measure. * * This function is the implementation of the Walktrap community * finding algorithm, see Pascal Pons, Matthieu Latapy: Computing @@ -92,8 +101,8 @@ using namespace igraph::walktrap; * 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 + * algorithm will be stored here (if not \c 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 @@ -102,13 +111,12 @@ using namespace igraph::walktrap; * 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 + * \param modularity Pointer to a vector. If not \c 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 + * \param membership Pointer to a vector. If not a \c 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. + * score is stored here. * \return Error code. * * \sa \ref igraph_community_spinglass(), \ref @@ -120,81 +128,106 @@ using namespace igraph::walktrap; * \example examples/simple/walktrap.c */ -int igraph_community_walktrap(const igraph_t *graph, +igraph_error_t igraph_community_walktrap(const igraph_t *graph, const igraph_vector_t *weights, - int steps, - igraph_matrix_t *merges, + igraph_integer_t steps, + igraph_matrix_int_t *merges, igraph_vector_t *modularity, - igraph_vector_t *membership) { + igraph_vector_int_t *membership) { - IGRAPH_HANDLE_EXCEPTIONS( - long int no_of_nodes = (long int) igraph_vcount(graph); - long int no_of_edges = (long int) igraph_ecount(graph); - int length = steps; - long max_memory = -1; - igraph_integer_t comp_count; - - if (steps <= 0) { - IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t comp_count; + igraph_matrix_int_t imerges, *pmerges = merges; + igraph_vector_t imodularity, *pmodularity = modularity; + + if (steps <= 0) { + IGRAPH_ERROR("Length of random walks must be positive for walktrap community detection.", IGRAPH_EINVAL); + } + + if (steps > INT_MAX) { + IGRAPH_ERROR("Length of random walks too large for walktrap community detection.", IGRAPH_EINVAL); + } + + int length = steps; + + if (weights) { + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } - if (weights) { - 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 non-negative.", IGRAPH_EINVAL); + } else if (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", 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); - } - } + if (membership) { + /* We need both 'modularity' and 'merges' to compute 'membership'. + * If they were not provided by the called, we allocate these here. */ + + if (! modularity) { + IGRAPH_VECTOR_INIT_FINALLY(&imodularity, 0); + pmodularity = &imodularity; } - if (membership && !(modularity && merges)) { - IGRAPH_ERROR("Cannot calculate membership without modularity or merges", - IGRAPH_EINVAL); + if (! merges) { + IGRAPH_MATRIX_INT_INIT_FINALLY(&imerges, 0, 0); + pmerges = &imerges; } + } + IGRAPH_HANDLE_EXCEPTIONS( Graph G; IGRAPH_CHECK(G.convert_from_igraph(graph, weights)); - if (merges || modularity) { - IGRAPH_CHECK(igraph_clusters(graph, /*membership=*/ 0, /*csize=*/ 0, - &comp_count, IGRAPH_WEAK)); + if (pmerges || pmodularity) { + IGRAPH_CHECK(igraph_connected_components(graph, /*membership=*/ NULL, /*csize=*/ NULL, + &comp_count, IGRAPH_WEAK)); } - if (merges) { - IGRAPH_CHECK(igraph_matrix_resize(merges, no_of_nodes - comp_count, 2)); + if (pmerges) { + IGRAPH_CHECK(igraph_matrix_int_resize(pmerges, no_of_nodes - comp_count, 2)); } - if (modularity) { - IGRAPH_CHECK(igraph_vector_resize(modularity, no_of_nodes - comp_count + 1)); - igraph_vector_null(modularity); + if (pmodularity) { + IGRAPH_CHECK(igraph_vector_resize(pmodularity, no_of_nodes - comp_count + 1)); + igraph_vector_null(pmodularity); } - Communities C(&G, length, max_memory, merges, modularity); + Communities C(&G, length, pmerges, pmodularity); 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)); + ); + + if (membership) { + igraph_integer_t m; + m = no_of_nodes > 0 ? igraph_vector_which_max(pmodularity) : 0; + IGRAPH_CHECK(igraph_community_to_membership(pmerges, no_of_nodes, + /*steps=*/ m, + membership, + /*csize=*/ NULL)); + + if (! merges) { + igraph_matrix_int_destroy(&imerges); + IGRAPH_FINALLY_CLEAN(1); } - - /* The walktrap implementation cannot work with NaN values internally, - * and produces 0 for the modularity of edgeless graphs. We correct - * this to NaN in the last step for consistency. */ - if (modularity && no_of_edges == 0) { - VECTOR(*modularity)[0] = IGRAPH_NAN; + if (! modularity) { + igraph_vector_destroy(&imodularity); + IGRAPH_FINALLY_CLEAN(1); } + } + + /* The walktrap implementation cannot work with NaN values internally, + * and produces 0 for the modularity of edgeless graphs. We correct + * this to NaN in the last step for consistency. */ + if (modularity && no_of_edges == 0) { + VECTOR(*modularity)[0] = IGRAPH_NAN; + } - return IGRAPH_SUCCESS; - ) + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp index 713135f3573..4e1dd71025e 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.cpp @@ -65,31 +65,26 @@ namespace igraph { namespace walktrap { IGRAPH_THREAD_LOCAL int Probabilities::length = 0; -IGRAPH_THREAD_LOCAL Communities* Probabilities::C = 0; -IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector1 = 0; -IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector2 = 0; -IGRAPH_THREAD_LOCAL int* Probabilities::id = 0; -IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = 0; -IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = 0; +IGRAPH_THREAD_LOCAL Communities* Probabilities::C = nullptr; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector1 = nullptr; +IGRAPH_THREAD_LOCAL double* Probabilities::tmp_vector2 = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::id = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices1 = nullptr; +IGRAPH_THREAD_LOCAL int* Probabilities::vertices2 = nullptr; IGRAPH_THREAD_LOCAL int Probabilities::current_id = 0; Neighbor::Neighbor() { - next_community1 = 0; - previous_community1 = 0; - next_community2 = 0; - previous_community2 = 0; + next_community1 = nullptr; + previous_community1 = nullptr; + next_community2 = nullptr; + previous_community2 = nullptr; heap_index = -1; } Probabilities::~Probabilities() { - C->memory_used -= memory(); - if (P) { - delete[] P; - } - if (vertices) { - delete[] vertices; - } + delete[] P; + delete[] vertices; } Probabilities::Probabilities(int community) { @@ -97,7 +92,7 @@ Probabilities::Probabilities(int community) { int nb_vertices1 = 0; int nb_vertices2 = 0; - double initial_proba = 1. / double(C->communities[community].size); + double initial_proba = 1. / static_cast(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; @@ -158,7 +153,7 @@ Probabilities::Probabilities(int community) { if (nb_vertices1 > (G->nb_vertices / 2)) { P = new double[G->nb_vertices]; size = G->nb_vertices; - vertices = 0; + vertices = nullptr; 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); @@ -184,7 +179,6 @@ Probabilities::Probabilities(int community) { } } } - C->memory_used += memory(); } Probabilities::Probabilities(int community1, int community2) { @@ -193,14 +187,14 @@ Probabilities::Probabilities(int community1, int community2) { Probabilities* P1 = C->communities[community1].P; Probabilities* P2 = C->communities[community2].P; - double w1 = double(C->communities[community1].size) / double(C->communities[community1].size + C->communities[community2].size); - double w2 = double(C->communities[community2].size) / double(C->communities[community1].size + C->communities[community2].size); + double w1 = C->communities[community1].size / static_cast(C->communities[community1].size + C->communities[community2].size); + double w2 = C->communities[community2].size / static_cast(C->communities[community1].size + C->communities[community2].size); if (P1->size == C->G->nb_vertices) { P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = 0; + vertices = nullptr; if (P2->size == C->G->nb_vertices) { // two full vectors for (int i = 0; i < C->G->nb_vertices; i++) { @@ -223,7 +217,7 @@ Probabilities::Probabilities(int community1, int community2) { if (P2->size == C->G->nb_vertices) { // P1 partial vector, P2 full vector P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = 0; + vertices = nullptr; int j = 0; for (int i = 0; i < P1->size; i++) { @@ -273,7 +267,7 @@ Probabilities::Probabilities(int community1, int community2) { if (nb_vertices1 > (C->G->nb_vertices / 2)) { P = new double[C->G->nb_vertices]; size = C->G->nb_vertices; - vertices = 0; + vertices = nullptr; for (int i = 0; i < C->G->nb_vertices; i++) { P[i] = 0.; } @@ -291,12 +285,10 @@ Probabilities::Probabilities(int community1, int community2) { } } } - - C->memory_used += memory(); } double Probabilities::compute_distance(const Probabilities* P2) const { - double r = 0.; + double r = 0.0; if (vertices) { if (P2->vertices) { // two partial vectors int i = 0; @@ -361,18 +353,10 @@ double Probabilities::compute_distance(const Probabilities* P2) const { return r; } -long Probabilities::memory() { - if (vertices) { - return (sizeof(Probabilities) + long(size) * (sizeof(double) + sizeof(int))); - } else { - return (sizeof(Probabilities) + long(size) * sizeof(double)); - } -} - Community::Community() { - P = 0; - first_neighbor = 0; - last_neighbor = 0; + P = nullptr; + first_neighbor = nullptr; + last_neighbor = nullptr; sub_community_of = -1; sub_communities[0] = -1; sub_communities[1] = -1; @@ -382,17 +366,13 @@ Community::Community() { } Community::~Community() { - if (P) { - delete P; - } + delete P; } Communities::Communities(Graph* graph, int random_walks_length, - long m, igraph_matrix_t *pmerges, + igraph_matrix_int_t *pmerges, igraph_vector_t *pmodularity) { - max_memory = m; - memory_used = 0; G = graph; merges = pmerges; mergeidx = 0; @@ -410,7 +390,6 @@ Communities::Communities(Graph* graph, int random_walks_length, 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; @@ -421,12 +400,6 @@ Communities::Communities(Graph* graph, int random_walks_length, // 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; @@ -452,26 +425,15 @@ Communities::Communities(Graph* graph, int random_walks_length, 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(double) + 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) { + if (N == nullptr) { 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++; */ @@ -498,9 +460,6 @@ Communities::~Communities() { delete[] members; delete[] communities; delete H; - if (min_delta_sigma) { - delete min_delta_sigma; - } delete[] Probabilities::tmp_vector1; delete[] Probabilities::tmp_vector2; @@ -509,22 +468,6 @@ Communities::~Communities() { delete[] Probabilities::vertices2; } -double Community::min_delta_sigma() { - double 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) { @@ -541,9 +484,9 @@ void Community::add_neighbor(Neighbor* N) { // add a new neighbor at the end of } else { first_neighbor = N; if (N->community1 == this_community) { - N->previous_community1 = 0; + N->previous_community1 = nullptr; } else { - N->previous_community2 = 0; + N->previous_community2 = nullptr; } } last_neighbor = N; @@ -593,96 +536,19 @@ 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, double 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); - } - } - - double 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); - } + N->delta_sigma = new_delta_sigma; + H->update(N); } - - void Communities::merge_communities(Neighbor* merge_N) { int c1 = merge_N->community1; int c2 = merge_N->community2; @@ -711,23 +577,11 @@ void Communities::merge_communities(Neighbor* merge_N) { if (communities[c1].P) { delete communities[c1].P; - communities[c1].P = 0; - if (max_memory != -1) { - min_delta_sigma->remove_community(c1); - } + communities[c1].P = nullptr; } 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.; + communities[c2].P = nullptr; } // update the new neighbors @@ -869,11 +723,6 @@ void Communities::merge_communities(Neighbor* merge_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--; } @@ -884,18 +733,12 @@ double Communities::merge_nearest_communities() { 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; @@ -927,18 +770,12 @@ double Communities::merge_nearest_communities() { return d; } -double Communities::compute_delta_sigma(int community1, int community2) { +double Communities::compute_delta_sigma(int community1, int community2) const { 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); diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h index 89c5fecedc8..484079f7061 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_communities.h @@ -59,7 +59,6 @@ #include "walktrap_graph.h" #include "walktrap_heap.h" -#include "igraph_community.h" #include "config.h" namespace igraph { @@ -84,9 +83,8 @@ class Probabilities { int* vertices; // the vertices corresponding to the stored probabilities, 0 if all the probabilities are stored double* 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 + explicit 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 @@ -111,15 +109,13 @@ class Community { double internal_weight; // sum of the weight of the internal edges double 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_communities[2]; // the two sub communities, -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); - double min_delta_sigma(); // compute the minimal delta sigma among all the neighbors of this community Community(); // create an empty community ~Community(); // destructor @@ -127,16 +123,11 @@ class Community { 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_matrix_int_t *merges; + igraph_integer_t 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 @@ -150,23 +141,18 @@ class 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 + igraph_matrix_int_t *merges = nullptr, + igraph_vector_t *modularity = nullptr); // 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) + double compute_delta_sigma(int c1, int c2) const; // compute delta_sigma(c1,c2) void remove_neighbor(Neighbor* N); void add_neighbor(Neighbor* N); void update_neighbor(Neighbor* N, double new_delta_sigma); - - void manage_memory(); - }; } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp index a428f18412b..5cb63bca5a4 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.cpp @@ -54,9 +54,12 @@ // see readme.txt for more details #include "walktrap_graph.h" + #include "igraph_interface.h" + #include -#include // strlen +#include +#include using namespace std; @@ -71,28 +74,23 @@ bool operator<(const Edge& E1, const Edge& E2) { Vertex::Vertex() { degree = 0; - edges = 0; + edges = nullptr; total_weight = 0.; } Vertex::~Vertex() { - if (edges) { - delete[] edges; - } + delete[] edges; } Graph::Graph() { nb_vertices = 0; nb_edges = 0; - vertices = 0; - index = 0; + vertices = nullptr; total_weight = 0.; } Graph::~Graph () { - if (vertices) { - delete[] vertices; - } + delete[] vertices; } class Edge_list { @@ -112,16 +110,11 @@ class Edge_list { V2 = new int[1024]; W = new double[1024]; } + ~Edge_list() { - if (V1) { - delete[] V1; - } - if (V2) { - delete[] V2; - } - if (W) { - delete[] W; - } + delete[] V1; + delete[] V2; + delete[] W; } }; @@ -149,28 +142,27 @@ void Edge_list::add(int v1, int v2, double w) { size++; } -int Graph::convert_from_igraph(const igraph_t *graph, +igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + // Refactoring the walktrap code to support larger graphs is pointless + // as running the algorithm on them would take an impractically long time. + if (no_of_nodes > INT_MAX || no_of_edges > INT_MAX) { + IGRAPH_ERROR("Graph too large for walktrap community detection.", IGRAPH_EINVAL); + } 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); + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + igraph_real_t w = weights ? VECTOR(*weights)[i] : 1.0; + EL.add(IGRAPH_FROM(graph, i), IGRAPH_TO(graph, i), w); } - G.nb_vertices = max_vertex + 1; + G.nb_vertices = no_of_nodes; G.vertices = new Vertex[G.nb_vertices]; G.nb_edges = 0; G.total_weight = 0.0; @@ -185,8 +177,8 @@ int Graph::convert_from_igraph(const igraph_t *graph, } 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)); + int deg = G.vertices[i].degree; + double 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; @@ -204,11 +196,11 @@ int Graph::convert_from_igraph(const igraph_t *graph, } for (int i = 0; i < G.nb_vertices; i++) { - /* Check for zero strength, as it may lead to crashed in walktrap algorithm. + /* Check for zero strength, as it may lead to crashes the in walktrap algorithm. * See https://github.com/igraph/igraph/pull/2043 */ if (G.vertices[i].total_weight == 0) { /* G.vertices will be destroyed by Graph::~Graph() */ - IGRAPH_ERROR("Vertex with zero strength found: all vertices must have positive strength for walktrap", + IGRAPH_ERROR("Vertex with zero strength found: all vertices must have positive strength for walktrap.", IGRAPH_EINVAL); } sort(G.vertices[i].edges, G.vertices[i].edges + G.vertices[i].degree); @@ -229,19 +221,5 @@ int Graph::convert_from_igraph(const igraph_t *graph, return IGRAPH_SUCCESS; } -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/vendor/cigraph/src/community/walktrap/walktrap_graph.h b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h index f0b82455a3a..2347e8cff83 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_graph.h @@ -89,13 +89,10 @@ class Graph { double 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); + igraph_error_t convert_from_igraph(const igraph_t *igraph, const igraph_vector_t *weights); }; } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp index 87904614126..dcb0518f18a 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.cpp @@ -92,7 +92,7 @@ void Neighbor_heap::move_down(int index) { Neighbor* Neighbor_heap::get_first() { if (size == 0) { - return 0; + return nullptr; } else { return H[0]; } @@ -127,10 +127,6 @@ void Neighbor_heap::update(Neighbor* N) { 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; @@ -141,101 +137,6 @@ 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(double))); -} - -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 double[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() { +bool Neighbor_heap::is_empty() const { return (size == 0); } diff --git a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h index 7df9c2e20ee..90b4f31f69b 100644 --- a/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h +++ b/src/vendor/cigraph/src/community/walktrap/walktrap_heap.h @@ -95,38 +95,12 @@ class Neighbor_heap { 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(); + bool is_empty() const; - Neighbor_heap(int max_size); + explicit 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(); - - double* delta_sigma; // the delta_sigma of the stored communities - - Min_delta_sigma_heap(int max_size); - ~Min_delta_sigma_heap(); -}; - } } /* end of namespaces */ diff --git a/src/vendor/cigraph/src/config.h.in b/src/vendor/cigraph/src/config.h.in index d6f1ea62f2a..9c5df6d90d6 100644 --- a/src/vendor/cigraph/src/config.h.in +++ b/src/vendor/cigraph/src/config.h.in @@ -1,19 +1,23 @@ -#ifndef IGRAPH_CONFIG_H -#define IGRAPH_CONFIG_H - -#cmakedefine HAVE_EXPM1 1 -#cmakedefine HAVE_FMIN 1 -#cmakedefine HAVE_FINITE 1 -#cmakedefine HAVE_ISFINITE 1 -#cmakedefine HAVE_LOG2 1 -#cmakedefine HAVE_LOG1P 1 -#cmakedefine HAVE_RINT 1 -#cmakedefine HAVE_RINTF 1 -#cmakedefine HAVE_ROUND 1 -#cmakedefine HAVE_STPCPY 1 +#ifndef IGRAPH_PRIVATE_CONFIG_H +#define IGRAPH_PRIVATE_CONFIG_H + +#include "igraph_config.h" + #cmakedefine HAVE_STRCASECMP 1 +#cmakedefine HAVE_STRNCASECMP 1 #cmakedefine HAVE__STRICMP 1 +#cmakedefine HAVE__STRNICMP 1 #cmakedefine HAVE_STRDUP 1 +#cmakedefine HAVE_STRNDUP 1 +#cmakedefine HAVE_USELOCALE 1 +#cmakedefine HAVE_XLOCALE 1 +#cmakedefine HAVE__CONFIGTHREADLOCALE 1 + +#cmakedefine HAVE_BUILTIN_OVERFLOW 1 + +#cmakedefine HAVE__UMUL128 1 +#cmakedefine HAVE___UMULH 1 +#cmakedefine HAVE___UINT128_T 1 #cmakedefine HAVE_GLPK 1 #cmakedefine HAVE_LIBXML 1 @@ -26,10 +30,4 @@ #define IGRAPH_F77_SAVE static @TLS_KEYWORD@ #define IGRAPH_THREAD_LOCAL @TLS_KEYWORD@ -#define PACKAGE_VERSION "@PACKAGE_VERSION@" -#define PACKAGE_VERSION_MAJOR @PACKAGE_VERSION_MAJOR@ -#define PACKAGE_VERSION_MINOR @PACKAGE_VERSION_MINOR@ -#define PACKAGE_VERSION_PATCH @PACKAGE_VERSION_PATCH@ -#define PACKAGE_VERSION_PRERELEASE "@PACKAGE_VERSION_PRERELEASE@" - #endif diff --git a/src/vendor/cigraph/src/connectivity/cohesive_blocks.c b/src/vendor/cigraph/src/connectivity/cohesive_blocks.c index aa4fa9f4341..955a0a9e0ca 100644 --- a/src/vendor/cigraph/src/connectivity/cohesive_blocks.c +++ b/src/vendor/cigraph/src/connectivity/cohesive_blocks.c @@ -36,25 +36,13 @@ #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); + igraph_integer_t 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(VECTOR(*ptr)[i]); - } - } -} - -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); + IGRAPH_FREE(VECTOR(*ptr)[i]); /* also sets it to NULL */ } } } @@ -65,23 +53,23 @@ static void igraph_i_cohesive_blocks_free_vectors(igraph_vector_ptr_t *ptr) { * all neighboring components. */ -static int igraph_i_cb_components(igraph_t *graph, +static igraph_error_t igraph_i_cb_components(igraph_t *graph, const igraph_vector_bool_t *excluded, - igraph_vector_long_t *components, - long int *no, + igraph_vector_int_t *components, + igraph_integer_t *no, /* working area follows */ - igraph_vector_long_t *compid, - igraph_dqueue_t *Q, - igraph_vector_t *neis) { + igraph_vector_int_t *compid, + igraph_dqueue_int_t *Q, + igraph_vector_int_t *neis) { - long int no_of_nodes = igraph_vcount(graph); - long int i; - long int cno = 0; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_integer_t 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); + igraph_vector_int_clear(components); + igraph_dqueue_int_clear(Q); + IGRAPH_CHECK(igraph_vector_int_resize(compid, no_of_nodes)); + igraph_vector_int_null(compid); for (i = 0; i < no_of_nodes; i++) { @@ -92,56 +80,56 @@ static int igraph_i_cb_components(igraph_t *graph, continue; } - IGRAPH_CHECK(igraph_dqueue_push(Q, i)); - IGRAPH_CHECK(igraph_vector_long_push_back(components, i)); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); + IGRAPH_CHECK(igraph_vector_int_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; + while (!igraph_dqueue_int_empty(Q)) { + igraph_integer_t node = igraph_dqueue_int_pop(Q); + igraph_integer_t j, n; IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL)); - n = igraph_vector_size(neis); + n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - long int v = (long int) VECTOR(*neis)[j]; + igraph_integer_t v = 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)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(components, v)); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, v)); } } } - } /* while !igraph_dqueue_empty */ + } /* while !igraph_dqueue_int_empty */ - IGRAPH_CHECK(igraph_vector_long_push_back(components, -1)); + IGRAPH_CHECK(igraph_vector_int_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. + * is found, with the entire graph G at its root. * - * This function implements cohesive blocking and + * + * This function implements cohesive blocking and * calculates the complete cohesive block hierarchy of a graph. * + * + * 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. + * https://doi.org/10.2307/3088904 + * * \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. + * list of integers vectors; the cohesive blocks will be stored here. + * Each block is encoded with a vector of type \ref igraph_vector_int_t 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. + * order as the blocks in the \p blocks vector list. * \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 + * ID (i.e. the position in the \p blocks vector list) of its * parent block is stored. For the top block in the hierarchy, - * -1 is stored. + * -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 + * here as an igraph graph. The vertex IDs correspond to the order * of the blocks in the \p blocks vector. * \return Error code. * @@ -195,14 +190,14 @@ static igraph_bool_t igraph_i_cb_isin(const igraph_vector_t *needle, * \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_error_t igraph_cohesive_blocks(const igraph_t *graph, + igraph_vector_int_list_t *blocks, + igraph_vector_int_t *cohesion, + igraph_vector_int_t *parent, igraph_t *block_tree) { /* Some implementation comments. Everything is relatively - straightforward, except, that we need to follow the vertex ids + 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. @@ -217,90 +212,82 @@ int igraph_cohesive_blocks(const igraph_t *graph, Qptr is an integer and points to the next graph to work on. */ + /* In theory, Q could be an igraph_graph_list_t; however, in that case + * we would not be able to pop off graphs from the front of the list as + * all elements of an igraph_graph_list_t are expected to be initialized, + * valid graphs. That's why we use an igraph_vector_ptr_t instead. */ + igraph_vector_ptr_t Q; - igraph_vector_ptr_t Qmapping; - igraph_vector_long_t Qparent; - igraph_vector_long_t Qcohesion; + igraph_vector_int_list_t Qmapping; + igraph_vector_int_t Qparent; + igraph_vector_int_t Qcohesion; igraph_vector_bool_t Qcheck; - long int Qptr = 0; + igraph_integer_t 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_int_list_t separators; + igraph_vector_int_t compvertices; + igraph_vector_int_t components; + igraph_vector_int_t newmapping; igraph_vector_bool_t marked; - igraph_vector_long_t compid; - igraph_dqueue_t bfsQ; - igraph_vector_t neis; + igraph_vector_int_t compid; + igraph_dqueue_int_t bfsQ; + igraph_vector_int_t neis; if (igraph_is_directed(graph)) { - IGRAPH_ERROR("Cohesive blocking only works on undirected graphs", + 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_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); + igraph_vector_int_list_clear(blocks); } if (cohesion) { - igraph_vector_clear(cohesion); + igraph_vector_int_clear(cohesion); } if (parent) { - igraph_vector_clear(parent); + igraph_vector_int_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_VECTOR_INT_LIST_INIT_FINALLY(&Qmapping, 1); - IGRAPH_CHECK(igraph_vector_bool_init(&Qcheck, 1)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &Qcheck); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Qparent, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Qcohesion, 1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&Qcheck, 1); - IGRAPH_CHECK(igraph_vector_ptr_init(&separators, 0)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &separators); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&separators, 0); - 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&compvertices, 0); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&marked, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&bfsQ, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsQ); + IGRAPH_VECTOR_INT_INIT_FINALLY(&compid, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&components, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newmapping, 0); /* 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_OOM(graph_copy, "Insufficient memory for cohesive blocking."); + 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)); + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /*checks=*/ true)); VECTOR(Qcohesion)[0] = conn; VECTOR(Qcheck)[0] = 0; @@ -308,35 +295,30 @@ int igraph_cohesive_blocks(const igraph_t *graph, 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_integer_t mynodes = igraph_vcount(mygraph); + igraph_integer_t i, nsep; + igraph_integer_t no, kept = 0; + igraph_integer_t cptr = 0; + igraph_integer_t nsepv = 0; + igraph_bool_t addedsep = false; + 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)); + nsep = igraph_vector_int_list_size(&separators); /* 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); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&separators, i); + igraph_integer_t j, n = igraph_vector_int_size(v); for (j = 0; j < n; j++) { - long int vv = (long int) VECTOR(*v)[j]; + igraph_integer_t vv = VECTOR(*v)[j]; if (!VECTOR(marked)[vv]) { nsepv++; - VECTOR(marked)[vv] = 1; + VECTOR(marked)[vv] = true; } } } @@ -355,46 +337,36 @@ int igraph_cohesive_blocks(const igraph_t *graph, 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_int_push_back(&components, i)); } } - IGRAPH_CHECK(igraph_vector_long_push_back(&components, -1)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_vector_int_clear(&compvertices); - while (1) { - long int v = VECTOR(components)[cptr++]; + while (true) { + igraph_integer_t v = VECTOR(components)[cptr++]; if (v < 0) { break; } - IGRAPH_CHECK(igraph_vector_push_back(&compvertices, v)); + IGRAPH_CHECK(igraph_vector_int_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_CHECK_OOM(newgraph, "Insufficient memory for cohesive blocking."); 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_vss_vector(&compvertices), + IGRAPH_SUBGRAPH_AUTO, + /*map=*/ NULL, + /*invmap=*/ &newmapping)); IGRAPH_FINALLY(igraph_destroy, newgraph); IGRAPH_CHECK(igraph_maxdegree(newgraph, &maxdeg, igraph_vss_all(), @@ -404,56 +376,50 @@ int igraph_cohesive_blocks(const igraph_t *graph, 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_vector_int_list_push_back_copy(&Qmapping, &newmapping)); 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_int_push_back(&Qcohesion, newconn)); + IGRAPH_CHECK(igraph_vector_int_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_FINALLY_CLEAN(2); } } - 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); + VECTOR(Q)[Qptr] = NULL; Qptr++; } - igraph_vector_long_destroy(&components); - igraph_vector_long_destroy(&compid); - igraph_dqueue_destroy(&bfsQ); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&newmapping); + igraph_vector_int_destroy(&components); + igraph_vector_int_destroy(&compid); + igraph_dqueue_int_destroy(&bfsQ); + igraph_vector_int_destroy(&neis); igraph_vector_bool_destroy(&marked); - igraph_vector_destroy(&compvertices); - igraph_vector_ptr_destroy(&separators); - IGRAPH_FINALLY_CLEAN(7); + igraph_vector_int_destroy(&compvertices); + igraph_vector_int_list_destroy(&separators); + IGRAPH_FINALLY_CLEAN(8); if (blocks || cohesion || parent || block_tree) { - igraph_integer_t noblocks = (igraph_integer_t) Qptr, badblocks = 0; + igraph_integer_t noblocks = Qptr, badblocks = 0; igraph_vector_bool_t removed; - long int i, resptr = 0; - igraph_vector_long_t rewritemap; + igraph_integer_t i, resptr = 0; + igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_init(&rewritemap, noblocks)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &rewritemap); for (i = 1; i < noblocks; i++) { - long int p = VECTOR(Qparent)[i]; + igraph_integer_t p = VECTOR(Qparent)[i]; while (VECTOR(removed)[p]) { p = VECTOR(Qparent)[p]; } @@ -465,16 +431,19 @@ int igraph_cohesive_blocks(const igraph_t *graph, /* 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); + igraph_integer_t j, n, p = VECTOR(Qparent)[i]; + igraph_vector_int_t *mapping, *pmapping; - if (!pmapping) { + if (p == 0) { continue; } + + mapping = igraph_vector_int_list_get_ptr(&Qmapping, i); + pmapping = igraph_vector_int_list_get_ptr(&Qmapping, p); + + n = igraph_vector_int_size(mapping); for (j = 0; j < n; j++) { - long int v = (long int) VECTOR(*mapping)[j]; + igraph_integer_t v = VECTOR(*mapping)[j]; VECTOR(*mapping)[j] = VECTOR(*pmapping)[v]; } } @@ -483,20 +452,20 @@ int igraph_cohesive_blocks(const igraph_t *graph, 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; + igraph_integer_t j, ic; + igraph_vector_int_t *ivec; if (!VECTOR(Qcheck)[i] || VECTOR(removed)[i]) { continue; } - ivec = VECTOR(Qmapping)[i]; + ivec = igraph_vector_int_list_get_ptr(&Qmapping, i); ic = VECTOR(Qcohesion)[i]; for (j = 1; j < noblocks; j++) { - igraph_vector_t *jvec; - long int jc; + igraph_vector_int_t *jvec; + igraph_integer_t jc; if (j == i || !VECTOR(Qcheck)[j] || VECTOR(removed)[j]) { continue; } - jvec = VECTOR(Qmapping)[j]; + jvec = igraph_vector_int_list_get_ptr(&Qmapping, j); jc = VECTOR(Qcohesion)[j]; if (igraph_i_cb_isin(ivec, jvec) && jc >= ic) { badblocks++; @@ -509,28 +478,25 @@ int igraph_cohesive_blocks(const igraph_t *graph, noblocks -= badblocks; if (blocks) { - IGRAPH_CHECK(igraph_vector_ptr_resize(blocks, noblocks)); + IGRAPH_CHECK(igraph_vector_int_list_resize(blocks, noblocks)); } if (cohesion) { - IGRAPH_CHECK(igraph_vector_resize(cohesion, noblocks)); + IGRAPH_CHECK(igraph_vector_int_resize(cohesion, noblocks)); } if (parent) { - IGRAPH_CHECK(igraph_vector_resize(parent, noblocks)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t p = VECTOR(Qparent)[i]; while (p >= 0 && VECTOR(removed)[p]) { p = VECTOR(Qparent)[p]; } @@ -543,28 +509,31 @@ int igraph_cohesive_blocks(const igraph_t *graph, } } if (blocks) { - VECTOR(*blocks)[resptr] = VECTOR(Qmapping)[i]; - VECTOR(Qmapping)[i] = 0; + IGRAPH_CHECK( + igraph_vector_int_update( + igraph_vector_int_list_get_ptr(blocks, resptr), + igraph_vector_int_list_get_ptr(&Qmapping, i) + ) + ); + igraph_vector_int_clear(igraph_vector_int_list_get_ptr(&Qmapping, i)); } 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_integer_t num_vertices = igraph_vcount(graph); + igraph_vector_int_t *orig = igraph_vector_int_list_get_ptr(blocks, 0); + IGRAPH_CHECK(igraph_vector_int_resize(orig, num_vertices)); + for (i = 0; i < num_vertices; i++) { + VECTOR(*orig)[i] = i; } - 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); + igraph_vector_int_t edges; + igraph_integer_t eptr = 0; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, noblocks * 2 - 2); for (i = 1; i < Qptr; i++) { if (VECTOR(removed)[i]) { continue; @@ -575,28 +544,24 @@ int igraph_cohesive_blocks(const igraph_t *graph, IGRAPH_CHECK(igraph_create(block_tree, &edges, noblocks, IGRAPH_DIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_long_destroy(&rewritemap); + igraph_vector_int_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_vector_int_destroy(&Qcohesion); + igraph_vector_int_destroy(&Qparent); + igraph_vector_int_list_destroy(&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); + IGRAPH_FINALLY_CLEAN(2); /* + the elements of Q, they were already destroyed */ - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/connectivity/components.c b/src/vendor/cigraph/src/connectivity/components.c index 7de37d110e8..34592300157 100644 --- a/src/vendor/cigraph/src/connectivity/components.c +++ b/src/vendor/cigraph/src/connectivity/components.c @@ -27,7 +27,6 @@ #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" @@ -36,17 +35,32 @@ #include "core/interruption.h" #include "operators/subgraph.h" -#include +static igraph_error_t igraph_i_connected_components_weak( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +); +static igraph_error_t igraph_i_connected_components_strong( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +); -static int igraph_i_clusters_weak(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 (deprecated alias). + * + * \deprecated-by igraph_connected_components 0.10 + */ -static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, - igraph_vector_t *csize, igraph_integer_t *no); +igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no, + igraph_connectedness_t mode) { + return igraph_connected_components(graph, membership, csize, no, mode); +} /** * \ingroup structural - * \function igraph_clusters + * \function igraph_connected_components * \brief Calculates the (weakly or strongly) connected components in a graph. * * \param graph The graph object to analyze. @@ -75,130 +89,137 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb * 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) { +igraph_error_t igraph_connected_components( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_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); + return igraph_i_connected_components_weak(graph, membership, csize, no); } else if (mode == IGRAPH_STRONG) { - return igraph_i_clusters_strong(graph, membership, csize, no); + return igraph_i_connected_components_strong(graph, membership, csize, no); } - IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot calculate connected components.", IGRAPH_EINVAL); } -static int igraph_i_clusters_weak(const igraph_t *graph, igraph_vector_t *membership, - igraph_vector_t *csize, igraph_integer_t *no) { +static igraph_error_t igraph_i_connected_components_weak( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + bool *already_added; + igraph_integer_t first_node, act_cluster_size = 0, no_of_clusters = 0; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - long int i; - igraph_vector_t neis = IGRAPH_VECTOR_NULL; + igraph_integer_t i; + igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; - already_added = IGRAPH_CALLOC(no_of_nodes, char); - if (already_added == 0) { - IGRAPH_ERROR("Cannot calculate clusters", IGRAPH_ENOMEM); - } + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for calculating weakly connected components."); 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); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); /* Memory for result, csize is dynamically allocated */ if (membership) { - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); } if (csize) { - igraph_vector_clear(csize); + igraph_vector_int_clear(csize); } /* The algorithm */ for (first_node = 0; first_node < no_of_nodes; ++first_node) { - if (already_added[first_node] == 1) { + if (already_added[first_node]) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[first_node] = 1; + already_added[first_node] = true; act_cluster_size = 1; if (membership) { - VECTOR(*membership)[first_node] = no_of_clusters - 1; + VECTOR(*membership)[first_node] = no_of_clusters; } - 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) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node)); + + while ( !igraph_dqueue_int_empty(&q) ) { + igraph_integer_t act_node = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, act_node, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; + if (already_added[neighbor]) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); - already_added[neighbor] = 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + already_added[neighbor] = true; act_cluster_size++; if (membership) { - VECTOR(*membership)[neighbor] = no_of_clusters - 1; + VECTOR(*membership)[neighbor] = no_of_clusters; } } } + no_of_clusters++; if (csize) { - IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); + IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_cluster_size)); } } /* Cleaning up */ if (no) { - *no = (igraph_integer_t) no_of_clusters - 1; + *no = no_of_clusters; } + /* Clean up */ IGRAPH_FREE(already_added); - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&neis); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); - return 0; -} + /* Update cache */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_clusters == 1); -static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *membership, - igraph_vector_t *csize, igraph_integer_t *no) { + return IGRAPH_SUCCESS; +} - long int no_of_nodes = igraph_vcount(graph); - igraph_vector_t next_nei = IGRAPH_VECTOR_NULL; +static igraph_error_t igraph_i_connected_components_strong( + const igraph_t *graph, igraph_vector_int_t *membership, + igraph_vector_int_t *csize, igraph_integer_t *no +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; - long int i, n, num_seen; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_integer_t i, n, num_seen; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - long int no_of_clusters = 1; - long int act_cluster_size; + igraph_integer_t no_of_clusters = 0; + igraph_integer_t act_cluster_size; - igraph_vector_t out = IGRAPH_VECTOR_NULL; + igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); if (membership) { - IGRAPH_CHECK(igraph_vector_resize(membership, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); } - IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); - igraph_vector_null(&out); + igraph_vector_int_null(&out); if (csize) { - igraph_vector_clear(csize); + igraph_vector_int_clear(csize); } IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); @@ -213,25 +234,24 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb continue; } - IGRAPH_CHECK(igraph_dqueue_push(&q, i)); - while (!igraph_dqueue_empty(&q)) { - long int act_node = (long int) igraph_dqueue_back(&q); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act_node = igraph_dqueue_int_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]; + igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; if (VECTOR(next_nei)[neighbor] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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); + IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node)); + igraph_dqueue_int_pop_back(&q); num_seen++; if (num_seen % 10000 == 0) { @@ -255,11 +275,11 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb /* 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 */ + igraph_vector_int_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); + while (!igraph_vector_int_empty(&out)) { + igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); if (VECTOR(next_nei)[grandfather] != 0) { continue; @@ -267,9 +287,9 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb VECTOR(next_nei)[grandfather] = 1; act_cluster_size = 1; if (membership) { - VECTOR(*membership)[grandfather] = no_of_clusters - 1; + VECTOR(*membership)[grandfather] = no_of_clusters; } - IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); num_seen++; if (num_seen % 10000 == 0) { @@ -279,20 +299,20 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb IGRAPH_ALLOW_INTERRUPTION(); } - while (!igraph_dqueue_empty(&q)) { - long int act_node = (long int) igraph_dqueue_pop_back(&q); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act_node = igraph_dqueue_int_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]; + igraph_integer_t neighbor = VECTOR(*tmp)[i]; if (VECTOR(next_nei)[neighbor] != 0) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); VECTOR(next_nei)[neighbor] = 1; act_cluster_size++; if (membership) { - VECTOR(*membership)[neighbor] = no_of_clusters - 1; + VECTOR(*membership)[neighbor] = no_of_clusters; } num_seen++; @@ -307,28 +327,33 @@ static int igraph_i_clusters_strong(const igraph_t *graph, igraph_vector_t *memb no_of_clusters++; if (csize) { - IGRAPH_CHECK(igraph_vector_push_back(csize, act_cluster_size)); + IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_cluster_size)); } } IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); if (no) { - *no = (igraph_integer_t) no_of_clusters - 1; + *no = no_of_clusters; } - /* Clean up, return */ - + /* Clean up */ igraph_adjlist_destroy(&adjlist); - igraph_vector_destroy(&out); - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&next_nei); + igraph_vector_int_destroy(&out); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&next_nei); IGRAPH_FINALLY_CLEAN(4); - return 0; + /* Update cache */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_clusters == 1); + if (no_of_clusters == 1) { + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, 1); + } + + return IGRAPH_SUCCESS; } -static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); +static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); /** * \ingroup structural @@ -347,6 +372,12 @@ static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); * argument that led us to change the definition: * https://github.com/igraph/igraph/issues/1539 * + * + * The return value of this function is cached in the graph itself, separately + * for weak and strong connectivity. Calling the function multiple times with + * no modifications to the graph in between will return a cached value in O(1) + * time. + * * \param graph The graph object to analyze. * \param res Pointer to a logical variable, the result will be stored * here. @@ -363,101 +394,112 @@ static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); * plus the number of edges in the graph. */ -int igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, +igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, igraph_connectedness_t mode) { - long int no_of_nodes = igraph_vcount(graph); + igraph_cached_property_t prop; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no; - 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 (!igraph_is_directed(graph)) { + mode = IGRAPH_WEAK; } - if (no_of_nodes == 1) { - *res = 1; - return IGRAPH_SUCCESS; + switch (mode) { + case IGRAPH_WEAK: + prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED; + break; + + case IGRAPH_STRONG: + prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED; + break; + + default: + IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); } - 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; + IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res); + 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 = false; + } else if (no_of_nodes == 1) { + *res = true; + } else if (mode == IGRAPH_WEAK) { + IGRAPH_CHECK(igraph_is_connected_weak(graph, res)); + } else { /* mode == IGRAPH_STRONG */ /* 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; + *res = false; + } else { + IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no)); + *res = (no == 1); } - - retval = igraph_i_clusters_strong(graph, NULL, NULL, &no); - *res = (no == 1); - return retval; } - IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); -} + /* Cache updates are done in igraph_i_connected_components_strong() and + * igraph_is_connected_weak() because those might be called from other + * places and we want to make use of the caching if so */ -static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { + return IGRAPH_SUCCESS; +} - 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; +static igraph_error_t igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t no_of_nodes = igraph_vcount(graph), no_of_edges = igraph_ecount(graph); + igraph_integer_t added_count; + bool *already_added; + igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; + igraph_dqueue_int_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; + *res = false; + goto exit; } /* A connected graph has at least |V| - 1 edges. */ if (no_of_edges < no_of_nodes - 1) { - *res = 0; - return IGRAPH_SUCCESS; + *res = false; + goto exit; } - already_added = IGRAPH_CALLOC(no_of_nodes, char); - if (already_added == 0) { - IGRAPH_ERROR("Weak connectedness check failed.", IGRAPH_ENOMEM); - } + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for computing weakly connected components."); IGRAPH_FINALLY(igraph_free, already_added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 10); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); /* Try to find at least two clusters */ - already_added[0] = 1; - IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + already_added[0] = true; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); added_count = 1; - while ( !igraph_dqueue_empty(&q)) { + while ( !igraph_dqueue_int_empty(&q)) { IGRAPH_ALLOW_INTERRUPTION(); - long int actnode = (long int) igraph_dqueue_pop(&q); + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) actnode, IGRAPH_ALL)); - long int nei_count = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); - for (long int i = 0; i < nei_count; i++) { - long int neighbor = (long int) VECTOR(neis)[i]; + for (igraph_integer_t i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; if (already_added[neighbor]) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); added_count++; - already_added[neighbor] = 1; + already_added[neighbor] = true; 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); + igraph_dqueue_int_clear(&q); break; } } @@ -467,31 +509,42 @@ static int igraph_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { *res = (added_count == no_of_nodes); IGRAPH_FREE(already_added); - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&neis); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); +exit: + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res); + if (igraph_is_directed(graph) && *res == 0) { + /* If the graph is not weakly connected, it is not strongly connected + * either so we can also cache that */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res); + } + return IGRAPH_SUCCESS; } /** * \function igraph_decompose_destroy - * \brief Free the memory allocated by \ref igraph_decompose(). + * \brief Frees the contents of a pointer vector holding graphs. * * 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. + * \p complist itself. Use \ref igraph_vector_ptr_destroy() to destroy + * \p complist. * - * \param complist The list of graph components, as returned by - * \ref igraph_decompose(). + * \param complist The list of graphs to destroy. * - * Time complexity: O(c), c is the number of components. + * Time complexity: O(n), n is the number of items. + * + * \deprecated 0.10.0 */ void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { - long int i; - for (i = 0; i < igraph_vector_ptr_size(complist); i++) { + igraph_integer_t i, n; + + n = igraph_vector_ptr_size(complist); + for (i = 0; i < n; i++) { if (VECTOR(*complist)[i] != 0) { igraph_destroy(VECTOR(*complist)[i]); IGRAPH_FREE(VECTOR(*complist)[i]); @@ -499,29 +552,26 @@ void igraph_decompose_destroy(igraph_vector_ptr_t *complist) { } } -static int igraph_i_decompose_weak(const igraph_t *graph, - igraph_vector_ptr_t *components, - long int maxcompno, long int minelements); +static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements); -static int igraph_i_decompose_strong(const igraph_t *graph, - igraph_vector_ptr_t *components, - long int maxcompno, long int minelements); +static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements); /** * \function igraph_decompose - * \brief Decompose a graph into connected components. + * \brief Decomposes 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.) + * Creates a 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 when there is only a single 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 components This list of graphs will contain the individual components. + * It should be initialized before calling this function and will be resized + * to hold the graphs. * \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 @@ -543,9 +593,9 @@ static int igraph_i_decompose_strong(const igraph_t *graph, * \example examples/simple/igraph_decompose.c */ -int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, +igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, igraph_connectedness_t mode, - long int maxcompno, long int minelements) { + igraph_integer_t maxcompno, igraph_integer_t minelements) { if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { return igraph_i_decompose_weak(graph, components, maxcompno, minelements); } else if (mode == IGRAPH_STRONG) { @@ -555,41 +605,37 @@ int igraph_decompose(const igraph_t *graph, igraph_vector_ptr_t *components, 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) { +static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t 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; + igraph_integer_t actstart; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t resco = 0; /* number of graphs created so far */ + bool *already_added; + igraph_dqueue_int_t q; + igraph_vector_int_t verts; + igraph_vector_int_t neis; + igraph_vector_int_t vids_old2new; + igraph_integer_t i; + igraph_t newg; if (maxcompno < 0) { - maxcompno = LONG_MAX; + maxcompno = IGRAPH_INTEGER_MAX; } - igraph_vector_ptr_clear(components); - IGRAPH_FINALLY(igraph_decompose_destroy, components); + igraph_graph_list_clear(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); - } + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for decomponsing graph into connected components."); 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); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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, @@ -604,49 +650,47 @@ static int igraph_i_decompose_weak(const igraph_t *graph, } IGRAPH_ALLOW_INTERRUPTION(); - igraph_vector_clear(&verts); + igraph_vector_int_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)); + already_added[actstart] = true; + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart)); /* add the neighbors, recursively */ - while (!igraph_dqueue_empty(&q) ) { + while (!igraph_dqueue_int_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)); + igraph_integer_t actvert = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actvert, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); /* 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) { + for (i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; + if (already_added[neighbor]) { continue; } /* add neighbor */ - already_added[neighbor] = 1; + already_added[neighbor] = true; /* recursion: append neighbor to the queues */ - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); - IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); } } /* ok, we have a component */ - if (igraph_vector_size(&verts) < minelements) { + if (igraph_vector_int_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), + graph, &newg, igraph_vss_vector(&verts), IGRAPH_SUBGRAPH_AUTO, &vids_old2new, /* invmap = */ 0, /* map_is_prepared = */ 1 )); + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ resco++; /* vids_old2new does not have to be cleaned up here; since we are doing @@ -656,58 +700,57 @@ static int igraph_i_decompose_weak(const igraph_t *graph, } /* for actstart++ */ - igraph_vector_destroy(&vids_old2new); - igraph_vector_destroy(&neis); - igraph_vector_destroy(&verts); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&vids_old2new); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&verts); + igraph_dqueue_int_destroy(&q); IGRAPH_FREE(already_added); - IGRAPH_FINALLY_CLEAN(6); /* + components */ + IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_decompose_strong(const igraph_t *graph, - igraph_vector_ptr_t *components, - long int maxcompno, long int minelements) { +static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, + igraph_graph_list_t *components, + igraph_integer_t maxcompno, igraph_integer_t minelements) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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; + igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; - long int i, n, num_seen; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_integer_t i, n, num_seen; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - long int no_of_clusters = 0; + igraph_integer_t no_of_clusters = 0; - igraph_vector_t out = IGRAPH_VECTOR_NULL; + igraph_vector_int_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; + igraph_vector_int_t verts; + igraph_vector_int_t vids_old2new; + igraph_t newg; if (maxcompno < 0) { - maxcompno = LONG_MAX; + maxcompno = IGRAPH_INTEGER_MAX; } - igraph_vector_ptr_clear(components); - IGRAPH_FINALLY(igraph_decompose_destroy, components); + igraph_graph_list_clear(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_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); - IGRAPH_CHECK(igraph_vector_reserve(&out, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); - igraph_vector_null(&out); + igraph_vector_int_null(&out); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); @@ -732,13 +775,13 @@ static int igraph_i_decompose_strong(const igraph_t *graph, } /* add this node to the queue for this component */ - IGRAPH_CHECK(igraph_dqueue_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); /* consume the tree from this node ("root") recursively * until there is no more */ - while (!igraph_dqueue_empty(&q)) { + while (!igraph_dqueue_int_empty(&q)) { /* this looks up but does NOT consume the queue */ - long int act_node = (long int) igraph_dqueue_back(&q); + igraph_integer_t act_node = igraph_dqueue_int_back(&q); /* get all neighbors of this node */ tmp = igraph_adjlist_get(&adjlist, act_node); @@ -750,18 +793,17 @@ static int igraph_i_decompose_strong(const igraph_t *graph, } 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]; + igraph_integer_t neighbor = VECTOR(*tmp)[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)); + IGRAPH_CHECK(igraph_dqueue_int_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_CHECK(igraph_vector_int_push_back(&out, act_node)); /* this consumes the queue, since there's nowhere to go */ - igraph_dqueue_pop_back(&q); + igraph_dqueue_int_pop_back(&q); num_seen++; if (num_seen % 10000 == 0) { @@ -785,13 +827,13 @@ static int igraph_i_decompose_strong(const igraph_t *graph, /* 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 */ + igraph_vector_int_null(&next_nei); /* mark already added vertices */ /* number of components built */ num_seen = 0; - while (!igraph_vector_empty(&out) && no_of_clusters < maxcompno) { + while (!igraph_vector_int_empty(&out) && no_of_clusters < maxcompno) { /* consume the vector from the last element */ - long int grandfather = (long int) igraph_vector_pop_back(&out); + igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); /* been here, done that * NOTE: next_nei is initialized as [0, 0, ...] */ @@ -800,14 +842,14 @@ static int igraph_i_decompose_strong(const igraph_t *graph, } /* collect all the members of this component */ - igraph_vector_clear(&verts); + igraph_vector_int_clear(&verts); /* this node is gone for any future components */ VECTOR(next_nei)[grandfather] = 1; /* add to component */ - IGRAPH_CHECK(igraph_vector_push_back(&verts, grandfather)); - IGRAPH_CHECK(igraph_dqueue_push(&q, grandfather)); + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); num_seen++; if (num_seen % 10000 == 0) { @@ -817,21 +859,21 @@ static int igraph_i_decompose_strong(const igraph_t *graph, IGRAPH_ALLOW_INTERRUPTION(); } - while (!igraph_dqueue_empty(&q)) { + while (!igraph_dqueue_int_empty(&q)) { /* consume the queue from this node */ - long int act_node = (long int) igraph_dqueue_pop_back(&q); + igraph_integer_t act_node = igraph_dqueue_int_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]; + igraph_integer_t neighbor = VECTOR(*tmp)[i]; if (VECTOR(next_nei)[neighbor] != 0) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); VECTOR(next_nei)[neighbor] = 1; /* add to component */ - IGRAPH_CHECK(igraph_vector_push_back(&verts, neighbor)); + IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); num_seen++; if (num_seen % 10000 == 0) { @@ -844,29 +886,27 @@ static int igraph_i_decompose_strong(const igraph_t *graph, } /* ok, we have a component */ - if (igraph_vector_size(&verts) < minelements) { + if (igraph_vector_int_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), + graph, &newg, igraph_vss_vector(&verts), IGRAPH_SUBGRAPH_AUTO, &vids_old2new, /* invmap = */ 0, /* map_is_prepared = */ 1 )); + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ /* 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 + * igraph_vector_int_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); + n = igraph_vector_int_size(&verts); for (i = 0; i < n; i++) { - VECTOR(vids_old2new)[(igraph_integer_t) VECTOR(verts)[i]] = 0; + VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0; } no_of_clusters++; @@ -876,25 +916,26 @@ static int igraph_i_decompose_strong(const igraph_t *graph, /* Clean up, return */ - igraph_vector_destroy(&vids_old2new); - igraph_vector_destroy(&verts); + igraph_vector_int_destroy(&vids_old2new); + igraph_vector_int_destroy(&verts); igraph_adjlist_destroy(&adjlist); - igraph_vector_destroy(&out); - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&next_nei); - IGRAPH_FINALLY_CLEAN(7); /* + components */ + igraph_vector_int_destroy(&out); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&next_nei); + IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_articulation_points - * Find the articulation points in a graph. + * \brief Finds 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. + * the number of (weakly) connected components in the graph. + * + * \param graph The input graph. It will be treated as undirected. * \param res Pointer to an initialized vector, the * articulation points will be stored here. * \return Error code. @@ -904,30 +945,14 @@ static int igraph_i_decompose_strong(const igraph_t *graph, * \sa \ref igraph_biconnected_components(), \ref igraph_clusters(), \ref igraph_bridges() */ -int igraph_articulation_points(const igraph_t *graph, - igraph_vector_t *res) { +igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_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); + return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, res); } /** * \function igraph_biconnected_components - * \brief Calculate biconnected components. + * \brief Calculates biconnected components. * * A graph is biconnected if the removal of any single vertex (and * its incident edges) does not disconnect it. @@ -949,17 +974,13 @@ void igraph_i_free_vectorlist(igraph_vector_ptr_t *list) { * 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 graph The input graph. It will be treated as undirected. + * \param no If not a NULL pointer, 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. @@ -984,68 +1005,60 @@ void igraph_i_free_vectorlist(igraph_vector_ptr_t *list) { * \example examples/simple/igraph_biconnected_components.c */ -int igraph_biconnected_components(const igraph_t *graph, +igraph_error_t 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_int_list_t *tree_edges, + igraph_vector_int_list_t *component_edges, + igraph_vector_int_list_t *components, + igraph_vector_int_t *articulation_points) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t nextptr; + igraph_vector_int_t num, low; igraph_vector_bool_t found; igraph_vector_int_t *adjedges; - igraph_stack_t path; - igraph_vector_t edgestack; + igraph_stack_int_t path; + igraph_stack_int_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_integer_t counter, rootdfs = 0; + igraph_vector_int_t vertex_added; + igraph_integer_t comps = 0; + igraph_vector_int_list_t *mycomponents = components, vcomponents; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&found, no_of_nodes); + + IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); + IGRAPH_STACK_INT_INIT_FINALLY(&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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes); if (no) { *no = 0; } if (tree_edges) { - igraph_vector_ptr_clear(tree_edges); + igraph_vector_int_list_clear(tree_edges); } if (components) { - igraph_vector_ptr_clear(components); + igraph_vector_int_list_clear(components); } if (component_edges) { - igraph_vector_ptr_clear(component_edges); + igraph_vector_int_list_clear(component_edges); } if (articulation_points) { - igraph_vector_clear(articulation_points); + igraph_vector_int_clear(articulation_points); } if (component_edges && !components) { mycomponents = &vcomponents; - IGRAPH_CHECK(igraph_vector_ptr_init(mycomponents, 0)); - IGRAPH_FINALLY(igraph_i_free_vectorlist, mycomponents); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0); } - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { if (VECTOR(low)[i] != 0) { continue; /* already visited */ @@ -1053,27 +1066,27 @@ int igraph_biconnected_components(const igraph_t *graph, IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_stack_push(&path, i)); + IGRAPH_CHECK(igraph_stack_int_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]; + while (!igraph_stack_int_empty(&path)) { + igraph_integer_t n; + igraph_integer_t act = igraph_stack_int_top(&path); + igraph_integer_t 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); + igraph_integer_t edge = VECTOR(*adjedges)[actnext]; + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge)); + IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); VECTOR(low)[nei] = VECTOR(num)[nei] = counter++; } else { /* Update low value if needed */ @@ -1084,9 +1097,9 @@ int igraph_biconnected_components(const igraph_t *graph, 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); + igraph_stack_int_pop(&path); + if (!igraph_stack_int_empty(&path)) { + igraph_integer_t prev = igraph_stack_int_top(&path); /* Update LOW value if needed */ if (VECTOR(low)[act] < VECTOR(low)[prev]) { VECTOR(low)[prev] = VECTOR(low)[act]; @@ -1095,7 +1108,7 @@ int igraph_biconnected_components(const igraph_t *graph, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev)); VECTOR(found)[prev] = 1; } if (no) { @@ -1105,40 +1118,30 @@ int igraph_biconnected_components(const igraph_t *graph, /*------------------------------------*/ /* Record the biconnected component just found */ if (tree_edges || mycomponents) { - igraph_vector_t *v = 0, *v2 = 0; + igraph_vector_int_t *v, *v2; 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); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &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); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &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); + while (!igraph_stack_int_empty(&edgestack)) { + igraph_integer_t e = igraph_stack_int_pop(&edgestack); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); if (tree_edges) { - IGRAPH_CHECK(igraph_vector_push_back(v, e)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(v2, from)); } if (VECTOR(vertex_added)[to] != comps) { VECTOR(vertex_added)[to] = comps; - IGRAPH_CHECK(igraph_vector_push_back(v2, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(v2, to)); } } if (from == prev || to == prev) { @@ -1146,76 +1149,63 @@ int igraph_biconnected_components(const igraph_t *graph, } } - 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); + igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1); + igraph_integer_t ii, no_vert = igraph_vector_int_size(nodes); + igraph_vector_int_t *vv; + + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv)); for (ii = 0; ii < no_vert; ii++) { - long int vert = (long int) VECTOR(*nodes)[ii]; + igraph_integer_t vert = VECTOR(*nodes)[ii]; igraph_vector_int_t *edges = igraph_inclist_get(&inclist, vert); - long int j, nn = igraph_vector_int_size(edges); + igraph_integer_t 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); + igraph_integer_t e = VECTOR(*edges)[j]; + igraph_integer_t 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_int_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_int_empty(&path) */ } - } /* !igraph_stack_empty(&path) */ + } /* !igraph_stack_int_empty(&path) */ if (articulation_points && rootdfs >= 2) { - IGRAPH_CHECK(igraph_vector_push_back(articulation_points, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i)); } } /* i < no_of_nodes */ if (mycomponents != components) { - igraph_i_free_vectorlist(mycomponents); + igraph_vector_int_list_destroy(mycomponents); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_long_destroy(&vertex_added); + igraph_vector_int_destroy(&vertex_added); igraph_inclist_destroy(&inclist); - igraph_vector_destroy(&edgestack); - igraph_stack_destroy(&path); + igraph_stack_int_destroy(&edgestack); + igraph_stack_int_destroy(&path); igraph_vector_bool_destroy(&found); - igraph_vector_long_destroy(&low); - igraph_vector_long_destroy(&num); - igraph_vector_long_destroy(&nextptr); + igraph_vector_int_destroy(&low); + igraph_vector_int_destroy(&num); + igraph_vector_int_destroy(&nextptr); IGRAPH_FINALLY_CLEAN(8); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_bridges - * \brief Find all bridges in a graph. + * \brief Finds all bridges in a graph. * * An edge is a bridge if its removal increases the number of (weakly) * connected components in the graph. @@ -1230,14 +1220,15 @@ int igraph_biconnected_components(const igraph_t *graph, * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), \ref igraph_clusters() */ -int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { +igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges) { + /* 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. Additionally, we use explicit stacks instead of recursion to avoid stack overflow. */ - long no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_inclist_t il; igraph_vector_bool_t visited; igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */ @@ -1249,26 +1240,21 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { 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, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes); - IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes); igraph_vector_int_fill(&incoming_edge, -1); - IGRAPH_CHECK(igraph_stack_int_init(&su, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &su); + IGRAPH_STACK_INT_INIT_FINALLY(&su, 0); + IGRAPH_STACK_INT_INIT_FINALLY(&si, 0); - IGRAPH_CHECK(igraph_stack_int_init(&si, 0)); - IGRAPH_FINALLY(igraph_stack_int_destroy, &si); - - igraph_vector_clear(bridges); + igraph_vector_int_clear(bridges); time = 0; - for (long start = 0; start < no_of_nodes; ++start) { + for (igraph_integer_t start = 0; start < no_of_nodes; ++start) { if (! VECTOR(visited)[start]) { /* Perform a DFS from 'start'. * The top of the su stack is u, the vertex currently being visited. @@ -1279,8 +1265,8 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); while (! igraph_stack_int_empty(&su)) { - long u = igraph_stack_int_pop(&su); - long i = igraph_stack_int_pop(&si); + igraph_integer_t u = igraph_stack_int_pop(&su); + igraph_integer_t i = igraph_stack_int_pop(&si); if (i == 0) { /* We are at the first step of visiting vertex u. */ @@ -1299,7 +1285,7 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { IGRAPH_CHECK(igraph_stack_int_push(&su, u)); IGRAPH_CHECK(igraph_stack_int_push(&si, i+1)); - long edge = (long) VECTOR(*incedges)[i]; + igraph_integer_t edge = VECTOR(*incedges)[i]; igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); if (! VECTOR(visited)[v]) { @@ -1315,12 +1301,12 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { * We are ready to update the 'low' value of its parent w, and decide * whether its incoming edge is a bridge. */ - long edge = VECTOR(incoming_edge)[u]; + igraph_integer_t edge = VECTOR(incoming_edge)[u]; if (edge >= 0) { - long w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ + igraph_integer_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u]; if (VECTOR(low)[u] > VECTOR(vis)[w]) { - IGRAPH_CHECK(igraph_vector_push_back(bridges, edge)); + IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge)); } } } @@ -1346,7 +1332,7 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { * \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 + * \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. @@ -1369,7 +1355,7 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p vertex is an invalid vertex id + * \p vertex is an invalid vertex ID * \cli IGRAPH_EINVMODE * invalid mode argument passed. * \endclist @@ -1382,16 +1368,18 @@ int igraph_bridges(const igraph_t *graph, igraph_vector_t *bridges) { * \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_t igraph_subcomponent( + const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vertex, + igraph_neimode_t mode +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; + bool *already_added; + igraph_integer_t i, vsize; + igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; + + if (vertex < 0 || vertex >= no_of_nodes) { IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && @@ -1399,43 +1387,40 @@ int igraph_subcomponent(const igraph_t *graph, igraph_vector_t *res, igraph_real 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); - } + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for computing subcomponent."); IGRAPH_FINALLY(igraph_free, already_added); - igraph_vector_clear(res); + igraph_vector_int_clear(res); - IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INT_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; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex)); + already_added[vertex] = true; - while (!igraph_dqueue_empty(&q)) { - long int actnode = (long int) igraph_dqueue_pop(&q); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &tmp, (igraph_integer_t) actnode, - mode)); - vsize = igraph_vector_size(&tmp); + IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode)); + vsize = igraph_vector_int_size(&tmp); for (i = 0; i < vsize; i++) { - long int neighbor = (long int) VECTOR(tmp)[i]; + igraph_integer_t neighbor = 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)); + already_added[neighbor] = true; + IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); } } - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&tmp); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&tmp); IGRAPH_FREE(already_added); IGRAPH_FINALLY_CLEAN(3); diff --git a/src/vendor/cigraph/src/connectivity/separators.c b/src/vendor/cigraph/src/connectivity/separators.c index c6723f8e5c0..3f3a958a12e 100644 --- a/src/vendor/cigraph/src/connectivity/separators.c +++ b/src/vendor/cigraph/src/connectivity/separators.c @@ -28,34 +28,33 @@ #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, +static igraph_error_t igraph_i_is_separator(const igraph_t *graph, igraph_vit_t *vit, - long int except, + igraph_integer_t except, igraph_bool_t *res, igraph_vector_bool_t *removed, - igraph_dqueue_t *Q, - igraph_vector_t *neis, - long int no_of_nodes) { + igraph_dqueue_int_t *Q, + igraph_vector_int_t *neis, + igraph_integer_t no_of_nodes) { - long int start = 0; + igraph_integer_t 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_integer_t 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); + igraph_integer_t v = IGRAPH_VIT_GET(*vit); if (!VECTOR(hit)[v]) { nohit++; VECTOR(hit)[v] = 1; @@ -65,7 +64,7 @@ static int igraph_i_is_separator(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); if (nohit >= no_of_nodes - 1) { *res = 0; - return 0; + return IGRAPH_SUCCESS; } } @@ -76,20 +75,20 @@ static int igraph_i_is_separator(const igraph_t *graph, for (IGRAPH_VIT_RESET(*vit); !IGRAPH_VIT_END(*vit); IGRAPH_VIT_NEXT(*vit)) { - VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; + VECTOR(*removed)[ IGRAPH_VIT_GET(*vit) ] = 1; } } else { /* There is an exception */ - long int i; + igraph_integer_t i; for (i = 0, IGRAPH_VIT_RESET(*vit); i < except; i++, IGRAPH_VIT_NEXT(*vit)) { - VECTOR(*removed)[ (long int) IGRAPH_VIT_GET(*vit) ] = 1; + VECTOR(*removed)[ 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; + VECTOR(*removed)[ IGRAPH_VIT_GET(*vit) ] = 1; } } @@ -103,23 +102,23 @@ static int igraph_i_is_separator(const igraph_t *graph, IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_dqueue_push(Q, start)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(Q)) { + igraph_integer_t node = igraph_dqueue_int_pop(Q); + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_neighbors(graph, neis, node, IGRAPH_ALL)); + n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (!VECTOR(*removed)[nei]) { - IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, nei)); VECTOR(*removed)[nei] = 1; } } } - /* Look for the next node that was neighter removed, not visited */ + /* Look for the next node that was neither removed, not visited */ while (start < no_of_nodes && VECTOR(*removed)[start]) { start++; } @@ -127,7 +126,7 @@ static int igraph_i_is_separator(const igraph_t *graph, /* If there is another component, then we have a separator */ *res = (start < no_of_nodes); - return 0; + return IGRAPH_SUCCESS; } /** @@ -146,34 +145,34 @@ static int igraph_i_is_separator(const igraph_t *graph, * \example examples/simple/igraph_is_separator.c */ -int igraph_is_separator(const igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_vector_bool_t removed; - igraph_dqueue_t Q; - igraph_vector_t neis; + igraph_dqueue_int_t Q; + igraph_vector_int_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_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_VECTOR_INT_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_int_destroy(&neis); + igraph_dqueue_int_destroy(&Q); igraph_vector_bool_destroy(&removed); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /** @@ -192,8 +191,7 @@ int igraph_is_separator(const igraph_t *graph, * * \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 candidate The candidate minimal separators. * \param res Pointer to a boolean variable, the result is stored * here. * \return Error code. @@ -205,15 +203,15 @@ int igraph_is_separator(const igraph_t *graph, * \example examples/simple/igraph_is_minimal_separator.c */ -int igraph_is_minimal_separator(const igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_vector_bool_t removed; - igraph_dqueue_t Q; - igraph_vector_t neis; - long int candsize; + igraph_dqueue_int_t Q; + igraph_vector_int_t neis; + igraph_integer_t candsize; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, candidate, &vit)); @@ -222,9 +220,9 @@ int igraph_is_minimal_separator(const igraph_t *graph, 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_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); /* Is it a separator at all? */ IGRAPH_CHECK(igraph_i_is_separator(graph, &vit, -1, res, &removed, @@ -239,7 +237,7 @@ int igraph_is_minimal_separator(const igraph_t *graph, * false for all vertices, then 'candidate' is a minimal * separator. */ - long int i; + igraph_integer_t 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, @@ -248,13 +246,13 @@ int igraph_is_minimal_separator(const igraph_t *graph, (*res) = (*res) ? 0 : 1; /* opposite */ } - igraph_vector_destroy(&neis); - igraph_dqueue_destroy(&Q); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&Q); igraph_vector_bool_destroy(&removed); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /* --------------------------------------------------------------------*/ @@ -262,25 +260,25 @@ int igraph_is_minimal_separator(const igraph_t *graph, #define UPDATEMARK() do { \ (*mark)++; \ if (!(*mark)) { \ - igraph_vector_null(leaveout); \ + igraph_vector_int_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) { +static igraph_error_t igraph_i_connected_components_leaveout(const igraph_adjlist_t *adjlist, + igraph_vector_int_t *components, + igraph_vector_int_t *leaveout, + igraph_integer_t *mark, + igraph_dqueue_int_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_integer_t i, no_of_nodes = igraph_adjlist_size(adjlist); - igraph_dqueue_clear(Q); - igraph_vector_clear(components); + igraph_dqueue_int_clear(Q); + igraph_vector_int_clear(components); for (i = 0; i < no_of_nodes; i++) { @@ -289,114 +287,95 @@ static int igraph_i_clusters_leaveout(const igraph_adjlist_t *adjlist, } VECTOR(*leaveout)[i] = *mark; - igraph_dqueue_push(Q, i); - igraph_vector_push_back(components, i); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(components, i)); - while (!igraph_dqueue_empty(Q)) { - long int act_node = (long int) igraph_dqueue_pop(Q); + while (!igraph_dqueue_int_empty(Q)) { + igraph_integer_t act_node = igraph_dqueue_int_pop(Q); igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, act_node); - long int j, n = igraph_vector_int_size(neis); + igraph_integer_t j, n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei] == *mark) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(Q, nei)); VECTOR(*leaveout)[nei] = *mark; - igraph_vector_push_back(components, nei); + IGRAPH_CHECK(igraph_vector_int_push_back(components, nei)); } } - igraph_vector_push_back(components, -1); + IGRAPH_CHECK(igraph_vector_int_push_back(components, -1)); } UPDATEMARK(); - return 0; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_separators_newsep(const igraph_vector_ptr_t *comps, - const igraph_vector_t *newc) { +static igraph_bool_t igraph_i_separators_is_not_seen_yet( + const igraph_vector_int_list_t *comps, const igraph_vector_int_t *newc +) { - long int co, nocomps = igraph_vector_ptr_size(comps); + igraph_integer_t co, nocomps = igraph_vector_int_list_size(comps); for (co = 0; co < nocomps; co++) { - igraph_vector_t *act = VECTOR(*comps)[co]; - if (igraph_vector_all_e(act, newc)) { - return 0; + igraph_vector_int_t *act = igraph_vector_int_list_get_ptr(comps, co); + if (igraph_vector_int_all_e(act, newc)) { + return false; } } /* If not found, then it is new */ - return 1; + return true; } -static int igraph_i_separators_store(igraph_vector_ptr_t *separators, +static igraph_error_t igraph_i_separators_store(igraph_vector_int_list_t *separators, const igraph_adjlist_t *adjlist, - igraph_vector_t *components, - igraph_vector_t *leaveout, - unsigned long int *mark, - igraph_vector_t *sorter) { + igraph_vector_int_t *components, + igraph_vector_int_t *leaveout, + igraph_integer_t *mark, + igraph_vector_int_t *sorter) { - /* We need to stote N(C), the neighborhood of C, but only if it is + /* We need to store 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); + igraph_integer_t cptr = 0, next, complen = igraph_vector_int_size(components); while (cptr < complen) { - long int saved = cptr; - igraph_vector_clear(sorter); + igraph_integer_t saved = cptr; + igraph_vector_int_clear(sorter); /* Calculate N(C) for the next C */ - while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { + while ( (next = VECTOR(*components)[cptr++]) != -1) { VECTOR(*leaveout)[next] = *mark; } cptr = saved; - while ( (next = (long int) VECTOR(*components)[cptr++]) != -1) { + while ( (next = VECTOR(*components)[cptr++]) != -1) { igraph_vector_int_t *neis = igraph_adjlist_get(adjlist, next); - long int j, nn = igraph_vector_int_size(neis); + igraph_integer_t j, nn = igraph_vector_int_size(neis); for (j = 0; j < nn; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (VECTOR(*leaveout)[nei] != *mark) { - igraph_vector_push_back(sorter, nei); + IGRAPH_CHECK(igraph_vector_int_push_back(sorter, nei)); VECTOR(*leaveout)[nei] = *mark; } } } - igraph_vector_sort(sorter); + igraph_vector_int_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); + if (igraph_i_separators_is_not_seen_yet(separators, sorter)) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(separators, sorter)); } } /* 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(VECTOR(*separators)[i]); - } - } + return IGRAPH_SUCCESS; } /** @@ -427,7 +406,7 @@ static void igraph_i_separators_free(igraph_vector_ptr_t *separators) { * \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 + * are stored here. It is a list of pointers to igraph_vector_int_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 @@ -443,8 +422,9 @@ static void igraph_i_separators_free(igraph_vector_ptr_t *separators) { * \example examples/simple/igraph_minimal_separators.c */ -int igraph_all_minimal_st_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators) { +igraph_error_t igraph_all_minimal_st_separators( + const igraph_t *graph, igraph_vector_int_list_t *separators +) { /* * Some notes about the tricks used here. For finding the components @@ -455,7 +435,7 @@ int igraph_all_minimal_st_separators(const igraph_t *graph, * 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. + * 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. * @@ -464,35 +444,31 @@ int igraph_all_minimal_st_separators(const igraph_t *graph, * the next separator to try as a basis. */ - long int no_of_nodes = igraph_vcount(graph); - igraph_vector_t leaveout; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t leaveout; igraph_vector_bool_t already_tried; - long int try_next = 0; - unsigned long int mark = 1; - long int v; + igraph_integer_t try_next = 0; + igraph_integer_t mark = 1; + igraph_integer_t v; igraph_adjlist_t adjlist; - igraph_vector_t components; - igraph_dqueue_t Q; - igraph_vector_t sorter; + igraph_vector_int_t components; + igraph_dqueue_int_t Q; + igraph_vector_int_t sorter; - igraph_vector_ptr_clear(separators); - IGRAPH_FINALLY(igraph_i_separators_free, separators); + igraph_vector_int_list_clear(separators); - IGRAPH_CHECK(igraph_vector_init(&leaveout, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_destroy, &leaveout); + IGRAPH_VECTOR_INT_INIT_FINALLY(&leaveout, no_of_nodes); 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_VECTOR_INT_INIT_FINALLY(&components, 0); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sorter, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&sorter, no_of_nodes)); /* --------------------------------------------------------------- * INITIALIZATION, we check whether the neighborhoods of the @@ -504,16 +480,16 @@ int igraph_all_minimal_st_separators(const igraph_t *graph, /* Mark v and its neighbors */ igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, v); - long int i, n = igraph_vector_int_size(neis); + igraph_integer_t 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]; + igraph_integer_t nei = VECTOR(*neis)[i]; VECTOR(leaveout)[nei] = mark; } /* Find the components */ - IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, &leaveout, - &mark, &Q)); + IGRAPH_CHECK(igraph_i_connected_components_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, @@ -526,27 +502,30 @@ int igraph_all_minimal_st_separators(const igraph_t *graph, * 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); + while (try_next < igraph_vector_int_list_size(separators)) { + /* copy "basis" out of the vector_list because we are going to + * mutate the vector_list later, and this can potentially invalidate + * the pointer */ + igraph_vector_int_t basis = *(igraph_vector_int_list_get_ptr(separators, try_next)); + igraph_integer_t b, basislen = igraph_vector_int_size(&basis); for (b = 0; b < basislen; b++) { /* Remove N(x) U basis */ - long int x = (long int) VECTOR(*basis)[b]; + igraph_integer_t x = VECTOR(basis)[b]; igraph_vector_int_t *neis = igraph_adjlist_get(&adjlist, x); - long int i, n = igraph_vector_int_size(neis); + igraph_integer_t i, n = igraph_vector_int_size(neis); for (i = 0; i < basislen; i++) { - long int sn = (long int) VECTOR(*basis)[i]; + igraph_integer_t sn = VECTOR(basis)[i]; VECTOR(leaveout)[sn] = mark; } for (i = 0; i < n; i++) { - long int nei = (long int) VECTOR(*neis)[i]; + igraph_integer_t nei = VECTOR(*neis)[i]; VECTOR(leaveout)[nei] = mark; } /* Find the components */ - IGRAPH_CHECK(igraph_i_clusters_leaveout(&adjlist, &components, - &leaveout, &mark, &Q)); + IGRAPH_CHECK(igraph_i_connected_components_leaveout( + &adjlist, &components, &leaveout, &mark, &Q)); /* Store the corresponding separators, N(C) for each component C */ IGRAPH_CHECK(igraph_i_separators_store(separators, &adjlist, @@ -559,85 +538,76 @@ int igraph_all_minimal_st_separators(const igraph_t *graph, /* --------------------------------------------------------------- */ - igraph_vector_destroy(&sorter); - igraph_dqueue_destroy(&Q); + igraph_vector_int_destroy(&sorter); + igraph_dqueue_int_destroy(&Q); igraph_adjlist_destroy(&adjlist); - igraph_vector_destroy(&components); + igraph_vector_int_destroy(&components); igraph_vector_bool_destroy(&already_tried); - igraph_vector_destroy(&leaveout); - IGRAPH_FINALLY_CLEAN(7); /* +1 for separators */ + igraph_vector_int_destroy(&leaveout); + IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } #undef UPDATEMARK -static int igraph_i_minimum_size_separators_append(igraph_vector_ptr_t *old, - igraph_vector_ptr_t *new) { +static igraph_error_t igraph_i_minimum_size_separators_append( + igraph_vector_int_list_t *old, igraph_vector_int_list_t *new +) { - long int olen = igraph_vector_ptr_size(old); - long int nlen = igraph_vector_ptr_size(new); - long int i; + igraph_integer_t olen = igraph_vector_int_list_size(old); + igraph_integer_t j; - for (i = 0; i < nlen; i++) { - igraph_vector_t *newvec = VECTOR(*new)[i]; - long int j; + while (!igraph_vector_int_list_empty(new)) { + igraph_vector_int_t *oldvec; + igraph_vector_int_t *newvec = igraph_vector_int_list_tail_ptr(new); + + /* Check whether the separator is already in `old' */ for (j = 0; j < olen; j++) { - igraph_vector_t *oldvec = VECTOR(*old)[j]; - if (igraph_vector_all_e(oldvec, newvec)) { + igraph_vector_int_t *oldvec = igraph_vector_int_list_get_ptr(old, j); + if (igraph_vector_int_all_e(oldvec, newvec)) { break; } } + if (j == olen) { - IGRAPH_CHECK(igraph_vector_ptr_push_back(old, newvec)); + /* We have found a new separator, append it to `old'. We do it by + * extending it with an empty vector and then swapping it with + * the new vector to be appended */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(old, &oldvec)); + igraph_vector_int_swap(oldvec, newvec); olen++; - } else { - igraph_vector_destroy(newvec); - igraph_free(newvec); } - VECTOR(*new)[i] = 0; + + igraph_vector_int_list_discard_back(new); } - igraph_vector_ptr_clear(new); - return 0; + return IGRAPH_SUCCESS; } -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; +static igraph_error_t igraph_i_minimum_size_separators_topkdeg( + const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t k +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t deg, order; + igraph_integer_t i; - IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_order1(°, &order, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_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_vector_int_destroy(&order); + igraph_vector_int_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); + return IGRAPH_SUCCESS; } /** @@ -653,13 +623,10 @@ static void igraph_i_separators_stcuts_free(igraph_vector_ptr_t *p) { * 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. + * \param separators An initialized list of integer vectors, the separators + * are stored here. It is a list of pointers to igraph_vector_int_t + * objects. Each vector will contain the IDs of the vertices in + * the separator. The separators are returned in an arbitrary order. * \return Error code. * * Time complexity: TODO. @@ -667,14 +634,15 @@ static void igraph_i_separators_stcuts_free(igraph_vector_ptr_t *p) { * \example examples/simple/igraph_minimum_size_separators.c */ -int igraph_minimum_size_separators(const igraph_t *graph, - igraph_vector_ptr_t *separators) { +igraph_error_t igraph_minimum_size_separators( + const igraph_t *graph, igraph_vector_int_list_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t conn; + igraph_vector_int_t X; + igraph_integer_t i, j, k, n; igraph_bool_t issepX; igraph_t Gbar; igraph_vector_t phi; @@ -687,86 +655,59 @@ int igraph_minimum_size_separators(const igraph_t *graph, IGRAPH_EINVAL); } - igraph_vector_ptr_clear(separators); - IGRAPH_FINALLY(igraph_i_separators_free, separators); + igraph_vector_int_list_clear(separators); /* ---------------------------------------------------------------- */ /* 1 Find the vertex connectivity of 'graph' */ - IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, - /* checks= */ 1)); k = conn; + IGRAPH_CHECK(igraph_vertex_connectivity(graph, &conn, /* checks= */ true)); + k = conn; /* Special cases for low connectivity, two exits here! */ if (conn == 0) { /* Nothing to do */ - IGRAPH_FINALLY_CLEAN(1); /* separators */ - return 0; + return IGRAPH_SUCCESS; } else if (conn == 1) { - igraph_vector_t ap; - long int i, n; - IGRAPH_VECTOR_INIT_FINALLY(&ap, 0); + igraph_vector_int_t ap; + IGRAPH_VECTOR_INT_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); + n = igraph_vector_int_size(&ap); + IGRAPH_CHECK(igraph_vector_int_list_resize(separators, n)); 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_int_t *v = igraph_vector_int_list_get_ptr(separators, i); + IGRAPH_CHECK(igraph_vector_int_push_back(v, VECTOR(ap)[i])); } - igraph_vector_destroy(&ap); - IGRAPH_FINALLY_CLEAN(2); /* +1 for separators */ - return 0; + igraph_vector_int_destroy(&ap); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } 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); + IGRAPH_CHECK(igraph_vector_int_list_resize(separators, no_of_nodes)); 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); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(separators, i); + IGRAPH_CHECK(igraph_vector_int_resize(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; + return IGRAPH_SUCCESS; } /* 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)); + IGRAPH_CHECK(igraph_simplify(&graph_copy, /* multiple */ true, /* loops */ true, 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_vector_int_init(&X, conn)); + IGRAPH_FINALLY(igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(separators, &X)); } /* Create Gbar, the Even-Tarjan reduction of graph */ @@ -783,15 +724,14 @@ int igraph_minimum_size_separators(const igraph_t *graph, IGRAPH_ALLOW_INTERRUPTION(); for (j = 0; j < no_of_nodes; j++) { - long int ii = (long int) VECTOR(X)[i]; + igraph_integer_t ii = 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)); + IGRAPH_CHECK(igraph_are_connected(&graph_copy, ii, j, &conn)); if (conn) { continue; /* they are connected */ } @@ -801,41 +741,34 @@ int igraph_minimum_size_separators(const igraph_t *graph, 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, + /* source= */ ii + no_of_nodes, + /* target= */ 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_vector_int_list_t stcuts; + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&stcuts, 0); 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, + /*source=*/ ii + no_of_nodes, + /*target=*/ j, /*capacity=*/ &capacity)); - IGRAPH_CHECK(igraph_i_minimum_size_separators_append(separators, - &stcuts)); - igraph_vector_ptr_destroy(&stcuts); + IGRAPH_CHECK(igraph_i_minimum_size_separators_append(separators, &stcuts)); + igraph_vector_int_list_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_add_edge(&graph_copy, ii, j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, ii + no_of_nodes, j)); + IGRAPH_CHECK(igraph_add_edge(&Gbar, j + no_of_nodes, ii)); IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); IGRAPH_CHECK(igraph_vector_push_back(&capacity, no_of_nodes)); @@ -845,9 +778,9 @@ int igraph_minimum_size_separators(const igraph_t *graph, igraph_vector_destroy(&phi); igraph_destroy(&Gbar); igraph_vector_destroy(&capacity); - igraph_vector_destroy(&X); + igraph_vector_int_destroy(&X); igraph_destroy(&graph_copy); - IGRAPH_FINALLY_CLEAN(6); /* +1 for separators */ + IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/adjacency.c b/src/vendor/cigraph/src/constructors/adjacency.c index 7fbf9de438d..1de5744646f 100644 --- a/src/vendor/cigraph/src/constructors/adjacency.c +++ b/src/vendor/cigraph/src/constructors/adjacency.c @@ -23,116 +23,229 @@ #include "igraph_constructors.h" #include "igraph_adjlist.h" -#include "igraph_attributes.h" #include "igraph_interface.h" +#include "igraph_sparsemat.h" + +static igraph_error_t igraph_i_adjacency_directed( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); +static igraph_error_t igraph_i_adjacency_max( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); +static igraph_error_t igraph_i_adjacency_undirected( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); +static igraph_error_t igraph_i_adjacency_upper( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); +static igraph_error_t igraph_i_adjacency_lower( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); +static igraph_error_t igraph_i_adjacency_min( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +); + +static igraph_error_t igraph_i_adjust_loop_edge_count( + igraph_integer_t* count, igraph_loops_t loops +) { + /* The compiler should be smart enough to figure out that this can be + * inlined */ + switch (loops) { + case IGRAPH_NO_LOOPS: + *count = 0; + break; + case IGRAPH_LOOPS_TWICE: + if (*count & 1) { + IGRAPH_ERROR("Odd number found in the diagonal of the adjacency matrix.", IGRAPH_EINVAL); + } + *count >>= 1; + break; + case IGRAPH_LOOPS_ONCE: + default: + break; + } -static int igraph_i_adjacency_directed(const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges); -static int igraph_i_adjacency_max(const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges); -static int igraph_i_adjacency_upper(const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges); -static int igraph_i_adjacency_lower(const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges); -static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges); + return IGRAPH_SUCCESS; +} -static int igraph_i_adjacency_directed(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { +static igraph_error_t igraph_i_adjacency_directed( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k; for (i = 0; i < no_of_nodes; i++) { for (j = 0; j < no_of_nodes; j++) { - long int M = (long int) MATRIX(*adjmatrix, i, j); + igraph_integer_t M = MATRIX(*adjmatrix, i, j); + if (i == j) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + } for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); } } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_adjacency_max(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { +static igraph_error_t igraph_i_adjacency_max( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M1, M2; for (i = 0; i < no_of_nodes; i++) { - for (j = i; j < no_of_nodes; j++) { - long int M1 = (long int) MATRIX(*adjmatrix, i, j); - long int M2 = (long int) MATRIX(*adjmatrix, j, i); + /* do the loops first */ + M1 = MATRIX(*adjmatrix, i, i); + if (M1) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); if (M1 < 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); } } } - return 0; + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_adjacency_undirected( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + if (!igraph_matrix_is_symmetric(adjmatrix)) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_adjacency_max(adjmatrix, edges, loops); } -static int igraph_i_adjacency_upper(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { +static igraph_error_t igraph_i_adjacency_upper( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M; for (i = 0; i < no_of_nodes; i++) { - for (j = i; j < no_of_nodes; j++) { - long int M = (long int) MATRIX(*adjmatrix, i, j); + /* do the loops first */ + M = MATRIX(*adjmatrix, i, i); + if (M) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M = MATRIX(*adjmatrix, i, j); for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); } } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_adjacency_lower(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { +static igraph_error_t igraph_i_adjacency_lower( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M; for (i = 0; i < no_of_nodes; i++) { - for (j = 0; j <= i; j++) { - long int M = (long int) MATRIX(*adjmatrix, i, j); + for (j = 0; j < i; j++) { + M = MATRIX(*adjmatrix, i, j); + for (k = 0; k < M; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + } + } + + /* do the loops as well */ + M = MATRIX(*adjmatrix, i, i); + if (M) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M, loops)); for (k = 0; k < M; k++) { - IGRAPH_CHECK(igraph_vector_push_back(edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); } } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, igraph_vector_t *edges) { +static igraph_error_t igraph_i_adjacency_min( + const igraph_matrix_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j, k, M1, M2; for (i = 0; i < no_of_nodes; i++) { - for (j = i; j < no_of_nodes; j++) { - long int M1 = (long int) MATRIX(*adjmatrix, i, j); - long int M2 = (long int) MATRIX(*adjmatrix, j, i); + /* do the loops first */ + M1 = MATRIX(*adjmatrix, i, i); + if (M1) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&M1, loops)); + for (k = 0; k < M1; k++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); if (M1 > 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); } } } - return 0; + return IGRAPH_SUCCESS; } + + /** * \ingroup generators * \function igraph_adjacency @@ -145,11 +258,8 @@ static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, igraph_vecto * \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): + * 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 @@ -184,257 +294,368 @@ static int igraph_i_adjacency_min(const igraph_matrix_t *adjmatrix, igraph_vecto * only the lower left triangle (including the diagonal) is * used for creating the edges. * \endclist + * \param loops Constant to specify how the diagonal of the matrix should be + * treated when creating loop edges. + * \clist + * \cli IGRAPH_NO_LOOPS + * Ignore the diagonal of the input matrix and do not create loops. + * \cli IGRAPH_LOOPS_ONCE + * Treat the diagonal entries as the number of loop edges incident on + * the corresponding vertex. + * \cli IGRAPH_LOOPS_TWICE + * Treat the diagonal entries as \em twice the number of loop edges + * incident on the corresponding vertex. Odd numbers in the diagonal + * will return an error code. + * \endclist * \return Error code, * \c IGRAPH_NONSQUARE: non-square matrix. + * \c IGRAPH_EINVAL: Negative entry was found in adjacency matrix, or an + * odd number was found in the diagonal with \c IGRAPH_LOOPS_TWICE * * 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_error_t igraph_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_loops_t loops +) { - igraph_vector_t edges = IGRAPH_VECTOR_NULL; - long int no_of_nodes; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); /* Some checks */ if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { - IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); + IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + } + + if (no_of_nodes != 0 && igraph_matrix_min(adjmatrix) < 0) { + IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, + igraph_matrix_min(adjmatrix)); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges, loops)); break; case IGRAPH_ADJ_MAX: - IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges)); + IGRAPH_CHECK(igraph_i_adjacency_max(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_adjacency_undirected(adjmatrix, &edges, loops)); break; case IGRAPH_ADJ_UPPER: - IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges)); + IGRAPH_CHECK(igraph_i_adjacency_upper(adjmatrix, &edges, loops)); break; case IGRAPH_ADJ_LOWER: - IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges)); + IGRAPH_CHECK(igraph_i_adjacency_lower(adjmatrix, &edges, loops)); break; case IGRAPH_ADJ_MIN: - IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges)); + IGRAPH_CHECK(igraph_i_adjacency_min(adjmatrix, &edges, loops)); break; case IGRAPH_ADJ_PLUS: - IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges)); + IGRAPH_CHECK(igraph_i_adjacency_directed(adjmatrix, &edges, loops)); break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - (mode == IGRAPH_ADJ_DIRECTED))); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_weighted_adjacency_directed( +static igraph_error_t igraph_i_weighted_adjacency_directed( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); -static int igraph_i_weighted_adjacency_plus( + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_plus( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); -static int igraph_i_weighted_adjacency_max( + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_max( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); -static int igraph_i_weighted_adjacency_upper( + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_upper( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); -static int igraph_i_weighted_adjacency_lower( + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_lower( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); -static int igraph_i_weighted_adjacency_min( + igraph_loops_t loops +); +static igraph_error_t igraph_i_weighted_adjacency_min( const igraph_matrix_t *adjmatrix, - igraph_vector_t *edges, + igraph_vector_int_t *edges, igraph_vector_t *weights, - igraph_bool_t loops); + igraph_loops_t loops +); + +static void igraph_i_adjust_loop_edge_weight(igraph_real_t* weight, igraph_loops_t loops) { + /* The compiler should be smart enough to figure out that this can be + * inlined */ + switch (loops) { + case IGRAPH_NO_LOOPS: + *weight = 0.0; + break; + case IGRAPH_LOOPS_TWICE: + *weight /= 2; + break; + case IGRAPH_LOOPS_ONCE: + default: + break; + } +} -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 igraph_error_t igraph_i_weighted_adjacency_directed( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t 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; + if (M != 0.0) { + if (i == j) { + igraph_i_adjust_loop_edge_weight(&M, loops); + if (M == 0.0) { + continue; + } + } + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - 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; + return IGRAPH_SUCCESS; } -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 igraph_error_t igraph_i_weighted_adjacency_plus( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; 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 (loops != IGRAPH_NO_LOOPS) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - if (i == j && !loops) { - continue; - } - if (i == j) { - M /= 2; + } + + for (j = i + 1; j < no_of_nodes; j++) { + M = MATRIX(*adjmatrix, i, j) + MATRIX(*adjmatrix, j, i); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - 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; + return IGRAPH_SUCCESS; } -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 igraph_error_t igraph_i_weighted_adjacency_max( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M1, M2; 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); + /* do the loops first */ + if (loops) { + M1 = MATRIX(*adjmatrix, i, i); + if (M1 != 0.0) { + igraph_i_adjust_loop_edge_weight(&M1, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); if (M1 < M2) { M1 = M2; } - if (M1 == 0.0) { - continue; - } - if (i == j && !loops) { - continue; + if (M1 != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); } - 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; + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_weighted_adjacency_undirected( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + if (!igraph_matrix_is_symmetric(adjmatrix)) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_weighted_adjacency_max(adjmatrix, edges, weights, 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) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; +static igraph_error_t igraph_i_weighted_adjacency_upper( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { + + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; 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; + /* do the loops first */ + if (loops) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - if (i == j && !loops) { - continue; + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + igraph_real_t M = MATRIX(*adjmatrix, i, j); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - 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; + return IGRAPH_SUCCESS; } -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 igraph_error_t igraph_i_weighted_adjacency_lower( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M; 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; + for (j = 0; j < i; j++) { + M = MATRIX(*adjmatrix, i, j); + if (M != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - if (i == j && !loops) { - continue; + } + + /* do the loops as well */ + if (loops) { + M = MATRIX(*adjmatrix, i, i); + if (M != 0.0) { + igraph_i_adjust_loop_edge_weight(&M, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M)); } - 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; + return IGRAPH_SUCCESS; } -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 igraph_error_t igraph_i_weighted_adjacency_min( + const igraph_matrix_t *adjmatrix, + igraph_vector_int_t *edges, + igraph_vector_t *weights, + igraph_loops_t loops +) { - long int no_of_nodes = igraph_matrix_nrow(adjmatrix); - long int i, j; + igraph_integer_t no_of_nodes = igraph_matrix_nrow(adjmatrix); + igraph_integer_t i, j; + igraph_real_t M1, M2; 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); + /* do the loops first */ + if (loops) { + M1 = MATRIX(*adjmatrix, i, i); + if (M1 != 0.0) { + igraph_i_adjust_loop_edge_weight(&M1, loops); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); + } + } + + /* then the rest */ + for (j = i + 1; j < no_of_nodes; j++) { + M1 = MATRIX(*adjmatrix, i, j); + M2 = MATRIX(*adjmatrix, j, i); if (M1 > M2) { M1 = M2; } - if (M1 == 0.0) { - continue; + if (M1 != 0.0) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, j)); + IGRAPH_CHECK(igraph_vector_push_back(weights, M1)); } - 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; + return IGRAPH_SUCCESS; } /** @@ -490,10 +711,19 @@ static int igraph_i_weighted_adjacency_min( * 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. + * \param weights Pointer to an initialized vector, the weights will be stored here. + * \param loops Constant to specify how the diagonal of the matrix should be + * treated when creating loop edges. + * \clist + * \cli IGRAPH_NO_LOOPS + * Ignore the diagonal of the input matrix and do not create loops. + * \cli IGRAPH_LOOPS_ONCE + * Treat the diagonal entries as the weight of the loop edge incident + * on the corresponding vertex. + * \cli IGRAPH_LOOPS_TWICE + * Treat the diagonal entries as \em twice the weight of the loop edge + * incident on the corresponding vertex. + * \endclist * \return Error code, * \c IGRAPH_NONSQUARE: non-square matrix. * @@ -502,77 +732,70 @@ static int igraph_i_weighted_adjacency_min( * * \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_error_t igraph_weighted_adjacency( + igraph_t *graph, const igraph_matrix_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_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; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + igraph_vector_clear(weights); /* 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)); + weights, loops)); break; case IGRAPH_ADJ_MAX: IGRAPH_CHECK(igraph_i_weighted_adjacency_max(adjmatrix, &edges, - &weights, loops)); + weights, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_weighted_adjacency_undirected(adjmatrix, &edges, + weights, loops)); break; case IGRAPH_ADJ_UPPER: IGRAPH_CHECK(igraph_i_weighted_adjacency_upper(adjmatrix, &edges, - &weights, loops)); + weights, loops)); break; case IGRAPH_ADJ_LOWER: IGRAPH_CHECK(igraph_i_weighted_adjacency_lower(adjmatrix, &edges, - &weights, loops)); + weights, loops)); break; case IGRAPH_ADJ_MIN: IGRAPH_CHECK(igraph_i_weighted_adjacency_min(adjmatrix, &edges, - &weights, loops)); + weights, loops)); break; case IGRAPH_ADJ_PLUS: IGRAPH_CHECK(igraph_i_weighted_adjacency_plus(adjmatrix, &edges, - &weights, loops)); + weights, loops)); break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); } - /* 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_CHECK(igraph_empty(graph, 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)); + if (igraph_vector_int_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ - igraph_vector_destroy(&edges); - igraph_vector_destroy(&weights); - igraph_vector_ptr_destroy(&attr_vec); - IGRAPH_FINALLY_CLEAN(3); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -606,15 +829,15 @@ int igraph_weighted_adjacency(igraph_t *graph, const igraph_matrix_t *adjmatrix, * Time complexity: O(|V|+|E|). * */ -int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, +igraph_error_t 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_integer_t no_of_nodes = igraph_adjlist_size(adjlist); + igraph_integer_t no_of_edges = 0; + igraph_integer_t i; - igraph_vector_t edges; - long int edgeptr = 0; + igraph_vector_int_t edges; + igraph_integer_t edgeptr = 0; duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */ @@ -626,15 +849,15 @@ int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, no_of_edges /= 2; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + IGRAPH_VECTOR_INT_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; + igraph_integer_t j, n = igraph_vector_int_size(neis); + igraph_integer_t loops = 0; for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (nei == i) { loops++; } else { @@ -668,14 +891,537 @@ int igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, } if (mode == IGRAPH_ALL) - IGRAPH_CHECK(igraph_create(graph, &edges, - (igraph_integer_t) no_of_nodes, 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 0)); else - IGRAPH_CHECK(igraph_create(graph, &edges, - (igraph_integer_t) no_of_nodes, 1)); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 1)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_directed( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_max( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + multi = multi > other ? multi : other; + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_min( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + multi = multi < other ? multi : other; + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_upper( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_lower( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to > from) { + continue; + } + igraph_integer_t multi = igraph_sparsemat_iterator_get(&it); + if (to == from) { + IGRAPH_CHECK(igraph_i_adjust_loop_edge_count(&multi, loops)); + } + for (igraph_integer_t count = 0; count < multi; count++) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(edges, to)); + } + } + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_adjacency_undirected( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_loops_t loops +) { + igraph_bool_t sym; + + IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); + if (!sym) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_sparse_adjacency_upper(adjmatrix, edges, loops); +} - igraph_vector_destroy(&edges); +/** + * \ingroup generators + * \function igraph_sparse_adjacency + * \brief Creates a graph from a sparse adjacency matrix. + * + * This has the same functionality as \ref igraph_adjacency(), but uses + * a column-compressed adjacency matrix. + * + * Time complexity: O(|E|), + * where |E| is the number of edges in the graph. + */ + +igraph_error_t igraph_sparse_adjacency(igraph_t *graph, igraph_sparsemat_t *adjmatrix, + igraph_adjacency_t mode, igraph_loops_t loops) { + + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); + igraph_integer_t no_of_nonzeros = igraph_sparsemat_count_nonzero(adjmatrix); + igraph_integer_t approx_no_of_edges; + + if (!igraph_sparsemat_is_cc(adjmatrix)) { + IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed " + "form.", IGRAPH_EINVAL); + } + if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + } + + if (no_of_nodes != 0 && igraph_sparsemat_min(adjmatrix) < 0) { + IGRAPH_ERRORF("Edge counts should be non-negative, found %g.", IGRAPH_EINVAL, + igraph_sparsemat_min(adjmatrix)); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + /* Approximate the number of edges in the graph based on the number of + * nonzero elements in the matrix */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + case IGRAPH_ADJ_PLUS: + case IGRAPH_ADJ_UPPER: + case IGRAPH_ADJ_LOWER: + approx_no_of_edges = no_of_nonzeros; + break; + case IGRAPH_ADJ_UNDIRECTED: + case IGRAPH_ADJ_MAX: + case IGRAPH_ADJ_MIN: + approx_no_of_edges = no_of_nonzeros / 2; + break; + default: + approx_no_of_edges = no_of_nonzeros; + break; + } + + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, approx_no_of_edges * 2)); + + /* Collect the edges */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_sparse_adjacency_max(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_sparse_adjacency_undirected(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_sparse_adjacency_upper(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_sparse_adjacency_lower(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_sparse_adjacency_min(adjmatrix, &edges, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_sparse_adjacency_directed(adjmatrix, &edges, loops)); + break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_max ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + igraph_real_t other; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight = weight > other ? weight : other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_min ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_integer_t e = 0; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight = weight < other ? weight : other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_plus ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_integer_t e = 0; + igraph_real_t other; + + igraph_sparsemat_iterator_init(&it, adjmatrix); + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + if (to < from) { + continue; + } + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } else { + other = igraph_sparsemat_get(adjmatrix, to, from); + weight += other; + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_upper( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to < from) { + continue; + } + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_lower( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to > from) { + continue; + } + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_sparse_weighted_adjacency_undirected ( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_bool_t sym; + + IGRAPH_CHECK(igraph_sparsemat_is_symmetric(adjmatrix, &sym)); + if (!sym) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + return igraph_i_sparse_weighted_adjacency_upper(adjmatrix, edges, weights, loops); +} + + +static igraph_error_t igraph_i_sparse_weighted_adjacency_directed( + igraph_sparsemat_t *adjmatrix, igraph_vector_int_t *edges, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_sparsemat_iterator_t it; + igraph_sparsemat_iterator_init(&it, adjmatrix); + igraph_integer_t e = 0; + + for (; !igraph_sparsemat_iterator_end(&it); igraph_sparsemat_iterator_next(&it)) { + igraph_integer_t from = igraph_sparsemat_iterator_row(&it); + igraph_integer_t to = igraph_sparsemat_iterator_col(&it); + igraph_real_t weight = igraph_sparsemat_iterator_get(&it); + if (to == from) { + igraph_i_adjust_loop_edge_weight(&weight, loops); + } + if (weight != 0) { + VECTOR(*weights)[e/2] = weight; + VECTOR(*edges)[e++] = from; + VECTOR(*edges)[e++] = to; + } + } + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, e/2); /* shrinks */ + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_sparse_weighted_adjacency + * \brief Creates a graph from a weighted sparse adjacency matrix. + * + * This has the same functionality as \ref igraph_weighted_adjacency(), but uses + * a column-compressed adjacency matrix. + * + * Time complexity: O(|E|), + * where |E| is the number of edges in the graph. + */ + + +igraph_error_t igraph_sparse_weighted_adjacency( + igraph_t *graph, igraph_sparsemat_t *adjmatrix, igraph_adjacency_t mode, + igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(adjmatrix); + igraph_integer_t no_of_edges = igraph_sparsemat_count_nonzero(adjmatrix); + + if (!igraph_sparsemat_is_cc(adjmatrix)) { + IGRAPH_ERROR("Sparse adjacency matrix should be in column-compressed form.", IGRAPH_EINVAL); + } + if (no_of_nodes != igraph_sparsemat_ncol(adjmatrix)) { + IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); + + /* Collect the edges */ + switch (mode) { + case IGRAPH_ADJ_DIRECTED: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_directed(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_MAX: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_max(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_UNDIRECTED: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_undirected(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_UPPER: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_upper(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_LOWER: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_lower(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_MIN: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_min(adjmatrix, &edges, + weights, loops)); + break; + case IGRAPH_ADJ_PLUS: + IGRAPH_CHECK(igraph_i_sparse_weighted_adjacency_plus(adjmatrix, &edges, + weights, loops)); + break; + default: + IGRAPH_ERROR("Invalid adjacency mode.", IGRAPH_EINVAL); + } + + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, (mode == IGRAPH_ADJ_DIRECTED))); + IGRAPH_FINALLY(igraph_destroy, graph); + if (igraph_vector_int_size(&edges) > 0) { + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + } + IGRAPH_FINALLY_CLEAN(1); + + /* Cleanup */ + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/atlas-edges.h b/src/vendor/cigraph/src/constructors/atlas-edges.h index 229e8f105ba..7038ed6488b 100644 --- a/src/vendor/cigraph/src/constructors/atlas-edges.h +++ b/src/vendor/cigraph/src/constructors/atlas-edges.h @@ -21,21 +21,15 @@ */ -#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 IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H +#define IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H +#include "igraph_decls.h" #include "igraph_types.h" -const igraph_real_t igraph_i_atlas_edges[] = { +__BEGIN_DECLS + +const igraph_integer_t igraph_i_atlas_edges[] = { 0, 0, 1, 0, 2, 0, @@ -1291,6 +1285,8 @@ const igraph_real_t igraph_i_atlas_edges[] = { 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}; +const igraph_integer_t 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 + +#endif /* IGRAPH_CONSTRUCTURS_ATLAS_EDGES_H */ diff --git a/src/vendor/cigraph/src/constructors/atlas.c b/src/vendor/cigraph/src/constructors/atlas.c index ffa2eaeeec3..1ab4615cf1f 100644 --- a/src/vendor/cigraph/src/constructors/atlas.c +++ b/src/vendor/cigraph/src/constructors/atlas.c @@ -59,24 +59,24 @@ * * \example examples/simple/igraph_atlas.c */ -int igraph_atlas(igraph_t *graph, int number) { +igraph_error_t igraph_atlas(igraph_t *graph, igraph_integer_t number) { igraph_integer_t pos, n, e; - igraph_vector_t v = IGRAPH_VECTOR_NULL; + igraph_vector_int_t v = IGRAPH_VECTOR_NULL; if (number < 0 || - number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(long int))) { + number >= (int) (sizeof(igraph_i_atlas_edges_pos) / sizeof(igraph_i_atlas_edges_pos[0]))) { 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]; + pos = igraph_i_atlas_edges_pos[number]; + n = igraph_i_atlas_edges[pos]; + e = igraph_i_atlas_edges[pos + 1]; IGRAPH_CHECK(igraph_create(graph, - igraph_vector_view(&v, igraph_i_atlas_edges + pos + 2, + igraph_vector_int_view(&v, igraph_i_atlas_edges + pos + 2, e * 2), n, IGRAPH_UNDIRECTED)); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/basic_constructors.c b/src/vendor/cigraph/src/constructors/basic_constructors.c index 9f16e08c28a..93d1f422093 100644 --- a/src/vendor/cigraph/src/constructors/basic_constructors.c +++ b/src/vendor/cigraph/src/constructors/basic_constructors.c @@ -44,17 +44,17 @@ * \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 + * 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. + * 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. + * vertex ID. * * Time complexity: O(|V|+|E|), * |V| is the number of vertices, @@ -63,33 +63,34 @@ * * \example examples/simple/igraph_create.c */ -int igraph_create(igraph_t *graph, const igraph_vector_t *edges, +igraph_error_t igraph_create(igraph_t *graph, const igraph_vector_int_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; + igraph_bool_t has_edges = igraph_vector_int_size(edges) > 0; + igraph_integer_t max; - if (! igraph_finite(max)) { - IGRAPH_ERROR("Invalid (non-finite or NaN) vertex index when creating graph.", IGRAPH_EINVAL); + if (igraph_vector_int_size(edges) % 2 != 0) { + IGRAPH_ERROR("Invalid (odd) edges vector.", IGRAPH_EINVEVECTOR); } - 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); + if (has_edges && !igraph_vector_int_isininterval(edges, 0, IGRAPH_VCOUNT_MAX-1)) { + IGRAPH_ERROR("Invalid (negative or too large) vertex ID.", IGRAPH_EINVVID); } + /* The + 1 here cannot overflow as above we have already + * checked that vertex IDs are within range. */ + max = has_edges ? igraph_vector_int_max(edges) + 1 : 0; + 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)); + n = igraph_vcount(graph); + if (n < max) { + IGRAPH_CHECK(igraph_add_vertices(graph, (max - n), 0)); } IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); } IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -99,16 +100,16 @@ int igraph_create(igraph_t *graph, const igraph_vector_t *edges, * * 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 + * 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. + * Note that only graphs which have vertex IDs smaller than + * the largest value representable by 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 n The number of vertices in the graph; a non-negative integer. * \param directed Logical constant; gives whether the graph should be * directed. Supported values are: * \clist @@ -118,8 +119,9 @@ int igraph_create(igraph_t *graph, const igraph_vector_t *edges, * 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. + * graph. Don't forget to supply an additional -1 after the last + * (meaningful) argument. The \p first parameter is present for + * technical reasons and represents the first variadic argument. * \return Error code. * * Time complexity: O(|V|+|E|), the number of vertices plus the number @@ -128,25 +130,24 @@ int igraph_create(igraph_t *graph, const igraph_vector_t *edges, * \example examples/simple/igraph_small.c */ -int igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, - ...) { - igraph_vector_t edges; +igraph_error_t igraph_small(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, + int first, ...) { + igraph_vector_int_t edges; va_list ap; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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); + va_start(ap, first); + int num = first; + while (num != -1) { + igraph_vector_int_push_back(&edges, num); + num = va_arg(ap, int); } + va_end(ap); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/circulant.c b/src/vendor/cigraph/src/constructors/circulant.c new file mode 100644 index 00000000000..34a796fa1b1 --- /dev/null +++ b/src/vendor/cigraph/src/constructors/circulant.c @@ -0,0 +1,111 @@ +/* + 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 . +*/ + +#include "igraph_constructors.h" + +#include "igraph_interface.h" + +#include "math/safe_intop.h" + +/** + * \function igraph_circulant + * \brief Creates a circulant graph. + * + * A circulant graph G(n, shifts) consists of \p n vertices v_0, ..., + * v_(n-1) such that for each \c s_i in the list of offsets \p shifts, \c v_j is + * connected to v_((j + s_i) mod n) for all j. + * + * + * The function can generate either directed or undirected graphs. It does not generate + * multi-edges or self-loops. + * + * \param graph Pointer to an uninitialized graph object, the result will + * be stored here. + * \param n Integer, the number of vertices in the circulant graph. + * \param shifts Integer vector, a list of the offsets within the circulant graph. + * \param directed Boolean, whether to create a directed graph. + * \return Error code. + * + * \sa \ref igraph_ring(), \ref igraph_generalized_petersen(), \ref igraph_extended_chordal_ring() + * + * Time complexity: O(|V||shifts|), the number of vertices in the graph times the number + * of shifts. + */ + +igraph_error_t igraph_circulant(igraph_t *graph, igraph_integer_t n, const igraph_vector_int_t *shifts, igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_vector_bool_t shift_seen; + igraph_integer_t i, j; + igraph_integer_t limit; + igraph_integer_t shift_size = igraph_vector_int_size(shifts); + + if (n < 0) { + IGRAPH_ERRORF("Number of nodes = %" IGRAPH_PRId " must be non-negative.", IGRAPH_EINVAL, n); + } + if (n == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + { + igraph_integer_t size; + IGRAPH_SAFE_MULT(n, shift_size, &size); + IGRAPH_SAFE_MULT(size, 2, &size); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); + } + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&shift_seen, n); + VECTOR(shift_seen)[0] = 1; /* do not allow self loops */ + + for (i = 0; i < shift_size; i++) { + /* simplify the shift */ + igraph_integer_t shift = VECTOR(*shifts)[i] % n; + if (shift < 0) { + shift += n; + } + if (!directed) { + if (shift >= (n + 1) / 2) { + shift = n - shift; + } + } + + /* only use shift if non-zero and we haven't seen it before */ + if (!VECTOR(shift_seen)[shift]) { + if (n % 2 == 0 && shift == n / 2 && !directed) { + limit = n / 2; /* this to avoid doubling up the n/2 shift for even n and undirected graph */ + } else { + limit = n; + } + for (j = 0; j < limit; j++) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (j + shift) % n)); + } + + VECTOR(shift_seen)[shift] = 1; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); + igraph_vector_bool_destroy(&shift_seen); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/constructors/de_bruijn.c b/src/vendor/cigraph/src/constructors/de_bruijn.c index 1a1abe0cefb..080321b3523 100644 --- a/src/vendor/cigraph/src/constructors/de_bruijn.c +++ b/src/vendor/cigraph/src/constructors/de_bruijn.c @@ -24,6 +24,8 @@ #include "igraph_interface.h" +#include "math/safe_intop.h" + /** * \function igraph_de_bruijn * \brief Generate a de Bruijn graph. @@ -53,15 +55,14 @@ * * 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) { +igraph_error_t 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; + igraph_integer_t no_of_nodes, no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j; if (m < 0 || n < 0) { IGRAPH_ERROR("`m' and `n' should be non-negative in a de Bruijn graph", @@ -75,25 +76,36 @@ int igraph_de_bruijn(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { 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)); + { + igraph_real_t no_of_nodes_real = pow(m, n); + no_of_nodes = no_of_nodes_real; + if (no_of_nodes != no_of_nodes_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for De Bruijn graph.", IGRAPH_EINVAL, + m, n); + } + } + /* no_of_edges = m * no_of_nodes */ + IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); + + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } for (i = 0; i < no_of_nodes; i++) { - long int basis = (i * mm) % no_of_nodes; + igraph_integer_t basis = (i * m) % no_of_nodes; for (j = 0; j < m; j++) { - igraph_vector_push_back(&edges, i); - igraph_vector_push_back(&edges, basis + j); + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, basis + j); } } - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - IGRAPH_DIRECTED)); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/famous.c b/src/vendor/cigraph/src/constructors/famous.c index 101878d88d4..31119cd3225 100644 --- a/src/vendor/cigraph/src/constructors/famous.c +++ b/src/vendor/cigraph/src/constructors/famous.c @@ -24,20 +24,20 @@ #include "igraph_constructors.h" -#include "internal/hacks.h" +#include "internal/hacks.h" /* strcasecmp */ -const igraph_real_t igraph_i_famous_bull[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -45,24 +45,24 @@ const igraph_real_t igraph_i_famous_coxeter[] = { 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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -70,59 +70,59 @@ const igraph_real_t igraph_i_famous_folkman[] = { 16, 12, 17, 13, 17, 13, 18, 14, 18, 14, 19 }; -const igraph_real_t igraph_i_famous_franklin[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -131,7 +131,7 @@ const igraph_real_t igraph_i_famous_levi[] = { 28, 28, 29 }; -const igraph_real_t igraph_i_famous_mcgee[] = { +const igraph_integer_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, @@ -139,7 +139,7 @@ const igraph_real_t igraph_i_famous_mcgee[] = { 21, 21, 22, 22, 23 }; -const igraph_real_t igraph_i_famous_meredith[] = { +const igraph_integer_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, @@ -157,14 +157,14 @@ const igraph_real_t igraph_i_famous_meredith[] = { 28, 38, 42, 35, 66, 59, 63, 52, 56, 45, 49 }; -const igraph_real_t igraph_i_famous_noperfectmatching[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -175,17 +175,17 @@ const igraph_real_t igraph_i_famous_nonline[] = { 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[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -193,18 +193,18 @@ const igraph_real_t igraph_i_famous_robertson[] = { 7, 14, 3, 14, 3, 11, 11, 18 }; -const igraph_real_t igraph_i_famous_smallestcyclicgroup[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -213,7 +213,7 @@ const igraph_real_t igraph_i_famous_thomassen[] = { 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[] = { +const igraph_integer_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, @@ -224,20 +224,20 @@ const igraph_real_t igraph_i_famous_tutte[] = { 40, 39, 41, 40, 41, 42, 43, 43, 45, 44, 45 }; -const igraph_real_t igraph_i_famous_uniquely3colorable[] = { +const igraph_integer_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[] = { +const igraph_integer_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[] = { +const igraph_integer_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, @@ -252,16 +252,15 @@ const igraph_real_t igraph_i_famous_zachary[] = { 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]; +static igraph_error_t igraph_i_famous(igraph_t *graph, const igraph_integer_t *data) { + igraph_integer_t no_of_nodes = data[0]; + igraph_integer_t no_of_edges = data[1]; igraph_bool_t directed = (igraph_bool_t) data[2]; - igraph_vector_t edges; + igraph_vector_int_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; + igraph_vector_int_view(&edges, data + 3, 2 * no_of_edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + return IGRAPH_SUCCESS; } /** @@ -417,14 +416,14 @@ static int igraph_i_famous(igraph_t *graph, const igraph_real_t *data) { * given name. * * \sa Other functions for creating graph structures: - * \ref igraph_ring(), \ref igraph_tree(), \ref igraph_lattice(), \ref - * igraph_full(). + * \ref igraph_ring(), \ref igraph_kary_tree(), \ref igraph_square_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) { +igraph_error_t igraph_famous(igraph_t *graph, const char *name) { if (!strcasecmp(name, "bull")) { return igraph_i_famous(graph, igraph_i_famous_bull); diff --git a/src/vendor/cigraph/src/constructors/full.c b/src/vendor/cigraph/src/constructors/full.c index bddcfb526bf..53054849b5b 100644 --- a/src/vendor/cigraph/src/constructors/full.c +++ b/src/vendor/cigraph/src/constructors/full.c @@ -24,6 +24,8 @@ #include "igraph_interface.h" +#include "math/safe_intop.h" + /** * \ingroup generators * \function igraph_full @@ -55,71 +57,275 @@ * O(|E|)=O(|V||V|) * here. * - * \sa \ref igraph_lattice(), \ref igraph_star(), \ref igraph_tree() + * \sa \ref igraph_square_lattice(), \ref igraph_star(), \ref igraph_kary_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_error_t 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; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t no_of_edges2; + igraph_integer_t i, j; if (n < 0) { - IGRAPH_ERROR("invalid number of vertices", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); if (directed && loops) { - IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * n)); + /* ecount = n * n */ + IGRAPH_SAFE_MULT(n, n, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); 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 */ + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ } } } else if (directed && !loops) { - IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * n * (n - 1))); + /* ecount = n * (n - 1) */ + IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); 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 */ + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_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 */ + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ } } } else if (!directed && loops) { - IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n + 1))); + /* ecount = n * (n + 1) / 2 */ + IGRAPH_SAFE_ADD(n, 1, &no_of_edges2); + IGRAPH_SAFE_MULT(n, no_of_edges2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); 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 */ + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ } } } else { - IGRAPH_CHECK(igraph_vector_reserve(&edges, n * (n - 1))); + /* ecount = n * (n - 1) / 2 */ + IGRAPH_SAFE_MULT(n, n - 1, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); 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_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, j); /* reserved */ } } } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_full_multipartite + * \brief Create a full multipartite graph. + * + * A multipartite graph contains two or more types of vertices and connections + * are only possible between two vertices of different types. This function + * creates a complete multipartite graph. + * + * \param graph Pointer to an igraph_t object, the graph will be + * created here. + * \param types Pointer to an integer vector. If not a null pointer, + * the type of each vertex will be stored here. + * \param n Pointer to an integer vector, the number of vertices + * of each type. + * \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 low-index vertices to high-index vertices; 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_bipartite() for full bipartite graphs. + */ +igraph_error_t igraph_full_multipartite(igraph_t *graph, + igraph_vector_int_t *types, + const igraph_vector_int_t *n, + igraph_bool_t directed, + igraph_neimode_t mode) { + + igraph_vector_int_t edges; + igraph_vector_int_t n_acc; + + igraph_integer_t no_of_types = igraph_vector_int_size(n); + + if (no_of_types == 0) { + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + if (types) { + igraph_vector_int_clear(types); + } + return IGRAPH_SUCCESS; + } + + if (igraph_vector_int_min(n) < 0) { + IGRAPH_ERROR("Number of vertices must not be negative in any partition.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&n_acc, no_of_types+1); + VECTOR(n_acc)[0] = 0; + for (igraph_integer_t i = 1; i < no_of_types+1; i++) { + IGRAPH_SAFE_ADD(VECTOR(n_acc)[i-1], VECTOR(*n)[i-1], + &VECTOR(n_acc)[i]); + } + + igraph_integer_t no_of_edges2 = 0; + + for (igraph_integer_t i = 0; i < no_of_types; i++) { + igraph_integer_t v = VECTOR(*n)[i]; + igraph_integer_t partial_sum = VECTOR(n_acc)[no_of_types] - v; + IGRAPH_SAFE_MULT(partial_sum, v, &partial_sum); + IGRAPH_SAFE_ADD(no_of_edges2, partial_sum, &no_of_edges2); + } + + if (directed && mode == IGRAPH_ALL) { + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + + igraph_integer_t ptr = 0; + + for (igraph_integer_t from_type = 0; from_type < no_of_types-1; from_type++) { + igraph_integer_t edge_from = VECTOR(n_acc)[from_type]; + for (igraph_integer_t i = 0; i < VECTOR(*n)[from_type]; i++) { + for (igraph_integer_t to_type = from_type+1; to_type < no_of_types; to_type++) { + igraph_integer_t edge_to = VECTOR(n_acc)[to_type]; + for (igraph_integer_t j = 0; j < VECTOR(*n)[to_type]; j++) { + if (!directed || mode == IGRAPH_OUT) { + VECTOR(edges)[ptr++] = edge_from; + VECTOR(edges)[ptr++] = edge_to; + } else if (mode == IGRAPH_IN) { + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_from; + } else { + VECTOR(edges)[ptr++] = edge_from; + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_to; + VECTOR(edges)[ptr++] = edge_from; + } + edge_to++; + } + } + edge_from++; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, VECTOR(n_acc)[no_of_types], directed)); + + if (types) { + IGRAPH_CHECK(igraph_vector_int_resize(types, VECTOR(n_acc)[no_of_types])); + if (VECTOR(n_acc)[no_of_types] > 0) { + igraph_integer_t v = 1; + for (igraph_integer_t i = 0; i < VECTOR(n_acc)[no_of_types]; i++) { + if (i == VECTOR(n_acc)[v]) { + v++; + } + VECTOR(*types)[i] = v-1; + } + } + } + + igraph_vector_int_destroy(&edges); + igraph_vector_int_destroy(&n_acc); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_turan + * \brief Create a Turán graph. + * + * Turán graphs are complete multipartite graphs with the property + * that the sizes of the partitions are as close to equal as possible. + * + * This function generates undirected graphs. The null graph is + * returned when the number of vertices is zero. A complete graph is + * returned if the number of partitions is greater than the number of + * vertices. + * + * \param graph Pointer to an igraph_t object, the graph will be + * created here. + * \param types Pointer to an integer vector. If not a null pointer, + * the type (partition index) of each vertex will be stored here. + * \param n Integer, the number of vertices in the graph. + * \param r Integer, the number of partitions of the graph, must be + * positive. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + * + * \sa \ref igraph_full_multipartite() for full multipartite graphs. + */ +igraph_error_t igraph_turan(igraph_t *graph, + igraph_vector_int_t *types, + igraph_integer_t n, + igraph_integer_t r) { + igraph_integer_t quotient; + igraph_integer_t remainder; + igraph_vector_int_t subsets; + + if (n < 0) { + IGRAPH_ERRORF("Number of vertices must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + + if (r <= 0) { + IGRAPH_ERRORF("Number of partitions must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, r); + } + + if (n == 0) { + IGRAPH_CHECK(igraph_empty(graph, 0, IGRAPH_UNDIRECTED)); + if (types) { + igraph_vector_int_clear(types); + } + return IGRAPH_SUCCESS; + } + + if (r > n) { + r = n; + } + + quotient = n / r; + remainder = n % r; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&subsets, r); + + igraph_vector_int_fill(&subsets, quotient); + for (igraph_integer_t i = 0; i < remainder; i++) { + VECTOR(subsets)[i]++; + } + + IGRAPH_CHECK(igraph_full_multipartite(graph, types, &subsets, + IGRAPH_UNDIRECTED, IGRAPH_ALL)); + igraph_vector_int_destroy(&subsets); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } /** * \function igraph_full_citation - * Creates a full citation graph + * \brief Creates a full citation graph. * * This is a directed graph, where every i->j edge is * present if and only if j<i. @@ -134,12 +340,21 @@ int igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, * * Time complexity: O(|V|^2), as we have many edges. */ -int igraph_full_citation(igraph_t *graph, igraph_integer_t n, +igraph_error_t 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_int_t edges; + igraph_integer_t i, j, ptr = 0; + + if (n < 0) { + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); + } + + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n, n-1, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } - IGRAPH_VECTOR_INIT_FINALLY(&edges, n * (n - 1)); for (i = 1; i < n; i++) { for (j = 0; j < i; j++) { VECTOR(edges)[ptr++] = i; @@ -148,7 +363,7 @@ int igraph_full_citation(igraph_t *graph, igraph_integer_t n, } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/generalized_petersen.c b/src/vendor/cigraph/src/constructors/generalized_petersen.c new file mode 100644 index 00000000000..0b34f7e8488 --- /dev/null +++ b/src/vendor/cigraph/src/constructors/generalized_petersen.c @@ -0,0 +1,95 @@ +/* + 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 . +*/ + +#include "igraph_constructors.h" + +#include "math/safe_intop.h" + +/** + * \function igraph_generalized_petersen + * \brief Creates a Generalized Petersen graph. + * + * The generalized Petersen graph G(n, k) consists of \p n vertices + * \c v_0, ..., \c v_n forming an "outer" cycle graph, and \p n additional vertices + * \c u_0, ..., \c u_n forming an "inner" circulant graph where u_i + * is connected to u_(i + k mod n). Additionally, all \c v_i are + * connected to \c u_i. + * + * + * G(n, k) has \c 2n vertices and \c 3n edges. The Petersen graph + * itself is G(5, 2). + * + * + * Reference: + * + * + * M. E. Watkins, + * A Theorem on Tait Colorings with an Application to the Generalized Petersen Graphs, + * Journal of Combinatorial Theory 6, 152-164 (1969). + * https://doi.org/10.1016%2FS0021-9800%2869%2980116-X + * + * \param graph Pointer to an uninitialized graph object, the result will + * be stored here. + * \param n Integer, \c n is the number of vertices in the inner and outer + * cycle/circulant graphs. It must be at least 3. + * \param k Integer, \c k is the shift of the circulant graph. It must be + * positive and less than n/2. + * \return Error code. + * + * \sa \ref igraph_famous() for the original Petersen graph. + * + * Time complexity: O(|V|), the number of vertices in the graph. + */ +igraph_error_t igraph_generalized_petersen(igraph_t *graph, igraph_integer_t n, igraph_integer_t k) { + /* This is a generalized Petersen graph constructor */ + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes, no_of_edges2; + igraph_integer_t i; + + if (n < 3) { + IGRAPH_ERRORF("n = %" IGRAPH_PRId " must be at least 3.", IGRAPH_EINVAL, n); + } + + IGRAPH_SAFE_MULT(n, 2, &no_of_nodes); + + /* The seemingly redundant k < n check avoids integer overflow on 2*k in 2*k < n. + * Note that 2*n has already been checked not to overflow above. */ + if (! (k > 0 && k < n && 2*k < n)) { + IGRAPH_ERRORF("k = %" IGRAPH_PRId " must be positive and less than n/2 with n = %" IGRAPH_PRId ".", IGRAPH_EINVAL, k, n); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_SAFE_MULT(n, 6, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + for (i = 0; i < n; i++) { + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, (i + 1) % n); + igraph_vector_int_push_back(&edges, i); + igraph_vector_int_push_back(&edges, i + n); + igraph_vector_int_push_back(&edges, i + n); + igraph_vector_int_push_back(&edges, ((i + k) % n) + n); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/constructors/kautz.c b/src/vendor/cigraph/src/constructors/kautz.c index 017d05c6a46..644037bbf8b 100644 --- a/src/vendor/cigraph/src/constructors/kautz.c +++ b/src/vendor/cigraph/src/constructors/kautz.c @@ -24,6 +24,8 @@ #include "igraph_interface.h" +#include "math/safe_intop.h" + /** * \function igraph_kautz * \brief Generate a Kautz graph. @@ -56,20 +58,19 @@ * 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) { +igraph_error_t 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; + igraph_integer_t no_of_nodes, no_of_edges; + igraph_integer_t allstrings; + igraph_integer_t i, j, idx = 0; + igraph_vector_int_t edges; + igraph_vector_int_t digits, table; + igraph_vector_int_t index1, index2; + igraph_integer_t actb = 0; + igraph_integer_t actvalue = 0; if (m < 0 || n < 0) { IGRAPH_ERROR("`m' and `n' should be non-negative in a Kautz graph", @@ -83,32 +84,50 @@ int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { 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); + /* no_of_nodes = ((m + 1) * pow(m, n)) */ + { + igraph_real_t m_to_pow_n_real = pow(m, n); + igraph_integer_t m_to_pow_n = m_to_pow_n_real; + if (m_to_pow_n != m_to_pow_n_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, + m, n); + } + IGRAPH_SAFE_MULT(m+1, m_to_pow_n, &no_of_nodes); + } + /* no_of_edges = m * no_of_nodes */ + IGRAPH_SAFE_MULT(no_of_nodes, m, &no_of_edges); + + { + igraph_real_t allstrings_real = pow(m + 1, n + 1); + allstrings = allstrings_real; + if (allstrings != allstrings_real) { + IGRAPH_ERRORF("Parameters (%" IGRAPH_PRId ", %" IGRAPH_PRId ") too large for Kautz graph.", IGRAPH_EINVAL, + m, n); + } + } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_long_init(&table, n + 1)); - IGRAPH_FINALLY(igraph_vector_long_destroy, &table); + IGRAPH_CHECK(igraph_vector_int_init(&table, n + 1)); + IGRAPH_FINALLY(igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_init(&digits, n + 1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &digits); + IGRAPH_CHECK(igraph_vector_int_init(&index1, pow(m + 1, n + 1))); + IGRAPH_FINALLY(igraph_vector_int_destroy, &index1); + IGRAPH_CHECK(igraph_vector_int_init(&index2, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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; + igraph_integer_t z = 0; if (VECTOR(digits)[actb] == 0) { z = 1; } @@ -132,7 +151,7 @@ int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { /* not yet, we need a valid prefix now */ while (1) { /* try to increase digits at position actb */ - long int next = VECTOR(digits)[actb] + 1; + igraph_integer_t next = VECTOR(digits)[actb] + 1; if (actb != 0 && VECTOR(digits)[actb - 1] == next) { next++; } @@ -149,15 +168,19 @@ int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { } } - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } /* 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; + igraph_integer_t fromvalue = VECTOR(index2)[i]; + igraph_integer_t lastdigit = fromvalue % (m + 1); + igraph_integer_t basis = (fromvalue * (m + 1)) % allstrings; for (j = 0; j <= m; j++) { - long int tovalue, to; + igraph_integer_t tovalue, to; if (j == lastdigit) { continue; } @@ -166,21 +189,20 @@ int igraph_kautz(igraph_t *graph, igraph_integer_t m, igraph_integer_t n) { if (to < 0) { continue; } - igraph_vector_push_back(&edges, i); - igraph_vector_push_back(&edges, to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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_vector_int_destroy(&index2); + igraph_vector_int_destroy(&index1); + igraph_vector_int_destroy(&digits); + igraph_vector_int_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_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/lattices.c b/src/vendor/cigraph/src/constructors/lattices.c new file mode 100644 index 00000000000..14755de4e3b --- /dev/null +++ b/src/vendor/cigraph/src/constructors/lattices.c @@ -0,0 +1,603 @@ +/* + 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, see . +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#define MIN(n, m) (n < m ? n : m) +#define MAX(n, m) (n < m ? m : n) + +#define VERTEX_INDEX(i, j) \ + lex_ordering ? row_count * (i - VECTOR(*row_start_vector)[j]) + j : (VECTOR(row_lengths_prefix_sum_vector)[j] + i - VECTOR(*row_start_vector)[j]) + +#define ROW_END(j) (VECTOR(*row_start_vector)[j] + VECTOR(*row_lengths_vector)[j] - 1) +#define ADD_EDGE_IJ_KL_IF_EXISTS(i, j, k, l) \ + if (VECTOR(*row_start_vector)[l] <= k && k <= ROW_END(l) && 0 <= l && l <= row_count - 1) \ + { \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ + if (directed && mutual) \ + { \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((k), (l))); /* reserved */ \ + igraph_vector_int_push_back(&edges, VERTEX_INDEX((i), (j))); /* reserved */ \ + } \ + } + +#define COMPUTE_NUMBER_OF_VERTICES() \ + do \ + { \ + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_prefix_sum_vector, row_count + 1); \ + VECTOR(row_lengths_prefix_sum_vector)[0] = 0; \ + for (i = 1; i < row_count + 1; i++) \ + { \ + IGRAPH_SAFE_ADD(VECTOR(row_lengths_prefix_sum_vector)[i - 1], VECTOR(*row_lengths_vector)[i - 1], &(VECTOR(row_lengths_prefix_sum_vector)[i])); \ + } \ + no_of_nodes = VECTOR(row_lengths_prefix_sum_vector)[row_count]; \ + } while (0) + +/** + * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1) provided a vertex + * exists. Thus, all vertices have degree at most 6. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) unless + * \c lex_ordering is set to true in which case the roles of the coordinates are reversed. + * + * \param graph An uninitialized graph object. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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 lex_ordering Boolean, set to true if the vertices of the resulting graph are ordered + * lexicographically with the 1st coordinate being more significant. Use only when all the + * rows have the number of vertices. + * \param row_lengths_vector Integer vector, defines the number of vertices with + * the second coordinate equal to the index. The length of this vector must match + * the length of \p row_start_vector. All coordinates must be non-negative. + * \param row_start_vector Integer vector, defines the leftmost coordinate of + * the vertex with the second coordinate equal to the index. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the + * row_start_vector. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + */ +static igraph_error_t triangular_lattice( + igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, igraph_bool_t lex_ordering, + const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector) { + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t row_lengths_prefix_sum_vector; + igraph_integer_t i, j; + + if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { + IGRAPH_ERRORF( + "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " + "row_start_vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_int_size(row_lengths_vector), + igraph_vector_int_size(row_start_vector)); + } + + + if (row_count > 0 && lex_ordering && !igraph_vector_int_isininterval(row_lengths_vector, VECTOR(*row_lengths_vector)[0], VECTOR(*row_lengths_vector)[0])) { + IGRAPH_ERROR( + "row_lengths_vector must have all the coordinates the same", IGRAPH_EINVAL); + } + + for (i = 0; i < row_count; i++) { + if (VECTOR(*row_lengths_vector)[i] < 0) { + IGRAPH_ERRORF( + "row_lengths_vector vector must have non-negative coordinates, " + "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", + IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + COMPUTE_NUMBER_OF_VERTICES(); + + /* computing the number of edges in the constructed triangular lattice */ + igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; + igraph_integer_t multiplier = mutual && directed ? 4 : 2; + for (j = 0; j < row_count - 1; j++) { + IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1))) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1]) + 1, + &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, MIN(ROW_END(j), ROW_END((j + 1)) + 1) - MAX(VECTOR(*row_start_vector)[j], VECTOR(*row_start_vector)[j + 1] + 1) + 1, + &no_of_edges2); + } + IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + /* constructing the edge array */ + igraph_integer_t k; + for (j = 0; j < row_count; j++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { + k = VECTOR(*row_start_vector)[j] + i; + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); + if (j < row_count - 1) { + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, k, (j + 1)); + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = size - i; + VECTOR(row_start_vector)[i] = 0; + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_rectangle_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size_x; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = size_y; + VECTOR(row_start_vector)[i] = (row_count - i) / 2; + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t triangular_lattice_hex_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count = size_y + size_z - 1; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_integer_t row_length = size_x; + igraph_integer_t row_start = size_y - 1; + igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); + igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); + igraph_integer_t sgn_flag = size_y < size_z ? 0 : -1; + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = row_length; + VECTOR(row_start_vector)[i] = row_start; + + if (i < first_threshold) { + row_length++; + row_start--; + } else if (i < second_threshold) { + row_start += sgn_flag; + } else { + row_length--; + } + } + + IGRAPH_CHECK(triangular_lattice(graph, directed, mutual, false, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_triangular_lattice + * \brief A triangular lattice with the given shape. + * + * \experimental + * + * Creates a triangular lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is generally connected with (i + 1, j), (i, j + 1), and (i - 1, j + 1). + * The function constructs a planar dual of the graph constructed by \ref igraph_hexagonal_lattice(). + * In particular, there a one-to-one correspondence between the vertices in the constructed graph + * and the cycles of length 6 in the graph constructed by \ref igraph_hexagonal_lattice() + * with the same \p dims parameter. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) + * + * \param graph An uninitialized graph object. + * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) + * If \p dims is of length 1, the resulting lattice has a triangular shape + * where each side of the triangle contains dims[0] vertices. + * If \p dims is of length 2, the resulting lattice has a + * "quasi rectangular" shape with the sides containing dims[0] and + * dims[1] vertices, respectively. + * If \p dims is of length 3, the resulting lattice has a hexagonal shape + * where the sides of the hexagon contain dims[0], dims[1] and + * dims[2] vertices. + * All coordinates must be non-negative. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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. + * \return Error code: + * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components + * at least 1. + * \sa \ref igraph_hexagonal_lattice() for creating a triangular lattice. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + * + */ +igraph_error_t igraph_triangular_lattice( + igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, + igraph_bool_t mutual) { + igraph_integer_t num_dims = igraph_vector_int_size(dims); + if (igraph_vector_int_any_smaller(dims, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); + } + /* If a coordinate of dims is 0 the result is an empty graph. */ + if (igraph_vector_int_contains(dims, 0)) { + return igraph_empty(graph, 0, directed); + } + + switch (num_dims) { + case 1: + IGRAPH_CHECK(triangular_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); + break; + case 2: + IGRAPH_CHECK(triangular_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); + break; + case 3: + IGRAPH_CHECK(triangular_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); + break; + default: + IGRAPH_ERRORF( + "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, num_dims); + } + + return IGRAPH_SUCCESS; +} + + +/** + * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is connected with (i + 1, j), and if i is odd also with (i - 1, j + 1) provided a vertex + * exists. Thus, all vertices have degree at most 3. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1). + * + * \param graph An uninitialized graph object. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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 row_lengths_vector Integer vector, defines the number of vertices with + * the second coordinate equal to the index. The length of this vector must match + * the length of \p row_start_vector. All coordinates must be non-negative. + * \param row_start_vector Integer vector, defines the leftmost coordinate of + * the vertex with the second coordinate equal to the index. + * + * \return Error code: + * \c IGRAPH_EINVAL: invalid (negative) length of row_lengths_vector does not match the length of the + * row_start_vector. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + */ +static igraph_error_t hexagonal_lattice( + igraph_t *graph, igraph_bool_t directed, igraph_bool_t mutual, + const igraph_vector_int_t *row_lengths_vector, const igraph_vector_int_t *row_start_vector +) { + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t row_count = igraph_vector_int_size(row_lengths_vector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t row_lengths_prefix_sum_vector; + igraph_integer_t i, j; + igraph_bool_t lex_ordering = false; + + if (igraph_vector_int_size(row_lengths_vector) != igraph_vector_int_size(row_start_vector)) { + IGRAPH_ERRORF( + "Length of row_lengths_vector vector (%" IGRAPH_PRId ") must match the length of the " + "row_start_vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_int_size(row_lengths_vector), + igraph_vector_int_size(row_start_vector) + ); + } + + for (i = 0; i < row_count; i++) { + if (VECTOR(*row_lengths_vector)[i] < 0) { + IGRAPH_ERRORF( + "row_lengths_vector vector must have non-negative coordinates, " + "was (%" IGRAPH_PRId ") for the (%" IGRAPH_PRId ")-th row.", + IGRAPH_EINVAL, VECTOR(*row_lengths_vector)[i], i); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + COMPUTE_NUMBER_OF_VERTICES(); + + /* computing the number of edges in the constructed hex lattice */ + igraph_integer_t no_of_edges2 = VECTOR(*row_lengths_vector)[row_count - 1] - 1; + igraph_integer_t multiplier = mutual && directed ? 4 : 2, low, high; + for (j = 0; j < row_count - 1; j++) { + IGRAPH_SAFE_ADD(no_of_edges2, VECTOR(*row_lengths_vector)[j] - 1, &no_of_edges2); + low = MAX((VECTOR(*row_start_vector)[j] - 1), (VECTOR(*row_start_vector)[j + 1])); + low = low % 2 ? low + 1 : low; + high = MIN((ROW_END(j) - 1), (ROW_END(j + 1))); + high = high % 2 ? high - 1 : high; + IGRAPH_SAFE_ADD(no_of_edges2, (high - low) / 2 + 1, &no_of_edges2); + } + IGRAPH_SAFE_MULT(no_of_edges2, multiplier, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + + /* constructing the edge array */ + igraph_integer_t k; + for (j = 0; j < row_count; j++) { + IGRAPH_ALLOW_INTERRUPTION(); + for (i = 0; i < VECTOR(*row_lengths_vector)[j]; i++) { + k = VECTOR(*row_start_vector)[j] + i; + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k + 1), j); + if (j < row_count - 1 && k % 2 == 1) { + ADD_EDGE_IJ_KL_IF_EXISTS(k, j, (k - 1), (j + 1)); + } + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&row_lengths_prefix_sum_vector); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_triangle_shape(igraph_t *graph, igraph_integer_t size, igraph_bool_t directed, igraph_bool_t mutual) { + igraph_integer_t row_count; + IGRAPH_SAFE_ADD(size, 2, &row_count); + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count - 1); + + for (i = 0; i < row_count - 1; i++) { + VECTOR(row_lengths_vector)[i] = 2 * (row_count - i) - (i ? 1 : 3); + VECTOR(row_start_vector)[i] = (i ? 0 : 1); + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_rectangle_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_bool_t directed, igraph_bool_t mutual +) { + igraph_integer_t row_count; + IGRAPH_SAFE_ADD(size_x, 1, &row_count); + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t actual_size_y; + IGRAPH_SAFE_ADD(size_y, 1, &actual_size_y); + IGRAPH_SAFE_MULT(actual_size_y, 2, &actual_size_y); + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_bool_t is_first_row, is_last_row, is_start_odd; + + for (i = 0; i < row_count; i++) { + is_first_row = !i; + is_last_row = i == row_count - 1; + is_start_odd = (row_count - i - 1) % 2; + VECTOR(row_lengths_vector)[i] = actual_size_y - (is_first_row || is_last_row ? 1 : 0); + VECTOR(row_start_vector)[i] = row_count - i - 1 + (is_first_row && !is_start_odd ? 1 : 0); + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t hexagonal_lattice_hex_shape( + igraph_t *graph, igraph_integer_t size_x, igraph_integer_t size_y, + igraph_integer_t size_z, igraph_bool_t directed, igraph_bool_t mutual +) { + igraph_integer_t row_count = size_y + size_z; + igraph_vector_int_t row_lengths_vector; + igraph_vector_int_t row_start_vector; + igraph_integer_t i; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_lengths_vector, row_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&row_start_vector, row_count); + + igraph_integer_t row_length; + IGRAPH_SAFE_MULT(size_x, 2, &row_length); + IGRAPH_SAFE_ADD(row_length, 1, &row_length); + igraph_integer_t row_start; + IGRAPH_SAFE_MULT(size_y, 2, &row_start); + IGRAPH_SAFE_ADD(row_start, -1, &row_start); + igraph_integer_t first_threshold = MIN(size_y - 1, size_z - 1); + igraph_integer_t second_threshold = MAX(size_y - 1, size_z - 1); + igraph_integer_t sgn_flag = size_y < size_z ? 0 : -2; + + for (i = 0; i < row_count; i++) { + VECTOR(row_lengths_vector)[i] = row_length; + VECTOR(row_start_vector)[i] = row_start; + + if (i < first_threshold) { + row_length += 2; + row_start -= 2; + } else if (i < second_threshold) { + row_start += sgn_flag; + } else { + row_length -= 2; + } + if (i == size_y - 1) { + row_start--; + row_length++; + } + if (i == size_z - 1) { + row_length++; + } + } + + IGRAPH_CHECK(hexagonal_lattice(graph, directed, mutual, &row_lengths_vector, &row_start_vector)); + + igraph_vector_int_destroy(&row_lengths_vector); + igraph_vector_int_destroy(&row_start_vector); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_hexagonal_lattice + * \brief A hexagonal lattice with the given shape. + * + * \experimental + * + * Creates a hexagonal lattice whose vertices have the form (i, j) for non-negative integers i and j + * and (i, j) is generally connected with (i + 1, j), and if i is odd also with (i - 1, j + 1). + * The function constructs a planar dual of the graph constructed by \ref igraph_triangular_lattice(). + * In particular, there a one-to-one correspondence between the cycles of length 6 in the constructed graph + * and the vertices of the graph constructed by \ref igraph_triangular_lattice() function + * with the same \p dims parameter. + * + * + * The vertices of the resulting graph are ordered lexicographically with the 2nd coordinate being + * more significant, e.g., (i, j) < (i + 1, j) and (i + 1, j) < (i, j + 1) + * + * \param graph An uninitialized graph object. + * \param dims Integer vector, defines the shape of the lattice. (Below the "edge length"s are in terms of graph theoretical path lengths.) + * If \p dims is of length 1, the resulting lattice has a triangular shape + * where each side of the triangle contains dims[0] vertices. + * If \p dims is of length 2, the resulting lattice has a + * "quasi rectangular" shape with the sides containing dims[0] and + * dims[1] vertices, respectively. + * If \p dims is of length 3, the resulting lattice has a hexagonal shape + * where the sides of the hexagon contain dims[0], dims[1] and + * dims[2] vertices. + * All coordinates must be non-negative. + * \param directed Boolean, whether to create a directed graph. + * If the \c mutual argument is 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. + * \return Error code: + * \c IGRAPH_EINVAL: The size of \p dims must be either 1, 2, or 3 with all the components + * at least 1. + * \sa \ref igraph_triangular_lattice() for creating a triangular lattice. + * + * Time complexity: O(|V|), where |V| is the number of vertices in the generated graph. + * + */ +igraph_error_t igraph_hexagonal_lattice( + igraph_t *graph, const igraph_vector_int_t *dims, igraph_bool_t directed, + igraph_bool_t mutual +) { + igraph_integer_t num_dims = igraph_vector_int_size(dims); + if (igraph_vector_int_any_smaller(dims, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); + } + /* If a coordinate of dims is 0 the result is an empty graph. */ + if (igraph_vector_int_any_smaller(dims, 1)) { + return igraph_empty(graph, 0, directed); + } + + switch (num_dims) { + case 1: + IGRAPH_CHECK(hexagonal_lattice_triangle_shape(graph, VECTOR(*dims)[0], directed, mutual)); + break; + case 2: + IGRAPH_CHECK(hexagonal_lattice_rectangle_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], directed, mutual)); + break; + case 3: + IGRAPH_CHECK(hexagonal_lattice_hex_shape(graph, VECTOR(*dims)[0], VECTOR(*dims)[1], VECTOR(*dims)[2], directed, mutual)); + break; + default: + IGRAPH_ERRORF( + "The size of the dimension vector must be 1, 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, num_dims + ); + } + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/constructors/lcf.c b/src/vendor/cigraph/src/constructors/lcf.c index b7ee6bed47f..76534632d6c 100644 --- a/src/vendor/cigraph/src/constructors/lcf.c +++ b/src/vendor/cigraph/src/constructors/lcf.c @@ -24,6 +24,8 @@ #include "igraph_operators.h" +#include "math/safe_intop.h" + /** * \function igraph_lcf_vector * \brief Creates a graph from LCF notation. @@ -43,20 +45,27 @@ * 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_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, + const igraph_vector_int_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; + igraph_vector_int_t edges; + igraph_integer_t no_of_shifts = igraph_vector_int_size(shifts); + igraph_integer_t ptr = 0, i, sptr = 0; + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_edges = n + no_of_shifts * repeats; + igraph_integer_t no_of_edges2; if (repeats < 0) { IGRAPH_ERROR("Number of repeats must not be negative.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + + /* no_of_edges = n + no_of_shifts * repeats */ + IGRAPH_SAFE_MULT(no_of_shifts, repeats, &no_of_edges); + IGRAPH_SAFE_ADD(no_of_edges, n, &no_of_edges); + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); if (no_of_nodes > 0) { /* Create a ring first */ @@ -69,21 +78,20 @@ int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, /* 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; + igraph_integer_t sh = VECTOR(*shifts)[sptr % no_of_shifts]; + igraph_integer_t from = sptr % no_of_nodes; + igraph_integer_t 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_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); + IGRAPH_CHECK(igraph_simplify(graph, true, true, NULL)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -112,30 +120,36 @@ int igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, * * \example examples/simple/igraph_lcf.c */ -int igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { - igraph_vector_t shifts; +igraph_error_t igraph_lcf(igraph_t *graph, igraph_integer_t n, ...) { + igraph_vector_int_t shifts; igraph_integer_t repeats; va_list ap; - IGRAPH_VECTOR_INIT_FINALLY(&shifts, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&shifts, 0); va_start(ap, n); while (1) { + igraph_error_t err; int num = va_arg(ap, int); if (num == 0) { break; } - IGRAPH_CHECK(igraph_vector_push_back(&shifts, num)); + err = igraph_vector_int_push_back(&shifts, num); + if (err != IGRAPH_SUCCESS) { + va_end(ap); + IGRAPH_ERROR("", err); + } } - if (igraph_vector_size(&shifts) == 0) { + va_end(ap); + if (igraph_vector_int_size(&shifts) == 0) { repeats = 0; } else { - repeats = (igraph_integer_t) igraph_vector_pop_back(&shifts); + repeats = igraph_vector_int_pop_back(&shifts); } IGRAPH_CHECK(igraph_lcf_vector(graph, n, &shifts, repeats)); - igraph_vector_destroy(&shifts); + igraph_vector_int_destroy(&shifts); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/linegraph.c b/src/vendor/cigraph/src/constructors/linegraph.c index 13f69e0e977..a75e74373db 100644 --- a/src/vendor/cigraph/src/constructors/linegraph.c +++ b/src/vendor/cigraph/src/constructors/linegraph.c @@ -29,98 +29,97 @@ /* 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; +static igraph_error_t igraph_i_linegraph_undirected(const igraph_t *graph, igraph_t *linegraph) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, n; + igraph_vector_int_t adjedges, adjedges2; + igraph_vector_int_t edges; + igraph_integer_t prev = -1; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&adjedges2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); + IGRAPH_VECTOR_INT_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_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); IGRAPH_ALLOW_INTERRUPTION(); if (from != prev) { - IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, - IGRAPH_ALL)); + IGRAPH_CHECK(igraph_incident(graph, &adjedges, from, IGRAPH_ALL)); } - n = igraph_vector_size(&adjedges); + n = igraph_vector_int_size(&adjedges); for (j = 0; j < n; j++) { - long int e = (long int) VECTOR(adjedges)[j]; + igraph_integer_t e = 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_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); } } - IGRAPH_CHECK(igraph_incident(graph, &adjedges2, (igraph_integer_t) to, - IGRAPH_ALL)); - n = igraph_vector_size(&adjedges2); + IGRAPH_CHECK(igraph_incident(graph, &adjedges2, to, IGRAPH_ALL)); + n = igraph_vector_int_size(&adjedges2); for (j = 0; j < n; j++) { - long int e = (long int) VECTOR(adjedges2)[j]; + igraph_integer_t e = VECTOR(adjedges2)[j]; if (e < i) { - IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); } } prev = from; } - igraph_vector_destroy(&adjedges); - igraph_vector_destroy(&adjedges2); + igraph_vector_int_destroy(&adjedges); + igraph_vector_int_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_CHECK(igraph_create(linegraph, &edges, no_of_edges, igraph_is_directed(graph))); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -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; +static igraph_error_t igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegraph) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, n; + igraph_vector_int_t adjedges; + igraph_vector_int_t edges; + igraph_integer_t prev = -1; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&adjedges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjedges, 0); for (i = 0; i < no_of_edges; i++) { - long int from = IGRAPH_FROM(graph, i); + igraph_integer_t from = IGRAPH_FROM(graph, i); IGRAPH_ALLOW_INTERRUPTION(); if (from != prev) { - IGRAPH_CHECK(igraph_incident(graph, &adjedges, (igraph_integer_t) from, - IGRAPH_IN)); + IGRAPH_CHECK(igraph_incident(graph, &adjedges, from, IGRAPH_IN)); } - n = igraph_vector_size(&adjedges); + n = igraph_vector_int_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)); + igraph_integer_t e = VECTOR(adjedges)[j]; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, e)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); } prev = from; } - igraph_vector_destroy(&adjedges); + igraph_vector_int_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_CHECK(igraph_create(linegraph, &edges, no_of_edges, igraph_is_directed(graph))); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -131,10 +130,10 @@ static int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegrap * 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 single vertex of an undirected self-loop is counted as two end points. * * - * The line graph L(G) of a G directed graph is slightly different, + * 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. @@ -146,6 +145,7 @@ static int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegrap * * 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. @@ -154,7 +154,7 @@ static int igraph_i_linegraph_directed(const igraph_t *graph, igraph_t *linegrap * Time complexity: O(|V|+|E|), the number of edges plus the number of vertices. */ -int igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { +igraph_error_t igraph_linegraph(const igraph_t *graph, igraph_t *linegraph) { if (igraph_is_directed(graph)) { return igraph_i_linegraph_directed(graph, linegraph); diff --git a/src/vendor/cigraph/src/constructors/prufer.c b/src/vendor/cigraph/src/constructors/prufer.c index 40fa3ea53a1..489026f9efd 100644 --- a/src/vendor/cigraph/src/constructors/prufer.c +++ b/src/vendor/cigraph/src/constructors/prufer.c @@ -22,7 +22,7 @@ #include "igraph_constructors.h" -#include "igraph_interface.h" +#include "math/safe_intop.h" /** * \ingroup generators @@ -47,29 +47,33 @@ * invalid Prüfer sequence given * \endclist * - * \sa \ref igraph_to_prufer(), \ref igraph_tree(), \ref igraph_tree_game() + * \sa \ref igraph_to_prufer(), \ref igraph_kary_tree(), \ref igraph_tree_game() * */ -int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { +igraph_error_t 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; + igraph_vector_int_t edges; + igraph_integer_t n; + igraph_integer_t i, k; + igraph_integer_t u, v; /* vertices */ + igraph_integer_t ec; - n = igraph_vector_int_size(prufer) + 2; + IGRAPH_SAFE_ADD(igraph_vector_int_size(prufer), 2, &n); IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, n); /* initializes vector to zeros */ - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1)); + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n - 1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } /* 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); + igraph_integer_t w = VECTOR(*prufer)[i]; + if (w >= n || w < 0) { + IGRAPH_ERROR("Invalid Prufer sequence.", IGRAPH_EINVAL); } - VECTOR(degree)[u] += 1; + VECTOR(degree)[w] += 1; } v = 0; /* initialize v now, in case Prufer sequence is empty */ @@ -109,9 +113,9 @@ int igraph_from_prufer(igraph_t *graph, const igraph_vector_int_t *prufer) { VECTOR(edges)[ec++] = v; VECTOR(edges)[ec++] = u; - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) n, /* directed = */ 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, n, IGRAPH_UNDIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); diff --git a/src/vendor/cigraph/src/constructors/regular.c b/src/vendor/cigraph/src/constructors/regular.c index ec87710c966..9a3267a054d 100644 --- a/src/vendor/cigraph/src/constructors/regular.c +++ b/src/vendor/cigraph/src/constructors/regular.c @@ -17,7 +17,6 @@ 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" @@ -27,6 +26,7 @@ #include "igraph_operators.h" #include "core/interruption.h" +#include "math/safe_intop.h" /** * \ingroup generators @@ -66,32 +66,36 @@ * Time complexity: O(|V|), the * number of vertices in the graph. * - * \sa \ref igraph_lattice(), \ref igraph_ring(), \ref igraph_tree() + * \sa \ref igraph_square_lattice(), \ref igraph_ring(), \ref igraph_kary_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_error_t 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; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVVID); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVVID); } if (center < 0 || center > n - 1) { - IGRAPH_ERROR("Invalid center vertex", IGRAPH_EINVAL); + 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); + IGRAPH_ERROR("Invalid star mode.", IGRAPH_EINVMODE); } if (mode != IGRAPH_STAR_MUTUAL) { - IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2); + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); } else { - IGRAPH_VECTOR_INIT_FINALLY(&edges, (n - 1) * 2 * 2); + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(n-1, 4, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); } if (mode == IGRAPH_STAR_OUT) { @@ -129,15 +133,184 @@ int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, IGRAPH_CHECK(igraph_create(graph, &edges, 0, (mode != IGRAPH_STAR_UNDIRECTED))); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_wheel + * \brief Creates a \em wheel graph, a union of a star and a cycle graph. + * + * A wheel graph on \p n vertices can be thought of as a wheel with + * n - 1 spokes. The cycle graph part makes up the rim, + * while the star graph part adds the spokes. + * + * + * Note that the two and three-vertex wheel graphs are non-simple: + * The two-vertex wheel graph contains a self-loop, while the three-vertex + * wheel graph contains parallel edges (a 1-cycle and a 2-cycle, respectively). + * + * \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_WHEEL_OUT + * directed wheel graph, edges point + * \em from the center to the other vertices. + * \cli IGRAPH_WHEEL_IN + * directed wheel graph, edges point + * \em to the center from the other vertices. + * \cli IGRAPH_WHEEL_MUTUAL + * directed wheel graph with mutual edges. + * \cli IGRAPH_WHEEL_UNDIRECTED + * an undirected wheel 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_square_lattice(), \ref igraph_ring(), \ref igraph_star(), + * \ref igraph_kary_tree() for creating other regular structures. + * + */ + +igraph_error_t igraph_wheel(igraph_t *graph, igraph_integer_t n, igraph_wheel_mode_t mode, + igraph_integer_t center) { + + igraph_star_mode_t star_mode; + igraph_vector_int_t rim_edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i; + + /* Firstly creates a star by the function \ref igraph_star() and makes + * use of its existing input parameter checking ability, it can check + * "Invalid number of vertices" and "Invalid center vertex". */ + switch (mode) + { + case IGRAPH_WHEEL_OUT: + star_mode = IGRAPH_STAR_OUT; + break; + case IGRAPH_WHEEL_IN: + star_mode = IGRAPH_STAR_IN; + break; + case IGRAPH_WHEEL_MUTUAL: + star_mode = IGRAPH_STAR_MUTUAL; + break; + case IGRAPH_WHEEL_UNDIRECTED: + star_mode = IGRAPH_STAR_UNDIRECTED; + break; + default: + IGRAPH_ERROR("Invalid wheel graph mode.", IGRAPH_EINVMODE); + } + + IGRAPH_CHECK(igraph_star(graph, n, star_mode, center)); + + /* If n <= 1, wheel graph is identical with star graph, + * no further processing is needed. */ + if (n <= 1) { + return IGRAPH_SUCCESS; + } + + /* Register the star for deallocation in case of error flow before + * the entire wheel is successfully created. */ + IGRAPH_FINALLY(igraph_destroy, graph); + + /* Add edges to the rim. As the rim (or cycle) has n - 1 vertices, + * it will have n - 1 edges. For MUTUAL mode, number of edges + * will be double. */ + if (mode == IGRAPH_WHEEL_MUTUAL) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 4 * (n-1)); + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&rim_edges, 2 * (n-1)); + } + + /* Assign first n-1 edges (MUTUAL will be handled later). */ + for (i = 0; i < n-2; i++) { + if ( i < center ) { + VECTOR(rim_edges)[2 * i] = i; + if ( i + 1 < center ) { + VECTOR(rim_edges)[2 * i + 1] = i + 1; + } else { + VECTOR(rim_edges)[2 * i + 1] = i + 2; + } + } else { + VECTOR(rim_edges)[2 * i] = i + 1; + VECTOR(rim_edges)[2 * i + 1] = i + 2; + } + } + + /* Assign the last edge (MUTUAL will be handled later). */ + if ( n - 2 < center ) { + VECTOR(rim_edges)[2 * n - 4] = n - 2; + } else { + VECTOR(rim_edges)[2 * n - 4] = n - 1; + } + if ( center > 0 ) { + VECTOR(rim_edges)[2 * n - 3] = 0; + } else { + VECTOR(rim_edges)[2 * n - 3] = 1; + } + + /* For MUTUAL mode, add reverse-direction edges. */ + if (mode == IGRAPH_WHEEL_MUTUAL) { + for (i=0; i < 2 * (n-1); i++) { + VECTOR(rim_edges)[4 * (n-1) - 1 - i] = VECTOR(rim_edges)[i]; + } + } + + /* Combine the rim into the star to make it a wheel graph. */ + IGRAPH_CHECK(igraph_add_edges(graph, &rim_edges, NULL)); + + igraph_vector_int_destroy(&rim_edges); + + /* 2 instead of 1 because the star graph is registered before. */ + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; } /** * \ingroup generators * \function igraph_lattice + * \brief Arbitrary dimensional square lattices (deprecated). + * + * \deprecated-by igraph_square_lattice 0.10.0 + */ +igraph_error_t igraph_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, + igraph_integer_t nei, igraph_bool_t directed, igraph_bool_t mutual, + igraph_bool_t circular) { + igraph_vector_bool_t periodic; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, igraph_vector_int_size(dimvector)); + igraph_vector_bool_fill(&periodic, circular); + + IGRAPH_CHECK(igraph_square_lattice(graph, dimvector, nei, directed, mutual, &periodic)); + + igraph_vector_bool_destroy(&periodic); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_square_lattice * \brief Arbitrary dimensional square lattices. * * Creates d-dimensional square lattices of the given size. Optionally, @@ -149,9 +322,9 @@ int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, * * * 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 + .... + * index of the vertex at position (i_1, i_2, i_3, ..., i_d) + * in a lattice of size (n_1, n_2, ..., n_d) will be + * i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + .... * * \param graph An uninitialized graph object. * \param dimvector Vector giving the sizes of the lattice in each of @@ -165,84 +338,110 @@ int igraph_star(igraph_t *graph, igraph_integer_t n, igraph_star_mode_t mode, * 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. + * \param periodic Boolean vector, defines whether the generated lattice is + * periodic along each dimension. The length of this vector must match + * the length of \p dimvector. This parameter may also be \c NULL, which + * implies that the lattice will not be periodic. * \return Error code: - * \c IGRAPH_EINVAL: invalid (negative) - * dimension vector. + * \c IGRAPH_EINVAL: invalid (negative) dimension vector or mismatch + * between the length of the dimension vector and the periodicity 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; +igraph_error_t igraph_square_lattice( + igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, + igraph_bool_t directed, igraph_bool_t mutual, const igraph_vector_bool_t *periodic +) { + + igraph_integer_t dims = igraph_vector_int_size(dimvector); + igraph_integer_t no_of_nodes; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t *coords, *weights; + igraph_integer_t i, j; int carry, pos; - if (igraph_vector_any_smaller(dimvector, 0)) { - IGRAPH_ERROR("Invalid dimension vector", IGRAPH_EINVAL); + if (igraph_vector_int_any_smaller(dimvector, 0)) { + IGRAPH_ERROR("Invalid dimension vector.", IGRAPH_EINVAL); } + if (periodic && igraph_vector_bool_size(periodic) != dims) { + IGRAPH_ERRORF( + "Length of periodicity vector must match the length of the " + "dimension vector (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, dims + ); + } + + /* compute no. of nodes in overflow-safe manner */ + IGRAPH_CHECK(igraph_i_safe_vector_int_prod(dimvector, &no_of_nodes)); + /* init coords & weights */ - coords = IGRAPH_CALLOC(dims, long int); - if (coords == 0) { - IGRAPH_ERROR("Lattice creation failed", IGRAPH_ENOMEM); - } + coords = IGRAPH_CALLOC(dims, igraph_integer_t); + IGRAPH_CHECK_OOM(coords, "Lattice creation failed."); IGRAPH_FINALLY(igraph_free, coords); - weights = IGRAPH_CALLOC(dims, long int); - if (weights == 0) { - IGRAPH_ERROR("Lattice creation failed", IGRAPH_ENOMEM); - } + + weights = IGRAPH_CALLOC(dims, igraph_integer_t); + IGRAPH_CHECK_OOM(weights, "Lattice creation failed."); 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]; + weights[i] = weights[i - 1] * 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + if (mutual && directed) { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } else { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes, dims, &no_of_edges2); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges2)); + } + +#define IS_PERIODIC(dim) ((periodic && VECTOR(*periodic)[dim])) for (i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); + + /* Connect the current node to the "next" node along each dimension */ for (j = 0; j < dims; j++) { - if (circular || coords[j] != VECTOR(*dimvector)[j] - 1) { - long int new_nei; + igraph_bool_t is_periodic = IS_PERIODIC(j); + + if (is_periodic|| coords[j] != VECTOR(*dimvector)[j] - 1) { + igraph_integer_t 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; + new_nei = i - (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 */ + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ } - } /* if circular || coords[j] */ - if (mutual && directed && (circular || coords[j] != 0)) { - long int new_nei; + } /* if is_periodic || coords[j] */ + if (mutual && directed && (is_periodic || coords[j] != 0)) { + igraph_integer_t 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; + new_nei = i + (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 */ + (VECTOR(*dimvector)[j] != 2 || !is_periodic)) { + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, new_nei - 1); /* reserved */ } - } /* if circular || coords[0] */ + } /* if is_periodic || coords[0] */ } /* for j= 2) { IGRAPH_CHECK(igraph_connect_neighborhood(graph, nei, IGRAPH_ALL)); } @@ -270,10 +468,10 @@ int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, /* clean up */ IGRAPH_FREE(coords); IGRAPH_FREE(weights); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -287,8 +485,9 @@ int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, * 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(). + * When \p n is 1 or 2, the result may not be a simple graph: + * the one-cycle contains a self-loop and the undirected or reciprocally + * connected directed two-cycle contains parallel edges. * * \param graph Pointer to an uninitialized graph object. * \param n The number of vertices in the graph. @@ -308,21 +507,57 @@ int igraph_lattice(igraph_t *graph, const igraph_vector_t *dimvector, * * \example examples/simple/igraph_ring.c */ -int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, +igraph_error_t 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; + igraph_vector_int_t edges; + igraph_integer_t no_of_edges, no_of_edges2; + igraph_integer_t i; 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; + if (n == 0) { + return igraph_empty(graph, 0, directed); + } - IGRAPH_CHECK(igraph_lattice(graph, &v, 1, directed, mutual, circular)); + no_of_edges = circular ? n : n-1; + if (directed && mutual) { + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges); + } + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + + if (directed && mutual) { + for (i=0; i < n-1; ++i) { + VECTOR(edges)[4*i] = i; + VECTOR(edges)[4*i+1] = i+1; + VECTOR(edges)[4*i+2] = i+1; + VECTOR(edges)[4*i+3] = i; + } + if (circular) { + /* Now i == n-1 */ + VECTOR(edges)[4*i] = i; + VECTOR(edges)[4*i+1] = 0; + VECTOR(edges)[4*i+2] = 0; + VECTOR(edges)[4*i+3] = i; + } + } else { + for (i=0; i < n-1; ++i) { + VECTOR(edges)[2*i] = i; + VECTOR(edges)[2*i+1] = i+1; + } + if (circular) { + /* Now i == n-1 */ + VECTOR(edges)[2*i] = i; + VECTOR(edges)[2*i+1] = 0; + } + } - igraph_vector_destroy(&v); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -330,8 +565,8 @@ int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, /** * \ingroup generators - * \function igraph_tree - * \brief Creates a tree in which almost all vertices have the same number of children. + * \function igraph_kary_tree + * \brief Creates a k-ary tree in which almost all vertices have k children. * * To obtain a completely symmetric tree with \c l layers, where each * vertex has precisely \p children descendants, use @@ -352,7 +587,7 @@ int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, * \clist * \cli IGRAPH_TREE_OUT * directed tree, the edges point - * from the parents to their children, + * from the parents to their children. * \cli IGRAPH_TREE_IN * directed tree, the edges point from * the children to their parents. @@ -370,15 +605,15 @@ int igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, * 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 + * \example examples/simple/igraph_kary_tree.c */ -int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, +igraph_error_t igraph_kary_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; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i, j; + igraph_integer_t idx = 0; + igraph_integer_t to = 1; if (n < 0) { IGRAPH_ERROR("Number of vertices cannot be negative.", IGRAPH_EINVAL); @@ -391,7 +626,15 @@ int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, n > 0 ? 2 * (n - 1) : 0); + { + igraph_integer_t no_of_edges2; + if (n > 0) { + IGRAPH_SAFE_MULT(n-1, 2, &no_of_edges2); + } else { + no_of_edges2 = 0; + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } i = 0; if (type == IGRAPH_TREE_OUT) { @@ -414,9 +657,180 @@ int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, IGRAPH_CHECK(igraph_create(graph, &edges, n, type != IGRAPH_TREE_UNDIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup generators + * \function igraph_tree + * \brief Creates a k-ary tree in which almost all vertices have k children (deprecated alias). + * + * \deprecated-by igraph_kary_tree 0.10.0 + */ +igraph_error_t igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, + igraph_tree_mode_t type) { + return igraph_kary_tree(graph, n, children, type); +} + +/** + * \ingroup generators + * \function igraph_symmetric_tree + * \brief Creates a symmetric tree with the specified number of branches at each level. + * + * This function creates a tree in which all vertices at distance \c d from the + * root have \p branching_counts[d] children. + * + * \param graph Pointer to an uninitialized graph object. + * \param branches Vector detailing the number of branches at each level. + * \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_INVMODE: invalid mode argument. + * \c IGRAPH_EINVAL: invalid number of children. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_kary_tree(), \ref igraph_regular_tree() and \ref igraph_star() + * for creating other regular tree structures; + * \ref igraph_from_prufer() for creating arbitrary trees; + * \ref igraph_tree_game() for uniform random sampling of trees. + * + * \example examples/simple/igraph_symmetric_tree.c + */ + +igraph_error_t igraph_symmetric_tree(igraph_t *graph, const igraph_vector_int_t *branches, + igraph_tree_mode_t type) { + + igraph_vector_int_t edges; + igraph_integer_t j, k, temp, no_of_nodes, idx, parent, child, level_end; + igraph_integer_t branching_counts_size = igraph_vector_int_size(branches); + + if (type != IGRAPH_TREE_OUT && type != IGRAPH_TREE_IN && type != IGRAPH_TREE_UNDIRECTED) { + IGRAPH_ERROR("Invalid tree orientation type.", IGRAPH_EINVMODE); + } + if (!igraph_vector_int_empty(branches) && igraph_vector_int_min(branches) <= 0) { + IGRAPH_ERROR("The number of branches must be positive at each level.", IGRAPH_EINVAL); + } + + /* Compute the number of vertices in the tree. */ + no_of_nodes = 1; + temp = 1; + for (j = 0; j < branching_counts_size; ++j) { + IGRAPH_SAFE_MULT(temp, VECTOR(*branches)[j], &temp); + IGRAPH_SAFE_ADD(no_of_nodes, temp, &no_of_nodes); + } + + /* Trees have precisely |E| = |V| - 1 edges. */ + { + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(no_of_nodes - 1, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } + + idx = 0; + + /* Current parent and child vertex ids. + * parent -> child edges will be added. */ + child = 1; + parent = 0; + for (k = 0; k < branching_counts_size; ++k) { + level_end = child; /* points to one past the last vertex of the current level of parents */ + while (parent < level_end) { + IGRAPH_ALLOW_INTERRUPTION(); + for (j = 0; j < VECTOR(*branches)[k]; j++) { + if (type == IGRAPH_TREE_IN) { + VECTOR(edges)[idx++] = child++; + VECTOR(edges)[idx++] = parent; + } else { + VECTOR(edges)[idx++] = parent; + VECTOR(edges)[idx++] = child++; + } + } + parent++; + } + } + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, type != IGRAPH_TREE_UNDIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_regular_tree + * \brief Creates a regular tree. + * + * All vertices of a regular tree, except its leaves, have the same total degree \p k. + * This is different from a k-ary tree (\ref igraph_kary_tree()), where all + * vertices have the same number of children, thus the degre of the root is + * one less than the degree of the other internal vertices. Regular trees + * are also referred to as Bethe lattices. + * + * \param graph Pointer to an uninitialized graph object. + * \param h The height of the tree, i.e. the distance between the root and the leaves. + * \param k The degree of the regular 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. + * + * Time complexity: O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + * + * \sa \ref igraph_kary_tree() to create k-ary tree where each vertex has the same + * number of children, i.e. out-degree, instead of the same total degree. + * \ref igraph_symmetric_tree() to use a different number of children at each level. + * + * \example examples/simple/igraph_regular_tree.c + */ + +igraph_error_t igraph_regular_tree(igraph_t *graph, igraph_integer_t h, igraph_integer_t k, igraph_tree_mode_t type) { + igraph_vector_int_t branching_counts; + + if (h < 1) { + IGRAPH_ERRORF("Height of regular tree must be positive, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, h); + } + if (k < 2 ) { + IGRAPH_ERRORF("Degree of regular tree must be at least 2, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, k); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&branching_counts, h); + igraph_vector_int_fill(&branching_counts, k-1); + if (h > 0) { + VECTOR(branching_counts)[0] += 1; + } + + IGRAPH_CHECK(igraph_symmetric_tree(graph, &branching_counts, type)); + + igraph_vector_int_destroy(&branching_counts); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /** @@ -458,24 +872,31 @@ int igraph_tree(igraph_t *graph, igraph_integer_t n, igraph_integer_t children, * 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_error_t igraph_extended_chordal_ring( + igraph_t *graph, igraph_integer_t nodes, const igraph_matrix_int_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; + igraph_vector_int_t edges; + igraph_integer_t period = igraph_matrix_int_ncol(W); + igraph_integer_t nrow = igraph_matrix_int_nrow(W); + igraph_integer_t i, j, mpos = 0, epos = 0; if (nodes < 3) { - IGRAPH_ERROR("An extended chordal ring has at least 3 nodes", IGRAPH_EINVAL); + 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); + if (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)); + { + /* ecount = nodes + nodes * nrow */ + igraph_integer_t no_of_edges2; + IGRAPH_SAFE_MULT(nodes, nrow, &no_of_edges2); + IGRAPH_SAFE_ADD(no_of_edges2, nodes, &no_of_edges2); + IGRAPH_SAFE_MULT(no_of_edges2, 2, &no_of_edges2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges2); + } for (i = 0; i < nodes - 1; i++) { VECTOR(edges)[epos++] = i; @@ -487,8 +908,8 @@ int igraph_extended_chordal_ring( 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; + igraph_integer_t offset = MATRIX(*W, j, mpos); + igraph_integer_t v = (i + offset) % nodes; if (v < 0) { v += nodes; /* handle negative offsets */ @@ -505,7 +926,7 @@ int igraph_extended_chordal_ring( } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/constructors/trees.c b/src/vendor/cigraph/src/constructors/trees.c new file mode 100644 index 00000000000..d747321b6db --- /dev/null +++ b/src/vendor/cigraph/src/constructors/trees.c @@ -0,0 +1,163 @@ +/* + 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 +*/ + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_vector.h" + +/** + * \function igraph_tree_from_parent_vector + * \brief Constructs a tree or forest from a vector encoding the parent of each vertex. + * + * \experimental + * + * Rooted trees and forests are conveniently represented using a \p parents + * vector where the ID of the parent of vertex \c v is stored in parents[v]. + * This function serves to construct an igraph graph from a parent vector representation. + * The result is guaranteed to be a forest or a tree. If the \p parents vector + * is found to encode a cycle or a self-loop, an error is raised. + * + * + * Several igraph functions produce such vectors, such as graph traversal + * functions (\ref igraph_bfs() and \ref igraph_dfs()), shortest path functions + * that construct a shortest path tree, as well as some other specialized + * functions like \ref igraph_dominator_tree() or \ref igraph_cohesive_blocks(). + * Vertices which do not have parents (i.e. roots) get a negative entry in the + * \p parents vector. + * + * + * Use \ref igraph_bfs() or \ref igraph_dfs() to convert a forest into a parent + * vector representation. For trees, i.e. forests with a single root, it is + * more convenient to use \ref igraph_bfs_simple(). + * + * \param graph Pointer to an uninitialized graph object. + * \param parents The parent vector. parents[v] is the ID of + * the parent vertex of \c v. parents[v] < 0 indicates that + * \c v does not have a parent. + * \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. + * + * \sa \ref igraph_bfs(), \ref igraph_bfs_simple() for back-conversion; + * \ref igraph_from_prufer() for creating trees from Prüfer sequences; + * \ref igraph_is_tree() and \ref igraph_is_forest() to check if a graph + * is a tree or forest. + * + * Time complexity: O(n) where n is the length of \p parents. + */ +igraph_error_t igraph_tree_from_parent_vector( + igraph_t *graph, + const igraph_vector_int_t *parents, + igraph_tree_mode_t type) { + + const igraph_integer_t no_of_nodes = igraph_vector_int_size(parents); + igraph_vector_int_t seen; + igraph_vector_int_t edges; + igraph_bool_t directed, intree; + + switch (type) { + case IGRAPH_TREE_OUT: + directed = true; intree = false; break; + case IGRAPH_TREE_IN: + directed = true; intree = true; break; + case IGRAPH_TREE_UNDIRECTED: + directed = false; intree = true; break; + default: + IGRAPH_ERROR("Invalid tree mode.", IGRAPH_EINVAL); + } + + /* Catch null graph case */ + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, no_of_nodes); + + /* A tree has no_of_nodes - 1 edges but a forest has fewer. In order to support + * the use case of extracting small sub-trees of large graphs, we only reserve + * the full amount of memory needed for a tree when the graph is small. + * This also eliminates the need to check for integer overflow. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 1024 ? 2048 : 2*(no_of_nodes-1)); + igraph_vector_int_clear(&edges); + + igraph_integer_t c=1; + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + igraph_integer_t v = i; + + if (VECTOR(seen)[v]) continue; + + while (true) { + igraph_integer_t u; + + VECTOR(seen)[v] = c; /* mark v as seen in the current round */ + u = VECTOR(*parents)[v]; + + if (u < 0) { + break; /* v is a root, stop traversal */ + } + if (u >= no_of_nodes) { + IGRAPH_ERROR("Invalid vertex ID in parent vector.", IGRAPH_EINVVID); + } + + if (intree) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + } else { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + } + + if (VECTOR(seen)[u]) { + if (VECTOR(seen)[u] == c) { + /* u was already seen in the current round, we found a cycle. + * We distinguish between self-loops, i.e. 1-cycles, and longer + * cycles in order to make the error message more useful. */ + IGRAPH_ERROR( + u==v + ? "Found a self-loop while constructing tree from parent vector." + : "Found a cycle while constructing tree from parent vector.", + IGRAPH_EINVAL); + } + break; /* u was seen in a previous round, stop traversal */ + } + + v = u; + } + + c++; + } + + igraph_vector_int_destroy(&seen); + IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/core/array.c b/src/vendor/cigraph/src/core/array.c index 4d9904a1fd0..07bc67fad5d 100644 --- a/src/vendor/cigraph/src/core/array.c +++ b/src/vendor/cigraph/src/core/array.c @@ -31,11 +31,11 @@ #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_LONG +#define BASE_INT #include "igraph_pmt.h" #include "array.pmt" #include "igraph_pmt_off.h" -#undef BASE_LONG +#undef BASE_INT #define BASE_CHAR #include "igraph_pmt.h" diff --git a/src/vendor/cigraph/src/core/array.pmt b/src/vendor/cigraph/src/core/array.pmt index 6d5c7358109..227061f57ab 100644 --- a/src/vendor/cigraph/src/core/array.pmt +++ b/src/vendor/cigraph/src/core/array.pmt @@ -23,27 +23,37 @@ #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); +#include "math/safe_intop.h" + +igraph_error_t FUNCTION(igraph_array3, init)(TYPE(igraph_array3) *a, + igraph_integer_t n1, igraph_integer_t n2, igraph_integer_t n3) { + + igraph_integer_t size, n1n2; + + IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); + + IGRAPH_SAFE_MULT(n1, n2, &n1n2); + IGRAPH_SAFE_MULT(n1n2, n3, &size); + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&a->data, size)); + a->n1 = n1; a->n2 = n2; a->n3 = n3; - a->n1n2 = n1 * n2; + a->n1n2 = n1n2; - return ret; + return IGRAPH_SUCCESS; } 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) { +igraph_integer_t 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) { +igraph_integer_t FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, igraph_integer_t idx) { switch (idx) { case 1: return a->n1; break; @@ -55,15 +65,25 @@ long int FUNCTION(igraph_array3, n)(const TYPE(igraph_array3) *a, long int idx) 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); +igraph_error_t FUNCTION(igraph_array3, resize)( + TYPE(igraph_array3) *a, igraph_integer_t n1, igraph_integer_t n2, + igraph_integer_t n3) { + + igraph_integer_t size, n1n2; + + IGRAPH_ASSERT(n1 >= 0 && n2 >= 0 && n3 >= 0); + + IGRAPH_SAFE_MULT(n1, n2, &n1n2); + IGRAPH_SAFE_MULT(n1n2, n3, &size); + + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&a->data, size)); + a->n1 = n1; a->n2 = n2; a->n3 = n3; - a->n1n2 = n1 * n2; + a->n1n2 = n1n2; - return ret; + return IGRAPH_SUCCESS; } void FUNCTION(igraph_array3, null)(TYPE(igraph_array3) *a) { @@ -82,9 +102,9 @@ 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, +igraph_error_t 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; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/buckets.c b/src/vendor/cigraph/src/core/buckets.c index 67be50764e9..b43fe809c23 100644 --- a/src/vendor/cigraph/src/core/buckets.c +++ b/src/vendor/cigraph/src/core/buckets.c @@ -45,34 +45,34 @@ * _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); +igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->buckets, size); b->max = -1; b->no = 0; IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } void igraph_buckets_destroy(igraph_buckets_t *b) { - igraph_vector_long_destroy(&b->bptr); - igraph_vector_long_destroy(&b->buckets); + igraph_vector_int_destroy(&b->bptr); + igraph_vector_int_destroy(&b->buckets); } -long int igraph_buckets_popmax(igraph_buckets_t *b) { +igraph_integer_t 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) { + igraph_integer_t max; + while ( (max = VECTOR(b->bptr)[b->max]) == 0) { b->max --; } - VECTOR(b->bptr)[(long int) b->max] = VECTOR(b->buckets)[max - 1]; + VECTOR(b->bptr)[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; +igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t bucket) { + igraph_integer_t ret = VECTOR(b->bptr)[bucket] - 1; VECTOR(b->bptr)[bucket] = VECTOR(b->buckets)[ret]; b->no--; return ret; @@ -83,62 +83,62 @@ 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) { + igraph_integer_t bucket) { return VECTOR(b->bptr)[bucket] == 0; } -void igraph_buckets_add(igraph_buckets_t *b, long int bucket, - long int elem) { +void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { - VECTOR(b->buckets)[(long int) elem] = VECTOR(b->bptr)[(long int) bucket]; - VECTOR(b->bptr)[(long int) bucket] = elem + 1; + VECTOR(b->buckets)[elem] = VECTOR(b->bptr)[bucket]; + VECTOR(b->bptr)[bucket] = elem + 1; if (bucket > b->max) { - b->max = (int) bucket; + b->max = bucket; } b->no++; } void igraph_buckets_clear(igraph_buckets_t *b) { - igraph_vector_long_null(&b->bptr); - igraph_vector_long_null(&b->buckets); + igraph_vector_int_null(&b->bptr); + igraph_vector_int_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); +igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->bptr, bsize); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->next, size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&b->prev, size); b->max = -1; b->no = 0; IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } 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); + igraph_vector_int_destroy(&b->bptr); + igraph_vector_int_destroy(&b->next); + igraph_vector_int_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); + igraph_vector_int_null(&b->bptr); + igraph_vector_int_null(&b->next); + igraph_vector_int_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) { +igraph_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b) { + igraph_integer_t max; + while ( (max = VECTOR(b->bptr)[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]; +igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t bucket) { + igraph_integer_t ret = VECTOR(b->bptr)[bucket] - 1; + igraph_integer_t next = VECTOR(b->next)[ret]; VECTOR(b->bptr)[bucket] = next; if (next != 0) { VECTOR(b->prev)[next - 1] = 0; @@ -153,38 +153,38 @@ 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) { + igraph_integer_t 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]; +void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { + igraph_integer_t 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->max = bucket; } b->no++; } /* Remove an arbitrary element */ -void igraph_dbuckets_delete(igraph_dbuckets_t *b, long int bucket, - long int elem) { +void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem) { if (VECTOR(b->bptr)[bucket] == elem + 1) { /* First element in bucket */ - long int next = VECTOR(b->next)[elem]; + igraph_integer_t 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]; + igraph_integer_t next = VECTOR(b->next)[elem]; + igraph_integer_t prev = VECTOR(b->prev)[elem]; if (next != 0) { VECTOR(b->prev)[next - 1] = prev; } diff --git a/src/vendor/cigraph/src/core/buckets.h b/src/vendor/cigraph/src/core/buckets.h index 31635f134e4..48e340b63ed 100644 --- a/src/vendor/cigraph/src/core/buckets.h +++ b/src/vendor/cigraph/src/core/buckets.h @@ -23,16 +23,7 @@ #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_decls.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -41,40 +32,40 @@ __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_vector_int_t bptr; + igraph_vector_int_t buckets; igraph_integer_t max, no; } igraph_buckets_t; -int igraph_buckets_init(igraph_buckets_t *b, long int bsize, long int size); +igraph_error_t igraph_buckets_init(igraph_buckets_t *b, igraph_integer_t bsize, igraph_integer_t 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_integer_t igraph_buckets_popmax(igraph_buckets_t *b); +igraph_integer_t igraph_buckets_pop(igraph_buckets_t *b, igraph_integer_t 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); + igraph_integer_t bucket); +void igraph_buckets_add(igraph_buckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); typedef struct igraph_dbuckets_t { - igraph_vector_long_t bptr; - igraph_vector_long_t next, prev; + igraph_vector_int_t bptr; + igraph_vector_int_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); +igraph_error_t igraph_dbuckets_init(igraph_dbuckets_t *b, igraph_integer_t bsize, igraph_integer_t 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_integer_t igraph_dbuckets_popmax(igraph_dbuckets_t *b); +igraph_integer_t igraph_dbuckets_pop(igraph_dbuckets_t *b, igraph_integer_t 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); + igraph_integer_t bucket); +void igraph_dbuckets_add(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); +void igraph_dbuckets_delete(igraph_dbuckets_t *b, igraph_integer_t bucket, + igraph_integer_t elem); __END_DECLS diff --git a/src/vendor/cigraph/src/core/cutheap.c b/src/vendor/cigraph/src/core/cutheap.c index 1ab07132323..96dd1be7367 100644 --- a/src/vendor/cigraph/src/core/cutheap.c +++ b/src/vendor/cigraph/src/core/cutheap.c @@ -32,10 +32,10 @@ #define INDEXINC 1 static void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, - long int hidx1, long int hidx2) { + igraph_integer_t hidx1, igraph_integer_t hidx2) { if (hidx1 != hidx2) { - long int idx1 = (long int) VECTOR(ch->index)[hidx1]; - long int idx2 = (long int) VECTOR(ch->index)[hidx2]; + igraph_integer_t idx1 = VECTOR(ch->index)[hidx1]; + igraph_integer_t idx2 = VECTOR(ch->index)[hidx2]; igraph_real_t tmp = VECTOR(ch->heap)[hidx1]; VECTOR(ch->heap)[hidx1] = VECTOR(ch->heap)[hidx2]; @@ -49,8 +49,8 @@ static void igraph_i_cutheap_switch(igraph_i_cutheap_t *ch, } } -static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, long int hidx) { - long int size = igraph_vector_size(&ch->heap); +static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, igraph_integer_t hidx) { + igraph_integer_t size = igraph_vector_size(&ch->heap); if (LEFTCHILD(hidx) >= size) { /* leaf node */ } else if (RIGHTCHILD(hidx) == size || @@ -70,7 +70,7 @@ static void igraph_i_cutheap_sink(igraph_i_cutheap_t *ch, long int hidx) { } } -static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, long int hidx) { +static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, igraph_integer_t hidx) { if (hidx == 0 || VECTOR(ch->heap)[hidx] < VECTOR(ch->heap)[PARENT(hidx)]) { /* at the top */ } else { @@ -79,19 +79,19 @@ static void igraph_i_cutheap_shift_up(igraph_i_cutheap_t *ch, long int hidx) { } } -int igraph_i_cutheap_init(igraph_i_cutheap_t *ch, igraph_integer_t nodes) { +igraph_error_t 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_CHECK(igraph_vector_int_init_range(&ch->index, 0, nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &ch->index); + IGRAPH_CHECK(igraph_vector_init_range(&ch->hptr, INDEXINC, nodes + INDEXINC)); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } void igraph_i_cutheap_destroy(igraph_i_cutheap_t *ch) { igraph_vector_destroy(&ch->hptr); - igraph_vector_destroy(&ch->index); + igraph_vector_int_destroy(&ch->index); igraph_vector_destroy(&ch->heap); } @@ -102,13 +102,13 @@ igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch) { /* 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); + return 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); + return ch->dnodes; } igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { @@ -116,14 +116,14 @@ igraph_real_t igraph_i_cutheap_maxvalue(igraph_i_cutheap_t *ch) { } 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]; + igraph_integer_t size = igraph_vector_size(&ch->heap); + igraph_integer_t maxindex = 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; + VECTOR(ch->hptr)[ igraph_vector_int_tail(&ch->index)] = INACTIVE; igraph_vector_pop_back(&ch->heap); - igraph_vector_pop_back(&ch->index); + igraph_vector_int_pop_back(&ch->index); igraph_i_cutheap_sink(ch, 0); return maxindex; @@ -131,23 +131,22 @@ igraph_integer_t igraph_i_cutheap_popmax(igraph_i_cutheap_t *ch) { /* 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]; +void igraph_i_cutheap_update( + igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add) { + igraph_real_t hidx = VECTOR(ch->hptr)[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); */ + igraph_integer_t hidx2 = (hidx - INDEXINC); + /* printf("updating vertex %li, heap index %li\n", 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); +igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex) { + igraph_integer_t i, j, n = igraph_vector_size(&ch->hptr); /* undefine */ VECTOR(ch->hptr)[vertex] = UNDEFINED; ch->dnodes -= 1; @@ -155,7 +154,7 @@ int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex) { IGRAPH_CHECK(igraph_vector_resize(&ch->heap, ch->dnodes)); igraph_vector_null(&ch->heap); - IGRAPH_CHECK(igraph_vector_resize(&ch->index, ch->dnodes)); + IGRAPH_CHECK(igraph_vector_int_resize(&ch->index, ch->dnodes)); j = 0; for (i = 0; i < n; i++) { @@ -166,5 +165,5 @@ int igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, long int vertex) { } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/cutheap.h b/src/vendor/cigraph/src/core/cutheap.h index d7cb4c7d870..62947c6b879 100644 --- a/src/vendor/cigraph/src/core/cutheap.h +++ b/src/vendor/cigraph/src/core/cutheap.h @@ -23,16 +23,7 @@ #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_decls.h" #include "igraph_types.h" #include "igraph_vector.h" @@ -42,21 +33,20 @@ __BEGIN_DECLS typedef struct igraph_i_cutheap_t { igraph_vector_t heap; - igraph_vector_t index; + igraph_vector_int_t index; igraph_vector_t hptr; - long int dnodes; + igraph_integer_t 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 igraph_error_t 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); +IGRAPH_PRIVATE_EXPORT void igraph_i_cutheap_update(igraph_i_cutheap_t *ch, igraph_integer_t index, igraph_real_t add); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_cutheap_reset_undefine(igraph_i_cutheap_t *ch, igraph_integer_t vertex); __END_DECLS diff --git a/src/vendor/cigraph/src/core/dqueue.c b/src/vendor/cigraph/src/core/dqueue.c index 212b2426f51..20e8d953f8e 100644 --- a/src/vendor/cigraph/src/core/dqueue.c +++ b/src/vendor/cigraph/src/core/dqueue.c @@ -30,11 +30,11 @@ #include "igraph_pmt_off.h" #undef BASE_IGRAPH_REAL -#define BASE_LONG +#define BASE_INT #include "igraph_pmt.h" #include "dqueue.pmt" #include "igraph_pmt_off.h" -#undef BASE_LONG +#undef BASE_INT #define BASE_CHAR #include "igraph_pmt.h" @@ -47,9 +47,3 @@ #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/vendor/cigraph/src/core/dqueue.pmt b/src/vendor/cigraph/src/core/dqueue.pmt index 6ef7b678cd5..9829f6c5eb2 100644 --- a/src/vendor/cigraph/src/core/dqueue.pmt +++ b/src/vendor/cigraph/src/core/dqueue.pmt @@ -23,11 +23,29 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "config.h" #include /* memcpy & co. */ #include +/* Notes on the internal representation of dqueue: + * + * 'stor_begin' points at the beginning of the allocated storage. + * 'stor_end' points one past the allocated storage. + * + * 'begin' points at the first element of the queue contents. + * 'end' points one past the last element. + * + * The queue elements are stored "cyclically" within the allocated + * buffer, and arithmetic on 'begin' and 'end' is done modulo + * 'size = stor_end - stor_begin'. Thus the smallest valid value of + * 'begin' and 'end' is 'stor_begin'. Their largest valid value is + * 'stor_end - 1'. + * + * This means that 'begin == end' would be true both when the queue + * is full and when it is empty. To distinguish between these + * two situations, 'end' is set to NULL when the queue is empty. + */ + /** * \section igraph_dqueue * @@ -47,27 +65,27 @@ * \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. + * \param capacity How many elements to allocate memory for. * \return Error code. * - * Time complexity: O(\p size). + * Time complexity: O(\p capacity). */ -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; +igraph_error_t FUNCTION(igraph_dqueue, init)(TYPE(igraph_dqueue)* q, igraph_integer_t capacity) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(capacity >= 0); + + if (capacity == 0) capacity = 1; + + q->stor_begin = IGRAPH_CALLOC(capacity, BASE); + IGRAPH_CHECK_OOM(q->stor_begin, "Cannot initialize dqueue."); + q->stor_end = q->stor_begin + capacity; q->begin = q->stor_begin; q->end = NULL; - return 0; + return IGRAPH_SUCCESS; } /** @@ -75,17 +93,14 @@ int FUNCTION(igraph_dqueue, init) (TYPE(igraph_dqueue)* q, long int size) { * \function igraph_dqueue_destroy * \brief Destroy a double ended queue. * - * \param q The queue to destroy + * \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; - } +void FUNCTION(igraph_dqueue, destroy)(TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_FREE(q->stor_begin); /* sets to NULL */ } /** @@ -94,15 +109,15 @@ void FUNCTION(igraph_dqueue, destroy) (TYPE(igraph_dqueue)* q) { * \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. + * \return Boolean, true if \p q contains at least one element, + * 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); +igraph_bool_t FUNCTION(igraph_dqueue, empty)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); return q->end == NULL; } @@ -111,14 +126,14 @@ igraph_bool_t FUNCTION(igraph_dqueue, empty) (const TYPE(igraph_dqueue)* q) { * \function igraph_dqueue_clear * \brief Remove all elements from the queue. * - * \param q 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); +void FUNCTION(igraph_dqueue, clear)(TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); q->begin = q->stor_begin; q->end = NULL; } @@ -128,17 +143,18 @@ void FUNCTION(igraph_dqueue, clear) (TYPE(igraph_dqueue)* q) { * \function igraph_dqueue_full * \brief Check whether the queue is full. * - * If a queue is full the next igraph_dqueue_push() operation will allocate + * If a queue is full the next \ref igraph_dqueue_push() operation will allocate * more memory. + * * \param q The queue. - * \return \c TRUE if \p q is full, \c FALSE otherwise. + * \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); +igraph_bool_t FUNCTION(igraph_dqueue, full)(TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); return q->begin == q->end; } @@ -153,9 +169,9 @@ igraph_bool_t FUNCTION(igraph_dqueue, full) (TYPE(igraph_dqueue)* q) { * 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); +igraph_integer_t FUNCTION(igraph_dqueue, size)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); if (q->end == NULL) { return 0; } else if (q->begin < q->end) { @@ -171,15 +187,17 @@ long int FUNCTION(igraph_dqueue, size) (const TYPE(igraph_dqueue)* q) { * \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); +BASE FUNCTION(igraph_dqueue, head)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ return *(q->begin); } @@ -189,15 +207,17 @@ BASE FUNCTION(igraph_dqueue, head) (const TYPE(igraph_dqueue)* q) { * \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); +BASE FUNCTION(igraph_dqueue, back)(const TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ if (q->end == q->stor_begin) { return *(q->stor_end - 1); } @@ -211,16 +231,18 @@ BASE FUNCTION(igraph_dqueue, back) (const TYPE(igraph_dqueue)* q) { * * 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 FUNCTION(igraph_dqueue, pop)(TYPE(igraph_dqueue)* q) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ 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; @@ -235,20 +257,22 @@ BASE FUNCTION(igraph_dqueue, pop) (TYPE(igraph_dqueue)* q) { /** * \ingroup dqueue * \function igraph_dqueue_pop_back - * \brief Remove the tail + * \brief Removes 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 FUNCTION(igraph_dqueue, pop_back)(TYPE(igraph_dqueue)* q) { BASE tmp; - IGRAPH_ASSERT(q != 0); - IGRAPH_ASSERT(q->stor_begin != 0); + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); + IGRAPH_ASSERT(q->stor_end != NULL); /* queue is not empty */ if (q->end != q->stor_begin) { tmp = *((q->end) - 1); q->end = (q->end) - 1; @@ -269,20 +293,21 @@ BASE FUNCTION(igraph_dqueue, pop_back) (TYPE(igraph_dqueue)* q) { * \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 + * number of elements in the queue otherwise. But note 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); +igraph_error_t FUNCTION(igraph_dqueue, push)(TYPE(igraph_dqueue)* q, BASE elem) { + IGRAPH_ASSERT(q != NULL); + IGRAPH_ASSERT(q->stor_begin != NULL); if (q->begin != q->end) { /* not full */ if (q->end == NULL) { @@ -297,12 +322,19 @@ int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem) { /* full, allocate more storage */ BASE *bigger = NULL, *old = q->stor_begin; + igraph_integer_t old_size = q->stor_end - q->stor_begin; + igraph_integer_t new_capacity = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; - bigger = IGRAPH_CALLOC( 2 * (q->stor_end - q->stor_begin) + 1, BASE ); - if (bigger == 0) { - IGRAPH_ERROR("dqueue push failed", IGRAPH_ENOMEM); + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to dqueue, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_capacity == 0) { + new_capacity = 1; } + bigger = IGRAPH_CALLOC(new_capacity, BASE); + IGRAPH_CHECK_OOM(bigger, "Cannot push to dqueue."); + if (q->stor_end - q->begin) { memcpy(bigger, q->begin, (size_t)(q->stor_end - q->begin) * sizeof(BASE)); @@ -312,10 +344,10 @@ int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem) { (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->end = bigger + old_size; + q->stor_end = bigger + new_capacity; q->stor_begin = bigger; - q->begin = bigger; + q->begin = bigger; *(q->end) = elem; (q->end)++; @@ -326,18 +358,18 @@ int FUNCTION(igraph_dqueue, push) (TYPE(igraph_dqueue)* q, BASE elem) { IGRAPH_FREE(old); } - return 0; + return IGRAPH_SUCCESS; } #if defined (OUT_FORMAT) #ifndef USING_R -int FUNCTION(igraph_dqueue, print)(const TYPE(igraph_dqueue)* q) { +igraph_error_t 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) { +igraph_error_t 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; @@ -365,12 +397,26 @@ int FUNCTION(igraph_dqueue, fprint)(const TYPE(igraph_dqueue)* q, FILE *file) { fprintf(file, "\n"); - return 0; + return IGRAPH_SUCCESS; } #endif -BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx) { +/** + * \ingroup dqueue + * \function igraph_dqueue_get + * \brief Access an element in a queue. + * + * \param q The queue. + * \param idx The index of the element within the queue. + * \return The desired element. + * + * Time complexity: O(1). + */ + +BASE FUNCTION(igraph_dqueue, get)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { + IGRAPH_ASSERT(idx >= 0); + IGRAPH_ASSERT(idx < FUNCTION(igraph_dqueue, size)(q)); if ((q->begin + idx < q->end) || (q->begin >= q->end && q->begin + idx < q->stor_end)) { return q->begin[idx]; @@ -378,6 +424,20 @@ BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, long int idx) { idx = idx - (q->stor_end - q->begin); return q->stor_begin[idx]; } else { - return 0; /* Error */ + /* The assertions at the top make it impossible to reach here, + but omitting this branch would cause compiler warnings. */ + IGRAPH_FATAL("Out of bounds access in dqueue."); } } + +/** + * \ingroup dqueue + * \function igraph_dqueue_e + * \brief Access an element in a queue (deprecated alias). + * + * \deprecated-by igraph_dqueue_get 0.10.2 + */ + +BASE FUNCTION(igraph_dqueue, e)(const TYPE(igraph_dqueue) *q, igraph_integer_t idx) { + return FUNCTION(igraph_dqueue, get)(q, idx); +} diff --git a/src/vendor/cigraph/src/core/error.c b/src/vendor/cigraph/src/core/error.c index 214b761b2be..6e928cc00f2 100644 --- a/src/vendor/cigraph/src/core/error.c +++ b/src/vendor/cigraph/src/core/error.c @@ -62,7 +62,7 @@ * 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(void) { +static IGRAPH_FUNCATTR_NORETURN void igraph_abort(void) { #ifndef USING_R #ifdef IGRAPH_SANITIZER_AVAILABLE fprintf(stderr, "\nStack trace:\n"); @@ -92,7 +92,7 @@ static const char *igraph_i_error_strings[] = { /* 4 */ "Invalid value", /* 5 */ "Already exists", /* 6 */ "Invalid edge vector", - /* 7 */ "Invalid vertex id", + /* 7 */ "Invalid vertex ID", /* 8 */ "Non-square matrix", /* 9 */ "Invalid mode", /* 10 */ "File operation error", @@ -143,26 +143,29 @@ static const char *igraph_i_error_strings[] = { /* 51 */ "Internal attribute handler error", /* 52 */ "Unimplemented attribute combination for this type", /* 53 */ "LAPACK call resulted in an error", - /* 54 */ "Internal DrL error", + /* 54 */ "Internal DrL error; this error should never be visible to the user, " + "please report this error along with the steps to reproduce it.", /* 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." + "please report this error along with the steps to reproduce it.", + /* 61 */ "Result too large", + /* 62 */ "Input problem has no solution" }; -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."; +const char *igraph_strerror(const igraph_error_t igraph_errno) { + if ((int) igraph_errno < 0 || + (int) igraph_errno >= sizeof(igraph_i_error_strings) / sizeof(igraph_i_error_strings[0])) { + IGRAPH_FATALF("Invalid error code %d; no error string available.", (int) igraph_errno); } return igraph_i_error_strings[igraph_errno]; } -int igraph_error(const char *reason, const char *file, int line, - int igraph_errno) { +igraph_error_t igraph_error(const char *reason, const char *file, int line, + igraph_error_t igraph_errno) { if (igraph_i_error_handler) { igraph_i_error_handler(reason, file, line, igraph_errno); @@ -174,17 +177,18 @@ int igraph_error(const char *reason, const char *file, int line, return igraph_errno; } -int igraph_errorf(const char *reason, const char *file, int line, - int igraph_errno, ...) { +igraph_error_t igraph_errorf(const char *reason, const char *file, int line, + igraph_error_t igraph_errno, ...) { va_list ap; va_start(ap, igraph_errno); vsnprintf(igraph_i_errormsg_buffer, sizeof(igraph_i_errormsg_buffer) / sizeof(char), reason, ap); + va_end(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) { +igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, + igraph_error_t 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); @@ -192,7 +196,7 @@ int igraph_errorvf(const char *reason, const char *file, int line, #ifndef USING_R void igraph_error_handler_abort(const char *reason, const char *file, - int line, int igraph_errno) { + int line, igraph_error_t igraph_errno) { fprintf(stderr, "Error at %s:%i : %s - %s.\n", file, line, reason, igraph_strerror(igraph_errno)); igraph_abort(); @@ -200,7 +204,7 @@ void igraph_error_handler_abort(const char *reason, const char *file, #endif void igraph_error_handler_ignore(const char *reason, const char *file, - int line, int igraph_errno) { + int line, igraph_error_t igraph_errno) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); @@ -211,7 +215,7 @@ void igraph_error_handler_ignore(const char *reason, const char *file, #ifndef USING_R void igraph_error_handler_printignore(const char *reason, const char *file, - int line, int igraph_errno) { + int line, igraph_error_t igraph_errno) { fprintf(stderr, "Error at %s:%i : %s - %s.\n", file, line, reason, igraph_strerror(igraph_errno)); IGRAPH_FINALLY_FREE(); @@ -228,43 +232,104 @@ igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_han /***** "Finally" stack *****/ IGRAPH_THREAD_LOCAL struct igraph_i_protectedPtr igraph_i_finally_stack[100]; +IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_size = 0; +IGRAPH_THREAD_LOCAL int igraph_i_finally_stack_level = 0; + +static void igraph_i_reset_finally_stack(void) { + igraph_i_finally_stack_size = 0; + igraph_i_finally_stack_level = 0; +} /* * 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); + int no = igraph_i_finally_stack_size; + if (no < 0) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATALF("Corrupt finally stack: it contains %d elements.", no); + } + if (no >= (int) (sizeof(igraph_i_finally_stack) / sizeof(igraph_i_finally_stack[0]))) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATALF("Finally stack too large: it contains %d elements.", no); + } 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); */ + igraph_i_finally_stack[no].level = igraph_i_finally_stack_level; + igraph_i_finally_stack_size++; } 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_i_finally_stack_size -= minus; + if (igraph_i_finally_stack_size < 0) { + int left = igraph_i_finally_stack_size + minus; + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); 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--) { + for (; igraph_i_finally_stack_size > 0; igraph_i_finally_stack_size--) { + int p = igraph_i_finally_stack_size - 1; + /* Call destructors only up to the current level */ + if (igraph_i_finally_stack[p].level < igraph_i_finally_stack_level) { + break; + } 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; + return igraph_i_finally_stack_size; +} + +/** + * \function IGRAPH_FINALLY_ENTER + * + * For internal use only. + * + * Opens a new level in the finally stack. Must have a matching + * IGRAPH_FINALLY_EXIT() call that closes the level and exits it. + * + * The finally stack is divided into "levels". A call to IGRAPH_FINALLY_FREE() + * will only unwind the current level of the finally stack, not any of the lower + * levels. This mechanism is used to allow some functions to pause stack unwinding + * until they can restore their data structures into a consistent state. + * See \ref igraph_add_edges() for an example usage. + */ +void IGRAPH_FINALLY_ENTER(void) { + int no = igraph_i_finally_stack_size; + /* Level indices must always be in increasing order in the finally stack */ + if (no > 0 && igraph_i_finally_stack[no-1].level > igraph_i_finally_stack_level) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATAL("Corrupt finally stack: cannot create new finally stack level before last one is freed."); + } + igraph_i_finally_stack_level++; +} + +/** + * \function IGRAPH_FINALLY_EXIT + * + * For internal use only. + * + * Exists the current level of the finally stack, see IGRAPH_FINALLY_ENTER() + * for details. If an error occured inbetween the last pair of + * IGRAPH_FINALLY_ENTER()/EXIT() calls, a call to igraph_error(), typically + * through IGRAPH_ERROR(), is mandatory directly after IGRAPH_FINALLY_EXIT(). + * This ensures that resource cleanup will properly resume. + */ +void IGRAPH_FINALLY_EXIT(void) { + igraph_i_finally_stack_level--; + if (igraph_i_finally_stack_level < 0) { + /* Reset finally stack in case fatal error handler does a longjmp instead of terminating the process: */ + igraph_i_reset_finally_stack(); + IGRAPH_FATAL("Corrupt finally stack: trying to exit outermost finally stack level."); + } } @@ -285,12 +350,10 @@ static IGRAPH_THREAD_LOCAL igraph_warning_handler_t *igraph_i_warning_handler = * but this is currently not used in igraph. */ -void igraph_warning_handler_ignore(const char *reason, const char *file, - int line, int igraph_errno) { +void igraph_warning_handler_ignore(const char *reason, const char *file, int line) { IGRAPH_UNUSED(reason); IGRAPH_UNUSED(file); IGRAPH_UNUSED(line); - IGRAPH_UNUSED(igraph_errno); } #ifndef USING_R @@ -309,34 +372,30 @@ void igraph_warning_handler_ignore(const char *reason, const char *file, * 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); +void igraph_warning_handler_print(const char *reason, const char *file, int line) { 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) { +void igraph_warning(const char *reason, const char *file, int line) { if (igraph_i_warning_handler) { - igraph_i_warning_handler(reason, file, line, igraph_errno); + igraph_i_warning_handler(reason, file, line); #ifndef USING_R } else { - igraph_warning_handler_print(reason, file, line, igraph_errno); + igraph_warning_handler_print(reason, file, line); #endif } - return igraph_errno; } -int igraph_warningf(const char *reason, const char *file, int line, - int igraph_errno, ...) { +void igraph_warningf(const char *reason, const char *file, int line, + ...) { va_list ap; - va_start(ap, igraph_errno); + va_start(ap, line); 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); + va_end(ap); + igraph_warning(igraph_i_warningmsg_buffer, file, line); } igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *new_handler) { diff --git a/src/vendor/cigraph/src/core/estack.c b/src/vendor/cigraph/src/core/estack.c index 41a62ed31c0..5f37d82c322 100644 --- a/src/vendor/cigraph/src/core/estack.c +++ b/src/vendor/cigraph/src/core/estack.c @@ -23,45 +23,45 @@ #include "core/estack.h" -int igraph_estack_init(igraph_estack_t *s, long int setsize, - long int stacksize) { +igraph_error_t igraph_estack_init(igraph_estack_t *s, igraph_integer_t setsize, + igraph_integer_t 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_CHECK(igraph_stack_int_init(&s->stack, stacksize)); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } void igraph_estack_destroy(igraph_estack_t *s) { - igraph_stack_long_destroy(&s->stack); + igraph_stack_int_destroy(&s->stack); igraph_vector_bool_destroy(&s->isin); } -int igraph_estack_push(igraph_estack_t *s, long int elem) { +igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem) { if ( !VECTOR(s->isin)[elem] ) { - IGRAPH_CHECK(igraph_stack_long_push(&s->stack, elem)); + IGRAPH_CHECK(igraph_stack_int_push(&s->stack, elem)); VECTOR(s->isin)[elem] = 1; } - return 0; + return IGRAPH_SUCCESS; } -long int igraph_estack_pop(igraph_estack_t *s) { - long int elem = igraph_stack_long_pop(&s->stack); +igraph_integer_t igraph_estack_pop(igraph_estack_t *s) { + igraph_integer_t elem = igraph_stack_int_pop(&s->stack); VECTOR(s->isin)[elem] = 0; return elem; } igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, - long int elem) { + igraph_integer_t elem) { return VECTOR(s->isin)[elem]; } -long int igraph_estack_size(const igraph_estack_t *s) { - return igraph_stack_long_size(&s->stack); +igraph_integer_t igraph_estack_size(const igraph_estack_t *s) { + return igraph_stack_int_size(&s->stack); } #ifndef USING_R -int igraph_estack_print(const igraph_estack_t *s) { - return igraph_stack_long_print(&s->stack); +igraph_error_t igraph_estack_print(const igraph_estack_t *s) { + return igraph_stack_int_print(&s->stack); } #endif diff --git a/src/vendor/cigraph/src/core/estack.h b/src/vendor/cigraph/src/core/estack.h index 65178baa55d..ad315b3edb0 100644 --- a/src/vendor/cigraph/src/core/estack.h +++ b/src/vendor/cigraph/src/core/estack.h @@ -24,24 +24,29 @@ #ifndef IGRAPH_ESTACK_H #define IGRAPH_ESTACK_H +#include "igraph_decls.h" #include "igraph_stack.h" #include "igraph_vector.h" +__BEGIN_DECLS + typedef struct igraph_estack_t { - igraph_stack_long_t stack; + igraph_stack_int_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 igraph_error_t igraph_estack_init( + igraph_estack_t *s, igraph_integer_t setsize, igraph_integer_t 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_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem); +IGRAPH_PRIVATE_EXPORT igraph_integer_t 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_integer_t elem); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_estack_size(const igraph_estack_t *s); + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_print(const igraph_estack_t *s); -IGRAPH_PRIVATE_EXPORT int igraph_estack_print(const igraph_estack_t *s); +__END_DECLS #endif diff --git a/src/vendor/cigraph/src/core/exceptions.h b/src/vendor/cigraph/src/core/exceptions.h index f1cb6ec93da..d48608215e9 100644 --- a/src/vendor/cigraph/src/core/exceptions.h +++ b/src/vendor/cigraph/src/core/exceptions.h @@ -1,19 +1,34 @@ #ifndef IGRAPH_HANDLE_EXCEPTIONS_H #define IGRAPH_HANDLE_EXCEPTIONS_H +#include "igraph_error.h" + #include #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(). + * + * In some cases IGRAPH_HANDLE_EXCEPTIONS() won't work because the + * C preprocessor gets confused by the code block. In that case one can use + * IGRAPH_HANDLE_EXCEPTIONS_BEGIN; and IGRAPH_HANDLE_EXCEPTIONS_END at the + * beginning and end of the code block. */ -#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); } +#define IGRAPH_HANDLE_EXCEPTIONS_BEGIN \ + try { +#define IGRAPH_HANDLE_EXCEPTIONS_END \ + } \ + catch (const std::bad_alloc &e) { IGRAPH_ERROR(e.what(), IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } \ + catch (const std::range_error &e) { IGRAPH_ERROR(e.what(), IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ } \ + catch (const std::exception &e) { IGRAPH_ERROR(e.what(), IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } \ + catch (...) { IGRAPH_ERROR("Unknown exception caught.", IGRAPH_FAILURE); /* LCOV_EXCL_LINE */ } +#define IGRAPH_HANDLE_EXCEPTIONS(code) \ + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; \ + code; \ + IGRAPH_HANDLE_EXCEPTIONS_END; #endif // IGRAPH_HANDLE_EXCEPTIONS_H diff --git a/src/vendor/cigraph/src/core/fixed_vectorlist.c b/src/vendor/cigraph/src/core/fixed_vectorlist.c index adc482eb381..8b247f75599 100644 --- a/src/vendor/cigraph/src/core/fixed_vectorlist.c +++ b/src/vendor/cigraph/src/core/fixed_vectorlist.c @@ -21,61 +21,39 @@ */ -#include "igraph_memory.h" - #include "core/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); + igraph_vector_int_list_destroy(&l->vecs); } -int igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, - const igraph_vector_t *from, - long int size) { +igraph_error_t igraph_fixed_vectorlist_convert( + igraph_fixed_vectorlist_t *l, const igraph_vector_int_t *from, + igraph_integer_t size +) { + igraph_vector_int_t sizes; + igraph_integer_t i, no = igraph_vector_int_size(from), to; - 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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&l->vecs, size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, size); for (i = 0; i < no; i++) { - long int to = (long int) VECTOR(*from)[i]; + to = 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]; + to = VECTOR(*from)[i]; if (to >= 0) { - igraph_vector_t *v = &(l->vecs[to]); - igraph_vector_push_back(v, i); + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&l->vecs, to); + IGRAPH_CHECK(igraph_vector_int_push_back(v, i)); } } - igraph_vector_destroy(&sizes); - IGRAPH_FINALLY_CLEAN(3); + igraph_vector_int_destroy(&sizes); + IGRAPH_FINALLY_CLEAN(2); /* + l->vecs */ - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/fixed_vectorlist.h b/src/vendor/cigraph/src/core/fixed_vectorlist.h index 4a0907f8c1d..34e2437873a 100644 --- a/src/vendor/cigraph/src/core/fixed_vectorlist.h +++ b/src/vendor/cigraph/src/core/fixed_vectorlist.h @@ -27,7 +27,7 @@ #include "igraph_decls.h" #include "igraph_types.h" #include "igraph_vector.h" -#include "igraph_vector_ptr.h" +#include "igraph_vector_list.h" __BEGIN_DECLS @@ -36,15 +36,14 @@ __BEGIN_DECLS /* -------------------------------------------------- */ typedef struct igraph_fixed_vectorlist_t { - igraph_vector_t *vecs; - igraph_vector_ptr_t v; - long int length; + igraph_vector_int_list_t vecs; + igraph_integer_t 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); +igraph_error_t igraph_fixed_vectorlist_convert(igraph_fixed_vectorlist_t *l, + const igraph_vector_int_t *from, + igraph_integer_t size); __END_DECLS diff --git a/src/vendor/cigraph/src/core/genheap.c b/src/vendor/cigraph/src/core/genheap.c new file mode 100644 index 00000000000..1e4ec3f3fa9 --- /dev/null +++ b/src/vendor/cigraph/src/core/genheap.c @@ -0,0 +1,307 @@ +/* + 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, see . + */ + +#include "igraph_memory.h" + +#include "core/genheap.h" + +#include /* memcpy */ + +#if defined(_MSC_VER) && _MSC_VER < 1927 + /* MSVC does not understand restrict before version 19.27 */ + #define restrict __restrict +#endif + +#define PARENT(x) (((x)+1)/2-1) +#define LEFTCHILD(x) (((x)+1)*2-1) +#define RIGHTCHILD(x) (((x)+1)*2) + +#define ELEM(h, x) ((char *) h->data + x * h->item_size) + +/* This is a smart indexed heap that can hold elements of arbitrary type. + * 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 swapfunc(char * restrict a, char * restrict b, size_t es) { + char t; + + do { + t = *a; + *a++ = *b; + *b++ = t; + } while (--es > 0); +} + +static void igraph_i_gen2wheap_switch(igraph_gen2wheap_t *h, + igraph_integer_t e1, igraph_integer_t e2) { + if (e1 != e2) { + igraph_integer_t tmp1, tmp2; + + swapfunc(ELEM(h, e1), ELEM(h, e2), h->item_size); + + 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_gen2wheap_shift_up(igraph_gen2wheap_t *h, + igraph_integer_t elem) { + if (elem == 0 || h->cmp(ELEM(h, elem), ELEM(h, PARENT(elem))) < 0) { + /* at the top */ + } else { + igraph_i_gen2wheap_switch(h, elem, PARENT(elem)); + igraph_i_gen2wheap_shift_up(h, PARENT(elem)); + } +} + +static void igraph_i_gen2wheap_sink(igraph_gen2wheap_t *h, + igraph_integer_t head) { + igraph_integer_t size = igraph_gen2wheap_size(h); + if (LEFTCHILD(head) >= size) { + /* no subtrees */ + } else if (RIGHTCHILD(head) == size || + h->cmp(ELEM(h, LEFTCHILD(head)), ELEM(h, RIGHTCHILD(head))) >= 0) { + /* sink to the left if needed */ + if (h->cmp(ELEM(h, head), ELEM(h, LEFTCHILD(head))) < 0) { + igraph_i_gen2wheap_switch(h, head, LEFTCHILD(head)); + igraph_i_gen2wheap_sink(h, LEFTCHILD(head)); + } + } else { + /* sink to the right */ + if (h->cmp(ELEM(h, head), ELEM(h, RIGHTCHILD(head))) < 0) { + igraph_i_gen2wheap_switch(h, head, RIGHTCHILD(head)); + igraph_i_gen2wheap_sink(h, RIGHTCHILD(head)); + } + } +} + +/* ------------------ */ +/* These are public */ +/* ------------------ */ + +/** + * Initializes a new two-way heap. The max_size parameter defines the maximum + * number of items that the heap can hold. + */ +igraph_error_t igraph_gen2wheap_init( + igraph_gen2wheap_t *h, + int (*cmp)(const void *, const void *), + size_t item_size, igraph_integer_t max_size +) { + /* TODO: Currently, storage is allocated for the maximum number of elements + * right from the start. This is sufficient for the only use case as of this + * writing, the D-SATUR graph colouring algorithm, but it may not be efficcient + * for other use cases. Consider improving this in the future. + */ + h->max_size = max_size; + /* We start with the biggest */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&h->index2, max_size); + h->cmp = cmp; + h->item_size = item_size; + h->data = igraph_calloc(max_size, item_size); + IGRAPH_CHECK_OOM(h->data, "Cannot initialize generic heap."); + IGRAPH_FINALLY(igraph_free, h->data); + IGRAPH_CHECK(igraph_vector_int_init(&h->index, 0)); + + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; +} + +/** + * Destroys a two-way heap. + */ +void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h) { + IGRAPH_FREE(h->data); + igraph_vector_int_destroy(&h->index); + igraph_vector_int_destroy(&h->index2); +} + +/** + * Returns the current number of elements in the two-way heap. + */ +igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h) { + return igraph_vector_int_size(&h->index); +} + +/** + * Clears a two-way heap, i.e. removes all the elements from the heap. + */ +void igraph_gen2wheap_clear(igraph_gen2wheap_t *h) { + igraph_vector_int_clear(&h->index); + igraph_vector_int_null(&h->index2); +} + +/** + * Returns whether the two-way heap is empty. + */ +igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h) { + return igraph_vector_int_empty(&h->index); +} + +/** + * Pushes a new element into the two-way heap, with the given associated index. + * The index must be between 0 and the size of the heap minus 1, inclusive. It + * is assumed (and not checked) that the heap does not have another item with + * the same index. + */ +igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, + igraph_integer_t idx, const void *elem) { + + igraph_integer_t size = igraph_vector_int_size(&h->index); + + if (size > IGRAPH_INTEGER_MAX - 2) { + /* to allow size+2 below */ + IGRAPH_ERROR("Cannot push to gen2wheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + + memcpy(ELEM(h, size), elem, h->item_size); + IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); + VECTOR(h->index2)[idx] = size + 2; + + /* maintain heap */ + igraph_i_gen2wheap_shift_up(h, size); + + return IGRAPH_SUCCESS; +} + +/** + * Returns the maximum number of elements that the two-way heap can hold. This + * is also one larger than the maximum allowed index that can be passed to + * \c igraph_gen2wheap_push_with_index . + */ +igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h) { + return h->max_size; +} + +/** + * Returns a pointer to the largest element in the heap. + */ +const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h) { + return ELEM(h, 0); +} + +/** + * Returns the index that was associated to the largest element in the heap + * when it was pushed to the heap. + */ +igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h) { + return VECTOR(h->index)[0]; +} + +/** + * Returns whether the heap contains an element with the given index, even if + * it was deactivated earlier. + */ +igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] != 0; +} + +/** + * Returns whether the heap contains an element with the given index \em and it + * has not been deactivated yet. + */ +igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + return VECTOR(h->index2)[idx] > 1; +} + +/** + * Returns a pointer to the item at the given index in the two-way heap. + */ +const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx) { + igraph_integer_t i = VECTOR(h->index2)[idx] - 2; + return ELEM(h, i); +} + +/** + * Deletes the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h) { + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 0; + igraph_i_gen2wheap_sink(h, 0); +} + +/** + * Deactivates the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h) { + igraph_integer_t tmpidx = VECTOR(h->index)[0]; + igraph_i_gen2wheap_switch(h, 0, igraph_gen2wheap_size(h) - 1); + igraph_vector_int_pop_back(&h->index); + VECTOR(h->index2)[tmpidx] = 1; + igraph_i_gen2wheap_sink(h, 0); +} + +/** + * Modifies the value associated to the given index in the two-way heap. + */ +void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem) { + + igraph_integer_t pos = VECTOR(h->index2)[idx] - 2; + + memcpy(ELEM(h, pos), elem, h->item_size); + igraph_i_gen2wheap_sink(h, pos); + igraph_i_gen2wheap_shift_up(h, pos); +} + +/** + * Checks that the heap is in a consistent state + */ +igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h) { + igraph_integer_t size = igraph_gen2wheap_size(h); + igraph_integer_t i; + igraph_bool_t error = false; + + /* Check the heap property */ + for (i = 0; i < size; i++) { + if (LEFTCHILD(i) >= size) { + break; + } + if (h->cmp(ELEM(h, LEFTCHILD(i)), ELEM(h, i)) > 0) { + error = true; break; + } + if (RIGHTCHILD(i) >= size) { + break; + } + if (h->cmp(ELEM(h, RIGHTCHILD(i)), ELEM(h, i)) > 0) { + error = true; break; + } + } + + if (error) { + IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/core/genheap.h b/src/vendor/cigraph/src/core/genheap.h new file mode 100644 index 00000000000..381631104b9 --- /dev/null +++ b/src/vendor/cigraph/src/core/genheap.h @@ -0,0 +1,73 @@ +/* + 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, see . + */ + +#include "igraph_types.h" +#include "igraph_vector.h" + +typedef struct igraph_gen2wheap_t { + /** Maximum number of items in the heap */ + igraph_integer_t max_size; + + /** The size of an individual item */ + size_t item_size; + + /** The items themselves in the heap */ + /* TODO: currently this is always allocated to have max_size */ + void *data; + + /** qsort-style comparison function used to order items */ + int (*cmp)(const void *, const void *); + + /** An integer index associated to each item in the heap; this vector is + * always modified in tandem with \c data . Values in this vector are + * between 0 and size-1 */ + igraph_vector_int_t index; + + /** + * A _reverse_ index that allows O(1) lookup of the position of a given + * value within the \c index member. Note that it uses two special values: + * index2[i] == 0 means that \c i is not in \c index at all, while + * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. + * The semantics of deactivation is up to the user of the data structure + * to decide. Other than these two special values, index2[i] == j means + * that index[j-2] == i and data[j-2] is the corresponding item in the heap + */ + igraph_vector_int_t index2; +} igraph_gen2wheap_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_init( + igraph_gen2wheap_t *h, + int (*cmp)(const void *, const void *), + size_t item_size, igraph_integer_t max_size +); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_destroy(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_size(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_clear(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_empty(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_push_with_index(igraph_gen2wheap_t *h, + igraph_integer_t idx, const void *elem); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT const void *igraph_gen2wheap_get(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_delete_max(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_deactivate_max(igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT void igraph_gen2wheap_modify(igraph_gen2wheap_t *h, igraph_integer_t idx, const void *elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_check(const igraph_gen2wheap_t *h); diff --git a/src/vendor/cigraph/src/core/grid.c b/src/vendor/cigraph/src/core/grid.c index cb5f88130bd..810f5d7b3c8 100644 --- a/src/vendor/cigraph/src/core/grid.c +++ b/src/vendor/cigraph/src/core/grid.c @@ -29,15 +29,16 @@ /* internal function */ -int igraph_2dgrid_which(igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, - long int *x, long int *y) { - +static void igraph_i_2dgrid_which( + igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t yc, + igraph_integer_t *x, igraph_integer_t *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)); + *x = floor((xc - (grid->minx)) / (grid->deltax)); } if (yc <= grid->miny) { @@ -45,22 +46,20 @@ int igraph_2dgrid_which(igraph_2dgrid_t *grid, igraph_real_t xc, igraph_real_t y } else if (yc >= grid->maxy) { *y = grid->stepsy - 1; } else { - *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); + *y = floor((yc - (grid->miny)) / (grid->deltay)); } - - return 0; } -int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, +igraph_error_t 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; + igraph_integer_t no_of_points; IGRAPH_ASSERT(minx <= maxx); IGRAPH_ASSERT(miny <= maxy); IGRAPH_ASSERT(deltax > 0 && deltay > 0); - IGRAPH_ASSERT(igraph_finite(minx) && igraph_finite(maxx) && igraph_finite(miny) && igraph_finite(maxy)); - IGRAPH_ASSERT(igraph_finite(deltax) && igraph_finite(deltay)); + IGRAPH_ASSERT(isfinite(minx) && isfinite(maxx) && isfinite(miny) && isfinite(maxy)); + IGRAPH_ASSERT(isfinite(deltax) && isfinite(deltay)); grid->coords = coords; grid->minx = minx; @@ -70,44 +69,44 @@ int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, grid->maxy = maxy; grid->deltay = deltay; - grid->stepsx = (long int) ceil((maxx - minx) / deltax); - grid->stepsy = (long int) ceil((maxy - miny) / deltay); + grid->stepsx = ceil((maxx - minx) / deltax); + grid->stepsy = 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)); + no_of_points = igraph_matrix_nrow(coords); - for (i = 0; i < igraph_vector_size(&grid->next); i++) { - VECTOR(grid->next)[i] = -1; - } + IGRAPH_CHECK(igraph_matrix_int_init(&grid->startidx, grid->stepsx, grid->stepsy)); + IGRAPH_FINALLY(igraph_matrix_int_destroy, &grid->startidx); + IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->next, no_of_points); + IGRAPH_VECTOR_INT_INIT_FINALLY(&grid->prev, no_of_points); + + igraph_vector_int_fill(&grid->prev, 0); + igraph_vector_int_fill(&grid->next, 0); grid->massx = 0; grid->massy = 0; grid->vertices = 0; IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } void igraph_2dgrid_destroy(igraph_2dgrid_t *grid) { - igraph_matrix_destroy(&grid->startidx); - igraph_vector_destroy(&grid->next); - igraph_vector_destroy(&grid->prev); + igraph_matrix_int_destroy(&grid->startidx); + igraph_vector_int_destroy(&grid->next); + igraph_vector_int_destroy(&grid->prev); } -void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, +void igraph_2dgrid_add(igraph_2dgrid_t *grid, igraph_integer_t elem, igraph_real_t xc, igraph_real_t yc) { - long int x, y; - long int first; + igraph_integer_t x, y; + igraph_integer_t 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); + igraph_i_2dgrid_which(grid, xc, yc, &x, &y); + first = MATRIX(grid->startidx, x, y); VECTOR(grid->prev)[elem] = 0; VECTOR(grid->next)[elem] = first; if (first != 0) { @@ -120,17 +119,17 @@ void igraph_2dgrid_add(igraph_2dgrid_t *grid, long int elem, grid->vertices += 1; } -void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem) { - long int x, y; - long int first; +void igraph_2dgrid_add2(igraph_2dgrid_t *grid, igraph_integer_t elem) { + igraph_integer_t x, y; + igraph_integer_t 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); + igraph_i_2dgrid_which(grid, xc, yc, &x, &y); + first = MATRIX(grid->startidx, x, y); VECTOR(grid->prev)[elem] = 0; VECTOR(grid->next)[elem] = first; if (first != 0) { @@ -143,33 +142,33 @@ void igraph_2dgrid_add2(igraph_2dgrid_t *grid, long int elem) { grid->vertices += 1; } -void igraph_2dgrid_move(igraph_2dgrid_t *grid, long int elem, +void igraph_2dgrid_move(igraph_2dgrid_t *grid, igraph_integer_t elem, igraph_real_t xc, igraph_real_t yc) { - long int oldx, oldy; - long int newx, newy; + igraph_integer_t oldx, oldy; + igraph_integer_t newx, newy; igraph_real_t oldxc = MATRIX(*grid->coords, elem, 0); igraph_real_t oldyc = MATRIX(*grid->coords, elem, 1); - long int first; + igraph_integer_t first; xc = oldxc + xc; yc = oldyc + yc; - igraph_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); - igraph_2dgrid_which(grid, xc, yc, &newx, &newy); + igraph_i_2dgrid_which(grid, oldxc, oldyc, &oldx, &oldy); + igraph_i_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) [ 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)[ VECTOR(grid->next)[elem] - 1 ] = VECTOR(grid->prev)[elem]; } /* add to this cell */ - first = (long int) MATRIX(grid->startidx, newx, newy); + first = MATRIX(grid->startidx, newx, newy); VECTOR(grid->prev)[elem] = 0; VECTOR(grid->next)[elem] = first; if (first != 0) { @@ -192,106 +191,33 @@ void igraph_2dgrid_getcenter(const igraph_2dgrid_t *grid, *massy = (grid->massy) / (grid->vertices); } -igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, long int elem) { +igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, igraph_integer_t 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 igraph_2dgrid_sq_dist(const igraph_2dgrid_t *grid, + igraph_integer_t e1, igraph_integer_t 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); + it->x = 0; it->y = 0; it->vid = 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); + it->vid = 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; + igraph_integer_t ret = it->vid; if (ret == 0) { return 0; @@ -318,37 +244,37 @@ igraph_integer_t igraph_2dgrid_next(igraph_2dgrid_t *grid, it->nx[it->ncells] = it->x; it->ny[it->ncells] = it->y; - it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; + it->nei = 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]); + it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } /* Next vertex */ - it->vid = (long int) VECTOR(grid->next)[ it->vid - 1 ]; + it->vid = 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); + it->vid = MATRIX(grid->startidx, it->x, it->y); } - return (igraph_integer_t) ret; + return ret; } igraph_integer_t igraph_2dgrid_next_nei(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it) { - long int ret = it->nei; + igraph_integer_t ret = it->nei; if (it->nei != 0) { - it->nei = (long int) VECTOR(grid->next) [ ret - 1 ]; + it->nei = 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]); + it->nei = MATRIX(grid->startidx, it->nx[it->ncells], it->ny[it->ncells]); } - return (igraph_integer_t) ret; + return ret; } diff --git a/src/vendor/cigraph/src/core/grid.h b/src/vendor/cigraph/src/core/grid.h index d362ab79fba..26d7a67e8f2 100644 --- a/src/vendor/cigraph/src/core/grid.h +++ b/src/vendor/cigraph/src/core/grid.h @@ -35,38 +35,34 @@ __BEGIN_DECLS */ 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_matrix_t *coords; /* The current coordinates in the grid */ + igraph_real_t minx, maxx, deltax; /* Minimum and maximum X coordinates and X spacing */ + igraph_real_t miny, maxy, deltay; /* Minimum and maximum Y coordinates and Y spacing */ + igraph_integer_t stepsx, stepsy; /* Number of cells in the X and Y directions */ + igraph_matrix_int_t startidx; /* startidx[i, j] is the index of an arbitrary point in that grid cell, plus one; zero means "empty cell" */ + igraph_vector_int_t next; /* next[i] is the index of the point following point i in the same cell, plus one; zero means "last point" */ + igraph_vector_int_t prev; /* prev[i] is the index of the point preceding point i in the same cell, plus one; zero means "first point" */ igraph_real_t massx, massy; /* The sum of the coordinates */ - long int vertices; /* Number of active vertices */ + igraph_integer_t vertices; /* Number of active vertices */ } igraph_2dgrid_t; -int igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords, +igraph_error_t 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, +void igraph_2dgrid_add(igraph_2dgrid_t *grid, igraph_integer_t 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, +void igraph_2dgrid_add2(igraph_2dgrid_t *grid, igraph_integer_t elem); +void igraph_2dgrid_move(igraph_2dgrid_t *grid, igraph_integer_t 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); +igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, igraph_integer_t elem); typedef struct igraph_2dgrid_iterator_t { - long int vid, x, y; - long int nei; - long int nx[4], ny[4], ncells; + igraph_integer_t vid, x, y; + igraph_integer_t nei; + igraph_integer_t nx[4], ny[4], ncells; } igraph_2dgrid_iterator_t; void igraph_2dgrid_reset(igraph_2dgrid_t *grid, igraph_2dgrid_iterator_t *it); diff --git a/src/vendor/cigraph/src/core/heap.c b/src/vendor/cigraph/src/core/heap.c index b94a8d122df..a202ea64a60 100644 --- a/src/vendor/cigraph/src/core/heap.c +++ b/src/vendor/cigraph/src/core/heap.c @@ -37,7 +37,7 @@ #undef HEAP_TYPE_MIN #undef BASE_IGRAPH_REAL -#define BASE_LONG +#define BASE_INT #define HEAP_TYPE_MAX #include "igraph_pmt.h" #include "heap.pmt" @@ -48,7 +48,7 @@ #include "heap.pmt" #include "igraph_pmt_off.h" #undef HEAP_TYPE_MIN -#undef BASE_LONG +#undef BASE_INT #define BASE_CHAR #define HEAP_TYPE_MAX diff --git a/src/vendor/cigraph/src/core/heap.pmt b/src/vendor/cigraph/src/core/heap.pmt index 2501c8b1b19..0aa14c97030 100644 --- a/src/vendor/cigraph/src/core/heap.pmt +++ b/src/vendor/cigraph/src/core/heap.pmt @@ -23,7 +23,6 @@ #include "igraph_memory.h" #include "igraph_error.h" -#include "config.h" #include /* memcpy & co. */ #include @@ -33,38 +32,39 @@ #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); +static void FUNCTION(igraph_heap, i_build)(BASE* arr, igraph_integer_t size, igraph_integer_t head); +static void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_integer_t elem); +static void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_integer_t head); +static void FUNCTION(igraph_heap, i_switch)(BASE* arr, igraph_integer_t e1, igraph_integer_t e2); /** * \ingroup heap * \function igraph_heap_init * \brief Initializes an empty heap object. * - * Creates an empty heap, but allocates size for some elements. + * Creates an \em empty heap, and also allocates memory + * for some elements. + * * \param h Pointer to an uninitialized heap object. - * \param alloc_size Number of elements to allocate memory for. + * \param capacity 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); +igraph_error_t FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { + IGRAPH_ASSERT(capacity >= 0); + if (capacity == 0 ) { + capacity = 1; } - h->stor_end = h->stor_begin + alloc_size; + h->stor_begin = IGRAPH_CALLOC(capacity, BASE); + IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap."); + h->stor_end = h->stor_begin + capacity; h->end = h->stor_begin; - h->destroy = 1; + h->destroy = true; - return 0; + return IGRAPH_SUCCESS; } /** @@ -72,8 +72,9 @@ int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int alloc_size) { * \function igraph_heap_init_array * \brief Build a heap from an array. * - * Initializes a heap object from an array, the heap is also + * 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. @@ -82,20 +83,18 @@ int FUNCTION(igraph_heap, init)(TYPE(igraph_heap)* h, long int alloc_size) { * 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) { +igraph_error_t FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, const BASE *data, igraph_integer_t len) { h->stor_begin = IGRAPH_CALLOC(len, BASE); - if (h->stor_begin == 0) { - IGRAPH_ERROR("heap init from array failed", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(h->stor_begin, "Cannot initialize heap from array."); h->stor_end = h->stor_begin + len; h->end = h->stor_end; - h->destroy = 1; + h->destroy = true; - memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t)); + memcpy(h->stor_begin, data, (size_t) len * sizeof(BASE)); FUNCTION(igraph_heap, i_build) (h->stor_begin, h->end - h->stor_begin, 0); - return 0; + return IGRAPH_SUCCESS; } /** @@ -110,9 +109,8 @@ int FUNCTION(igraph_heap, init_array)(TYPE(igraph_heap) *h, BASE* data, long int 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; + if (h->stor_begin != NULL) { + IGRAPH_FREE(h->stor_begin); /* sets to NULL */ } } } @@ -123,23 +121,44 @@ void FUNCTION(igraph_heap, destroy)(TYPE(igraph_heap)* h) { * \brief Decides whether a heap object is empty. * * \param h The heap object. - * \return \c TRUE if the heap is empty, \c FALSE otherwise. + * \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_bool_t FUNCTION(igraph_heap, empty)(const 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_clear + * \brief Removes all elements from a heap. + * + * This function simply sets the size of the heap to zero, it does + * not free any allocated memory. For that you have to call + * \ref igraph_heap_destroy(). + * + * \param h The heap object. + * + * Time complexity: O(1). + */ + +void FUNCTION(igraph_heap, clear)(TYPE(igraph_heap)* h) { + IGRAPH_ASSERT(h != NULL); + IGRAPH_ASSERT(h->stor_begin != NULL); + h->end = h->stor_begin; +} + /** * \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. @@ -149,13 +168,17 @@ igraph_bool_t FUNCTION(igraph_heap, empty)(TYPE(igraph_heap)* h) { * that n push operations are performed in O(n log n) time. */ -int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { +igraph_error_t 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; + igraph_integer_t old_size = FUNCTION(igraph_heap, size)(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to heap, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -169,7 +192,7 @@ int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { FUNCTION(igraph_heap, i_shift_up)(h->stor_begin, FUNCTION(igraph_heap, size)(h), FUNCTION(igraph_heap, size)(h) - 1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -179,13 +202,14 @@ int FUNCTION(igraph_heap, push)(TYPE(igraph_heap)* h, BASE elem) { * * 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) { +BASE FUNCTION(igraph_heap, top)(const TYPE(igraph_heap)* h) { IGRAPH_ASSERT(h != NULL); IGRAPH_ASSERT(h->stor_begin != NULL); IGRAPH_ASSERT(h->stor_begin != h->end); @@ -196,10 +220,11 @@ BASE FUNCTION(igraph_heap, top)(TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_delete_top - * \brief Return and removes the top element + * \brief Removes and returns 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. * @@ -224,16 +249,17 @@ BASE FUNCTION(igraph_heap, delete_top)(TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_size - * \brief Number of elements + * \brief Number of elements in the heap. * * 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_integer_t FUNCTION(igraph_heap, size)(const TYPE(igraph_heap)* h) { IGRAPH_ASSERT(h != NULL); IGRAPH_ASSERT(h->stor_begin != NULL); return h->end - h->stor_begin; @@ -242,38 +268,39 @@ long int FUNCTION(igraph_heap, size)(TYPE(igraph_heap)* h) { /** * \ingroup heap * \function igraph_heap_reserve - * \brief Allocate more memory + * \brief Reserves memory for a heap. * * Allocates memory for future use. The size of the heap is - * unchanged. If the heap is larger than the \p size parameter then + * unchanged. If the heap is larger than the \p capacity parameter then * nothing happens. + * * \param h The heap object. - * \param size The number of elements to allocate memory for. + * \param capacity The number of elements to allocate memory for. * \return Error code. * - * Time complexity: O(\p size) if \p size is larger than the current + * Time complexity: O(\p capacity) if \p capacity 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); +igraph_error_t FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, igraph_integer_t capacity) { + igraph_integer_t actual_size = FUNCTION(igraph_heap, size)(h); BASE *tmp; IGRAPH_ASSERT(h != NULL); IGRAPH_ASSERT(h->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); - if (size <= actual_size) { - return 0; + if (capacity <= actual_size) { + return IGRAPH_SUCCESS; } - tmp = IGRAPH_REALLOC(h->stor_begin, (size_t) size, BASE); - if (tmp == 0) { - IGRAPH_ERROR("heap reserve failed", IGRAPH_ENOMEM); - } + tmp = IGRAPH_REALLOC(h->stor_begin, (size_t) capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for heap."); + h->stor_begin = tmp; - h->stor_end = h->stor_begin + size; + h->stor_end = h->stor_begin + capacity; h->end = h->stor_begin + actual_size; - return 0; + return IGRAPH_SUCCESS; } /** @@ -282,7 +309,7 @@ int FUNCTION(igraph_heap, reserve)(TYPE(igraph_heap)* h, long int size) { */ void FUNCTION(igraph_heap, i_build)(BASE* arr, - long int size, long int head) { + igraph_integer_t size, igraph_integer_t head) { if (RIGHTCHILD(head) < size) { /* both subtrees */ @@ -304,7 +331,7 @@ void FUNCTION(igraph_heap, i_build)(BASE* arr, * called directly. */ -void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem) { +void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, igraph_integer_t size, igraph_integer_t elem) { if (elem == 0 || arr[elem] HEAPLESS arr[PARENT(elem)]) { /* at the top */ @@ -320,7 +347,7 @@ void FUNCTION(igraph_heap, i_shift_up)(BASE* arr, long int size, long int elem) * called directly. */ -void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) { +void FUNCTION(igraph_heap, i_sink)(BASE* arr, igraph_integer_t size, igraph_integer_t head) { if (LEFTCHILD(head) >= size) { /* no subtrees */ @@ -346,7 +373,7 @@ void FUNCTION(igraph_heap, i_sink)(BASE* arr, long int size, long int head) { * called directly. */ -void FUNCTION(igraph_heap, i_switch)(BASE* arr, long int e1, long int e2) { +void FUNCTION(igraph_heap, i_switch)(BASE* arr, igraph_integer_t e1, igraph_integer_t e2) { if (e1 != e2) { BASE tmp = arr[e1]; arr[e1] = arr[e2]; diff --git a/src/vendor/cigraph/src/core/indheap.c b/src/vendor/cigraph/src/core/indheap.c index 67592b058f7..ea6926bf58f 100644 --- a/src/vendor/cigraph/src/core/indheap.c +++ b/src/vendor/cigraph/src/core/indheap.c @@ -37,71 +37,75 @@ #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); +static void igraph_indheap_i_build(igraph_indheap_t* h, igraph_integer_t head); +static void igraph_indheap_i_shift_up(igraph_indheap_t* h, igraph_integer_t elem); +static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head); +static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); /** * \ingroup indheap * \brief Initializes an indexed heap (constructor). * - * @return Error code: + * \return Error code: * - IGRAPH_ENOMEM: out of memory */ -int igraph_indheap_init(igraph_indheap_t* h, long int alloc_size) { - if (alloc_size <= 0 ) { +igraph_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t alloc_size) { + IGRAPH_ASSERT(alloc_size >= 0); + 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); + if (! h->stor_begin) { + h->index_begin = NULL; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - h->index_begin = IGRAPH_CALLOC(alloc_size, long int); - if (h->index_begin == 0) { + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! h->index_begin) { IGRAPH_FREE(h->stor_begin); - h->stor_begin = 0; - IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); + h->stor_begin = NULL; + IGRAPH_ERROR("indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } h->stor_end = h->stor_begin + alloc_size; h->end = h->stor_begin; h->destroy = 1; - return 0; + return IGRAPH_SUCCESS; } -int igraph_indheap_clear(igraph_indheap_t *h) { +void 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: + * \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; +igraph_error_t igraph_indheap_init_array(igraph_indheap_t *h, const igraph_real_t *data, igraph_integer_t len) { + igraph_integer_t i; + igraph_integer_t alloc_size; - h->stor_begin = IGRAPH_CALLOC(len, igraph_real_t); - if (h->stor_begin == 0) { + IGRAPH_ASSERT(len >= 0); + alloc_size = (len <= 0) ? 1 : len; + + h->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_real_t); + if (! h->stor_begin) { h->index_begin = 0; - IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - h->index_begin = IGRAPH_CALLOC(len, long int); - if (h->index_begin == 0) { + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! h->index_begin) { IGRAPH_FREE(h->stor_begin); h->stor_begin = 0; - IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("indheap init from array failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - h->stor_end = h->stor_begin + len; - h->end = h->stor_end; + h->stor_end = h->stor_begin + alloc_size; + h->end = h->stor_begin + len; h->destroy = 1; memcpy(h->stor_begin, data, (size_t) len * sizeof(igraph_real_t)); @@ -109,9 +113,9 @@ int igraph_indheap_init_array (igraph_indheap_t *h, igraph_real_t* data, lon h->index_begin[i] = i + 1; } - igraph_indheap_i_build (h, 0); + igraph_indheap_i_build(h, 0); - return 0; + return IGRAPH_SUCCESS; } /** @@ -119,7 +123,7 @@ int igraph_indheap_init_array (igraph_indheap_t *h, igraph_real_t* data, lon * \brief Destroys an initialized indexed heap. */ -void igraph_indheap_destroy (igraph_indheap_t* h) { +void igraph_indheap_destroy(igraph_indheap_t* h) { IGRAPH_ASSERT(h != 0); if (h->destroy) { if (h->stor_begin != 0) { @@ -138,7 +142,7 @@ void igraph_indheap_destroy (igraph_indheap_t* h) { * \brief Checks whether a heap is empty. */ -igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h) { +igraph_bool_t igraph_indheap_empty(const igraph_indheap_t *h) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); return h->stor_begin == h->end; @@ -149,13 +153,17 @@ igraph_bool_t igraph_indheap_empty (igraph_indheap_t* h) { * \brief Adds an element to an indexed heap. */ -int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem) { +igraph_error_t 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; + igraph_integer_t old_size = igraph_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -169,7 +177,7 @@ int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem) { /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -177,13 +185,17 @@ int igraph_indheap_push (igraph_indheap_t* h, igraph_real_t elem) { * \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_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t 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; + igraph_integer_t old_size = igraph_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -197,7 +209,7 @@ int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_rea /* maintain indheap */ igraph_indheap_i_shift_up(h, igraph_indheap_size(h) - 1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -205,8 +217,8 @@ int igraph_indheap_push_with_index(igraph_indheap_t* h, long int idx, igraph_rea * \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; +void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem) { + igraph_integer_t i, n; IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); @@ -219,13 +231,11 @@ int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem) } if (i == n) { - return 0; + return; } /* maintain indheap */ igraph_indheap_i_build(h, 0); - - return 0; } /** @@ -233,7 +243,7 @@ int igraph_indheap_modify(igraph_indheap_t* h, long int idx, igraph_real_t elem) * \brief Returns the largest element in an indexed heap. */ -igraph_real_t igraph_indheap_max (igraph_indheap_t* h) { +igraph_real_t igraph_indheap_max(const igraph_indheap_t *h) { IGRAPH_ASSERT(h != NULL); IGRAPH_ASSERT(h->stor_begin != NULL); IGRAPH_ASSERT(h->stor_begin != h->end); @@ -265,7 +275,7 @@ igraph_real_t igraph_indheap_delete_max(igraph_indheap_t* h) { * \brief Gives the number of elements in an indexed heap. */ -long int igraph_indheap_size (igraph_indheap_t* h) { +igraph_integer_t igraph_indheap_size(const igraph_indheap_t* h) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); return h->end - h->stor_begin; @@ -275,33 +285,33 @@ long int igraph_indheap_size (igraph_indheap_t* h) { * \ingroup indheap * \brief Reserves more memory for an indexed heap. * - * @return Error code: + * \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_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size) { + igraph_integer_t actual_size = igraph_indheap_size(h); igraph_real_t *tmp1; - long int *tmp2; + igraph_integer_t *tmp2; IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); if (size <= actual_size) { - return 0; + return IGRAPH_SUCCESS; } tmp1 = IGRAPH_CALLOC(size, igraph_real_t); if (tmp1 == 0) { - IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, tmp1); - tmp2 = IGRAPH_CALLOC(size, long int); + tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); if (tmp2 == 0) { - IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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)); + memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(igraph_integer_t)); IGRAPH_FREE(h->stor_begin); IGRAPH_FREE(h->index_begin); @@ -311,7 +321,7 @@ int igraph_indheap_reserve (igraph_indheap_t* h, long int size) { h->end = h->stor_begin + actual_size; IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** @@ -319,7 +329,7 @@ int igraph_indheap_reserve (igraph_indheap_t* h, long int size) { * \brief Returns the index of the largest element in an indexed heap. */ -long int igraph_indheap_max_index(igraph_indheap_t *h) { +igraph_integer_t igraph_indheap_max_index(const igraph_indheap_t *h) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); return h->index_begin[0]; @@ -331,9 +341,9 @@ long int igraph_indheap_max_index(igraph_indheap_t *h) { * directly. */ -static void igraph_indheap_i_build(igraph_indheap_t* h, long int head) { +static void igraph_indheap_i_build(igraph_indheap_t* h, igraph_integer_t head) { - long int size = igraph_indheap_size(h); + igraph_integer_t size = igraph_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_indheap_i_build(h, LEFTCHILD(head) ); @@ -354,7 +364,7 @@ static void igraph_indheap_i_build(igraph_indheap_t* h, long int head) { * directly. */ -static void igraph_indheap_i_shift_up(igraph_indheap_t *h, long int elem) { +static void igraph_indheap_i_shift_up(igraph_indheap_t *h, igraph_integer_t elem) { if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { /* at the top */ @@ -370,9 +380,9 @@ static void igraph_indheap_i_shift_up(igraph_indheap_t *h, long int elem) { * directly. */ -static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) { +static void igraph_indheap_i_sink(igraph_indheap_t* h, igraph_integer_t head) { - long int size = igraph_indheap_size(h); + igraph_integer_t size = igraph_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -397,7 +407,7 @@ static void igraph_indheap_i_sink(igraph_indheap_t* h, long int head) { * directly. */ -static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e2) { +static void igraph_indheap_i_switch(igraph_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2) { if (e1 != e2) { igraph_real_t tmp = h->stor_begin[e1]; h->stor_begin[e1] = h->stor_begin[e2]; @@ -405,7 +415,7 @@ static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e tmp = h->index_begin[e1]; h->index_begin[e1] = h->index_begin[e2]; - h->index_begin[e2] = (long int) tmp; + h->index_begin[e2] = tmp; } } @@ -415,49 +425,50 @@ static void igraph_indheap_i_switch(igraph_indheap_t* h, long int e1, long int e /* 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); +/* static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t head); */ /* Unused function */ +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t* h, igraph_integer_t elem); +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, igraph_integer_t head); +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2); /** * \ingroup doubleindheap * \brief Initializes an empty doubly indexed heap object (constructor). * - * @return Error code: + * \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 ) { +igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t* h, igraph_integer_t alloc_size) { + IGRAPH_ASSERT(alloc_size >= 0); + 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); + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + h->index_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); 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); + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - h->index2_begin = IGRAPH_CALLOC(alloc_size, long int); + h->index2_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); 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); + IGRAPH_ERROR("d_indheap init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - return 0; + return IGRAPH_SUCCESS; } /** @@ -465,7 +476,7 @@ int igraph_d_indheap_init (igraph_d_indheap_t* h, long int alloc_size) * \brief Destroys an initialized doubly indexed heap object. */ -void igraph_d_indheap_destroy (igraph_d_indheap_t* h) { +void igraph_d_indheap_destroy(igraph_d_indheap_t* h) { IGRAPH_ASSERT(h != 0); if (h->destroy) { if (h->stor_begin != 0) { @@ -488,7 +499,7 @@ void igraph_d_indheap_destroy (igraph_d_indheap_t* h) { * \brief Decides whether a heap is empty. */ -igraph_bool_t igraph_d_indheap_empty (igraph_d_indheap_t* h) { +igraph_bool_t igraph_d_indheap_empty(const igraph_d_indheap_t *h) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); return h->stor_begin == h->end; @@ -499,14 +510,18 @@ igraph_bool_t igraph_d_indheap_empty (igraph_d_indheap_t* h) { * \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_error_t igraph_d_indheap_push(igraph_d_indheap_t* h, igraph_real_t elem, + igraph_integer_t idx, igraph_integer_t 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; + igraph_integer_t old_size = igraph_d_indheap_size(h); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to indheap, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -521,7 +536,7 @@ int igraph_d_indheap_push (igraph_d_indheap_t* h, igraph_real_t elem, /* maintain d_indheap */ igraph_d_indheap_i_shift_up(h, igraph_d_indheap_size(h) - 1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -529,7 +544,7 @@ int igraph_d_indheap_push (igraph_d_indheap_t* h, igraph_real_t elem, * \brief Returns the largest element in the heap. */ -igraph_real_t igraph_d_indheap_max (igraph_d_indheap_t* h) { +igraph_real_t igraph_d_indheap_max(const igraph_d_indheap_t *h) { IGRAPH_ASSERT(h != NULL); IGRAPH_ASSERT(h->stor_begin != NULL); IGRAPH_ASSERT(h->stor_begin != h->end); @@ -561,7 +576,7 @@ igraph_real_t igraph_d_indheap_delete_max(igraph_d_indheap_t* h) { * \brief Gives the number of elements in the heap. */ -long int igraph_d_indheap_size (igraph_d_indheap_t* h) { +igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t* h) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); return h->end - h->stor_begin; @@ -571,40 +586,40 @@ long int igraph_d_indheap_size (igraph_d_indheap_t* h) { * \ingroup doubleindheap * \brief Allocates memory for a heap. * - * @return Error code: + * \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_error_t igraph_d_indheap_reserve(igraph_d_indheap_t* h, igraph_integer_t size) { + igraph_integer_t actual_size = igraph_d_indheap_size(h); igraph_real_t *tmp1; - long int *tmp2, *tmp3; + igraph_integer_t *tmp2, *tmp3; IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); if (size <= actual_size) { - return 0; + return IGRAPH_SUCCESS; } tmp1 = IGRAPH_CALLOC(size, igraph_real_t); if (tmp1 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, tmp1); - tmp2 = IGRAPH_CALLOC(size, long int); + tmp2 = IGRAPH_CALLOC(size, igraph_integer_t); if (tmp2 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, tmp2); - tmp3 = IGRAPH_CALLOC(size, long int); + tmp3 = IGRAPH_CALLOC(size, igraph_integer_t); if (tmp3 == 0) { - IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("d_indheap reserve failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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)); + memcpy(tmp2, h->index_begin, (size_t) actual_size * sizeof(igraph_integer_t)); + memcpy(tmp3, h->index2_begin, (size_t) actual_size * sizeof(igraph_integer_t)); IGRAPH_FREE(h->stor_begin); IGRAPH_FREE(h->index_begin); IGRAPH_FREE(h->index2_begin); @@ -616,7 +631,7 @@ int igraph_d_indheap_reserve (igraph_d_indheap_t* h, long int size) { h->index2_begin = tmp3; IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -624,7 +639,7 @@ int igraph_d_indheap_reserve (igraph_d_indheap_t* h, long int size) { * \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) { +void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *idx2) { IGRAPH_ASSERT(h != 0); IGRAPH_ASSERT(h->stor_begin != 0); (*idx) = h->index_begin[0]; @@ -638,9 +653,9 @@ void igraph_d_indheap_max_index(igraph_d_indheap_t *h, long int *idx, long int * /* Unused function, temporarily disabled */ #if 0 -static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) { +static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, igraph_integer_t head) { - long int size = igraph_d_indheap_size(h); + igraph_integer_t size = igraph_d_indheap_size(h); if (RIGHTCHILD(head) < size) { /* both subtrees */ igraph_d_indheap_i_build(h, LEFTCHILD(head) ); @@ -661,7 +676,7 @@ static void igraph_d_indheap_i_build(igraph_d_indheap_t* h, long int head) { * \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) { +static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, igraph_integer_t elem) { if (elem == 0 || h->stor_begin[elem] < h->stor_begin[PARENT(elem)]) { /* at the top */ @@ -676,9 +691,9 @@ static void igraph_d_indheap_i_shift_up(igraph_d_indheap_t *h, long int elem) { * \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) { +static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, igraph_integer_t head) { - long int size = igraph_d_indheap_size(h); + igraph_integer_t size = igraph_d_indheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -702,9 +717,9 @@ static void igraph_d_indheap_i_sink(igraph_d_indheap_t* h, long int head) { * \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) { +static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, igraph_integer_t e1, igraph_integer_t e2) { if (e1 != e2) { - long int tmpi; + igraph_integer_t tmpi; igraph_real_t tmp = h->stor_begin[e1]; h->stor_begin[e1] = h->stor_begin[e2]; h->stor_begin[e2] = tmp; @@ -738,9 +753,9 @@ static void igraph_d_indheap_i_switch(igraph_d_indheap_t* h, long int e1, long i normal heap does this in O(n) time.... */ static void igraph_i_2wheap_switch(igraph_2wheap_t *h, - long int e1, long int e2) { + igraph_integer_t e1, igraph_integer_t e2) { if (e1 != e2) { - long int tmp1, tmp2; + igraph_integer_t tmp1, tmp2; igraph_real_t tmp3 = VECTOR(h->data)[e1]; VECTOR(h->data)[e1] = VECTOR(h->data)[e2]; VECTOR(h->data)[e2] = tmp3; @@ -757,7 +772,7 @@ static void igraph_i_2wheap_switch(igraph_2wheap_t *h, } static void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, - long int elem) { + igraph_integer_t elem) { if (elem == 0 || VECTOR(h->data)[elem] < VECTOR(h->data)[PARENT(elem)]) { /* at the top */ } else { @@ -767,8 +782,8 @@ static void igraph_i_2wheap_shift_up(igraph_2wheap_t *h, } static void igraph_i_2wheap_sink(igraph_2wheap_t *h, - long int head) { - long int size = igraph_2wheap_size(h); + igraph_integer_t head) { + igraph_integer_t size = igraph_2wheap_size(h); if (LEFTCHILD(head) >= size) { /* no subtrees */ } else if (RIGHTCHILD(head) == size || @@ -791,87 +806,142 @@ static void igraph_i_2wheap_sink(igraph_2wheap_t *h, /* These are public */ /* ------------------ */ -int igraph_2wheap_init(igraph_2wheap_t *h, long int size) { - h->size = size; +/** + * Initializes a new two-way heap. The max_size parameter defines the maximum + * number of items that the heap can hold. + */ +igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t max_size) { + h->max_size = max_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_INT_INIT_FINALLY(&h->index2, max_size); 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_CHECK(igraph_vector_int_init(&h->index, 0)); + /* IGRAPH_FINALLY(igraph_vector_int_destroy, &h->index); */ IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } +/** + * Destroys a two-way heap. + */ 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); + igraph_vector_int_destroy(&h->index); + igraph_vector_int_destroy(&h->index2); } -int igraph_2wheap_clear(igraph_2wheap_t *h) { +/** + * Clears a two-way heap, i.e. removes all the elements from the heap. + */ +void 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_vector_int_clear(&h->index); + igraph_vector_int_null(&h->index2); } +/** + * Returns whether the two-way heap is empty. + */ 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) { +/** + * Pushes a new element into the two-way heap, with the given associated index. + * The index must be between 0 and the size of the heap minus 1, inclusive. It + * is assumed (and not checked) that the heap does not have another item with + * the same index. + */ +igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, + igraph_integer_t idx, igraph_real_t elem) { /* printf("-> %.2g [%li]\n", elem, idx); */ - long int size = igraph_vector_size(&h->data); + igraph_integer_t size = igraph_vector_size(&h->data); + + if (size > IGRAPH_INTEGER_MAX - 2) { + /* to allow size+2 below */ + IGRAPH_ERROR("Cannot push to 2wheap, already at maximum size.", IGRAPH_EOVERFLOW); + } + IGRAPH_CHECK(igraph_vector_push_back(&h->data, elem)); - IGRAPH_CHECK(igraph_vector_long_push_back(&h->index, idx)); + IGRAPH_CHECK(igraph_vector_int_push_back(&h->index, idx)); VECTOR(h->index2)[idx] = size + 2; /* maintain heap */ igraph_i_2wheap_shift_up(h, size); - return 0; + return IGRAPH_SUCCESS; } -long int igraph_2wheap_size(const igraph_2wheap_t *h) { +/** + * Returns the current number of elements in the two-way heap. + */ +igraph_integer_t 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; +/** + * Returns the maximum number of elements that the two-way heap can hold. This + * is also one larger than the maximum allowed index that can be passed to + * \c igraph_2wheap_push_with_index . + */ +igraph_integer_t igraph_2wheap_max_size(const igraph_2wheap_t *h) { + return h->max_size; } +/** + * Returns the largest element in the heap. + */ 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) { +/** + * Returns the index that was associated to the largest element in the heap + * when it was pushed to the heap. + */ +igraph_integer_t 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) { +/** + * Returns whether the heap contains an element with the given index, even if + * it was deactivated earlier. + */ +igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx) { return VECTOR(h->index2)[idx] != 0; } -igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, long int idx) { +/** + * Returns whether the heap contains an element with the given index \em and it + * has not been deactivated yet. + */ +igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t 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; +/** + * Returns the item at the given index in the two-way heap. + */ +igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t idx) { + igraph_integer_t i = VECTOR(h->index2)[idx] - 2; return VECTOR(h->data)[i]; } +/** + * Deletes and returns the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ 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_integer_t 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); + igraph_vector_int_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); @@ -880,26 +950,39 @@ igraph_real_t igraph_2wheap_delete_max(igraph_2wheap_t *h) { return tmp; } +/** + * Deactivates and returns the largest element from the two-way heap. + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ 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_integer_t 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); + igraph_vector_int_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) { +/** + * Deletes the largest element from the heap and returns it along with its + * associated index (the latter being returned in an output argument). + * + * This function does \em not change the indices associated to the elements + * that remain in the heap. + */ +igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, igraph_integer_t *idx) { igraph_real_t tmp = VECTOR(h->data)[0]; - long int tmpidx = VECTOR(h->index)[0]; + igraph_integer_t 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); + igraph_vector_int_pop_back(&h->index); VECTOR(h->index2)[tmpidx] = 0; igraph_i_2wheap_sink(h, 0); @@ -909,25 +992,27 @@ igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, long int *idx) return tmp; } -int igraph_2wheap_modify(igraph_2wheap_t *h, long int idx, igraph_real_t elem) { +/** + * Modifies the value associated to the given index in the two-way heap. + */ +void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem) { - long int pos = VECTOR(h->index2)[idx] - 2; + igraph_integer_t 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; +/** + * Checks that the heap is in a consistent state + */ +igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h) { + igraph_integer_t size = igraph_2wheap_size(h); + igraph_integer_t i; + igraph_bool_t error = false; /* Check the heap property */ for (i = 0; i < size; i++) { @@ -935,19 +1020,19 @@ int igraph_2wheap_check(igraph_2wheap_t *h) { break; } if (VECTOR(h->data)[LEFTCHILD(i)] > VECTOR(h->data)[i]) { - error = 1; break; + error = true; break; } if (RIGHTCHILD(i) >= size) { break; } if (VECTOR(h->data)[RIGHTCHILD(i)] > VECTOR(h->data)[i]) { - error = 1; break; + error = true; break; } } if (error) { - IGRAPH_ERROR("Inconsistent heap", IGRAPH_EINTERNAL); + IGRAPH_ERROR("Inconsistent heap.", IGRAPH_EINTERNAL); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/indheap.h b/src/vendor/cigraph/src/core/indheap.h index 16977f9fa62..9f340c33301 100644 --- a/src/vendor/cigraph/src/core/indheap.h +++ b/src/vendor/cigraph/src/core/indheap.h @@ -42,25 +42,25 @@ 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_bool_t destroy; + igraph_integer_t* 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_error_t igraph_indheap_init(igraph_indheap_t* h, igraph_integer_t size); +igraph_error_t igraph_indheap_init_array(igraph_indheap_t *t, const igraph_real_t *data, igraph_integer_t len); +void igraph_indheap_destroy(igraph_indheap_t* h); +void igraph_indheap_clear(igraph_indheap_t *h); +igraph_bool_t igraph_indheap_empty(const igraph_indheap_t* h); +igraph_error_t igraph_indheap_push(igraph_indheap_t* h, igraph_real_t elem); +igraph_error_t igraph_indheap_push_with_index(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); +void igraph_indheap_modify(igraph_indheap_t* h, igraph_integer_t idx, igraph_real_t elem); +igraph_real_t igraph_indheap_max(const 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); +igraph_integer_t igraph_indheap_size(const igraph_indheap_t *h); +igraph_error_t igraph_indheap_reserve(igraph_indheap_t* h, igraph_integer_t size); +igraph_integer_t igraph_indheap_max_index(const igraph_indheap_t *h); /* -------------------------------------------------- */ @@ -81,24 +81,24 @@ 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_bool_t destroy; + igraph_integer_t* index_begin; + igraph_integer_t* 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 igraph_error_t igraph_d_indheap_init(igraph_d_indheap_t *h, igraph_integer_t 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_bool_t igraph_d_indheap_empty(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_push(igraph_d_indheap_t *h, igraph_real_t elem, + igraph_integer_t idx, igraph_integer_t idx2); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_d_indheap_max(const 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); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_d_indheap_reserve(igraph_d_indheap_t *h, igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, igraph_integer_t *idx, igraph_integer_t *idx2); /* -------------------------------------------------- */ /* Two-way indexed heap */ @@ -110,30 +110,47 @@ IGRAPH_PRIVATE_EXPORT void igraph_d_indheap_max_index(igraph_d_indheap_t *h, lon normal heap does this in O(n) time.... */ typedef struct igraph_2wheap_t { - long int size; + /** Number of items in the heap */ + igraph_integer_t max_size; + + /** The items themselves in the heap */ igraph_vector_t data; - igraph_vector_long_t index; - igraph_vector_long_t index2; + + /** An integer index associated to each item in the heap; this vector is + * always modified in tandem with \c data . Values in this vector are + * between 0 and size-1 */ + igraph_vector_int_t index; + + /** + * A _reverse_ index that allows O(1) lookup of the position of a given + * value within the \c index member. Note that it uses two special values: + * index2[i] == 0 means that \c i is not in \c index at all, while + * index2[i] == 1 means that \c i is in \c index but it was _deactivated_. + * The semantics of deactivation is up to the user of the data structure + * to decide. Other than these two special values, index2[i] == j means + * that index[j-2] == i and data[j-2] is the corresponding item in the heap + */ + igraph_vector_int_t index2; } igraph_2wheap_t; -IGRAPH_PRIVATE_EXPORT int igraph_2wheap_init(igraph_2wheap_t *h, long int size); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_init(igraph_2wheap_t *h, igraph_integer_t 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 void igraph_2wheap_clear(igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_push_with_index(igraph_2wheap_t *h, + igraph_integer_t 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_integer_t igraph_2wheap_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT igraph_integer_t 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_integer_t 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_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_get(const igraph_2wheap_t *h, igraph_integer_t 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); +IGRAPH_PRIVATE_EXPORT igraph_real_t igraph_2wheap_delete_max_index(igraph_2wheap_t *h, igraph_integer_t *idx); +IGRAPH_PRIVATE_EXPORT void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_2wheap_check(const igraph_2wheap_t *h); __END_DECLS diff --git a/src/vendor/cigraph/src/core/interruption.c b/src/vendor/cigraph/src/core/interruption.c index d4bf3c91a33..794220389ac 100644 --- a/src/vendor/cigraph/src/core/interruption.c +++ b/src/vendor/cigraph/src/core/interruption.c @@ -24,19 +24,17 @@ #include "igraph_interrupt.h" #include "config.h" -IGRAPH_THREAD_LOCAL igraph_interruption_handler_t -*igraph_i_interruption_handler = 0; +IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler = 0; -int igraph_allow_interruption(void* data) { +igraph_error_t 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_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/vendor/cigraph/src/core/interruption.h b/src/vendor/cigraph/src/core/interruption.h index 6afda321451..a90ac88a804 100644 --- a/src/vendor/cigraph/src/core/interruption.h +++ b/src/vendor/cigraph/src/core/interruption.h @@ -30,8 +30,7 @@ __BEGIN_DECLS -extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t -*igraph_i_interruption_handler; +extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t *igraph_i_interruption_handler; /** * \define IGRAPH_ALLOW_INTERRUPTION @@ -46,12 +45,11 @@ extern IGRAPH_THREAD_LOCAL igraph_interruption_handler_t #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); } \ + if (igraph_i_interruption_handler) { \ + if (igraph_allow_interruption(NULL) != IGRAPH_SUCCESS) { \ + return IGRAPH_INTERRUPTED; \ + } \ + } \ } while (0) __END_DECLS diff --git a/src/vendor/cigraph/src/core/marked_queue.c b/src/vendor/cigraph/src/core/marked_queue.c index 8166793cbed..29fe4116cba 100644 --- a/src/vendor/cigraph/src/core/marked_queue.c +++ b/src/vendor/cigraph/src/core/marked_queue.c @@ -25,64 +25,64 @@ #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)); +igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, + igraph_integer_t size) { + IGRAPH_CHECK(igraph_dqueue_int_init(&q->Q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q->Q); + IGRAPH_CHECK(igraph_vector_int_init(&q->set, size)); q->mark = 1; q->size = 0; IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -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_int_destroy(igraph_marked_queue_int_t *q) { + igraph_vector_int_destroy(&q->set); + igraph_dqueue_int_destroy(&q->Q); } -void igraph_marked_queue_reset(igraph_marked_queue_t *q) { - igraph_dqueue_clear(&q->Q); +void igraph_marked_queue_int_reset(igraph_marked_queue_int_t *q) { + igraph_dqueue_int_clear(&q->Q); q->size = 0; q->mark += 1; if (q->mark == 0) { - igraph_vector_long_null(&q->set); + igraph_vector_int_null(&q->set); q->mark += 1; } } -igraph_bool_t igraph_marked_queue_empty(const igraph_marked_queue_t *q) { +igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q) { return q->size == 0; } -long int igraph_marked_queue_size(const igraph_marked_queue_t *q) { +igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_t *q) { return q->size; } -igraph_bool_t igraph_marked_queue_iselement(const igraph_marked_queue_t *q, - long int elem) { +igraph_bool_t igraph_marked_queue_int_iselement(const igraph_marked_queue_int_t *q, + igraph_integer_t elem) { return (VECTOR(q->set)[elem] == q->mark); } -int igraph_marked_queue_push(igraph_marked_queue_t *q, long int elem) { +igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem) { if (VECTOR(q->set)[elem] != q->mark) { - IGRAPH_CHECK(igraph_dqueue_push(&q->Q, elem)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, elem)); VECTOR(q->set)[elem] = q->mark; q->size += 1; } - return 0; + return IGRAPH_SUCCESS; } -int igraph_marked_queue_start_batch(igraph_marked_queue_t *q) { - IGRAPH_CHECK(igraph_dqueue_push(&q->Q, BATCH_MARKER)); - return 0; +igraph_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q->Q, BATCH_MARKER)); + return IGRAPH_SUCCESS; } -void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q) { - long int size = igraph_dqueue_size(&q->Q); - long int elem; +void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q) { + igraph_integer_t size = igraph_dqueue_int_size(&q->Q); + igraph_integer_t elem; while (size > 0 && - (elem = (long int) igraph_dqueue_pop_back(&q->Q)) != BATCH_MARKER) { + (elem = igraph_dqueue_int_pop_back(&q->Q)) != BATCH_MARKER) { VECTOR(q->set)[elem] = 0; size--; q->size--; @@ -90,26 +90,26 @@ void igraph_marked_queue_pop_back_batch(igraph_marked_queue_t *q) { } #ifndef USING_R -int igraph_marked_queue_print(const igraph_marked_queue_t *q) { - IGRAPH_CHECK(igraph_dqueue_print(&q->Q)); - return 0; +igraph_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q) { + IGRAPH_CHECK(igraph_dqueue_int_print(&q->Q)); + return IGRAPH_SUCCESS; } #endif -int igraph_marked_queue_fprint(const igraph_marked_queue_t *q, FILE *file) { - IGRAPH_CHECK(igraph_dqueue_fprint(&q->Q, file)); - return 0; +igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file) { + IGRAPH_CHECK(igraph_dqueue_int_fprint(&q->Q, file)); + return IGRAPH_SUCCESS; } -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)); +igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, + igraph_vector_int_t *vec) { + igraph_integer_t i, p, n = igraph_dqueue_int_size(&q->Q); + IGRAPH_CHECK(igraph_vector_int_resize(vec, q->size)); for (i = 0, p = 0; i < n; i++) { - igraph_real_t e = igraph_dqueue_e(&q->Q, i); + igraph_integer_t e = igraph_dqueue_int_get(&q->Q, i); if (e != BATCH_MARKER) { VECTOR(*vec)[p++] = e; } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/marked_queue.h b/src/vendor/cigraph/src/core/marked_queue.h index be36e284327..564fe9f4e52 100644 --- a/src/vendor/cigraph/src/core/marked_queue.h +++ b/src/vendor/cigraph/src/core/marked_queue.h @@ -24,11 +24,14 @@ #ifndef IGRAPH_MARKED_QUEUE_H #define IGRAPH_MARKED_QUEUE_H +#include "igraph_decls.h" #include "igraph_vector.h" #include "igraph_dqueue.h" #include +__BEGIN_DECLS + /* 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. @@ -39,32 +42,34 @@ 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; +typedef struct igraph_marked_queue_int_t { + igraph_dqueue_int_t Q; + igraph_vector_int_t set; + igraph_integer_t mark; + igraph_integer_t size; +} igraph_marked_queue_int_t; + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_init(igraph_marked_queue_int_t *q, + igraph_integer_t size); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_destroy(igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_reset(igraph_marked_queue_int_t *q); -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_int_empty(const igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_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 igraph_error_t igraph_marked_queue_int_print(const igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_fprint(const igraph_marked_queue_int_t *q, FILE *file); -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_int_iselement(const igraph_marked_queue_int_t *q, + igraph_integer_t elem); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_push(igraph_marked_queue_int_t *q, igraph_integer_t elem); -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 igraph_error_t igraph_marked_queue_int_start_batch(igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT void igraph_marked_queue_int_pop_back_batch(igraph_marked_queue_int_t *q); -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 igraph_error_t igraph_marked_queue_int_as_vector(const igraph_marked_queue_int_t *q, + igraph_vector_int_t *vec); -IGRAPH_PRIVATE_EXPORT int igraph_marked_queue_as_vector(const igraph_marked_queue_t *q, - igraph_vector_t *vec); +__END_DECLS #endif diff --git a/src/vendor/cigraph/src/core/math.h b/src/vendor/cigraph/src/core/math.h index c50bd132ef7..891abc135b2 100644 --- a/src/vendor/cigraph/src/core/math.h +++ b/src/vendor/cigraph/src/core/math.h @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2008-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + 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 @@ -15,74 +13,71 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - + along with this program. If not, see . */ -#ifndef IGRAPH_MATH_H -#define IGRAPH_MATH_H - -#include "igraph_decls.h" +#ifndef IGRAPH_CORE_MATH_H +#define IGRAPH_CORE_MATH_H -#include "config.h" +/* Use math constants with MSVC */ +#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES +#endif #include -#include -__BEGIN_DECLS +/* Math constants are not part of standard C */ -/** - * \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 +#ifndef M_E +#define M_E 2.71828182845904523536028747135266250 +#endif -/* - * 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) +#ifndef M_LOG2E +#define M_LOG2E 1.44269504088896340735992468100189214 #endif -#ifndef HAVE_LOG1P - #define log1p(a) igraph_log1p(a) + +#ifndef M_LOG10E +#define M_LOG10E 0.434294481903251827651128918916605082 #endif -#ifndef HAVE_FMIN - #define fmin(a,b) igraph_fmin((a),(b)) + +#ifndef M_LN2 +#define M_LN2 0.693147180559945309417232121458176568 #endif -#ifndef HAVE_ROUND - #define round igraph_i_round + +#ifndef M_LN10 +#define M_LN10 2.30258509299404568401799145468436421 #endif #ifndef M_PI - #define M_PI 3.14159265358979323846 +#define M_PI 3.14159265358979323846264338327950288 #endif + #ifndef M_PI_2 - #define M_PI_2 1.57079632679489661923 +#define M_PI_2 1.57079632679489661923132169163975144 #endif -#ifndef M_LN2 - #define M_LN2 0.69314718055994530942 + +#ifndef M_PI_4 +#define M_PI_4 0.785398163397448309615660845819875721 #endif -#ifndef M_SQRT2 - #define M_SQRT2 1.4142135623730950488016887 + +#ifndef M_1_PI +#define M_1_PI 0.318309886183790671537767526745028724 #endif -#ifndef M_LN_SQRT_2PI - #define M_LN_SQRT_2PI 0.918938533204672741780329736406 /* log(sqrt(2*pi)) - == log(2*pi)/2 */ + +#ifndef M_2_PI +#define M_2_PI 0.636619772367581343075535053490057448 #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); +#ifndef M_2_SQRTPI +#define M_2_SQRTPI 1.12837916709551257389615890312154517 +#endif -__END_DECLS +#ifndef M_SQRT2 +#define M_SQRT2 1.41421356237309504880168872420969808 +#endif +#ifndef M_SQRT1_2 +#define M_SQRT1_2 0.707106781186547524400844362104849039 #endif + +#endif /* IGRAPH_CORE_MATH_H */ diff --git a/src/vendor/cigraph/src/core/matrix.c b/src/vendor/cigraph/src/core/matrix.c index 80bd31e471b..62a1c3c2fde 100644 --- a/src/vendor/cigraph/src/core/matrix.c +++ b/src/vendor/cigraph/src/core/matrix.c @@ -21,8 +21,8 @@ */ -#include "igraph_types.h" #include "igraph_matrix.h" +#include "igraph_types.h" #define BASE_IGRAPH_REAL #include "igraph_pmt.h" @@ -36,12 +36,6 @@ #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" @@ -60,99 +54,251 @@ #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; -} +/** + * \ingroup matrix + * \function igraph_matrix_complex_real + * \brief Gives the real part of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param real Pointer to an initialized matrix. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ -int igraph_matrix_complex_real(const igraph_matrix_complex_t *v, +igraph_error_t igraph_matrix_complex_real(const igraph_matrix_complex_t *m, igraph_matrix_t *real) { - long int nrow = igraph_matrix_complex_nrow(v); - long int ncol = igraph_matrix_complex_ncol(v); + igraph_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); IGRAPH_CHECK(igraph_matrix_resize(real, nrow, ncol)); - IGRAPH_CHECK(igraph_vector_complex_real(&v->data, &real->data)); - return 0; + IGRAPH_CHECK(igraph_vector_complex_real(&m->data, &real->data)); + return IGRAPH_SUCCESS; } -int igraph_matrix_complex_imag(const igraph_matrix_complex_t *v, +/** + * \ingroup matrix + * \function igraph_matrix_complex_imag + * \brief Gives the imaginary part of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param imag Pointer to an initialized matrix. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_imag(const igraph_matrix_complex_t *m, igraph_matrix_t *imag) { - long int nrow = igraph_matrix_complex_nrow(v); - long int ncol = igraph_matrix_complex_ncol(v); + igraph_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); IGRAPH_CHECK(igraph_matrix_resize(imag, nrow, ncol)); - IGRAPH_CHECK(igraph_vector_complex_imag(&v->data, &imag->data)); - return 0; + IGRAPH_CHECK(igraph_vector_complex_imag(&m->data, &imag->data)); + return IGRAPH_SUCCESS; } -int igraph_matrix_complex_realimag(const igraph_matrix_complex_t *v, +/** + * \ingroup matrix + * \function igraph_matrix_complex_realimag + * \brief Gives the real and imaginary parts of a complex matrix. + * + * \param m Pointer to a complex matrix. + * \param real Pointer to an initialized matrix. The real part will be stored here. + * \param imag Pointer to an initialized matrix. The imaginary part will be stored here. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_realimag(const igraph_matrix_complex_t *m, 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_integer_t nrow = igraph_matrix_complex_nrow(m); + igraph_integer_t ncol = igraph_matrix_complex_ncol(m); 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, + IGRAPH_CHECK(igraph_vector_complex_realimag(&m->data, &real->data, &imag->data)); - return 0; + return IGRAPH_SUCCESS; } -int igraph_matrix_complex_create(igraph_matrix_complex_t *v, +/** + * \ingroup matrix + * \function igraph_matrix_complex_create + * \brief Creates a complex matrix from a real and imaginary part. + * + * \param m Pointer to an uninitialized complex matrix. + * \param real Pointer to the real part of the complex matrix. + * \param imag Pointer to the imaginary part of the complex matrix. + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_create(igraph_matrix_complex_t *m, const igraph_matrix_t *real, const igraph_matrix_t *imag) { - IGRAPH_CHECK(igraph_vector_complex_create(&v->data, &real->data, - &imag->data)); - return 0; + igraph_integer_t nrowr = igraph_matrix_nrow(real); + igraph_integer_t ncolr = igraph_matrix_ncol(real); + igraph_integer_t nrowi = igraph_matrix_nrow(imag); + igraph_integer_t ncoli = igraph_matrix_ncol(imag); + + if (nrowr != nrowi || ncolr != ncoli) { + IGRAPH_ERRORF("Dimensions of real (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " + "imaginary (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", + IGRAPH_EINVAL, nrowr, ncolr, nrowi, ncoli); + } + + IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); + + for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { + VECTOR(m->data)[i] = igraph_complex(VECTOR(real->data)[i], VECTOR(imag->data)[i]); + } + + return IGRAPH_SUCCESS; } -int igraph_matrix_complex_create_polar(igraph_matrix_complex_t *v, +/** + * \ingroup matrix + * \function igraph_matrix_complex_create_polar + * \brief Creates a complex matrix from a magnitude and an angle. + * + * \param m Pointer to an uninitialized complex matrix. + * \param r Pointer to a real matrix containing magnitudes. + * \param theta Pointer to a real matrix containing arguments (phase angles). + * \return Error code. + * + * Time complexity: O(n), + * n is the + * number of elements in the matrix. + */ + +igraph_error_t igraph_matrix_complex_create_polar(igraph_matrix_complex_t *m, 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_integer_t nrowr = igraph_matrix_nrow(r); + igraph_integer_t ncolr = igraph_matrix_ncol(r); + igraph_integer_t nrowt = igraph_matrix_nrow(theta); + igraph_integer_t ncolt = igraph_matrix_ncol(theta); + + if (nrowr != nrowt || ncolr != ncolt) { + IGRAPH_ERRORF("Dimensions of magnitude (%" IGRAPH_PRId " by %" IGRAPH_PRId ") and " + "angle (%" IGRAPH_PRId " by %" IGRAPH_PRId ") matrices must match.", + IGRAPH_EINVAL, nrowr, ncolr, nrowt, ncolt); + } + + IGRAPH_CHECK(igraph_matrix_complex_init(m, nrowr, ncolr)); + + for (igraph_integer_t i = 0; i < nrowr * ncolr; i++) { + VECTOR(m->data)[i] = igraph_complex_polar(VECTOR(r->data)[i], VECTOR(theta->data)[i]); + } + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_matrix_complex_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two complex matrices are equal within a relative tolerance. + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. + * \return True if the two matrices are almost equal, false if there is at least + * one differing element or if the matrices are not of the same dimensions. + */ +igraph_bool_t igraph_matrix_complex_all_almost_e(igraph_matrix_complex_t *lhs, + igraph_matrix_complex_t *rhs, + igraph_real_t eps) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_complex_all_almost_e(&lhs->data, &rhs->data, eps); } +/** + * Deprecated in favour of \ref igraph_matrix_all_almost_e() which uses + * relative tolerances. Will be removed in 0.11. + * + * Checks if two matrices are equal within an absolute tolerance. + */ 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); + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_e_tol(&lhs->data, &rhs->data, tol); } -int igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { + +/** + * \function igraph_matrix_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two matrices are equal within a relative tolerance. + * + * \param lhs The first matrix. + * \param rhs The second matrix. + * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. + * \return True if the two matrices are almost equal, false if there is at least + * one differing element or if the matrices are not of the same dimensions. + */ +igraph_bool_t igraph_matrix_all_almost_e(const igraph_matrix_t *lhs, + const igraph_matrix_t *rhs, + igraph_real_t eps) { + return lhs->ncol == rhs->ncol && lhs->nrow == rhs->nrow && + igraph_vector_all_almost_e(&lhs->data, &rhs->data, eps); +} + + +/** + * \function igraph_matrix_zapsmall + * \brief Replaces small elements of a matrix by exact zeros. + * + * Matrix elements which are smaller in magnitude than the given absolute + * tolerance will be replaced by exact zeros. The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param m The matrix to process, it will be changed in-place. + * \param tol Tolerance value. Numbers smaller than this in magnitude will + * be replaced by zeros. Pass in zero to use the default tolerance. + * Must not be negative. + * \return Error code. + * + * \sa \ref igraph_matrix_all_almost_e() and \ref igraph_almost_equals() to + * perform comparisons with relative tolerances. + */ +igraph_error_t igraph_matrix_zapsmall(igraph_matrix_t *m, igraph_real_t tol) { return igraph_vector_zapsmall(&m->data, tol); } + +/** + * \function igraph_matrix_complex_zapsmall + * \brief Replaces small elements of a complex matrix by exact zeros. + * + * Similarly to \ref igraph_matrix_zapsmall(), small elements will be replaced + * by zeros. The operation is performed separately on the real and imaginary + * parts of the numbers. This way, complex numbers with a large real part and + * tiny imaginary part will effectively be transformed to real numbers. + * The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param m The matrix to process, it will be changed in-place. + * \param tol Tolerance value. Real and imaginary parts smaller than this in + * magnitude will be replaced by zeros. Pass in zero to use the default + * tolerance. Must not be negative. + * \return Error code. + * + * \sa \ref igraph_matrix_complex_all_almost_e() and + * \ref igraph_complex_almost_equals() to perform comparisons with relative + * tolerances. + */ +igraph_error_t igraph_matrix_complex_zapsmall(igraph_matrix_complex_t *m, igraph_real_t tol) { + return igraph_vector_complex_zapsmall(&m->data, tol); +} diff --git a/src/vendor/cigraph/src/core/matrix.pmt b/src/vendor/cigraph/src/core/matrix.pmt index 27071ace39f..693ccce0ca2 100644 --- a/src/vendor/cigraph/src/core/matrix.pmt +++ b/src/vendor/cigraph/src/core/matrix.pmt @@ -21,10 +21,10 @@ */ -#include "igraph_memory.h" -#include "igraph_random.h" #include "igraph_error.h" +#include "math/safe_intop.h" + #include /* memcpy & co. */ #include @@ -58,26 +58,116 @@ * \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. + * 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)); +igraph_error_t FUNCTION(igraph_matrix, init)( + TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { + igraph_integer_t size; + IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); + IGRAPH_SAFE_MULT(nrow, ncol, &size); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&m->data, size)); 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) { +/** + * \ingroup matrix + * \function igraph_matrix_view + * \brief Creates a matrix view into an existing array. + * + * + * This function lets you treat an existing C array as a matrix. The elements + * of the matrix are assumed to be stored in column-major order in the array, + * i.e. the elements of the first column are stored first, followed by the + * second column and so on. + * + * + * Since this function creates a view into an existing array, you must \em not + * destroy the \c igraph_matrix_t object when you are done with it. Similarly, + * you must \em not call any function on it that may attempt to modify the size + * of the matrix. Modifying an element in the matrix will modify the underlying + * array as the two share the same memory block. + * + * \param m Pointer to a not yet initialized matrix object where the view will + * be created. + * \param data The array that the matrix provides a view into. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \return Pointer to the matrix object, the same as the \p m parameter, for + * convenience. + * + * Time complexity: O(1). + */ + +const TYPE(igraph_matrix)* FUNCTION(igraph_matrix, view)( + const TYPE(igraph_matrix) *m, const BASE *data, + igraph_integer_t nrow, igraph_integer_t ncol) { + /* temporarily cast away the constness */ + TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; + + /* No overflow checking, as this function does not return igraph_error_t. + * It is the caller's resposibility to ensure that the size of 'data' + * matches nrow*ncol, which also implies that nrow*ncol does not overflow. */ + + FUNCTION(igraph_vector, view)(&m2->data, data, ncol * nrow); + m2->nrow = nrow; + m2->ncol = ncol; + + return m; +} + +/** + * \ingroup matrix + * \function igraph_matrix_view_from_vector + * \brief Creates a matrix view that treats an existing vector as a matrix. + * + * + * This function lets you treat an existing igraph vector as a matrix. The + * elements of the matrix are assumed to be stored in column-major order in the + * vector, i.e. the elements of the first column are stored first, followed by + * the second column and so on. + * + * + * Since this function creates a view into an existing vector, you must \em not + * destroy the \c igraph_matrix_t object when you are done with it. Similarly, + * you must \em not call any function on it that may attempt to modify the size + * of the vector. Modifying an element in the matrix will modify the underlying + * vector as the two share the same memory block. + * + * + * Additionally, you must \em not attempt to grow the underlying vector by any + * vector operation as that may result in a re-allocation of the backing memory + * block of the vector, and the matrix view will not be informed about the + * re-allocation so it will point to an invalid memory area afterwards. + * + * \param m Pointer to a not yet initialized matrix object where the view will + * be created. + * \param v The vector that the matrix will provide a view into. + * \param nrow The number of rows in the matrix. The number of columns will be + * derived implicitly from the size of the vector. If the number of + * items in the vector is not divisible by the number of rows, the + * last few elements of the vector will not be covered by the view. + * \return Error code. + * + * Time complexity: O(1). + */ + +IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( + const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, + igraph_integer_t nrow +) { + /* temporarily cast away the constness */ TYPE(igraph_matrix) *m2 = (TYPE(igraph_matrix)*)m; - FUNCTION(igraph_vector, view)(&m2->data, data, nrow * ncol); + + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + igraph_integer_t ncol = nrow > 0 ? size / nrow : 0; + + FUNCTION(igraph_vector, view)(&m2->data, VECTOR(*v), ncol * nrow); m2->nrow = nrow; m2->ncol = ncol; + return m; } @@ -117,7 +207,7 @@ void FUNCTION(igraph_matrix, destroy)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { +igraph_integer_t FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { return FUNCTION(igraph_vector, capacity)(&m->data); } @@ -148,11 +238,14 @@ long int FUNCTION(igraph_matrix, capacity)(const TYPE(igraph_matrix) *m) { * 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)); +igraph_error_t FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, igraph_integer_t nrow, igraph_integer_t ncol) { + igraph_integer_t size; + IGRAPH_ASSERT(nrow >= 0 && ncol >= 0); + IGRAPH_SAFE_MULT(nrow, ncol, &size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, size)); m->nrow = nrow; m->ncol = ncol; - return 0; + return IGRAPH_SUCCESS; } /** @@ -160,36 +253,18 @@ int FUNCTION(igraph_matrix, resize)(TYPE(igraph_matrix) *m, long int nrow, long * \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. + * This function attempts to deallocate the unused reserved storage + * of a matrix. * - * - * 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. + * Time complexity: operating system dependent, O(n) at worst. */ -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; +void FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { + FUNCTION(igraph_vector, resize_min)(&m->data); } @@ -204,7 +279,7 @@ int FUNCTION(igraph_matrix, resize_min)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { +igraph_integer_t FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { return (m->nrow) * (m->ncol); } @@ -219,7 +294,7 @@ long int FUNCTION(igraph_matrix, size)(const TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { +igraph_integer_t FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { return m->nrow; } @@ -234,7 +309,7 @@ long int FUNCTION(igraph_matrix, nrow)(const TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -long int FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) { +igraph_integer_t FUNCTION(igraph_matrix, ncol)(const TYPE(igraph_matrix) *m) { return m->ncol; } @@ -290,9 +365,11 @@ void FUNCTION(igraph_matrix, null)(TYPE(igraph_matrix) *m) { * 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; +igraph_error_t FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, igraph_integer_t n) { + igraph_integer_t new_ncol; + IGRAPH_SAFE_ADD(m->ncol, n, &new_ncol); + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow, new_ncol)); + return IGRAPH_SUCCESS; } /** @@ -308,15 +385,17 @@ int FUNCTION(igraph_matrix, add_cols)(TYPE(igraph_matrix) *m, long int n) { * 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); +igraph_error_t FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, igraph_integer_t n) { + igraph_integer_t new_nrow, new_size; + IGRAPH_SAFE_ADD(m->nrow, n, &new_nrow); + IGRAPH_SAFE_MULT(m->ncol, new_nrow, &new_size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, new_size)); + for (igraph_integer_t i = m->ncol - 1; i >= 0; i--) { + FUNCTION(igraph_vector, move_interval)(&m->data, (m->nrow)*i, (m->nrow) * (i + 1), + new_nrow * i); } - m->nrow += n; - return 0; + m->nrow = new_nrow; + return IGRAPH_SUCCESS; } /** @@ -332,10 +411,10 @@ int FUNCTION(igraph_matrix, add_rows)(TYPE(igraph_matrix) *m, long int n) { * resized matrix. */ -int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col) { +igraph_error_t FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, igraph_integer_t col) { FUNCTION(igraph_vector, remove_section)(&m->data, (m->nrow)*col, (m->nrow) * (col + 1)); m->ncol--; - return 0; + return IGRAPH_SUCCESS; } /** @@ -347,8 +426,9 @@ int FUNCTION(igraph_matrix, remove_col)(TYPE(igraph_matrix) *m, long int col) { * matrix. */ -int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, long int *index, long int nremove) { - long int i, j; +igraph_error_t FUNCTION(igraph_matrix, permdelete_rows)( + TYPE(igraph_matrix) *m, igraph_integer_t *index, igraph_integer_t nremove) { + igraph_integer_t i, j; for (j = 0; j < m->nrow; j++) { if (index[j] != 0) { for (i = 0; i < m->ncol; i++) { @@ -362,37 +442,91 @@ int FUNCTION(igraph_matrix, permdelete_rows)(TYPE(igraph_matrix) *m, long int *i (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; + return IGRAPH_SUCCESS; } /** - * \ingroup matrix - * \function igraph_matrix_delete_rows_neg - * \brief Removes columns from a matrix (for internal use). + * Copies matrix data stored contiguously while transposing. Applications include implementing + * matrix transposition as well as changing between row-major and column-major storage formats. * - * Time complexity: linear with the number of elements of the original - * matrix. + * \param dst Data will be copied into this vector. It must have length m-by-n. It will not be resized. + * \param src Vector containing the data to be copied. It is assumed to have length m-by-n. + * Must not be the same as \p dst . + * \param m The size of contiguous blocks. This is the number of columns when using column-major + * storage format, or the number of rows when using row-major format. + * \param n The number of blocks. The is the number of rows when using column-major format, + * or the number of column when using row-major format. */ +static void FUNCTION(igraph_i, transpose_copy)( + TYPE(igraph_vector) *dst, const TYPE(igraph_vector) *src, + size_t m, size_t n) { + + IGRAPH_ASSERT(dst != src); -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); + /* Block size of 4 was found to yield the best performance when benchmarking with: + * - Intel Core i7-7920HQ on macOS. + * - AMD Ryzen Threadripper 3990X on Linux. + */ + const size_t blocksize = 4; + for (size_t i=0; i < m; i += blocksize) { + for (size_t j=0; j < n; j++) { + for (size_t k=0; k < blocksize && i+k < m; k++) { + VECTOR(*dst)[j + (i+k)*n] = VECTOR(*src)[i+k + j*m]; } } - idx = 0; } - IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(m, m->nrow - nremove, m->ncol)); +} + +/** + * \ingroup matrix + * \function igraph_matrix_init_array + * \brief Initializes a matrix from an ordinary C array (constructor). + * + * The array is assumed to store the matrix data contiguously, either in + * a column-major or row-major format. In other words, \p data may + * store concatenated matrix columns or concatenated matrix rows. + * Constructing a matrix from column-major data is faster, as this is + * igraph's native storage format. + * + * \param v Pointer to an uninitialized matrix object. + * \param data A regular C array, storing the elements of the matrix in + * column-major order, i.e. the elements of the first column are stored + * first, followed by the second column and so on. + * \param nrow The number of rows in the matrix. + * \param ncol The number of columns in the matrix. + * \param storage \c IGRAPH_ROW_MAJOR if the array is in row-major format, + \c IGRAPH_COLUMN_MAJOR if the array is in column-major format. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system specific, usually + * O(\p nrow \p ncol). + */ + +igraph_error_t FUNCTION(igraph_matrix, init_array)( + TYPE(igraph_matrix) *m, const BASE *data, + igraph_integer_t nrow, igraph_integer_t ncol, + igraph_matrix_storage_t storage) { + igraph_integer_t length; + TYPE(igraph_vector) v; + + IGRAPH_SAFE_MULT(nrow, ncol, &length); + IGRAPH_CHECK(FUNCTION(igraph_matrix, init)(m, nrow, ncol)); + FUNCTION(igraph_vector, view)(&v, data, length); + if (storage == IGRAPH_COLUMN_MAJOR) { + IGRAPH_CHECK(FUNCTION(igraph_vector, update)(&m->data, &v)); + } else if (storage == IGRAPH_ROW_MAJOR) { + FUNCTION(igraph_i, transpose_copy)(&m->data, &v, ncol, nrow); + } else { + IGRAPH_ERROR("Invalid storage type argument", IGRAPH_EINVAL); + } - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup matrix - * \function igraph_matrix_copy + * \function igraph_matrix_init_copy * \brief Copies a matrix. * * @@ -406,13 +540,25 @@ int FUNCTION(igraph_matrix, delete_rows_neg)(TYPE(igraph_matrix) *m, * 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)); +igraph_error_t FUNCTION(igraph_matrix, init_copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { + IGRAPH_CHECK(FUNCTION(igraph_vector, init_copy)(&to->data, &from->data)); to->nrow = from->nrow; to->ncol = from->ncol; return IGRAPH_SUCCESS; } +/** + * \ingroup matrix + * \function igraph_matrix_copy + * \brief Copies a matrix (deprecated alias). + * + * \deprecated-by igraph_matrix_init_copy 0.10 + */ + +igraph_error_t FUNCTION(igraph_matrix, copy)(TYPE(igraph_matrix) *to, const TYPE(igraph_matrix) *from) { + return FUNCTION(igraph_matrix, init_copy)(to, from); +} + #ifndef NOTORDERED /** @@ -469,20 +615,20 @@ void FUNCTION(igraph_matrix, scale)(TYPE(igraph_matrix) *m, BASE by) { * columns of the result matrix. */ -int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, +igraph_error_t 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); + const igraph_vector_int_t *rows) { + igraph_integer_t norows = igraph_vector_int_size(rows); + igraph_integer_t 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); + MATRIX(*res, i, j) = MATRIX(*m, VECTOR(*rows)[i], j); } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -505,23 +651,22 @@ int FUNCTION(igraph_matrix, select_rows)(const TYPE(igraph_matrix) *m, * columns of the result matrix. */ -int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, +igraph_error_t 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; + const igraph_vector_int_t *rows, + const igraph_vector_int_t *cols) { + igraph_integer_t nrows = igraph_vector_int_size(rows); + igraph_integer_t ncols = igraph_vector_int_size(cols); + igraph_integer_t 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]); + MATRIX(*res, i, j) = MATRIX(*m, VECTOR(*rows)[i], VECTOR(*cols)[j]); } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -538,17 +683,17 @@ int FUNCTION(igraph_matrix, select_rows_cols)(const TYPE(igraph_matrix) *m, * Time complexity: O(n), the number of rows in the matrix. */ -int FUNCTION(igraph_matrix, get_col)(const TYPE(igraph_matrix) *m, +igraph_error_t 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); + igraph_integer_t index) { + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /** @@ -570,6 +715,9 @@ BASE FUNCTION(igraph_matrix, sum)(const TYPE(igraph_matrix) *m) { * \function igraph_matrix_all_e * \brief Are all elements equal? * + * Checks element-wise equality of two matrices. For matrices containing floating + * point values, consider using \ref igraph_matrix_all_almost_e(). + * * \param lhs The first matrix. * \param rhs The second matrix. * \return Positive integer (=true) if the elements in the \p lhs are all @@ -694,10 +842,10 @@ FUNCTION(igraph_matrix, all_ge)(const TYPE(igraph_matrix) *lhs, 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); + igraph_integer_t col1 = FUNCTION(igraph_matrix, ncol)(m1); + igraph_integer_t col2 = FUNCTION(igraph_matrix, ncol)(m2); + igraph_integer_t row1 = FUNCTION(igraph_matrix, nrow)(m1); + igraph_integer_t row2 = FUNCTION(igraph_matrix, nrow)(m2); if (col1 != col2 || row1 != row2) { IGRAPH_WARNING("Comparing non-conformant matrices"); } @@ -706,42 +854,67 @@ igraph_real_t FUNCTION(igraph_matrix, maxdifference)(const TYPE(igraph_matrix) * #endif +#define SWAP(TYPE,a,b) do { TYPE igraph_i_tmp = (a); (a) = (b); (b) = igraph_i_tmp; } while (0) + /** * \function igraph_matrix_transpose - * \brief Transpose a matrix. + * \brief Transpose of a matrix. + * + * Calculates the transpose of a matrix. When the matrix is non-square, + * this function reallocates the storage used for the 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 ]; +igraph_error_t FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { + if (m->nrow > 1 && m->ncol > 1) { + if (m->nrow == m->ncol) { + /* In-place transpose for square matrices. */ + + /* Block size of 4 was found to yield the best performance during benchmarking, + * see igraph_i_transpose_copy() */ + const size_t blocksize = 4; + const size_t n = m->nrow; + size_t k=0; + for (k=0; k + blocksize - 1 < n; k += blocksize) { + for (size_t i = k; i < k + blocksize; ++i) { + for (size_t j = i + 1; j < k + blocksize; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + for (size_t i = k + blocksize; i < n; ++i) { + for (size_t j = k; j < k + blocksize; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + } + for (size_t i = k; i < n; ++i) { + for (size_t j = i + 1; j < n; ++j) { + SWAP(BASE, VECTOR(m->data)[j + i*n], VECTOR(m->data)[i + j*n]); + } + } + } else { + /* Allocate new storage for non-square matrices. */ + TYPE(igraph_vector) newdata; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&newdata, m->nrow * m->ncol)); + FUNCTION(igraph_i, transpose_copy)(&newdata, &m->data, m->nrow, m->ncol); + FUNCTION(igraph_vector, destroy)(&m->data); + m->data = newdata; } - 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; + SWAP(igraph_integer_t, m->nrow, m->ncol); + + return IGRAPH_SUCCESS; } +#undef SWAP + /** - * \function igraph_matrix_e + * \function igraph_matrix_get * Extract an element from a matrix. * * Use this if you need a function for some reason and cannot use the @@ -754,13 +927,13 @@ int FUNCTION(igraph_matrix, transpose)(TYPE(igraph_matrix) *m) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, - long int row, long int col) { +BASE FUNCTION(igraph_matrix, get)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { return MATRIX(*m, row, col); } /** - * \function igraph_matrix_e_ptr + * \function igraph_matrix_get_ptr * Pointer to an element of a matrix. * * The function returns a pointer to an element. No range checking is @@ -773,11 +946,35 @@ BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, * Time complexity: O(1). */ -BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, - long int row, long int col) { +BASE* FUNCTION(igraph_matrix, get_ptr)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { return &MATRIX(*m, row, col); } +/** + * \function igraph_matrix_e + * Extract an element from a matrix (deprecated alias). + * + * \deprecated-by igraph_matrix_get 0.10.0 + */ + +BASE FUNCTION(igraph_matrix, e)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return FUNCTION(igraph_matrix, get)(m, row, col); +} + +/** + * \function igraph_matrix_e_ptr + * Pointer to an element of a matrix. + * + * \deprecated-by igraph_matrix_get_ptr 0.10.0 + */ + +BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, + igraph_integer_t row, igraph_integer_t col) { + return FUNCTION(igraph_matrix, get_ptr)(m, row, col); +} + /** * \function igraph_matrix_set * Set an element. @@ -791,8 +988,9 @@ BASE* FUNCTION(igraph_matrix, e_ptr)(const TYPE(igraph_matrix) *m, * Time complexity: O(1). */ -void FUNCTION(igraph_matrix, set)(TYPE(igraph_matrix)* m, long int row, long int col, - BASE value) { +void FUNCTION(igraph_matrix, set)( + TYPE(igraph_matrix)* m, igraph_integer_t row, igraph_integer_t col, + BASE value) { MATRIX(*m, row, col) = value; } @@ -824,12 +1022,12 @@ void FUNCTION(igraph_matrix, fill)(TYPE(igraph_matrix) *m, BASE e) { * Time complexity: O(mn), the number of elements. */ -int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -847,17 +1045,19 @@ int FUNCTION(igraph_matrix, update)(TYPE(igraph_matrix) *to, * matrix. */ -int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, +igraph_error_t 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; + igraph_integer_t tocols = to->ncol, fromcols = from->ncol; + igraph_integer_t torows = to->nrow, fromrows = from->nrow; + igraph_integer_t 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))); + igraph_integer_t new_size; /* new_size = tocols * (fromrows + torows) */ + IGRAPH_SAFE_ADD(fromrows, torows, &new_size); + IGRAPH_SAFE_MULT(tocols, new_size, &new_size); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&to->data, new_size)); to->nrow += fromrows; offset = (tocols - 1) * fromrows; @@ -872,11 +1072,11 @@ int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, offset = torows; offset2 = 0; for (c = 0; c < tocols; c++) { memcpy(VECTOR(to->data) + offset, VECTOR(from->data) + offset2, - sizeof(BASE) * (size_t) fromrows); + sizeof(BASE) * fromrows); offset += fromrows + torows; offset2 += fromrows; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -892,22 +1092,27 @@ int FUNCTION(igraph_matrix, rbind)(TYPE(igraph_matrix) *to, * Time complexity: O(mn), the number of elements on the new matrix. */ -int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, +igraph_error_t 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; + igraph_integer_t tocols = to->ncol, fromcols = from->ncol; + igraph_integer_t 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)); + + igraph_integer_t new_tocols; + IGRAPH_SAFE_ADD(tocols, fromcols, &new_tocols); + IGRAPH_CHECK(FUNCTION(igraph_matrix, resize)(to, torows, new_tocols)); + FUNCTION(igraph_vector, copy_to)(&from->data, VECTOR(to->data) + tocols * torows); - return 0; + + return IGRAPH_SUCCESS; } /** * \function igraph_matrix_swap - * Swap two matrices. + * \brief Swap two matrices. * * The contents of the two matrices will be swapped. * \param m1 The first matrix. @@ -917,8 +1122,20 @@ int FUNCTION(igraph_matrix, cbind)(TYPE(igraph_matrix) *to, * 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); +igraph_error_t FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) *m2) { + igraph_integer_t tmp; + + tmp = m1->nrow; + m1->nrow = m2->nrow; + m2->nrow = tmp; + + tmp = m1->ncol; + m1->ncol = m2->ncol; + m2->ncol = tmp; + + IGRAPH_CHECK(FUNCTION(igraph_vector, swap)(&m1->data, &m2->data)); + + return IGRAPH_SUCCESS; } /** @@ -935,10 +1152,10 @@ int FUNCTION(igraph_matrix, swap)(TYPE(igraph_matrix) *m1, TYPE(igraph_matrix) * * 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; +igraph_error_t FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, + TYPE(igraph_vector) *res, igraph_integer_t index) { + igraph_integer_t rows = m->nrow, cols = m->ncol; + igraph_integer_t i, j; if (index >= rows) { IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); @@ -948,7 +1165,7 @@ int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, for (i = index, j = 0; j < cols; i += rows, j++) { VECTOR(*res)[j] = VECTOR(m->data)[i]; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -967,10 +1184,10 @@ int FUNCTION(igraph_matrix, get_row)(const TYPE(igraph_matrix) *m, * 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; +igraph_error_t FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, igraph_integer_t index) { + igraph_integer_t rows = m->nrow, cols = m->ncol; + igraph_integer_t i, j; if (index >= rows) { IGRAPH_ERROR("Index out of range for selecting matrix row", IGRAPH_EINVAL); @@ -981,7 +1198,7 @@ int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, for (i = index, j = 0; j < cols; i += rows, j++) { VECTOR(m->data)[i] = VECTOR(*v)[j]; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1000,10 +1217,10 @@ int FUNCTION(igraph_matrix, set_row)(TYPE(igraph_matrix) *m, * 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; +igraph_error_t FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, + const TYPE(igraph_vector) *v, igraph_integer_t index) { + igraph_integer_t rows = m->nrow, cols = m->ncol; + igraph_integer_t i, j; if (index >= cols) { IGRAPH_ERROR("Index out of range for setting matrix column", IGRAPH_EINVAL); @@ -1014,7 +1231,7 @@ int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, for (i = index * rows, j = 0; j < rows; i++, j++) { VECTOR(m->data)[i] = VECTOR(*v)[j]; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1030,16 +1247,16 @@ int FUNCTION(igraph_matrix, set_col)(TYPE(igraph_matrix) *m, * 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; +igraph_error_t FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, + igraph_integer_t i, igraph_integer_t j) { + igraph_integer_t ncol = m->ncol, nrow = m->nrow; + igraph_integer_t n = nrow * ncol; + igraph_integer_t index1, index2; if (i >= nrow || j >= nrow) { IGRAPH_ERROR("Cannot swap rows, index out of range", IGRAPH_EINVAL); } if (i == j) { - return 0; + return IGRAPH_SUCCESS; } for (index1 = i, index2 = j; index1 < n; index1 += nrow, index2 += nrow) { BASE tmp; @@ -1047,7 +1264,7 @@ int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, VECTOR(m->data)[index1] = VECTOR(m->data)[index2]; VECTOR(m->data)[index2] = tmp; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1063,22 +1280,22 @@ int FUNCTION(igraph_matrix, swap_rows)(TYPE(igraph_matrix) *m, * 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; +igraph_error_t FUNCTION(igraph_matrix, swap_cols)(TYPE(igraph_matrix) *m, + igraph_integer_t i, igraph_integer_t j) { + igraph_integer_t ncol = m->ncol, nrow = m->nrow; + igraph_integer_t k, index1, index2; if (i >= ncol || j >= ncol) { IGRAPH_ERROR("Cannot swap columns, index out of range", IGRAPH_EINVAL); } if (i == j) { - return 0; + return IGRAPH_SUCCESS; } 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; + return IGRAPH_SUCCESS; } /** @@ -1108,7 +1325,7 @@ void FUNCTION(igraph_matrix, add_constant)(TYPE(igraph_matrix) *m, BASE plus) { * Time complexity: O(mn), the number of elements. */ -int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, +igraph_error_t 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); @@ -1129,7 +1346,7 @@ int FUNCTION(igraph_matrix, add)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, +igraph_error_t 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); @@ -1150,7 +1367,7 @@ int FUNCTION(igraph_matrix, sub)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, +igraph_error_t 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); @@ -1171,7 +1388,7 @@ int FUNCTION(igraph_matrix, mul_elements)(TYPE(igraph_matrix) *m1, * Time complexity: O(mn), the number of elements. */ -int FUNCTION(igraph_matrix, div_elements)(TYPE(igraph_matrix) *m1, +igraph_error_t 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); @@ -1200,52 +1417,48 @@ igraph_real_t FUNCTION(igraph_matrix, min)(const TYPE(igraph_matrix) *m) { * \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 + * \param i Pointer to an igraph_integer_t. The row index of the * minimum is stored here. - * \param j Pointer to a long int. The column index of + * \param j Pointer to an igraph_integer_t. 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); +void FUNCTION(igraph_matrix, which_min)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { + igraph_integer_t 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 + * \param i Pointer to an igraph_integer_t. The row index of the * maximum is stored here. - * \param j Pointer to a long int. The column index of + * \param j Pointer to an igraph_integer_t. 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); +void FUNCTION(igraph_matrix, which_max)( + const TYPE(igraph_matrix) *m, igraph_integer_t *i, igraph_integer_t *j) { + igraph_integer_t vmax = FUNCTION(igraph_vector, which_max)(&m->data); *i = vmax % m->nrow; *j = vmax / m->nrow; - return 0; } /** @@ -1255,54 +1468,52 @@ int FUNCTION(igraph_matrix, which_max)(const TYPE(igraph_matrix) *m, * 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, +void FUNCTION(igraph_matrix, minmax)(const TYPE(igraph_matrix) *m, BASE *min, BASE *max) { - return FUNCTION(igraph_vector, minmax)(&m->data, min, max); + FUNCTION(igraph_vector, minmax)(&m->data, min, max); } /** * \function igraph_matrix_which_minmax - * \brief Indices of the minimum and maximum elements + * \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 + * \param imin Pointer to an igraph_integer_t, the row index of * the minimum is stored here. - * \param jmin Pointer to a long int, the column index of + * \param jmin Pointer to an igraph_integer_t, the column index of * the minimum is stored here. - * \param imax Pointer to a long int, the row index of + * \param imax Pointer to an igraph_integer_t, the row index of * the maximum is stored here. - * \param jmax Pointer to a long int, the column index of + * \param jmax Pointer to an igraph_integer_t, 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; +void FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, + igraph_integer_t *imin, igraph_integer_t *jmin, + igraph_integer_t *imax, igraph_integer_t *jmax) { + igraph_integer_t 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 @@ -1313,7 +1524,7 @@ int FUNCTION(igraph_matrix, which_minmax)(const TYPE(igraph_matrix) *m, * * Checks whether all elements are zero. * \param m The input matrix. - * \return Boolean, \c TRUE is \p m contains only zeros and \c FALSE + * \return Boolean, \c true is \p m contains only zeros and \c false * otherwise. * * Time complexity: O(mn), the number of elements. @@ -1330,8 +1541,8 @@ igraph_bool_t FUNCTION(igraph_matrix, isnull)(const TYPE(igraph_matrix) *m) { * 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. + * \return Boolean, \c true if the matrix contains zero elements, and + * \c false otherwise. * * Time complexity: O(1). */ @@ -1346,8 +1557,8 @@ igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) { * * 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. + * \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. @@ -1355,10 +1566,10 @@ igraph_bool_t FUNCTION(igraph_matrix, empty)(const TYPE(igraph_matrix) *m) { igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m) { - long int n = m->nrow; - long int r, c; + igraph_integer_t n = m->nrow; + igraph_integer_t r, c; if (m->ncol != n) { - return 0; + return false; } for (r = 1; r < n; r++) { for (c = 0; c < r; c++) { @@ -1366,16 +1577,16 @@ igraph_bool_t FUNCTION(igraph_matrix, is_symmetric)(const TYPE(igraph_matrix) *m BASE a2 = MATRIX(*m, c, r); #ifdef EQ if (!EQ(a1, a2)) { - return 0; + return false; } #else if (a1 != a2) { - return 0; + return false; } #endif } } - return 1; + return true; } /** @@ -1407,14 +1618,13 @@ BASE FUNCTION(igraph_matrix, prod)(const TYPE(igraph_matrix) *m) { * Time complexity: O(mn), the number of elements in the matrix. */ -int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, +igraph_error_t 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_integer_t nrow = m->nrow, ncol = m->ncol; + igraph_integer_t r, c; IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, nrow)); for (r = 0; r < nrow; r++) { - sum = ZERO; + BASE sum = ZERO; for (c = 0; c < ncol; c++) { #ifdef SUM SUM(sum, sum, MATRIX(*m, r, c)); @@ -1424,7 +1634,7 @@ int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, } VECTOR(*res)[r] = sum; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1440,14 +1650,13 @@ int FUNCTION(igraph_matrix, rowsum)(const TYPE(igraph_matrix) *m, * Time complexity: O(mn), the number of elements in the matrix. */ -int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, +igraph_error_t 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_integer_t nrow = m->nrow, ncol = m->ncol; + igraph_integer_t r, c; IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(res, ncol)); for (c = 0; c < ncol; c++) { - sum = ZERO; + BASE sum = ZERO; for (r = 0; r < nrow; r++) { #ifdef SUM SUM(sum, sum, MATRIX(*m, r, c)); @@ -1457,7 +1666,7 @@ int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, } VECTOR(*res)[c] = sum; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1467,7 +1676,7 @@ int FUNCTION(igraph_matrix, colsum)(const TYPE(igraph_matrix) *m, * 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 + * \return Boolean, \c true if the matrix contains \p e, \c false * otherwise. * * Time complexity: O(mn), the number of elements. @@ -1488,22 +1697,21 @@ igraph_bool_t FUNCTION(igraph_matrix, contains)(const TYPE(igraph_matrix) *m, * \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 + * \param pos Pointer to an igraph_integer_t. 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 + * \param row Pointer to an igraph_integer_t. If the element is * found, then this is set to its row index. - * \param col Pointer to a long int. If the element is + * \param col Pointer to an igraph_integer_t. If the element is * found, then this is set to its column index. - * \return Boolean, \c TRUE if the element is found, \c FALSE + * \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_integer_t from, BASE what, igraph_integer_t *pos, + igraph_integer_t *row, igraph_integer_t *col) { igraph_bool_t find = FUNCTION(igraph_vector, search)(&m->data, from, what, pos); if (find) { *row = *pos % m->nrow; @@ -1524,9 +1732,9 @@ igraph_bool_t FUNCTION(igraph_matrix, search)(const TYPE(igraph_matrix) *m, * Time complexity: O(mn), the number of elements in the matrix. */ -int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) { +igraph_error_t FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, igraph_integer_t row) { - long int c, r, index = row + 1, leap = 1, n = m->nrow * m->ncol; + igraph_integer_t 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); } @@ -1541,7 +1749,7 @@ int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) { } m->nrow--; IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(&m->data, m->nrow * m->ncol)); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1562,80 +1770,109 @@ int FUNCTION(igraph_matrix, remove_row)(TYPE(igraph_matrix) *m, long int row) { * columns of the result matrix. */ -int FUNCTION(igraph_matrix, select_cols)(const TYPE(igraph_matrix) *m, +igraph_error_t 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; + const igraph_vector_int_t *cols) { + igraph_integer_t ncols = igraph_vector_int_size(cols); + igraph_integer_t nrows = m->nrow; + igraph_integer_t 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]); + MATRIX(*res, i, j) = MATRIX(*m, i, VECTOR(*cols)[j]); } } - return 0; + return IGRAPH_SUCCESS; } #ifdef OUT_FORMAT - #ifndef USING_R -int FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) { +igraph_error_t FUNCTION(igraph_matrix, printf)(const TYPE(igraph_matrix) *m, + const char *format) { + igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); + igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); + igraph_integer_t i, j; - 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(format, MATRIX(*m, i, j)); } printf("\n"); } - return 0; + return IGRAPH_SUCCESS; } +#endif /* USING_R */ +#endif /* OUT_FORMAT */ -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"); - } +#if defined(OUT_FORMAT) || defined(FPRINTFUNC) + +#ifndef USING_R - return 0; +igraph_error_t FUNCTION(igraph_matrix, print)(const TYPE(igraph_matrix) *m) { + return FUNCTION(igraph_matrix, fprint)(m, stdout); } +#endif /* USING_R */ + +igraph_error_t FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, FILE *file) { + igraph_integer_t nr = FUNCTION(igraph_matrix, nrow)(m); + igraph_integer_t nc = FUNCTION(igraph_matrix, ncol)(m); + igraph_integer_t i, j; + igraph_vector_int_t column_width; + +#ifdef OUT_FORMAT + /* Insert dynamic width specifier '*' in format string. */ + char format[ sizeof(OUT_FORMAT) + 1 ] = "%*"; + strcpy(format + 2, (const char *) OUT_FORMAT + 1); #endif -int FUNCTION(igraph_matrix, fprint)(const TYPE(igraph_matrix) *m, - FILE *file) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&column_width, nc); + + /* First we detect the width needed for each matrix column. + * snprintf() may be passed a NULL pointer with a buffer size of 0. + * It will then return the number of characters that would have been written, + * without writing anything. */ + for (j = 0; j < nc; j++) { + for (i = 0; i < nr; i++) { + const int min_width = 1; /* minimum field width */ + int width; +#ifdef SNPRINTFUNC + width = SNPRINTFUNC(NULL, 0, MATRIX(*m, i, j)); +#else + width = snprintf(NULL, 0, OUT_FORMAT, MATRIX(*m, i, j)); +#endif + if (width < min_width) { + width = min_width; + } + if (width > VECTOR(column_width)[j]) { + VECTOR(column_width)[j] = width; + } + } + } - 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)); +#ifdef FPRINTFUNC_ALIGNED + FPRINTFUNC_ALIGNED(file, VECTOR(column_width)[j], MATRIX(*m, i, j)); +#else + fprintf(file, format, VECTOR(column_width)[j], MATRIX(*m, i, j)); +#endif } fprintf(file, "\n"); } - return 0; + igraph_vector_int_destroy(&column_width); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } -#endif +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ diff --git a/src/vendor/cigraph/src/core/matrix_list.c b/src/vendor/cigraph/src/core/matrix_list.c new file mode 100644 index 00000000000..f00e5d7d1c5 --- /dev/null +++ b/src/vendor/cigraph/src/core/matrix_list.c @@ -0,0 +1,54 @@ +/* -*- 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 + +*/ + +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_matrix_list.h" + +#define MATRIX_LIST + +#define BASE_IGRAPH_REAL +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_IGRAPH_REAL + +static igraph_error_t igraph_i_matrix_list_init_item( + const igraph_matrix_list_t* list, igraph_matrix_t* item +) { + IGRAPH_UNUSED(list); + return igraph_matrix_init(item, 0, 0); +} + +static igraph_error_t igraph_i_matrix_list_copy_item( + igraph_matrix_t* dest, const igraph_matrix_t* source +) { + return igraph_matrix_init_copy(dest, source); +} + +static void igraph_i_matrix_list_destroy_item(igraph_matrix_t* item) { + igraph_matrix_destroy(item); +} + +#undef MATRIX_LIST diff --git a/src/vendor/cigraph/src/core/memory.c b/src/vendor/cigraph/src/core/memory.c index a59afe712a3..490db99a7a0 100644 --- a/src/vendor/cigraph/src/core/memory.c +++ b/src/vendor/cigraph/src/core/memory.c @@ -24,30 +24,64 @@ #include "igraph_memory.h" /** - * \function igraph_free - * \brief Deallocate memory that was allocated by igraph functions. + * \section about-alloc-funcs About allocation 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. + * manually when the user does not need them any more. \c igraph_vector_ptr_t + * has functions to deallocate the contained pointers on its own, but in this + * case it has to be ensured that these pointers are allocated by a function + * that corresponding to the deallocator function that igraph uses. + * + * + * To this end, igraph exports the memory allocation functions that are used + * internally so the user of the library can ensure that the proper functions + * are used when pointers are moved between the code written by the user and + * the code of the igraph library. * * - * Here is a complete example on how to use \c igraph_free properly. + * Additionally, the memory allocator functions used by igraph work around the + * quirk of classical \c malloc(), \c realloc() and \c calloc() implementations + * where the behaviour of allocating zero bytes is undefined. igraph allocator + * functions will always allocate at least one byte. + */ + +/** + * \function igraph_free + * \brief Deallocate memory that was allocated by igraph functions. * - * \example examples/simple/igraph_free.c + * This function exposes the \c free() function used internally by igraph. * - * \param p Pointer to the piece of memory to be deallocated. - * \return Error code, currently always zero, meaning success. + * \param ptr Pointer to the piece of memory to be deallocated. * * Time complexity: platform dependent, ideally it should be O(1). * - * \sa \ref igraph_malloc() + * \sa \ref igraph_calloc(), \ref igraph_malloc(), \ref igraph_realloc() + */ + +void igraph_free(void *ptr) { + IGRAPH_FREE(ptr); +} + + +/** + * \function igraph_calloc + * \brief Allocate memory that can be safely deallocated by igraph functions. + * + * This function behaves like \c calloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. + * + * \param count Number of items to be allocated. + * \param size Size of a single item to be allocated. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. + * + * \sa \ref igraph_malloc(), \ref igraph_realloc(), \ref igraph_free() */ -void igraph_free(void *p) { - IGRAPH_FREE(p); +void *igraph_calloc(size_t count, size_t size) { + return (void *) IGRAPH_CALLOC(count * size, char); } @@ -55,19 +89,36 @@ void igraph_free(void *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()). + * This function behaves like \c malloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. + * + * \param size Number of bytes to be allocated. Zero is treated as one byte. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. + * + * \sa \ref igraph_calloc(), \ref igraph_realloc(), \ref igraph_free() + */ + +void *igraph_malloc(size_t size) { + return IGRAPH_MALLOC(size); +} + + +/** + * \function igraph_realloc + * \brief Reallocate memory that can be safely deallocated by igraph functions. + * + * This function behaves like \c realloc(), but it ensures that at least one + * byte is allocated even when the caller asks for zero bytes. * - * \param n Number of bytes to be allocated. - * \return Pointer to the piece of allocated memory. + * \param ptr The pointer to reallocate. + * \param size Number of bytes to be allocated. + * \return Pointer to the piece of allocated memory; \c NULL if the allocation + * failed. * - * \sa \ref igraph_free() + * \sa \ref igraph_free(), \ref igraph_malloc() */ -void *igraph_malloc(size_t n) { - return malloc(n); +void *igraph_realloc(void* ptr, size_t size) { + return (void*) IGRAPH_REALLOC(ptr, size, char); } diff --git a/src/vendor/cigraph/src/core/printing.c b/src/vendor/cigraph/src/core/printing.c index ac29f23c175..106c2a795bd 100644 --- a/src/vendor/cigraph/src/core/printing.c +++ b/src/vendor/cigraph/src/core/printing.c @@ -21,126 +21,213 @@ */ +#include "igraph_complex.h" +#include "igraph_error.h" #include "igraph_types.h" #include -#ifdef _MSC_VER - #define snprintf _snprintf -#endif - +/* The number of digits chosen here will be used in all places where + * igraph_real_fprintf_precise() is used, including all textual graph + * formats such as GML, GraphML, Pajek, etc. DBL_DIG digits are sufficient + * to preserve the decimal representation during a + * decimal (textual) -> binary -> decimal (textual) round-trip conversion. + * This many digits are however not sufficient for a lossless + * binary -> decimal -> binary conversion. Thus, writing numerical attributes + * to a file and reading them back in may cause a tiny change in the last + * binary digit of numbers. This change is minute, always smaller than 10^-15 + * times the original number, thus acceptable. + * + * We could output more digits, but that would come with its own problem: + * It would sometimes cause a change in the decimal representation originally + * input by users, which is surprising and confusing. For example, + * + * printf("%.17g\n", 100.1) + * + * outputs 100.09999999999999 instead of 100.1. We can prevent this by + * using DBL_DIG == 15 digits instead of 17, which would be required + * for a lossless binary -> decimal -> binary round-tripping. + * + * This justifies using DBL_DIG digits, and not more, in all places. + */ #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 15 digits for %g */ + /* Assume a precision of 15 digits for %g, which is what IEEE-754 doubles require. */ #define IGRAPH_REAL_PRINTF_PRECISE_FORMAT "%.15g" #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)) { + if (isfinite(val)) { return fprintf(file, "%g", val); - } else if (igraph_is_nan(val)) { + } else if (isnan(val)) { return fprintf(file, "NaN"); - } else if (igraph_is_inf(val)) { + } else if (isinf(val)) { if (val < 0) { return fprintf(file, "-Inf"); } else { return fprintf(file, "Inf"); } - } else { - /* fallback */ - return fprintf(file, "%g", val); } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ } -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)) { +#ifndef USING_R +int igraph_real_printf(igraph_real_t val) { + return igraph_real_fprintf(stdout, val); +} +#endif + +int igraph_real_fprintf_aligned(FILE *file, int width, igraph_real_t val) { + if (isfinite(val)) { + return fprintf(file, "%*g", width, val); + } else if (isnan(val)) { + return fprintf(file, "%*s", width, "NaN"); + } else if (isinf(val)) { if (val < 0) { - return snprintf(str, size, "-Inf"); + return fprintf(file, "%*s", width, "-Inf"); } else { - return snprintf(str, size, "Inf"); + return fprintf(file, "%*s", width, "Inf"); } - } else { - /* fallback */ - return snprintf(str, size, "%g", val); } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ } #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)) { +int igraph_real_printf_aligned(int width, igraph_real_t val) { + return igraph_real_fprintf_aligned(stdout, width, val); +} +#endif + +int igraph_real_snprintf(char *str, size_t size, igraph_real_t val) { + if (isfinite(val)) { + return snprintf(str, size, "%g", val); + } else if (isnan(val)) { + return snprintf(str, size, "NaN"); + } else if (isinf(val)) { if (val < 0) { - return printf("-Inf"); + return snprintf(str, size, "-Inf"); } else { - return printf("Inf"); + return snprintf(str, size, "Inf"); } - } else { - /* fallback */ - return printf(IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ } -#endif int igraph_real_fprintf_precise(FILE *file, igraph_real_t val) { - if (igraph_finite(val)) { + if (isfinite(val)) { return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); - } else if (igraph_is_nan(val)) { + } else if (isnan(val)) { return fprintf(file, "NaN"); - } else if (igraph_is_inf(val)) { + } else if (isinf(val)) { if (val < 0) { return fprintf(file, "-Inf"); } else { return fprintf(file, "Inf"); } - } else { - /* fallback */ - return fprintf(file, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#ifndef USING_R +int igraph_real_printf_precise(igraph_real_t val) { + return igraph_real_fprintf_precise(stdout, val); } +#endif -int igraph_real_snprintf_precise(char* str, size_t size, igraph_real_t val) { - if (igraph_finite(val)) { +int igraph_real_snprintf_precise(char *str, size_t size, igraph_real_t val) { + if (isfinite(val)) { return snprintf(str, size, IGRAPH_REAL_PRINTF_PRECISE_FORMAT, val); - } else if (igraph_is_nan(val)) { + } else if (isnan(val)) { return snprintf(str, size, "NaN"); - } else if (igraph_is_inf(val)) { + } else if (isinf(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); } + IGRAPH_FATAL("Value is not finite, not infinite and not NaN either!"); /* LCOV_EXCL_LINE */ +} + +#define PROPAGATE() \ + do { \ + if (res < 0) { \ + return -1; \ + } \ + cnt += res; \ + } while (0) + +int igraph_complex_fprintf(FILE *file, igraph_complex_t val) { + int res, cnt = 0; + igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); + res = igraph_real_fprintf(file, re); + PROPAGATE(); + if (! signbit(im)) { + res = fprintf(file, "+"); + PROPAGATE(); + } + res = igraph_real_fprintf(file, im); + PROPAGATE(); + res = fprintf(file, "i"); + PROPAGATE(); + return cnt; +} + +#undef PROPAGATE + +#ifndef USING_R +int igraph_complex_printf(igraph_complex_t val) { + return igraph_complex_fprintf(stdout, val); +} +#endif + +#define PROPAGATE() \ + do { \ + if (res < 0) { \ + return -1; \ + } \ + cnt += res; \ + /* remember that 'size' is unsigned, can't check if size - res < 0! */ \ + if (size > res) size -= res; \ + else size = 0; \ + if (size == 0) str = NULL; else str += res; \ + } while (0) + +int igraph_complex_snprintf(char *str, size_t size, igraph_complex_t val) { + int res, cnt = 0; + igraph_real_t re = IGRAPH_REAL(val), im = IGRAPH_IMAG(val); + res = igraph_real_snprintf(str, size, re); + PROPAGATE(); + if (! signbit(im)) { + res = snprintf(str, size, "+"); + PROPAGATE(); + } + res = igraph_real_snprintf(str, size, im); + PROPAGATE(); + res = snprintf(str, size, "i"); + PROPAGATE(); + return cnt; } + +int igraph_complex_fprintf_aligned(FILE *file, int width, igraph_complex_t val) { + /* Most characters produces by %g is 13, so including 'i' and null terminator we + * need up to 13 + 13 + 1 + 1 = 28 characters in total. */ + char buf[28]; + + if (igraph_complex_snprintf(buf, sizeof(buf) / sizeof(buf[0]), val) < 0) { + return -1; + } + return fprintf(file, "%*s", width, buf); +} + +#ifndef USING_R +int igraph_complex_printf_aligned(int width, igraph_complex_t val) { + return igraph_complex_fprintf_aligned(stdout, width, val); +} +#endif + +#undef PROPAGATE diff --git a/src/vendor/cigraph/src/core/progress.c b/src/vendor/cigraph/src/core/progress.c index 436d403dbb7..8cf35a8016c 100644 --- a/src/vendor/cigraph/src/core/progress.c +++ b/src/vendor/cigraph/src/core/progress.c @@ -52,7 +52,7 @@ static IGRAPH_THREAD_LOCAL char igraph_i_progressmsg_buffer[1000]; * Time complexity: O(1). */ -int igraph_progress(const char *message, igraph_real_t percent, void *data) { +igraph_error_t 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; @@ -90,12 +90,13 @@ int igraph_progress(const char *message, igraph_real_t percent, void *data) { * \return */ -int igraph_progressf(const char *message, igraph_real_t percent, void *data, +igraph_error_t 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); + va_end(ap); return igraph_progress(igraph_i_progressmsg_buffer, percent, data); } @@ -122,7 +123,7 @@ int igraph_progressf(const char *message, igraph_real_t percent, void *data, * Time complexity: O(1). */ -int igraph_progress_handler_stderr(const char *message, igraph_real_t percent, +igraph_error_t igraph_progress_handler_stderr(const char *message, igraph_real_t percent, void* data) { IGRAPH_UNUSED(data); fputs(message, stderr); diff --git a/src/vendor/cigraph/src/core/psumtree.c b/src/vendor/cigraph/src/core/psumtree.c index 3907efdb5fb..f0bfc73720e 100644 --- a/src/vendor/cigraph/src/core/psumtree.c +++ b/src/vendor/cigraph/src/core/psumtree.c @@ -27,11 +27,9 @@ #include "igraph_psumtree.h" #include "igraph_error.h" -#include +#include "math/safe_intop.h" -static double igraph_i_log2(double f) { - return log(f) / log(2.0); -} +#include /** * \ingroup psumtree @@ -83,17 +81,27 @@ static double igraph_i_log2(double f) { * 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 + * \param t The tree to initialize. + * \param size The number of elements in the tree. It must be at least one. + * \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) { +igraph_error_t igraph_psumtree_init(igraph_psumtree_t *t, igraph_integer_t size) { + igraph_integer_t vecsize; + + IGRAPH_ASSERT(size > 0); + 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; + + /* offset = 2^ceiling(log2(size)) - 1 */ + IGRAPH_CHECK(igraph_i_safe_next_pow_2(size, &t->offset)); + t->offset -= 1; + + IGRAPH_SAFE_ADD(t->offset, t->size, &vecsize); + IGRAPH_CHECK(igraph_vector_init(&t->v, vecsize)); + + return IGRAPH_SUCCESS; } /** @@ -104,7 +112,7 @@ int igraph_psumtree_init(igraph_psumtree_t *t, long int size) { * \param t The tree to reset. */ void igraph_psumtree_reset(igraph_psumtree_t *t) { - igraph_vector_fill(&(t->v), 0); + igraph_vector_null(&t->v); } /** @@ -138,7 +146,7 @@ void igraph_psumtree_destroy(igraph_psumtree_t *t) { * * Time complexity: O(1) */ -igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx) { +igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, igraph_integer_t idx) { const igraph_vector_t *tree = &t->v; return VECTOR(*tree)[t->offset + idx]; } @@ -169,11 +177,11 @@ igraph_real_t igraph_psumtree_get(const igraph_psumtree_t *t, long int idx) { * * 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_error_t igraph_psumtree_search(const igraph_psumtree_t *t, igraph_integer_t *idx, igraph_real_t search) { const igraph_vector_t *tree = &t->v; - long int i = 1; - long int size = igraph_vector_size(tree); + igraph_integer_t i = 1; + igraph_integer_t size = igraph_vector_size(tree); IGRAPH_ASSERT(search >= 0); IGRAPH_ASSERT(search < igraph_psumtree_sum(t)); @@ -208,12 +216,12 @@ int igraph_psumtree_search(const igraph_psumtree_t *t, long int *idx, * * 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_error_t igraph_psumtree_update(igraph_psumtree_t *t, igraph_integer_t idx, igraph_real_t new_value) { const igraph_vector_t *tree = &t->v; igraph_real_t difference; - if (new_value >= 0 && igraph_finite(new_value)) { + if (new_value >= 0 && isfinite(new_value)) { idx = idx + t->offset + 1; difference = new_value - VECTOR(*tree)[idx - 1]; @@ -241,7 +249,7 @@ int igraph_psumtree_update(igraph_psumtree_t *t, long int idx, * * Time complexity: O(1). */ -long int igraph_psumtree_size(const igraph_psumtree_t *t) { +igraph_integer_t igraph_psumtree_size(const igraph_psumtree_t *t) { return t->size; } diff --git a/src/vendor/cigraph/src/core/set.c b/src/vendor/cigraph/src/core/set.c index 7ce264d222d..a15c9d9151b 100644 --- a/src/vendor/cigraph/src/core/set.c +++ b/src/vendor/cigraph/src/core/set.c @@ -21,9 +21,7 @@ */ -#include "igraph_types.h" #include "igraph_memory.h" -#include "igraph_error.h" #include "core/set.h" @@ -36,8 +34,12 @@ * \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 + * Initializes an empty set (with zero elements). Allocates memory for + * the requested capacity. No re-allocation will be necessary until the + * number of elements exceeds this initial capacity. + * + * \param set Pointer to the set to be initialized. + * \param capacity The expected number of elements in the set. * * \return error code: * \c IGRAPH_ENOMEM if there is not enough memory. @@ -45,18 +47,19 @@ * 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; +igraph_error_t igraph_set_init(igraph_set_t *set, igraph_integer_t capacity) { + igraph_integer_t alloc_size; - if (size < 0) { - size = 0; - } - alloc_size = size > 0 ? size : 1; + IGRAPH_ASSERT(capacity >= 0); + alloc_size = capacity > 0 ? capacity : 1; set->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_integer_t); + if (! set->stor_begin) { + IGRAPH_ERROR("Cannot initialize set.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } set->stor_end = set->stor_begin + alloc_size; set->end = set->stor_begin; - return 0; + return IGRAPH_SUCCESS; } /** @@ -64,15 +67,14 @@ int igraph_set_init(igraph_set_t *set, int long size) { * \function igraph_set_destroy * \brief Destroys a set object. * - * \param set pointer to the set to be destroyed + * \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; + IGRAPH_ASSERT(set != NULL); + if (set->stor_begin != NULL) { + IGRAPH_FREE(set->stor_begin); /* sets to NULL */ } } @@ -92,38 +94,37 @@ void igraph_set_destroy(igraph_set_t* set) { * Time complexity: O(1) */ igraph_bool_t igraph_set_inited(igraph_set_t* set) { - return (set->stor_begin != 0); + return (set->stor_begin != NULL); } /** * \ingroup set * \function igraph_set_reserve - * \brief Reserve memory for a set. + * \brief Reserves memory for a set. * * \param set The set object. - * \param size the new \em allocated size of the set. + * \param capacity the new \em allocated capacity 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_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t capacity) { + igraph_integer_t 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; + if (capacity <= actual_size) { + return IGRAPH_SUCCESS; } - tmp = IGRAPH_REALLOC(set->stor_begin, (size_t) size, igraph_integer_t); - if (tmp == 0) { - IGRAPH_ERROR("cannot reserve space for set", IGRAPH_ENOMEM); - } + tmp = IGRAPH_REALLOC(set->stor_begin, capacity, igraph_integer_t); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for set."); + set->stor_begin = tmp; - set->stor_end = set->stor_begin + size; + set->stor_end = set->stor_begin + capacity; set->end = set->stor_begin + actual_size; - return 0; + return IGRAPH_SUCCESS; } /** @@ -146,13 +147,14 @@ igraph_bool_t igraph_set_empty(const igraph_set_t* set) { /** * \ingroup set * \function igraph_set_clear - * \brief Removes all elements from a set. + * \brief Removes all elements from the 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. + * + * \param set The set object. * * Time complexity: O(1). */ @@ -166,15 +168,17 @@ void igraph_set_clear(igraph_set_t* set) { /** * \ingroup set * \function igraph_set_size - * \brief Gives the size (=length) of the set. + * \brief Gives the size of the set. * - * \param v The set object + * The number of elements in the set. + * + * \param set The set object * \return The size of the set. * * Time complexity: O(1). */ -long int igraph_set_size(const igraph_set_t* set) { +igraph_integer_t igraph_set_size(const igraph_set_t* set) { IGRAPH_ASSERT(set != NULL); IGRAPH_ASSERT(set->stor_begin != NULL); return set->end - set->stor_begin; @@ -193,9 +197,9 @@ long int igraph_set_size(const igraph_set_t* set) { * * 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_error_t igraph_set_add(igraph_set_t* set, igraph_integer_t e) { + igraph_integer_t left, right, middle; + igraph_integer_t size; IGRAPH_ASSERT(set != NULL); IGRAPH_ASSERT(set->stor_begin != NULL); @@ -226,7 +230,10 @@ int igraph_set_add(igraph_set_t* set, igraph_integer_t e) { if (left >= size || set->stor_begin[left] != e) { /* full, allocate more storage */ if (set->stor_end == set->end) { - long int new_size = size * 2; + igraph_integer_t new_size = size < IGRAPH_INTEGER_MAX/2 ? size * 2 : IGRAPH_INTEGER_MAX; + if (size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add to set, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -236,13 +243,13 @@ int igraph_set_add(igraph_set_t* set, igraph_integer_t e) { /* 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])); + (size - left) * sizeof(set->stor_begin[0])); set->stor_begin[left] = e; set->end += 1; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -256,8 +263,8 @@ int igraph_set_add(igraph_set_t* set, igraph_integer_t e) { * * 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_bool_t igraph_set_contains(const igraph_set_t* set, igraph_integer_t e) { + igraph_integer_t left, right, middle; IGRAPH_ASSERT(set != NULL); IGRAPH_ASSERT(set->stor_begin != NULL); @@ -266,7 +273,7 @@ igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { right = igraph_set_size(set) - 1; if (right == -1) { - return 0; /* the set is empty */ + return false; /* the set is empty */ } /* search for the new element */ @@ -277,7 +284,7 @@ igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { } else if (SET(*set)[middle] < e) { left = middle; } else { - return 1; + return true; } } @@ -287,23 +294,23 @@ igraph_bool_t igraph_set_contains(igraph_set_t* set, igraph_integer_t e) { /** * \ingroup set * \function igraph_set_iterate - * \brief Iterates through the element to the set. + * \brief Iterates through the element of 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 + * This should be a pointer to an \c igraph_integer_t variable * which must be zero for the first invocation. - * The object should not be adjusted and its value should + * The object must 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 + * \param element The next element or 0 (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_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t *state, + igraph_integer_t *element) { IGRAPH_ASSERT(set != 0); IGRAPH_ASSERT(set->stor_begin != 0); IGRAPH_ASSERT(state != 0); @@ -312,9 +319,9 @@ igraph_bool_t igraph_set_iterate(igraph_set_t* set, long int* state, if (*state < igraph_set_size(set)) { *element = set->stor_begin[*state]; *state = *state + 1; - return 1; + return true; } else { *element = 0; - return 0; + return false; } } diff --git a/src/vendor/cigraph/src/core/set.h b/src/vendor/cigraph/src/core/set.h index 95697c0fe32..7b435e3e567 100644 --- a/src/vendor/cigraph/src/core/set.h +++ b/src/vendor/cigraph/src/core/set.h @@ -24,6 +24,7 @@ #define IGRAPH_CORE_SET_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -48,16 +49,16 @@ typedef struct s_set { 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 igraph_error_t igraph_set_init(igraph_set_t* set, igraph_integer_t 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_error_t igraph_set_reserve(igraph_set_t* set, igraph_integer_t 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_PRIVATE_EXPORT igraph_integer_t igraph_set_size(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_set_add(igraph_set_t* v, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_contains(const igraph_set_t *set, igraph_integer_t e); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_set_iterate(const igraph_set_t *set, igraph_integer_t* state, igraph_integer_t* element); __END_DECLS diff --git a/src/vendor/cigraph/src/core/sparsemat.c b/src/vendor/cigraph/src/core/sparsemat.c index 7bc0166608d..b73455eaa56 100644 --- a/src/vendor/cigraph/src/core/sparsemat.c +++ b/src/vendor/cigraph/src/core/sparsemat.c @@ -21,18 +21,30 @@ */ -#include - #include "igraph_sparsemat.h" -#include "igraph_error.h" -#include "igraph_interface.h" + +#include "igraph_attributes.h" #include "igraph_constructors.h" +#include "igraph_interface.h" #include "igraph_memory.h" +#include "igraph_types.h" #include "igraph_vector_ptr.h" -#include "igraph_attributes.h" +#include "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ + +#include #include +#include +#undef cs /* because otherwise it messes up the name of the 'cs' member in igraph_sparsemat_t */ + +/* Returns the number of potential nonzero elements in the given sparse matrix. + * The returned value can be used to iterate over A->cs->x no matter whether the + * matrix is in triplet or column-compressed form */ +static CS_INT igraph_i_sparsemat_count_elements(const igraph_sparsemat_t* A) { + return A->cs->nz < 0 ? A->cs->p[A->cs->n] : A->cs->nz; +} + /** * \section about_sparsemat About sparse matrices * @@ -98,7 +110,10 @@ * Time complexity: TODO. */ -int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) { +igraph_error_t igraph_sparsemat_init(igraph_sparsemat_t *A, igraph_integer_t rows, + igraph_integer_t cols, igraph_integer_t nzmax) { + IGRAPH_STATIC_ASSERT(sizeof(igraph_integer_t) == sizeof(CS_INT)); + IGRAPH_STATIC_ASSERT(sizeof(igraph_real_t) == sizeof(CS_ENTRY)); if (rows < 0) { IGRAPH_ERROR("Negative number of rows", IGRAPH_EINVAL); @@ -110,14 +125,14 @@ int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) A->cs = cs_spalloc( rows, cols, nzmax, /*values=*/ 1, /*triplet=*/ 1); if (!A->cs) { - IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot allocate memory for sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - return 0; + return IGRAPH_SUCCESS; } /** - * \function igraph_sparsemat_copy + * \function igraph_sparsemat_init_copy * \brief Copies a sparse matrix. * * Create a sparse matrix object, by copying another one. The source @@ -135,8 +150,9 @@ int igraph_sparsemat_init(igraph_sparsemat_t *A, int rows, int cols, int nzmax) * number of non-zero elements. */ -int igraph_sparsemat_copy(igraph_sparsemat_t *to, - const igraph_sparsemat_t *from) { +igraph_error_t igraph_sparsemat_init_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from +) { CS_INT ne = from->cs->nz == -1 ? from->cs->n + 1 : from->cs->nzmax; @@ -149,11 +165,24 @@ int igraph_sparsemat_copy(igraph_sparsemat_t *to, 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)); + memcpy(to->cs->p, from->cs->p, sizeof(CS_INT) * (size_t) ne); + memcpy(to->cs->i, from->cs->i, sizeof(CS_INT) * (size_t) (from->cs->nzmax)); + memcpy(to->cs->x, from->cs->x, sizeof(CS_ENTRY) * (size_t) (from->cs->nzmax)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_copy + * \brief Copies a sparse matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_copy 0.10 + */ - return 0; +igraph_error_t igraph_sparsemat_copy( + igraph_sparsemat_t *to, const igraph_sparsemat_t *from +) { + return igraph_sparsemat_init_copy(to, from); } /** @@ -187,9 +216,9 @@ void igraph_sparsemat_destroy(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax) { +igraph_error_t igraph_sparsemat_realloc(igraph_sparsemat_t *A, igraph_integer_t nzmax) { if (!cs_sprealloc(A->cs, nzmax)) { - IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Could not allocate more memory for sparse matrix.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } return IGRAPH_SUCCESS; } @@ -204,7 +233,7 @@ int igraph_sparsemat_realloc(igraph_sparsemat_t *A, int nzmax) { * Time complexity: O(1). */ -long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { +igraph_integer_t igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { return A->cs->m; } @@ -218,7 +247,7 @@ long int igraph_sparsemat_nrow(const igraph_sparsemat_t *A) { * Time complexity: O(1). */ -long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { +igraph_integer_t igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { return A->cs->n; } @@ -236,7 +265,7 @@ long int igraph_sparsemat_ncol(const igraph_sparsemat_t *A) { */ igraph_sparsemat_type_t igraph_sparsemat_type(const igraph_sparsemat_t *A) { - return A->cs->nz < 0 ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; + return igraph_sparsemat_is_cc(A) ? IGRAPH_SPARSEMAT_CC : IGRAPH_SPARSEMAT_TRIPLET; } /** @@ -286,57 +315,59 @@ igraph_bool_t igraph_sparsemat_is_cc(const igraph_sparsemat_t *A) { * 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) { +igraph_error_t 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* pinv; CS_INT i; if (nrow != igraph_vector_int_size(p)) { - IGRAPH_ERROR("Invalid row permutation length", IGRAPH_FAILURE); + IGRAPH_ERROR("Invalid row permutation length.", IGRAPH_FAILURE); } if (ncol != igraph_vector_int_size(q)) { - IGRAPH_ERROR("Invalid column permutation length", IGRAPH_FAILURE); + 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); + pinv = IGRAPH_CALLOC(nrow, CS_INT); + if (pinv == 0) { + IGRAPH_ERROR("Cannot allocate index vector for permutation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, pinv); for (i = 0; i < nrow; i++) { - VECTOR(pinv)[ VECTOR(*p)[i] ] = (int) i; + pinv[ VECTOR(*p)[i] ] = i; } /* And call the permutation routine */ - res->cs = cs_permute(A->cs, VECTOR(pinv), VECTOR(*q), /*values=*/ 1); + res->cs = cs_permute(A->cs, pinv, (const CS_INT*) VECTOR(*q), /*values=*/ 1); if (!res->cs) { IGRAPH_ERROR("Cannot index sparse matrix", IGRAPH_FAILURE); } - igraph_vector_int_destroy(&pinv); + IGRAPH_FREE(pinv); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, +static igraph_error_t 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; + igraph_integer_t idx_rows = igraph_vector_int_size(p); + igraph_integer_t k; /* Create index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, - (int) idx_rows)); + IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, 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_entry(&II2, k, VECTOR(*p)[k], 1.0)); } IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); igraph_sparsemat_destroy(&II2); @@ -356,25 +387,24 @@ static int igraph_i_sparsemat_index_rows(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, +static igraph_error_t 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; + igraph_integer_t idx_cols = igraph_vector_int_size(q); + igraph_integer_t k; /* Create index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, (int) ncol, (int) idx_cols, - (int) idx_cols)); + IGRAPH_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, 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_entry(&JJ2, VECTOR(*q)[k], k, 1.0)); } IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); igraph_sparsemat_destroy(&JJ2); @@ -394,7 +424,7 @@ static int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -424,7 +454,7 @@ static int igraph_i_sparsemat_index_cols(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_index(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_index(const igraph_sparsemat_t *A, const igraph_vector_int_t *p, const igraph_vector_int_t *q, igraph_sparsemat_t *res, @@ -433,9 +463,9 @@ int igraph_sparsemat_index(const igraph_sparsemat_t *A, 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_integer_t idx_rows = p ? igraph_vector_int_size(p) : -1; + igraph_integer_t idx_cols = q ? igraph_vector_int_size(q) : -1; + igraph_integer_t k; igraph_sparsemat_t *myres = res, mres; @@ -460,11 +490,10 @@ int igraph_sparsemat_index(const igraph_sparsemat_t *A, } /* Create first index matrix */ - IGRAPH_CHECK(igraph_sparsemat_init(&II2, (int) idx_rows, (int) nrow, - (int) idx_rows)); + IGRAPH_CHECK(igraph_sparsemat_init(&II2, idx_rows, nrow, 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_entry(&II2, k, VECTOR(*p)[k], 1.0)); } IGRAPH_CHECK(igraph_sparsemat_compress(&II2, &II)); igraph_sparsemat_destroy(&II2); @@ -472,11 +501,10 @@ int igraph_sparsemat_index(const igraph_sparsemat_t *A, 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_CHECK(igraph_sparsemat_init(&JJ2, ncol, idx_cols, 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_entry(&JJ2, VECTOR(*q)[k], k, 1.0)); } IGRAPH_CHECK(igraph_sparsemat_compress(&JJ2, &JJ)); igraph_sparsemat_destroy(&JJ2); @@ -505,7 +533,7 @@ int igraph_sparsemat_index(const igraph_sparsemat_t *A, igraph_sparsemat_destroy(myres); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -526,8 +554,8 @@ int igraph_sparsemat_index(const igraph_sparsemat_t *A, * Time complexity: O(1) on average. */ -int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, - igraph_real_t elem) { +igraph_error_t igraph_sparsemat_entry(igraph_sparsemat_t *A, + igraph_integer_t row, igraph_integer_t 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); @@ -557,7 +585,7 @@ int igraph_sparsemat_entry(igraph_sparsemat_t *A, int row, int col, * Time complexity: O(nz) where \c nz is the number of non-zero elements. */ -int igraph_sparsemat_compress(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_compress(const igraph_sparsemat_t *A, igraph_sparsemat_t *res) { if (! igraph_sparsemat_is_triplet(A)) { @@ -571,6 +599,75 @@ int igraph_sparsemat_compress(const igraph_sparsemat_t *A, return IGRAPH_SUCCESS; } +static igraph_real_t igraph_i_sparsemat_get_cc( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + /* elements in column 'col' are at indices + * A->cs->p[col] .. A->cs->p[col+1] (open from right) in + * A->cs->x . + * + * Their corresponding row indices are in A->cs->i . + */ + + CS_INT lo = A->cs->p[col]; + CS_INT hi = A->cs->p[col + 1]; + igraph_real_t result = 0.0; + + /* TODO: this could be faster with binary search if A->cs->i + * is sorted, which I think should be */ + for (; lo < hi; lo++) { + if (A->cs->i[lo] == row) { + result += A->cs->x[lo]; + } + } + + return result; +} + +static igraph_real_t igraph_i_sparsemat_get_triplet( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + igraph_sparsemat_iterator_t it; + igraph_real_t result = 0.0; + + igraph_sparsemat_iterator_init(&it, A); + while (!igraph_sparsemat_iterator_end(&it)) { + if ( + igraph_sparsemat_iterator_row(&it) == row && + igraph_sparsemat_iterator_col(&it) == col + ) { + result += igraph_sparsemat_iterator_get(&it); + } + igraph_sparsemat_iterator_next(&it); + } + + return result; +} + +/** + * \function igraph_sparsemat_get + * \brief Return the value of a single element from a sparse matrix. + * + * \param A The input matrix, in triplet or column-compressed format. + * \param row The row index + * \param col The column index + * \return The value of the cell with the given row and column indices in the + * matrix; zero if the indices are out of bounds. + * + * Time complexity: TODO. + */ +igraph_real_t igraph_sparsemat_get( + const igraph_sparsemat_t *A, igraph_integer_t row, igraph_integer_t col +) { + if (row < 0 || col < 0 || row >= A->cs->m || col >= A->cs->n) { + return 0.0; + } else if (igraph_sparsemat_is_cc(A)) { + return igraph_i_sparsemat_get_cc(A, row, col); + } else { + return igraph_i_sparsemat_get_triplet(A, row, col); + } +} + /** * \function igraph_sparsemat_transpose * \brief Transposes a sparse matrix. @@ -578,55 +675,52 @@ int igraph_sparsemat_compress(const igraph_sparsemat_t *A, * \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) { +igraph_error_t igraph_sparsemat_transpose( + const igraph_sparsemat_t *A, igraph_sparsemat_t *res +) { - if (A->cs->nz < 0) { + if (igraph_sparsemat_is_cc(A)) { /* column-compressed */ - res->cs = cs_transpose(A->cs, values); + res->cs = cs_transpose(A->cs, /* values = */ 1); if (!res->cs) { IGRAPH_ERROR("Cannot transpose sparse matrix", IGRAPH_FAILURE); } } else { /* triplets */ CS_INT *tmp; - IGRAPH_CHECK(igraph_sparsemat_copy(res, A)); + IGRAPH_CHECK(igraph_sparsemat_init_copy(res, A)); tmp = res->cs->p; res->cs->p = res->cs->i; res->cs->i = tmp; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igraph_bool_t *result) { +static igraph_error_t 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_integer_t nz; - IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t, /*values=*/ 1)); + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &t)); IGRAPH_FINALLY(igraph_sparsemat_destroy, &t); IGRAPH_CHECK(igraph_sparsemat_dupl(&t)); - IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt, /*values=*/ 1)); + IGRAPH_CHECK(igraph_sparsemat_transpose(&t, &tt)); igraph_sparsemat_destroy(&t); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_sparsemat_destroy, &tt); - IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t, /*values=*/ 1)); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tt, &t)); 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) * + res = memcmp(t.cs->i, tt.cs->i, sizeof(CS_INT) * (size_t) nz) == 0; + res = res && memcmp(t.cs->p, tt.cs->p, sizeof(CS_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; + res = res && memcmp(t.cs->x, tt.cs->x, sizeof(CS_ENTRY) * (size_t)nz) == 0; igraph_sparsemat_destroy(&t); igraph_sparsemat_destroy(&tt); @@ -637,7 +731,7 @@ static int igraph_i_sparsemat_is_symmetric_cc(const igraph_sparsemat_t *A, igrap return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, igraph_bool_t *result) { +static igraph_error_t 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)); @@ -649,24 +743,24 @@ static int igraph_i_sparsemat_is_symmetric_triplet(const igraph_sparsemat_t *A, return IGRAPH_SUCCESS; } -igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A) { - igraph_bool_t res = 0; +/** + * \function igraph_sparsemat_is_symmetric + * \brief Returns whether a sparse matrix is symmetric. + * + * \param A The input matrix + * \param result Pointer to an \c igraph_bool_t ; the result is provided here. + * \return Error code. + */ +igraph_error_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *A, igraph_bool_t *result) { 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); + *result = false; + } else if (igraph_sparsemat_is_cc(A)) { + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_cc(A, result)); } else { - igraph_i_sparsemat_is_symmetric_triplet(A, &res); + IGRAPH_CHECK(igraph_i_sparsemat_is_symmetric_triplet(A, result)); } - - return res; + return IGRAPH_SUCCESS; } /** @@ -677,20 +771,32 @@ igraph_bool_t igraph_sparsemat_is_symmetric(const igraph_sparsemat_t *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) { +igraph_error_t igraph_sparsemat_dupl(igraph_sparsemat_t *A) { if (!cs_dupl(A->cs)) { IGRAPH_ERROR("Cannot remove duplicates from sparse matrix", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; +} + +struct fkeep_wrapper_data { + igraph_integer_t (*fkeep) (igraph_integer_t, igraph_integer_t, igraph_real_t, void*); + void* data; +}; + +static CS_INT fkeep_wrapper(CS_INT row, CS_INT col, double value, void* data) { + return ((struct fkeep_wrapper_data*)data)->fkeep( + row, col, value, ((struct fkeep_wrapper_data*)data)->data + ); } /** @@ -701,10 +807,11 @@ int igraph_sparsemat_dupl(igraph_sparsemat_t *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, + * first is an \c igraph_integer_t, the row index of the entry, the second is + * another \c igraph_integer_t, 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 @@ -716,18 +823,22 @@ int igraph_sparsemat_dupl(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -int igraph_sparsemat_fkeep( +igraph_error_t igraph_sparsemat_fkeep( igraph_sparsemat_t *A, igraph_integer_t (*fkeep)(igraph_integer_t, igraph_integer_t, igraph_real_t, void*), void *other ) { + struct fkeep_wrapper_data wrapper_data = { + /* .fkeep = */ fkeep, + /* .data = */ 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) { + if (cs_fkeep(A->cs, fkeep_wrapper, &wrapper_data) < 0) { IGRAPH_ERROR("External function cs_keep has returned an unknown error while filtering the matrix.", IGRAPH_FAILURE); } @@ -740,19 +851,20 @@ int igraph_sparsemat_fkeep( * * 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) { +igraph_error_t igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { if (!cs_dropzeros(A->cs)) { IGRAPH_ERROR("Cannot drop zeros from sparse matrix", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -762,6 +874,7 @@ int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { * 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. @@ -769,7 +882,7 @@ int igraph_sparsemat_dropzeros(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { +igraph_error_t igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { IGRAPH_ASSERT(A); if (!igraph_sparsemat_is_cc(A)) { @@ -787,6 +900,7 @@ int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { * \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 @@ -798,7 +912,7 @@ int igraph_sparsemat_droptol(igraph_sparsemat_t *A, igraph_real_t tol) { * Time complexity: TODO. */ -int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_multiply(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_sparsemat_t *res) { @@ -807,7 +921,7 @@ int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot multiply matrices", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -827,7 +941,7 @@ int igraph_sparsemat_multiply(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_add(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_add(const igraph_sparsemat_t *A, const igraph_sparsemat_t *B, igraph_real_t alpha, igraph_real_t beta, @@ -838,7 +952,7 @@ int igraph_sparsemat_add(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot add matrices", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -855,7 +969,7 @@ int igraph_sparsemat_add(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, const igraph_vector_t *x, igraph_vector_t *res) { @@ -870,7 +984,7 @@ int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -879,6 +993,7 @@ int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, * * 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. @@ -887,7 +1002,7 @@ int igraph_sparsemat_gaxpy(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, +igraph_error_t igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { @@ -903,7 +1018,7 @@ int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -912,6 +1027,7 @@ int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, * * 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. @@ -920,7 +1036,7 @@ int igraph_sparsemat_lsolve(const igraph_sparsemat_t *L, * Time complexity: TODO. */ -int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, +igraph_error_t igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, const igraph_vector_t *b, igraph_vector_t *res) { @@ -937,7 +1053,7 @@ int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, IGRAPH_ERROR("Cannot perform lower triangular solve", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -945,6 +1061,7 @@ int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, * \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. @@ -953,7 +1070,7 @@ int igraph_sparsemat_ltsolve(const igraph_sparsemat_t *L, * Time complexity: TODO. */ -int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, +igraph_error_t igraph_sparsemat_usolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { @@ -969,7 +1086,7 @@ int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, IGRAPH_ERROR("Cannot perform upper triangular solve", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -978,6 +1095,7 @@ int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, * * 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. @@ -986,7 +1104,7 @@ int igraph_sparsemat_usolve(const igraph_sparsemat_t *U, * Time complexity: TODO. */ -int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, +igraph_error_t igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, const igraph_vector_t *b, igraph_vector_t *res) { @@ -1004,7 +1122,7 @@ int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1012,6 +1130,7 @@ int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, * \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. @@ -1023,10 +1142,10 @@ int igraph_sparsemat_utsolve(const igraph_sparsemat_t *U, * Time complexity: TODO. */ -int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - int order) { + igraph_integer_t order) { if (A->cs->m != A->cs->n) { IGRAPH_ERROR("Cannot perform sparse symmetric solve", @@ -1041,7 +1160,7 @@ int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot perform sparse symmetric solve", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1049,6 +1168,7 @@ int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, * \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. @@ -1063,10 +1183,10 @@ int igraph_sparsemat_cholsol(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_lusol(const igraph_sparsemat_t *A, const igraph_vector_t *b, igraph_vector_t *res, - int order, + igraph_integer_t order, igraph_real_t tol) { if (A->cs->m != A->cs->n) { @@ -1082,26 +1202,26 @@ int igraph_sparsemat_lusol(const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot perform LU solve", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - igraph_vector_t edges; + igraph_vector_int_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; + igraph_integer_t from = 0; + igraph_integer_t to = 0; + igraph_integer_t 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); while (*p < no_of_edges) { while (to < * (p + 1)) { @@ -1115,31 +1235,30 @@ static int igraph_i_sparsemat_cc(igraph_t *graph, const igraph_sparsemat_t *A, from++; p++; } - igraph_vector_resize(&edges, e); + igraph_vector_int_resize(&edges, e); - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - igraph_vector_t edges; + igraph_vector_int_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; + igraph_integer_t 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); for (e = 0; e < 2 * no_of_edges; i++, j++) { if (directed || *i >= *j) { @@ -1147,14 +1266,13 @@ static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t VECTOR(edges)[e++] = (*j); } } - igraph_vector_resize(&edges, e); + igraph_vector_int_resize(&edges, e); - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1166,6 +1284,7 @@ static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t * 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. @@ -1176,34 +1295,34 @@ static int igraph_i_sparsemat_triplet(igraph_t *graph, const igraph_sparsemat_t * Time complexity: TODO. */ -int igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, igraph_bool_t directed) { - if (A->cs->nz < 0) { + if (igraph_sparsemat_is_cc(A)) { 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, +static igraph_error_t 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_int_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_integer_t from = 0; + igraph_integer_t to = 0; + igraph_integer_t e = 0, w = 0; IGRAPH_UNUSED(attr); - igraph_vector_resize(edges, no_of_edges * 2); - igraph_vector_resize(weights, no_of_edges); + IGRAPH_CHECK(igraph_vector_int_resize(edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); while (*p < no_of_edges) { while (to < * (p + 1)) { @@ -1220,17 +1339,17 @@ static int igraph_i_weighted_sparsemat_cc(const igraph_sparsemat_t *A, p++; } - igraph_vector_resize(edges, e); - igraph_vector_resize(weights, w); + igraph_vector_int_resize(edges, e); /* shrinks */ + igraph_vector_resize(weights, w); /* shrinks */ - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, +static igraph_error_t 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_int_t *edges, igraph_vector_t *weights) { IGRAPH_UNUSED(A); IGRAPH_UNUSED(directed); IGRAPH_UNUSED(attr); @@ -1241,12 +1360,13 @@ static int igraph_i_weighted_sparsemat_triplet(const igraph_sparsemat_t *A, IGRAPH_UNIMPLEMENTED); } -int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, +igraph_error_t 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; + igraph_vector_int_t edges; + igraph_vector_t weights; + CS_INT pot_edges = igraph_i_sparsemat_count_elements(A); const char* default_attr = "weight"; igraph_vector_ptr_t attr_vec; igraph_attribute_record_t attr_rec; @@ -1256,11 +1376,11 @@ int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, IGRAPH_ERROR("Cannot create graph object", IGRAPH_NONSQUARE); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, pot_edges * 2); + IGRAPH_VECTOR_INT_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) { + if (igraph_sparsemat_is_cc(A)) { IGRAPH_CHECK(igraph_i_weighted_sparsemat_cc(A, directed, attr, loops, &edges, &weights)); } else { @@ -1276,57 +1396,20 @@ int igraph_weighted_sparsemat(igraph_t *graph, const igraph_sparsemat_t *A, VECTOR(attr_vec)[0] = &attr_rec; /* Create graph */ - IGRAPH_CHECK(igraph_empty(graph, (igraph_integer_t) no_of_nodes, directed)); + IGRAPH_CHECK(igraph_empty(graph, no_of_nodes, directed)); IGRAPH_FINALLY(igraph_destroy, graph); - if (igraph_vector_size(&edges) > 0) { + if (igraph_vector_int_size(&edges) > 0) { IGRAPH_CHECK(igraph_add_edges(graph, &edges, &attr_vec)); } IGRAPH_FINALLY_CLEAN(1); /* Cleanup */ - igraph_vector_destroy(&edges); + igraph_vector_int_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; + return IGRAPH_SUCCESS; } #define CHECK(x) if ((x)<0) { IGRAPH_ERROR("Cannot write to file", IGRAPH_EFILE); } @@ -1338,6 +1421,7 @@ int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { * 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. @@ -1347,10 +1431,10 @@ int igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { * n is the number columns in the matrix. */ -int igraph_sparsemat_print(const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_print(const igraph_sparsemat_t *A, FILE *outstream) { - if (A->cs->nz < 0) { + if (igraph_sparsemat_is_cc(A)) { /* CC */ CS_INT j, p; for (j = 0; j < A->cs->n; j++) { @@ -1369,27 +1453,30 @@ int igraph_sparsemat_print(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } #undef CHECK -static int igraph_i_sparsemat_eye_triplet(igraph_sparsemat_t *A, int n, int nzmax, - igraph_real_t value) { - long int i; +static igraph_error_t igraph_i_sparsemat_eye_triplet( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value +) { + igraph_integer_t 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); + IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, value)); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n, - igraph_real_t value) { - CS_INT i; +static igraph_error_t igraph_i_sparsemat_eye_cc( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_real_t value +) { + igraph_integer_t i; A->cs = cs_spalloc(n, n, n, /*values=*/ 1, /*triplet=*/ 0); if (!A->cs) { @@ -1403,11 +1490,11 @@ static int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n, } A->cs->p [n] = n; - return 0; + return IGRAPH_SUCCESS; } /** - * \function igraph_sparsemat_eye + * \function igraph_sparsemat_init_eye * \brief Creates a sparse identity matrix. * * \param A An uninitialized sparse matrix, the result is stored @@ -1424,32 +1511,48 @@ static int igraph_i_sparsemat_eye_cc(igraph_sparsemat_t *A, int n, * Time complexity: O(n). */ -int igraph_sparsemat_eye(igraph_sparsemat_t *A, int n, int nzmax, - igraph_real_t value, - igraph_bool_t compress) { +igraph_error_t igraph_sparsemat_init_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress +) { if (compress) { - return (igraph_i_sparsemat_eye_cc(A, n, value)); + return igraph_i_sparsemat_eye_cc(A, n, value); } else { - return (igraph_i_sparsemat_eye_triplet(A, n, nzmax, value)); + 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) { +/** + * \function igraph_sparsemat_eye + * \brief Creates a sparse identity matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_eye 0.10 + */ + +igraph_error_t igraph_sparsemat_eye( + igraph_sparsemat_t *A, igraph_integer_t n, igraph_integer_t nzmax, + igraph_real_t value, igraph_bool_t compress +) { + return igraph_sparsemat_init_eye(A, n, nzmax, value, compress); +} + +static igraph_error_t igraph_i_sparsemat_init_diag_triplet( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values +) { - int i, n = (int) igraph_vector_size(values); + CS_INT i, n = 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]); + IGRAPH_CHECK(igraph_sparsemat_entry(A, i, i, VECTOR(*values)[i])); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_init_diag_cc(igraph_sparsemat_t *A, const igraph_vector_t *values) { CS_INT i, n = igraph_vector_size(values); @@ -1466,12 +1569,12 @@ static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, } A->cs->p [n] = n; - return 0; + return IGRAPH_SUCCESS; } /** - * \function igraph_sparsemat_diag + * \function igraph_sparsemat_init_diag * \brief Creates a sparse diagonal matrix. * * \param A An uninitialized sparse matrix, the result is stored @@ -1488,18 +1591,32 @@ static int igraph_i_sparsemat_diag_cc(igraph_sparsemat_t *A, * 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) { - +igraph_error_t igraph_sparsemat_init_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress +) { if (compress) { - return (igraph_i_sparsemat_diag_cc(A, values)); + return (igraph_i_sparsemat_init_diag_cc(A, values)); } else { - return (igraph_i_sparsemat_diag_triplet(A, nzmax, values)); + return (igraph_i_sparsemat_init_diag_triplet(A, nzmax, values)); } } -static int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, +/** + * \function igraph_sparsemat_diag + * \brief Creates a sparse diagonal matrix (deprecated alias). + * + * \deprecated-by igraph_sparsemat_init_diag 0.10 + */ + +igraph_error_t igraph_sparsemat_diag( + igraph_sparsemat_t *A, igraph_integer_t nzmax, const igraph_vector_t *values, + igraph_bool_t compress +) { + return igraph_sparsemat_init_diag(A, nzmax, values, compress); +} + +static igraph_error_t igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -1509,7 +1626,7 @@ static int igraph_i_sparsemat_arpack_multiply(igraph_real_t *to, igraph_vector_view(&vfrom, from, n); igraph_vector_null(&vto); IGRAPH_CHECK(igraph_sparsemat_gaxpy(A, &vfrom, &vto)); - return 0; + return IGRAPH_SUCCESS; } typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { @@ -1519,7 +1636,7 @@ typedef struct igraph_i_sparsemat_arpack_rssolve_data_t { igraph_sparsemat_solve_t method; } igraph_i_sparsemat_arpack_rssolve_data_t; -static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, +static igraph_error_t igraph_i_sparsemat_arpack_solve(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -1539,7 +1656,7 @@ static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1547,10 +1664,10 @@ static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, * \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 + * \param options It is passed to \ref igraph_arpack_rssolve(). Supply + * \c NULL here to use the defaults. 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 @@ -1573,20 +1690,28 @@ static int igraph_i_sparsemat_arpack_solve(igraph_real_t *to, * Time complexity: TODO. */ -int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, +igraph_error_t 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); + igraph_integer_t n = igraph_sparsemat_nrow(A); if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } - options->n = n; + if (n > INT_MAX) { + IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); + } + + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + + options->n = (int) n; if (options->mode == 1) { IGRAPH_CHECK(igraph_arpack_rssolve(igraph_i_sparsemat_arpack_multiply, @@ -1603,7 +1728,7 @@ int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, /*-----------------------------------*/ /* Create (A-sigma*I) */ - IGRAPH_CHECK(igraph_sparsemat_eye(&eye, /*n=*/ n, /*nzmax=*/ n, + IGRAPH_CHECK(igraph_sparsemat_init_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, @@ -1642,7 +1767,7 @@ int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, IGRAPH_FINALLY_CLEAN(3); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1650,10 +1775,11 @@ int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, * \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. + * igraph_arpack_rnsolve(). Supply \c NULL here to use the defaults. + * 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. @@ -1669,19 +1795,27 @@ int igraph_sparsemat_arpack_rssolve(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, +igraph_error_t 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); + igraph_integer_t n = igraph_sparsemat_nrow(A); + + if (n > INT_MAX) { + IGRAPH_ERROR("Matrix too large for ARPACK", IGRAPH_EOVERFLOW); + } if (n != igraph_sparsemat_ncol(A)) { IGRAPH_ERROR("Non-square matrix for ARPACK", IGRAPH_NONSQUARE); } - options->n = n; + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + + options->n = (int) n; return igraph_arpack_rnsolve(igraph_i_sparsemat_arpack_multiply, (void*) A, options, storage, @@ -1695,6 +1829,7 @@ int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, * 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 @@ -1708,15 +1843,15 @@ int igraph_sparsemat_arpack_rnsolve(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_symbqr(igraph_integer_t order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { - dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 1); + dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 1); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic QR decomposition", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1725,6 +1860,7 @@ int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, * * 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 @@ -1738,15 +1874,15 @@ int igraph_sparsemat_symbqr(long int order, const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_symblu(igraph_integer_t order, const igraph_sparsemat_t *A, igraph_sparsemat_symbolic_t *dis) { - dis->symbolic = cs_sqr((int) order, A->cs, /*qr=*/ 0); + dis->symbolic = cs_sqr(order, A->cs, /*qr=*/ 0); if (!dis->symbolic) { IGRAPH_ERROR("Cannot do symbolic LU decomposition", IGRAPH_FAILURE); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1754,6 +1890,7 @@ int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, * \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. @@ -1768,14 +1905,14 @@ int igraph_sparsemat_symblu(long int order, const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_lu(const igraph_sparsemat_t *A, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -1783,6 +1920,7 @@ int igraph_sparsemat_lu(const igraph_sparsemat_t *A, * \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(). @@ -1797,14 +1935,14 @@ int igraph_sparsemat_lu(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_qr(const igraph_sparsemat_t *A, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -1812,6 +1950,7 @@ int igraph_sparsemat_qr(const igraph_sparsemat_t *A, * \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 @@ -1825,11 +1964,11 @@ int igraph_sparsemat_qr(const igraph_sparsemat_t *A, * Time complexity: TODO. */ -int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, +igraph_error_t 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_integer_t n = din->numeric->L->n; igraph_real_t *workspace; if (res != b) { @@ -1838,7 +1977,7 @@ int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, workspace = IGRAPH_CALLOC(n, igraph_real_t); if (!workspace) { - IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot LU (re)solve sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, workspace); @@ -1858,7 +1997,7 @@ int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, IGRAPH_FREE(workspace); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1867,6 +2006,7 @@ int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, * * 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 @@ -1880,13 +2020,13 @@ int igraph_sparsemat_luresol(const igraph_sparsemat_symbolic_t *dis, * Time complexity: TODO. */ -int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, +igraph_error_t 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_integer_t n = din->numeric->L->n; igraph_real_t *workspace; - int k; + igraph_integer_t k; if (res != b) { IGRAPH_CHECK(igraph_vector_update(res, b)); @@ -1917,7 +2057,7 @@ int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, IGRAPH_FREE(workspace); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1926,6 +2066,7 @@ int igraph_sparsemat_qrresol(const igraph_sparsemat_symbolic_t *dis, * * Frees the memory allocated by \ref igraph_sparsemat_symbqr() or * \ref igraph_sparsemat_symblu(). + * * \param dis The symbolic analysis. * * Time complexity: O(1). @@ -1942,6 +2083,7 @@ void igraph_sparsemat_symbolic_destroy(igraph_sparsemat_symbolic_t *dis) { * * 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). @@ -1968,12 +2110,12 @@ void igraph_sparsemat_numeric_destroy(igraph_sparsemat_numeric_t *din) { * matrix. */ -int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, +igraph_error_t 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; + igraph_integer_t nrow = igraph_matrix_nrow(mat); + igraph_integer_t ncol = igraph_matrix_ncol(mat); + igraph_integer_t i, j, nzmax = 0; for (i = 0; i < nrow; i++) { for (j = 0; j < ncol; j++) { @@ -1993,14 +2135,14 @@ int igraph_matrix_as_sparsemat(igraph_sparsemat_t *res, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, +static igraph_error_t 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); + igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); + igraph_integer_t ncol = igraph_sparsemat_ncol(spmat); CS_INT from = 0, to = 0; CS_INT *p = spmat->cs->p; CS_INT *i = spmat->cs->i; @@ -2021,13 +2163,13 @@ static int igraph_i_sparsemat_as_matrix_cc(igraph_matrix_t *res, p++; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, +static igraph_error_t 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); + igraph_integer_t nrow = igraph_sparsemat_nrow(spmat); + igraph_integer_t ncol = igraph_sparsemat_ncol(spmat); CS_INT *i = spmat->cs->p; CS_INT *j = spmat->cs->i; CS_ENTRY *x = spmat->cs->x; @@ -2041,7 +2183,7 @@ static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, MATRIX(*res, *j, *i) += *x; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2058,7 +2200,7 @@ static int igraph_i_sparsemat_as_matrix_triplet(igraph_matrix_t *res, * matrix. */ -int igraph_sparsemat_as_matrix(igraph_matrix_t *res, +igraph_error_t 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)); @@ -2086,7 +2228,7 @@ igraph_real_t igraph_sparsemat_max(igraph_sparsemat_t *A) { IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + n = igraph_i_sparsemat_count_elements(A); if (n == 0) { return IGRAPH_NEGINFINITY; } @@ -2122,7 +2264,7 @@ igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + n = igraph_i_sparsemat_count_elements(A); if (n == 0) { return IGRAPH_POSINFINITY; } @@ -2150,7 +2292,7 @@ igraph_real_t igraph_sparsemat_min(igraph_sparsemat_t *A) { */ -int igraph_sparsemat_minmax(igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_minmax(igraph_sparsemat_t *A, igraph_real_t *min, igraph_real_t *max) { CS_INT i, n; CS_ENTRY *ptr; @@ -2158,11 +2300,11 @@ int igraph_sparsemat_minmax(igraph_sparsemat_t *A, IGRAPH_CHECK(igraph_sparsemat_dupl(A)); ptr = A->cs->x; - n = A->cs->nz == -1 ? A->cs->p[A->cs->n] : A->cs->nz; + n = igraph_i_sparsemat_count_elements(A); if (n == 0) { *min = IGRAPH_POSINFINITY; *max = IGRAPH_NEGINFINITY; - return 0; + return IGRAPH_SUCCESS; } *min = *max = *ptr; for (i = 1; i < n; i++, ptr++) { @@ -2172,7 +2314,7 @@ int igraph_sparsemat_minmax(igraph_sparsemat_t *A, *min = *ptr; } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2185,15 +2327,15 @@ int igraph_sparsemat_minmax(igraph_sparsemat_t *A, * Time complexity: TODO. */ -long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { +igraph_integer_t igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { CS_INT i, n; CS_ENTRY *ptr; - int res = 0; + igraph_integer_t 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; + n = igraph_i_sparsemat_count_elements(A); if (n == 0) { return 0; } @@ -2218,16 +2360,16 @@ long int igraph_sparsemat_count_nonzero(igraph_sparsemat_t *A) { * Time complexity: TODO. */ -long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, +igraph_integer_t igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, igraph_real_t tol) { CS_INT i, n; CS_ENTRY *ptr; - int res = 0; + igraph_integer_t 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; + n = igraph_i_sparsemat_count_elements(A); if (n == 0) { return 0; } @@ -2239,7 +2381,7 @@ long int igraph_sparsemat_count_nonzerotol(igraph_sparsemat_t *A, return res; } -static int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, igraph_vector_t *res) { CS_INT i; CS_INT *pi = A->cs->i; @@ -2252,10 +2394,10 @@ static int igraph_i_sparsemat_rowsums_triplet(const igraph_sparsemat_t *A, VECTOR(*res)[ *pi ] += *px; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, +static igraph_error_t 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; @@ -2268,7 +2410,7 @@ static int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, VECTOR(*res)[ *pi ] += *px; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2283,7 +2425,7 @@ static int igraph_i_sparsemat_rowsums_cc(const igraph_sparsemat_t *A, * Time complexity: O(nz), the number of non-zero elements. */ -int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2292,15 +2434,14 @@ int igraph_sparsemat_rowsums(const igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A, +static igraph_error_t 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); + igraph_vector_fill(res, IGRAPH_INFINITY); for (i = 0; i < A->cs->nz; i++, pi++, px++) { if (*px < VECTOR(*res)[ *pi ]) { @@ -2308,15 +2449,14 @@ static int igraph_i_sparsemat_rowmins_triplet(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, +static igraph_error_t 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)); @@ -2325,7 +2465,7 @@ static int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); - igraph_vector_fill(res, inf); + igraph_vector_fill(res, IGRAPH_INFINITY); for (; pi < A->cs->i + ne; pi++, px++) { if (*px < VECTOR(*res)[ *pi ]) { @@ -2333,10 +2473,10 @@ static int igraph_i_sparsemat_rowmins_cc(igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2346,15 +2486,14 @@ int igraph_sparsemat_rowmins(igraph_sparsemat_t *A, } -static int igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A, +static igraph_error_t 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); + igraph_vector_fill(res, -IGRAPH_INFINITY); for (i = 0; i < A->cs->nz; i++, pi++, px++) { if (*px > VECTOR(*res)[ *pi ]) { @@ -2362,15 +2501,14 @@ static int igraph_i_sparsemat_rowmaxs_triplet(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, +static igraph_error_t 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)); @@ -2379,7 +2517,7 @@ static int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, A->cs->m)); - igraph_vector_fill(res, inf); + igraph_vector_fill(res, -IGRAPH_INFINITY); for (; pi < A->cs->i + ne; pi++, px++) { if (*px > VECTOR(*res)[ *pi ]) { @@ -2387,10 +2525,10 @@ static int igraph_i_sparsemat_rowmaxs_cc(igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2399,15 +2537,14 @@ int igraph_sparsemat_rowmaxs(igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A, +static igraph_error_t 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); + igraph_vector_fill(res, IGRAPH_INFINITY); for (i = 0; i < A->cs->nz; i++, pp++, px++) { if (*px < VECTOR(*res)[ *pp ]) { @@ -2415,17 +2552,16 @@ static int igraph_i_sparsemat_colmins_triplet(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, +static igraph_error_t 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)); @@ -2435,7 +2571,7 @@ static int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, inf); + igraph_vector_fill(res, IGRAPH_INFINITY); pr = VECTOR(*res); for (; pp < A->cs->p + n; pp++, pr++) { @@ -2445,10 +2581,10 @@ static int igraph_i_sparsemat_colmins_cc(igraph_sparsemat_t *A, } } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_colmins(igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2457,15 +2593,14 @@ int igraph_sparsemat_colmins(igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A, +static igraph_error_t 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); + igraph_vector_fill(res, -IGRAPH_INFINITY); for (i = 0; i < A->cs->nz; i++, pp++, px++) { if (*px > VECTOR(*res)[ *pp ]) { @@ -2473,17 +2608,16 @@ static int igraph_i_sparsemat_colmaxs_triplet(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, +static igraph_error_t 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)); @@ -2493,7 +2627,7 @@ static int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, pi = A->cs->i; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, inf); + igraph_vector_fill(res, -IGRAPH_INFINITY); pr = VECTOR(*res); for (; pp < A->cs->p + n; pp++, pr++) { @@ -2503,10 +2637,10 @@ static int igraph_i_sparsemat_colmaxs_cc(igraph_sparsemat_t *A, } } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2515,18 +2649,17 @@ int igraph_sparsemat_colmaxs(igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A, +static igraph_error_t 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_fill(res, IGRAPH_INFINITY); igraph_vector_int_null(pos); for (i = 0; i < A->cs->nz; i++, pi++, px++, pp++) { @@ -2536,18 +2669,17 @@ static int igraph_i_sparsemat_which_min_rows_triplet(igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, +static igraph_error_t 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_integer_t j; IGRAPH_CHECK(igraph_sparsemat_dupl(A)); @@ -2558,7 +2690,7 @@ static int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, 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_fill(res, IGRAPH_INFINITY); igraph_vector_int_null(pos); for (j = 0; pp < A->cs->p + n; pp++, j++) { @@ -2570,10 +2702,10 @@ static int igraph_i_sparsemat_which_min_rows_cc(igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos) { if (igraph_sparsemat_is_triplet(A)) { @@ -2583,7 +2715,7 @@ int igraph_sparsemat_which_min_rows(igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos) { @@ -2591,11 +2723,10 @@ static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, 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_fill(res, IGRAPH_INFINITY); igraph_vector_int_null(pos); for (i = 0; i < A->cs->nz; i++, pi++, pp++, px++) { @@ -2605,17 +2736,16 @@ static int igraph_i_sparsemat_which_min_cols_triplet(igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, +static igraph_error_t 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)); @@ -2623,7 +2753,7 @@ static int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, px = A->cs->x; IGRAPH_CHECK(igraph_vector_resize(res, n)); - igraph_vector_fill(res, inf); + igraph_vector_fill(res, IGRAPH_INFINITY); pr = VECTOR(*res); IGRAPH_CHECK(igraph_vector_int_resize(pos, n)); igraph_vector_int_null(pos); @@ -2637,10 +2767,10 @@ static int igraph_i_sparsemat_which_min_cols_cc(igraph_sparsemat_t *A, } } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, igraph_vector_t *res, igraph_vector_int_t *pos) { if (igraph_sparsemat_is_triplet(A)) { @@ -2650,7 +2780,7 @@ int igraph_sparsemat_which_min_cols(igraph_sparsemat_t *A, } } -static int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, +static igraph_error_t igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, igraph_vector_t *res) { CS_INT i; CS_INT *pp = A->cs->p; @@ -2663,10 +2793,10 @@ static int igraph_i_sparsemat_colsums_triplet(const igraph_sparsemat_t *A, VECTOR(*res)[ *pp ] += *px; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, +static igraph_error_t 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; @@ -2683,7 +2813,7 @@ static int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, *pr += *px; } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2700,7 +2830,7 @@ static int igraph_i_sparsemat_colsums_cc(const igraph_sparsemat_t *A, * the number of columns. */ -int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, +igraph_error_t 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); @@ -2722,17 +2852,16 @@ int igraph_sparsemat_colsums(const igraph_sparsemat_t *A, * matrix. */ -int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { +igraph_error_t 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; + CS_ENTRY *stop = px + igraph_i_sparsemat_count_elements(A); for (; px < stop; px++) { *px *= by; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2748,9 +2877,9 @@ int igraph_sparsemat_scale(igraph_sparsemat_t *A, igraph_real_t by) { * Time complexity: O(1). */ -int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) { +igraph_error_t igraph_sparsemat_add_rows(igraph_sparsemat_t *A, igraph_integer_t n) { A->cs->m += n; - return 0; + return IGRAPH_SUCCESS; } /** @@ -2766,14 +2895,14 @@ int igraph_sparsemat_add_rows(igraph_sparsemat_t *A, long int n) { * Time complexity: TODO. */ -int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) { +igraph_error_t igraph_sparsemat_add_cols(igraph_sparsemat_t *A, igraph_integer_t 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); + CS_INT *newp = cs_realloc(A->cs->p, (A->cs->n + n + 1), sizeof(CS_INT), &realloc_ok); if (!realloc_ok) { - IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add columns to sparse matrix", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } if (newp != A->cs->p) { A->cs->p = newp; @@ -2783,15 +2912,15 @@ int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) { } A->cs->n += n; } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sparsemat_resize - * \brief Resizes a sparse matrix. + * \brief Resizes a sparse matrix and clears all the elements. * * This function resizes a sparse matrix. The resized sparse matrix - * will be empty. + * will become empty, even if it contained nonzero entries. * * \param A The initialized sparse matrix to resize. * \param nrow The new number of rows. @@ -2802,21 +2931,21 @@ int igraph_sparsemat_add_cols(igraph_sparsemat_t *A, long int n) { * 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) { +igraph_error_t igraph_sparsemat_resize(igraph_sparsemat_t *A, igraph_integer_t nrow, + igraph_integer_t ncol, igraph_integer_t nzmax) { - if (A->cs->nz < 0) { + if (igraph_sparsemat_is_cc(A)) { igraph_sparsemat_t tmp; - IGRAPH_CHECK(igraph_sparsemat_init(&tmp, (int) nrow, (int) ncol, nzmax)); + IGRAPH_CHECK(igraph_sparsemat_init(&tmp, nrow, 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->m = nrow; + A->cs->n = ncol; A->cs->nz = 0; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2835,15 +2964,38 @@ int igraph_sparsemat_resize(igraph_sparsemat_t *A, long int nrow, * 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; - } +igraph_integer_t igraph_sparsemat_nonzero_storage(const igraph_sparsemat_t *A) { + return igraph_i_sparsemat_count_elements(A); } -int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, + +/** + * \function igraph_sparsemat_getelements + * \brief Returns all elements of a sparse matrix. + * + * This function will return the elements of a sparse matrix in three vectors. + * Two vectors will indicate where the elements are located, and one will + * specify the elements themselves. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param i An initialized integer vector. This will store the rows of the + * returned elements. + * \param j An initialized integer 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. + */ + +igraph_error_t igraph_sparsemat_getelements(const igraph_sparsemat_t *A, igraph_vector_int_t *i, igraph_vector_int_t *j, igraph_vector_t *x) { @@ -2853,25 +3005,25 @@ int igraph_sparsemat_getelements(const igraph_sparsemat_t *A, 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)); + memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(CS_INT)); + memcpy(VECTOR(*j), A->cs->p, (size_t) (A->cs->n + 1) * sizeof(CS_INT)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); } 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)); + memcpy(VECTOR(*i), A->cs->i, (size_t) nz * sizeof(CS_INT)); + memcpy(VECTOR(*j), A->cs->p, (size_t) nz * sizeof(CS_INT)); + memcpy(VECTOR(*x), A->cs->x, (size_t) nz * sizeof(CS_ENTRY)); } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, +igraph_error_t 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 no_of_edges = igraph_i_sparsemat_count_elements(A); CS_INT e; for (e = 0; e < no_of_edges; e++, x++, i++) { @@ -2879,10 +3031,10 @@ int igraph_sparsemat_scale_rows(igraph_sparsemat_t *A, (*x) *= f; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, +static igraph_error_t 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; @@ -2899,10 +3051,10 @@ static int igraph_i_sparsemat_scale_cols_cc(igraph_sparsemat_t *A, (*x) *= f; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, +static igraph_error_t 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; @@ -2914,26 +3066,26 @@ static int igraph_i_sparsemat_scale_cols_triplet(igraph_sparsemat_t *A, (*x) *= f; } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, +igraph_error_t igraph_sparsemat_scale_cols(igraph_sparsemat_t *A, const igraph_vector_t *fact) { - if (A->cs->nz < 0) { + if (igraph_sparsemat_is_cc(A)) { 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, +igraph_error_t 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; + igraph_integer_t m = igraph_sparsemat_nrow(A); + igraph_integer_t n = igraph_sparsemat_ncol(A); + igraph_integer_t p = igraph_matrix_ncol(B); + igraph_integer_t i; if (igraph_matrix_nrow(B) != n) { IGRAPH_ERROR("Invalid dimensions in sparse-dense matrix product", @@ -2950,16 +3102,16 @@ int igraph_sparsemat_multiply_by_dense(const igraph_sparsemat_t *A, } } - return 0; + return IGRAPH_SUCCESS; } -int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, +igraph_error_t 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; + igraph_integer_t m = igraph_matrix_nrow(A); + igraph_integer_t n = igraph_matrix_ncol(A); + igraph_integer_t p = igraph_sparsemat_ncol(B); + igraph_integer_t r, c; CS_INT *Bp = B->cs->p; if (igraph_sparsemat_nrow(B) != n) { @@ -2977,7 +3129,7 @@ int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, for (c = 0; c < p; c++) { for (r = 0; r < m; r++) { - int idx = *Bp; + igraph_integer_t idx = *Bp; while (idx < * (Bp + 1)) { MATRIX(*res, r, c) += MATRIX(*A, r, B->cs->i[idx]) * B->cs->x[idx]; idx++; @@ -2986,7 +3138,7 @@ int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, Bp++; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -3039,53 +3191,108 @@ int igraph_sparsemat_dense_multiply(const igraph_matrix_t *A, * 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) { +igraph_error_t igraph_sparsemat_view(igraph_sparsemat_t *A, igraph_integer_t nzmax, igraph_integer_t m, igraph_integer_t n, + igraph_integer_t *p, igraph_integer_t *i, igraph_real_t *x, igraph_integer_t nz) { - A->cs = IGRAPH_CALLOC(1, cs_di); + A->cs = IGRAPH_CALLOC(1, cs_igraph); A->cs->nzmax = nzmax; A->cs->m = m; A->cs->n = n; - A->cs->p = p; - A->cs->i = i; + A->cs->p = (CS_INT*) p; + A->cs->i = (CS_INT*) 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) { +/** + * \function igraph_sparsemat_sort + * \brief Sorts all elements of a sparse matrix by row and column indices. + * + * This function will sort the elements of a sparse matrix such that iterating + * over the entries will return them sorted by column indices; elements in the + * same column are then sorted by row indices. + * + * \param A A sparse matrix in either triplet or compressed form. + * \param sorted An uninitialized sparse matrix; the result will be returned + * here. The result will be in triplet form if the input was in triplet + * form, otherwise it will be in compressed form. Note that sorting is + * more efficient when the matrix is already in compressed form. + * \return Error code. + * + * Time complexity: TODO + */ +igraph_error_t igraph_sparsemat_sort(const igraph_sparsemat_t *A, + igraph_sparsemat_t *sorted) { igraph_sparsemat_t tmp; + igraph_sparsemat_t tmp2; - 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); + if (igraph_sparsemat_is_cc(A)) { + /* for column-compressed matrices, we will transpose the matrix twice, + * which will sort the indices as a side effect */ + IGRAPH_CHECK(igraph_sparsemat_transpose(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_transpose(&tmp, sorted)); + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_sparsemat_iterator_t it; + + /* for triplet matrices, we convert it to compressed column representation, + * sort it, then we convert back */ + IGRAPH_CHECK(igraph_sparsemat_compress(A, &tmp)); + IGRAPH_FINALLY(igraph_sparsemat_destroy, &tmp); + IGRAPH_CHECK(igraph_sparsemat_sort(&tmp, &tmp2)); + + igraph_sparsemat_destroy(&tmp); + tmp = tmp2; /* tmp is still protected in the FINALLY stack */ + + IGRAPH_CHECK(igraph_sparsemat_init( + sorted, + igraph_sparsemat_nrow(&tmp), + igraph_sparsemat_ncol(&tmp), + igraph_i_sparsemat_count_elements(&tmp) + )); + IGRAPH_FINALLY(igraph_sparsemat_destroy, sorted); + + IGRAPH_CHECK(igraph_sparsemat_iterator_init(&it, &tmp)); + while (!igraph_sparsemat_iterator_end(&it)) { + IGRAPH_CHECK(igraph_sparsemat_entry( + sorted, + igraph_sparsemat_iterator_row(&it), + igraph_sparsemat_iterator_col(&it), + igraph_sparsemat_iterator_get(&it) + )); + igraph_sparsemat_iterator_next(&it); + } + + igraph_sparsemat_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* tmp + sorted */ + } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sparsemat_getelements_sorted - * \brief Returns the sorted elements of a sparse matrix. + * \brief Returns all elements of a sparse matrix, sorted by row and column indices. + * + * This function will sort a sparse matrix and return the elements in three + * vectors. Two vectors will indicate where the elements are located, + * and one will specify the elements themselves. * - * 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. + * + * Sorting is done based on the \em indices of the elements, not their + * numeric values. The returned entries will be sorted by column indices; + * entries in the same column are then sorted by row indices. * * \param A A sparse matrix in either triplet or compressed form. - * \param i An initialized int vector. This will store the rows of the + * \param i An initialized integer vector. This will store the rows of the * returned elements. - * \param j An initialized int vector. For a triplet matrix this will + * \param j An initialized integer 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 @@ -3097,40 +3304,115 @@ int igraph_sparsemat_sort(const igraph_sparsemat_t *A, * \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. + * Time complexity: TODO. */ -int igraph_sparsemat_getelements_sorted(const igraph_sparsemat_t *A, +igraph_error_t 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)); - } + 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); + + /* TODO: in triplets format, we could in theory sort the entries without + * going through an extra sorting step (which temporarily converts the + * matrix into compressed format). This is not implemented yet. */ return IGRAPH_SUCCESS; } -int igraph_sparsemat_nzmax(const igraph_sparsemat_t *A) { +igraph_integer_t 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; +igraph_error_t igraph_sparsemat_neg(igraph_sparsemat_t *A) { + CS_INT i; + CS_INT nz = igraph_i_sparsemat_count_elements(A); CS_ENTRY *px = A->cs->x; for (i = 0; i < nz; i++, px++) { *px = - (*px); } - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_normalize_cols + * \brief Normalizes the column sums of a sparse matrix to a given value. + * + * \param sparsemat the sparse matrix to normalize + * \param allow_zeros whether to allow columns with zero sums + * \return \c IGRAPH_SUCCESS if everything was successful, + * \c IGRAPH_EINVAL if there is at least one column with zero sum and it + * is disallowed, + * \c IGRAPH_ENOMEM for out-of-memory conditions + */ + +igraph_error_t igraph_sparsemat_normalize_cols( + igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros +) { + igraph_vector_t sum; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); + igraph_integer_t i; + + IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); + + IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] != 0.0) { + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } else if (!allow_zeros) { + IGRAPH_ERROR("Columns with zero sum are not allowed", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum)); + + igraph_vector_destroy(&sum); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_sparsemat_normalize_rows + * \brief Normalizes the row sums of a sparse matrix to a given value. + * + * \param sparsemat the sparse matrix to normalize + * \param allow_zeros whether to allow rows with zero sums + * \return \c IGRAPH_SUCCESS if everything was successful, + * \c IGRAPH_EINVAL if there is at least one row with zero sum and it + * is disallowed, + * \c IGRAPH_ENOMEM for out-of-memory conditions + */ + +igraph_error_t igraph_sparsemat_normalize_rows( + igraph_sparsemat_t *sparsemat, igraph_bool_t allow_zeros +) { + igraph_vector_t sum; + igraph_integer_t no_of_nodes = igraph_sparsemat_nrow(sparsemat); + igraph_integer_t i; + + IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); + + IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum)); + for (i = 0; i < no_of_nodes; i++) { + if (VECTOR(sum)[i] != 0.0) { + VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + } else if (!allow_zeros) { + IGRAPH_ERROR("Rows with zero sum are not allowed", IGRAPH_EINVAL); + } + } + IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum)); + + igraph_vector_destroy(&sum); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /** @@ -3144,8 +3426,9 @@ int igraph_sparsemat_neg(igraph_sparsemat_t *A) { * 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) { +igraph_error_t igraph_sparsemat_iterator_init( + igraph_sparsemat_iterator_t *it, const igraph_sparsemat_t *sparsemat +) { it->mat = sparsemat; igraph_sparsemat_iterator_reset(it); @@ -3162,7 +3445,7 @@ int igraph_sparsemat_iterator_init(igraph_sparsemat_iterator_t *it, * Time complexity: O(n), the number of columns of the sparse matrix. */ -int igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { +igraph_error_t igraph_sparsemat_iterator_reset(igraph_sparsemat_iterator_t *it) { it->pos = 0; it->col = 0; if (!igraph_sparsemat_is_triplet(it->mat)) { @@ -3202,7 +3485,7 @@ igraph_sparsemat_iterator_end(const igraph_sparsemat_iterator_t *it) { * Time complexity: O(1). */ -int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { +igraph_integer_t igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { return it->mat->cs->i[it->pos]; } @@ -3216,7 +3499,7 @@ int igraph_sparsemat_iterator_row(const igraph_sparsemat_iterator_t *it) { * Time complexity: O(1). */ -int igraph_sparsemat_iterator_col(const igraph_sparsemat_iterator_t *it) { +igraph_integer_t 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 { @@ -3249,7 +3532,7 @@ igraph_sparsemat_iterator_get(const igraph_sparsemat_iterator_t *it) { * Time complexity: O(n), the number of columns of the sparse matrix. */ -int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) { +igraph_integer_t 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) { @@ -3268,6 +3551,6 @@ int igraph_sparsemat_iterator_next(igraph_sparsemat_iterator_t *it) { * Time complexity: O(1). */ -int igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { +igraph_integer_t igraph_sparsemat_iterator_idx(const igraph_sparsemat_iterator_t *it) { return it->pos; } diff --git a/src/vendor/cigraph/src/core/spmatrix.c b/src/vendor/cigraph/src/core/spmatrix.c deleted file mode 100644 index 7d2a7ec2637..00000000000 --- a/src/vendor/cigraph/src/core/spmatrix.c +++ /dev/null @@ -1,1066 +0,0 @@ -/* -*- 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/vendor/cigraph/src/core/stack.c b/src/vendor/cigraph/src/core/stack.c index 66ce72939da..69b3b3efef9 100644 --- a/src/vendor/cigraph/src/core/stack.c +++ b/src/vendor/cigraph/src/core/stack.c @@ -21,7 +21,6 @@ */ -#include "igraph_error.h" #include "igraph_types.h" #include "igraph_stack.h" @@ -31,12 +30,6 @@ #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" @@ -54,35 +47,3 @@ #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/vendor/cigraph/src/core/stack.pmt b/src/vendor/cigraph/src/core/stack.pmt index dc57fcc4804..4be8cff534d 100644 --- a/src/vendor/cigraph/src/core/stack.pmt +++ b/src/vendor/cigraph/src/core/stack.pmt @@ -24,7 +24,6 @@ #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" -#include "config.h" #include /* memcpy & co. */ #include @@ -35,28 +34,27 @@ * \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. + * \param capacity 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_error_t FUNCTION(igraph_stack, init)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { + igraph_integer_t alloc_size; + IGRAPH_ASSERT(capacity >= 0); + alloc_size = capacity > 0 ? capacity : 1; 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); + if (s->stor_begin == NULL) { + IGRAPH_ERROR("Cannot initialize stack.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } s->stor_end = s->stor_begin + alloc_size; s->end = s->stor_begin; - return 0; + return IGRAPH_SUCCESS; } /** @@ -74,12 +72,34 @@ int FUNCTION(igraph_stack, init) (TYPE(igraph_stack)* s, long int size) { void FUNCTION(igraph_stack, destroy) (TYPE(igraph_stack)* s) { IGRAPH_ASSERT(s != NULL); - if (s->stor_begin != 0) { + if (s->stor_begin != NULL) { IGRAPH_FREE(s->stor_begin); s->stor_begin = NULL; } } +/** + * \ingroup stack + * \function igraph_stack_capacity + * \brief Returns the allocated capacity of the stack. + * + * Note that this might be different from the size of the stack (as + * queried by \ref igraph_stack_size()), and specifies how many elements + * the stack can hold, without reallocation. + * + * \param v Pointer to the (previously initialized) stack object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_stack_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(igraph_stack, capacity)(const TYPE(igraph_stack) *s) { + return s->stor_end - s->stor_begin; +} + /** * \ingroup stack * \function igraph_stack_reserve @@ -96,25 +116,28 @@ void FUNCTION(igraph_stack, destroy) (TYPE(igraph_stack)* s) { * the stack. */ -int FUNCTION(igraph_stack, reserve) (TYPE(igraph_stack)* s, long int size) { - long int actual_size = FUNCTION(igraph_stack, size)(s); +igraph_error_t FUNCTION(igraph_stack, reserve)(TYPE(igraph_stack)* s, igraph_integer_t capacity) { + igraph_integer_t current_capacity; BASE *tmp; + IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); - if (size <= actual_size) { - return 0; - } + current_capacity = FUNCTION(igraph_stack, capacity)(s); - tmp = IGRAPH_REALLOC(s->stor_begin, (size_t) size, BASE); - if (tmp == 0) { - IGRAPH_ERROR("stack reserve failed", IGRAPH_ENOMEM); + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; } + + tmp = IGRAPH_REALLOC(s->stor_begin, capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for stack."); + + s->end = tmp + (s->end - s->stor_begin); s->stor_begin = tmp; - s->stor_end = s->stor_begin + size; - s->end = s->stor_begin + actual_size; + s->stor_end = s->stor_begin + capacity; - return 0; + return IGRAPH_SUCCESS; } /** @@ -123,16 +146,15 @@ int FUNCTION(igraph_stack, reserve) (TYPE(igraph_stack)* s, long int size) { * \brief Decides whether a stack object is empty. * * \param s The stack object. - * \return Boolean, \c TRUE if the stack is empty, \c FALSE + * \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_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; } @@ -147,7 +169,7 @@ igraph_bool_t FUNCTION(igraph_stack, empty) (TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -long int FUNCTION(igraph_stack, size) (const TYPE(igraph_stack)* s) { +igraph_integer_t 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; @@ -163,7 +185,7 @@ long int FUNCTION(igraph_stack, size) (const TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -void FUNCTION(igraph_stack, clear) (TYPE(igraph_stack)* s) { +void FUNCTION(igraph_stack, clear)(TYPE(igraph_stack)* s) { IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); s->end = s->stor_begin; @@ -184,34 +206,27 @@ void FUNCTION(igraph_stack, clear) (TYPE(igraph_stack)* s) { * in O(n) time. */ -int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { +igraph_error_t 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); + if (s->stor_end == s->end) { + /* full, allocate more storage */ + igraph_integer_t old_size = FUNCTION(igraph_stack, size)(s); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to stack, already at maximum size.", IGRAPH_EOVERFLOW); } - 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; + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(igraph_stack, reserve)(s, new_size)); + } - *(s->end) = elem; - (s->end) += 1; + *(s->end) = elem; + s->end += 1; - IGRAPH_FREE(old); - } else { - *(s->end) = elem; - (s->end) += 1; - } - return 0; + return IGRAPH_SUCCESS; } /** @@ -227,8 +242,7 @@ int FUNCTION(igraph_stack, push)(TYPE(igraph_stack)* s, BASE elem) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_stack, pop) (TYPE(igraph_stack)* s) { - +BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s) { IGRAPH_ASSERT(s != NULL); IGRAPH_ASSERT(s->stor_begin != NULL); IGRAPH_ASSERT(s->end != NULL); @@ -252,8 +266,7 @@ BASE FUNCTION(igraph_stack, pop) (TYPE(igraph_stack)* s) { * Time complexity: O(1). */ -BASE FUNCTION(igraph_stack, top) (const TYPE(igraph_stack)* s) { - +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); @@ -262,32 +275,32 @@ BASE FUNCTION(igraph_stack, top) (const TYPE(igraph_stack)* s) { return *(s->end - 1); } -#if defined (OUT_FORMAT) -#ifndef USING_R +#if defined(OUT_FORMAT) || defined(FPRINTFUNC) -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; +#ifndef USING_R +igraph_error_t FUNCTION(igraph_stack, print)(const TYPE(igraph_stack) *s) { + return FUNCTION(igraph_stack, fprint)(s, stdout); } -#endif +#endif /* USING_R */ -int FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { - long int i, n = FUNCTION(igraph_stack, size)(s); +igraph_error_t FUNCTION(igraph_stack, fprint)(const TYPE(igraph_stack) *s, FILE *file) { + igraph_integer_t i, n = FUNCTION(igraph_stack, size)(s); if (n != 0) { +#ifdef FPRINTFUNC + FPRINTFUNC(file, s->stor_begin[0]); +#else fprintf(file, OUT_FORMAT, s->stor_begin[0]); +#endif } for (i = 1; i < n; i++) { +#ifdef FPRINTFUNC + fputc(' ', file); fprintf(file, OUT_FORMAT, s->stor_begin[i]); +#else fprintf(file, " " OUT_FORMAT, s->stor_begin[i]); +#endif } fprintf(file, "\n"); - return 0; + return IGRAPH_SUCCESS; } -#endif +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ diff --git a/src/vendor/cigraph/src/core/statusbar.c b/src/vendor/cigraph/src/core/statusbar.c index 07cdad28196..48e47b9492b 100644 --- a/src/vendor/cigraph/src/core/statusbar.c +++ b/src/vendor/cigraph/src/core/statusbar.c @@ -33,7 +33,7 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; /** * \function igraph_status - * Report status from an igraph function. + * \brief Reports 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 @@ -41,6 +41,7 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; * \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. @@ -51,7 +52,7 @@ static IGRAPH_THREAD_LOCAL igraph_status_handler_t *igraph_i_status_handler = 0; * Time complexity: O(1). */ -int igraph_status(const char *message, void *data) { +igraph_error_t igraph_status(const char *message, void *data) { if (igraph_i_status_handler) { if (igraph_i_status_handler(message, data) != IGRAPH_SUCCESS) { return IGRAPH_INTERRUPTED; @@ -62,7 +63,7 @@ int igraph_status(const char *message, void *data) { /** * \function igraph_statusf - * Report status, more flexible printf-like version. + * \brief 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. @@ -76,14 +77,15 @@ int igraph_status(const char *message, void *data) { * \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(). + * \c IGRAPH_INTERRUPTED is returned by \ref igraph_status(). */ -int igraph_statusf(const char *message, void *data, ...) { +igraph_error_t igraph_statusf(const char *message, void *data, ...) { char buffer[300]; va_list ap; va_start(ap, data); vsnprintf(buffer, sizeof(buffer) - 1, message, ap); + va_end(ap); return igraph_status(buffer, data); } @@ -93,8 +95,9 @@ int igraph_statusf(const char *message, void *data, ...) { * \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. + * A simple status handler function that writes the status + * message to the standard error. + * * \param message The status message. * \param data Additional context, with user-defined semantics. * Existing igraph functions pass a null pointer here. @@ -103,10 +106,10 @@ int igraph_statusf(const char *message, void *data, ...) { * Time complexity: O(1). */ -int igraph_status_handler_stderr(const char *message, void *data) { +igraph_error_t igraph_status_handler_stderr(const char *message, void *data) { IGRAPH_UNUSED(data); fputs(message, stderr); - return 0; + return IGRAPH_SUCCESS; } #endif diff --git a/src/vendor/cigraph/src/core/strvector.c b/src/vendor/cigraph/src/core/strvector.c index 080b3563ed8..3e268108837 100644 --- a/src/vendor/cigraph/src/core/strvector.c +++ b/src/vendor/cigraph/src/core/strvector.c @@ -26,17 +26,26 @@ #include "igraph_memory.h" #include "igraph_error.h" +#include "internal/hacks.h" /* strdup */ +#include "math/safe_intop.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. + * + * The igraph_strvector_t type is a vector of null-terminated + * strings. It is used internally for storing graph attribute names as well as + * string attributes in the C attribute handler. + * + * + * + * This container automatically manages the memory of its elements. + * The strings within an igraph_strvector_t should be considered + * constant, and not modified directly. Functions that add new elements + * always make copies of the string passed to them. + * * * * \example examples/simple/igraph_strvector.c @@ -46,11 +55,12 @@ /** * \ingroup strvector * \function igraph_strvector_init - * \brief Initialize + * \brief Initializes a string vector. * * 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. @@ -58,29 +68,35 @@ * 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); +igraph_error_t igraph_strvector_init(igraph_strvector_t *sv, igraph_integer_t size) { + + sv->stor_begin = IGRAPH_CALLOC(size, char*); + IGRAPH_CHECK_OOM(sv->stor_begin, "Cannot initialize string vector."); + + for (igraph_integer_t i = 0; i < size; i++) { + sv->stor_begin[i] = IGRAPH_CALLOC(1, char); + if (sv->stor_begin[i] == NULL) { + /* LCOV_EXCL_START */ + for (igraph_integer_t j = 0; j < i; j++) { + IGRAPH_FREE(sv->stor_begin[j]); + } + IGRAPH_FREE(sv->stor_begin); + IGRAPH_ERROR("Cannot initialize string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + /* LCOV_EXCL_STOP */ } - sv->data[i][0] = '\0'; + sv->stor_begin[i][0] = '\0'; } - sv->len = len; - return 0; + sv->stor_end = sv->stor_begin + size; + sv->end = sv->stor_end; + + return IGRAPH_SUCCESS; } /** * \ingroup strvector * \function igraph_strvector_destroy - * \brief Free allocated memory + * \brief Frees the memory allocated for the string vector. * * Destroy a string vector. It may be reinitialized with \ref * igraph_strvector_init() later. @@ -91,48 +107,44 @@ int igraph_strvector_init(igraph_strvector_t *sv, long int len) { */ 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); + char **ptr; + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + for (ptr = sv->stor_begin; ptr < sv->end; ptr++) { + IGRAPH_FREE(*ptr); } + IGRAPH_FREE(sv->stor_begin); } /** * \ingroup strvector * \function igraph_strvector_get - * \brief Indexing + * \brief Retrieves an element of the string vector. * * 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]; +const char *igraph_strvector_get(const igraph_strvector_t *sv, igraph_integer_t idx) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + IGRAPH_ASSERT(sv->stor_begin[idx] != NULL); + return sv->stor_begin[idx]; } /** * \ingroup strvector * \function igraph_strvector_set - * \brief Set an element + * \brief Sets an element of the string vector from a string. * * 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. @@ -142,38 +154,19 @@ void igraph_strvector_get(const igraph_strvector_t *sv, long int idx, * depending on the memory management, if reallocation is needed. */ -int igraph_strvector_set(igraph_strvector_t *sv, long int idx, +igraph_error_t igraph_strvector_set(igraph_strvector_t *sv, igraph_integer_t 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; + return igraph_strvector_set_len(sv, idx, value, strlen(value)); } /** * \ingroup strvector - * \function igraph_strvector_set2 - * \brief Sets an element. + * \function igraph_strvector_set_len + * \brief Sets an element of the string vector given a buffer and its size. * * 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. @@ -183,27 +176,20 @@ int igraph_strvector_set(igraph_strvector_t *sv, long int idx, * 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'; +igraph_error_t igraph_strvector_set_len(igraph_strvector_t *sv, igraph_integer_t idx, + const char *value, size_t len) { + char *tmp; + + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + IGRAPH_ASSERT(sv->stor_begin[idx] != NULL); + + tmp = IGRAPH_REALLOC(sv->stor_begin[idx], len + 1, char); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new item in string vector."); + + sv->stor_begin[idx] = tmp; + memcpy(sv->stor_begin[idx], value, len * sizeof(char)); + sv->stor_begin[idx][len] = '\0'; return IGRAPH_SUCCESS; } @@ -212,33 +198,36 @@ int igraph_strvector_set2(igraph_strvector_t *sv, long int idx, * \ingroup strvector * \function igraph_strvector_remove_section * \brief Removes a section from a string vector. - * \todo repair realloc + * + * This function removes the range [from, to) from the string vector. + * + * \param sv The string vector. + * \param from The position of the first element to remove. + * \param to The position of the first element \em not to remove. */ -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); +void igraph_strvector_remove_section( + igraph_strvector_t *sv, igraph_integer_t from, igraph_integer_t to) { + igraph_integer_t size = igraph_strvector_size(sv); + igraph_integer_t i; - for (i = from; i < to; i++) { - if (v->data[i] != 0) { - IGRAPH_FREE(v->data[i]); - } + if (from < 0) { + from = 0; } - for (i = 0; i < v->len - to; i++) { - v->data[from + i] = v->data[to + i]; + + if (to > size) { + to = size; } - v->len -= (to - from); + if (to > from) { + for (i = from; i < to; i++) { + IGRAPH_FREE(sv->stor_begin[i]); + } - /* try to make it smaller */ - /* tmp=IGRAPH_REALLOC(v->data, v->len, char*); */ - /* if (tmp!=0) { */ - /* v->data=tmp; */ - /* } */ + memmove(sv->stor_begin + from, sv->stor_begin + to, + sizeof(char*) * (sv->end - sv->stor_begin - to)); + sv->end -= (to - from); + } } /** @@ -247,49 +236,23 @@ void igraph_strvector_remove_section(igraph_strvector_t *v, long int from, * \brief Removes a single element from a string vector. * * The string will be one shorter. - * \param v The string vector. + * \param sv 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); +void igraph_strvector_remove(igraph_strvector_t *sv, igraph_integer_t elem) { + igraph_strvector_remove_section(sv, 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 + * \function igraph_strvector_init_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. @@ -297,72 +260,139 @@ void igraph_strvector_move_interval(igraph_strvector_t *v, long int begin, * 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); +igraph_error_t igraph_strvector_init_copy(igraph_strvector_t *to, + const igraph_strvector_t *from) { + igraph_integer_t from_size = igraph_strvector_size(from); + + to->stor_begin = IGRAPH_CALLOC(from_size, char*); + IGRAPH_CHECK_OOM(to->stor_begin, "Cannot copy string vector."); + + for (igraph_integer_t i = 0; i < from_size; i++) { + to->stor_begin[i] = strdup(igraph_strvector_get(from, i)); + if (to->stor_begin[i] == NULL) { + /* LCOV_EXCL_START */ + for (igraph_integer_t j = 0; j < i; j++) { + IGRAPH_FREE(to->stor_begin[j]); + } + IGRAPH_FREE(to->stor_begin); + IGRAPH_ERROR("Cannot copy string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + /* LCOV_EXCL_STOP */ } } - return 0; + to->stor_end = to->stor_begin + from_size; + to->end = to->stor_end; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_copy + * \brief Initialization by copying (deprecated alias). + * + * \deprecated-by igraph_strvector_init_copy 0.10.0 + */ + +igraph_error_t igraph_strvector_copy(igraph_strvector_t *to, + const igraph_strvector_t *from) { + return igraph_strvector_init_copy(to, from); } /** * \function igraph_strvector_append - * Concatenate two string vectors. + * \brief Concatenates two string vectors. + * + * Appends the contents of the \p from vector to the \p to vector. + * If the \p from vector is no longer needed after this operation, + * use \ref igraph_strvector_merge() for better performance. * * \param to The first string vector, the result is stored here. * \param from The second string vector, it is kept unchanged. * \return Error code. * + * \sa \ref igraph_strvector_merge() + * * 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, +igraph_error_t 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; - } + igraph_integer_t len1 = igraph_strvector_size(to), len2 = igraph_strvector_size(from); + igraph_integer_t newlen; + igraph_bool_t error = false; + char *tmp; + + IGRAPH_SAFE_ADD(len1, len2, &newlen); + IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); + + for (igraph_integer_t i = 0; i < len2; i++) { + tmp = strdup(igraph_strvector_get(from, i)); + if (!tmp) { + error = true; + break; + } else { + *(to->end) = tmp; + to->end++; } } + if (error) { - igraph_strvector_resize(to, len1); - IGRAPH_ERROR("Cannot append string vector", IGRAPH_ENOMEM); + igraph_strvector_resize(to, len1); /* always shrinks */ + IGRAPH_ERROR("Cannot append string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_merge + * \brief Moves the contents of a string vector to the end of another. + * + * Transfers the contents of the \p from vector to the end of \p to, clearing + * \p from in the process. If this operation fails, both vectors are left intact. + * This function does not copy or reallocate individual strings, therefore it + * performs better than \ref igraph_strvector_append(). + * + * \param to The target vector. The contents of \p from will be appended to it. + * \param from The source vector. It will be cleared. + * \return Error code. + * + * \sa \ref igraph_strvector_append() + * + * Time complexity: O(l2) if \p to has sufficient capacity, O(2*l1+l2) otherwise, + * where l1 and l2 are the lengths of \p to and \from respectively. + */ +igraph_error_t igraph_strvector_merge(igraph_strvector_t *to, igraph_strvector_t *from) { + char **p1, **p2, **pe; + igraph_integer_t newlen; + + IGRAPH_SAFE_ADD(igraph_strvector_size(to), igraph_strvector_size(from), &newlen); + IGRAPH_CHECK(igraph_strvector_reserve(to, newlen)); + + /* transfer contents of 'from' to 'to */ + for (p1 = to->end, p2 = from->stor_begin, pe = to->stor_begin + newlen; + p1 < pe; ++p1, ++p2) + { + *p1 = *p2; } - return 0; + to->end = pe; + + /* clear 'from' */ + from->end = from->stor_begin; + + return IGRAPH_SUCCESS; } /** * \function igraph_strvector_clear - * Remove all elements + * \brief Removes all elements from a string vector. * * 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, @@ -370,28 +400,23 @@ int igraph_strvector_append(igraph_strvector_t *to, */ void igraph_strvector_clear(igraph_strvector_t *sv) { - long int i, n = igraph_strvector_size(sv); - char **tmp; + igraph_integer_t n = igraph_strvector_size(sv); - 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; + for (igraph_integer_t i = 0; i < n; i++) { + IGRAPH_FREE(sv->stor_begin[i]); } + sv->end = sv->stor_begin; } /** * \ingroup strvector * \function igraph_strvector_resize - * \brief Resize + * \brief Resizes a string vector. * * 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 sv The string vector. * \param newsize The new size. * \return Error code. * @@ -400,166 +425,245 @@ void igraph_strvector_clear(igraph_strvector_t *sv) { * 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; +igraph_error_t igraph_strvector_resize(igraph_strvector_t *sv, igraph_integer_t newsize) { + igraph_integer_t toadd = newsize - igraph_strvector_size(sv); + igraph_integer_t oldsize = igraph_strvector_size(sv); - 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 (newsize < oldsize) { + for (igraph_integer_t i = newsize; i < oldsize; i++) { + IGRAPH_FREE(sv->stor_begin[i]); } - 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]); + sv->end = sv->stor_begin + newsize; + } else if (newsize > oldsize) { + IGRAPH_CHECK(igraph_strvector_reserve(sv, newsize)); + + for (igraph_integer_t i = 0; i < toadd; i++) { + sv->stor_begin[oldsize + i] = IGRAPH_CALLOC(1, char); + if (sv->stor_begin[oldsize + i] == NULL) { + /* LCOV_EXCL_START */ + for (igraph_integer_t j = 0; j < i; j++) { + IGRAPH_FREE(sv->stor_begin[oldsize + j]); } + IGRAPH_ERROR("Cannot resize string vector.", IGRAPH_ENOMEM); + /* LCOV_EXCL_STOP */ } - /* 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); + sv->stor_begin[oldsize + i][0] = '\0'; } + sv->end = sv->stor_begin + newsize; } - v->len = newsize; - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup strvector - * \function igraph_strvector_size - * \brief Gives the size of a string vector. + * \function igraph_strvector_capacity + * \brief Returns the capacity of a string vector. * * \param sv The string vector. - * \return The length of the string vector. + * \return The capacity 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; +igraph_integer_t igraph_strvector_capacity(const igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + return sv->stor_end - sv->stor_begin; } /** * \ingroup strvector - * \function igraph_strvector_add - * \brief Adds an element to the back of a string vector. + * \function igraph_strvector_reserve + * \brief Reserves memory for a string vector. * - * \param v The string vector. - * \param value The string to add, it will be copied. - * \return Error code. + * + * \a igraph string 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. * - * Time complexity: O(n+l), n is the total number of strings, l is the - * length of the new string. + * + * Note that this function does \em not change the size of the + * string vector. Let us see a small example to clarify things: if you + * reserve space for 100 strings and the size of your + * vector was (and still is) 60, then you can surely add additional 40 + * strings to your vector before it will be copied. + * + * \param sv The string vector object. + * \param capacity The new \em allocated size of the string 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 igraph_strvector_add(igraph_strvector_t *v, const char *value) { - long int s = igraph_strvector_size(v); - long int value_len = strlen(value); +igraph_error_t igraph_strvector_reserve(igraph_strvector_t *sv, igraph_integer_t capacity) { + igraph_integer_t current_capacity = igraph_strvector_capacity(sv); 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); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; } - strcpy(v->data[s], value); - v->len += 1; - return 0; + tmp = IGRAPH_REALLOC(sv->stor_begin, capacity, char *); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for new items in string vector."); + + sv->end = tmp + (sv->end - sv->stor_begin); + sv->stor_begin = tmp; + sv->stor_end = sv->stor_begin + capacity; + + return IGRAPH_SUCCESS; } /** * \ingroup strvector - * \function igraph_strvector_permdelete - * \brief Removes elements from a string vector (for internal use) + * \function igraph_strvector_resize_min + * \brief Deallocates the unused memory of a string vector. + * + * This function attempts to deallocate the unused reserved storage + * of a string vector. If it succeeds, \ref igraph_strvector_size() and + * \ref igraph_strvector_capacity() will be the same. The data in the + * string vector is always preserved, even if deallocation is not successful. + * + * \param sv The string vector. + * + * Time complexity: Operating system dependent, at most O(n). */ -void igraph_strvector_permdelete(igraph_strvector_t *v, const igraph_vector_t *index, - long int nremove) { - long int i; +void igraph_strvector_resize_min(igraph_strvector_t *sv) { + igraph_integer_t size; 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]); - } + if (sv->stor_end == sv->end) { + return; } - /* 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; + + size = (sv->end - sv->stor_begin); + tmp = IGRAPH_REALLOC(sv->stor_begin, size, char *); + + if (tmp != NULL) { + sv->stor_begin = tmp; + sv->stor_end = sv->end = sv->stor_begin + size; } - v->len -= nremove; } /** * \ingroup strvector - * \function igraph_strvector_remove_negidx - * \brief Removes elements from a string vector (for internal use) + * \function igraph_strvector_size + * \brief Returns the size of a string vector. + * + * \param sv The string vector. + * \return The length of the string vector. + * + * Time complexity: O(1). */ -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]); +igraph_integer_t igraph_strvector_size(const igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + return sv->end - sv->stor_begin; +} + +/** + * Ensures that the vector has at least one extra slot at the end of its + * allocated storage area. + */ +static igraph_error_t igraph_i_strvector_expand_if_full(igraph_strvector_t *sv) { + IGRAPH_ASSERT(sv != NULL); + IGRAPH_ASSERT(sv->stor_begin != NULL); + + if (sv->stor_end == sv->end) { + igraph_integer_t old_size = igraph_strvector_size(sv); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add new item to string vector, already at maximum size.", IGRAPH_EOVERFLOW); } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(igraph_strvector_reserve(sv, new_size)); } - /* 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; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_push_back + * \brief Adds an element to the back of a string vector. + * + * \param sv 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. + */ + +igraph_error_t igraph_strvector_push_back(igraph_strvector_t *sv, const char *value) { + IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); + char *tmp = strdup(value); + IGRAPH_CHECK_OOM(tmp, "Cannot push new string to string vector."); + *sv->end = tmp; + sv->end++; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_push_back_len + * \brief Adds a string of the given length to the back of a string vector. + * + * \param sv The string vector. + * \param value The start of the string to add. At most \p len characters will be copied. + * \param len The length of the string. + * \return Error code. + * + * Time complexity: O(n+l), n is the total number of strings, l is the + * length of the new string. + */ + +igraph_error_t igraph_strvector_push_back_len( + igraph_strvector_t *sv, + const char *value, igraph_integer_t len) { + + IGRAPH_CHECK(igraph_i_strvector_expand_if_full(sv)); + char *tmp = strndup(value, len); + if (! tmp) { + IGRAPH_ERROR("Cannot add string to string vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - v->len -= nremove; + *sv->end = tmp; + sv->end++; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup strvector + * \function igraph_strvector_add + * \brief Adds an element to the back of a string vector (deprecated alias). + * + * \deprecated-by igraph_strvector_push_back 0.10.0 + */ + +igraph_error_t igraph_strvector_add(igraph_strvector_t *sv, const char *value) { + return igraph_strvector_push_back(sv, value); +} + +/** + * \ingroup strvector + * \function igraph_strvector_set2 + * \brief Sets an element of the string vector given a buffer and its size (deprecated alias). + * + * \deprecated-by igraph_strvector_set_len 0.10.0 + */ + +igraph_error_t igraph_strvector_set2( + igraph_strvector_t *sv, igraph_integer_t idx, const char *value, size_t len +) { + return igraph_strvector_set_len(sv, idx, value, len); } /** @@ -567,37 +671,46 @@ void igraph_strvector_remove_negidx(igraph_strvector_t *v, const igraph_vector_t * \function igraph_strvector_print * \brief Prints a string vector. * - * \param v The string vector. + * \param sv 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, +igraph_error_t igraph_strvector_print(const igraph_strvector_t *sv, FILE *file, const char *sep) { - long int i, n = igraph_strvector_size(v); + igraph_integer_t n = igraph_strvector_size(sv); if (n != 0) { - fprintf(file, "%s", STR(*v, 0)); + fprintf(file, "%s", STR(*sv, 0)); } - for (i = 1; i < n; i++) { - fprintf(file, "%s%s", sep, STR(*v, i)); + for (igraph_integer_t i = 1; i < n; i++) { + fprintf(file, "%s%s", sep, STR(*sv, i)); } return IGRAPH_SUCCESS; } -int igraph_strvector_index(const igraph_strvector_t *v, +/** + * \ingroup strvector + * \function igraph_strvector_set + * \brief Takes elements at given positions from a string vector. + * + * \param sv The string vector. + * \param newv An initialized string vector, it will be resized as needed. + * \param idx An integer vector of indices to take from \p sv. + * \return Error code. + */ +igraph_error_t igraph_strvector_index(const igraph_strvector_t *sv, igraph_strvector_t *newv, - const igraph_vector_t *idx) { + const igraph_vector_int_t *idx) { - long int i, newlen = igraph_vector_size(idx); + igraph_integer_t newlen = igraph_vector_int_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); + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_integer_t j = VECTOR(*idx)[i]; + const char *str = igraph_strvector_get(sv, j); + IGRAPH_CHECK(igraph_strvector_set(newv, i, str)); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/trie.c b/src/vendor/cigraph/src/core/trie.c index 1ea96870cf4..f9971519d2f 100644 --- a/src/vendor/cigraph/src/core/trie.c +++ b/src/vendor/cigraph/src/core/trie.c @@ -23,28 +23,27 @@ #include "igraph_types.h" #include "igraph_memory.h" -#include "igraph_random.h" -#include "igraph_error.h" #include "core/trie.h" - -#include "config.h" +#include "internal/hacks.h" /* strdup */ #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. + +/* + * igraph_trie_t is a data structures that stores an ordered list of strings. + * It allows an efficient lookup of the index of a string. It has the capability + * to also store the list of strings directly for reverse lookup of strings + * by index. */ -static int igraph_i_trie_init_node(igraph_trie_node_t *t) { +/* Allocates memory for a trie node. */ +static igraph_error_t 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_VECTOR_INT_INIT_FINALLY(&t->values, 0); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } static void igraph_i_trie_destroy_node(igraph_trie_node_t *t); @@ -52,44 +51,44 @@ 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. + * + * \param t An uninitialized trie. + * \param storekeys Specifies whether keys are stored for reverse lookup. + * \return Error code: Errors by \ref igraph_strvector_init(), + * \ref igraph_vector_ptr_init() and \ref igraph_vector_init() might be returned. */ -int igraph_trie_init(igraph_trie_t *t, igraph_bool_t storekeys) { +igraph_error_t 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 ); + IGRAPH_CHECK(igraph_i_trie_init_node(&t->node)); + IGRAPH_FINALLY(igraph_i_trie_destroy_node, &t->node); if (storekeys) { IGRAPH_CHECK(igraph_strvector_init(&t->keys, 0)); } IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -/** - * \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_integer_t i; igraph_strvector_destroy(&t->strs); - for (i = 0; i < igraph_vector_ptr_size(&t->children); i++) { + igraph_integer_t children_size = igraph_vector_ptr_size(&t->children); + for (i = 0; i < children_size; 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); + igraph_vector_int_destroy(&t->values); if (sfree) { IGRAPH_FREE(t); } } +/* Deallocates a trie node. */ static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { igraph_i_trie_destroy_node_helper(t, 0); } @@ -97,24 +96,21 @@ static void igraph_i_trie_destroy_node(igraph_trie_node_t *t) { /** * \ingroup igraphtrie * \brief Destroys a trie (frees allocated memory). + * + * \param t The trie. */ 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); + igraph_i_trie_destroy_node(&t->node); } -/** - * \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; +/* Computes the location (index) of the first difference between 'str' and 'key' */ +static size_t igraph_i_strdiff(const char *str, const char *key) { + size_t diff = 0; while (key[diff] != '\0' && str[diff] != '\0' && str[diff] == key[diff]) { diff++; } @@ -125,23 +121,25 @@ static long int igraph_i_strdiff(const char *str, const char *key) { * \ingroup igraphtrie * \brief Search/insert in a trie (not to be called directly). * - * @return Error code: - * - IGRAPH_ENOMEM: out of memory + * \return Error code, usually \c IGRAPH_ENOMEM. */ -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; +static igraph_error_t igraph_i_trie_get_node( + igraph_trie_node_t *t, const char *key, igraph_integer_t newvalue, + igraph_integer_t *id +) { + const char *str; + igraph_integer_t 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); + igraph_integer_t strs_size = igraph_strvector_size(&t->strs); + for (i = 0; i < strs_size; i++) { + size_t diff; + str = igraph_strvector_get(&t->strs, i); diff = igraph_i_strdiff(str, key); if (diff == 0) { @@ -154,12 +152,12 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, /* ------------------------------------ */ /* They are exactly the same */ if (VECTOR(t->values)[i] != -1) { - *id = (long int) VECTOR(t->values)[i]; - return 0; + *id = VECTOR(t->values)[i]; + return IGRAPH_SUCCESS; } else { VECTOR(t->values)[i] = newvalue; - *id = (long int) newvalue; - return 0; + *id = newvalue; + return IGRAPH_SUCCESS; } } else if (str[diff] == '\0') { @@ -168,27 +166,29 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, /* 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); + return igraph_i_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); + if (! node) { + IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); - IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&node->values, 1); IGRAPH_CHECK(igraph_strvector_set(&node->strs, 0, key + diff)); + IGRAPH_FINALLY_CLEAN(4); + 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; + *id = newvalue; + return IGRAPH_SUCCESS; } else { *id = -1; - return 0; + return IGRAPH_SUCCESS; } } else if (key[diff] == '\0' && add) { @@ -198,32 +198,34 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, 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); + if (! node) { + IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 1); - IGRAPH_VECTOR_INIT_FINALLY(&node->values, 1); + IGRAPH_VECTOR_INT_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); - } + IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); + str2[diff] = '\0'; IGRAPH_FINALLY(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_FREE(str2); - IGRAPH_FINALLY_CLEAN(4); + IGRAPH_FINALLY_CLEAN(5); VECTOR(t->values)[i] = newvalue; VECTOR(t->children)[i] = node; - *id = (long int) newvalue; - return 0; + *id = newvalue; + return IGRAPH_SUCCESS; } else if (add) { @@ -232,12 +234,13 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, 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); + if (! node) { + IGRAPH_ERROR("Cannot add to trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, node); IGRAPH_STRVECTOR_INIT_FINALLY(&node->strs, 2); IGRAPH_VECTOR_PTR_INIT_FINALLY(&node->children, 2); - IGRAPH_VECTOR_INIT_FINALLY(&node->values, 2); + IGRAPH_VECTOR_INT_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]; @@ -246,26 +249,27 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, VECTOR(node->values)[1] = newvalue; str2 = strdup(str); - if (str2 == 0) { - IGRAPH_ERROR("cannot add to trie", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(str2, "Cannot add to trie."); + str2[diff] = '\0'; IGRAPH_FINALLY(igraph_free, str2); + IGRAPH_CHECK(igraph_strvector_set(&t->strs, i, str2)); + IGRAPH_FREE(str2); - IGRAPH_FINALLY_CLEAN(4); + IGRAPH_FINALLY_CLEAN(5); VECTOR(t->values)[i] = -1; VECTOR(t->children)[i] = node; - *id = (long int) newvalue; - return 0; + *id = newvalue; + return IGRAPH_SUCCESS; } else { /* ------------------------------------------------- */ /* No match, but we requested not to add the new key */ *id = -1; - return 0; + return IGRAPH_SUCCESS; } } @@ -273,49 +277,51 @@ int igraph_trie_get_node(igraph_trie_node_t *t, const char *key, /* 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; + /* Memory saving at the cost of performance may be possible by using the pattern + * CHECK(reserve(vec, size(vec) + 1)); + * push_back(vec, value); + * This was the original pattern used before igraph 0.10. */ + IGRAPH_CHECK(igraph_strvector_push_back(&t->strs, key)); + IGRAPH_CHECK(igraph_vector_ptr_push_back(&t->children, NULL)); + IGRAPH_CHECK(igraph_vector_int_push_back(&t->values, newvalue)); + *id = newvalue; } else { *id = -1; } - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup igraphtrie - * \brief Search/insert in a trie. + * \brief Search/insert a null-terminated string in a trie. + * + * \param t The trie. + * \param key The string to search for. If not found, it will be inserted. + * \param id The index of the string is stored here. + * \return Error code, usually \c IGRAPH_ENOMEM. */ -int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) { +igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id) { if (!t->storekeys) { - IGRAPH_CHECK(igraph_trie_get_node( (igraph_trie_node_t*) t, - key, t->maxvalue + 1, id)); + IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, 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); + igraph_error_t ret; + + IGRAPH_FINALLY_ENTER(); /* 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); + ret = igraph_strvector_push_back(&t->keys, key); + if (ret != IGRAPH_SUCCESS) { + IGRAPH_FINALLY_EXIT(); 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); + ret = igraph_i_trie_get_node(&t->node, key, t->maxvalue + 1, id); + if (ret != IGRAPH_SUCCESS) { + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); /* shrinks, error safe */ + IGRAPH_FINALLY_EXIT(); IGRAPH_ERROR("cannot get element from trie", ret); } @@ -323,73 +329,109 @@ int igraph_trie_get(igraph_trie_t *t, const char *key, long int *id) { if (*id > t->maxvalue) { t->maxvalue = *id; } else { - igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); + igraph_strvector_resize(&t->keys, igraph_strvector_size(&t->keys) - 1); /* shrinks, error safe */ } - igraph_set_error_handler(oldhandler); + IGRAPH_FINALLY_EXIT(); } - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup igraphtrie - * \brief Search/insert in a trie (for internal use). + * \brief Search/insert a string of given length in a trie. + * + * This function is identical to \ref igraph_trie_get(), except that + * it takes a string of a given length as input instead of a null-terminated + * string. * - * @return Error code: - * - IGRAPH_ENOMEM: out of memory + * \param t The trie. + * \param key The string to search for. If not found, it will be inserted. + * \param length The length of \p key. + * \param id The index of the string is stored here. + * \return Error code, usually \c IGRAPH_ENOMEM. */ -int igraph_trie_get2(igraph_trie_t *t, const char *key, long int length, - long int *id) { - char *tmp = IGRAPH_CALLOC(length + 1, char); +igraph_error_t igraph_trie_get_len( + igraph_trie_t *t, const char *key, + igraph_integer_t length, + igraph_integer_t *id) { - if (tmp == 0) { - IGRAPH_ERROR("Cannot get from trie", IGRAPH_ENOMEM); + char *tmp = strndup(key, length); + if (! tmp) { + IGRAPH_ERROR("Cannot get from trie.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - - 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; + + return IGRAPH_SUCCESS; } /** * \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. + * + * This variant does not add \p key to the trie if it does not exist. + * In this case, a negative \p id is returned. + * + * \param t The trie. + * \param key The string to search for. + * \param id If \p key is found, its index is stored here. Otherwise, + * a negative value is returned. + * \param Error code. */ -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; +igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id) { + IGRAPH_CHECK(igraph_i_trie_get_node(&t->node, key, -1, id)); + return IGRAPH_SUCCESS; } /** * \ingroup igraphtrie * \brief Get an element of a trie based on its index. + * + * \param t The trie. + * \param idx The index of the string. It is not checked that it is within range. + * \return The string with the given index. If the trie does not store the keys for + * reverse lookup, \c NULL is returned. */ -void igraph_trie_idx(igraph_trie_t *t, long int idx, char **str) { - igraph_strvector_get(&t->keys, idx, str); +const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx) { + if (! t->storekeys) { + return NULL; + } + return igraph_strvector_get(&t->keys, idx); } /** * \ingroup igraphtrie * \brief Returns the size of a trie. + * + * \param t The trie. + * \return The size of the trie, i.e. one larger than the maximum index. */ -long int igraph_trie_size(igraph_trie_t *t) { +igraph_integer_t 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; +/** + * \ingroup igraphtrie + * \brief Retrieves all the keys from the trie. + * + * + * Note that the returned pointer is a \em borrowed reference into the internal + * string vector of the trie. Do \em not modify it and do \em not use it after + * the trie was destroyed. + * + * \param t The trie. + * \return The borrowed reference. + */ + +const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t) { + return &t->keys; } diff --git a/src/vendor/cigraph/src/core/trie.h b/src/vendor/cigraph/src/core/trie.h index d5f87ffc4c9..9b1e23d7be5 100644 --- a/src/vendor/cigraph/src/core/trie.h +++ b/src/vendor/cigraph/src/core/trie.h @@ -39,33 +39,33 @@ __BEGIN_DECLS typedef struct s_igraph_trie_node { igraph_strvector_t strs; igraph_vector_ptr_t children; - igraph_vector_t values; + igraph_vector_int_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_trie_node_t node; + igraph_integer_t 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_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 igraph_error_t 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); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_get(igraph_trie_t *t, const char *key, igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_check(igraph_trie_t *t, const char *key, igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_get_len(igraph_trie_t *t, const char *key, igraph_integer_t length, + igraph_integer_t *id); +IGRAPH_PRIVATE_EXPORT const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT igraph_integer_t igraph_trie_size(igraph_trie_t *t); + +const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t); __END_DECLS diff --git a/src/vendor/cigraph/src/core/typed_list.pmt b/src/vendor/cigraph/src/core/typed_list.pmt new file mode 100644 index 00000000000..75aaa80e8a4 --- /dev/null +++ b/src/vendor/cigraph/src/core/typed_list.pmt @@ -0,0 +1,1099 @@ +/* -*- 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 + +*/ + +#include /* memmove */ + +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_qsort.h" + +#if defined(VECTOR_LIST) + /* It was indicated that every item in a list is a vector of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_VECTOR + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the vector */ + #if defined(BASE_IGRAPH_REAL) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector,f) + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define ITEM_FUNCTION(f) CONCAT2x(igraph_vector_bool,f) + #else + #define ITEM_FUNCTION(f) CONCAT3(igraph_vector,SHORT,f) + #endif +#elif defined(MATRIX_LIST) + /* It was indicated that every item in a list is a matrix of the base type + * so let's define ITEM_TYPE appropriately */ + #define ITEM_TYPE BASE_MATRIX + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the matrix */ + #if defined(BASE_IGRAPH_REAL) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix,f) + #elif defined(BASE_BOOL) + /* Special case because stdbool.h defines bool as a macro to _Bool which would + * screw things up */ + #define ITEM_FUNCTION(f) CONCAT2x(igraph_matrix_bool,f) + #else + #define ITEM_FUNCTION(f) CONCAT3(igraph_matrix,SHORT,f) + #endif +#else + #define ITEM_TYPE BASE + + /* Define the macro that creates the name of a function that refers to a single + * _item_ in the vector */ + #if defined(BASE_GRAPH) + #define ITEM_FUNCTION(f) CONCAT2x(igraph,f) + #endif +#endif + +static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item); +static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source); +static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item); + +static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); +static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end); +static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* list); +static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2); + +/** + * \ingroup vector_list + * \function igraph_vector_list_init + * \brief Initializes a list of vectors (constructor). + * + * + * This function constructs a list of vectors of the given size, and initializes + * each vector in the newly created list to become an empty vector. + * + * + * Vector objects initialized by this function are \em owned by the list, and + * they will be destroyed automatically when the list is destroyed with + * \ref igraph_vector_list_destroy(). + * + * \param v Pointer to a not yet initialized list of vectors. + * \param size The size of the list. + * \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 and initialize the corresponding vectors; + * n is the number of elements. + */ + +igraph_error_t FUNCTION(init)(TYPE* v, igraph_integer_t size) { + igraph_integer_t alloc_size = size > 0 ? size : 1; + IGRAPH_ASSERT(size >= 0); + v->stor_begin = IGRAPH_CALLOC(alloc_size, ITEM_TYPE); + if (v->stor_begin == 0) { + IGRAPH_ERROR("Cannot initialize list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + v->stor_end = v->stor_begin + alloc_size; + v->end = v->stor_begin + size; + + IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin, v->end)); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_destroy + * \brief Destroys a list of vectors object. + * + * + * All lists initialized by \ref igraph_vector_list_init() should be properly + * destroyed by this function. A destroyed list of vectors needs to be + * reinitialized by \ref igraph_vector_list_init() if you want to use it again. + * + * + * Vectors that are in the list when it is destroyed are also destroyed + * implicitly. + * + * \param v Pointer to the (previously initialized) list object to + * destroy. + * + * Time complexity: operating system dependent. + */ + +void FUNCTION(destroy)(TYPE* v) { + IGRAPH_ASSERT(v != 0); + + if (v->stor_begin != 0) { + FUNCTION(clear)(v); + IGRAPH_FREE(v->stor_begin); + v->stor_begin = NULL; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_capacity + * \brief Returns the allocated capacity of the list. + * + * Note that this might be different from the size of the list (as + * queried by \ref igraph_vector_list_size()), and specifies how many vectors + * the list can hold, without reallocation. + * + * \param v Pointer to the (previously initialized) list object to query. + * \return The allocated capacity. + * + * \sa \ref igraph_vector_list_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(capacity)(const TYPE* v) { + return v->stor_end - v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_reserve + * \brief Reserves memory for a list. + * + * + * \a igraph lists are flexible, they can grow and shrink. Growing + * however occasionally needs the data in the list to be copied. + * In order to avoid this, you can call this function to reserve space for + * future growth of the list. + * + * + * Note that this function does \em not change the size of the list, neither + * does it initialize any new vectors. Let us see a small example to clarify + * things: if you reserve space for 100 elements and the size of your + * list was (and still is) 60, then you can surely add additional 40 + * new vectors to your list before it will be copied. + * \param v The list object. + * \param capacity The new \em allocated size of the list. + * \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 list. + */ + +igraph_error_t FUNCTION(reserve)(TYPE* v, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + ITEM_TYPE *tmp; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = FUNCTION(capacity)(v); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(v->stor_begin, capacity, ITEM_TYPE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for list."); + + v->end = tmp + (v->end - v->stor_begin); + v->stor_begin = tmp; + v->stor_end = v->stor_begin + capacity; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_empty + * \brief Decides whether the size of the list is zero. + * + * \param v The list object. + * \return Non-zero number (true) if the size of the list is zero and + * zero (false) otherwise. + * + * Time complexity: O(1). + */ + +igraph_bool_t FUNCTION(empty)(const TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin == v->end; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_size + * \brief Returns the size (=length) of the vector. + * + * \param v The list object + * \return The size of the list. + * + * Time complexity: O(1). + */ + +igraph_integer_t FUNCTION(size)(const TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->end - v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_resize + * \brief Resize the list of vectors. + * + * + * Note that this function does not free any memory, just sets the + * size of the list to the given one. It can on the other hand + * allocate more memory if the new size is larger than the previous + * one. + * + * + * When the new size is larger than the current size, the newly added + * vectors in the list are initialized to empty vectors. When the new + * size is smaller than the current size, the vectors that were removed + * from the end of the list are destroyed automatically. + * + * \param v The list object + * \param new_size The new size of the list. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the list is made smaller. + * \sa \ref igraph_vector_list_reserve() for allocating memory for future + * extensions of a list. + * + * Time complexity: O(m) if the new size is smaller (m is the number of items + * that were removed from the list), operating system dependent if the new + * size is larger. In the latter case it is usually around O(n), where n is the + * new size of the vector. + */ +igraph_error_t FUNCTION(resize)(TYPE* v, igraph_integer_t new_size) { + igraph_integer_t old_size; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); + + old_size = FUNCTION(size)(v); + + if (old_size < new_size) { + IGRAPH_CHECK(INTERNAL_FUNCTION(init_slice)(v, v->stor_begin + old_size, v->stor_begin + new_size)); + } else if (old_size > new_size) { + INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin + new_size, v->stor_begin + old_size); + } + + v->end = v->stor_begin + new_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_list_clear + * \brief Removes all elements from a list of vectors. + * + * + * This function sets the size of the list to zero, and it also destroys all + * the vectors that were placed in the list before clearing it. + * + * \param v The list object. + * + * Time complexity: O(n), n is the number of items being deleted. + */ +void FUNCTION(clear)(TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + INTERNAL_FUNCTION(destroy_slice)(v, v->stor_begin, v->end); + v->end = v->stor_begin; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_get_ptr + * \brief Retrieve the address of a vector in the vector list. + * \param v The list object. + * \param pos The position of the vector in the list. The position of the first + * vector is zero. + * \return A pointer to the vector. It remains valid as long as the underlying + * list of vectors is not modified. + * + * Time complexity: O(1). + */ +ITEM_TYPE* FUNCTION(get_ptr)(const TYPE* v, igraph_integer_t pos) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + return v->stor_begin + pos; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_set + * \brief Sets the vector at the given index in the list. + * + * + * This function destroys the vector that is already at the given index \p pos + * in the list, and replaces it with the vector pointed to by \p e. + * The ownership of the vector pointed to by \p e is taken by the list so + * the user is not responsible for destroying \p e any more; it will be + * destroyed when the list itself is destroyed or if \p e gets removed from the + * list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param pos The index to modify in the list. + * \param e The vector to set in the list. + * + * Time complexity: O(1). + */ +void FUNCTION(set)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + pos); + v->stor_begin[pos] = *e; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_replace + * \brief Replaces the vector at the given index in the list with another one. + * + * + * This function replaces the vector that is already at the given index \p pos + * in the list with the vector pointed to by \p e. The ownership of the vector + * pointed to by \p e is taken by the list so the user is not responsible for + * destroying \p e any more. At the same time, the ownership of the vector that + * \em was in the list at position \p pos will be transferred to the caller and + * \p e will be updated to point to it, so the caller becomes responsible for + * destroying it when it does not need the vector any more. + * + * \param v The list object. + * \param pos The index to modify in the list. + * \param e The vector to swap with the one already in the list. + * + * Time complexity: O(1). + */ +void FUNCTION(replace)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + ITEM_TYPE old_value = *(FUNCTION(get_ptr)(v, pos)); + v->stor_begin[pos] = *e; + *e = old_value; +} + +/** + * \function igraph_vector_list_swap + * \brief Swaps all elements of two vector lists. + * + * \param v1 The first list. + * \param v2 The second list. + * \return Error code. + * + * Time complexity: O(1). + */ + +igraph_error_t FUNCTION(swap)(TYPE *v1, TYPE *v2) { + TYPE tmp; + + tmp = *v1; + *v1 = *v2; + *v2 = tmp; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_list_swap_elements + * \brief Swap two elements in a vector list. + * + * Note that currently no range checking is performed. + * \param v The input list. + * \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). + */ + +igraph_error_t FUNCTION(swap_elements)(TYPE *v1, igraph_integer_t i, igraph_integer_t j) { + ITEM_TYPE tmp = v1->stor_begin[i]; + v1->stor_begin[i] = v1->stor_begin[j]; + v1->stor_begin[j] = tmp; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_tail_ptr + * \brief Retrieve the address of the last vector in the vector list. + * \param v The list object. + * \return A pointer to the last vector in the list, or \c NULL if the list + * is empty. + * + * Time complexity: O(1). + */ +ITEM_TYPE* FUNCTION(tail_ptr)(const TYPE *v) { + igraph_integer_t size = FUNCTION(size)(v); + return size > 0 ? FUNCTION(get_ptr)(v, size - 1) : 0; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard + * \brief Discard the item at the given index in the vector list. + * + * + * This function removes the vector at the given index from the list, and + * moves all subsequent items in the list by one slot to the left to fill + * the gap. The vector that was removed from the list is destroyed automatically. + * + * \param v The list object. + * \param index Index of the item to be discarded and destroyed. + * \sa \ref igraph_vector_list_discard_fast() if you do not care about the + * order of the items in the list, \ref igraph_vector_list_remove() if you + * want to gain ownership of the item that was removed instead of destroying it. + * + * Time complexity: O(n), where n is the number of items in the list. + */ +void FUNCTION(discard)(TYPE* v, igraph_integer_t index) { + igraph_integer_t size = FUNCTION(size)(v); + + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); + memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); + v->end -= 1; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard_back + * \brief Discard the last item in the vector list. + * + * + * This function removes the last vector from the list and destroys it. + * + * \param v The list object. + * + * Time complexity: O(1). + */ +void FUNCTION(discard_back)(TYPE* v) { + igraph_integer_t size = FUNCTION(size)(v); + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->end - 1); + v->end -= 1; + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_discard_fast + * \brief Discard the item at the given index in the vector list and move the last item to its place. + * + * + * This function removes the vector at the given index from the list, and + * moves the last item in the list to \p index to fill the gap. The vector that + * was removed from the list is destroyed automatically. + * + * \param v The list object. + * \param index Index of the item to be discarded and destroyed. + * \sa \ref igraph_vector_list_discard() if you want to preserve the order of the + * items in the list, \ref igraph_vector_list_remove_fast() if you want to gain + * ownership of the item that was removed instead of destroying it. + * + * Time complexity: O(1). + */ +void FUNCTION(discard_fast)(TYPE* v, igraph_integer_t index) { + igraph_integer_t size = FUNCTION(size)(v); + + if (size > 0) { + INTERNAL_FUNCTION(destroy_item)(v->stor_begin + index); + v->end -= 1; + v->stor_begin[index] = *(v->end); + } +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back + * \brief Append an existing vector to the list, transferring ownership. + * + * + * This function resizes the list to be one element longer, and sets the very last + * element in the list to the specified vector \p e . The list takes ownership + * of the vector so the user is not responsible for freeing \p e any more; + * the vector will be destroyed when the list itself is destroyed or if \p e gets + * removed from the list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param e Pointer to the vector to append to the list. + * \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_list_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). + */ +igraph_error_t FUNCTION(push_back)(TYPE* v, ITEM_TYPE* e) { + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + *(v->end) = *e; + v->end += 1; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back_copy + * \brief Append the copy of a vector to the list. + * + * + * This function resizes the list to be one element longer, and copies the + * specified vector given as an argument to the last element. The newly added + * element is owned by the list, but the ownership of the original vector is + * retained at the caller. + * + * \param v The list object. + * \param e Pointer to the vector to copy to the end of the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back() plus the time + * needed to copy the vector (which is O(n) for n elements in the vector). + */ +igraph_error_t FUNCTION(push_back_copy)(TYPE* v, const ITEM_TYPE* e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(push_back)(v, ©)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_push_back_new + * \brief Append a new vector to the list. + * + * + * This function resizes the list to be one element longer. The newly added + * element will be an empty vector that is owned by the list. A pointer to + * the newly added element is returned in the last argument if it is not + * \c NULL . + * + * \param v The list object. + * \param result Pointer to a vector pointer; this will be updated to point to + * the newly added vector. May be \c NULL if you do not need a pointer + * to the newly added vector. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back(). + */ +igraph_error_t FUNCTION(push_back_new)(TYPE* v, ITEM_TYPE** e) { + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, v->end)); + if (e) { + *e = v->end; + } + v->end += 1; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert + * \brief Insert an existing vector into the list, transferring ownership. + * + * + * This function inserts \p e into the list at the given index, moving other + * items towards the end of the list as needed. The list takes ownership + * of the vector so the user is not responsible for freeing \p e any more; + * the vector will be destroyed when the list itself is destroyed or if \p e gets + * removed from the list without passing on the ownership to somewhere else. + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param e Pointer to the vector to insert into the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: O(n). + */ +igraph_error_t FUNCTION(insert)(TYPE* v, igraph_integer_t pos, ITEM_TYPE* e) { + igraph_integer_t size = FUNCTION(size)(v); + IGRAPH_ASSERT(0 <= pos && pos <= size); + IGRAPH_CHECK(INTERNAL_FUNCTION(expand_if_full)(v)); + if (pos < size) { + memmove(v->stor_begin + pos + 1, v->stor_begin + pos, sizeof(ITEM_TYPE) * (size - pos)); + } + v->end += 1; + v->stor_begin[pos] = *e; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert_copy + * \brief Insert the copy of a vector to the list. + * + * + * This function inserts a copy of \p e into the list at the given index, moving + * other items towards the end of the list as needed. The newly added + * element is owned by the list, but the ownership of the original vector is + * retained at the caller. + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param e Pointer to the vector to copy to the end of the list. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_insert() plus the time + * needed to copy the vector (which is O(n) for n elements in the vector). + */ +igraph_error_t FUNCTION(insert_copy)(TYPE* v, igraph_integer_t pos, const ITEM_TYPE* e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(copy_item)(©, e)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_insert_new + * \brief Insert a new vector into the list. + * + * + * This function inserts a newly created empty vector into the list at the given + * index, moving other items towards the end of the list as needed. The newly + * added vector is owned by the list. A pointer to the new element is returned + * in the last argument if it is not \c NULL . + * + * \param v The list object. + * \param pos The position where the new element is to be inserted. + * \param result Pointer to a vector pointer; this will be updated to point to + * the newly added vector. May be \c NULL if you do not need a pointer + * to the newly added vector. + * \return Error code: + * \c IGRAPH_ENOMEM: not enough memory. + * + * Time complexity: same as \ref igraph_vector_list_push_back(). + */ +igraph_error_t FUNCTION(insert_new)(TYPE* v, igraph_integer_t pos, ITEM_TYPE** e) { + ITEM_TYPE copy; + IGRAPH_CHECK(INTERNAL_FUNCTION(init_item)(v, ©)); + IGRAPH_FINALLY(INTERNAL_FUNCTION(destroy_item), ©); + IGRAPH_CHECK(FUNCTION(insert)(v, pos, ©)); + IGRAPH_FINALLY_CLEAN(1); + if (e) { + *e = FUNCTION(get_ptr)(v, pos); + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove + * \brief Remove the item at the given index from the vector list and transfer ownership to the caller. + * + * + * This function removes the vector at the given index from the list, and + * moves all subsequent items in the list by one slot to the left to fill + * the gap. The vector that was removed from the list is returned in \p e + * and its ownership is passed back to the caller; in other words, the caller + * becomes responsible for destroying the vector when it is not needed any more. + * + * \param v The list object. + * \param index Index of the item to be removed. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. It is an error to supply a null pointer here. + * \sa \ref igraph_vector_list_discard() if you are not interested in the item + * that was removed, \ref igraph_vector_list_remove_fast() if you do not care + * about the order of the items in the list. + * + * Time complexity: O(n), where n is the number of items in the list. + */ +igraph_error_t FUNCTION(remove)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { + igraph_integer_t size = FUNCTION(size)(v); + + IGRAPH_ASSERT(result != 0); + + if (index < 0 || index >= size) { + IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); + } + + *result = *(FUNCTION(get_ptr)(v, index)); + + memmove(v->stor_begin + index, v->stor_begin + index + 1, sizeof(ITEM_TYPE) * (size - index - 1)); + v->end -= 1; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_pop_back + * \brief Remove the last item from the vector list and transfer ownership to the caller. + * + * + * This function removes the last vector from the list. The vector that was + * removed from the list is returned and its ownership is passed back to the + * caller; in other words, the caller becomes responsible for destroying + * the vector when it is not needed any more. + * + * + * It is an error to call this function with an empty vector. + * + * \param v The list object. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. + * + * Time complexity: O(1). + */ +ITEM_TYPE FUNCTION(pop_back)(TYPE* v) { + IGRAPH_ASSERT(!FUNCTION(empty)(v)); + v->end -= 1; + return *(v->end); +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove_fast + * \brief Remove the item at the given index in the vector list, move the last item to its place and transfer ownership to the caller. + * + * + * This function removes the vector at the given index from the list, + * moves the last item in the list to \p index to fill the gap, and then + * transfers ownership of the removed vector back to the caller; in other words, + * the caller becomes responsible for destroying the vector when it is not + * needed any more. + * + * \param v The list object. + * \param index Index of the item to be removed. + * \param result Pointer to an \c igraph_vector_t object; it will be updated to the + * item that was removed from the list. Ownership of this vector is + * passed on to the caller. It is an error to supply a null pointer here. + * \sa \ref igraph_vector_list_remove() if you want to preserve the order of the + * items in the list, \ref igraph_vector_list_discard_fast() if you are not + * interested in the item that was removed. + * + * Time complexity: O(1). + */ +igraph_error_t FUNCTION(remove_fast)(TYPE* v, igraph_integer_t index, ITEM_TYPE* result) { + igraph_integer_t size = FUNCTION(size)(v); + + IGRAPH_ASSERT(result != 0); + + if (index < 0 || index >= size) { + IGRAPH_ERROR("invalid index when removing item", IGRAPH_EINVAL); + } + + *result = *(FUNCTION(get_ptr)(v, index)); + + v->end -= 1; + v->stor_begin[index] = *(v->end); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_permute + * \brief Permutes the elements of a list in place according to an index vector. + * + * + * This function takes a list \c v and a corresponding index vector \c index, + * and permutes the elements of \c v such that \c v[index[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the list minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. Memory may be leaked if the index vector does not satisfy these + * conditions. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_list_sort_ind(); passing in the index vector + * from \ref igraph_vector_list_sort_ind() will sort the original vector. + * + * \param v the list to permute + * \param index the index vector + * + * Time complexity: O(n), the number of items in the list. + */ +igraph_error_t FUNCTION(permute)(TYPE* v, const igraph_vector_int_t* index) { + ITEM_TYPE* work; + igraph_integer_t i, size; + + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + + size = igraph_vector_int_size(index); + IGRAPH_ASSERT(FUNCTION(size)(v) == size); + + work = IGRAPH_CALLOC(size, ITEM_TYPE); + if (work == 0) { + IGRAPH_ERROR("Cannot permute list.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + for (i = 0; i < size; i++) { + work[i] = v->stor_begin[VECTOR(*index)[i]]; + } + + memcpy(v->stor_begin, work, sizeof(ITEM_TYPE) * size); + + IGRAPH_FREE(work); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_sort + * \brief Sorts the elements of the list into ascending order. + * + * \param v Pointer to an initialized list object. + * \param cmp A comparison function that takes pointers to two vectors and + * returns zero if the two vectors are considered equal, any negative + * number if the first vector is smaller and any positive number if the + * second vector is smaller. + * \return Error code. + * + * Time complexity: O(n log n) for n elements. + */ +void FUNCTION(sort)(TYPE *v, int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*)) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_qsort( + v->stor_begin, FUNCTION(size)(v), sizeof(ITEM_TYPE), + (int(*)(const void*, const void*))cmp + ); +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_sort_ind + * \brief Returns a permutation of indices that sorts the list. + * + * Takes an unsorted list \p v as input and computes an array of + * indices \p inds such that v[ inds[i] ], with i increasing from 0, is + * an ordered array according to the comparison function \p cmp. The order of + * indices for identical elements is not defined. + * + * \param v the list to be sorted + * \param inds the output array of indices. This must be initialized, + * but will be resized + * \param cmp A comparison function that takes pointers to two vectors and + * returns zero if the two vectors are considered equal, any negative + * number if the first vector is smaller and any positive number if the + * second vector is smaller. + * \return Error code. + * + * Time complexity: O(n log n) for n elements. + */ +igraph_error_t FUNCTION(sort_ind)( + TYPE *v, igraph_vector_int_t *inds, + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) +) { + igraph_integer_t i, n = FUNCTION(size)(v); + ITEM_TYPE **vind, *first; + + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + if (n == 0) { + return IGRAPH_SUCCESS; + } + + vind = IGRAPH_CALLOC(n, ITEM_TYPE*); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_list_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + for (i = 0; i < n; i++) { + vind[i] = v->stor_begin + i; + } + first = vind[0]; + igraph_qsort_r( + vind, n, sizeof(ITEM_TYPE*), (void*) cmp, + (int(*)(void*, const void*, const void*)) INTERNAL_FUNCTION(sort_ind_cmp) + ); + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = vind[i] - first; + } + IGRAPH_FREE(vind); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector_list + * \function igraph_vector_list_remove_consecutive_duplicates + * \brief Removes consecutive duplicates from a vector list. + * + * Removes consecutive duplicate vectors from the list. Optionally, a custom + * equivalence relation may be used to determine when two vectors are + * considered to be the same. + * + * + * An efficient way to remove all duplicates, not just consecutive ones, + * is to first sort the vector list using \ref igraph_vector_list_sort(), + * then use this function. This will of course re-order the list. + * + * \param v The list to remove consecutive duplicates from. + * \param eq A comparison function that takes pointers to two vectors and + * returns true if they are equivalent. It is assumed that it implements + * a transitive, but not necessarily symmetric relation. + * Use \ref igraph_vector_all_e() to consider vector equivalent only + * when their contents are identical. + * + * \sa \ref igraph_vector_list_sort() + * + * Time complexity: O(n), the number of items in the list. + */ +void FUNCTION(remove_consecutive_duplicates)( + TYPE *v, igraph_bool_t (*eq)(const ITEM_TYPE*, const ITEM_TYPE*) +) { + igraph_integer_t i, j, n = FUNCTION(size)(v); + ITEM_TYPE *p = v->stor_begin; + + if (n < 2) { + return; + } + + for (i=0, j=0; i < n-1; ++i) { + if (eq(&p[i], &p[i+1])) { + INTERNAL_FUNCTION(destroy_item)(&p[i]); + } else { + p[j++] = p[i]; + } + } + p[j++] = p[n-1]; + + v->end = p + j; +} + +/** + * \function igraph_vector_list_reverse + * \brief Reverses the elements of a vector list. + * + * The first element will be last, the last element will be + * first, etc. + * \param v The input vector list. + * \return Error code, currently always \c IGRAPH_SUCCESS. + * + * Time complexity: O(n), the number of elements. + */ +igraph_error_t FUNCTION(reverse)(TYPE *v) { + igraph_integer_t n = FUNCTION(size)(v), n2 = n / 2; + igraph_integer_t i, j; + for (i = 0, j = n - 1; i < n2; i++, j--) { + ITEM_TYPE tmp; + tmp = VECTOR(*v)[i]; + VECTOR(*v)[i] = VECTOR(*v)[j]; + VECTOR(*v)[j] = tmp; + } + return IGRAPH_SUCCESS; +} + +/* ************************************************************************ */ + +#ifndef CUSTOM_INIT_DESTROY + +static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item) { + IGRAPH_UNUSED(list); + return ITEM_FUNCTION(init)(item, 0); +} + +static igraph_error_t INTERNAL_FUNCTION(copy_item)(ITEM_TYPE* dest, const ITEM_TYPE* source) { + return ITEM_FUNCTION(init_copy)(dest, source); +} + +static void INTERNAL_FUNCTION(destroy_item)(ITEM_TYPE* item) { + ITEM_FUNCTION(destroy)(item); +} + +#endif + +/* ************************************************************************ */ + +static igraph_error_t INTERNAL_FUNCTION(init_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { + ITEM_TYPE* current; + igraph_error_t retval; + + for (current = start; current < end; current++) { + retval = INTERNAL_FUNCTION(init_item)(list, current); + if (retval) { + INTERNAL_FUNCTION(destroy_slice)(list, start, current); + IGRAPH_CHECK(retval); + } + } + + return IGRAPH_SUCCESS; +} + +static void INTERNAL_FUNCTION(destroy_slice)(const TYPE* list, ITEM_TYPE* start, ITEM_TYPE* end) { + IGRAPH_UNUSED(list); + for (; start < end; start++) { + INTERNAL_FUNCTION(destroy_item)(start); + } +} + +/** + * Ensures that the vector has at least one extra slot at the end of its + * allocated storage area. + */ +static igraph_error_t INTERNAL_FUNCTION(expand_if_full)(TYPE* v) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + if (v->stor_end == v->end) { + igraph_integer_t old_size = FUNCTION(size)(v); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot add new item to list, already at maximum size.", IGRAPH_EOVERFLOW); + } + if (new_size == 0) { + new_size = 1; + } + IGRAPH_CHECK(FUNCTION(reserve)(v, new_size)); + } + + return IGRAPH_SUCCESS; +} + +/** + * Helper function passed to qsort from igraph_vector_qsort_ind + */ +static int INTERNAL_FUNCTION(sort_ind_cmp)(void *thunk, const void *p1, const void *p2) { + int (*cmp)(const ITEM_TYPE*, const ITEM_TYPE*) = (int (*)(const ITEM_TYPE*, const ITEM_TYPE*)) thunk; + ITEM_TYPE **pa = (ITEM_TYPE **) p1; + ITEM_TYPE **pb = (ITEM_TYPE **) p2; + return cmp(*pa, *pb); +} + +#undef ITEM_FUNCTION diff --git a/src/vendor/cigraph/src/core/vector.c b/src/vendor/cigraph/src/core/vector.c index 68512db3581..92e0dad5ba2 100644 --- a/src/vendor/cigraph/src/core/vector.c +++ b/src/vendor/cigraph/src/core/vector.c @@ -21,10 +21,11 @@ */ -#include "igraph_error.h" -#include "igraph_types.h" #include "igraph_vector.h" + #include "igraph_complex.h" +#include "igraph_types.h" +#include "igraph_nongraph.h" #include @@ -34,18 +35,6 @@ #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" @@ -70,51 +59,48 @@ #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. + * \brief Transform a real vector to an integer 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. + * \param to Pointer to an initialized integer 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_error_t igraph_vector_floor(const igraph_vector_t *from, igraph_vector_int_t *to) { + igraph_integer_t i, n = igraph_vector_size(from); - IGRAPH_CHECK(igraph_vector_long_resize(to, n)); + IGRAPH_CHECK(igraph_vector_int_resize(to, n)); for (i = 0; i < n; i++) { - VECTOR(*to)[i] = (long int) floor(VECTOR(*from)[i]); + VECTOR(*to)[i] = 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_error_t igraph_vector_round(const igraph_vector_t *from, igraph_vector_int_t *to) { + igraph_integer_t i, n = igraph_vector_size(from); - IGRAPH_CHECK(igraph_vector_long_resize(to, n)); + IGRAPH_CHECK(igraph_vector_int_resize(to, n)); for (i = 0; i < n; i++) { - VECTOR(*to)[i] = (long int) round(VECTOR(*from)[i]); + VECTOR(*to)[i] = round(VECTOR(*from)[i]); } - return 0; -} -int igraph_vector_order2(igraph_vector_t *v) { + return IGRAPH_SUCCESS; +} +igraph_error_t igraph_vector_order2(igraph_vector_t *v) { igraph_indheap_t heap; - igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v)); + IGRAPH_CHECK(igraph_indheap_init_array(&heap, VECTOR(*v), igraph_vector_size(v))); IGRAPH_FINALLY(igraph_indheap_destroy, &heap); igraph_vector_clear(v); @@ -125,22 +111,22 @@ int igraph_vector_order2(igraph_vector_t *v) { igraph_indheap_destroy(&heap); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup vector - * \function igraph_vector_order - * \brief Calculate the order of the elements in a vector. + * \function igraph_vector_int_pair_order + * \brief Calculates the order of the elements in a pair of integer vectors of equal length. * - * * 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 v The original \ref igraph_vector_int_t object. + * \param v2 A secondary key, another \ref igraph_vector_int_t object. + * \param res An initialized \ref igraph_vector_int_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 @@ -148,23 +134,23 @@ int igraph_vector_order2(igraph_vector_t *v) { * 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_error_t igraph_vector_int_pair_order(const igraph_vector_int_t* v, + const igraph_vector_int_t* v2, + igraph_vector_int_t* res, igraph_integer_t nodes) { + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_vector_int_t ptr; + igraph_vector_int_t rad; + igraph_integer_t 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, nodes + 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); for (i = 0; i < edges; i++) { - long int radix = (long int) v2->stor_begin[i]; + igraph_integer_t radix = VECTOR(*v2)[i]; if (VECTOR(ptr)[radix] != 0) { VECTOR(rad)[i] = VECTOR(ptr)[radix]; } @@ -174,21 +160,21 @@ int igraph_vector_order(const igraph_vector_t* v, 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; + igraph_integer_t next = VECTOR(ptr)[i] - 1; + VECTOR(*res)[j++] = next; while (VECTOR(rad)[next] != 0) { - next = (long int) VECTOR(rad)[next] - 1; - res->stor_begin[j++] = next; + next = VECTOR(rad)[next] - 1; + VECTOR(*res)[j++] = next; } } } - igraph_vector_null(&ptr); - igraph_vector_null(&rad); + igraph_vector_int_null(&ptr); + igraph_vector_int_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]; + igraph_integer_t edge = VECTOR(*res)[edges - i - 1]; + igraph_integer_t radix = VECTOR(*v)[edge]; if (VECTOR(ptr)[radix] != 0) { VECTOR(rad)[edge] = VECTOR(ptr)[radix]; } @@ -198,38 +184,39 @@ int igraph_vector_order(const igraph_vector_t* v, 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; + igraph_integer_t next = VECTOR(ptr)[i] - 1; + VECTOR(*res)[j++] = next; while (VECTOR(rad)[next] != 0) { - next = (long int) VECTOR(rad)[next] - 1; - res->stor_begin[j++] = next; + next = VECTOR(rad)[next] - 1; + VECTOR(*res)[j++] = next; } } } - igraph_vector_destroy(&ptr); - igraph_vector_destroy(&rad); + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -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_error_t igraph_vector_int_order1(const igraph_vector_int_t* v, + igraph_vector_int_t* res, + igraph_integer_t nodes) { + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_vector_int_t ptr; + igraph_vector_int_t rad; + igraph_integer_t 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, nodes + 1); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t radix = v->stor_begin[i]; if (VECTOR(ptr)[radix] != 0) { VECTOR(rad)[i] = VECTOR(ptr)[radix]; } @@ -239,153 +226,149 @@ int igraph_vector_order1(const igraph_vector_t* v, j = 0; for (i = 0; i < nodes + 1; i++) { if (VECTOR(ptr)[i] != 0) { - long int next = (long int) VECTOR(ptr)[i] - 1; + igraph_integer_t next = VECTOR(ptr)[i] - 1; res->stor_begin[j++] = next; while (VECTOR(rad)[next] != 0) { - next = (long int) VECTOR(rad)[next] - 1; + next = VECTOR(rad)[next] - 1; res->stor_begin[j++] = next; } } } - igraph_vector_destroy(&ptr); - igraph_vector_destroy(&rad); + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -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_error_t igraph_vector_rank( + const igraph_vector_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { - IGRAPH_ASSERT(v != NULL); - IGRAPH_ASSERT(v->stor_begin != NULL); + igraph_vector_int_t rad; + igraph_vector_int_t ptr; + igraph_integer_t edges = igraph_vector_size(v); + igraph_integer_t i, c = 0; - IGRAPH_VECTOR_INIT_FINALLY(&ptr, (long int) nodes + 1); - IGRAPH_VECTOR_INIT_FINALLY(&rad, edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, 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; + igraph_integer_t elem = VECTOR(*v)[i]; + VECTOR(ptr)[i] = VECTOR(rad)[elem]; + VECTOR(rad)[elem] = 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; - } + for (i = 0; i < nodes; i++) { + igraph_integer_t p = VECTOR(rad)[i]; + while (p != 0) { + VECTOR(*res)[p - 1] = c++; + p = VECTOR(ptr)[p - 1]; } } - igraph_vector_destroy(&ptr); - igraph_vector_destroy(&rad); + igraph_vector_int_destroy(&ptr); + igraph_vector_int_destroy(&rad); IGRAPH_FINALLY_CLEAN(2); - - return 0; + return IGRAPH_SUCCESS; } -int igraph_vector_rank(const igraph_vector_t *v, igraph_vector_t *res, - long int nodes) { +igraph_error_t igraph_vector_int_rank( + const igraph_vector_int_t *v, igraph_vector_int_t *res, igraph_integer_t nodes) { - igraph_vector_t rad; - igraph_vector_t ptr; - long int edges = igraph_vector_size(v); - long int i, c = 0; + igraph_vector_int_t rad; + igraph_vector_int_t ptr; + igraph_integer_t edges = igraph_vector_int_size(v); + igraph_integer_t i, c = 0; - IGRAPH_VECTOR_INIT_FINALLY(&rad, nodes); - IGRAPH_VECTOR_INIT_FINALLY(&ptr, edges); - IGRAPH_CHECK(igraph_vector_resize(res, edges)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rad, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ptr, edges); + IGRAPH_CHECK(igraph_vector_int_resize(res, edges)); for (i = 0; i < edges; i++) { - long int elem = (long int) VECTOR(*v)[i]; + igraph_integer_t elem = 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]; + igraph_integer_t p = VECTOR(rad)[i]; while (p != 0) { VECTOR(*res)[p - 1] = c++; - p = (long int) VECTOR(ptr)[p - 1]; + p = VECTOR(ptr)[p - 1]; } } - igraph_vector_destroy(&ptr); - igraph_vector_destroy(&rad); + igraph_vector_int_destroy(&ptr); + igraph_vector_int_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; + return IGRAPH_SUCCESS; } -#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; -} +/** + * \ingroup vector + * \function igraph_vector_complex_real + * \brief Gives the real part of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ -int igraph_vector_complex_real(const igraph_vector_complex_t *v, +igraph_error_t igraph_vector_complex_real(const igraph_vector_complex_t *v, igraph_vector_t *real) { - long int i, n = igraph_vector_complex_size(v); + igraph_integer_t 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; + return IGRAPH_SUCCESS; } -int igraph_vector_complex_imag(const igraph_vector_complex_t *v, +/** + * \ingroup vector + * \function igraph_vector_complex_imag + * \brief Gives the imaginary part of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The result will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t igraph_vector_complex_imag(const igraph_vector_complex_t *v, igraph_vector_t *imag) { - long int i, n = igraph_vector_complex_size(v); + igraph_integer_t 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; + return IGRAPH_SUCCESS; } -int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, +/** + * \ingroup vector + * \function igraph_vector_complex_realimag + * \brief Gives the real and imaginary parts of a complex vector. + * + * \param v Pointer to a complex vector. + * \param real Pointer to an initialized vector. The real part will be stored here. + * \param imag Pointer to an initialized vector. The imaginary part will be stored here. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t 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_integer_t 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++) { @@ -394,13 +377,26 @@ int igraph_vector_complex_realimag(const igraph_vector_complex_t *v, VECTOR(*imag)[i] = IGRAPH_IMAG(z); } - return 0; + return IGRAPH_SUCCESS; } -int igraph_vector_complex_create(igraph_vector_complex_t *v, +/** + * \ingroup vector + * \function igraph_vector_complex_create + * \brief Creates a complex vector from a real and imaginary part. + * + * \param v Pointer to an uninitialized complex vector. + * \param real Pointer to the real part of the complex vector. + * \param imag Pointer to the imaginary part of the complex vector. + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t 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); + igraph_integer_t i, n = igraph_vector_size(real); if (n != igraph_vector_size(imag)) { IGRAPH_ERROR("Real and imag vector sizes don't match", IGRAPH_EINVAL); } @@ -412,13 +408,26 @@ int igraph_vector_complex_create(igraph_vector_complex_t *v, VECTOR(*v)[i] = igraph_complex(VECTOR(*real)[i], VECTOR(*imag)[i]); } - return 0; + return IGRAPH_SUCCESS; } -int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, +/** + * \ingroup vector + * \function igraph_vector_complex_create_polar + * \brief Creates a complex matrix from a magnitude and an angle. + * + * \param m Pointer to an uninitialized complex vector. + * \param r Pointer to a real vector containing magnitudes. + * \param theta Pointer to a real vector containing arguments (phase angles). + * \return Error code. + * + * Time complexity: O(n), n is the number of elements in the vector. + */ + +igraph_error_t 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); + igraph_integer_t i, n = igraph_vector_size(r); if (n != igraph_vector_size(theta)) { IGRAPH_ERROR("'r' and 'theta' vector sizes don't match", IGRAPH_EINVAL); } @@ -430,13 +439,53 @@ int igraph_vector_complex_create_polar(igraph_vector_complex_t *v, VECTOR(*v)[i] = igraph_complex_polar(VECTOR(*r)[i], VECTOR(*theta)[i]); } - return 0; + return IGRAPH_SUCCESS; } +/** + * \function igraph_vector_complex_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two complex vectors are equal within a relative tolerance. + * + * \param lhs The first vector. + * \param rhs The second vector. + * \param eps Relative tolerance, see \ref igraph_complex_almost_equals() for details. + * \return True if the two vectors are almost equal, false if there is at least + * one differing element or if the vectors are not of the same size. + */ +igraph_bool_t igraph_vector_complex_all_almost_e(const igraph_vector_complex_t *lhs, + const igraph_vector_complex_t *rhs, + igraph_real_t eps) { + + igraph_integer_t n = igraph_vector_complex_size(lhs); + + if (lhs == rhs) { + return true; + } + + if (igraph_vector_complex_size(rhs) != n) { + return false; + } + + for (igraph_integer_t i=0; i < n; i++) { + if (! igraph_complex_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) + return false; + } + + return true; +} + +/** + * Deprecated in favour of \ref igraph_vector_all_almost_e() which uses + * relative tolerances. Will be removed in 0.11. + * + * Checks if two vectors are equal within an absolute tolerance. + */ 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_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -444,7 +493,7 @@ igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, s = igraph_vector_size(lhs); if (s != igraph_vector_size(rhs)) { - return 0; + return false; } else { if (tol == 0) { tol = DBL_EPSILON; @@ -453,20 +502,72 @@ igraph_bool_t igraph_vector_e_tol(const igraph_vector_t *lhs, igraph_real_t l = VECTOR(*lhs)[i]; igraph_real_t r = VECTOR(*rhs)[i]; if (l < r - tol || l > r + tol) { - return 0; + return false; } } - return 1; + return true; + } +} + +/** + * \function igraph_vector_all_almost_e + * \brief Are all elements almost equal? + * + * Checks if the elements of two vectors are equal within a relative tolerance. + * + * \param lhs The first vector. + * \param rhs The second vector. + * \param eps Relative tolerance, see \ref igraph_almost_equals() for details. + * \return True if the two vectors are almost equal, false if there is at least + * one differing element or if the vectors are not of the same size. + */ +igraph_bool_t igraph_vector_all_almost_e(const igraph_vector_t *lhs, + const igraph_vector_t *rhs, + igraph_real_t eps) { + + igraph_integer_t n = igraph_vector_size(lhs); + + if (lhs == rhs) { + return true; + } + + if (igraph_vector_size(rhs) != n) { + return false; } + + for (igraph_integer_t i=0; i < n; i++) { + if (! igraph_almost_equals(VECTOR(*lhs)[i], VECTOR(*rhs)[i], eps)) + return false; + } + + return true; } -int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { - long int i, n = igraph_vector_size(v); +/** + * \function igraph_vector_zapsmall + * \brief Replaces small elements of a vector by exact zeros. + * + * Vector elements which are smaller in magnitude than the given absolute + * tolerance will be replaced by exact zeros. The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param v The vector to process, it will be changed in-place. + * \param tol Tolerance value. Numbers smaller than this in magnitude will + * be replaced by zeros. Pass in zero to use the default tolerance. + * Must not be negative. + * \return Error code. + * + * \sa \ref igraph_vector_all_almost_e() and \ref igraph_almost_equals() to + * perform comparisons with relative tolerances. + */ +igraph_error_t igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { + igraph_integer_t i, n = igraph_vector_size(v); if (tol < 0.0) { - IGRAPH_ERROR("`tol' tolerance must be non-negative", IGRAPH_EINVAL); + IGRAPH_ERROR("Tolerance must be positive or zero.", IGRAPH_EINVAL); } if (tol == 0.0) { - tol = sqrt(DBL_EPSILON); + tol = pow(DBL_EPSILON, 2.0/3); } for (i = 0; i < n; i++) { igraph_real_t val = VECTOR(*v)[i]; @@ -474,7 +575,55 @@ int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { VECTOR(*v)[i] = 0.0; } } - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vector_complex_zapsmall + * \brief Replaces small elements of a complex vector by exact zeros. + * + * Similarly to \ref igraph_vector_zapsmall(), small elements will be replaced + * by zeros. The operation is performed separately on the real and imaginary + * parts of the numbers. This way, complex numbers with a large real part and + * tiny imaginary part will effectively be transformed to real numbers. + * The default tolerance + * corresponds to two-thirds of the representable digits of \type igraph_real_t, + * i.e. DBL_EPSILON^(2/3) which is approximately 10^-10. + * + * \param v The vector to process, it will be changed in-place. + * \param tol Tolerance value. Real and imaginary parts smaller than this in + * magnitude will be replaced by zeros. Pass in zero to use the default + * tolerance. Must not be negative. + * \return Error code. + * + * \sa \ref igraph_vector_complex_all_almost_e() and + * \ref igraph_complex_almost_equals() to perform comparisons with relative + * tolerances. + */ +igraph_error_t igraph_vector_complex_zapsmall(igraph_vector_complex_t *v, igraph_real_t tol) { + igraph_integer_t i, n = igraph_vector_complex_size(v); + if (tol < 0.0) { + IGRAPH_ERROR("Tolerance must be positive or zero.", IGRAPH_EINVAL); + } + if (tol == 0.0) { + tol = pow(DBL_EPSILON, 2.0/3); + } + for (i = 0; i < n; i++) { + igraph_complex_t val = VECTOR(*v)[i]; + igraph_bool_t zapped = false; + if (IGRAPH_REAL(val) < tol && IGRAPH_REAL(val) > -tol) { + IGRAPH_REAL(val) = 0.0; + zapped = true; + } + if (IGRAPH_IMAG(val) < tol && IGRAPH_IMAG(val) > -tol) { + IGRAPH_IMAG(val) = 0.0; + zapped = true; + } + if (zapped) { + VECTOR(*v)[i] = val; + } + } + return IGRAPH_SUCCESS; } /** @@ -482,17 +631,17 @@ int igraph_vector_zapsmall(igraph_vector_t *v, igraph_real_t tol) { * \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 + * \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_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) { igraph_real_t *ptr; igraph_bool_t *ptr_nan; @@ -502,7 +651,7 @@ int igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) 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; + *ptr_nan = isnan(*ptr); } return IGRAPH_SUCCESS; } @@ -512,7 +661,6 @@ int igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool_t *is_nan) * \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. * @@ -525,10 +673,10 @@ igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v) IGRAPH_ASSERT(v->stor_begin != NULL); ptr = v->stor_begin; while (ptr < v->end) { - if (igraph_is_nan(*ptr)) { - return 1; + if (isnan(*ptr)) { + return true; } ptr++; } - return 0; + return false; } diff --git a/src/vendor/cigraph/src/core/vector.pmt b/src/vendor/cigraph/src/core/vector.pmt index 5548f588faf..ecc45a126f5 100644 --- a/src/vendor/cigraph/src/core/vector.pmt +++ b/src/vendor/cigraph/src/core/vector.pmt @@ -26,6 +26,8 @@ #include "igraph_random.h" #include "igraph_qsort.h" +#include "math/safe_intop.h" + #include /* memcpy & co. */ #include #include /* va_start & co */ @@ -36,46 +38,55 @@ * \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. + * interface to arrays containing numbers. It is something similar to (but much + * simpler than) the \type vector template in the C++ standard library. + * + * There are multiple variants of \type igraph_vector_t; the basic variant + * stores doubles, but there is also \type igraph_vector_int_t for integers (of + * type \type igraph_integer_t), \c igraph_vector_bool_t for booleans (of type + * \type igraph_bool_t) and so on. Vectors are used extensively in \a igraph; all + * functions that expect or return a list of numbers use \type igraph_vector_t or + * \type igraph_vector_int_t to achieve this. Integer vectors are typically used + * when the vector is supposed to hold vertex or edge identifiers, while + * \type igraph_vector_t is used when the vector is expected to hold fractional + * numbers or infinities. + * + * The \type igraph_vector_t type and its variants usually use O(n) space + * to store n elements. Sometimes they use 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 and its variants 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. + * + * Almost all of the functions described below for \type igraph_vector_t + * also exist for all the other vector type variants. These variants are not + * documented separately; you can simply replace \c vector with \c vector_int, + * \c vector_bool or something similar if you need a function for another + * variant. For instance, to initialize a vector of type \type igraph_vector_int_t, + * you need to use \ref igraph_vector_int_init() and not \ref igraph_vector_init(). */ /** * \ingroup vector * \section igraph_vector_constructors_and_destructors Constructors and - * Destructors + * 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 + * \ref igraph_vector_init_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 + * igraph_vector_init_array() creates a vector by copying a regular C array. + * \ref igraph_vector_init_range() creates a vector containing a regular * sequence with increment one. * * \ref igraph_vector_view() is a special constructor, it allows you to @@ -88,7 +99,7 @@ * \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. + * you must not call \ref igraph_vector_destroy() on these. */ /** @@ -121,17 +132,23 @@ * 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_error_t FUNCTION(igraph_vector, init)(TYPE(igraph_vector)* v, igraph_integer_t size) { + igraph_integer_t alloc_size; IGRAPH_ASSERT(size >= 0); + alloc_size = size > 0 ? size : 1; + + /* When this function fails, it should leave stor_begin set to NULL, + * so that vector_destroy() is still safe to call on the vector. + * This simplifies freeing partially initialized data structures, + * such as adjacency lists, when an error occurs mid-initialization. */ v->stor_begin = IGRAPH_CALLOC(alloc_size, BASE); - if (v->stor_begin == 0) { - IGRAPH_ERROR("cannot init vector", IGRAPH_ENOMEM); + if (v->stor_begin == NULL) { + IGRAPH_ERROR("Cannot initialize vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } v->stor_end = v->stor_begin + alloc_size; v->end = v->stor_begin + size; - return 0; + return IGRAPH_SUCCESS; } /** @@ -145,7 +162,8 @@ int FUNCTION(igraph_vector, init) (TYPE(igraph_vector)* v, int long size) { * 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 data Pointer, the C array. It may not be \c NULL, except + * when \p length is zero. * \param length The length of the C array. * \return Pointer to the vector object, the same as the * \p v parameter, for convenience. @@ -153,12 +171,19 @@ int FUNCTION(igraph_vector, init) (TYPE(igraph_vector)* v, int long size) { * Time complexity: O(1) */ -const TYPE(igraph_vector)*FUNCTION(igraph_vector, view) (const TYPE(igraph_vector) *v, - const BASE *data, - long int length) { +const TYPE(igraph_vector)* FUNCTION(igraph_vector, view) (const TYPE(igraph_vector) *v, + const BASE *data, igraph_integer_t length) { + const static BASE dummy = ZERO; TYPE(igraph_vector) *v2 = (TYPE(igraph_vector)*)v; - IGRAPH_ASSERT(data != 0); + /* When the length is zero, we allow 'data' to be NULL. + * An igraph_vector_t may never contain a NULL pointer, + * thus we use a pointer to a dummy variable in this case. */ + if (length == 0) { + data = &dummy; + } else { + IGRAPH_ASSERT(data != NULL); + } v2->stor_begin = (BASE*)data; v2->stor_end = (BASE*)data + length; @@ -198,7 +223,7 @@ const TYPE(igraph_vector)*FUNCTION(igraph_vector, view) (const TYPE(igraph_vecto * elements in the vector. */ -int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) { +igraph_error_t 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)); @@ -209,7 +234,7 @@ int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) { } va_end(ap); - return 0; + return IGRAPH_SUCCESS; } /** @@ -237,7 +262,7 @@ int FUNCTION(igraph_vector, init_real)(TYPE(igraph_vector) *v, int no, ...) { * complexity of the memory allocation. */ -int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, +igraph_error_t FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, double endmark, ...) { int i = 0, n = 0; va_list ap; @@ -262,7 +287,7 @@ int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, va_end(ap); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -280,7 +305,7 @@ int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, * \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 + * \sa \ref igraph_vector_init_real() and \ref igraph_vector_init_int_end(), these are * similar functions. * * Time complexity: at least O(n) for @@ -288,7 +313,7 @@ int FUNCTION(igraph_vector, init_real_end)(TYPE(igraph_vector) *v, * complexity of the memory allocation. */ -int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) { +igraph_error_t 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)); @@ -299,7 +324,7 @@ int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) { } va_end(ap); - return 0; + return IGRAPH_SUCCESS; } /** @@ -327,7 +352,7 @@ int FUNCTION(igraph_vector, init_int)(TYPE(igraph_vector) *v, int no, ...) { * complexity of the memory allocation. */ -int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, ...) { +igraph_error_t FUNCTION(igraph_vector, init_int_end)(TYPE(igraph_vector) *v, int endmark, ...) { int i = 0, n = 0; va_list ap; @@ -351,7 +376,7 @@ int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, . va_end(ap); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } #endif /* ifndef BASE_COMPLEX */ @@ -364,7 +389,7 @@ int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, . * * 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 + * reinitialized by \ref igraph_vector_init(), \ref igraph_vector_init_array() or * another constructor. * \param v Pointer to the (previously initialized) vector object to * destroy. @@ -373,8 +398,10 @@ int FUNCTION(igraph_vector_init, int_end)(TYPE(igraph_vector) *v, int endmark, . */ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { - IGRAPH_ASSERT(v != 0); - if (v->stor_begin != 0) { + IGRAPH_ASSERT(v != NULL); + /* vector_init() will leave stor_begin set to NULL when it fails. + * We handle these cases gracefully. */ + if (v->stor_begin != NULL) { IGRAPH_FREE(v->stor_begin); v->stor_begin = NULL; } @@ -383,11 +410,12 @@ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { /** * \ingroup vector * \function igraph_vector_capacity - * \brief Returns the allocated capacity of the vector + * \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 + * 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. @@ -397,7 +425,7 @@ void FUNCTION(igraph_vector, destroy) (TYPE(igraph_vector)* v) { * Time complexity: O(1). */ -long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { +igraph_integer_t FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { return v->stor_end - v->stor_begin; } @@ -420,7 +448,7 @@ long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { * 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. + * \param capacity The new \em allocated size of the vector. * \return Error code: * \c IGRAPH_ENOMEM if there is not enough memory. * @@ -429,24 +457,28 @@ long int FUNCTION(igraph_vector, capacity)(const TYPE(igraph_vector)*v) { * 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); +igraph_error_t FUNCTION(igraph_vector, reserve)(TYPE(igraph_vector)* v, igraph_integer_t capacity) { + igraph_integer_t current_capacity; BASE *tmp; + IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); - if (size <= actual_size) { - return 0; - } + IGRAPH_ASSERT(capacity >= 0); - tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) size, BASE); - if (tmp == 0) { - IGRAPH_ERROR("cannot reserve space for vector", IGRAPH_ENOMEM); + current_capacity = FUNCTION(igraph_vector, capacity)(v); + + if (capacity <= current_capacity) { + return IGRAPH_SUCCESS; } + + tmp = IGRAPH_REALLOC(v->stor_begin, capacity, BASE); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for vector."); + + v->end = tmp + (v->end - v->stor_begin); v->stor_begin = tmp; - v->stor_end = v->stor_begin + size; - v->end = v->stor_begin + actual_size; + v->stor_end = v->stor_begin + capacity; - return 0; + return IGRAPH_SUCCESS; } /** @@ -461,7 +493,7 @@ int FUNCTION(igraph_vector, reserve) (TYPE(igraph_vector)* v, long int size) { * Time complexity: O(1). */ -igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v) { +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; @@ -478,7 +510,7 @@ igraph_bool_t FUNCTION(igraph_vector, empty) (const TYPE(igraph_vector)* v) * Time complexity: O(1). */ -long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v) { +igraph_integer_t 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; @@ -498,7 +530,7 @@ long int FUNCTION(igraph_vector, size) (const TYPE(igraph_vector)* v) { * Time complexity: O(1). */ -void FUNCTION(igraph_vector, clear) (TYPE(igraph_vector)* v) { +void FUNCTION(igraph_vector, clear)(TYPE(igraph_vector)* v) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); v->end = v->stor_begin; @@ -529,13 +561,17 @@ void FUNCTION(igraph_vector, clear) (TYPE(igraph_vector)* v) { * complexity of memory allocation is at most linear.) */ -int FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { +igraph_error_t 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; + /* full, allocate more storage */ + igraph_integer_t old_size = FUNCTION(igraph_vector, size)(v); + igraph_integer_t new_size = old_size < IGRAPH_INTEGER_MAX/2 ? old_size * 2 : IGRAPH_INTEGER_MAX; + if (old_size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot push to vector, already at maximum size.", IGRAPH_EOVERFLOW); + } if (new_size == 0) { new_size = 1; } @@ -545,7 +581,7 @@ int FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { *(v->end) = e; v->end += 1; - return 0; + return IGRAPH_SUCCESS; } /** @@ -562,19 +598,20 @@ int FUNCTION(igraph_vector, push_back) (TYPE(igraph_vector)* v, BASE e) { * \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_error_t FUNCTION(igraph_vector, insert)( + TYPE(igraph_vector) *v, igraph_integer_t pos, BASE value) { + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + IGRAPH_ASSERT(0 <= pos && pos <= size); + if (size == IGRAPH_INTEGER_MAX) { + IGRAPH_ERROR("Cannot insert to vector, already at maximum size.", IGRAPH_EOVERFLOW); } - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (long) size + 1)); - if (((unsigned long)pos) < size) { + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, size + 1)); + if (pos < size) { memmove(v->stor_begin + pos + 1, v->stor_begin + pos, - sizeof(BASE) * (size - (size_t) pos)); + sizeof(BASE) * (size - pos)); } v->stor_begin[pos] = value; - return 0; + return IGRAPH_SUCCESS; } /** @@ -584,8 +621,8 @@ int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, * 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. + * igraph_vector_get() queries and \ref igraph_vector_set() sets an element of a + * vector. \ref igraph_vector_get_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 @@ -595,18 +632,19 @@ int FUNCTION(igraph_vector, insert)(TYPE(igraph_vector) *v, long int pos, /** * \ingroup vector - * \function igraph_vector_e + * \function igraph_vector_get * \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. + * \sa \ref igraph_vector_get_ptr() and the \ref VECTOR macro. * * Time complexity: O(1). */ -BASE FUNCTION(igraph_vector, e) (const TYPE(igraph_vector)* v, long int pos) { +BASE FUNCTION(igraph_vector, get)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); return * (v->stor_begin + pos); @@ -614,35 +652,66 @@ BASE FUNCTION(igraph_vector, e) (const TYPE(igraph_vector)* v, long int /** * \ingroup vector - * \function igraph_vector_e_ptr - * \brief Get the address of an element of a vector + * \function igraph_vector_get_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. + * \sa \ref igraph_vector_get() and the \ref VECTOR macro. * * Time complexity: O(1). */ -BASE* FUNCTION(igraph_vector, e_ptr) (const TYPE(igraph_vector)* v, long int pos) { +BASE* FUNCTION(igraph_vector, get_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); return v->stor_begin + pos; } +/** + * \ingroup vector + * \function igraph_vector_e + * \brief Access an element of a vector (deprecated alias). + * + * \deprecated-by igraph_vector_get 0.10.0 + */ + +BASE FUNCTION(igraph_vector, e)(const TYPE(igraph_vector)* v, igraph_integer_t pos) { + return FUNCTION(igraph_vector, get)(v, 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_get() and the \ref VECTOR macro. + * + * Time complexity: O(1). + */ + +BASE* FUNCTION(igraph_vector, e_ptr) (const TYPE(igraph_vector)* v, igraph_integer_t pos) { + return FUNCTION(igraph_vector, get_ptr)(v, 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(). + * \sa \ref igraph_vector_get(). */ -void FUNCTION(igraph_vector, set) (TYPE(igraph_vector)* v, - long int pos, BASE value) { +void FUNCTION(igraph_vector, set)(TYPE(igraph_vector)* v, igraph_integer_t pos, BASE value) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); *(v->stor_begin + pos) = value; @@ -658,6 +727,7 @@ void FUNCTION(igraph_vector, set) (TYPE(igraph_vector)* v, * 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 @@ -668,16 +738,16 @@ 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)); + memset(v->stor_begin, 0, sizeof(BASE) * FUNCTION(igraph_vector, size)(v)); } } /** * \function igraph_vector_fill - * \brief Fill a vector with a constant element + * \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. * @@ -693,14 +763,48 @@ void FUNCTION(igraph_vector, fill) (TYPE(igraph_vector)* v, BASE e) { } } +#ifndef NOTORDERED + +/** + * \ingroup vector + * \function igraph_vector_range + * \brief Updates a vector to store a range. + * + * Sets the elements of the vector to contain the numbers \p start, \p start+1, + * ..., \p end-1. Note that the range is closed from the left and open from the + * right, according to C conventions. The vector will be resized to fit the range. + * + * \param v The vector to update. + * \param start The lower limit in the range (inclusive). + * \param end The upper limit in the range (exclusive). + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory. + * + * Time complexity: O(n), the number of elements in the vector. + */ + +igraph_error_t FUNCTION(igraph_vector, range)(TYPE(igraph_vector) *v, BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(v, (to - from))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from; + from = from + ONE; + } + + return IGRAPH_SUCCESS; +} + +#endif + /** * \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. * @@ -718,8 +822,8 @@ BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) { * \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. * @@ -727,22 +831,89 @@ BASE FUNCTION(igraph_vector, tail)(const TYPE(igraph_vector) *v) { */ 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 != 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; + + (v->end)--; + + return *(v->end); } #ifndef NOTORDERED +/** + * \ingroup vector + * \function igraph_vector_permute + * \brief Permutes the elements of a vector in place according to an index vector. + * + * This function takes a vector \c v and a corresponding index vector \c ind, + * and permutes the elements of \c v such that \c v[ind[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the vector minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_qsort_ind(); passing in the index vector + * from \ref igraph_vector_qsort_ind() will sort the original vector. + * + * + * As a special case, this function allows the index vector to be \em shorter + * than the vector being permuted, in which case the elements whose indices do + * not occur in the index vector will be removed from the vector. + * + * \param v the vector to permute + * \param ind the index vector + * + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: O(n), the size of the vector. + */ +igraph_error_t FUNCTION(igraph_vector, permute)(TYPE(igraph_vector)* v, const igraph_vector_int_t* index) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + IGRAPH_ASSERT(FUNCTION(igraph_vector, size)(v) >= igraph_vector_int_size(index)); + + TYPE(igraph_vector) v_copy; + BASE *v_ptr; + igraph_integer_t *ind_ptr; + + /* There is a more space-efficient algorithm that needs O(1) space only, + * but it messes up the index vector, which we don't want */ + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(&v_copy, igraph_vector_int_size(index))); + IGRAPH_FINALLY(FUNCTION(igraph_vector, destroy), &v_copy); + + for ( + v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; + ind_ptr < index->end; + v_ptr++, ind_ptr++ + ) { + *v_ptr = VECTOR(*v)[*ind_ptr]; + } + + IGRAPH_CHECK(FUNCTION(igraph_vector, update)(v, &v_copy)); + + FUNCTION(igraph_vector, destroy)(&v_copy); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + /** * \ingroup vector * \function igraph_vector_sort_cmp - * \brief Internal comparison function of vector elements, used by - * \ref igraph_vector_sort(). + * \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) { @@ -755,8 +926,7 @@ static int FUNCTION(igraph_vector, sort_cmp)(const void *a, const void *b) { /** * \ingroup vector * \function igraph_vector_reverse_sort_cmp - * \brief Internal comparison function of vector elements, used by - * \ref igraph_vector_reverse_sort(). + * \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) { @@ -783,7 +953,7 @@ static int FUNCTION(igraph_vector, reverse_sort_cmp)(const void *a, const void * 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), + igraph_qsort(v->stor_begin, FUNCTION(igraph_vector, size)(v), sizeof(BASE), FUNCTION(igraph_vector, sort_cmp)); } @@ -804,7 +974,7 @@ void FUNCTION(igraph_vector, sort)(TYPE(igraph_vector) *v) { 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), + igraph_qsort(v->stor_begin, FUNCTION(igraph_vector, size)(v), sizeof(BASE), FUNCTION(igraph_vector, reverse_sort_cmp)); } @@ -840,7 +1010,7 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v /** * \function igraph_vector_qsort_ind - * \brief Return a permutation of indices that sorts a vector + * \brief Returns 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 @@ -851,9 +1021,10 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v * * \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. + * but will be resized + * \param order whether the output array should be sorted in ascending + * or descending order. Use \c IGRAPH_ASCENDING for ascending and + * \c IGRAPH_DESCENDING for descending order. * \return Error code. * * This routine uses igraph's built-in qsort routine. @@ -863,61 +1034,61 @@ static int FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)(const void *p1, const v * 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; +igraph_error_t FUNCTION(igraph_vector, qsort_ind)(const TYPE(igraph_vector) *v, + igraph_vector_int_t *inds, igraph_order_t order) { + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); BASE **vind, *first; - size_t n = (size_t) FUNCTION(igraph_vector, size)(v); - IGRAPH_CHECK(igraph_vector_resize(inds, (long) n)); + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); if (n == 0) { - return 0; + return IGRAPH_SUCCESS; } vind = IGRAPH_CALLOC(n, BASE*); if (vind == 0) { - IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("igraph_vector_qsort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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)); + if (order == IGRAPH_ASCENDING) { + igraph_qsort(vind, n, sizeof(BASE*), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)); } else { - igraph_qsort(vind, n, sizeof(BASE**), FUNCTION(igraph_vector, i_qsort_ind_cmp_asc)); + igraph_qsort(vind, n, sizeof(BASE*), FUNCTION(igraph_vector, i_qsort_ind_cmp_desc)); } for (i = 0; i < n; i++) { VECTOR(*inds)[i] = vind[i] - first; } IGRAPH_FREE(vind); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_vector_lex_cmp - * \brief Lexicographical comparison of two vectors. + * \brief Lexicographical comparison of two vectors (type-safe variant). * - * * 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(). + * This function is typically used together with \ref igraph_vector_list_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 **). + * \param lhs Pointer to the first vector. + * \param rhs Pointer to the second vector. * \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 + * \sa \ref igraph_vector_lex_cmp_untyped() for an untyped variant of this + * function, or \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 + * \example examples/simple/igraph_vector_int_list_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; +int FUNCTION(igraph_vector, lex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs +) { + igraph_integer_t i, sa, sb; + const TYPE(igraph_vector) *a = lhs, *b = rhs; sa = FUNCTION(igraph_vector, size)(a); sb = FUNCTION(igraph_vector, size)(b); @@ -941,35 +1112,63 @@ int FUNCTION(igraph_vector, lex_cmp)(const void *lhs, const void *rhs) { return -1; } +/** + * \function igraph_vector_lex_cmp_untyped + * \brief Lexicographical comparison of two vectors (non-type-safe). + * + * + * 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_lex_cmp() for a type-safe variant of this + * function, or \ref igraph_vector_colex_cmp_untyped() to compare vectors starting from + * the last element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + */ +int FUNCTION(igraph_vector, lex_cmp_untyped)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + return FUNCTION(igraph_vector, lex_cmp)(a, b); +} + /** * \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(). + * This function is typically used together with \ref igraph_vector_list_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 **). + * \param lhs Pointer to a pointer to the first vector. + * \param rhs Pointer to a pointer to the second vector. * \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 + * \sa \ref igraph_vector_colex_cmp_untyped() for an untyped variant of this + * function, or \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 + * \example examples/simple/igraph_vector_int_list_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; +int FUNCTION(igraph_vector, colex_cmp)( + const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs +) { + igraph_integer_t i, sa, sb, rai, rbi; + const TYPE(igraph_vector) *a = lhs, *b = rhs; sa = FUNCTION(igraph_vector, size)(a); sb = FUNCTION(igraph_vector, size)(b); @@ -995,6 +1194,37 @@ int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, const void *rhs) { /* a is shorter, and equal to the last part of b */ return -1; } + +/** + * \function igraph_vector_colex_cmp_untyped + * \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_colex_cmp() for a type-safe variant of this + * function, \ref igraph_vector_lex_cmp_untyped() to compare vectors starting from + * the first element. + * + * Time complexity: O(n), the number of elements in the smaller vector. + */ +int FUNCTION(igraph_vector, colex_cmp_untyped)(const void *lhs, const void *rhs) { + const TYPE(igraph_vector) *a = * (TYPE(igraph_vector) **) lhs; + const TYPE(igraph_vector) *b = * (TYPE(igraph_vector) **) rhs; + return FUNCTION(igraph_vector, colex_cmp)(a, b); +} + #endif /*NOTORDERED*/ /** @@ -1009,7 +1239,7 @@ int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, const void *rhs) { * 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. + * \param new_size 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 @@ -1025,12 +1255,12 @@ int FUNCTION(igraph_vector, colex_cmp)(const void *lhs, const void *rhs) { * n is the new size of the vector. */ -int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize) { +igraph_error_t FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, igraph_integer_t new_size) { 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; + IGRAPH_CHECK(FUNCTION(igraph_vector, reserve)(v, new_size)); + v->end = v->stor_begin + new_size; + return IGRAPH_SUCCESS; } /** @@ -1038,34 +1268,32 @@ int FUNCTION(igraph_vector, resize)(TYPE(igraph_vector)* v, long int newsize) { * \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. + * This function attempts to deallocate the unused reserved storage + * of a vector. If it succeeds, \ref igraph_vector_size() and + * \ref igraph_vector_capacity() will be the same. The data in the + * vector is always preserved, even if deallocation is not successful. + * * \param v Pointer to an initialized vector. - * \return Error code. * * \sa \ref igraph_vector_resize(), \ref igraph_vector_reserve(). * - * Time complexity: operating system dependent. + * Time complexity: operating system dependent, O(n) at worst. */ -int FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { - size_t size; +void FUNCTION(igraph_vector, resize_min)(TYPE(igraph_vector)*v) { + igraph_integer_t size; BASE *tmp; if (v->stor_end == v->end) { - return 0; + return; } - size = (size_t) (v->end - v->stor_begin); + size = (v->end - v->stor_begin); tmp = IGRAPH_REALLOC(v->stor_begin, size, BASE); - if (tmp == 0) { - IGRAPH_ERROR("cannot resize vector", IGRAPH_ENOMEM); - } else { + + if (tmp != NULL) { v->stor_begin = tmp; v->stor_end = v->end = v->stor_begin + size; } - - return 0; } #ifndef NOTORDERED @@ -1098,16 +1326,16 @@ BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { 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 */ +#if defined(BASE_IGRAPH_REAL) + if (isnan(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)) +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) return *ptr; /* Result is NaN */ #endif ptr++; @@ -1131,7 +1359,7 @@ BASE FUNCTION(igraph_vector, max)(const TYPE(igraph_vector)* v) { * * Time complexity: O(n), n is the size of the vector. */ -long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { +igraph_integer_t FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { if (!FUNCTION(igraph_vector, empty)(v)) { BASE *max; BASE *ptr; @@ -1139,16 +1367,16 @@ long int FUNCTION(igraph_vector, which_max)(const TYPE(igraph_vector)* v) { 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 */ +#if defined(BASE_IGRAPH_REAL) + if (isnan(*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)) { +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { return ptr - v->stor_begin; /* Result is NaN */ } #endif @@ -1178,16 +1406,16 @@ BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { 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 */ +#if defined(BASE_IGRAPH_REAL) + if (isnan(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)) { +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { return *ptr; /* Result is NaN */ } #endif @@ -1210,7 +1438,7 @@ BASE FUNCTION(igraph_vector, min)(const TYPE(igraph_vector)* v) { * * Time complexity: O(n), the number of elements. */ -long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { +igraph_integer_t FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { if (!FUNCTION(igraph_vector, empty)(v)) { BASE *min; BASE *ptr; @@ -1218,16 +1446,16 @@ long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { 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 */ +#if defined(BASE_IGRAPH_REAL) + if (isnan(*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)) { +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { return ptr - v->stor_begin; /* Result is NaN */ } #endif @@ -1246,7 +1474,7 @@ long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { /** * \ingroup vector - * \function igraph_vector_init_copy + * \function igraph_vector_init_array * \brief Initializes a vector from an ordinary C array (constructor). * * \param v Pointer to an uninitialized vector object. @@ -1259,17 +1487,17 @@ long int FUNCTION(igraph_vector, which_min)(const TYPE(igraph_vector)* v) { * 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); +igraph_error_t FUNCTION(igraph_vector, init_array)( + TYPE(igraph_vector) *v, const BASE *data, igraph_integer_t length) { + + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, length)); + + /* Note: memcpy() behaviour is undefined if data==NULL, even if length==0. */ + if (length > 0) { + memcpy(v->stor_begin, data, length * sizeof(BASE)); } - v->stor_end = v->stor_begin + length; - v->end = v->stor_end; - memcpy(v->stor_begin, data, (size_t) length * sizeof(BASE)); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1291,13 +1519,13 @@ 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)); + memcpy(to, v->stor_begin, sizeof(BASE) * (v->end - v->stor_begin)); } } /** * \ingroup vector - * \function igraph_vector_copy + * \function igraph_vector_init_copy * \brief Initializes a vector from another vector object (constructor). * * @@ -1313,24 +1541,33 @@ void FUNCTION(igraph_vector, copy_to)(const TYPE(igraph_vector) *v, BASE *to) { * 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_error_t FUNCTION(igraph_vector, init_copy)( + TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from +) { + igraph_integer_t 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)); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(to, from_size)); - return 0; + memcpy(to->stor_begin, from->stor_begin, from_size * sizeof(BASE)); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_copy + * \brief Initializes a vector from another vector object (deprecated alias). + * + * \deprecated-by igraph_vector_init_copy 0.10 + */ + +igraph_error_t FUNCTION(igraph_vector, copy)(TYPE(igraph_vector) *to, + const TYPE(igraph_vector) *from) { + return FUNCTION(igraph_vector, init_copy)(to, from); } /** @@ -1421,7 +1658,7 @@ BASE FUNCTION(igraph_vector, prod)(const TYPE(igraph_vector) *v) { * Time complexity: O(n), the size of the vector. */ -int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, +igraph_error_t FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { BASE res = ZERO; BASE *p, *p2; @@ -1442,7 +1679,7 @@ int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, *p2 = res; } - return 0; + return IGRAPH_SUCCESS; } #ifndef NOTORDERED @@ -1450,11 +1687,14 @@ int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, /** * \ingroup vector * \function igraph_vector_init_seq - * \brief Initializes a vector with a sequence. + * \brief Initializes a vector with a sequence, inclusive endpoints (deprecated). * * - * The vector will contain the numbers \p from, - * \p from+1, ..., \p to. + * The vector will contain the numbers \p from, \p from+1, ..., \p to. Note that + * both endpoints are \em inclusive, contrary to typical usage of ranges in C. + * + * \deprecated-by igraph_vector_init_range 0.10.0 + * * \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). @@ -1465,16 +1705,46 @@ int FUNCTION(igraph_vector, cumsum)(TYPE(igraph_vector) *to, * of elements in the vector. */ -int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, - BASE from, BASE to) { +igraph_error_t 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))); + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from + 1))); for (p = v->stor_begin; p < v->end; p++) { *p = from++; } - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vector + * \function igraph_vector_init_range + * \brief Initializes a vector with a range. + * + * + * The vector will contain the numbers \p start, \p start+1, ..., \p end-1. Note + * that the range is closed from the left and open from the right, according to + * C conventions. + * + * \param v Pointer to an uninitialized vector object. + * \param start The lower limit in the range (inclusive). + * \param end The upper limit in the range (exclusive). + * \return Error code: + * \c IGRAPH_ENOMEM: out of memory. + * + * Time complexity: O(n), the number of elements in the vector. + */ + +igraph_error_t FUNCTION(igraph_vector, init_range)(TYPE(igraph_vector) *v, BASE from, BASE to) { + BASE *p; + IGRAPH_CHECK(FUNCTION(igraph_vector, init)(v, (to - from))); + + for (p = v->stor_begin; p < v->end; p++) { + *p = from; + from = from + ONE; + } + + return IGRAPH_SUCCESS; } #endif @@ -1485,8 +1755,6 @@ int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, * \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. @@ -1496,16 +1764,26 @@ int FUNCTION(igraph_vector, init_seq)(TYPE(igraph_vector) *v, * vector. */ -void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, - long int from, long int to) { +void FUNCTION(igraph_vector, remove_section)( + TYPE(igraph_vector) *v, igraph_integer_t from, igraph_integer_t to) { + igraph_integer_t size = FUNCTION(igraph_vector, size)(v); + IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); - /* Not removing from the end? */ - if (to < FUNCTION(igraph_vector, size)(v)) { + + if (from < 0) { + from = 0; + } + + if (to > size) { + to = size; + } + + if (to > from) { memmove(v->stor_begin + from, v->stor_begin + to, - sizeof(BASE) * (size_t) (v->end - v->stor_begin - to)); + sizeof(BASE) * (v->end - v->stor_begin - to)); + v->end -= (to - from); } - v->end -= (to - from); } /** @@ -1522,20 +1800,48 @@ void FUNCTION(igraph_vector, remove_section)(TYPE(igraph_vector) *v, * vector. */ -void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem) { +void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, igraph_integer_t 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_remove_fast + * \brief Removes a single element from a vector, \em not keeping the order of the remaining elements. + * + * This function removes the element with the given element from the vector by + * swapping it with the last element and then popping it off. You can use this + * function instead of \ref igraph_vector_remove() to gain some speed if the + * order of elements does not matter. + * + * + * 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(1). + */ + +void FUNCTION(igraph_vector, remove_fast)(TYPE(igraph_vector) *v, igraph_integer_t elem) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + + igraph_integer_t len = FUNCTION(igraph_vector, size)(v); + VECTOR(*v)[elem] = VECTOR(*v)[len - 1]; + FUNCTION(igraph_vector, pop_back)(v); +} + /** * \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. + * The source and the destination sections are allowed to overlap; this will + * be handled internally by the function. * \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. @@ -1546,46 +1852,22 @@ void FUNCTION(igraph_vector, remove)(TYPE(igraph_vector) *v, long int elem) { * 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_error_t FUNCTION(igraph_vector, move_interval)(TYPE(igraph_vector) *v, + igraph_integer_t begin, igraph_integer_t end, igraph_integer_t 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)); + sizeof(BASE) * (end - begin)); - return 0; + return IGRAPH_SUCCESS; } /** - * \ingroup vector - * \function igraph_vector_permdelete - * \brief Remove elements of a vector (for internal use). + * \deprecated-by igraph_vector_move_interval 0.10.0 */ - -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; +igraph_error_t FUNCTION(igraph_vector, move_interval2)(TYPE(igraph_vector) *v, + igraph_integer_t begin, igraph_integer_t end, igraph_integer_t to) { + return FUNCTION(igraph_vector, move_interval)(v, begin, end, to); } #ifndef NOTORDERED @@ -1593,14 +1875,13 @@ void FUNCTION(igraph_vector, permdelete)(TYPE(igraph_vector) *v, /** * \ingroup vector * \function igraph_vector_isininterval - * \brief Checks if all elements of a vector are in the given - * interval. + * \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 True (positive integer) if the vector is empty or 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 @@ -1658,18 +1939,21 @@ igraph_bool_t FUNCTION(igraph_vector, any_smaller)(const TYPE(igraph_vector) *v, * \function igraph_vector_all_e * \brief Are all elements equal? * + * Checks element-wise equality of two vectors. For vectors containing floating + * point values, consider using \ref igraph_matrix_all_almost_e(). + * * \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. + * \return True if the elements in the \p lhs are all + * equal to the corresponding elements in \p rhs. Returns + * 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_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1677,7 +1961,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return 0; + return false; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; @@ -1687,10 +1971,10 @@ igraph_bool_t FUNCTION(igraph_vector, all_e)(const TYPE(igraph_vector) *lhs, #else if (l != r) { #endif - return 0; + return false; } } - return 1; + return true; } } @@ -1719,7 +2003,7 @@ FUNCTION(igraph_vector, is_equal)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - long int i, s; + igraph_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1727,16 +2011,16 @@ igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return 0; + return false; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l >= r) { - return 0; + return false; } } - return 1; + return true; } } @@ -1758,7 +2042,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_l)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - long int i, s; + igraph_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1766,16 +2050,16 @@ igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return 0; + return false; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l <= r) { - return 0; + return false; } } - return 1; + return true; } } @@ -1797,7 +2081,7 @@ igraph_bool_t FUNCTION(igraph_vector, all_g)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - long int i, s; + igraph_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1805,16 +2089,16 @@ FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return 0; + return false; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l > r) { - return 0; + return false; } } - return 1; + return true; } } @@ -1836,7 +2120,7 @@ FUNCTION(igraph_vector, all_le)(const TYPE(igraph_vector) *lhs, igraph_bool_t FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, const TYPE(igraph_vector) *rhs) { - long int i, s; + igraph_integer_t i, s; IGRAPH_ASSERT(lhs != 0); IGRAPH_ASSERT(rhs != 0); IGRAPH_ASSERT(lhs->stor_begin != 0); @@ -1844,16 +2128,16 @@ FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, s = FUNCTION(igraph_vector, size)(lhs); if (s != FUNCTION(igraph_vector, size)(rhs)) { - return 0; + return false; } else { for (i = 0; i < s; i++) { BASE l = VECTOR(*lhs)[i]; BASE r = VECTOR(*rhs)[i]; if (l < r) { - return 0; + return false; } } - return 1; + return true; } } @@ -1863,8 +2147,7 @@ FUNCTION(igraph_vector, all_ge)(const TYPE(igraph_vector) *lhs, #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); + BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end); /** * \ingroup vector @@ -1880,7 +2163,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto * * \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 + * \param pos Pointer to an \type igraph_integer_t. 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 @@ -1895,7 +2178,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto */ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, - BASE what, long int *pos) { + BASE what, igraph_integer_t *pos) { return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, 0, FUNCTION(igraph_vector, size)(v)); } @@ -1917,7 +2200,7 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, * * \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 + * \param pos Pointer to an \type igraph_integer_t. 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). @@ -1931,10 +2214,9 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch)(const TYPE(igraph_vector) *v, */ 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; + BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { + igraph_integer_t left = start; + igraph_integer_t right = end - 1; if (left < 0) IGRAPH_ERROR("Invalid start position.", IGRAPH_EINVAL); @@ -1950,14 +2232,13 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) } 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; + BASE what, igraph_integer_t *pos, igraph_integer_t start, igraph_integer_t end) { + igraph_integer_t left = start; + igraph_integer_t right = end - 1; while (left <= right) { /* (right + left) / 2 could theoretically overflow for long vectors */ - long int middle = left + ((right - left) >> 1); + igraph_integer_t middle = left + ((right - left) >> 1); if (VECTOR(*v)[middle] > what) { right = middle - 1; } else if (VECTOR(*v)[middle] < what) { @@ -1966,7 +2247,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto if (pos != 0) { *pos = middle; } - return 1; + return true; } } @@ -1975,7 +2256,7 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto *pos = left; } - return 0; + return false; } /** @@ -1997,29 +2278,29 @@ igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vecto 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; + igraph_integer_t left = 0; + igraph_integer_t 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); + igraph_integer_t 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 true; } } - return 0; + return false; } #endif /** * \function igraph_vector_scale - * \brief Multiply all elements of a vector by a constant + * \brief Multiplies all elements of a vector by a constant. * * \param v The vector. * \param by The constant. @@ -2031,7 +2312,7 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch2)(const TYPE(igraph_vector) *v, */ void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) { - long int i; + igraph_integer_t i; for (i = 0; i < FUNCTION(igraph_vector, size)(v); i++) { #ifdef PROD PROD(VECTOR(*v)[i], VECTOR(*v)[i], by); @@ -2054,7 +2335,7 @@ void FUNCTION(igraph_vector, scale)(TYPE(igraph_vector) *v, BASE by) { */ void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) { - long int i, n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); for (i = 0; i < n; i++) { #ifdef SUM SUM(VECTOR(*v)[i], VECTOR(*v)[i], plus); @@ -2072,7 +2353,7 @@ void FUNCTION(igraph_vector, add_constant)(TYPE(igraph_vector) *v, BASE plus) { * 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. + * \return \c true if the element is found and \c false otherwise. * * Time complexity: O(n), the length of the vector. */ @@ -2086,27 +2367,28 @@ igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, #else if (*p == e) { #endif - return 1; + return true; } p++; } - return 0; + return false; } /** * \function igraph_vector_search - * \brief Search from a given position + * \brief Searches in a vector 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 + * \return Boolean, \c true if the element was found, \c false * otherwise. * * Time complexity: O(m), the number of elements to search, the length @@ -2114,9 +2396,8 @@ igraph_bool_t FUNCTION(igraph_vector, contains)(const TYPE(igraph_vector) *v, */ 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); + igraph_integer_t from, BASE what, igraph_integer_t *pos) { + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); for (i = from; i < n; i++) { #ifdef EQ if (EQ(VECTOR(*v)[i], what)) { @@ -2133,9 +2414,9 @@ igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, if (pos != 0) { *pos = i; } - return 1; + return true; } else { - return 0; + return false; } } @@ -2146,10 +2427,10 @@ igraph_bool_t FUNCTION(igraph_vector, search)(const TYPE(igraph_vector) *v, * \ingroup internal */ -int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, +igraph_error_t FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, BASE elem) { - long int i = 0, n = FUNCTION(igraph_vector, size)(v); - long int s; + igraph_integer_t i = 0, n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t s; while (i < n && VECTOR(*v)[i] < elem) { i++; } @@ -2160,7 +2441,7 @@ int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, } FUNCTION(igraph_vector, remove_section)(v, 0, i + (s - i) / 2); - return 0; + return IGRAPH_SUCCESS; } #endif @@ -2177,38 +2458,39 @@ int FUNCTION(igraph_vector, filter_smaller)(TYPE(igraph_vector) *v, * Time complexity: O(n), the number of elements in the new vector. */ -int FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, +igraph_error_t FUNCTION(igraph_vector, append)(TYPE(igraph_vector) *to, const TYPE(igraph_vector) *from) { - long tosize, fromsize; + igraph_integer_t tosize, fromsize; + igraph_integer_t newsize; tosize = FUNCTION(igraph_vector, size)(to); fromsize = FUNCTION(igraph_vector, size)(from); - IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, tosize + fromsize)); + IGRAPH_SAFE_ADD(tosize, fromsize, &newsize); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, newsize)); memcpy(to->stor_begin + tosize, from->stor_begin, - sizeof(BASE) * (size_t) fromsize); + sizeof(BASE) * fromsize); to->end = to->stor_begin + tosize + fromsize; - return 0; + return IGRAPH_SUCCESS; } /** * \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_error_t FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, + TYPE(igraph_vector) *res, igraph_integer_t from, igraph_integer_t 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; + (to - from) * sizeof(BASE)); + return IGRAPH_SUCCESS; } #ifndef NOTORDERED /** * \function igraph_vector_maxdifference - * \brief The maximum absolute difference of \p m1 and \p m2 + * \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 @@ -2224,10 +2506,10 @@ int FUNCTION(igraph_vector, get_interval)(const TYPE(igraph_vector) *v, 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_integer_t n1 = FUNCTION(igraph_vector, size)(m1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(m2); + igraph_integer_t n = n1 < n2 ? n1 : n2; + igraph_integer_t i; igraph_real_t diff = 0.0; for (i = 0; i < n; i++) { @@ -2236,8 +2518,8 @@ igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) * if (d > diff) { diff = d; } - #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) - else if (igraph_is_nan(d)) { /* Result is NaN */ + #if defined(BASE_IGRAPH_REAL) + else if (isnan(d)) { /* Result is NaN */ return d; }; #endif @@ -2262,17 +2544,17 @@ igraph_real_t FUNCTION(igraph_vector, maxdifference)(const TYPE(igraph_vector) * * Time complexity: O(n), the number of elements in \p from. */ -int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, +igraph_error_t 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)); + igraph_integer_t n = FUNCTION(igraph_vector, size)(from); + IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(to, n)); memcpy(to->stor_begin, from->stor_begin, sizeof(BASE)*n); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_vector_swap - * \brief Swap elements of two vectors. + * \brief Swap all elements of two vectors. * * \param v1 The first vector. * \param v2 The second vector. @@ -2281,7 +2563,7 @@ int FUNCTION(igraph_vector, update)(TYPE(igraph_vector) *to, * Time complexity: O(1). */ -int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { +igraph_error_t FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) *v2) { TYPE(igraph_vector) tmp; @@ -2306,13 +2588,13 @@ int FUNCTION(igraph_vector, swap)(TYPE(igraph_vector) *v1, TYPE(igraph_vector) * * Time complexity: O(1). */ -int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, - long int i, long int j) { +igraph_error_t FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, + igraph_integer_t i, igraph_integer_t j) { BASE tmp = VECTOR(*v)[i]; VECTOR(*v)[i] = VECTOR(*v)[j]; VECTOR(*v)[j] = tmp; - return 0; + return IGRAPH_SUCCESS; } /** @@ -2327,23 +2609,23 @@ int FUNCTION(igraph_vector, swap_elements)(TYPE(igraph_vector) *v, * Time complexity: O(n), the number of elements. */ -int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { +igraph_error_t FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { - long int n = FUNCTION(igraph_vector, size)(v), n2 = n / 2; - long int i, j; + igraph_integer_t n = FUNCTION(igraph_vector, size)(v), n2 = n / 2; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /** * \ingroup vector * \function igraph_vector_shuffle - * \brief Shuffles a vector in-place using the Fisher-Yates method + * \brief Shuffles a vector in-place using the Fisher-Yates method. * * * The Fisher-Yates shuffle ensures that every permutation is @@ -2373,9 +2655,9 @@ int FUNCTION(igraph_vector, reverse)(TYPE(igraph_vector) *v) { * \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; +igraph_error_t FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { + igraph_integer_t n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t k; BASE dummy; RNG_BEGIN(); @@ -2404,14 +2686,14 @@ int FUNCTION(igraph_vector, shuffle)(TYPE(igraph_vector) *v) { * Time complexity: O(n), the number of elements. */ -int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, +igraph_error_t 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; + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; if (n1 != n2) { - IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_ERROR("Vectors to be added must have the same sizes.", IGRAPH_EINVAL); } @@ -2423,7 +2705,7 @@ int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, #endif } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2440,14 +2722,14 @@ int FUNCTION(igraph_vector, add)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the length of the vectors. */ -int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, +igraph_error_t 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; + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; if (n1 != n2) { - IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_ERROR("Vectors to be subtracted must have the same sizes.", IGRAPH_EINVAL); } @@ -2459,7 +2741,7 @@ int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, #endif } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2475,14 +2757,14 @@ int FUNCTION(igraph_vector, sub)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the number of elements. */ -int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, +igraph_error_t 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; + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; if (n1 != n2) { - IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_ERROR("Vectors to be multiplied must have the same sizes.", IGRAPH_EINVAL); } @@ -2494,7 +2776,7 @@ int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, #endif } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2512,14 +2794,14 @@ int FUNCTION(igraph_vector, mul)(TYPE(igraph_vector) *v1, * Time complexity: O(n), the length of the vectors. */ -int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, +igraph_error_t 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; + igraph_integer_t n1 = FUNCTION(igraph_vector, size)(v1); + igraph_integer_t n2 = FUNCTION(igraph_vector, size)(v2); + igraph_integer_t i; if (n1 != n2) { - IGRAPH_ERROR("Vectors must have the same number of elements for swapping", + IGRAPH_ERROR("Vectors to be divided must have the same sizes.", IGRAPH_EINVAL); } @@ -2531,23 +2813,23 @@ int FUNCTION(igraph_vector, div)(TYPE(igraph_vector) *v1, #endif } - return 0; + return IGRAPH_SUCCESS; } #ifndef NOABS -int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { +igraph_error_t 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); + igraph_integer_t 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; + return IGRAPH_SUCCESS; } #endif @@ -2561,25 +2843,23 @@ int FUNCTION(igraph_vector, abs)(TYPE(igraph_vector) *v) { * 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. + * \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. * * Time complexity: O(n), the number of elements. */ -int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, +void 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 */ + #if defined(BASE_IGRAPH_REAL) + if (isnan(*min)) { return; }; /* Result is NaN */ #endif ptr = v->stor_begin + 1; while (ptr < v->end) { @@ -2588,40 +2868,38 @@ int FUNCTION(igraph_vector, minmax)(const TYPE(igraph_vector) *v, } else if (*ptr < *min) { *min = *ptr; } - #if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) - else if (igraph_is_nan(*ptr)) { /* Result is NaN */ + #if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { /* Result is NaN */ *min = *max = *ptr; - return IGRAPH_SUCCESS; + return; }; #endif ptr++; } - return IGRAPH_SUCCESS; } /** * \function igraph_vector_which_minmax - * \brief Index of the minimum and maximum elements + * \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) { +void FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, + igraph_integer_t *which_min, igraph_integer_t *which_max) { BASE *min, *max; BASE *ptr; IGRAPH_ASSERT(v != NULL); @@ -2629,10 +2907,10 @@ int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, 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 */ + #if defined(BASE_IGRAPH_REAL) + if (isnan(*ptr)) { /* Result is NaN */ *which_min = *which_max = 0; - return IGRAPH_SUCCESS; + return; } #endif while (ptr < v->end) { @@ -2641,17 +2919,16 @@ int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, } else if (*ptr < *min) { min = ptr; } -#if defined(BASE_IGRAPH_REAL) || defined(BASE_FLOAT) - else if (igraph_is_nan(*ptr)) { /* Result is NaN */ +#if defined(BASE_IGRAPH_REAL) + else if (isnan(*ptr)) { /* Result is NaN */ *which_min = *which_max = ptr - v->stor_begin; - return IGRAPH_SUCCESS; + return; } #endif ptr++; } *which_min = min - v->stor_begin; *which_max = max - v->stor_begin; - return IGRAPH_SUCCESS; } #endif @@ -2662,21 +2939,21 @@ int FUNCTION(igraph_vector, which_minmax)(const TYPE(igraph_vector) *v, * * 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. + * \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; + igraph_integer_t n = FUNCTION(igraph_vector, size)(v); + igraph_integer_t i = 0; #ifdef EQ - while (i < n && EQ(VECTOR(*v)[i], ZERO)) { + while (i < n && EQ(VECTOR(*v)[i], (BASE) ZERO)) { #else - while (i < n && VECTOR(*v)[i] == ZERO) { + while (i < n && VECTOR(*v)[i] == (BASE) ZERO) { #endif i++; } @@ -2686,14 +2963,14 @@ igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v) { #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, +igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( + const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, + const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, TYPE(igraph_vector) *result); /** * \function igraph_vector_intersect_sorted - * \brief Calculates the intersection of two sorted vectors + * \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. @@ -2721,9 +2998,9 @@ int FUNCTION(igraph_i_vector, intersect_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, +igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { - long int size1, size2; + igraph_integer_t size1, size2; size1 = FUNCTION(igraph_vector, size)(v1); size2 = FUNCTION(igraph_vector, size)(v2); @@ -2731,22 +3008,22 @@ int FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, FUNCTION(igraph_vector, clear)(result); if (size1 == 0 || size2 == 0) { - return 0; + return IGRAPH_SUCCESS; } IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( v1, 0, size1, v2, 0, size2, result)); - return 0; + return IGRAPH_SUCCESS; } -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, +igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( + const TYPE(igraph_vector) *v1, igraph_integer_t begin1, igraph_integer_t end1, + const TYPE(igraph_vector) *v2, igraph_integer_t begin2, igraph_integer_t end2, TYPE(igraph_vector) *result) { - long int size1, size2, probe1, probe2; + igraph_integer_t size1, size2, probe1, probe2; if (begin1 == end1 || begin2 == end2) { - return 0; + return IGRAPH_SUCCESS; } size1 = end1 - begin1; @@ -2780,12 +3057,12 @@ int FUNCTION(igraph_i_vector, intersect_sorted)( )); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_vector_difference_sorted - * \brief Calculates the difference between two sorted vectors (considered as sets) + * \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. @@ -2794,9 +3071,9 @@ int FUNCTION(igraph_i_vector, intersect_sorted)( * \param v2 the second vector * \param result the result vector */ -int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, +igraph_error_t 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; + igraph_integer_t i, j, i0, j0; i0 = FUNCTION(igraph_vector, size)(v1); j0 = FUNCTION(igraph_vector, size)(v2); i = j = 0; @@ -2810,7 +3087,7 @@ int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, 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); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * i0); return IGRAPH_SUCCESS; } @@ -2822,7 +3099,7 @@ int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, } if (i > 0) { IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(result, i)); - memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * (size_t) i); + memcpy(result->stor_begin, v1->stor_begin, sizeof(BASE) * i); } while (i < i0 && j < j0) { @@ -2843,43 +3120,21 @@ int FUNCTION(igraph_vector, difference_sorted)(const TYPE(igraph_vector) *v1, } } if (i < i0) { - long int oldsize = FUNCTION(igraph_vector, size)(result); + igraph_integer_t 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)); + sizeof(BASE) * (i0 - i)); } - return 0; + return IGRAPH_SUCCESS; } #endif -#if defined(OUT_FORMAT) - +#ifdef 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); +igraph_error_t FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, const char *format) { + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); if (n != 0) { printf(format, VECTOR(*v)[0]); } @@ -2887,13 +3142,21 @@ int FUNCTION(igraph_vector, printf)(const TYPE(igraph_vector) *v, putchar(' '); printf(format, VECTOR(*v)[i]); } printf("\n"); - return 0; + return IGRAPH_SUCCESS; } +#endif /* USING_R */ +#endif /* OUT_FORMAT */ + +#if defined(OUT_FORMAT) || defined(FPRINTFUNC) +#ifndef USING_R +igraph_error_t FUNCTION(igraph_vector, print)(const TYPE(igraph_vector) *v) { + return FUNCTION(igraph_vector, fprint)(v, stdout); +} #endif -int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { - long int i, n = FUNCTION(igraph_vector, size)(v); +igraph_error_t FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { + igraph_integer_t i, n = FUNCTION(igraph_vector, size)(v); if (n != 0) { #ifdef FPRINTFUNC FPRINTFUNC(file, VECTOR(*v)[0]); @@ -2909,34 +3172,34 @@ int FUNCTION(igraph_vector, fprint)(const TYPE(igraph_vector) *v, FILE *file) { #endif } fprintf(file, "\n"); - return 0; + return IGRAPH_SUCCESS; } -#endif +#endif /* defined(OUT_FORMAT) || defined(FPRINTFUNC) */ -int FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, +igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, - const igraph_vector_t *idx) { + const igraph_vector_int_t *idx) { - long int i, newlen = igraph_vector_size(idx); + igraph_integer_t i, j, newlen = igraph_vector_int_size(idx); IGRAPH_CHECK(FUNCTION(igraph_vector, resize)(newv, newlen)); for (i = 0; i < newlen; i++) { - long int j = (long int) VECTOR(*idx)[i]; + j = VECTOR(*idx)[i]; VECTOR(*newv)[i] = VECTOR(*v)[j]; } - return 0; + return IGRAPH_SUCCESS; } -int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, +igraph_error_t 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); + igraph_integer_t i, n = igraph_vector_int_size(idx); tmp = IGRAPH_CALLOC(n, BASE); if (!tmp) { - IGRAPH_ERROR("Cannot index vector", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot index vector.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } for (i = 0; i < n; i++) { @@ -2947,5 +3210,5 @@ int FUNCTION(igraph_vector, index_int)(TYPE(igraph_vector) *v, v->stor_begin = tmp; v->stor_end = v->end = tmp + n; - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/core/vector_list.c b/src/vendor/cigraph/src/core/vector_list.c new file mode 100644 index 00000000000..5f761b11530 --- /dev/null +++ b/src/vendor/cigraph/src/core/vector_list.c @@ -0,0 +1,171 @@ +/* -*- 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 + +*/ + +#include "igraph_vector_list.h" + +#define VECTOR_LIST + +#define BASE_IGRAPH_REAL +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef BASE_IGRAPH_REAL + +#define BASE_INT +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef BASE_INT + +#undef VECTOR_LIST + +/** + * \ingroup vector_list + * \section about_igraph_vector_list_t_objects About \type igraph_vector_list_t objects + * + * The \type igraph_vector_list_t data type is essentially a list of + * \type igraph_vector_t objects with automatic memory management. It is something + * similar to (but much simpler than) the \type vector template in the C++ + * standard library where the elements are vectors themselves. + * + * There are multiple variants of \type igraph_vector_list_t; the basic variant + * stores vectors of doubles (i.e. each item is an \ref igraph_vector_t), but + * there is also \type igraph_vector_int_list_t for integers (where each item is + * an \type igraph_vector_int_t), \type igraph_matrix_list_t for matrices of + * doubles and so on. The following list summarizes the variants that are + * currently available in the library: + * + * \ilist + * \ili \type igraph_vector_list_t for lists of vectors of floating-point numbers + * (\type igraph_vector_t) + * \ili \type igraph_vector_int_list_t for lists of integer vectors + * (\type igraph_vector_int_t) + * \ili \type igraph_matrix_list_t for lists of matrices of floating-point numbers + * (\type igraph_matrix_t) + * \ili \type igraph_graph_list_t for lists of graphs (\type igraph_t) + * \endilist + * + * Lists of vectors are used in \a igraph in many + * cases, e.g., when returning lists of paths, cliques or vertex sets. + * Functions that expect or return a list of numeric vectors typically use + * \type igraph_vector_list_t or \type igraph_vector_int_list_t to achieve this. + * Lists of integer vectors are used when the vectors in the list are supposed + * to hold vertex or edge identifiers, while lists of floating-point vectors + * are used when the vectors are expected to hold fractional numbers or + * infinities. + * + * The elements in an \type igraph_vector_list_t object and its variants are + * indexed from zero, we follow the usual C convention here. + * + * Almost all of the functions described below for \type igraph_vector_list_t + * also exist for all the other vector list variants. These variants are not + * documented separately; you can simply replace \c vector_list with, say, + * \c vector_int_list if you need a function for another variant. For instance, + * to initialize a list of integer vectors, you need to use + * \c igraph_vector_int_list_init() and not \ref igraph_vector_list_init(). + * + * Before diving into a detailed description of the functions related to + * lists of vectors, we must also talk about the \em ownership rules of these + * objects. The most important rule is that the vectors in the list are + * owned by the list itself, meaning that the user is \em not responsible + * for allocating memory for the vectors or for freeing the memory associated + * to the vectors. It is the responsibility of the list to allocate and initialize + * the vectors when new items are created in the list, and it is also the + * responsibility of the list to destroy the items when they are removed from + * the list without passing on their ownership to the user. As a consequence, + * the list may not contain "uninitialized" or "null" items; each item is + * initialized when it comes to existence. If you create a list containing + * one million vectors, you are not only allocating memory for one million + * \ref igraph_vector_t object but you are also initializing one million + * vectors. Also, if you have a list containing one million vectors and you + * clear the list by calling \ref igraph_vector_list_clear(), the list will + * implicitly destroy these lists, and any pointers that you may hold to the + * items become invalid at once. + * + * Speaking about pointers, the typical way of working with vectors in + * a list is to obtain a pointer to one of the items via the + * \ref igraph_vector_list_get_ptr() method and then passing this pointer + * onwards to functions that manipulate \ref igraph_vector_t objects. However, + * note that the pointers are \em ephemeral in the sense that they may be + * invalidated any time when the list is modified because a modification may + * involve the re-allocation of the internal storage of the list if more space + * is needed, and the pointers that you obtained will not follow the + * reallocation. This limitation does not appear often in real-world usage of + * \c igraph_vector_list_t and in general, the advantages of the automatic + * memory management outweigh this limitation. + */ + +/** + * \ingroup vector_list + * \section igraph_vector_list_constructors_and_destructors Constructors and + * destructors + * + * \type igraph_vector_list_t objects have to be initialized before using + * them, this is analogous to calling a constructor on them. + * \ref igraph_vector_list_init() is the basic constructor; it creates a list + * of the given length and also initializes each vector in the newly created + * list to zero length. + * + * If an \type igraph_vector_list_t object is not needed any more, it + * should be destroyed to free its allocated memory by calling the + * \type igraph_vector_list_t destructor, \ref igraph_vector_list_destroy(). + * Calling the destructor also destroys all the vectors inside the vector + * list due to the ownership rules. If you want to keep a few of the vectors + * in the vector list, you need to copy them with \ref igraph_vector_init_copy() or + * \ref igraph_vector_update(), or you need to remove them from the list and + * take ownership by calling \ref igraph_vector_list_pop_back(), + * \ref igraph_vector_list_remove() or \ref igraph_vector_list_remove_fast() . + */ + + +/** + * \ingroup vector_list + * \section igraph_vector_list_accessing_elements Accessing elements + * + * Elements of a vector list may be accessed with the + * \ref igraph_vector_list_get_ptr() function. The function returns a \em pointer + * to the vector with a given index inside the list, and you may then pass + * this pointer onwards to other functions that can query or manipulate + * vectors. The pointer itself is guaranteed to stay valid as long as the + * list itself is not modified; however, \em any modification to the list + * will invalidate the pointer, even modifications that are seemingly unrelated + * to the vector that the pointer points to (such as adding a new vector at + * the end of the list). This is because the list data structure may be forced + * to re-allocate its internal storage if a new element does not fit into the + * already allocated space, and there are no guarantees that the re-allocated + * block remains at the same memory location (typically it gets moved elsewhere). + * + * + * Note that the standard \ref VECTOR macro that works for ordinary vectors + * does not work for lists of vectors to access the i-th element (but of course + * you can use it to index into an existing vector that you retrieved from the + * vector list with \ref igraph_vector_list_get_ptr() ). This is because the + * macro notation would allow one to overwrite the vector in the list with + * another one without the list knowing about it, so the list would not be able + * to destroy the vector that was overwritten by a new one. + * + * + * \ref igraph_vector_list_tail_ptr() returns a pointer to the last + * vector in the list, or \c NULL if the list is empty. There is no + * igraph_vector_list_head_ptr(), however, as it is easy to + * write igraph_vector_list_get_ptr(v, 0) instead. + */ diff --git a/src/vendor/cigraph/src/core/vector_ptr.c b/src/vendor/cigraph/src/core/vector_ptr.c index d3358bc3322..ce063fd69da 100644 --- a/src/vendor/cigraph/src/core/vector_ptr.c +++ b/src/vendor/cigraph/src/core/vector_ptr.c @@ -21,13 +21,14 @@ */ -#include "igraph_types.h" #include "igraph_vector_ptr.h" + +#include "igraph_types.h" #include "igraph_memory.h" -#include "igraph_error.h" #include "igraph_qsort.h" #include /* memcpy & co. */ +#include /* uintptr_t */ #include /** @@ -42,11 +43,6 @@ * 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 @@ -61,7 +57,7 @@ * 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 + * instance, a vector of graphs generated by some function can * be destroyed with a single call to \ref igraph_vector_ptr_destroy_all() * if the item destructor is set to \ref igraph_destroy(). */ @@ -86,28 +82,29 @@ * 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_error_t igraph_vector_ptr_init(igraph_vector_ptr_t* v, igraph_integer_t size) { + igraph_integer_t 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); + IGRAPH_ERROR("vector ptr init failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } v->stor_end = v->stor_begin + alloc_size; v->end = v->stor_begin + size; v->item_destructor = 0; - return 0; + return IGRAPH_SUCCESS; } /** */ -const igraph_vector_ptr_t *igraph_vector_ptr_view(const igraph_vector_ptr_t *v, void *const *data, - long int length) { +const igraph_vector_ptr_t *igraph_vector_ptr_view( + const igraph_vector_ptr_t *v, void *const *data, igraph_integer_t length +) { igraph_vector_ptr_t *v2 = (igraph_vector_ptr_t*) v; v2->stor_begin = (void **)data; v2->stor_end = (void**)data + length; @@ -214,25 +211,25 @@ void igraph_vector_ptr_destroy_all(igraph_vector_ptr_t* v) { * - 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); +igraph_error_t igraph_vector_ptr_reserve(igraph_vector_ptr_t* v, igraph_integer_t capacity) { + igraph_integer_t actual_size = igraph_vector_ptr_size(v); void **tmp; IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); - if (size <= igraph_vector_ptr_size(v)) { - return 0; + if (capacity <= igraph_vector_ptr_size(v)) { + return IGRAPH_SUCCESS; } - tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) size, void*); - if (tmp == 0) { - IGRAPH_ERROR("vector ptr reserve failed", IGRAPH_ENOMEM); - } + tmp = IGRAPH_REALLOC(v->stor_begin, (size_t) capacity, void*); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for pointer vector."); + v->stor_begin = tmp; - v->stor_end = v->stor_begin + size; + v->stor_end = v->stor_begin + capacity; v->end = v->stor_begin + actual_size; - return 0; + return IGRAPH_SUCCESS; } /** @@ -257,7 +254,7 @@ igraph_bool_t igraph_vector_ptr_empty(const igraph_vector_ptr_t* v) { * Time complexity: O(1). */ -long int igraph_vector_ptr_size(const igraph_vector_ptr_t* v) { +igraph_integer_t 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; @@ -301,7 +298,7 @@ void igraph_vector_ptr_clear(igraph_vector_ptr_t* v) { * \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 + * \sa \ref 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 @@ -309,13 +306,13 @@ void igraph_vector_ptr_clear(igraph_vector_ptr_t* v) { * push_back operations need O(n) time to complete. */ -int igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e) { +igraph_error_t 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; + igraph_integer_t new_size = igraph_vector_ptr_size(v) * 2; if (new_size == 0) { new_size = 1; } @@ -325,9 +322,24 @@ int igraph_vector_ptr_push_back(igraph_vector_ptr_t* v, void* e) { *(v->end) = e; v->end += 1; - return 0; + return IGRAPH_SUCCESS; } + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_pop_back + * \brief Removes and returns the last element of a pointer vector. + * + * + * It is an error to call this function with an empty vector. + * + * \param v The pointer vector. + * \return The removed last element. + * + * Time complexity: O(1). + */ + void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); @@ -350,20 +362,20 @@ void *igraph_vector_ptr_pop_back(igraph_vector_ptr_t *v) { * \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_error_t igraph_vector_ptr_insert(igraph_vector_ptr_t* v, igraph_integer_t pos, void* e) { + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /** * \ingroup vectorptr - * \function igraph_vector_ptr_e + * \function igraph_vector_ptr_get * \brief Access an element of a pointer vector. * * \param v Pointer to a pointer vector. @@ -373,12 +385,24 @@ int igraph_vector_ptr_insert(igraph_vector_ptr_t* v, long int pos, void* e) { * Time complexity: O(1). */ -void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, long int pos) { +void *igraph_vector_ptr_get(const igraph_vector_ptr_t* v, igraph_integer_t pos) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); return *(v->stor_begin + pos); } +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_e + * \brief Access an element of a pointer vector (deprecated alias). + * + * \deprecated-by igraph_vector_ptr_get 0.10.0 + */ + +void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, igraph_integer_t pos) { + return igraph_vector_ptr_get(v, pos); +} + /** * \ingroup vectorptr * \function igraph_vector_ptr_set @@ -391,7 +415,7 @@ void *igraph_vector_ptr_e(const igraph_vector_ptr_t* v, long int pos) { * Time complexity: O(1). */ -void igraph_vector_ptr_set(igraph_vector_ptr_t* v, long int pos, void* value) { +void igraph_vector_ptr_set(igraph_vector_ptr_t* v, igraph_integer_t pos, void* value) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); *(v->stor_begin + pos) = value; @@ -430,31 +454,36 @@ void igraph_vector_ptr_null(igraph_vector_ptr_t* v) { * needed to allocate the memory for the vector elements. */ -int igraph_vector_ptr_resize(igraph_vector_ptr_t* v, long int newsize) { +igraph_error_t igraph_vector_ptr_resize(igraph_vector_ptr_t* v, igraph_integer_t newsize) { IGRAPH_CHECK(igraph_vector_ptr_reserve(v, newsize)); v->end = v->stor_begin + newsize; - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup vectorptr * \brief Initializes a pointer vector from an array (constructor). * + * \param v Pointer to an uninitialized + * igraph_vector_ptr_t object to be initialized. + * \param data The array of pointers that serves as the initial contents of the + * pointer vector. + * \param length Integer, the length of the array. * \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) { +igraph_error_t igraph_vector_ptr_init_array(igraph_vector_ptr_t *v, void *const *data, igraph_integer_t length) { v->stor_begin = IGRAPH_CALLOC(length, void*); if (v->stor_begin == 0) { - IGRAPH_ERROR("cannot init ptr vector from array", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot initialize pointer vector from array", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + return IGRAPH_SUCCESS; } /** @@ -473,8 +502,8 @@ void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { /** * \ingroup vectorptr - * \function igraph_vector_ptr_copy - * \brief Copy a pointer vector (constructor). + * \function igraph_vector_ptr_init_copy + * \brief Initializes a pointer vector from another one (constructor). * * * This function creates a pointer vector by copying another one. This @@ -497,8 +526,8 @@ void igraph_vector_ptr_copy_to(const igraph_vector_ptr_t *v, void** to) { * 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_error_t igraph_vector_ptr_init_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + igraph_integer_t from_size; IGRAPH_ASSERT(from != NULL); /* IGRAPH_ASSERT(from->stor_begin != NULL); */ /* TODO */ @@ -507,7 +536,7 @@ int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *f to->stor_begin = IGRAPH_CALLOC(from_size, void*); if (to->stor_begin == 0) { - IGRAPH_ERROR("cannot copy ptr vector", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot copy pointer vector", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } to->stor_end = to->stor_begin + igraph_vector_ptr_size(from); to->end = to->stor_end; @@ -515,7 +544,19 @@ int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *f memcpy(to->stor_begin, from->stor_begin, (size_t) igraph_vector_ptr_size(from)*sizeof(void*)); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_copy + * \brief Initializes a pointer vector from another one (deprecated alias). + * + * \deprecated-by igraph_vector_ptr_init_copy 0.10 + */ + +igraph_error_t igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + return igraph_vector_ptr_init_copy(to, from); } /** @@ -523,7 +564,7 @@ int igraph_vector_ptr_copy(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *f * \brief Remove an element from a pointer vector. */ -void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos) { +void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, igraph_integer_t 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. */ @@ -553,46 +594,23 @@ void igraph_vector_ptr_remove(igraph_vector_ptr_t *v, long int pos) { * 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_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, const igraph_vector_ptr_t *from) { + igraph_integer_t origsize = igraph_vector_ptr_size(to); + igraph_integer_t othersize = igraph_vector_ptr_size(from); + igraph_integer_t 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; + return IGRAPH_SUCCESS; } @@ -637,3 +655,148 @@ igraph_finally_func_t* igraph_vector_ptr_get_item_destructor(const igraph_vector IGRAPH_ASSERT(v != 0); return v->item_destructor; } + +typedef int cmp_t (const void *, const void *); + +/** + * Comparison function passed to qsort_r from igraph_vector_ptr_sort_ind + */ +static int igraph_vector_ptr_i_sort_ind_cmp(void *thunk, const void *p1, const void *p2) { + cmp_t *cmp = (cmp_t *) thunk; + uintptr_t *pa = (uintptr_t*) p1; + uintptr_t *pb = (uintptr_t*) p2; + void **item_a_ptr = (void**) *pa; + void **item_b_ptr = (void**) *pb; + return cmp(*item_a_ptr, *item_b_ptr); +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_sort_ind + * \brief Returns a permutation of indices that sorts a vector of pointers. + * + * 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. + * + * \param v the array to be sorted + * \param inds the output array of indices. This must be initialized, + * but will be resized + * \param cmp a comparator function that takes two elements of the pointer + * vector being sorted (these are constant pointers on their own) + * and returns a negative value if the item \em "pointed to" by the + * first pointer is smaller than the item \em "pointed to" by the + * second pointer, a positive value if it is larger, or zero if the + * two items are equal + * \return Error code. + * + * This routine uses the C library 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. + */ + +igraph_error_t igraph_vector_ptr_sort_ind(igraph_vector_ptr_t *v, + igraph_vector_int_t *inds, cmp_t *cmp) { + igraph_integer_t i; + uintptr_t *vind, first; + igraph_integer_t n = igraph_vector_ptr_size(v); + + IGRAPH_CHECK(igraph_vector_int_resize(inds, n)); + if (n == 0) { + return IGRAPH_SUCCESS; + } + + vind = IGRAPH_CALLOC(n, uintptr_t); + if (vind == 0) { + IGRAPH_ERROR("igraph_vector_ptr_sort_ind failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + for (i = 0; i < n; i++) { + vind[i] = (uintptr_t) &VECTOR(*v)[i]; + } + + first = vind[0]; + + igraph_qsort_r(vind, n, sizeof(vind[0]), (void*)cmp, igraph_vector_ptr_i_sort_ind_cmp); + + for (i = 0; i < n; i++) { + VECTOR(*inds)[i] = (vind[i] - first) / sizeof(uintptr_t); + } + + IGRAPH_FREE(vind); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup vectorptr + * \function igraph_vector_ptr_permute + * \brief Permutes the elements of a pointer vector in place according to an index vector. + * + * + * This function takes a vector \c v and a corresponding index vector \c ind, + * and permutes the elements of \c v such that \c v[ind[i]] is moved to become + * \c v[i] after the function is executed. + * + * + * It is an error to call this function with an index vector that does not + * represent a valid permutation. Each element in the index vector must be + * between 0 and the length of the vector minus one (inclusive), and each such + * element must appear only once. The function does not attempt to validate the + * index vector. + * + * + * The index vector that this function takes is compatible with the index vector + * returned from \ref igraph_vector_ptr_sort_ind(); passing in the index vector + * from \ref igraph_vector_ptr_sort_ind() will sort the original vector. + * + * + * As a special case, this function allows the index vector to be \em shorter + * than the vector being permuted, in which case the elements whose indices do + * not occur in the index vector will be removed from the vector. + * + * \param v the vector to permute + * \param ind the index vector + * + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: O(n), the size of the vector. + */ +igraph_error_t igraph_vector_ptr_permute(igraph_vector_ptr_t* v, const igraph_vector_int_t* index) { + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + IGRAPH_ASSERT(index != NULL); + IGRAPH_ASSERT(index->stor_begin != NULL); + IGRAPH_ASSERT(igraph_vector_ptr_size(v) >= igraph_vector_int_size(index)); + + igraph_vector_ptr_t v_copy; + void** v_ptr; + igraph_integer_t *ind_ptr; + + /* There is a more space-efficient algorithm that needs O(1) space only, + * but it messes up the index vector, which we don't want */ + + IGRAPH_CHECK(igraph_vector_ptr_init(&v_copy, igraph_vector_int_size(index))); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &v_copy); + + for ( + v_ptr = v_copy.stor_begin, ind_ptr = index->stor_begin; + ind_ptr < index->end; + v_ptr++, ind_ptr++ + ) { + *v_ptr = VECTOR(*v)[*ind_ptr]; + } + + IGRAPH_CHECK(igraph_vector_ptr_resize(v, igraph_vector_int_size(index))); + igraph_vector_ptr_copy_to(&v_copy, VECTOR(*v)); + + igraph_vector_ptr_destroy(&v_copy); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/f2c.h b/src/vendor/cigraph/src/f2c.h index b8ec2e24d46..db5c073aa3a 100644 --- a/src/vendor/cigraph/src/f2c.h +++ b/src/vendor/cigraph/src/f2c.h @@ -7,6 +7,7 @@ #ifndef F2C_INCLUDE #define F2C_INCLUDE +#include "igraph_error.h" #include "linalg/blas_internal.h" #include "linalg/lapack_internal.h" #include "linalg/arpack_internal.h" @@ -145,7 +146,7 @@ union Multitype { /* for multiple entry points */ typedef union Multitype Multitype; -/*typedef long int Long;*/ /* No longer used; formerly in Namelist */ +/*typedef igraph_integer_t Long;*/ /* No longer used; formerly in Namelist */ struct Vardesc { /* for Namelist */ char *name; diff --git a/src/vendor/cigraph/src/flow/flow.c b/src/vendor/cigraph/src/flow/flow.c index ff82d89643b..ba1beb5f6fa 100644 --- a/src/vendor/cigraph/src/flow/flow.c +++ b/src/vendor/cigraph/src/flow/flow.c @@ -40,9 +40,8 @@ #include "core/buckets.h" #include "core/cutheap.h" #include "core/interruption.h" -#include "core/math.h" - -#include "config.h" +#include "flow/flow_internal.h" +#include "math/safe_intop.h" /* * Some general remarks about the functions in this file. @@ -157,33 +156,34 @@ * undirected edge. */ -static int igraph_i_maxflow_undirected(const igraph_t *graph, +static igraph_error_t 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_vector_int_t *cut, + igraph_vector_int_t *partition, + igraph_vector_int_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_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; igraph_vector_t newcapacity; igraph_t newgraph; - long int i; + igraph_integer_t size; /* 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 + sure that the edge IDs will be handled properly to build the new capacity vector. */ - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&newcapacity, no_of_edges * 2); - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 4)); + IGRAPH_SAFE_MULT(no_of_edges, 4, &size); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, size)); 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++) { + IGRAPH_CHECK(igraph_vector_int_resize(&edges, size)); + for (igraph_integer_t 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] = @@ -197,8 +197,8 @@ static int igraph_i_maxflow_undirected(const igraph_t *graph, partition2, source, target, &newcapacity, stats)); if (cut) { - long int i, cs = igraph_vector_size(cut); - for (i = 0; i < cs; i++) { + igraph_integer_t cs = igraph_vector_int_size(cut); + for (igraph_integer_t i = 0; i < cs; i++) { if (VECTOR(*cut)[i] >= no_of_edges) { VECTOR(*cut)[i] -= no_of_edges; } @@ -208,22 +208,21 @@ static int igraph_i_maxflow_undirected(const igraph_t *graph, /* 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 + 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++) { + for (igraph_integer_t 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_int_destroy(&edges); igraph_vector_destroy(&newcapacity); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } #define FIRST(i) (VECTOR(*first)[(i)]) @@ -254,33 +253,33 @@ static int igraph_i_maxflow_undirected(const igraph_t *graph, &first, ¤t, &to, &excess, \ &rescap, &rev)) -static void igraph_i_mf_gap(long int b, igraph_maxflow_stats_t *stats, +static void igraph_i_mf_gap(igraph_integer_t 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_integer_t no_of_nodes, + igraph_vector_int_t *distance) { IGRAPH_UNUSED(buckets); - long int bo; + igraph_integer_t 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); + igraph_integer_t 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) { +static void igraph_i_mf_relabel(igraph_integer_t v, igraph_integer_t no_of_nodes, + igraph_vector_int_t *distance, + igraph_vector_int_t *first, + igraph_vector_t *rescap, igraph_vector_int_t *to, + igraph_vector_int_t *current, + igraph_maxflow_stats_t *stats, igraph_integer_t *nrelabelsince) { - long int min = no_of_nodes; - long int k, l, min_edge = 0; + igraph_integer_t min = no_of_nodes; + igraph_integer_t k, l, min_edge = 0; (stats->norelabel)++; (*nrelabelsince)++; DIST(v) = no_of_nodes; for (k = FIRST(v), l = LAST(v); k < l; k++) { @@ -296,14 +295,14 @@ static void igraph_i_mf_relabel(long int v, long int no_of_nodes, } } -static void igraph_i_mf_push(long int v, long int e, long int n, - igraph_vector_long_t *current, +static void igraph_i_mf_push(igraph_integer_t v, igraph_integer_t e, igraph_integer_t n, + igraph_vector_int_t *current, igraph_vector_t *rescap, igraph_vector_t *excess, - long int target, long int source, + igraph_integer_t target, igraph_integer_t 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_vector_int_t *distance, + igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, + igraph_integer_t *npushsince) { IGRAPH_UNUSED(current); @@ -314,7 +313,7 @@ static void igraph_i_mf_push(long int v, long int e, long int n, (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); + igraph_buckets_add(buckets, DIST(n), n); } RESCAP(e) -= delta; RESCAP(REV(e)) += delta; @@ -322,26 +321,26 @@ static void igraph_i_mf_push(long int v, long int e, long int n, EXCESS(v) -= delta; } -static void igraph_i_mf_discharge(long int v, - igraph_vector_long_t *current, - igraph_vector_long_t *first, +static void igraph_i_mf_discharge(igraph_integer_t v, + igraph_vector_int_t *current, + igraph_vector_int_t *first, igraph_vector_t *rescap, - igraph_vector_long_t *to, - igraph_vector_long_t *distance, + igraph_vector_int_t *to, + igraph_vector_int_t *distance, igraph_vector_t *excess, - long int no_of_nodes, long int source, - long int target, igraph_buckets_t *buckets, + igraph_integer_t no_of_nodes, igraph_integer_t source, + igraph_integer_t target, igraph_buckets_t *buckets, igraph_dbuckets_t *ibuckets, - igraph_vector_long_t *rev, + igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, - int *npushsince, int *nrelabelsince) { + igraph_integer_t *npushsince, igraph_integer_t *nrelabelsince) { do { - long int i; - long int start = (long int) CURRENT(v); - long int stop = (long int) LAST(v); + igraph_integer_t i; + igraph_integer_t start = CURRENT(v); + igraph_integer_t stop = LAST(v); for (i = start; i < stop; i++) { if (RESCAP(i) > 0) { - long int nei = HEAD(i); + igraph_integer_t nei = HEAD(i); if (DIST(v) == DIST(nei) + 1) { PUSH((v), i, nei); if (EXCESS(v) == 0) { @@ -351,7 +350,7 @@ static void igraph_i_mf_discharge(long int v, } } if (i == stop) { - long int origdist = DIST(v); + igraph_integer_t origdist = DIST(v); RELABEL(v); if (igraph_buckets_empty_bucket(buckets, origdist) && igraph_dbuckets_empty_bucket(ibuckets, origdist)) { @@ -368,31 +367,31 @@ static void igraph_i_mf_discharge(long int v, } 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, +static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, + igraph_integer_t source, igraph_integer_t target, + igraph_integer_t 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) { + igraph_vector_int_t *distance, + igraph_vector_int_t *first, igraph_vector_int_t *current, + igraph_vector_int_t *to, igraph_vector_t *excess, + igraph_vector_t *rescap, igraph_vector_int_t *rev) { - long int k, l; + igraph_integer_t k, l; IGRAPH_UNUSED(source); igraph_buckets_clear(buckets); igraph_dbuckets_clear(ibuckets); - igraph_vector_long_fill(distance, no_of_nodes); + igraph_vector_int_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; + IGRAPH_CHECK(igraph_dqueue_int_push(bfsq, target)); + while (!igraph_dqueue_int_empty(bfsq)) { + igraph_integer_t node = igraph_dqueue_int_pop(bfsq); + igraph_integer_t ndist = DIST(node) + 1; for (k = FIRST(node), l = LAST(node); k < l; k++) { if (RESCAP(REV(k)) > 0) { - long int nei = HEAD(k); + igraph_integer_t nei = HEAD(k); if (DIST(nei) == no_of_nodes) { DIST(nei) = ndist; CURRENT(nei) = FIRST(nei); @@ -401,24 +400,28 @@ static void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq, } else { igraph_dbuckets_add(ibuckets, ndist, nei); } - igraph_dqueue_long_push(bfsq, nei); + IGRAPH_CHECK(igraph_dqueue_int_push(bfsq, nei)); } } } } + + return IGRAPH_SUCCESS; } /** * \function igraph_maxflow - * Maximum network flow between a pair of vertices + * \brief Maximum network flow between a pair of vertices. * - * This function implements the Goldberg-Tarjan algorithm for + * 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 ACM, 35(4), 921-940, 1988 + * https://doi.org/10.1145/48014.61051. * - * The input of the function is a graph, a vector + * + * 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 @@ -436,16 +439,16 @@ static void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq, * \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, + * 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 + * 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 + * 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 @@ -481,28 +484,28 @@ static void igraph_i_mf_bfs(igraph_dqueue_long_t *bfsq, * \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_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, + igraph_vector_t *flow, igraph_vector_int_t *cut, + igraph_vector_int_t *partition, igraph_vector_int_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_nodes = igraph_vcount(graph); + igraph_integer_t no_of_orig_edges = 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_vector_int_t from, to, rev, distance; + igraph_vector_int_t edges, rank; + igraph_vector_int_t current, first; igraph_buckets_t buckets; igraph_dbuckets_t ibuckets; - igraph_dqueue_long_t bfsq; + igraph_dqueue_int_t bfsq; - long int i, j, idx; - int npushsince = 0, nrelabelsince = 0; + igraph_integer_t i, j, idx; + igraph_integer_t npushsince = 0, nrelabelsince = 0; igraph_maxflow_stats_t local_stats; /* used if the user passed a null pointer for stats */ @@ -514,14 +517,14 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, IGRAPH_CHECK(igraph_i_maxflow_undirected(graph, value, flow, cut, partition, partition2, source, target, capacity, stats)); - return 0; + return IGRAPH_SUCCESS; } if (capacity && igraph_vector_size(capacity) != no_of_orig_edges) { - IGRAPH_ERROR("Invalid capacity vector", IGRAPH_EINVAL); + IGRAPH_ERROR("Capacity vector must match number of edges in length.", IGRAPH_EINVAL); } if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { - IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid source or target vertex.", IGRAPH_EINVVID); } stats->nopush = stats->norelabel = stats->nogap = stats->nogapnodes = @@ -572,26 +575,26 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, #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_CHECK(igraph_dqueue_int_init(&bfsq, no_of_nodes)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &bfsq); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rev, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&rescap, no_of_edges); + IGRAPH_VECTOR_INIT_FINALLY(&excess, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&distance, no_of_nodes); + IGRAPH_VECTOR_INT_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t pos = VECTOR(rank)[i]; + igraph_integer_t pos2 = VECTOR(rank)[i + 1]; VECTOR(from)[pos] = VECTOR(edges)[i]; VECTOR(to)[pos] = VECTOR(edges)[i + 1]; VECTOR(from)[pos2] = VECTOR(edges)[i + 1]; @@ -610,8 +613,8 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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] ]); + igraph_integer_t n = (VECTOR(from)[i] - + VECTOR(from)[ VECTOR(first)[idx] ]); for (j = 0; j < n; j++) { idx++; VECTOR(first)[idx] = i; } @@ -621,17 +624,17 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, VECTOR(first)[idx++] = no_of_edges; } - igraph_vector_long_destroy(&from); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&from); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); if (!flow) { - igraph_vector_destroy(&rank); + igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(¤t, no_of_nodes); for (i = 0; i < no_of_nodes; i++) { VECTOR(current)[i] = VECTOR(first)[i]; } @@ -653,11 +656,11 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, } } - BFS(); + IGRAPH_CHECK(BFS()); (stats->nobfs)++; while (!igraph_buckets_empty(&buckets)) { - long int vertex = igraph_buckets_popmax(&buckets); + igraph_integer_t vertex = igraph_buckets_popmax(&buckets); DISCHARGE(vertex); if (npushsince > no_of_nodes / 2 && nrelabelsince > no_of_nodes) { (stats->nobfs)++; @@ -676,50 +679,50 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, /* 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_dqueue_int_t Q; igraph_vector_bool_t added; - long int marked = 0; + igraph_integer_t 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_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); - igraph_dqueue_push(&Q, target); - VECTOR(added)[(long int)target] = 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); + VECTOR(added)[target] = true; marked++; - while (!igraph_dqueue_empty(&Q)) { - long int actnode = (long int) igraph_dqueue_pop(&Q); + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { - long int nei = HEAD(i); + igraph_integer_t nei = HEAD(i); if (!VECTOR(added)[nei] && RESCAP(REV(i)) > 0.0) { - VECTOR(added)[nei] = 1; + VECTOR(added)[nei] = true; marked++; - IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); } } } - igraph_dqueue_destroy(&Q); + igraph_dqueue_int_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); + igraph_vector_int_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); + igraph_integer_t f = IGRAPH_FROM(graph, i); + igraph_integer_t t = IGRAPH_TO(graph, i); if (!VECTOR(added)[f] && VECTOR(added)[t]) { - IGRAPH_CHECK(igraph_vector_push_back(cut, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(cut, i)); } } } if (partition2) { - long int x = 0; - IGRAPH_CHECK(igraph_vector_resize(partition2, marked)); + igraph_integer_t x = 0; + IGRAPH_CHECK(igraph_vector_int_resize(partition2, marked)); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(added)[i]) { VECTOR(*partition2)[x++] = i; @@ -728,8 +731,8 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, } if (partition) { - long int x = 0; - IGRAPH_CHECK(igraph_vector_resize(partition, + igraph_integer_t x = 0; + IGRAPH_CHECK(igraph_vector_int_resize(partition, no_of_nodes - marked)); for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(added)[i]) { @@ -745,57 +748,57 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (flow) { /* Initialize the backward distances, with a breadth-first search from the source */ - igraph_dqueue_t Q; + igraph_dqueue_int_t Q; igraph_vector_int_t added; - long int j, k, l; + igraph_integer_t j, k, l; igraph_t flow_graph; - igraph_vector_t flow_edges; + igraph_vector_int_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); + IGRAPH_CHECK(igraph_dqueue_int_init(&Q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &Q); + + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + VECTOR(added)[source] = 1; + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); DIST(actnode) = actdist; for (i = FIRST(actnode), j = LAST(actnode); i < j; i++) { - long int nei = HEAD(i); + igraph_integer_t 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_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); } } - } /* !igraph_dqueue_empty(&Q) */ + } /* !igraph_dqueue_int_empty(&Q) */ igraph_vector_int_destroy(&added); - igraph_dqueue_destroy(&Q); + igraph_dqueue_int_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); + igraph_buckets_add(&buckets, DIST(i), i); } } /* Now we return the flow to the source */ while (!igraph_buckets_empty(&buckets)) { - long int vertex = igraph_buckets_popmax(&buckets); + igraph_integer_t vertex = igraph_buckets_popmax(&buckets); /* DISCHARGE(vertex) comes here */ do { - for (i = (long int) CURRENT(vertex), j = LAST(vertex); i < j; i++) { + for (i = CURRENT(vertex), j = LAST(vertex); i < j; i++) { if (RESCAP(i) > 0) { - long int nei = HEAD(i); + igraph_integer_t nei = HEAD(i); if (DIST(vertex) == DIST(nei) + 1) { igraph_real_t delta = @@ -805,7 +808,7 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (nei != source && EXCESS(nei) == 0.0 && DIST(nei) != no_of_nodes) { - igraph_buckets_add(&buckets, (long int) DIST(nei), nei); + igraph_buckets_add(&buckets, DIST(nei), nei); } EXCESS(nei) += delta; @@ -822,8 +825,8 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, if (i == j) { /* RELABEL(vertex) comes here */ - igraph_real_t min; - long int min_edge = 0; + igraph_integer_t min; + igraph_integer_t min_edge = 0; DIST(vertex) = min = no_of_nodes; for (k = FIRST(vertex), l = LAST(vertex); k < l; k++) { if (RESCAP(k) > 0) { @@ -840,7 +843,7 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, DIST(vertex) = min; CURRENT(vertex) = min_edge; /* Vertex is still active */ - igraph_buckets_add(&buckets, (long int) DIST(vertex), vertex); + igraph_buckets_add(&buckets, DIST(vertex), vertex); } /* TODO: gap heuristics here ??? */ @@ -851,7 +854,7 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, break; - } while (1); + } while (true); } /* We need to eliminate flow cycles now. Before that we check that @@ -872,19 +875,19 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t pos = VECTOR(rank)[i]; if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) { - IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, + IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, IGRAPH_FROM(graph, j))); - IGRAPH_CHECK(igraph_vector_push_back(&flow_edges, + IGRAPH_CHECK(igraph_vector_int_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_vector_int_destroy(&flow_edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &flow_graph); IGRAPH_CHECK(igraph_is_dag(&flow_graph, &dag)); @@ -892,11 +895,11 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, IGRAPH_FINALLY_CLEAN(1); if (!dag) { - igraph_vector_long_t stack; + igraph_vector_int_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(&stack, 0)); + IGRAPH_FINALLY(igraph_vector_int_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); @@ -904,25 +907,25 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, #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]; + igraph_integer_t pos = VECTOR(rank)[i]; + igraph_integer_t pos2 = 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(¤t); + igraph_vector_int_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; + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, -1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, source)); + VECTOR(added)[source] = 1; + while (!igraph_vector_int_empty(&stack) && + igraph_vector_int_tail(&stack) != target) { + igraph_integer_t actnode = igraph_vector_int_tail(&stack); + igraph_integer_t edge = FIRST(actnode) + CURRENT(actnode); + igraph_integer_t nei; while (edge < LAST(actnode) && MYCAP(edge) == 0.0) { edge++; } @@ -931,8 +934,8 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, edge)); + IGRAPH_CHECK(igraph_vector_int_push_back(&stack, nei)); VECTOR(added)[nei] = 1; CURRENT(actnode) += 1; } else if (edge < LAST(actnode) && VECTOR(added)[nei] == 1) { @@ -940,19 +943,19 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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; + igraph_integer_t idx; + for (idx = igraph_vector_int_size(&stack) - 2; idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { - long int e = VECTOR(stack)[idx]; + igraph_integer_t 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; + for (idx = igraph_vector_int_size(&stack) - 2; idx >= 0 && VECTOR(stack)[idx + 1] != nei; idx -= 2) { - long int e = VECTOR(stack)[idx]; + igraph_integer_t e = VECTOR(stack)[idx]; if (e >= 0) { MYCAP(e) -= thisflow; RESCAP(e) += thisflow; @@ -965,35 +968,35 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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); + igraph_vector_int_pop_back(&stack); + igraph_vector_int_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); + if (!igraph_vector_int_empty(&stack)) { + igraph_integer_t pl = igraph_vector_int_size(&stack); igraph_real_t thisflow = EXCESS(target); for (i = 2; i < pl; i += 2) { - long int edge = VECTOR(stack)[i]; + igraph_integer_t 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]; + igraph_integer_t edge = VECTOR(stack)[i]; MYCAP(edge) -= thisflow; } } - } while (!igraph_vector_long_empty(&stack)); + } while (!igraph_vector_int_empty(&stack)); igraph_vector_destroy(&mycap); igraph_vector_int_destroy(&added); - igraph_vector_long_destroy(&stack); + igraph_vector_int_destroy(&stack); IGRAPH_FINALLY_CLEAN(3); } @@ -1001,41 +1004,43 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, 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]; + igraph_integer_t pos = VECTOR(rank)[i]; VECTOR(*flow)[j] = (capacity ? VECTOR(*capacity)[j] : 1.0) - RESCAP(pos); } - igraph_vector_destroy(&rank); + igraph_vector_int_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_int_destroy(¤t); + igraph_vector_int_destroy(&first); + igraph_vector_int_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_vector_int_destroy(&rev); + igraph_vector_int_destroy(&to); + igraph_dqueue_int_destroy(&bfsq); IGRAPH_FINALLY_CLEAN(10); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_maxflow_value - * \brief Maximum flow in a network with the push/relabel algorithm + * \brief Maximum flow in a network with the push/relabel algorithm. * - * This function implements the Goldberg-Tarjan algorithm for + * 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 ACM, 35(4), 921-940, 1988 + * https://doi.org/10.1145/48014.61051. * - * The input of the function is a graph, a vector + * + * 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 @@ -1045,18 +1050,21 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, * 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. + * value. * - * According to a theorem by Ford and Fulkerson + * + * 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(). + * igraph_st_mincut_value() gives the same result in all cases as \ref + * igraph_maxflow_value(). * - * Note that the value of the maximum flow is the same as the + * + * 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. @@ -1075,19 +1083,19 @@ int igraph_maxflow(const igraph_t *graph, igraph_real_t *value, * properties based on the maximum flow. */ -int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, +igraph_error_t 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, + return igraph_maxflow(graph, value, /*flow=*/ NULL, /*cut=*/ NULL, + /*partition=*/ NULL, /*partition1=*/ NULL, source, target, capacity, stats); } /** * \function igraph_st_mincut_value - * \brief The minimum s-t cut in a graph + * \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 @@ -1098,6 +1106,7 @@ int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, * 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. @@ -1113,7 +1122,7 @@ int igraph_maxflow_value(const igraph_t *graph, igraph_real_t *value, * igraph_maxflow_value(), |V| is the number of vertices. */ -int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, +igraph_error_t 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) { @@ -1123,29 +1132,30 @@ int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, IGRAPH_CHECK(igraph_maxflow_value(graph, value, source, target, capacity, 0)); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_st_mincut - * Minimum cut between a source and a target vertex + * \brief 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 + * \param cut Pointer to an initialized 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 + * \param partition Pointer to an initialized 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 + * \param partition2 Pointer to an initialized 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. @@ -1161,9 +1171,9 @@ int igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *value, * 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_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_t *cut, igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, igraph_integer_t source, igraph_integer_t target, const igraph_vector_t *capacity) { @@ -1172,49 +1182,6 @@ int igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, 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_integer_t last; igraph_real_t acut; - long int a, n; + igraph_integer_t a, n; igraph_vector_int_t *edges, *edges2; igraph_vector_int_t *neis, *neis2; @@ -1323,9 +1290,9 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, 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_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t to = VECTOR(*neis)[i]; + igraph_real_t weight = capacity ? VECTOR(*capacity)[edge] : 1.0; igraph_i_cutheap_update(&heap, to, weight); } @@ -1349,8 +1316,8 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, /* 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, a)); + IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, last)); } /* First remove the a--last edge if there is one, a is still the last deactivated vertex */ @@ -1388,8 +1355,8 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, 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; + igraph_integer_t nei = VECTOR(*neis)[i]; + igraph_integer_t n2, j; neis2 = igraph_adjlist_get(&adjlist, nei); n2 = igraph_vector_int_size(neis2); for (j = 0; j < n2; j++) { @@ -1421,32 +1388,31 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, 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_integer_t bignode = VECTOR(mergehist)[2 * mincut_step + 1]; + igraph_integer_t i, idx; + igraph_integer_t size = 1; + bool *mark; + + mark = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(mark, "Not enough memory for minimum cut."); IGRAPH_FINALLY(igraph_free, mark); /* first count the vertices in the partition */ - mark[bignode] = 1; + mark[bignode] = true; for (i = mincut_step - 1; i >= 0; i--) { - if ( mark[ (long int) VECTOR(mergehist)[2 * i] ] ) { + if ( mark[ VECTOR(mergehist)[2 * i] ] ) { size++; - mark [ (long int) VECTOR(mergehist)[2 * i + 1] ] = 1; + mark [ VECTOR(mergehist)[2 * i + 1] ] = true; } } /* now store them, if requested */ if (partition) { - IGRAPH_CHECK(igraph_vector_resize(partition, size)); + IGRAPH_CHECK(igraph_vector_int_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] ]) { + if (mark[ VECTOR(mergehist)[2 * i] ]) { VECTOR(*partition)[idx++] = VECTOR(mergehist)[2 * i + 1]; } } @@ -1454,7 +1420,7 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, /* The other partition too? */ if (partition2) { - IGRAPH_CHECK(igraph_vector_resize(partition2, no_of_nodes - size)); + IGRAPH_CHECK(igraph_vector_int_resize(partition2, no_of_nodes - size)); idx = 0; for (i = 0; i < no_of_nodes; i++) { if (!mark[i]) { @@ -1469,77 +1435,77 @@ static int igraph_i_mincut_undirected(const igraph_t *graph, need that anymore. Then we copy it to 'cut'; */ if (cut) { igraph_integer_t from, to; - igraph_vector_clear(&mergehist); + igraph_vector_int_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_edge(graph, i, &from, &to); + if ((mark[from] && !mark[to]) || + (mark[to] && !mark[from])) { + IGRAPH_CHECK(igraph_vector_int_push_back(&mergehist, i)); } } - igraph_vector_clear(cut); - IGRAPH_CHECK(igraph_vector_append(cut, &mergehist)); + igraph_vector_int_clear(cut); + IGRAPH_CHECK(igraph_vector_int_append(cut, &mergehist)); } - igraph_free(mark); - igraph_vector_destroy(&mergehist); + IGRAPH_FREE(mark); + igraph_vector_int_destroy(&mergehist); IGRAPH_FINALLY_CLEAN(2); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_mincut_directed(const igraph_t *graph, +static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, igraph_real_t *value, - igraph_vector_t *partition, - igraph_vector_t *partition2, - igraph_vector_t *cut, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, const igraph_vector_t *capacity) { - long int i; - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_integer_t 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; + igraph_vector_int_t mypartition, mypartition2, mycut; + igraph_vector_int_t *ppartition = 0, *ppartition2 = 0, *pcut = 0; + igraph_vector_int_t bestpartition, bestpartition2, bestcut; if (partition) { - IGRAPH_VECTOR_INIT_FINALLY(&bestpartition, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestpartition, 0); } if (partition2) { - IGRAPH_VECTOR_INIT_FINALLY(&bestpartition2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestpartition2, 0); } if (cut) { - IGRAPH_VECTOR_INIT_FINALLY(&bestcut, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bestcut, 0); } if (partition) { - IGRAPH_VECTOR_INIT_FINALLY(&mypartition, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition, 0); ppartition = &mypartition; } if (partition2) { - IGRAPH_VECTOR_INIT_FINALLY(&mypartition2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mypartition2, 0); ppartition2 = &mypartition2; } if (cut) { - IGRAPH_VECTOR_INIT_FINALLY(&mycut, 0); + IGRAPH_VECTOR_INT_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)); + /*target=*/ i, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; if (cut) { - IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); + IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); } if (partition) { - IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); } if (partition2) { - IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); } if (minmaxflow == 0) { @@ -1548,18 +1514,18 @@ static int igraph_i_mincut_directed(const igraph_t *graph, } IGRAPH_CHECK(igraph_maxflow(graph, /*value=*/ &flow, /*flow=*/ 0, pcut, ppartition, ppartition2, - /*source=*/ (igraph_integer_t) i, + /*source=*/ i, /*target=*/ 0, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; if (cut) { - IGRAPH_CHECK(igraph_vector_update(&bestcut, &mycut)); + IGRAPH_CHECK(igraph_vector_int_update(&bestcut, &mycut)); } if (partition) { - IGRAPH_CHECK(igraph_vector_update(&bestpartition, &mypartition)); + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition, &mypartition)); } if (partition2) { - IGRAPH_CHECK(igraph_vector_update(&bestpartition2, &mypartition2)); + IGRAPH_CHECK(igraph_vector_int_update(&bestpartition2, &mypartition2)); } if (minmaxflow == 0) { @@ -1573,34 +1539,34 @@ static int igraph_i_mincut_directed(const igraph_t *graph, } if (cut) { - igraph_vector_destroy(&mycut); + igraph_vector_int_destroy(&mycut); IGRAPH_FINALLY_CLEAN(1); } if (partition) { - igraph_vector_destroy(&mypartition); + igraph_vector_int_destroy(&mypartition); IGRAPH_FINALLY_CLEAN(1); } if (partition2) { - igraph_vector_destroy(&mypartition2); + igraph_vector_int_destroy(&mypartition2); IGRAPH_FINALLY_CLEAN(1); } if (cut) { - IGRAPH_CHECK(igraph_vector_update(cut, &bestcut)); - igraph_vector_destroy(&bestcut); + IGRAPH_CHECK(igraph_vector_int_update(cut, &bestcut)); + igraph_vector_int_destroy(&bestcut); IGRAPH_FINALLY_CLEAN(1); } if (partition2) { - IGRAPH_CHECK(igraph_vector_update(partition2, &bestpartition2)); - igraph_vector_destroy(&bestpartition2); + IGRAPH_CHECK(igraph_vector_int_update(partition2, &bestpartition2)); + igraph_vector_int_destroy(&bestpartition2); IGRAPH_FINALLY_CLEAN(1); } if (partition) { - IGRAPH_CHECK(igraph_vector_update(partition, &bestpartition)); - igraph_vector_destroy(&bestpartition); + IGRAPH_CHECK(igraph_vector_int_update(partition, &bestpartition)); + igraph_vector_int_destroy(&bestpartition); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1622,6 +1588,7 @@ static int igraph_i_mincut_directed(const igraph_t *graph, * * 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. @@ -1633,7 +1600,7 @@ static int igraph_i_mincut_directed(const igraph_t *graph, * 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 + * \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 @@ -1651,11 +1618,11 @@ static int igraph_i_mincut_directed(const igraph_t *graph, * \example examples/simple/igraph_mincut.c */ -int igraph_mincut(const igraph_t *graph, +igraph_error_t igraph_mincut(const igraph_t *graph, igraph_real_t *value, - igraph_vector_t *partition, - igraph_vector_t *partition2, - igraph_vector_t *cut, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, const igraph_vector_t *capacity) { if (igraph_is_directed(graph)) { @@ -1671,11 +1638,11 @@ int igraph_mincut(const igraph_t *graph, return IGRAPH_SUCCESS; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_mincut_value_undirected(const igraph_t *graph, +static igraph_error_t 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); @@ -1683,7 +1650,7 @@ static int igraph_i_mincut_value_undirected(const igraph_t *graph, /** * \function igraph_mincut_value - * \brief The minimum edge cut in a graph + * \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 @@ -1717,22 +1684,22 @@ static int igraph_i_mincut_value_undirected(const igraph_t *graph, * documentation of \ref igraph_maxflow_value(). */ -int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t minmaxflow, flow; - long int i; + igraph_integer_t i; minmaxflow = IGRAPH_INFINITY; if (!igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_mincut_value_undirected(graph, res, capacity)); - return 0; + return IGRAPH_SUCCESS; } for (i = 1; i < no_of_nodes; i++) { - IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, 0, i, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; @@ -1740,7 +1707,7 @@ int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, break; } } - IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, (igraph_integer_t) i, 0, + IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, i, 0, capacity, 0)); if (flow < minmaxflow) { minmaxflow = flow; @@ -1754,133 +1721,137 @@ int igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, *res = minmaxflow; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_st_vertex_connectivity_directed(const igraph_t *graph, +static igraph_error_t igraph_i_st_vertex_connectivity_check_errors(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target, - igraph_vconn_nei_t neighbors) { + igraph_vconn_nei_t neighbors, + igraph_bool_t *done, + igraph_integer_t *no_conn) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t eid; + igraph_bool_t conn; + *done = 1; + *no_conn = 0; - 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 == target) { + IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); + } if (source < 0 || source >= no_of_nodes || target < 0 || target >= no_of_nodes) { - IGRAPH_ERROR("Invalid source or target vertex", IGRAPH_EINVAL); + 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); + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { + IGRAPH_ERROR("Source and target vertices connected.", IGRAPH_EINVAL); } break; case IGRAPH_VCONN_NEI_NEGATIVE: - IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); - if (conn1) { + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { *res = -1; - return 0; + return IGRAPH_SUCCESS; } break; case IGRAPH_VCONN_NEI_NUMBER_OF_NODES: - IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn1)); - if (conn1) { + IGRAPH_CHECK(igraph_are_connected(graph, source, target, &conn)); + if (conn) { *res = no_of_nodes; - return 0; + return IGRAPH_SUCCESS; } break; case IGRAPH_VCONN_NEI_IGNORE: + IGRAPH_CHECK(igraph_get_eid(graph, &eid, source, target, IGRAPH_DIRECTED, /*error=*/ false)); + if (eid >= 0) { + IGRAPH_CHECK(igraph_count_multiple_1(graph, no_conn, eid)); + } break; default: - IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'", IGRAPH_EINVAL); + IGRAPH_ERROR("Unknown `igraph_vconn_nei_t'.", IGRAPH_EINVAL); break; } + *done = 0; + return IGRAPH_SUCCESS; +} - /* 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; - } - } +static igraph_error_t 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) { - 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges; + igraph_real_t real_res; + igraph_t newgraph; + igraph_integer_t i, len; + igraph_bool_t done; + igraph_integer_t no_conn; + igraph_vector_int_t incs; + igraph_vector_t capacity; + + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); + if (done) { + return IGRAPH_SUCCESS; } - IGRAPH_CHECK(igraph_create(&newgraph, &edges, 2 * no_of_nodes, - igraph_is_directed(graph))); + /* Create the new graph */ + IGRAPH_CHECK(igraph_i_split_vertices(graph, &newgraph)); + IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_destroy(&edges); + /* Create the capacity vector, fill it with ones */ + no_of_edges = igraph_ecount(&newgraph); + IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); + igraph_vector_fill(&capacity, 1); + + /* "Disable" the edges incident on the input half of the source vertex + * and the output half of the target vertex */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); + IGRAPH_CHECK(igraph_incident(&newgraph, &incs, source + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (i = 0; i < len; i++) { + VECTOR(capacity)[VECTOR(incs)[i]] = 0; + } + IGRAPH_CHECK(igraph_incident(&newgraph, &incs, target, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (i = 0; i < len; i++) { + VECTOR(capacity)[VECTOR(incs)[i]] = 0; + } + igraph_vector_int_destroy(&incs); 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; + source, target + no_of_nodes, &capacity, 0)); + *res = (igraph_integer_t) real_res; + + *res -= no_conn; + igraph_vector_destroy(&capacity); igraph_destroy(&newgraph); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, +static igraph_error_t 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); - } + igraph_bool_t done; + igraph_integer_t no_conn; - 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_i_st_vertex_connectivity_check_errors(graph, res, source, target, neighbors, &done, &no_conn)); + if (done) { + return IGRAPH_SUCCESS; } IGRAPH_CHECK(igraph_copy(&newgraph, graph)); @@ -1894,12 +1865,12 @@ static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_st_vertex_connectivity - * \brief The vertex connectivity of a pair of vertices + * \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 @@ -1912,6 +1883,7 @@ static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, * * 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. @@ -1919,9 +1891,9 @@ static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, * \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 + * \c IGRAPH_VCONN_NEI_NEGATIVE, return -1. + * \c IGRAPH_VCONN_NEI_NUMBER_OF_NODES, return the number of nodes. + * \c IGRAPH_VCONN_NEI_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? @@ -1935,16 +1907,11 @@ static int igraph_i_st_vertex_connectivity_undirected(const igraph_t *graph, * \ref igraph_maxflow_value(). */ -int igraph_st_vertex_connectivity(const igraph_t *graph, +igraph_error_t 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, @@ -1955,28 +1922,91 @@ int igraph_st_vertex_connectivity(const igraph_t *graph, neighbors)); } - return 0; + return IGRAPH_SUCCESS; } -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; +static igraph_error_t igraph_i_vertex_connectivity_directed( + const igraph_t *graph, igraph_integer_t *res, igraph_bool_t all_edges_are_mutual +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges; + igraph_integer_t i, j, k, len; igraph_integer_t minconn = no_of_nodes - 1, conn = 0; + igraph_t split_graph; + igraph_vector_t capacity; + igraph_bool_t done; + igraph_integer_t dummy_num_connections; + igraph_vector_int_t incs; + igraph_real_t real_res; + + /* Create the new graph */ + IGRAPH_CHECK(igraph_i_split_vertices(graph, &split_graph)); + IGRAPH_FINALLY(igraph_destroy, &split_graph); + + /* Create the capacity vector, fill it with ones */ + no_of_edges = igraph_ecount(&split_graph); + IGRAPH_VECTOR_INIT_FINALLY(&capacity, no_of_edges); + igraph_vector_fill(&capacity, 1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&incs, 0); for (i = 0; i < no_of_nodes; i++) { - for (j = 0; j < no_of_nodes; j++) { + for (j = all_edges_are_mutual ? i + 1 : 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)); + /* Check for easy cases */ + IGRAPH_CHECK(igraph_i_st_vertex_connectivity_check_errors( + graph, &conn, i, j, IGRAPH_VCONN_NEI_NUMBER_OF_NODES, &done, + &dummy_num_connections + )); + + /* 'done' will be set to true if the two vertices are already + * connected, and in this case 'res' will be set to the number of + * nodes-1. + * + * Also, since we used IGRAPH_VCONN_NEI_NUMBER_OF_NODES, + * dummy_num_connections will always be zero, no need to deal with + * it */ + IGRAPH_ASSERT(dummy_num_connections == 0); + + if (!done) { + /* "Disable" the edges incident on the input half of the source vertex + * and the output half of the target vertex */ + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 0; + } + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 0; + } + + /* Do the maximum flow */ + IGRAPH_CHECK(igraph_maxflow_value( + &split_graph, &real_res, i, j + no_of_nodes, &capacity, 0 + )); + + /* Restore the capacities */ + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, i + no_of_nodes, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 1; + } + IGRAPH_CHECK(igraph_incident(&split_graph, &incs, j, IGRAPH_ALL)); + len = igraph_vector_int_size(&incs); + for (k = 0; k < len; k++) { + VECTOR(capacity)[VECTOR(incs)[k]] = 1; + } + + conn = (igraph_integer_t) real_res; + } + if (conn < minconn) { minconn = conn; if (conn == 0) { @@ -1984,7 +2014,8 @@ static int igraph_i_vertex_connectivity_directed(const igraph_t *graph, } } } - if (conn == 0) { + + if (minconn == 0) { break; } } @@ -1993,10 +2024,15 @@ static int igraph_i_vertex_connectivity_directed(const igraph_t *graph, *res = minconn; } - return 0; + igraph_vector_int_destroy(&incs); + igraph_vector_destroy(&capacity); + igraph_destroy(&split_graph); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; } -static int igraph_i_vertex_connectivity_undirected(const igraph_t *graph, +static igraph_error_t igraph_i_vertex_connectivity_undirected(const igraph_t *graph, igraph_integer_t *res) { igraph_t newgraph; @@ -2004,16 +2040,16 @@ static int igraph_i_vertex_connectivity_undirected(const igraph_t *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_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res, /* all_edges_are_mutual = */ 1)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ -static int igraph_i_connectivity_checks(const igraph_t *graph, +static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t *found) { igraph_bool_t conn; @@ -2022,7 +2058,7 @@ static int igraph_i_connectivity_checks(const igraph_t *graph, if (igraph_vcount(graph) == 0) { *res = 0; *found = 1; - return 0; + return IGRAPH_SUCCESS; } IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_STRONG)); @@ -2030,12 +2066,12 @@ static int igraph_i_connectivity_checks(const igraph_t *graph, *res = 0; *found = 1; } else { - igraph_vector_t degree; - IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + igraph_vector_int_t degree; + IGRAPH_VECTOR_INT_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) { + if (igraph_vector_int_min(°ree) == 1) { *res = 1; *found = 1; } @@ -2043,35 +2079,38 @@ static int igraph_i_connectivity_checks(const igraph_t *graph, /* 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) { + if (igraph_vector_int_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) { + if (igraph_vector_int_min(°ree) == 1) { *res = 1; *found = 1; } } } - igraph_vector_destroy(°ree); + igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_vertex_connectivity - * The vertex connectivity of a graph + * \brief 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. + * conditional density, Sociological Methodology 31:305--359, 2001 + * https://doi.org/10.1111/0081-1750.00098. + * * \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 @@ -2089,10 +2128,10 @@ static int igraph_i_connectivity_checks(const igraph_t *graph, * and \ref igraph_edge_connectivity(). */ -int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { - igraph_bool_t ret = 0; + igraph_bool_t ret = false; if (checks) { IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); @@ -2101,18 +2140,18 @@ int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, /* Are we done yet? */ if (!ret) { if (igraph_is_directed(graph)) { - IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res)); + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(graph, res, /* all_edges_are_mutual = */ 0)); } else { IGRAPH_CHECK(igraph_i_vertex_connectivity_undirected(graph, res)); } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_st_edge_connectivity - * \brief Edge connectivity of a pair of vertices + * \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 @@ -2121,6 +2160,7 @@ int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, * * 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. @@ -2134,7 +2174,7 @@ int igraph_vertex_connectivity(const igraph_t *graph, igraph_integer_t *res, * igraph_vertex_connectivity(). */ -int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { igraph_real_t flow; @@ -2146,7 +2186,7 @@ int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, IGRAPH_CHECK(igraph_maxflow_value(graph, &flow, source, target, 0, 0)); *res = (igraph_integer_t) flow; - return 0; + return IGRAPH_SUCCESS; } @@ -2161,7 +2201,9 @@ int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, * 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. + * density, Sociological Methodology 31:305--359, 2001 + * https://doi.org/10.1111/0081-1750.00098. + * * \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 @@ -2181,9 +2223,9 @@ int igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, * \ref igraph_vertex_connectivity(). */ -int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { - igraph_bool_t ret = 0; + igraph_bool_t ret = false; igraph_integer_t number_of_nodes = igraph_vcount(graph); /* igraph_mincut_value returns infinity for the singleton graph, @@ -2192,7 +2234,7 @@ int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, * This is consistent with what other software packages return. */ if (number_of_nodes <= 1) { *res = 0; - return 0; + return IGRAPH_SUCCESS; } /* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ @@ -2206,7 +2248,7 @@ int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, *res = (igraph_integer_t)real_res; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -2221,6 +2263,7 @@ int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, * * 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. @@ -2235,7 +2278,7 @@ int igraph_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, * igraph_st_edge_connectivity(), \ref igraph_maxflow_value(). */ -int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { @@ -2249,7 +2292,7 @@ int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, *res = (igraph_integer_t) flow; - return 0; + return IGRAPH_SUCCESS; } /** @@ -2263,6 +2306,7 @@ int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, * 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. @@ -2276,7 +2320,7 @@ int igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, * igraph_vertex_connectivity(), \ref igraph_maxflow_value(). */ -int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t source, igraph_integer_t target) { @@ -2291,19 +2335,18 @@ int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, 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). */ + graph. Finally we add 1 for each removed connection. */ 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_integer_t num_removed_edges; + + IGRAPH_CHECK(igraph_es_all_between(&es, source, target, 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)); + num_removed_edges = igraph_ecount(graph) - igraph_ecount(&newgraph); if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(&newgraph, res, @@ -2316,29 +2359,25 @@ int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, } if (res) { - *res += 1; + *res += num_removed_edges; } - IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FINALLY_CLEAN(2); 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)); + 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; + return IGRAPH_SUCCESS; } /** @@ -2350,6 +2389,7 @@ int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, * 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 @@ -2369,7 +2409,7 @@ int igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, * igraph_edge_connectivity(), \ref igraph_mincut_value(). */ -int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { return igraph_edge_connectivity(graph, res, checks); } @@ -2381,8 +2421,8 @@ int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, * 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. + * 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. @@ -2402,11 +2442,11 @@ int igraph_adhesion(const igraph_t *graph, igraph_integer_t *res, * \ref igraph_maxflow_value(). */ -int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, +igraph_error_t igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, igraph_bool_t checks) { IGRAPH_CHECK(igraph_vertex_connectivity(graph, res, checks)); - return 0; + return IGRAPH_SUCCESS; } /** @@ -2427,8 +2467,12 @@ int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, * Gomory-Hu tree. See the following paper for more details: * * + * Reference: + * + * * Gusfield D: Very simple methods for all pairs network flow analysis. SIAM J - * Comput 19(1):143-155, 1990. + * Comput 19(1):143-155, 1990 + * https://doi.org/10.1137/0219009. * * \param graph The input graph. * \param tree Pointer to an uninitialized graph; the result will be @@ -2447,27 +2491,27 @@ int igraph_cohesion(const igraph_t *graph, igraph_integer_t *res, * * \sa \ref igraph_maxflow() */ -int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, +igraph_error_t 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_int_t neighbors; igraph_vector_t flow_values; - igraph_vector_t partition; - igraph_vector_t partition2; + igraph_vector_int_t partition; + igraph_vector_int_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_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_INT_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&partition, 0); + IGRAPH_VECTOR_INT_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 @@ -2479,30 +2523,30 @@ int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, 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]; + target = VECTOR(neighbors)[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; + VECTOR(flow_values)[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); + n = igraph_vector_int_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; + if (VECTOR(neighbors)[mid] == target) { + VECTOR(neighbors)[mid] = source; + } else if (VECTOR(neighbors)[target] == mid) { + VECTOR(neighbors)[target] = source; + VECTOR(neighbors)[source] = mid; + VECTOR(flow_values)[source] = VECTOR(flow_values)[target]; + VECTOR(flow_values)[target] = flow_value; } } } @@ -2511,21 +2555,21 @@ int igraph_gomory_hu_tree(const igraph_t *graph, igraph_t *tree, 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))); + IGRAPH_CHECK(igraph_vector_int_resize(&partition, no_of_nodes > 0 ? 2 * (no_of_nodes - 1) : 0)); 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]; + VECTOR(partition)[mid] = i; + VECTOR(partition)[mid + 1] = VECTOR(neighbors)[i]; } - /* Create the tree graph; we use igraph_subgraph_edges here to keep the + /* Create the tree graph; we use igraph_subgraph_from_edges here to keep the * graph and vertex attributes */ - IGRAPH_CHECK(igraph_subgraph_edges(graph, tree, igraph_ess_none(), 0)); + IGRAPH_CHECK(igraph_subgraph_from_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_vector_int_destroy(&partition2); + igraph_vector_int_destroy(&partition); + igraph_vector_int_destroy(&neighbors); IGRAPH_FINALLY_CLEAN(3); /* Return the flow values to the caller */ diff --git a/src/vendor/cigraph/src/flow/flow_conversion.c b/src/vendor/cigraph/src/flow/flow_conversion.c new file mode 100644 index 00000000000..01b20a5dddd --- /dev/null +++ b/src/vendor/cigraph/src/flow/flow_conversion.c @@ -0,0 +1,93 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2010-2023 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_conversion.h" +#include "igraph_interface.h" + +#include "flow/flow_internal.h" + +/** + * \function igraph_i_split_vertices + * \brief Splits each vertex in the graph into an input and an output vertex. + * + * This function implements a transformation that allows us to calculate the + * vertex connectivity of a directed graph either for a specific s-t pair or + * for all s-t pairs using flows. The transformation splits each vertex into + * an input vertex and an output vertex. All inbound edges of the original + * vertex are rewired to point to the input vertex, and all outbound edges of + * the original vertex are rewired to original from the output vertex, while + * adding a single directed edge from the input vertex to the output vertex. + * + * + * s-t vertex connectivities can then be calculated on this modified graph by + * setting the capacity of each edge to 1, \em except for the following edges: + * the edges incident of the input half of the source vertex and the edges + * incident on the output half of the target vertex. The max flow on this + * modified graph will be equal to the s-t vertex connectivity of the original + * graph. + * + * + * This function prepares the graph only but does not supply a capacity vector; + * it is the responsibility of the caller to provide the capacities. + * + * + * If the original graph had \em n vertices, he function guarantees that the + * first \em n vertices of the result graph will correspond to the \em output + * halves of the vertices and the remaining \em n vertices will correspond to + * the \em input halves, in the same order as in the original graph. + * + * \param graph the input graph + * \param result an uninitialized graph object; the result will be returned here + */ +igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i; + igraph_vector_int_t edges; + + if (!igraph_is_directed(graph)) { + IGRAPH_ERROR("Input graph must be directed.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * (no_of_edges + no_of_nodes))); + IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0)); + IGRAPH_CHECK(igraph_vector_int_resize(&edges, 2 * (no_of_edges + no_of_nodes))); + + for (i = 0; i < 2 * no_of_edges; i += 2) { + igraph_integer_t to = VECTOR(edges)[i + 1]; + 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(result, &edges, 2 * no_of_nodes, IGRAPH_DIRECTED)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/flow/flow_internal.h b/src/vendor/cigraph/src/flow/flow_internal.h index ccb47846252..a269d0090dd 100644 --- a/src/vendor/cigraph/src/flow/flow_internal.h +++ b/src/vendor/cigraph/src/flow/flow_internal.h @@ -23,18 +23,21 @@ #ifndef IGRAPH_FLOW_INTERNAL_H #define IGRAPH_FLOW_INTERNAL_H +#include "igraph_datatype.h" +#include "igraph_decls.h" #include "igraph_types.h" +#include "core/estack.h" +#include "core/marked_queue.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); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_all_st_cuts_pivot( + const igraph_t *graph, const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg); + +igraph_error_t igraph_i_split_vertices(const igraph_t* graph, igraph_t* result); __END_DECLS diff --git a/src/vendor/cigraph/src/flow/st-cuts.c b/src/vendor/cigraph/src/flow/st-cuts.c index 1fa7a0ab730..2beb3c11651 100644 --- a/src/vendor/cigraph/src/flow/st-cuts.c +++ b/src/vendor/cigraph/src/flow/st-cuts.c @@ -1,8 +1,7 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2010-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2010-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 @@ -29,35 +28,36 @@ #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/flow_internal.h" +#include "graph/attributes.h" +#include "math/safe_intop.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); +typedef igraph_error_t igraph_provan_shier_pivot_t(const igraph_t *graph, + const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, + igraph_integer_t source, + igraph_integer_t target, + igraph_integer_t *v, + igraph_vector_int_t *Isv, + void *arg); /** * \function igraph_even_tarjan_reduction - * Even-Tarjan reduction of a graph + * \brief 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'. + * original vertex \c 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 \c i to \c 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 @@ -66,6 +66,7 @@ typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, * 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. + * https://doi.org/10.1137/0204043 * * \param graph A graph. Although directness is not checked, this function * is commonly used only on directed graphs. @@ -74,7 +75,7 @@ typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, * \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). + * are equal to |V| (which is used to indicate infinity). * \return Error code. * * Time complexity: O(|E|+|V|). @@ -82,20 +83,28 @@ typedef int igraph_provan_shier_pivot_t(const igraph_t *graph, * \example examples/simple/even_tarjan.c */ -int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, +igraph_error_t 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); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + igraph_integer_t new_no_of_nodes; + igraph_integer_t new_no_of_edges = no_of_edges * 2; - 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_int_t edges; + igraph_integer_t edgeptr = 0, capptr = 0; + igraph_integer_t i; - igraph_vector_t edges; - long int edgeptr = 0, capptr = 0; - long int i; + IGRAPH_SAFE_MULT(no_of_nodes, 2, &new_no_of_nodes); + IGRAPH_SAFE_ADD(new_no_of_edges, no_of_nodes, &new_no_of_edges); + + /* To ensure the size of the edges vector will not overflow. */ + if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } - IGRAPH_VECTOR_INIT_FINALLY(&edges, new_no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, new_no_of_edges * 2); if (capacity) { IGRAPH_CHECK(igraph_vector_resize(capacity, new_no_of_edges)); @@ -116,8 +125,8 @@ int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, /* 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); + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); VECTOR(edges)[edgeptr++] = from + no_of_nodes; VECTOR(edges)[edgeptr++] = to; VECTOR(edges)[edgeptr++] = to + no_of_nodes; @@ -128,26 +137,26 @@ int igraph_even_tarjan_reduction(const igraph_t *graph, igraph_t *graphbar, } } - IGRAPH_CHECK(igraph_create(graphbar, &edges, (igraph_integer_t) - new_no_of_nodes, IGRAPH_DIRECTED)); + IGRAPH_CHECK(igraph_create(graphbar, &edges, new_no_of_nodes, + IGRAPH_DIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_residual_graph(const igraph_t *graph, +static igraph_error_t 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) { + igraph_vector_int_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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, no_new_edges = 0; + igraph_integer_t edgeptr = 0, capptr = 0; for (i = 0; i < no_of_edges; i++) { if (VECTOR(*flow)[i] < VECTOR(*capacity)[i]) { @@ -155,7 +164,7 @@ static int igraph_i_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); + IGRAPH_CHECK(igraph_vector_int_resize(tmp, no_new_edges * 2)); if (residual_capacity) { IGRAPH_CHECK(igraph_vector_resize(residual_capacity, no_new_edges)); } @@ -163,8 +172,8 @@ static int igraph_i_residual_graph(const igraph_t *graph, 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); + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); VECTOR(*tmp)[edgeptr++] = from; VECTOR(*tmp)[edgeptr++] = to; if (residual_capacity) { @@ -173,20 +182,20 @@ static int igraph_i_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, + IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes, IGRAPH_DIRECTED)); - return 0; + return IGRAPH_SUCCESS; } -int igraph_residual_graph(const igraph_t *graph, +igraph_error_t 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); + igraph_vector_int_t tmp; + igraph_integer_t no_of_edges = igraph_ecount(graph); if (igraph_vector_size(capacity) != no_of_edges) { IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); @@ -195,27 +204,27 @@ int igraph_residual_graph(const igraph_t *graph, IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_residual_graph(graph, capacity, residual, residual_capacity, flow, &tmp)); - igraph_vector_destroy(&tmp); + igraph_vector_int_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_reverse_residual_graph(const igraph_t *graph, +static igraph_error_t 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) { + igraph_vector_int_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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, no_new_edges = 0; + igraph_integer_t edgeptr = 0; for (i = 0; i < no_of_edges; i++) { igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0; @@ -227,11 +236,11 @@ static int igraph_i_reverse_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_vector_resize(tmp, no_new_edges * 2)); + IGRAPH_CHECK(igraph_vector_int_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_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); igraph_real_t cap = capacity ? VECTOR(*capacity)[i] : 1.0; if (VECTOR(*flow)[i] > 0) { VECTOR(*tmp)[edgeptr++] = from; @@ -243,18 +252,18 @@ static int igraph_i_reverse_residual_graph(const igraph_t *graph, } } - IGRAPH_CHECK(igraph_create(residual, tmp, (igraph_integer_t) no_of_nodes, + IGRAPH_CHECK(igraph_create(residual, tmp, no_of_nodes, IGRAPH_DIRECTED)); - return 0; + return IGRAPH_SUCCESS; } -int igraph_reverse_residual_graph(const igraph_t *graph, +igraph_error_t 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); + igraph_vector_int_t tmp; + igraph_integer_t no_of_edges = igraph_ecount(graph); if (capacity && igraph_vector_size(capacity) != no_of_edges) { IGRAPH_ERROR("Invalid `capacity' vector size", IGRAPH_EINVAL); @@ -262,81 +271,80 @@ int igraph_reverse_residual_graph(const igraph_t *graph, if (igraph_vector_size(flow) != no_of_edges) { IGRAPH_ERROR("Invalid `flow' vector size", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_i_reverse_residual_graph(graph, capacity, residual, flow, &tmp)); - igraph_vector_destroy(&tmp); + igraph_vector_int_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } typedef struct igraph_i_dbucket_t { - igraph_vector_long_t head; - igraph_vector_long_t next; + igraph_vector_int_t head; + igraph_vector_int_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)); +static igraph_error_t igraph_i_dbucket_init(igraph_i_dbucket_t *buck, igraph_integer_t size) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&buck->head, size); + IGRAPH_CHECK(igraph_vector_int_init(&buck->next, size)); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } static void igraph_i_dbucket_destroy(igraph_i_dbucket_t *buck) { - igraph_vector_long_destroy(&buck->head); - igraph_vector_long_destroy(&buck->next); + igraph_vector_int_destroy(&buck->head); + igraph_vector_int_destroy(&buck->next); } -static int igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, long int bid, - long int elem) { +static igraph_error_t igraph_i_dbucket_insert(igraph_i_dbucket_t *buck, igraph_integer_t bid, + igraph_integer_t 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; + return IGRAPH_SUCCESS; } -static long int igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, - long int bid) { +static igraph_integer_t igraph_i_dbucket_empty(const igraph_i_dbucket_t *buck, + igraph_integer_t 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; +static igraph_integer_t igraph_i_dbucket_delete(igraph_i_dbucket_t *buck, igraph_integer_t bid) { + igraph_integer_t 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) { +static igraph_error_t igraph_i_dominator_LINK(igraph_integer_t v, igraph_integer_t w, + igraph_vector_int_t *ancestor) { VECTOR(*ancestor)[w] = v + 1; - return 0; + return IGRAPH_SUCCESS; } /* 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; +static igraph_error_t igraph_i_dominator_COMPRESS(igraph_integer_t v, + igraph_vector_int_t *ancestor, + igraph_vector_int_t *label, + igraph_vector_int_t *semi) { + igraph_stack_int_t path; + igraph_integer_t w = v; + igraph_integer_t top, pretop; - IGRAPH_CHECK(igraph_stack_long_init(&path, 10)); - IGRAPH_FINALLY(igraph_stack_long_destroy, &path); + IGRAPH_CHECK(igraph_stack_int_init(&path, 10)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &path); while (VECTOR(*ancestor)[w] != 0) { - IGRAPH_CHECK(igraph_stack_long_push(&path, w)); + IGRAPH_CHECK(igraph_stack_int_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); + top = igraph_stack_int_pop(&path); + while (!igraph_stack_int_empty(&path)) { + pretop = igraph_stack_int_pop(&path); if (VECTOR(*semi)[VECTOR(*label)[top]] < VECTOR(*semi)[VECTOR(*label)[pretop]]) { @@ -347,16 +355,16 @@ static int igraph_i_dominator_COMPRESS(long int v, top = pretop; } - igraph_stack_long_destroy(&path); + igraph_stack_int_destroy(&path); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -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) { +static igraph_integer_t igraph_i_dominator_EVAL(igraph_integer_t v, + igraph_vector_int_t *ancestor, + igraph_vector_int_t *label, + igraph_vector_int_t *semi) { if (VECTOR(*ancestor)[v] == 0) { return v; } else { @@ -369,7 +377,7 @@ static long int igraph_i_dominator_EVAL(long int v, /** * \function igraph_dominator_tree - * Calculates the dominator tree of a flowgraph + * \brief 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 @@ -386,24 +394,27 @@ static long int igraph_i_dominator_EVAL(long int v, * 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. + * https://doi.org/10.1145/357062.357071 * * \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 + * then these vertices will be collected in the \p leftout * vector. - * \param root The id of the root (or source) vertex, this will be the + * \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 + * reachable from the root, -2 is stored here. For + * the root vertex itself, -1 is added. + * \param domtree Pointer to an \em uninitialized igraph_t, + * or \c 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 + * Graph and vertex attributes are preserved, but edge attributes + * are discarded. + * \param leftout Pointer to an initialized vector object, or \c NULL. If + * not \c 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 @@ -419,64 +430,58 @@ static long int igraph_i_dominator_EVAL(long int v, * \example examples/simple/dominator_tree.c */ -int igraph_dominator_tree(const igraph_t *graph, +igraph_error_t igraph_dominator_tree(const igraph_t *graph, igraph_integer_t root, - igraph_vector_t *dom, + igraph_vector_int_t *dom, igraph_t *domtree, - igraph_vector_t *leftout, + igraph_vector_int_t *leftout, igraph_neimode_t mode) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_vector_int_t parent; + igraph_vector_int_t semi; /* +1 always */ + igraph_vector_int_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; + igraph_vector_int_t ancestor; + igraph_vector_int_t label; - long int i; + igraph_neimode_t invmode = IGRAPH_REVERSE_MODE(mode); - igraph_vector_t vdom, *mydom = dom; + igraph_vector_int_t vdom, *mydom = dom; - long int component_size = 0; + igraph_integer_t component_size = 0; if (root < 0 || root >= no_of_nodes) { - IGRAPH_ERROR("Invalid root vertex id for dominator tree", - IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid root vertex ID for dominator tree.", + IGRAPH_EINVVID); } if (!igraph_is_directed(graph)) { - IGRAPH_ERROR("Dominator tree of an undirected graph requested", + IGRAPH_ERROR("Dominator tree of an undirected graph requested.", IGRAPH_EINVAL); } if (mode == IGRAPH_ALL) { - IGRAPH_ERROR("Invalid neighbor mode for dominator tree", + IGRAPH_ERROR("Invalid neighbor mode for dominator tree.", IGRAPH_EINVAL); } if (dom) { - IGRAPH_CHECK(igraph_vector_resize(dom, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(dom, no_of_nodes)); } else { mydom = &vdom; - IGRAPH_VECTOR_INIT_FINALLY(mydom, no_of_nodes); + IGRAPH_VECTOR_INT_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_vector_int_fill(mydom, -2); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&semi, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestor, no_of_nodes); + IGRAPH_CHECK(igraph_vector_int_init_range(&label, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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)); @@ -486,26 +491,26 @@ int igraph_dominator_tree(const igraph_t *graph, /* DFS first, to set semi, vertex and parent, step 1 */ - IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ 0, + IGRAPH_CHECK(igraph_dfs(graph, root, mode, /*unreachable=*/ false, /*order=*/ &vertex, - /*order_out=*/ 0, /*father=*/ &parent, - /*dist=*/ 0, /*in_callback=*/ 0, - /*out_callback=*/ 0, /*extra=*/ 0)); + /*order_out=*/ NULL, /*parents=*/ &parent, + /*dist=*/ NULL, /*in_callback=*/ NULL, + /*out_callback=*/ NULL, /*extra=*/ NULL)); - for (i = 0; i < no_of_nodes; i++) { - if (IGRAPH_FINITE(VECTOR(vertex)[i])) { - long int t = (long int) VECTOR(vertex)[i]; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (VECTOR(vertex)[i] >= 0) { + igraph_integer_t t = 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)); + igraph_integer_t n = no_of_nodes - component_size; + igraph_integer_t p = 0, j; + IGRAPH_CHECK(igraph_vector_int_resize(leftout, n)); for (j = 0; j < no_of_nodes && p < n; j++) { - if (!IGRAPH_FINITE(VECTOR(parent)[j])) { + if (VECTOR(parent)[j] < -1) { VECTOR(*leftout)[p++] = j; } } @@ -513,12 +518,12 @@ int igraph_dominator_tree(const igraph_t *graph, /* 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++) { + for (igraph_integer_t 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])) { + igraph_integer_t n = igraph_vector_int_size(v); + for (igraph_integer_t j = 0; j < n; ) { + igraph_integer_t v2 = VECTOR(*v)[j]; + if (VECTOR(parent)[v2] >= -1) { j++; } else { VECTOR(*v)[j] = VECTOR(*v)[n - 1]; @@ -530,23 +535,22 @@ int igraph_dominator_tree(const igraph_t *graph, /* 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; + for (igraph_integer_t i = component_size - 1; i > 0; i--) { + igraph_integer_t w = VECTOR(vertex)[i] - 1; igraph_vector_int_t *predw = igraph_adjlist_get(&pred, w); - long int j, n = igraph_vector_int_size(predw); + igraph_integer_t 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); + igraph_integer_t v = VECTOR(*predw)[j]; + igraph_integer_t 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); + igraph_i_dbucket_insert(&bucket, VECTOR(vertex)[ VECTOR(semi)[w] - 1 ] - 1, w); + igraph_i_dominator_LINK(VECTOR(parent)[w], w, &ancestor); + while (!igraph_i_dbucket_empty(&bucket, VECTOR(parent)[w])) { + igraph_integer_t v = igraph_i_dbucket_delete(&bucket, VECTOR(parent)[w]); + igraph_integer_t u = igraph_i_dominator_EVAL(v, &ancestor, &label, &semi); VECTOR(*mydom)[v] = VECTOR(semi)[u] < VECTOR(semi)[v] ? u : VECTOR(parent)[w]; } @@ -554,30 +558,30 @@ int igraph_dominator_tree(const igraph_t *graph, /* Finally, step 4 */ - for (i = 1; i < component_size; i++) { - long int w = (long int) VECTOR(vertex)[i] - 1; + for (igraph_integer_t i = 1; i < component_size; i++) { + igraph_integer_t w = 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)[w] = VECTOR(*mydom)[VECTOR(*mydom)[w]]; } } - VECTOR(*mydom)[(long int)root] = -1; + VECTOR(*mydom)[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_vector_int_destroy(&label); + igraph_vector_int_destroy(&ancestor); + igraph_vector_int_destroy(&vertex); + igraph_vector_int_destroy(&semi); + igraph_vector_int_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])) { + igraph_vector_int_t edges; + igraph_integer_t ptr = 0; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, component_size * 2 - 2); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (i != root && VECTOR(*mydom)[i] >= 0) { if (mode == IGRAPH_OUT) { VECTOR(edges)[ptr++] = VECTOR(*mydom)[i]; VECTOR(edges)[ptr++] = i; @@ -587,98 +591,96 @@ int igraph_dominator_tree(const igraph_t *graph, } } } - IGRAPH_CHECK(igraph_create(domtree, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_CHECK(igraph_create(domtree, &edges, no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_I_ATTRIBUTE_DESTROY(domtree); - IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, /*graph=*/ 1, /*vertex=*/ 1, - /*edge=*/ 0); + IGRAPH_I_ATTRIBUTE_COPY(domtree, graph, + /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); } if (!dom) { - igraph_vector_destroy(&vdom); + igraph_vector_int_destroy(&vdom); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t { - igraph_stack_t *stack; + igraph_stack_int_t *stack; igraph_vector_bool_t *nomark; const igraph_vector_bool_t *GammaX; - long int root; - const igraph_vector_t *map; + igraph_integer_t root; + const igraph_vector_int_t *map; } igraph_i_all_st_cuts_minimal_dfs_data_t; -static igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_incb( +static igraph_error_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_stack_int_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]; + const igraph_vector_int_t *map = data->map; + igraph_integer_t realvid = VECTOR(*map)[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); + if (VECTOR(*GammaX)[realvid]) { + if (!igraph_stack_int_empty(stack)) { + igraph_integer_t top = igraph_stack_int_top(stack); VECTOR(*nomark)[top] = 1; /* we just found a smaller one */ } - igraph_stack_push(stack, realvid); /* TODO: error check */ + IGRAPH_CHECK(igraph_stack_int_push(stack, realvid)); } - return 0; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_all_st_cuts_minimal_dfs_otcb( +static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_outcb( 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_stack_int_t *stack = data->stack; + const igraph_vector_int_t *map = data->map; + igraph_integer_t realvid = VECTOR(*map)[vid]; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); - if (!igraph_stack_empty(stack) && - igraph_stack_top(stack) == realvid) { - igraph_stack_pop(stack); + if (!igraph_stack_int_empty(stack) && + igraph_stack_int_top(stack) == realvid) { + igraph_stack_int_pop(stack); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_all_st_cuts_minimal(const igraph_t *graph, +static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, const igraph_t *domtree, - long int root, - const igraph_marked_queue_t *X, + igraph_integer_t root, + const igraph_marked_queue_int_t *X, const igraph_vector_bool_t *GammaX, - const igraph_vector_t *invmap, - igraph_vector_t *minimal) { + const igraph_vector_int_t *invmap, + igraph_vector_int_t *minimal) { - long int no_of_nodes = igraph_vcount(graph); - igraph_stack_t stack; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_stack_int_t stack; igraph_vector_bool_t nomark; igraph_i_all_st_cuts_minimal_dfs_data_t data; - long int i; + igraph_integer_t 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); + IGRAPH_STACK_INT_INIT_FINALLY(&stack, 10); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&nomark, no_of_nodes); data.stack = &stack; data.nomark = &nomark; @@ -690,91 +692,87 @@ static int igraph_i_all_st_cuts_minimal(const igraph_t *graph, 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; + VECTOR(nomark)[i] = (VECTOR(*GammaX)[i] == 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, + IGRAPH_CHECK(igraph_dfs(domtree, root, IGRAPH_IN, + /*unreachable=*/ false, /*order=*/ NULL, + /*order_out=*/ NULL, /*parents=*/ NULL, + /*dist=*/ NULL, + /*in_callback=*/ igraph_i_all_st_cuts_minimal_dfs_incb, + /*out_callback=*/ igraph_i_all_st_cuts_minimal_dfs_outcb, /*extra=*/ &data)); - igraph_vector_clear(minimal); + igraph_vector_int_clear(minimal); for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(nomark)[i]) { - IGRAPH_CHECK(igraph_vector_push_back(minimal, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i)); } } igraph_vector_bool_destroy(&nomark); - igraph_stack_destroy(&stack); + igraph_stack_int_destroy(&stack); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /* 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_error_t igraph_i_all_st_cuts_pivot( + const igraph_t *graph, const igraph_marked_queue_int_t *S, + const igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_integer_t *v, igraph_vector_int_t *Isv, void *arg +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_t Sbar; - igraph_vector_t Sbar_map, Sbar_invmap; - igraph_vector_t keep; + igraph_vector_int_t Sbar_map, Sbar_invmap; + igraph_vector_int_t keep; igraph_t domtree; - igraph_vector_t leftout; - long int i, nomin, n; - long int root; - igraph_vector_t M; + igraph_vector_int_t leftout; + igraph_integer_t i, nomin, n; + igraph_integer_t root; + igraph_vector_int_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_vector_int_t Nuv; + igraph_vector_int_t Isv_min; + igraph_vector_int_t GammaS_vec; + igraph_integer_t 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_INT_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0); - IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); + IGRAPH_VECTOR_INT_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)); + if (!igraph_marked_queue_int_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); } } - Sbar_size = igraph_vector_size(&keep); + Sbar_size = igraph_vector_int_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_vector_int_destroy(&keep); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &Sbar); - root = (long int) VECTOR(Sbar_map)[target] - 1; + root = 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, + IGRAPH_VECTOR_INT_INIT_FINALLY(&leftout, 0); + IGRAPH_CHECK(igraph_dominator_tree(&Sbar, root, /*dom=*/ 0, &domtree, &leftout, IGRAPH_IN)); IGRAPH_FINALLY(igraph_destroy, &domtree); @@ -787,24 +785,24 @@ int igraph_i_all_st_cuts_pivot(const igraph_t *graph, /* 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; + if (igraph_marked_queue_int_size(S) == 0) { + VECTOR(GammaS)[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, + if (igraph_marked_queue_int_iselement(S, i)) { + igraph_vector_int_t neis; + igraph_integer_t j; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; - if (!igraph_marked_queue_iselement(S, nei)) { + igraph_integer_t nei = VECTOR(neis)[j]; + if (!igraph_marked_queue_int_iselement(S, nei)) { VECTOR(GammaS)[nei] = 1; } } - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } } @@ -814,180 +812,195 @@ int igraph_i_all_st_cuts_pivot(const igraph_t *graph, 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); + n = igraph_vector_int_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; + VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[VECTOR(leftout)[i]]; + VECTOR(GammaS)[VECTOR(leftout)[i]] = 0; } - IGRAPH_VECTOR_INIT_FINALLY(&M, 0); + IGRAPH_VECTOR_INT_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); + igraph_vector_int_clear(Isv); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Nuv, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&GammaS_vec, i)); } } - nomin = igraph_vector_size(&M); + nomin = igraph_vector_int_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 */ + igraph_integer_t min = VECTOR(Sbar_map)[ VECTOR(M)[i] ] - 1; + igraph_integer_t nuvsize, isvlen, j; + IGRAPH_CHECK(igraph_dfs(&domtree, min, IGRAPH_IN, + /*unreachable=*/ NULL, /*order=*/ &Nuv, + /*order_out=*/ NULL, /*parents=*/ NULL, /*dist=*/ NULL, + /*in_callback=*/ NULL, /*out_callback=*/ NULL, + /*extra=*/ NULL)); + /* Remove the negative 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]; + igraph_integer_t t = VECTOR(Nuv)[nuvsize]; + if (t >= 0) { + VECTOR(Nuv)[nuvsize] = VECTOR(Sbar_invmap)[t]; } else { break; } } - igraph_vector_resize(&Nuv, nuvsize); + igraph_vector_int_resize(&Nuv, nuvsize); /* shrinks, error safe */ /* -------------------------------------------------------------*/ /* 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, + /*mode=*/ IGRAPH_OUT, /*unreachable=*/ false, /*restricted=*/ &Nuv, - /*order=*/ &Isv_min, /*rank=*/ 0, - /*father=*/ 0, /*pred=*/ 0, /*succ=*/ 0, - /*dist=*/ 0, /*callback=*/ 0, /*extra=*/ 0)); + /*order=*/ &Isv_min, /*rank=*/ NULL, + /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, + /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { - if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { + if (VECTOR(Isv_min)[isvlen] < 0) { break; } } - igraph_vector_resize(&Isv_min, isvlen); + igraph_vector_int_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]; + igraph_integer_t u = 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]; + *v = 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)); + IGRAPH_CHECK(igraph_vector_int_append(&Nuv, &leftout)); + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, + /*roots=*/ NULL, /*mode=*/ IGRAPH_OUT, + /*unreachable=*/ false, /*restricted=*/ &Nuv, + /*order=*/ &Isv_min, /*rank=*/ NULL, + /*parents=*/ NULL, /*pred=*/ NULL, /*succ=*/ NULL, + /*dist=*/ NULL, /*callback=*/ NULL, /*extra=*/ NULL)); for (isvlen = 0; isvlen < no_of_nodes; isvlen++) { - if (!IGRAPH_FINITE(VECTOR(Isv_min)[isvlen])) { + if (VECTOR(Isv_min)[isvlen] < 0) { break; } } - igraph_vector_resize(&Isv_min, isvlen); - igraph_vector_update(Isv, &Isv_min); + igraph_vector_int_resize(&Isv_min, isvlen); + igraph_vector_int_update(Isv, &Isv_min); break; } } - igraph_vector_destroy(&GammaS_vec); - igraph_vector_destroy(&Isv_min); - igraph_vector_destroy(&Nuv); + igraph_vector_int_destroy(&GammaS_vec); + igraph_vector_int_destroy(&Isv_min); + igraph_vector_int_destroy(&Nuv); IGRAPH_FINALLY_CLEAN(3); - igraph_vector_destroy(&M); + igraph_vector_int_destroy(&M); igraph_vector_bool_destroy(&GammaS); igraph_destroy(&domtree); - igraph_vector_destroy(&leftout); + igraph_vector_int_destroy(&leftout); igraph_destroy(&Sbar); - igraph_vector_destroy(&Sbar_map); - igraph_vector_destroy(&Sbar_invmap); + igraph_vector_int_destroy(&Sbar_map); + igraph_vector_int_destroy(&Sbar_invmap); IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } -/* 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); +/* TODO: This is a temporary recursive version */ - /* Go down left in the search tree */ - igraph_provan_shier_list(graph, S, T, source, target, - result, pivot, pivot_arg); +static igraph_error_t igraph_i_provan_shier_list_recursive( + const igraph_t *graph, igraph_marked_queue_int_t *S, + igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, + igraph_vector_int_t *Isv, void *pivot_arg +) { - /* Take out v from T */ - igraph_estack_pop(T); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t v = 0; + igraph_integer_t i, n; + + pivot(graph, S, T, source, target, &v, Isv, pivot_arg); + if (igraph_vector_int_empty(Isv)) { + if (igraph_marked_queue_int_size(S) != 0 && igraph_marked_queue_int_size(S) != no_of_nodes) { + igraph_vector_int_t *vec; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(result, &vec)); + IGRAPH_CHECK(igraph_marked_queue_int_as_vector(S, vec)); + } + } else { /* Add Isv to S */ - igraph_marked_queue_start_batch(S); - n = igraph_vector_size(&Isv); + IGRAPH_CHECK(igraph_marked_queue_int_start_batch(S)); + n = igraph_vector_int_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]); + if (!igraph_marked_queue_int_iselement(S, VECTOR(*Isv)[i])) { + IGRAPH_CHECK(igraph_marked_queue_int_push(S, VECTOR(*Isv)[i])); } } + igraph_vector_int_clear(Isv); /* Go down right in the search tree */ - - igraph_provan_shier_list(graph, S, T, source, target, - result, pivot, pivot_arg); + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, Isv, pivot_arg)); /* Take out Isv from S */ - igraph_marked_queue_pop_back_batch(S); + igraph_marked_queue_int_pop_back_batch(S); + + /* Put v into T */ + IGRAPH_CHECK(igraph_estack_push(T, v)); + + /* Go down left in the search tree */ + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, Isv, pivot_arg)); + + /* Take out v from T */ + igraph_estack_pop(T); + } - igraph_vector_destroy(&Isv); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_provan_shier_list( + const igraph_t *graph, igraph_marked_queue_int_t *S, + igraph_estack_t *T, igraph_integer_t source, igraph_integer_t target, + igraph_vector_int_list_t *result, igraph_provan_shier_pivot_t *pivot, + void *pivot_arg +) { + igraph_vector_int_t Isv; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv, 0); + + IGRAPH_CHECK(igraph_i_provan_shier_list_recursive( + graph, S, T, source, target, result, pivot, &Isv, pivot_arg + )); - return 0; + /* Reverse the result to stay compatible with versions before 0.10.3 */ + IGRAPH_CHECK(igraph_vector_int_list_reverse(result)); + + igraph_vector_int_destroy(&Isv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /** @@ -1000,22 +1013,15 @@ int igraph_provan_shier_list(const igraph_t *graph, * (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 + * \param cuts An initialized list of integer vectors, the cuts are stored + * here. 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 + * \param partition1s An initialized list of integer vectors, 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 + * 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. @@ -1024,9 +1030,9 @@ int igraph_provan_shier_list(const igraph_t *graph, * 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_error_t igraph_all_st_cuts(const igraph_t *graph, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_t *partition1s, igraph_integer_t source, igraph_integer_t target) { @@ -1037,12 +1043,13 @@ int igraph_all_st_cuts(const igraph_t *graph, 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_marked_queue_int_t S; igraph_estack_t T; - igraph_vector_ptr_t *mypartition1s = partition1s, vpartition1s; - long int i, nocuts; + igraph_vector_int_list_t *mypartition1s = partition1s, vpartition1s; + igraph_vector_int_t cut; + igraph_integer_t i, nocuts; if (!igraph_is_directed(graph)) { IGRAPH_ERROR("Listing all s-t cuts only implemented for " @@ -1051,20 +1058,17 @@ int igraph_all_st_cuts(const igraph_t *graph, if (!partition1s) { mypartition1s = &vpartition1s; - IGRAPH_CHECK(igraph_vector_ptr_init(mypartition1s, 0)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, mypartition1s); + IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, mypartition1s); } else { - igraph_vector_ptr_clear(mypartition1s); + igraph_vector_int_list_clear(mypartition1s); } - IGRAPH_CHECK(igraph_marked_queue_init(&S, no_of_nodes)); - IGRAPH_FINALLY(igraph_marked_queue_destroy, &S); + IGRAPH_CHECK(igraph_marked_queue_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_int_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); - } + IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); /* We call it with S={}, T={} */ IGRAPH_CHECK(igraph_provan_shier_list(graph, &S, &T, @@ -1072,73 +1076,64 @@ int igraph_all_st_cuts(const igraph_t *graph, igraph_i_all_st_cuts_pivot, /*pivot_arg=*/ 0)); - nocuts = igraph_vector_ptr_size(mypartition1s); + nocuts = igraph_vector_int_list_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)); + igraph_vector_int_t inS; + IGRAPH_CHECK(igraph_vector_int_init(&inS, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &inS); + igraph_vector_int_list_clear(cuts); + IGRAPH_CHECK(igraph_vector_int_list_reserve(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); + igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); + igraph_integer_t cutsize = 0; + igraph_integer_t j, partlen = igraph_vector_int_size(part); /* Mark elements */ for (j = 0; j < partlen; j++) { - long int v = (long int) VECTOR(*part)[j]; + igraph_integer_t v = 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]; + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t to = IGRAPH_TO(graph, j); + igraph_integer_t pfrom = VECTOR(inS)[from]; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_vector_int_resize(&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]; + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t to = IGRAPH_TO(graph, j); + igraph_integer_t pfrom = VECTOR(inS)[from]; + igraph_integer_t pto = VECTOR(inS)[to]; if ((pfrom == i + 1 && pto != i + 1)) { - VECTOR(*cut)[cutsize++] = j; + VECTOR(cut)[cutsize++] = j; } } - VECTOR(*cuts)[i] = cut; - IGRAPH_FINALLY_CLEAN(1); + /* Add the vector to 'cuts' */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); } - igraph_vector_long_destroy(&inS); + igraph_vector_int_destroy(&inS); IGRAPH_FINALLY_CLEAN(1); } + igraph_vector_int_destroy(&cut); igraph_estack_destroy(&T); - igraph_marked_queue_destroy(&S); - IGRAPH_FINALLY_CLEAN(2); + igraph_marked_queue_int_destroy(&S); + IGRAPH_FINALLY_CLEAN(3); 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_vector_int_list_destroy(mypartition1s); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /* We need to find the minimal active elements of Sbar. I.e. all @@ -1153,33 +1148,32 @@ int igraph_all_st_cuts(const igraph_t *graph, zero-indegree vertices. */ -static int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, +static igraph_error_t 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) { + const igraph_vector_int_t *invmap, + igraph_vector_int_t *minimal) { - long int no_of_nodes = igraph_vcount(Sbar); - igraph_vector_t indeg; - long int i, minsize; - igraph_vector_t neis; + igraph_integer_t no_of_nodes = igraph_vcount(Sbar); + igraph_vector_int_t indeg; + igraph_integer_t i, minsize; + igraph_vector_int_t neis; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INIT_FINALLY(&indeg, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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 ACTIVE(x) (VECTOR(*active)[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); + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_neighbors(Sbar, &neis, i, IGRAPH_OUT)); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = VECTOR(neis)[j]; VECTOR(indeg)[nei] -= 1; } } @@ -1191,7 +1185,7 @@ static int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, } } - IGRAPH_CHECK(igraph_vector_resize(minimal, minsize)); + IGRAPH_CHECK(igraph_vector_int_resize(minimal, minsize)); for (minsize = 0, i = 0; i < no_of_nodes; i++) { if (ACTIVE(i) && ZEROIN(i)) { @@ -1202,52 +1196,52 @@ static int igraph_i_all_st_mincuts_minimal(const igraph_t *Sbar, #undef ACTIVE #undef ZEROIN - igraph_vector_destroy(&indeg); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&indeg); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } 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, +static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, + const igraph_marked_queue_int_t *S, const igraph_estack_t *T, - long int source, - long int target, - long int *v, - igraph_vector_t *Isv, + igraph_integer_t source, + igraph_integer_t target, + igraph_integer_t *v, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j; + igraph_vector_int_t Sbar_map, Sbar_invmap; + igraph_vector_int_t keep; igraph_t Sbar; - igraph_vector_t M; - long int nomin; + igraph_vector_int_t M; + igraph_integer_t nomin; IGRAPH_UNUSED(source); IGRAPH_UNUSED(target); - if (igraph_marked_queue_size(S) == no_of_nodes) { - igraph_vector_clear(Isv); - return 0; + if (igraph_marked_queue_int_size(S) == no_of_nodes) { + igraph_vector_int_clear(Isv); + return IGRAPH_SUCCESS; } /* Create the graph induced by Sbar */ - IGRAPH_VECTOR_INIT_FINALLY(&Sbar_map, 0); - IGRAPH_VECTOR_INIT_FINALLY(&Sbar_invmap, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_map, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&Sbar_invmap, 0); - IGRAPH_VECTOR_INIT_FINALLY(&keep, 0); + IGRAPH_VECTOR_INT_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)); + if (!igraph_marked_queue_int_iselement(S, i)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&keep, i)); } } @@ -1263,16 +1257,16 @@ static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, /* ------------------------------------------------------------- */ /* Identify the set M of minimal elements that are active */ - IGRAPH_VECTOR_INIT_FINALLY(&M, 0); + IGRAPH_VECTOR_INT_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); + igraph_vector_int_clear(Isv); + nomin = igraph_vector_int_size(&M); for (i = 0; i < nomin; i++) { - long int min = (long int) VECTOR(Sbar_invmap)[ (long int) VECTOR(M)[i] ]; + igraph_integer_t min = VECTOR(Sbar_invmap)[ VECTOR(M)[i] ]; if (min != target) if (!igraph_estack_iselement(T, min)) { break; @@ -1281,42 +1275,42 @@ static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, 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] ]; + igraph_vector_int_t Isv_min; + IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); + *v = VECTOR(Sbar_invmap)[ VECTOR(M)[i] ]; /* TODO: restricted == keep ? */ - IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ (igraph_integer_t) *v,/*roots=*/ 0, - /*mode=*/ IGRAPH_IN, /*unreachable=*/ 0, + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v,/*roots=*/ NULL, + /*mode=*/ IGRAPH_IN, /*unreachable=*/ false, /*restricted=*/ &keep, /*order=*/ &Isv_min, - /*rank=*/ 0, /*father=*/ 0, /*pred=*/ 0, - /*succ=*/ 0, /*dist=*/ 0, /*callback=*/ 0, - /*extra=*/ 0)); + /*rank=*/ NULL, /*parents=*/ NULL, /*pred=*/ NULL, + /*succ=*/ NULL, /*dist=*/ NULL, /*callback=*/ NULL, + /*extra=*/ NULL)); for (j = 0; j < no_of_nodes; j++) { - igraph_real_t u = VECTOR(Isv_min)[j]; - if (!IGRAPH_FINITE(u)) { + igraph_integer_t u = VECTOR(Isv_min)[j]; + if (u < 0) { break; } if (!igraph_estack_iselement(T, u)) { - IGRAPH_CHECK(igraph_vector_push_back(Isv, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(Isv, u)); } } - igraph_vector_destroy(&Isv_min); + igraph_vector_int_destroy(&Isv_min); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_destroy(&M); + igraph_vector_int_destroy(&M); igraph_destroy(&Sbar); - igraph_vector_destroy(&keep); - igraph_vector_destroy(&Sbar_invmap); - igraph_vector_destroy(&Sbar_map); + igraph_vector_int_destroy(&keep); + igraph_vector_int_destroy(&Sbar_invmap); + igraph_vector_int_destroy(&Sbar_map); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_all_st_mincuts - * All minimum s-t cuts of a directed graph + * \brief 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 @@ -1331,15 +1325,16 @@ static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, * \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 + * here. It is a list of pointers to \ref igraph_vector_int_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 + * \ref igraph_vector_int_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 + * here. It is a list of pointers to \ref igraph_vector_int_t + * objects. 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. @@ -1358,135 +1353,124 @@ static int igraph_i_all_st_mincuts_pivot(const igraph_t *graph, * \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_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, + igraph_vector_int_list_t *cuts, + igraph_vector_int_list_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_vector_int_t NtoL; + igraph_vector_int_t cut; + igraph_integer_t newsource, newtarget; + igraph_marked_queue_int_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 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_vector_int_list_t closedsets; + igraph_vector_int_list_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); + 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); + IGRAPH_ERROR("Invalid source vertex.", IGRAPH_EINVVID); } if (target < 0 || target >= no_of_nodes) { - IGRAPH_ERROR("Invalid `target' vertex", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid target vertex.", IGRAPH_EINVVID); } if (source == target) { - IGRAPH_ERROR("`source' and 'target' are the same vertex", IGRAPH_EINVAL); + IGRAPH_ERROR("Source and target vertices are the same.", IGRAPH_EINVAL); + } + if (capacity && igraph_vector_size(capacity) != no_of_edges) { + IGRAPH_ERROR("Capacity vector length must agree with number of edges.", IGRAPH_EINVAL); } - if (capacity != NULL && igraph_vector_min(capacity) <= 0) - { + if (capacity && no_of_edges > 0 && 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); + IGRAPH_CHECK(igraph_vector_int_list_init(mypartition1s, 0)); + IGRAPH_FINALLY(igraph_vector_int_list_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, + IGRAPH_CHECK(igraph_maxflow(graph, value, &flow, /*cut=*/ NULL, + /*partition1=*/ NULL, /*partition2=*/ NULL, /*source=*/ source, /*target=*/ target, capacity, &stats)); /* -------------------------------------------------------------------- */ /* Then we need the reverse residual graph */ - IGRAPH_CHECK(igraph_reverse_residual_graph(graph, capacity, &residual, - &flow)); + 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]; + IGRAPH_VECTOR_INT_INIT_FINALLY(&NtoL, 0); + IGRAPH_CHECK(igraph_connected_components( + &residual, /*membership=*/ &NtoL, /*csize=*/ NULL, + /*no=*/ &proj_nodes, IGRAPH_STRONG + )); + IGRAPH_CHECK(igraph_contract_vertices(&residual, /*mapping=*/ &NtoL, /*vertex_comb=*/ NULL)); + IGRAPH_CHECK(igraph_simplify(&residual, /*multiple=*/ true, /*loops=*/ true, /*edge_comb=*/ NULL)); + + newsource = VECTOR(NtoL)[source]; + newtarget = VECTOR(NtoL)[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]; + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t pfrom = VECTOR(NtoL)[from]; + igraph_integer_t pto = VECTOR(NtoL)[to]; if (!VECTOR(VE1bool)[pfrom]) { - VECTOR(VE1bool)[pfrom] = 1; - VE1size++; + VECTOR(VE1bool)[pfrom] = true; } if (!VECTOR(VE1bool)[pto]) { - VECTOR(VE1bool)[pto] = 1; - VE1size++; + VECTOR(VE1bool)[pto] = true; } } } - 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); + igraph_vector_int_list_clear(cuts); } if (partition1s) { - igraph_vector_ptr_clear(partition1s); + igraph_vector_int_list_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_marked_queue_int_init(&S, no_of_nodes)); + IGRAPH_FINALLY(igraph_marked_queue_int_destroy, &S); IGRAPH_CHECK(igraph_estack_init(&T, no_of_nodes, 0)); IGRAPH_FINALLY(igraph_estack_destroy, &T); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cut, 0); pivot_data.active = &VE1bool; - IGRAPH_CHECK(igraph_vector_ptr_init(&closedsets, 0)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &closedsets); /* TODO */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&closedsets, 0); IGRAPH_CHECK(igraph_provan_shier_list(&residual, &S, &T, newsource, newtarget, &closedsets, igraph_i_all_st_mincuts_pivot, @@ -1497,96 +1481,86 @@ int igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value, 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]; + igraph_integer_t id = 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)); + nocuts = igraph_vector_int_list_size(&closedsets); + igraph_vector_int_list_clear(mypartition1s); + IGRAPH_CHECK(igraph_vector_int_list_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 */ + igraph_vector_int_t *supercut = igraph_vector_int_list_get_ptr(&closedsets, i); + igraph_integer_t j, supercutsize = igraph_vector_int_size(supercut); + + igraph_vector_int_clear(&cut); for (j = 0; j < supercutsize; j++) { - long int vtx = (long int) VECTOR(*supercut)[j]; - long int ovtx = (long int) VECTOR(revmap_ptr)[vtx]; + igraph_integer_t vtx = VECTOR(*supercut)[j]; + igraph_integer_t ovtx = VECTOR(revmap_ptr)[vtx]; while (ovtx != 0) { ovtx--; - IGRAPH_CHECK(igraph_vector_push_back(cut, ovtx)); - ovtx = (long int) VECTOR(revmap_next)[ovtx]; + IGRAPH_CHECK(igraph_vector_int_push_back(&cut, ovtx)); + ovtx = 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_CHECK(igraph_vector_int_list_push_back_copy(mypartition1s, &cut)); + + /* TODO: we could already reclaim the memory taken by 'supercut' here */ } igraph_vector_destroy(&revmap_next); igraph_vector_destroy(&revmap_ptr); - igraph_vector_ptr_destroy(&closedsets); + igraph_vector_int_list_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)); + igraph_vector_int_t memb; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&memb, no_of_nodes); + IGRAPH_CHECK(igraph_vector_int_list_reserve(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); + igraph_vector_int_t *part = igraph_vector_int_list_get_ptr(mypartition1s, i); + igraph_integer_t j, n = igraph_vector_int_size(part); + + igraph_vector_int_clear(&cut); for (j = 0; j < n; j++) { - long int vtx = (long int) VECTOR(*part)[j]; + igraph_integer_t vtx = 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); + igraph_integer_t from = IGRAPH_FROM(graph, j); + igraph_integer_t 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 */ + IGRAPH_CHECK(igraph_vector_int_push_back(&cut, j)); } } } - VECTOR(*cuts)[i] = v; - IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(cuts, &cut)); } - igraph_vector_long_destroy(&memb); + + igraph_vector_int_destroy(&memb); IGRAPH_FINALLY_CLEAN(1); } + igraph_vector_int_destroy(&cut); igraph_estack_destroy(&T); - igraph_marked_queue_destroy(&S); + igraph_marked_queue_int_destroy(&S); igraph_vector_bool_destroy(&VE1bool); - igraph_vector_destroy(&VE1); - igraph_vector_destroy(&NtoL); + igraph_vector_int_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_vector_int_list_destroy(mypartition1s); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/barabasi.c b/src/vendor/cigraph/src/games/barabasi.c index f83d234c030..d18619b9c64 100644 --- a/src/vendor/cigraph/src/games/barabasi.c +++ b/src/vendor/cigraph/src/games/barabasi.c @@ -31,49 +31,56 @@ #include "igraph_random.h" #include "core/interruption.h" +#include "math/safe_intop.h" -static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, +/* Attraction function for barabasi_game. + * We special-case power == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ +static igraph_real_t attraction(igraph_real_t degree, igraph_real_t power, igraph_real_t A) { + return ( power == 0 ? 1.0 : pow(degree, power) ) + A; +} + +static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_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, +static igraph_error_t 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, + const igraph_vector_int_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, +static igraph_error_t 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, + const igraph_vector_int_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, +static igraph_error_t igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_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; + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t *bag; + igraph_integer_t bagp = 0; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t resp; + igraph_integer_t i, j, k; + igraph_integer_t bagsize, start_nodes, start_edges, new_edges, no_of_edges; if (!directed) { outpref = 1; @@ -82,43 +89,52 @@ static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, 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]); + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; } else { new_edges = 0; } } else { - new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } - no_of_edges = start_edges + new_edges; resp = start_edges * 2; - bagsize = no_of_nodes + no_of_edges + (outpref ? no_of_edges : 0); + bagsize = no_of_nodes; + IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); + if (outpref) { + IGRAPH_SAFE_ADD(bagsize, no_of_edges, &bagsize); + } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); - bag = IGRAPH_CALLOC(bagsize, long int); + bag = IGRAPH_CALLOC(bagsize, igraph_integer_t); if (bag == 0) { - IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("barabasi_game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_vector_int_t deg; + igraph_integer_t ii, jj, sn = igraph_vcount(start_from); igraph_neimode_t mm = outpref ? IGRAPH_ALL : IGRAPH_IN; - IGRAPH_VECTOR_INIT_FINALLY(°, sn); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t d = VECTOR(deg)[ii]; for (jj = 0; jj <= d; jj++) { bag[bagp++] = ii; } } - igraph_vector_destroy(°); + igraph_vector_int_destroy(°); IGRAPH_FINALLY_CLEAN(1); } else { bag[bagp++] = 0; @@ -126,8 +142,8 @@ static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, /* 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); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); + igraph_vector_int_resize(&edges, no_of_edges * 2); } RNG_BEGIN(); @@ -141,17 +157,17 @@ static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, /* draw edges */ if (outseq) { - no_of_neighbors = (long int) VECTOR(*outseq)[k]; + no_of_neighbors = VECTOR(*outseq)[k]; } for (j = 0; j < no_of_neighbors; j++) { - long int to = bag[RNG_INTEGER(0, bagp - 1)]; + igraph_integer_t 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]; + bag[bagp++] = VECTOR(edges)[resp - 2 * j - 1]; if (outpref) { bag[bagp++] = i; } @@ -161,32 +177,31 @@ static int igraph_i_barabasi_game_bag(igraph_t *graph, igraph_integer_t n, RNG_END(); IGRAPH_FREE(bag); - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, +static igraph_error_t 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, + const igraph_vector_int_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_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_vector_int_t edges; + igraph_integer_t 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; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; + igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; if (!directed) { outpref = 1; @@ -195,54 +210,61 @@ static int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, 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]); + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; } else { new_edges = 0; } } else { - new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } - no_of_edges = start_edges + new_edges; edgeptr = start_edges * 2; - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); - /* first node(s) */ + /* First node(s): */ if (start_from) { - long int ii, sn = igraph_vcount(start_from); + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_vector_int_resize(°ree, no_of_nodes)); for (ii = 0; ii < sn; ii++) { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); } } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.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); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); + igraph_vector_int_resize(&edges, no_of_edges * 2); } RNG_BEGIN(); - /* and the rest */ + /* 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_integer_t to; IGRAPH_ALLOW_INTERRUPTION(); if (outseq) { - no_of_neighbors = (long int) VECTOR(*outseq)[k]; + no_of_neighbors = VECTOR(*outseq)[k]; } for (j = 0; j < no_of_neighbors; j++) { if (sum == 0) { @@ -258,48 +280,48 @@ static int igraph_i_barabasi_game_psumtree_multiple(igraph_t *graph, } /* 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)); + igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(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)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(VECTOR(degree)[i], power, A))); } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); } } RNG_END(); igraph_psumtree_destroy(&sumtree); - igraph_vector_destroy(°ree); + igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_barabasi_game_psumtree(igraph_t *graph, +static igraph_error_t 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, + const igraph_vector_int_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_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_vector_int_t edges; + igraph_integer_t 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; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; + igraph_integer_t start_nodes, start_edges, new_edges, no_of_edges; if (!directed) { outpref = 1; @@ -308,63 +330,70 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, 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]); + if (igraph_vector_int_size(outseq) > 1) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &new_edges)); + new_edges -= VECTOR(*outseq)[0]; } else { new_edges = 0; } } else { - new_edges = (no_of_nodes - start_nodes) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - start_nodes, no_of_neighbors, &new_edges); + } + IGRAPH_SAFE_ADD(start_edges, new_edges, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } - 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_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); RNG_BEGIN(); - /* first node(s) */ + /* First node(s): */ if (start_from) { - long int ii, sn = igraph_vcount(start_from); + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_vector_int_resize(°ree, no_of_nodes)); for (ii = 0; ii < sn; ii++) { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, pow(VECTOR(degree)[ii], power) + A)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, ii, attraction(VECTOR(degree)[ii], power, A))); } } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, A)); + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); } /* Initialize the edges vector */ if (start_from) { - IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ 0)); + IGRAPH_CHECK(igraph_get_edgelist(start_from, &edges, /* bycol= */ false)); } - /* and the rest */ + /* 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_integer_t to; IGRAPH_ALLOW_INTERRUPTION(); if (outseq) { - no_of_neighbors = (long int) VECTOR(*outseq)[k]; + no_of_neighbors = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); edgeptr += 2; - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, pow(VECTOR(degree)[to], power) + A)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, attraction(VECTOR(degree)[to], power, A))); } } else { for (j = 0; j < no_of_neighbors; j++) { @@ -377,36 +406,36 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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)); + igraph_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, attraction(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)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(VECTOR(degree)[i], power, A))); } else { - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, A)); + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, i, attraction(0, power, A))); } } RNG_END(); igraph_psumtree_destroy(&sumtree); - igraph_vector_destroy(°ree); + igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -414,39 +443,66 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, * \function igraph_barabasi_game * \brief Generates a graph based on the Barabási-Albert model. * + * This function implements several variants of the preferential attachment + * process, including linear and non-linear varieties of the Barabási-Albert + * and Price models. The graph construction starts with a single vertex, + * or an existing graph given by the \p start_from parameter. Then new vertices + * are added one at a time. Each new vertex connects to \p m existing vertices, + * choosing them with probabilities proportional to + * + * + * d^power + A, + * + * + * where \c d is the in- or total degree of the existing vertex (controlled + * by the \p outpref argument), while \p power and \p A are given by + * parameters. The constant attractiveness \p A + * is used to ensure that vertices with zero in-degree can also be + * connected to with non-zero probability. + * + * + * Barabási, A.-L. and Albert R. 1999. Emergence of scaling in + * random networks, Science, 286 509--512. + * https://doi.org/10.1126/science.286.5439.509 + * + * + * de Solla Price, D. J. 1965. Networks of Scientific Papers, Science, + * 149 510--515. + * https://doi.org/10.1126/science.149.3683.510 + * * \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 power Power of the preferential attachment. In the classic preferential + * attachment model power=1. Other values allow for + * sampling from a non-linear preferential attachment model. + * Negative values are only allowed when no zero-degree vertices + * are present during the construction process, i.e. when + * the starting graph has no isolated vertices and \p outpref + * is set to \c true. * \param m The number of outgoing edges generated for each - * vertex. (Only if \p outseq is \c NULL.) + * vertex. Only used when \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. + * constant, this can be a \c NULL pointer or an empty 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 A The constant attractiveness of vertices. When \p outpref + * is set to \c false, it should be positive to ensure that + * zero in-degree vertices can be connected to as well. * \param directed Boolean, whether to generate a directed graph. + * When set to \c false, outpref is assumed to be \c true. * \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 + * 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 @@ -461,7 +517,7 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, * 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 + * \param start_from Either a \c 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 @@ -470,8 +526,7 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, * 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. + * \c IGRAPH_EINVAL: invalid \p n, \p m, \p A or \p outseq parameter. * * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges. @@ -479,34 +534,28 @@ static int igraph_i_barabasi_game_psumtree(igraph_t *graph, * \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_error_t igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, igraph_real_t power, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_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; + igraph_integer_t start_nodes = start_from ? igraph_vcount(start_from) : 0; + igraph_integer_t newn = start_from ? n - start_nodes : n; /* Fix obscure parameterizations */ - if (outseq && igraph_vector_size(outseq) == 0) { - outseq = 0; + if (outseq && igraph_vector_int_empty(outseq)) { + outseq = NULL; } if (!directed) { - outpref = 1; + outpref = true; } /* 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) { @@ -515,14 +564,13 @@ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, 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) { + if (outseq && igraph_vector_int_size(outseq) != newn) { IGRAPH_ERROR("Invalid out-degree sequence length.", IGRAPH_EINVAL); } - if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) { + if (!outseq && m < 0) { IGRAPH_ERROR("Number of edges added per step must not be negative.", IGRAPH_EINVAL); } - if (outseq && igraph_vector_min(outseq) < 0) { + if (outseq && igraph_vector_int_min(outseq) < 0) { IGRAPH_ERROR("Negative out-degree in sequence.", IGRAPH_EINVAL); } if (!outpref && A <= 0) { @@ -535,7 +583,7 @@ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, } if (algo == IGRAPH_BARABASI_BAG) { if (power != 1) { - IGRAPH_ERROR("Power must be one for 'bag' algorithm.", IGRAPH_EINVAL); + 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.", @@ -550,29 +598,37 @@ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, IGRAPH_EINVAL); } - if (power <= 0) { - IGRAPH_ERRORF("Preferential attachment exponent must be positive, got %g.", - IGRAPH_EINVAL, - power); - } - 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) { + switch (algo) { + case IGRAPH_BARABASI_BAG: + return igraph_i_barabasi_game_bag(graph, n, m, outseq, outpref, directed, start_from); + + case 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) { + case IGRAPH_BARABASI_PSUMTREE_MULTIPLE: return igraph_i_barabasi_game_psumtree_multiple(graph, n, power, m, - outseq, outpref, A, - directed, start_from); + outseq, outpref, A, + directed, start_from); + default: + IGRAPH_ERROR("Invalid algorithm for Barabasi game.", IGRAPH_EINVAL); } +} - return 0; +/* Attraction function for barabasi_aging_game. + * We special-case deg_exp == 0 to ensure that 0^0 is computed as 1 instead of NaN. */ +static igraph_real_t attraction_aging( + igraph_real_t deg, igraph_real_t age, + igraph_real_t deg_exp, igraph_real_t age_exp, + igraph_real_t deg_A, igraph_real_t age_A, + igraph_real_t deg_coef, igraph_real_t age_coef) { + + igraph_real_t dp = deg_exp == 0 ? 1.0 : pow(deg, deg_exp); + igraph_real_t ap = pow(age, age_exp); + return (deg_coef * dp + deg_A) * (age_coef * ap + age_A); } /** @@ -588,7 +644,7 @@ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, * 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. + * which are multiplied to obtain the final weight. * * * The age \c l is based on the number of vertices in the @@ -626,10 +682,10 @@ int igraph_barabasi_game(igraph_t *graph, igraph_integer_t n, * 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_error_t igraph_barabasi_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -639,25 +695,25 @@ int igraph_barabasi_aging_game(igraph_t *graph, 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_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t binwidth; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; igraph_psumtree_t sumtree; - long int edgeptr = 0; - igraph_vector_t degree; + igraph_integer_t edgeptr = 0; + igraph_vector_int_t degree; if (no_of_nodes < 0) { - IGRAPH_ERRORF("Number of nodes must not be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + IGRAPH_ERRORF("Number of nodes must not be negative, got %" IGRAPH_PRId ".", 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).", + if (outseq != 0 && igraph_vector_int_size(outseq) != 0 && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("The length of the out-degree sequence (%" IGRAPH_PRId ") does not agree with the number of nodes (%" IGRAPH_PRId ").", IGRAPH_EINVAL, - igraph_vector_size(outseq), no_of_nodes); + igraph_vector_int_size(outseq), no_of_nodes); } - if ( (outseq == 0 || igraph_vector_size(outseq) == 0) && m < 0) { + if ( (outseq == 0 || igraph_vector_int_size(outseq) == 0) && m < 0) { IGRAPH_ERRORF("The number of edges per time step must not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, m); @@ -689,47 +745,45 @@ int igraph_barabasi_aging_game(igraph_t *graph, zero_age_appeal); } - if (pa_exp <= 0) { - IGRAPH_ERRORF("Preferential attachment exponent must be positive, got %g.", - IGRAPH_EINVAL, - pa_exp); - } - 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) { + if (outseq == 0 || igraph_vector_int_size(outseq) == 0) { no_of_neighbors = m; - no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); } else { - no_of_edges = 0; - for (i = 1; i < igraph_vector_size(outseq); i++) { - no_of_edges += VECTOR(*outseq)[i]; - } + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); RNG_BEGIN(); - /* first node */ - IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_deg_appeal * (1 + zero_age_appeal))); + /* First node: */ + /* Any weight may be used for the first node. In the first step, it will be connected to + * with certainty, after which its weight will be set appropriately. */ + IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, 1.0)); - /* and the rest */ + /* And the rest: */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - long int to; + igraph_integer_t to; IGRAPH_ALLOW_INTERRUPTION(); - if (outseq != 0 && igraph_vector_size(outseq) != 0) { - no_of_neighbors = (long int) VECTOR(*outseq)[i]; + if (outseq != 0 && igraph_vector_int_size(outseq) != 0) { + no_of_neighbors = VECTOR(*outseq)[i]; } sum = igraph_psumtree_sum(&sumtree); for (j = 0; j < no_of_neighbors; j++) { @@ -746,48 +800,59 @@ int igraph_barabasi_aging_game(igraph_t *graph, } /* 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_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; + igraph_integer_t 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) + attraction_aging(VECTOR(degree)[n], age+1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) )); } 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) + attraction_aging(VECTOR(degree)[i], 1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) )); } else { IGRAPH_CHECK(igraph_psumtree_update( - &sumtree, i, (1 + zero_age_appeal) * zero_deg_appeal + &sumtree, i, + attraction_aging(0, 1, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) )); } /* 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_integer_t shnode = i - binwidth * k; + igraph_integer_t deg = VECTOR(degree)[shnode]; + igraph_integer_t 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) + attraction_aging(deg, age + 2, + pa_exp, aging_exp, + zero_deg_appeal, zero_age_appeal, + deg_coef, age_coef) )); } } RNG_END(); - igraph_vector_destroy(°ree); + igraph_vector_int_destroy(°ree); igraph_psumtree_destroy(&sumtree); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/callaway_traits.c b/src/vendor/cigraph/src/games/callaway_traits.c index 3bdb8974b30..e4ce24b69f6 100644 --- a/src/vendor/cigraph/src/games/callaway_traits.c +++ b/src/vendor/cigraph/src/games/callaway_traits.c @@ -31,9 +31,8 @@ * \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. + * vertices with a given probability. * * * The simulation goes like this: in each discrete time step a new @@ -71,23 +70,29 @@ * 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_error_t 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_int_t *node_type_vec) { + igraph_integer_t i, j; + igraph_vector_int_t edges; igraph_vector_t cumdist; igraph_real_t maxcum; - igraph_vector_t *nodetypes; + igraph_vector_int_t *nodetypes; /* Argument contracts */ - if(nodes < 0){ + if (nodes < 0) { IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } + if (edges_per_step < 0) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); + } + if (types < 1) { IGRAPH_ERROR("The number of vertex types must be at least 1.", IGRAPH_EINVAL); } @@ -104,7 +109,7 @@ int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (igraph_is_nan(lo)) { + if (isnan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -115,12 +120,12 @@ int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); + igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ 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)) { + if (isnan(lo) || isnan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -129,7 +134,7 @@ int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); if (type_dist) { @@ -150,35 +155,35 @@ int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, if (node_type_vec) { nodetypes = node_type_vec; - IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(nodetypes, nodes)); } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); if (! nodetypes) { - IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); } RNG_BEGIN(); for (i = 0; i < nodes; i++) { igraph_real_t uni = RNG_UNIF(0, maxcum); - long int type; + igraph_integer_t 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]; + igraph_integer_t node1 = RNG_INTEGER(0, i); + igraph_integer_t node2 = RNG_INTEGER(0, i); + igraph_integer_t type1 = VECTOR(*nodetypes)[node1]; + igraph_integer_t type2 = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, node1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, node2)); } } } @@ -186,14 +191,14 @@ int igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nodes, RNG_END(); if (! node_type_vec) { - igraph_vector_destroy(nodetypes); + igraph_vector_int_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_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/citations.c b/src/vendor/cigraph/src/games/citations.c index 48552c21132..1cac157c699 100644 --- a/src/vendor/cigraph/src/games/citations.c +++ b/src/vendor/cigraph/src/games/citations.c @@ -29,8 +29,10 @@ #include "igraph_random.h" #include "igraph_interface.h" +#include "math/safe_intop.h" + typedef struct { - long int no; + igraph_integer_t no; igraph_psumtree_t *sumtrees; } igraph_i_citing_cited_type_game_struct_t; @@ -64,6 +66,7 @@ static void igraph_i_citing_cited_type_game_free ( * 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. @@ -71,7 +74,7 @@ static void igraph_i_citing_cited_type_game_free ( * 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 + * \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. @@ -85,33 +88,38 @@ static void igraph_i_citing_cited_type_game_free ( * 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_error_t 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_integer_t 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; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; + igraph_integer_t *lastcit; + igraph_integer_t *index; + igraph_integer_t 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.", + "Number of agebins is %" IGRAPH_PRId ", preference vector is of length %" IGRAPH_PRId ".", IGRAPH_EINVAL, agebins, igraph_vector_size(preference)); } - if (nodes < 0 ) { - IGRAPH_ERRORF("Number of nodes should be non-negative, received %"IGRAPH_PRId".", + 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".", + if (edges_per_node < 0) { + IGRAPH_ERRORF("Number of edges per node should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_node); + } + if (agebins < 1) { + IGRAPH_ERRORF("Number of age bins should be at least 1, received %" IGRAPH_PRId ".", IGRAPH_EINVAL, agebins); } @@ -132,23 +140,23 @@ int igraph_lastcit_game(igraph_t *graph, } binwidth = no_of_nodes / agebins + 1; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - lastcit = IGRAPH_CALLOC(no_of_nodes, long int); + lastcit = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (!lastcit) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, lastcit); - index = IGRAPH_CALLOC(no_of_nodes + 1, long int); + index = IGRAPH_CALLOC(no_of_nodes + 1, igraph_integer_t); if (!index) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes * edges_per_node)); /* The first node */ IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, VECTOR(*preference)[agebins])); @@ -161,7 +169,7 @@ int igraph_lastcit_game(igraph_t *graph, /* Add new edges */ for (j = 0; j < edges_per_node; j++) { - long int to; + igraph_integer_t to; igraph_real_t sum = igraph_psumtree_sum(&sumtree); if (sum == 0) { /* If none of the so-far added nodes have positive weight, @@ -170,8 +178,8 @@ int igraph_lastcit_game(igraph_t *graph, } else { igraph_psumtree_search(&sumtree, &to, RNG_UNIF(0, sum)); } - igraph_vector_push_back(&edges, i); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ lastcit[to] = i + 1; IGRAPH_CHECK(igraph_psumtree_update(&sumtree, to, VECTOR(*preference)[0])); } @@ -183,10 +191,10 @@ int igraph_lastcit_game(igraph_t *graph, /* 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]; + igraph_integer_t shnode = i - binwidth * k; + igraph_integer_t 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]; + igraph_integer_t cnode = VECTOR(edges)[j + 1]; if (lastcit[cnode] == shnode + 1) { IGRAPH_CHECK(igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k])); } @@ -203,10 +211,10 @@ int igraph_lastcit_game(igraph_t *graph, IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -244,21 +252,26 @@ int igraph_lastcit_game(igraph_t *graph, * vertices and edges, respectively. */ -int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_t *types, +igraph_error_t igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, const igraph_vector_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed) { - igraph_vector_t edges; + igraph_vector_int_t edges; igraph_vector_t cumsum; igraph_real_t sum, nnval; - long int i, j, type; - long int pref_len = igraph_vector_size(pref); + igraph_integer_t i, j, type; + igraph_integer_t 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 (igraph_vector_int_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") must match number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(types), nodes); + } + if (edges_per_step < 0) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); } if (nodes == 0) { @@ -267,20 +280,20 @@ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } /* 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)); + if (igraph_vector_int_min(types) < 0) { + IGRAPH_ERRORF("Types should be non-negative, but found %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_vector_int_min(types)); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes * edges_per_step)); /* first node */ VECTOR(cumsum)[0] = 0; - type = (long int) VECTOR(*types)[0]; + type = VECTOR(*types)[0]; if (type >= pref_len) { goto err_pref_too_short; } @@ -294,16 +307,16 @@ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, for (i = 1; i < nodes; i++) { for (j = 0; j < edges_per_step; j++) { - long int to; + igraph_integer_t 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); + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to - 1); /* reserved */ } - type = (long int) VECTOR(*types)[i]; + type = VECTOR(*types)[i]; if (type >= pref_len) { goto err_pref_too_short; } @@ -312,7 +325,7 @@ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, goto err_pref_neg; } sum += nnval; - igraph_vector_push_back(&cumsum, sum); + igraph_vector_push_back(&cumsum, sum); /* reserved */ } RNG_END(); @@ -320,14 +333,14 @@ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, igraph_vector_destroy(&cumsum); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_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); + IGRAPH_ERRORF("Preference vector should have length at least %" IGRAPH_PRId " with the given types.", IGRAPH_EINVAL, + igraph_vector_int_max(types) + 1); err_pref_neg: IGRAPH_ERRORF("Preferences should be non-negative, but found %g.", IGRAPH_EINVAL, @@ -335,7 +348,7 @@ int igraph_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game_struct_t *s) { - long int i; + igraph_integer_t i; if (!s->sumtrees) { return; } @@ -369,12 +382,14 @@ static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game * \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 + * \param types A numeric vector 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 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. @@ -383,38 +398,43 @@ static void igraph_i_citing_cited_type_game_free(igraph_i_citing_cited_type_game * vertices and edges, respectively. */ -int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, - const igraph_vector_t *types, +igraph_error_t igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, + const igraph_vector_int_t *types, const igraph_matrix_t *pref, igraph_integer_t edges_per_step, igraph_bool_t directed) { - igraph_vector_t edges; + igraph_vector_int_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; + igraph_integer_t no_of_types; + igraph_integer_t i, j, no_of_edges, no_of_edge_endpoints; - if (igraph_vector_size(types) != nodes) { - IGRAPH_ERRORF("Length of types vector (%ld) not equal to number" + if (igraph_vector_int_size(types) != nodes) { + IGRAPH_ERRORF("Length of types vector (%" IGRAPH_PRId ") not equal to number" " of nodes (%" IGRAPH_PRId ").", - IGRAPH_EINVAL, igraph_vector_size(types), nodes); + IGRAPH_EINVAL, igraph_vector_int_size(types), nodes); + } + if (edges_per_step < 0 ) { + IGRAPH_ERRORF("Number of edges per step should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_EINVAL, + edges_per_step); } /* avoid calling vector_max on empty vector */ - no_of_types = nodes == 0 ? 0 : igraph_vector_max(types) + 1; + no_of_types = nodes == 0 ? 0 : igraph_vector_int_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_ERRORF("Number of preference matrix columns (%" IGRAPH_PRId ") not " + "equal to number of types (%" IGRAPH_PRId ").", 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_ERRORF("Number of preference matrix rows (%" IGRAPH_PRId ") not " + "equal to number of types (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_matrix_nrow(pref), no_of_types); @@ -425,11 +445,11 @@ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, return igraph_empty(graph, 0, directed); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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_ERROR("Citing-cited type game failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_i_citing_cited_type_game_free, &str); @@ -439,11 +459,13 @@ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_types); - IGRAPH_CHECK(igraph_vector_reserve(&edges, nodes * edges_per_step)); + IGRAPH_SAFE_MULT(nodes, edges_per_step, &no_of_edges); + IGRAPH_SAFE_MULT(no_of_edges, 2, &no_of_edge_endpoints); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edge_endpoints)); /* First node */ for (i = 0; i < no_of_types; i++) { - long int type = (long int) VECTOR(*types)[0]; + igraph_integer_t type = VECTOR(*types)[0]; if ( MATRIX(*pref, i, type) < 0) { IGRAPH_ERRORF("Preference matrix contains negative entry: %g.", IGRAPH_EINVAL, MATRIX(*pref, i, type)); } @@ -454,10 +476,10 @@ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, RNG_BEGIN(); for (i = 1; i < nodes; i++) { - long int type = (long int) VECTOR(*types)[i]; + igraph_integer_t type = VECTOR(*types)[i]; igraph_real_t sum = VECTOR(sums)[type]; for (j = 0; j < edges_per_step; j++) { - long int to; + igraph_integer_t to; if (sum == 0) { /* If none of the so-far added nodes have positive weight, * we choose one uniformly to connect to. */ @@ -465,8 +487,8 @@ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, } else { igraph_psumtree_search(&sumtrees[type], &to, RNG_UNIF(0, sum)); } - igraph_vector_push_back(&edges, i); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, i); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ } /* add i */ @@ -484,9 +506,11 @@ int igraph_citing_cited_type_game(igraph_t *graph, igraph_integer_t nodes, igraph_i_citing_cited_type_game_free(&str); IGRAPH_FINALLY_CLEAN(1); - igraph_create(graph, &edges, nodes, directed); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&sums); IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/correlated.c b/src/vendor/cigraph/src/games/correlated.c index 732d00b75d5..20f0c95ad3e 100644 --- a/src/vendor/cigraph/src/games/correlated.c +++ b/src/vendor/cigraph/src/games/correlated.c @@ -34,7 +34,7 @@ /* 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 + * upper triangular in undirected) 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. */ @@ -44,9 +44,9 @@ /* 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_integer_t *a = (const igraph_integer_t *) va; + const igraph_integer_t *b = (const igraph_integer_t *) vb; + const igraph_integer_t 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]); @@ -60,8 +60,8 @@ static int code_cmp(void *graph, const void *va, const void *vb) { } /* 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); +static void sort_edges(igraph_vector_int_t *edges, const igraph_t *graph) { + igraph_qsort_r(VECTOR(*edges), igraph_vector_int_size(edges) / 2, 2*sizeof(igraph_integer_t), (void *) graph, code_cmp); } /** @@ -87,25 +87,26 @@ static void sort_edges(igraph_vector_t *edges, const igraph_t *graph) { * \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_error_t 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) { + const igraph_vector_int_t *permutation) { - long int no_of_nodes = igraph_vcount(old_graph); - long int no_of_edges = igraph_ecount(old_graph); + igraph_integer_t no_of_nodes = igraph_vcount(old_graph); + igraph_integer_t 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_all = directed ? ((igraph_real_t) no_of_nodes) * (no_of_nodes - 1) : + ((igraph_real_t) 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_vector_t add, delete; + igraph_vector_int_t 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_integer_t p_e = 0, p_a = 0, p_d = 0; + igraph_integer_t no_add, no_del; igraph_real_t next_e, next_a, next_d; - long int i; + igraph_integer_t i, newec; igraph_bool_t simple; if (corr < 0 || corr > 1) { @@ -117,7 +118,7 @@ int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, IGRAPH_EINVAL, p); } if (permutation) { - if (igraph_vector_size(permutation) != no_of_nodes) { + if (igraph_vector_int_size(permutation) != no_of_nodes) { IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game.", IGRAPH_EINVAL); } @@ -131,34 +132,32 @@ int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, /* Special cases */ if (corr == 0) { - return igraph_erdos_renyi_game(new_graph, IGRAPH_ERDOS_RENYI_GNP, - no_of_nodes, p, directed, - IGRAPH_NO_LOOPS); + return igraph_erdos_renyi_game_gnp(new_graph, 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_VECTOR_INT_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); + newec = igraph_vector_int_size(&edges); for (i = 0; i < newec; i++) { - int tmp = VECTOR(edges)[i]; + igraph_integer_t 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_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INT_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_VECTOR_INT_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(), + /* The sampling 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. */ @@ -202,36 +201,36 @@ int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, /* 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]; + igraph_integer_t td = VECTOR(delete)[i]; + igraph_integer_t from = VECTOR(edges)[2 * td]; + igraph_integer_t to = VECTOR(edges)[2 * td + 1]; VECTOR(delete)[i] = CODE(from, to); } - IGRAPH_CHECK(igraph_vector_reserve(&newedges, + IGRAPH_CHECK(igraph_vector_int_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; } } + { if (p_e < no_of_edges) { next_e=CODEE(); } else { next_e = IGRAPH_INFINITY; } } #define UPD_A() \ { if (p_a < no_add) { \ - next_a = VECTOR(add)[p_a] + p_e; } else { next_a = inf; } } + next_a = VECTOR(add)[p_a] + p_e; } else { next_a = IGRAPH_INFINITY; } } #define UPD_D() \ { if (p_d < no_del) { \ - next_d = VECTOR(delete)[p_d]; } else { next_d = inf; } } + next_d = VECTOR(delete)[p_d]; } else { next_d = IGRAPH_INFINITY; } } UPD_E(); UPD_A(); UPD_D(); - while (next_e != inf || next_a != inf || next_d != inf) { + while (next_e != IGRAPH_INFINITY || next_a != IGRAPH_INFINITY || next_d != IGRAPH_INFINITY) { 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])); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, VECTOR(edges)[2 * p_e])); + IGRAPH_CHECK(igraph_vector_int_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) { @@ -243,41 +242,41 @@ int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, } else { /* add an edge */ - long int to, from; - IGRAPH_ASSERT(igraph_finite(next_a)); + igraph_integer_t to, from; + IGRAPH_ASSERT(isfinite(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); + to = floor(next_a / no_of_nodes); + from = 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); + to = floor((sqrt(8 * next_a + 1) + 1) / 2); + from = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&newedges, to)); p_a++; UPD_A(); } } - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&add); igraph_vector_destroy(&delete); IGRAPH_FINALLY_CLEAN(3); if (permutation) { - long int newec = igraph_vector_size(&newedges); + newec = igraph_vector_int_size(&newedges); for (i = 0; i < newec; i++) { - long int tmp = VECTOR(newedges)[i]; + igraph_integer_t 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_vector_int_destroy(&newedges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -314,13 +313,12 @@ int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph, * \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_error_t 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) { + const igraph_vector_int_t *permutation) { - IGRAPH_CHECK(igraph_erdos_renyi_game(graph1, IGRAPH_ERDOS_RENYI_GNP, n, p, - directed, IGRAPH_NO_LOOPS)); + IGRAPH_CHECK(igraph_erdos_renyi_game_gnp(graph1, n, p, directed, IGRAPH_NO_LOOPS)); IGRAPH_CHECK(igraph_correlated_game(graph1, graph2, corr, p, permutation)); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/degree_sequence.c b/src/vendor/cigraph/src/games/degree_sequence.c index b4e6dd4efab..6ccf8b1f648 100644 --- a/src/vendor/cigraph/src/games/degree_sequence.c +++ b/src/vendor/cigraph/src/games/degree_sequence.c @@ -27,25 +27,29 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_graphicality.h" +#include "igraph_interface.h" #include "igraph_memory.h" +#include "igraph_operators.h" #include "igraph_random.h" #include "igraph_vector_ptr.h" #include "core/interruption.h" #include "core/set.h" +#include "games/degree_sequence_vl/degree_sequence_vl.h" +#include "math/safe_intop.h" -static int igraph_i_degree_sequence_game_simple(igraph_t *graph, - const igraph_vector_t *out_seq, - const igraph_vector_t *in_seq) { +static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq) { - long int outsum = 0, insum = 0; - igraph_bool_t directed = (in_seq != 0 && igraph_vector_size(in_seq) != 0); + igraph_integer_t outsum = 0, insum = 0; + igraph_bool_t directed = (in_seq != 0 && igraph_vector_int_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_integer_t no_of_nodes, no_of_edges; + igraph_integer_t *bag1 = 0, *bag2 = 0; + igraph_integer_t bagp1 = 0, bagp2 = 0; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t i, j; IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, °seq_ok)); if (!degseq_ok) { @@ -53,17 +57,17 @@ static int igraph_i_degree_sequence_game_simple(igraph_t *graph, "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL); } - outsum = (long int) igraph_vector_sum(out_seq); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); if (directed) { - insum = (long int) igraph_vector_sum(in_seq); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(in_seq, &insum)); } - no_of_nodes = igraph_vector_size(out_seq); + no_of_nodes = igraph_vector_int_size(out_seq); no_of_edges = directed ? outsum : outsum / 2; - bag1 = IGRAPH_CALLOC(outsum, long int); + bag1 = IGRAPH_CALLOC(outsum, igraph_integer_t); if (bag1 == 0) { - IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, bag1); @@ -73,9 +77,9 @@ static int igraph_i_degree_sequence_game_simple(igraph_t *graph, } } if (directed) { - bag2 = IGRAPH_CALLOC(insum, long int); + bag2 = IGRAPH_CALLOC(insum, igraph_integer_t); if (bag2 == 0) { - IGRAPH_ERROR("degree sequence game (simple)", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, bag2); for (i = 0; i < no_of_nodes; i++) { @@ -85,30 +89,30 @@ static int igraph_i_degree_sequence_game_simple(igraph_t *graph, } } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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 */ + igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); + igraph_integer_t to = RNG_INTEGER(0, bagp2 - 1); + igraph_vector_int_push_back(&edges, bag1[from]); /* safe, already reserved */ + igraph_vector_int_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 */ + igraph_integer_t from = RNG_INTEGER(0, bagp1 - 1); + igraph_integer_t to; + igraph_vector_int_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 */ + igraph_vector_int_push_back(&edges, bag1[to]); /* ditto */ bag1[to] = bag1[bagp1 - 1]; bagp1--; } @@ -123,43 +127,42 @@ static int igraph_i_degree_sequence_game_simple(igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_degree_sequence_game_no_multiple_undirected( - igraph_t *graph, const igraph_vector_t *seq) { +static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( + igraph_t *graph, const igraph_vector_int_t *seq) { - igraph_vector_t stubs = IGRAPH_VECTOR_NULL; + igraph_vector_int_t stubs; igraph_vector_int_t *neis; - igraph_vector_t residual_degrees = IGRAPH_VECTOR_NULL; + igraph_vector_int_t residual_degrees; 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_integer_t i, j, k; + igraph_integer_t 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_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); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(seq, &outsum)); + no_of_nodes = igraph_vector_int_size(seq); /* Allocate required data structures */ - IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, 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_VECTOR_INT_INIT_FINALLY(&stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&stubs, outsum)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_degrees, no_of_nodes); IGRAPH_CHECK(igraph_set_init(&incomplete_vertices, 0)); IGRAPH_FINALLY(igraph_set_destroy, &incomplete_vertices); @@ -179,30 +182,30 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected( igraph_adjlist_clear(&al); /* Initialize the residual degrees from the degree sequence */ - IGRAPH_CHECK(igraph_vector_update(&residual_degrees, seq)); + IGRAPH_CHECK(igraph_vector_int_update(&residual_degrees, seq)); /* While there are some unconnected stubs left... */ while (!finished && !failed) { /* Construct the initial stub vector */ - igraph_vector_clear(&stubs); + igraph_vector_int_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); + igraph_vector_int_push_back(&stubs, i); } } /* Clear the skipped stub counters and the set of incomplete vertices */ - igraph_vector_null(&residual_degrees); + igraph_vector_int_null(&residual_degrees); igraph_set_clear(&incomplete_vertices); /* Shuffle the stubs in-place */ - igraph_vector_shuffle(&stubs); + igraph_vector_int_shuffle(&stubs); /* Connect the stubs where possible */ - k = igraph_vector_size(&stubs); + k = igraph_vector_int_size(&stubs); for (i = 0; i < k; ) { - from = (igraph_integer_t) VECTOR(stubs)[i++]; - to = (igraph_integer_t) VECTOR(stubs)[i++]; + from = VECTOR(stubs)[i++]; + to = VECTOR(stubs)[i++]; if (from > to) { dummy = from; from = to; to = dummy; @@ -256,8 +259,8 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected( /* Clean up */ igraph_set_destroy(&incomplete_vertices); - igraph_vector_destroy(&residual_degrees); - igraph_vector_destroy(&stubs); + igraph_vector_int_destroy(&residual_degrees); + igraph_vector_int_destroy(&stubs); IGRAPH_FINALLY_CLEAN(3); /* Create the graph. We cannot use IGRAPH_ALL here for undirected graphs @@ -273,39 +276,39 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected( 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) { +static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t *graph, + const igraph_vector_int_t *out_seq, const igraph_vector_int_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 in_stubs; + igraph_vector_int_t out_stubs; igraph_vector_int_t *neis; - igraph_vector_t residual_in_degrees = IGRAPH_VECTOR_NULL; - igraph_vector_t residual_out_degrees = IGRAPH_VECTOR_NULL; + igraph_vector_int_t residual_in_degrees = IGRAPH_VECTOR_NULL; + igraph_vector_int_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_integer_t i, j, k; + igraph_integer_t 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_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); + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); + no_of_nodes = igraph_vector_int_size(out_seq); /* Allocate required data structures */ - IGRAPH_CHECK(igraph_adjlist_init_empty(&al, (igraph_integer_t) no_of_nodes)); + IGRAPH_CHECK(igraph_adjlist_init_empty(&al, 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_VECTOR_INT_INIT_FINALLY(&out_stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&out_stubs, outsum)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_stubs, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&in_stubs, outsum)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&residual_out_degrees, no_of_nodes); + IGRAPH_VECTOR_INT_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)); @@ -327,37 +330,37 @@ static int igraph_i_degree_sequence_game_no_multiple_directed(igraph_t *graph, 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)); + IGRAPH_CHECK(igraph_vector_int_update(&residual_out_degrees, out_seq)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_vector_int_clear(&out_stubs); + igraph_vector_int_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); + igraph_vector_int_push_back(&out_stubs, i); } for (j = 0; j < VECTOR(residual_in_degrees)[i]; j++) { - igraph_vector_push_back(&in_stubs, i); + igraph_vector_int_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_vector_int_null(&residual_out_degrees); + igraph_vector_int_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); + igraph_vector_int_shuffle(&out_stubs); /* Connect the stubs where possible */ - k = igraph_vector_size(&out_stubs); + k = igraph_vector_int_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]; + from = VECTOR(out_stubs)[i]; + to = VECTOR(in_stubs)[i]; neis = igraph_adjlist_get(&al, from); if (from == to || igraph_vector_int_binsearch(neis, to, &j)) { @@ -402,10 +405,10 @@ static int igraph_i_degree_sequence_game_no_multiple_directed(igraph_t *graph, /* 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_vector_int_destroy(&residual_in_degrees); + igraph_vector_int_destroy(&residual_out_degrees); + igraph_vector_int_destroy(&in_stubs); + igraph_vector_int_destroy(&out_stubs); IGRAPH_FINALLY_CLEAN(6); /* Create the graph */ @@ -427,31 +430,31 @@ static int igraph_i_degree_sequence_game_no_multiple_directed(igraph_t *graph, VECTOR(vec)[j] = temp; \ } -static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t *graph, const igraph_vector_t *degseq) { +static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirected(igraph_t *graph, const igraph_vector_int_t *degseq) { igraph_vector_int_t stubs; - igraph_vector_t edges; + igraph_vector_int_t edges; igraph_bool_t degseq_ok; igraph_vector_ptr_t adjlist; - long i, j; - long vcount, ecount, stub_count; + igraph_integer_t i, j; + igraph_integer_t 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); + IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence.", IGRAPH_EINVAL); } - stub_count = (long) igraph_vector_sum(degseq); + stub_count = igraph_vector_int_sum(degseq); ecount = stub_count / 2; - vcount = igraph_vector_size(degseq); + vcount = igraph_vector_int_size(degseq); IGRAPH_VECTOR_INT_INIT_FINALLY(&stubs, stub_count); - IGRAPH_VECTOR_INIT_FINALLY(&edges, stub_count); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, stub_count); /* Fill stubs vector. */ { - long k = 0; + igraph_integer_t k = 0; for (i = 0; i < vcount; ++i) { - long deg = (long) VECTOR(*degseq)[i]; + igraph_integer_t deg = VECTOR(*degseq)[i]; for (j = 0; j < deg; ++j) { VECTOR(stubs)[k++] = i; } @@ -465,21 +468,21 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t 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_ERROR("Cannot sample from configuration model (simple graphs).", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; - IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*degseq)[i])); + IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*degseq)[i])); } RNG_BEGIN(); for (;;) { - igraph_bool_t success = 1; + igraph_bool_t success = true; /* 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; + igraph_integer_t k, from, to; k = RNG_INTEGER(2*i, stub_count-1); SWAP_INT_ELEM(stubs, 2*i, k); @@ -492,13 +495,13 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t /* self-loop, fail */ if (from == to) { - success = 0; + success = false; break; } /* multi-edge, fail */ if (igraph_set_contains((igraph_set_t *) VECTOR(adjlist)[to], from)) { - success = 0; + success = false; break; } @@ -531,46 +534,46 @@ static int igraph_i_degree_sequence_game_no_multiple_undirected_uniform(igraph_t IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_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) { +static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directed( + igraph_t *graph, const igraph_vector_int_t *out_deg, const igraph_vector_int_t *in_deg) { igraph_vector_int_t out_stubs, in_stubs; - igraph_vector_t edges; + igraph_vector_int_t edges; igraph_bool_t degseq_ok; igraph_vector_ptr_t adjlist; - long i, j; - long vcount, ecount; + igraph_integer_t i, j; + igraph_integer_t 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); + ecount = igraph_vector_int_sum(out_deg); + vcount = igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2 * ecount); /* Fill in- and out-stubs vectors. */ { - long k = 0, l = 0; + igraph_integer_t k = 0, l = 0; for (i = 0; i < vcount; ++i) { - long dout, din; + igraph_integer_t dout, din; - dout = (long) VECTOR(*out_deg)[i]; + dout = VECTOR(*out_deg)[i]; for (j = 0; j < dout; ++j) { VECTOR(out_stubs)[k++] = i; } - din = (long) VECTOR(*in_deg)[i]; + din = VECTOR(*in_deg)[i]; for (j = 0; j < din; ++j) { VECTOR(in_stubs)[l++] = i; } @@ -584,21 +587,21 @@ static int igraph_i_degree_sequence_game_no_multiple_directed_uniform( 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_ERROR("Out of memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; - IGRAPH_CHECK(igraph_set_reserve(set, (long) VECTOR(*out_deg)[i])); + IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*out_deg)[i])); } RNG_BEGIN(); for (;;) { - igraph_bool_t success = 1; + igraph_bool_t success = true; /* 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_integer_t k, from, to; igraph_set_t *set; k = RNG_INTEGER(i, ecount-1); @@ -609,14 +612,14 @@ static int igraph_i_degree_sequence_game_no_multiple_directed_uniform( /* self-loop, fail */ if (to == from) { - success = 0; + success = false; break; } /* multi-edge, fail */ set = (igraph_set_t *) VECTOR(adjlist)[from]; if (igraph_set_contains(set, to)) { - success = 0; + success = false; break; } @@ -649,7 +652,7 @@ static int igraph_i_degree_sequence_game_no_multiple_directed_uniform( IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -657,12 +660,17 @@ static int igraph_i_degree_sequence_game_no_multiple_directed_uniform( #undef SWAP_INT_ELEM +igraph_error_t igraph_i_degree_sequence_game_edge_switching( + igraph_t *graph, + const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { -/* This is in gengraph_mr-connected.cpp */ + IGRAPH_CHECK(igraph_realize_degree_sequence(graph, out_seq, in_seq, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_rewire(graph, 10 * igraph_ecount(graph), IGRAPH_REWIRING_SIMPLE)); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} -int igraph_degree_sequence_game_vl(igraph_t *graph, - const igraph_vector_t *out_seq, - const igraph_vector_t *in_seq); /** * \ingroup generators @@ -679,7 +687,7 @@ int igraph_degree_sequence_game_vl(igraph_t *graph, * graph is generated), or the in-degree sequence. * \param method The method to generate the graph. Possible values: * \clist - * \cli IGRAPH_DEGSEQ_SIMPLE + * \cli IGRAPH_DEGSEQ_CONFIGURATION * 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 @@ -697,23 +705,28 @@ int igraph_degree_sequence_game_vl(igraph_t *graph, * 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 + * \cli IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE + * This method is identical to \c IGRAPH_DEGSEQ_CONFIGURATION, 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_FAST_HEUR_SIMPLE * This method generates simple graphs. - * It is similar to \c IGRAPH_DEGSEQ_SIMPLE + * It is similar to \c IGRAPH_DEGSEQ_CONFIGURATION * 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_EDGE_SWITCHING_SIMPLE + * This is an MCMC sampler based on degree-preserving edge switches. + * It generates simple undirected or directed graphs. + * It uses \ref igraph_realize_degree_sequence() to construct an initial + * graph, then rewires it using \ref igraph_rewire(). * \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. + * edge switches. * 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, @@ -735,41 +748,44 @@ int igraph_degree_sequence_game_vl(igraph_t *graph, * 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() + * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game_gnm(), + * \ref igraph_erdos_renyi_game_gnp(), \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_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_int_t *out_deg, + const igraph_vector_int_t *in_deg, igraph_degseq_t method) { - if (in_deg && igraph_vector_empty(in_deg) && !igraph_vector_empty(out_deg)) { + if (in_deg && igraph_vector_int_empty(in_deg) && !igraph_vector_int_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_CONFIGURATION: + return igraph_i_degree_sequence_game_configuration(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: + case IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE: if (in_deg == 0) { - return igraph_i_degree_sequence_game_no_multiple_undirected(graph, out_deg); + return igraph_i_degree_sequence_game_fast_heur_undirected(graph, out_deg); } else { - return igraph_i_degree_sequence_game_no_multiple_directed(graph, out_deg, in_deg); + return igraph_i_degree_sequence_game_fast_heur_directed(graph, out_deg, in_deg); } - case IGRAPH_DEGSEQ_SIMPLE_NO_MULTIPLE_UNIFORM: + case IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE: if (in_deg == 0) { - return igraph_i_degree_sequence_game_no_multiple_undirected_uniform(graph, out_deg); + return igraph_i_degree_sequence_game_configuration_simple_undirected(graph, out_deg); } else { - return igraph_i_degree_sequence_game_no_multiple_directed_uniform(graph, out_deg, in_deg); + return igraph_i_degree_sequence_game_configuration_simple_directed(graph, out_deg, in_deg); } + case IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE: + return igraph_i_degree_sequence_game_edge_switching(graph, out_deg, in_deg); + default: - IGRAPH_ERROR("Invalid degree sequence game method", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid degree sequence game method.", IGRAPH_EINVAL); } } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h b/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h new file mode 100644 index 00000000000..c4e9eeb884a --- /dev/null +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/degree_sequence_vl.h @@ -0,0 +1,34 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H +#define IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H + +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq); + +__END_DECLS + +#endif /* IGRAPH_GAMES_DEGREE_SEQUENCE_VL_H */ diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp deleted file mode 100644 index 11848d29c2d..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h deleted file mode 100644 index f5c7a415fb5..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_box_list.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * - * 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h index b52ebd17ed3..c96e24b35bc 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_definitions.h @@ -25,6 +25,7 @@ #include #include +#include "igraph_types.h" #include "internal/hacks.h" namespace gengraph { @@ -46,18 +47,12 @@ void SET_VERBOSE(int v); // Random number generator void my_srandom(int); -int my_random(); +long 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 @@ -66,19 +61,16 @@ typedef unsigned int ip_addr; defmin(int) defmin(double) defmin(unsigned long) + defmin(long long) #endif //min #ifndef max #define defmax(type) inline type max(type a, type b) { return a>b ? a : b; } defmax(int) defmax(double) defmax(unsigned long) + defmax(long 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 @@ -90,8 +82,8 @@ typedef unsigned int ip_addr; //Edge type typedef struct { - int from; - int to; + igraph_integer_t from; + igraph_integer_t to; } edge; // Tag Int @@ -113,18 +105,20 @@ inline double logp(double x) { //Fast search or replace -inline int* fast_rpl(int *m, const int a, const int b) { +inline igraph_integer_t* fast_rpl(igraph_integer_t *m, igraph_integer_t a, igraph_integer_t 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) { +inline igraph_integer_t* fast_search(igraph_integer_t *m, igraph_integer_t size, igraph_integer_t a) { + igraph_integer_t *p = m + size; + while (m != p--) { + if (*p == a) { return p; } + } return NULL; } @@ -164,7 +158,7 @@ inline unsigned char prev_dist(const unsigned char c) { // random number in ]0,1[, _very_ accurate around 0 inline double random_float() { - int r = my_random(); + long r = my_random(); double mul = inv_RANDMAX; while (r <= 0x7FFFFF) { r <<= 8; @@ -179,10 +173,10 @@ inline double random_float() { // Random bit generator, sparwise. static int _random_bits_stored = 0; -static int _random_bits = 0; +static long _random_bits = 0; inline int random_bit() { - int a = _random_bits; + long a = _random_bits; _random_bits = a >> 1; if (_random_bits_stored--) { return a & 0x1; diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp index 0f5a784a893..2db0a080abd 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -19,12 +19,7 @@ * 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 @@ -38,80 +33,35 @@ using namespace std; namespace gengraph { -// shuffle an int[] randomly -void random_permute(int *a, int n); +// shuffle an igraph_integer_t[] randomly +void random_permute(igraph_integer_t *a, igraph_integer_t n); // sort an array of positive integers in time & place O(n + max) -void cumul_sort(int *q, int n); - +void cumul_sort(igraph_integer_t *q, igraph_integer_t 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++) { + for (igraph_integer_t i = 0; i < n; i++) { total += deg[i]; } } degree_sequence:: -degree_sequence(int n0, int *degs) { +degree_sequence(igraph_integer_t n0, igraph_integer_t *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]; - } +degree_sequence(const igraph_vector_int_t *out_seq) { + n = igraph_vector_int_size(out_seq); + deg = &VECTOR(*out_seq)[0]; compute_total(); } @@ -119,177 +69,13 @@ degree_sequence(const igraph_vector_t *out_seq) { #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; + igraph_integer_t i; + igraph_integer_t dm = dmax() + 1; // Sort vertices using basket-sort, in descending degrees - int *nb = new int[dm]; - int *sorted = new int[n]; + igraph_integer_t *nb = new igraph_integer_t[dm]; + igraph_integer_t *sorted = new igraph_integer_t[n]; // init basket for (i = 0; i < dm; i++) { nb[i] = 0; @@ -299,9 +85,9 @@ bool degree_sequence::havelhakimi() { nb[deg[i]]++; } // cumul - int c = 0; + igraph_integer_t c = 0; for (i = dm - 1; i >= 0; i--) { - int t = nb[i]; + igraph_integer_t t = nb[i]; nb[i] = c; c += t; } @@ -311,8 +97,8 @@ bool degree_sequence::havelhakimi() { } // Binding process starts - int first = 0; // vertex with biggest residual degree - int d = dm - 1; // maximum residual degree available + igraph_integer_t first = 0; // vertex with biggest residual degree + igraph_integer_t 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) @@ -321,14 +107,14 @@ bool degree_sequence::havelhakimi() { d--; } // store it in dv - int dv = d; + igraph_integer_t 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 + igraph_integer_t dc = d; // residual degree of vertices we bind to + igraph_integer_t fc = ++first; // position of the first vertex with degree dc while (dv > 0 && dc > 0) { - int lc = nb[dc]; + igraph_integer_t lc = nb[dc]; if (lc != fc) { while (dv > 0 && lc > fc) { // binds v with sorted[--lc] @@ -351,71 +137,4 @@ bool degree_sequence::havelhakimi() { 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h index 61c287df809..ef286bf0f8a 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_degree_sequence.h @@ -22,73 +22,61 @@ #define DEGREE_SEQUENCE_H #include "igraph_types.h" -#include "igraph_datatype.h" +#include "igraph_vector.h" namespace gengraph { class degree_sequence { private: - int n; - int * deg; - int total; + igraph_integer_t n; + igraph_integer_t *deg; + igraph_integer_t total; public : // #vertices - inline int size() { + inline igraph_integer_t size() { return n; }; - inline int sum() { + inline igraph_integer_t sum() { return total; }; - inline int operator[](int i) { + inline igraph_integer_t operator[](igraph_integer_t i) { return deg[i]; }; - inline int *seq() { + inline igraph_integer_t *seq() { return deg; }; - inline void assign(int n0, int* d0) { + inline void assign(igraph_integer_t n0, igraph_integer_t* d0) { n = n0; deg = d0; }; - inline int dmax() { - int dm = deg[0]; - for (int i = 1; i < n; i++) if (deg[i] > dm) { + inline igraph_integer_t dmax() { + igraph_integer_t dm = deg[0]; + for (igraph_integer_t 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); + degree_sequence(igraph_integer_t n, igraph_integer_t *degs); // igraph constructor - degree_sequence(const igraph_vector_t *out_seq); + degree_sequence(const igraph_vector_int_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(); +#if 0 // raw print (vertex by vertex) void print(); // distribution print (degree frequency) void print_cumul(); +#endif // is degree sequence realizable ? bool havelhakimi(); diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp index dc055a2200d..586d944fc4c 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_hash.cpp @@ -18,31 +18,28 @@ * 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" +#include +#include +#include +#include +#include + namespace gengraph { //_________________________________________________________________________ void graph_molloy_hash::compute_neigh() { - int *p = links; - for (int i = 0; i < n; i++) { + igraph_integer_t *p = links; + for (igraph_integer_t i = 0; i < n; i++) { neigh[i] = p; p += HASH_SIZE(deg[i]); } @@ -51,49 +48,47 @@ void graph_molloy_hash::compute_neigh() { //_________________________________________________________________________ void graph_molloy_hash::compute_size() { size = 0; - for (int i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { size += HASH_SIZE(deg[i]); } } //_________________________________________________________________________ void graph_molloy_hash::init() { - for (int i = 0; i < size; i++) { + for (igraph_integer_t 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); + alloc(degs); } //_________________________________________________________________________ -int graph_molloy_hash::alloc(degree_sequence °s) { +igraph_integer_t 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]; + deg = new igraph_integer_t[n + size]; if (deg == NULL) { return 0; } - int i; + igraph_integer_t i; for (i = 0; i < n; i++) { deg[i] = degs[i]; } links = deg + n; init(); - neigh = new int*[n]; + neigh = new igraph_integer_t*[n]; if (neigh == NULL) { return 0; } compute_neigh(); - return sizeof(int *)*n + sizeof(int) * (n + size); + return sizeof(igraph_integer_t *)*n + sizeof(igraph_integer_t) * (n + size); } //_________________________________________________________________________ @@ -109,7 +104,7 @@ graph_molloy_hash::~graph_molloy_hash() { } //_________________________________________________________________________ -graph_molloy_hash::graph_molloy_hash(int *svg) { +graph_molloy_hash::graph_molloy_hash(igraph_integer_t *svg) { // Read n n = *(svg++); // Read a @@ -119,21 +114,20 @@ graph_molloy_hash::graph_molloy_hash(int *svg) { 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[] +igraph_integer_t *graph_molloy_hash::hard_copy() { + igraph_integer_t *hc = new igraph_integer_t[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; + memcpy(hc + 2, deg, sizeof(igraph_integer_t)*n); + igraph_integer_t *p = hc + 2 + n; + igraph_integer_t *l = links; + for (igraph_integer_t i = 0; i < n; i++) for (igraph_integer_t j = HASH_SIZE(deg[i]); j--; l++) { + igraph_integer_t d; if ((d = *l) != HASH_NONE && d >= i) { *(p++) = d; } @@ -145,20 +139,20 @@ int *graph_molloy_hash::hard_copy() { //_________________________________________________________________________ bool graph_molloy_hash::is_connected() { bool *visited = new bool[n]; - int *buff = new int[n]; - int comp_size = depth_search(visited, buff); + igraph_integer_t *buff = new igraph_integer_t[n]; + igraph_integer_t 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) { +igraph_integer_t* graph_molloy_hash::backup() { + igraph_integer_t *b = new igraph_integer_t[a / 2]; + igraph_integer_t *c = b; + igraph_integer_t *p = links; + for (igraph_integer_t i = 0; i < n; i++) + for (igraph_integer_t d = HASH_SIZE(deg[i]); d--; p++) if (*p != HASH_NONE && *p > i) { *(c++) = *p; } assert(c == b + (a / 2)); @@ -166,11 +160,11 @@ int* graph_molloy_hash::backup() { } //_________________________________________________________________________ -void graph_molloy_hash::restore(int* b) { +void graph_molloy_hash::restore(igraph_integer_t* b) { init(); - int i; - int *dd = new int[n]; - memcpy(dd, deg, sizeof(int)*n); + igraph_integer_t i; + igraph_integer_t *dd = new igraph_integer_t[n]; + memcpy(dd, deg, sizeof(igraph_integer_t)*n); for (i = 0; i < n; i++) { deg[i] = 0; } @@ -184,7 +178,7 @@ void graph_molloy_hash::restore(int* b) { } //_________________________________________________________________________ -bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) { +bool graph_molloy_hash::isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { if (K < 2) { return false; } @@ -193,18 +187,18 @@ bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) { return false; } #endif //OPT_ISOLATED - int *seen = Kbuff; - int *known = Kbuff; - int *max = Kbuff + K; + igraph_integer_t *seen = Kbuff; + igraph_integer_t *known = Kbuff; + igraph_integer_t *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]) { + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t 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 @@ -226,19 +220,19 @@ bool graph_molloy_hash::isolated(int v, int K, int *Kbuff, bool *visited) { } //_________________________________________________________________________ -int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) { +int graph_molloy_hash::random_edge_swap(igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { // Pick two random vertices a and c - int f1 = pick_random_vertex(); - int f2 = pick_random_vertex(); + igraph_integer_t f1 = pick_random_vertex(); + igraph_integer_t 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; + igraph_integer_t *f1t1 = random_neighbour(f1); + igraph_integer_t t1 = *f1t1; + igraph_integer_t *f2t2 = random_neighbour(f2); + igraph_integer_t t2 = *f2t2; // Check simplicity if (t1 == t2 || f1 == t2 || f2 == t1) { return 0; @@ -247,10 +241,10 @@ int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) { 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); + igraph_integer_t *f1t2 = H_rpl(neigh[f1], deg[f1], f1t1, t2); + igraph_integer_t *f2t1 = H_rpl(neigh[f2], deg[f2], f2t2, t1); + igraph_integer_t *t1f2 = H_rpl(neigh[t1], deg[t1], f1, f2); + igraph_integer_t *t2f1 = H_rpl(neigh[t2], deg[t2], f2, f1); // isolation test if (K <= 2) { return 1; @@ -267,16 +261,16 @@ int graph_molloy_hash::random_edge_swap(int K, int *Kbuff, bool *visited) { } //_________________________________________________________________________ -unsigned long graph_molloy_hash::shuffle(unsigned long times, - unsigned long maxtimes, int type) { +igraph_integer_t graph_molloy_hash::shuffle(igraph_integer_t times, + igraph_integer_t maxtimes, int type) { igraph_progress("Shuffle", 0, 0); // assert(verify()); // counters - unsigned long nb_swaps = 0; - unsigned long all_swaps = 0; + igraph_integer_t nb_swaps = 0; + igraph_integer_t all_swaps = 0; unsigned long cost = 0; // window - double T = double(min((unsigned long)(a), times) / 10); + double T = double(((a < times) ? a : times) / 10); if (type == OPTIMAL_HEURISTICS) { T = double(optimal_window()); } @@ -285,14 +279,14 @@ unsigned long graph_molloy_hash::shuffle(unsigned long times, } // isolation test parameter, and buffers double K = 2.4; - int *Kbuff = new int[int(K) + 1]; + igraph_integer_t *Kbuff = new igraph_integer_t[int(K) + 1]; bool *visited = new bool[n]; - for (int i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { visited[i] = false; } // Used for monitoring , active only if VERBOSE() - int failures = 0; - int successes = 0; + igraph_integer_t failures = 0; + igraph_integer_t successes = 0; double avg_K = 0; double avg_T = 0; unsigned long next = times; @@ -301,36 +295,36 @@ unsigned long graph_molloy_hash::shuffle(unsigned long times, // Shuffle: while #edge swap attempts validated by connectivity < times ... while (times > nb_swaps && maxtimes > all_swaps) { // Backup graph - int *save = backup(); + igraph_integer_t *save = backup(); // Prepare counters, K, T - unsigned long swaps = 0; - int K_int = 0; + igraph_integer_t swaps = 0; + igraph_integer_t K_int = 0; if (type == FINAL_HEURISTICS || type == BRUTE_FORCE_HEURISTICS) { - K_int = int(K); + K_int = igraph_integer_t(K); } - unsigned long T_int = (unsigned long)(floor(T)); + igraph_integer_t T_int = (igraph_integer_t)(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); + cost += K_int + T_int; } // Perform T edge swap attempts - for (int i = T_int; i > 0; i--) { + for (igraph_integer_t i = T_int; i > 0; i--) { // try one swap - swaps += (unsigned long)(random_edge_swap(K_int, Kbuff, visited)); + swaps += 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)); + next = (nb_swaps + swaps) + (times / 1000 > 100 ? times / 1000 : 100); int progress = int(double(nb_swaps + swaps) / double(times)); igraph_progress("Shuffle", progress, 0); } } // test connectivity - cost += (unsigned long)(a / 2); + cost += (a / 2); bool ok = is_connected(); // performance monitor { @@ -383,7 +377,7 @@ unsigned long graph_molloy_hash::shuffle(unsigned long times, } else { K *= 1.35; delete[] Kbuff; - Kbuff = new int[int(K) + 1]; + Kbuff = new igraph_integer_t[igraph_integer_t(K) + 1]; } break; case OPTIMAL_HEURISTICS: @@ -392,7 +386,7 @@ unsigned long graph_molloy_hash::shuffle(unsigned long times, } break; case BRUTE_FORCE_HEURISTICS: - K *= 2; delete[] Kbuff; Kbuff = new int[int(K) + 1]; + K *= 2; delete[] Kbuff; Kbuff = new igraph_integer_t[igraph_integer_t(K) + 1]; break; default: throw std::invalid_argument("Error in graph_molloy_hash::shuffle(): Unknown heuristics type."); @@ -406,40 +400,30 @@ unsigned long graph_molloy_hash::shuffle(unsigned long times, 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; + igraph_integer_t i, j; for (i = 0; i < n; i++) { - fprintf(f, "%d", i); + fprintf(f, "%" IGRAPH_PRId, i); for (j = 0; j < HASH_SIZE(deg[i]); j++) if (neigh[i][j] != HASH_NONE) { - fprintf(f, " %d", neigh[i][j]); + fprintf(f, " %" IGRAPH_PRId, 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_error_t graph_molloy_hash::print(igraph_t *graph) { + igraph_integer_t i, j; + igraph_integer_t ptr = 0; + igraph_vector_int_t edges; - IGRAPH_VECTOR_INIT_FINALLY(&edges, a); // every edge is counted twice.... + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, a); // every edge is counted twice.... for (i = 0; i < n; i++) { for (j = 0; j < HASH_SIZE(deg[i]); j++) { @@ -453,25 +437,25 @@ int graph_molloy_hash::print(igraph_t *graph) { } IGRAPH_CHECK(igraph_create(graph, &edges, n, /*undirected=*/ 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } //_________________________________________________________________________ -bool graph_molloy_hash::try_shuffle(int T, int K, int *backup_graph) { +bool graph_molloy_hash::try_shuffle(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *backup_graph) { // init all - int *Kbuff = NULL; + igraph_integer_t *Kbuff = NULL; bool *visited = NULL; if (K > 2) { - Kbuff = new int[K]; + Kbuff = new igraph_integer_t[K]; visited = new bool[n]; - for (int i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { visited[i] = false; } } - int *back = backup_graph; + igraph_integer_t *back = backup_graph; if (back == NULL) { back = backup(); } @@ -522,7 +506,7 @@ bool bernoulli_param_is_lower(int success, int trials, double param) { //_________________________________________________________________________ #define _MIN_SUCCESS_FOR_BERNOULLI_TRUST 100 -double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) { +double graph_molloy_hash::average_cost(igraph_integer_t T, igraph_integer_t *backup, double min_cost) { if (T < 1) { return 1e+99; } @@ -543,11 +527,11 @@ double graph_molloy_hash::average_cost(int T, int *backup, double min_cost) { } //_________________________________________________________________________ -int graph_molloy_hash::optimal_window() { - int Tmax; - int optimal_T = 1; +igraph_integer_t graph_molloy_hash::optimal_window() { + igraph_integer_t Tmax; + igraph_integer_t optimal_T = 1; double min_cost = 1e+99; - int *back = backup(); + igraph_integer_t *back = backup(); // on cherche une borne sup pour Tmax int been_greater = 0; for (Tmax = 1; Tmax <= 5 * a ; Tmax *= 2) { @@ -562,29 +546,19 @@ int graph_molloy_hash::optimal_window() { 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); + igraph_integer_t T_low = igraph_integer_t(double(optimal_T) / span); + igraph_integer_t T_high = igraph_integer_t(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; } @@ -620,21 +594,21 @@ double graph_molloy_hash::eval_K(int quality) { } //_________________________________________________________________________ -double graph_molloy_hash::effective_K(int K, int quality) { +double graph_molloy_hash::effective_K(igraph_integer_t K, int quality) { if (K < 3) { return 0.0; } long sum_K = 0; - int *Kbuff = new int[K]; + igraph_integer_t *Kbuff = new igraph_integer_t[K]; bool *visited = new bool[n]; - int i; + igraph_integer_t i; for (i = 0; i < n; i++) { visited[i] = false; } - for (int i = 0; i < quality; i++) { + for (i = 0; i < quality; i++) { // assert(verify()); - int f1, f2, t1, t2; - int *f1t1, *f2t2; + igraph_integer_t f1, f2, t1, t2; + igraph_integer_t *f1t1, *f2t2; do { // Pick two random vertices do { @@ -665,14 +639,14 @@ double graph_molloy_hash::effective_K(int K, int quality) { } //_________________________________________________________________________ -long graph_molloy_hash::effective_isolated(int v, int K, int *Kbuff, bool *visited) { - int i; +igraph_integer_t graph_molloy_hash::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + igraph_integer_t i; for (i = 0; i < K; i++) { Kbuff[i] = -1; } - long count = 0; - int left = K; - int *KB = Kbuff; + igraph_integer_t count = 0; + igraph_integer_t left = K; + igraph_integer_t *KB = Kbuff; //yapido = (my_random()%1000 == 0); depth_isolated(v, count, left, K, KB, visited); while (KB-- != Kbuff) { @@ -683,7 +657,7 @@ long graph_molloy_hash::effective_isolated(int v, int K, int *Kbuff, bool *visit } //_________________________________________________________________________ -void graph_molloy_hash::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) { +void graph_molloy_hash::depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited) { if (left_to_explore == 0) { return; } @@ -700,16 +674,16 @@ void graph_molloy_hash::depth_isolated(int v, long &calls, int &left_to_explore, // print(); // fflush(stdout); calls++; - int *copy = NULL; - int *w = neigh[v]; + igraph_integer_t *copy = NULL; + igraph_integer_t *w = neigh[v]; if (IS_HASH(deg[v])) { - copy = new int[deg[v]]; + copy = new igraph_integer_t[deg[v]]; H_copy(copy, w, deg[v]); w = copy; } qsort(deg, w, deg[v]); w += deg[v]; - for (int i = deg[v]; i--; ) { + for (igraph_integer_t i = deg[v]; i--; ) { if (visited[*--w]) { calls++; } else { @@ -725,19 +699,19 @@ void graph_molloy_hash::depth_isolated(int v, long &calls, int &left_to_explore, } //_________________________________________________________________________ -int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) { - for (int i = 0; i < n; i++) { +igraph_integer_t graph_molloy_hash::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { + for (igraph_integer_t i = 0; i < n; i++) { visited[i] = false; } - int *to_visit = buff; - int nb_visited = 1; + igraph_integer_t *to_visit = buff; + igraph_integer_t 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++) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t k = HASH_SIZE(deg[v]); k--; ww++) { if (HASH_NONE != (w = *ww) && !visited[w]) { visited[w] = true; nb_visited++; @@ -755,418 +729,4 @@ int graph_molloy_hash::depth_search(bool *visited, int *buff, int v0) { // 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 #include // This class handles graphs with a constant degree sequence. @@ -45,35 +47,35 @@ class graph_molloy_hash { private: // Number of vertices - int n; + igraph_integer_t n; //Number of arcs ( = #edges * 2 ) - int a; + igraph_integer_t a; //Total size of links[] - int size; + igraph_integer_t size; // The degree sequence of the graph - int *deg; + igraph_integer_t *deg; // The array containing all links - int *links; + igraph_integer_t *links; // The array containing pointers to adjacency list of every vertices - int **neigh; + igraph_integer_t **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 &); + igraph_integer_t 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]; + inline bool add_edge(igraph_integer_t u, igraph_integer_t v, igraph_integer_t *realdeg) { + igraph_integer_t 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(fast_search(neigh[u], (u == n - 1 ? links + size : neigh[u + 1]) - neigh[u], v) == NULL); + assert(fast_search(neigh[v], (v == n - 1 ? links + size : neigh[v + 1]) - neigh[v], u) == NULL); assert(deg[u] < deg_u); - int deg_v = realdeg[v]; + igraph_integer_t deg_v = realdeg[v]; if (IS_HASH(deg_u)) { *H_add(neigh[u], HASH_EXPAND(deg_u), v) = v; } else { @@ -92,70 +94,70 @@ class graph_molloy_hash { return true; } // Swap edges - inline void swap_edges(int from1, int to1, int from2, int to2) { + inline void swap_edges(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t 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(); + // Backup graph [sizeof(igraph_integer_t) bytes per edge] + igraph_integer_t* 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); + void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited); public: //degree of v - inline int degree(const int v) { + inline igraph_integer_t degree(igraph_integer_t v) { return deg[v]; }; // For debug purposes : verify validity of the graph (symetry, simplicity) - bool verify(); + //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 *); + graph_molloy_hash(igraph_integer_t *); // Create hard copy of graph - int *hard_copy(); + igraph_integer_t *hard_copy(); // Restore from backup - void restore(int* back); + void restore(igraph_integer_t* back); //Clear hash tables void init(); // nb arcs - inline int nbarcs() { + inline igraph_integer_t nbarcs() { return a; }; // nb vertices - inline int nbvertices() { + inline igraph_integer_t nbvertices() { return n; }; // print graph in SUCC_LIST mode, in stdout - void print(FILE *f = stdout); - int print(igraph_t *graph); + /* void print(FILE *f = stdout); */ + igraph_error_t print(igraph_t *graph); // Test if graph is connected bool is_connected(); // is edge ? - inline bool is_edge(int u, int v) { + inline bool is_edge(igraph_integer_t u, igraph_integer_t 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)); @@ -166,51 +168,19 @@ class graph_molloy_hash { } } // 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); + int random_edge_swap(igraph_integer_t K = 0, igraph_integer_t *Kbuff = NULL, bool *visited = NULL); // Connected Shuffle - unsigned long shuffle(unsigned long, unsigned long, int type); + igraph_integer_t shuffle(igraph_integer_t, igraph_integer_t, int type); // Optimal window for the gkantsidis heuristics - int optimal_window(); + igraph_integer_t optimal_window(); // Average unitary cost per post-validated edge swap, for some window - double average_cost(int T, int *back, double min_cost); + double average_cost(igraph_integer_t T, igraph_integer_t *back, double min_cost); // Get caracteristic K double eval_K(int quality = 100); // Get effective K - double effective_K(int K, int quality = 10000); + double effective_K(igraph_integer_t 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); - //___________________________________________________________________________________ - */ - + bool try_shuffle(igraph_integer_t T, igraph_integer_t K, igraph_integer_t *back = NULL); }; } // namespace gengraph diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp index 2859cdbe4d7..dc45a2aba4c 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.cpp @@ -23,15 +23,13 @@ #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" @@ -39,44 +37,17 @@ 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) { +igraph_integer_t graph_molloy_opt::max_degree() { + igraph_integer_t m = 0; + for (igraph_integer_t 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++) { + igraph_integer_t *p = links; + for (igraph_integer_t i = 0; i < n; i++) { neigh[i] = p; p += deg[i]; } @@ -86,12 +57,12 @@ 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 = new igraph_integer_t[n + a]; + for (igraph_integer_t i = 0; i < n; i++) { deg[i] = degs[i]; } links = deg + n; - neigh = new int*[n]; + neigh = new igraph_integer_t*[n]; compute_neigh(); } @@ -148,7 +119,7 @@ graph_molloy_opt::graph_molloy_opt(degree_sequence °s) { // if(VERBOSE()) fprintf(stderr,"done\n"); // } -graph_molloy_opt::graph_molloy_opt(int *svg) { +graph_molloy_opt::graph_molloy_opt(igraph_integer_t *svg) { // Read n n = *(svg++); // Read a @@ -158,7 +129,6 @@ graph_molloy_opt::graph_molloy_opt(int *svg) { degree_sequence dd(n, svg); // Build neigh[] and alloc links[] alloc(dd); - dd.detach(); // Read links[] restore(svg + n); } @@ -178,14 +148,14 @@ graph_molloy_opt::~graph_molloy_opt() { detach(); } -int* graph_molloy_opt::backup(int *b) { +igraph_integer_t* graph_molloy_opt::backup(igraph_integer_t *b) { if (b == NULL) { - b = new int[a / 2]; + b = new igraph_integer_t[a / 2]; } - int *c = b; - for (int i = 0; i < n; i++) { - int *p = neigh[i]; - for (int d = deg[i]; d--; p++) { + igraph_integer_t *c = b; + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t *p = neigh[i]; + for (igraph_integer_t d = deg[i]; d--; p++) { assert(*p != i); if (*p >= i) { *(c++) = *p; @@ -196,15 +166,15 @@ int* graph_molloy_opt::backup(int *b) { return b; } -int *graph_molloy_opt::hard_copy() { - int *hc = new int[2 + n + a / 2]; // to store n,a,deg[] and links[] +igraph_integer_t *graph_molloy_opt::hard_copy() { + igraph_integer_t *hc = new igraph_integer_t[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++) { + memcpy(hc + 2, deg, sizeof(igraph_integer_t)*n); + igraph_integer_t *c = hc + 2 + n; + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t *p = neigh[i]; + for (igraph_integer_t d = deg[i]; d--; p++) { assert(*p != i); if (*p >= i) { *(c++) = *p; @@ -215,15 +185,15 @@ int *graph_molloy_opt::hard_copy() { return hc; } -void graph_molloy_opt::restore(int* b) { - int i; +void graph_molloy_opt::restore(igraph_integer_t* b) { + igraph_integer_t i; for (i = 0; i < n; i++) { deg[i] = 0; } - int *p = links; + igraph_integer_t *p = links; for (i = 0; i < n - 1; i++) { p += deg[i]; - deg[i] = int(neigh[i + 1] - neigh[i]); + deg[i] = igraph_integer_t(neigh[i + 1] - neigh[i]); assert((neigh[i] + deg[i]) == neigh[i + 1]); while (p != neigh[i + 1]) { // b points to the current 'j' @@ -233,141 +203,107 @@ void graph_molloy_opt::restore(int* b) { } } -int* graph_molloy_opt::backup_degs(int *b) { +igraph_integer_t* graph_molloy_opt::backup_degs(igraph_integer_t *b) { if (b == NULL) { - b = new int[n]; + b = new igraph_integer_t[n]; } - memcpy(b, deg, sizeof(int)*n); + memcpy(b, deg, sizeof(igraph_integer_t)*n); return b; } -void graph_molloy_opt::restore_degs_only(int *b) { - memcpy(deg, b, sizeof(int)*n); +void graph_molloy_opt::restore_degs_only(igraph_integer_t *b) { + memcpy(deg, b, sizeof(igraph_integer_t)*n); refresh_nbarcs(); } -void graph_molloy_opt::restore_degs_and_neigh(int *b) { +void graph_molloy_opt::restore_degs_and_neigh(igraph_integer_t *b) { restore_degs_only(b); compute_neigh(); } -void graph_molloy_opt::restore_degs(int last_degree) { +void graph_molloy_opt::restore_degs(igraph_integer_t 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])); + for (igraph_integer_t i = n - 2; i >= 0; i--) { + a += (deg[i] = igraph_integer_t(neigh[i + 1] - neigh[i])); } refresh_nbarcs(); } void graph_molloy_opt::clean() { - int *b = hard_copy(); + igraph_integer_t *b = hard_copy(); replace(b); delete[] b; } -void graph_molloy_opt::replace(int *_hardcopy) { +void graph_molloy_opt::replace(igraph_integer_t *_hardcopy) { delete[] deg; n = *(_hardcopy++); a = *(_hardcopy++); - deg = new int[a + n]; - memcpy(deg, _hardcopy, sizeof(int)*n); + deg = new igraph_integer_t[a + n]; + memcpy(deg, _hardcopy, sizeof(igraph_integer_t)*n); links = deg + n; compute_neigh(); restore(_hardcopy + n); } -int* graph_molloy_opt::components(int *comp) { - int i; +igraph_integer_t* graph_molloy_opt::components(igraph_integer_t *comp) { + igraph_integer_t i; // breadth-first search buffer - int *buff = new int[n]; + igraph_integer_t *buff = new igraph_integer_t[n]; // comp[i] will contain the index of the component that contains vertex i if (comp == NULL) { - comp = new int[n]; + comp = new igraph_integer_t[n]; } - memset(comp, 0, sizeof(int)*n); + memset(comp, 0, sizeof(igraph_integer_t)*n); // current component index - int curr_comp = 0; + igraph_integer_t curr_comp = 0; // loop over all non-visited vertices... - for (int v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { + for (igraph_integer_t v0 = 0; v0 < n; v0++) if (comp[v0] == 0) { curr_comp++; // initiate breadth-first search - int *to_visit = buff; - int *visited = buff; + igraph_integer_t *to_visit = buff; + igraph_integer_t *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) { + igraph_integer_t v = *(visited++); + igraph_integer_t d = deg[v]; + for (igraph_integer_t *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); + igraph_integer_t nb_comp = 0; + memset(buff, 0, sizeof(igraph_integer_t)*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); + igraph_integer_t offset = 0; + igraph_integer_t *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]) { } + for (igraph_integer_t *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; + igraph_integer_t i; + igraph_integer_t dmax = max_degree() + 1; // Sort vertices using basket-sort, in descending degrees - int *nb = new int[dmax]; - int *sorted = new int[n]; + igraph_integer_t *nb = new igraph_integer_t[dmax]; + igraph_integer_t *sorted = new igraph_integer_t[n]; // init basket for (i = 0; i < dmax; i++) { nb[i] = 0; @@ -377,7 +313,7 @@ bool graph_molloy_opt::havelhakimi() { nb[deg[i]]++; } // cumul - int c = 0; + igraph_integer_t c = 0; for (i = dmax - 1; i >= 0; i--) { c += nb[i]; nb[i] = -nb[i] + c; @@ -388,30 +324,30 @@ bool graph_molloy_opt::havelhakimi() { } // Binding process starts - int first = 0; // vertex with biggest residual degree - int d = dmax - 1; // maximum residual degree available + igraph_integer_t first = 0; // vertex with biggest residual degree + igraph_integer_t 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]; + igraph_integer_t v = sorted[first]; // look for current degree of v while (nb[d] <= first) { d--; } // store it in dv - int dv = d; + igraph_integer_t 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 + igraph_integer_t dc = d; // residual degree of vertices we bind to + igraph_integer_t fc = ++first; // position of the first vertex with degree dc while (dv > 0 && dc > 0) { - int lc = nb[dc]; + igraph_integer_t lc = nb[dc]; if (lc != fc) { while (dv > 0 && lc > fc) { // binds v with sorted[--lc] dv--; - int w = sorted[--lc]; + igraph_integer_t w = sorted[--lc]; *(neigh[v]++) = w; *(neigh[w]++) = v; } @@ -428,12 +364,11 @@ bool graph_molloy_opt::havelhakimi() { /* 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 + * level. Therefore, we use IGRAPH_FATAL(), 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); + IGRAPH_FATALF("Error in graph_molloy_opt::havelhakimi(): " + "Couldn't bind vertex %" IGRAPH_PRId " entirely (%" IGRAPH_PRId " edges remaining)", + v, dv); return false; } } @@ -446,20 +381,22 @@ bool graph_molloy_opt::havelhakimi() { 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; + for (igraph_integer_t i = n; i > 0; visited[--i] = false) { } + igraph_integer_t *to_visit = new igraph_integer_t[n]; + igraph_integer_t *stop = to_visit; + igraph_integer_t 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]) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *w = neigh[v]; + for (igraph_integer_t k = deg[v]; k--; w++) { + if (!visited[*w]) { visited[*w] = true; left--; *(to_visit++) = *w; } + } } delete[] visited; delete[] stop; @@ -474,13 +411,13 @@ bool graph_molloy_opt::make_connected() { // fprintf(stderr,"\ngraph::make_connected() failed : #edges < #vertices-1\n"); return false; } - int i; + igraph_integer_t 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]; + igraph_integer_t *buff = new igraph_integer_t[MC_BUFF_SIZE]; unsigned char * dist = new unsigned char[n]; #define NOT_VISITED 255 #define FORBIDDEN 254 @@ -489,43 +426,39 @@ bool graph_molloy_opt::make_connected() { // 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; + igraph_integer_t *ffub = buff + MC_BUFF_SIZE; edge *edges = (edge *) ffub; - int *trees = ffub; - int *min_ffub = buff + 1 + (MC_BUFF_SIZE % 2 ? 0 : 1); + igraph_integer_t *trees = ffub; + igraph_integer_t *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) { + for (igraph_integer_t 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); + // 0-degree vertex found, cannot create connected graph return false; } dist[v0] = 0; // root - int *to_visit = buff; - int *current = buff; + igraph_integer_t *to_visit = buff; + igraph_integer_t *current = buff; *(to_visit++) = v0; // explore component connected to v0 bool is_a_tree = true; while (current != to_visit) { - int v = *(current++); + igraph_integer_t 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++) { + igraph_integer_t* ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t k = deg[v]; k--; ww++) { if (dist[w = *ww] == NOT_VISITED) { // we didn't visit *w yet dist[w] = next_dist; @@ -606,7 +539,7 @@ bool graph_molloy_opt::make_connected() { 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) { +bool graph_molloy_opt::swap_edges_simple(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t to2) { if (from1 == to1 || from1 == from2 || from1 == to2 || to1 == from2 || to1 == to2 || from2 == to2) { return false; } @@ -617,222 +550,27 @@ bool graph_molloy_opt::swap_edges_simple(int from1, int to1, int from2, int 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; + igraph_integer_t i, j; for (i = 0; i < n; i++) { if (!NOZERO || deg[i] > 0) { - fprintf(f, "%d", i); + fprintf(f, "%" IGRAPH_PRId, i); for (j = 0; j < deg[i]; j++) { - fprintf(f, " %d", neigh[i][j]); + fprintf(f, " %" IGRAPH_PRId, neigh[i][j]); } fprintf(f, "\n"); } } } -long graph_molloy_opt::effective_isolated(int v, int K, int *Kbuff, bool *visited) { - int i; +igraph_integer_t graph_molloy_opt::effective_isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { + igraph_integer_t i; for (i = 0; i < K; i++) { Kbuff[i] = -1; } - long count = 0; - int left = K; - int *KB = Kbuff; + igraph_integer_t count = 0; + igraph_integer_t left = K; + igraph_integer_t *KB = Kbuff; //yapido = (my_random()%1000 == 0); depth_isolated(v, count, left, K, KB, visited); while (KB-- != Kbuff) { @@ -842,7 +580,7 @@ long graph_molloy_opt::effective_isolated(int v, int K, int *Kbuff, bool *visite return count; } -void graph_molloy_opt::depth_isolated(int v, long &calls, int &left_to_explore, int dmax, int * &Kbuff, bool *visited) { +void graph_molloy_opt::depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &Kbuff, bool *visited) { if (left_to_explore == 0) { return; } @@ -857,10 +595,10 @@ void graph_molloy_opt::depth_isolated(int v, long &calls, int &left_to_explore, *(Kbuff++) = v; visited[v] = true; calls++; - int *w = neigh[v]; + igraph_integer_t *w = neigh[v]; qsort(deg, w, deg[v]); w += deg[v]; - for (int i = deg[v]; i--; ) { + for (igraph_integer_t i = deg[v]; i--; ) { if (visited[*--w]) { calls++; } else { @@ -872,19 +610,19 @@ void graph_molloy_opt::depth_isolated(int v, long &calls, int &left_to_explore, } } -int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) { - for (int i = 0; i < n; i++) { +igraph_integer_t graph_molloy_opt::depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t v0) { + for (igraph_integer_t i = 0; i < n; i++) { visited[i] = false; } - int *to_visit = buff; - int nb_visited = 1; + igraph_integer_t *to_visit = buff; + igraph_integer_t 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]) { + igraph_integer_t v = *(--to_visit); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; + for (igraph_integer_t k = deg[v]; k--; ww++) if (!visited[w = *ww]) { visited[w] = true; nb_visited++; *(to_visit++) = w; @@ -893,23 +631,23 @@ int graph_molloy_opt::depth_search(bool *visited, int *buff, int v0) { 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++) { +igraph_integer_t graph_molloy_opt::width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0, igraph_integer_t toclear) { + if (toclear >= 0) for (igraph_integer_t i = 0; i < toclear; i++) { dist[buff[i]] = 0; - } else for (int i = 0; i < n; i++) { + } else for (igraph_integer_t i = 0; i < n; i++) { dist[i] = 0; } - int *to_visit = buff; - int *to_add = buff; - int nb_visited = 1; + igraph_integer_t *to_visit = buff; + igraph_integer_t *to_add = buff; + igraph_integer_t 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; + igraph_integer_t v = *(to_visit++); + igraph_integer_t *ww = neigh[v]; + igraph_integer_t w; unsigned char d = next_dist(dist[v]); - for (int k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { + for (igraph_integer_t k = deg[v]; k--; ww++) if (dist[w = *ww] == 0) { dist[w] = d; nb_visited++; *(to_add++) = w; @@ -918,83 +656,26 @@ int graph_molloy_opt::width_search(unsigned char *dist, int *buff, int v0, int t 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) { +igraph_integer_t graph_molloy_opt::breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist) { unsigned char last_dist = 0; unsigned char curr_dist = 1; - int *to_visit = buff; - int *visited = buff; + igraph_integer_t *to_visit = buff; + igraph_integer_t *visited = buff; *(to_visit++) = src; paths[src] = 1.0; dist[src] = curr_dist; - int nb_visited = 1; + igraph_integer_t nb_visited = 1; while (visited != to_visit) { - int v = *(visited++); + igraph_integer_t v = *(visited++); if (last_dist == (curr_dist = dist[v])) { break; } unsigned char nd = next_dist(curr_dist); - int *ww = neigh[v]; + igraph_integer_t *ww = neigh[v]; double p = paths[v]; - for (int k = deg[v]; k--;) { - int w = *(ww++); + for (igraph_integer_t k = deg[v]; k--;) { + igraph_integer_t w = *(ww++); unsigned char d = dist[w]; if (d == 0) { // not visited yet ! @@ -1007,8 +688,7 @@ int graph_molloy_opt::breadth_path_search(int src, int *buff, double *paths, uns } } 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); + throw std::runtime_error("Fatal error: too many (>MAX_DOUBLE) possible paths in graph."); } } } @@ -1017,467 +697,8 @@ int graph_molloy_opt::breadth_path_search(int src, int *buff, double *paths, uns 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; +igraph_integer_t *graph_molloy_opt::vertices_real(igraph_integer_t &nb_v) { + igraph_integer_t *yo; if (nb_v < 0) { nb_v = 0; for (yo = deg; yo != deg + n; ) if (*(yo++) > 0) { @@ -1488,14 +709,13 @@ int *graph_molloy_opt::vertices_real(int &nb_v) { IGRAPH_WARNING("graph is empty"); return NULL; } - int *buff = new int[nb_v]; + igraph_integer_t *buff = new igraph_integer_t[nb_v]; yo = buff; - for (int i = 0; i < n; i++) if (deg[i] > 0) { + for (igraph_integer_t 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); + IGRAPH_WARNINGF("wrong #vertices in graph_molloy_opt::vertices_real(%" IGRAPH_PRId ")", nb_v); delete[] buff; return NULL; } else { @@ -1503,120 +723,7 @@ int *graph_molloy_opt::vertices_real(int &nb_v) { } } -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) { +bool graph_molloy_opt::isolated(igraph_integer_t v, igraph_integer_t K, igraph_integer_t *Kbuff, bool *visited) { if (K < 2) { return false; } @@ -1625,17 +732,17 @@ bool graph_molloy_opt::isolated(int v, int K, int *Kbuff, bool *visited) { return false; } #endif //OPT_ISOLATED - int *seen = Kbuff; - int *known = Kbuff; - int *max = Kbuff + (K - 1); + igraph_integer_t *seen = Kbuff; + igraph_integer_t *known = Kbuff; + igraph_integer_t *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]) { + igraph_integer_t *w = neigh[v]; + for (igraph_integer_t d = deg[v]; d--; w++) if (!visited[*w]) { #ifdef OPT_ISOLATED if (K <= deg[*w] + 1 || known == max) { #else //OPT_ISOLATED @@ -1656,138 +763,12 @@ bool graph_molloy_opt::isolated(int v, int K, int *Kbuff, bool *visited) { 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); // } @@ -1795,7 +776,7 @@ int graph_molloy_opt::cycles(int v) { bool graph_molloy_opt::verify(int mode) { IGRAPH_UNUSED(mode); #ifndef NDEBUG - int i, j, k; + igraph_integer_t i, j, k; assert(neigh[0] == links); // verify edges count if ((mode & VERIFY_NOARCS) == 0) { @@ -1819,8 +800,8 @@ bool graph_molloy_opt::verify(int mode) { // assert(neigh[i][j]!=neigh[i][k]); // verify symmetry for (i = 0; i < n; i++) for (j = 0; j < deg[i]; j++) { - int v = neigh[i][j]; - int nb = 0; + igraph_integer_t v = neigh[i][j]; + igraph_integer_t nb = 0; for (k = 0; k < deg[v]; k++) if (neigh[v][k] == i) { nb++; } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h index f057d606910..657273c6033 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_graph_molloy_optimized.h @@ -23,7 +23,6 @@ #include "gengraph_definitions.h" #include "gengraph_degree_sequence.h" -#include "gengraph_qsort.h" #include #include "gengraph_random.h" @@ -38,28 +37,28 @@ class graph_molloy_opt { // Random generator KW_RNG::RNG rng; // Number of vertices - int n; + igraph_integer_t n; //Number of arcs ( = #edges * 2 ) - int a; + igraph_integer_t a; // The degree sequence of the graph - int *deg; + igraph_integer_t *deg; // The array containing all links - int *links; + igraph_integer_t *links; // The array containing pointers to adjacency list of every vertices - int **neigh; + igraph_integer_t **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; ) { + for (igraph_integer_t* 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) { + inline void swap_edges(igraph_integer_t from1, igraph_integer_t to1, igraph_integer_t from2, igraph_integer_t to2) { fast_rpl(neigh[from1], to1, to2); fast_rpl(neigh[from2], to2, to1); fast_rpl(neigh[to1], from1, from2); @@ -67,103 +66,86 @@ class graph_molloy_opt { } // Swap edges only if they are simple. return false if unsuccessful. - bool swap_edges_simple(int, int, int, int); + bool swap_edges_simple(igraph_integer_t, igraph_integer_t, igraph_integer_t, igraph_integer_t); // 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); + void depth_isolated(igraph_integer_t v, igraph_integer_t &calls, igraph_integer_t &left_to_explore, igraph_integer_t dmax, igraph_integer_t * &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); + igraph_integer_t width_search(unsigned char *dist, igraph_integer_t *buff, igraph_integer_t v0 = 0, igraph_integer_t toclear = -1); // depth-first search. - int depth_search(bool *visited, int *buff, int v0 = 0); + igraph_integer_t depth_search(bool *visited, igraph_integer_t *buff, igraph_integer_t 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); + igraph_integer_t breadth_path_search(igraph_integer_t src, igraph_integer_t *buff, double *paths, unsigned char *dist); // 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); + igraph_integer_t *components(igraph_integer_t *comp = NULL); public: // neigh[] - inline int** neighbors() { + inline igraph_integer_t** neighbors() { return neigh; }; // deg[] - inline int* degrees() { + inline igraph_integer_t* degrees() { return deg; }; //adjacency list of v - inline int* operator[](const int v) { + inline igraph_integer_t* operator[](const igraph_integer_t v) { return neigh[v]; }; //degree of v - inline int degree(const int v) { + inline igraph_integer_t degree(const igraph_integer_t 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); + //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 *); + graph_molloy_opt(igraph_integer_t *); // Create hard copy of graph - int *hard_copy(); + igraph_integer_t *hard_copy(); // Remove unused edges, updates neigh[], recreate links[] void clean(); // nb arcs - inline int nbarcs() { + inline igraph_integer_t nbarcs() { return a; }; // last degree - inline int last_degree() { + inline igraph_integer_t last_degree() { return deg[n - 1]; }; // nb vertices - inline int nbvertices() { + inline igraph_integer_t nbvertices() { return n; }; // nb vertices having degree > 0 - inline int nbvertices_real() { - int s = 0; - for (int *d = deg + n; d-- != deg; ) if (*d) { + inline igraph_integer_t nbvertices_real() { + igraph_integer_t s = 0; + for (igraph_integer_t *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(); + igraph_integer_t *vertices_real(igraph_integer_t &nb_v); // 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) @@ -173,72 +155,35 @@ class graph_molloy_opt { // 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); + igraph_integer_t max_degree(); // is edge ? - inline bool is_edge(const int u, const int v) { + inline bool is_edge(const igraph_integer_t u, const igraph_integer_t 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); + // Backup graph [sizeof(igraph_integer_t) bytes per edge] + igraph_integer_t* backup(igraph_integer_t *here = NULL); // Restore from backup. Assume that degrees haven't changed - void restore(int* back); + void restore(igraph_integer_t* back); // Resplace with hard backup. - void replace(int* _hardbackup); + void replace(igraph_integer_t* _hardbackup); // Backup degs of graph - int* backup_degs(int *here = NULL); + igraph_integer_t* backup_degs(igraph_integer_t *here = NULL); // Restore degs from neigh[]. Need last degree, though - void restore_degs(int last_degree); + void restore_degs(igraph_integer_t last_degree); // Restore degs[] from backup. Assume that links[] has only been permuted - void restore_degs_only(int* backup_degs); + void restore_degs_only(igraph_integer_t* 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); + void restore_degs_and_neigh(igraph_integer_t* backup_degs); // 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); + //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); + //void remove_vertex(int v); // For debug purposes : verify validity of the graph (symetry, simplicity) #define VERIFY_NORMAL 0 diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h index 7084edc6c70..9689c0a79b0 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_hash.h @@ -85,10 +85,15 @@ namespace gengraph { #define HASH_NONE (-1) #ifdef HASH_SIZE_IS_POWER2 -inline int HASH_EXPAND(int x) { +inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { + /* Returns pow(2, floor(log2(x)) + 2) if x > 0, 1 otherwise. Works up to + * x == 2^64, starts to break down afterwards */ _HASH_EXP_CALL(); x += x; x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> 8; x |= x >> 16; +#if IGRAPH_INTEGER_SIZE == 64 + x |= x >> 32; +#endif return x + 1; } #define HASH_KEY(x,size) ((x*2198737)&((size)-1)) @@ -104,19 +109,19 @@ inline int HASH_EXPAND(int 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) { +inline igraph_integer_t HASH_KEY(igraph_integer_t x, igraph_integer_t size) { assert(x >= 0); return x % size; }; -inline int HASH_EXPAND(const int x) { +inline igraph_integer_t HASH_EXPAND(igraph_integer_t x) { _HASH_EXP_CALL(); return x + (x >> 1); }; -inline int HASH_UNEXPAND(const int x) { +inline int HASH_UNEXPAND(igraph_integer_t x) { return ((x << 1) + 1) / 3; }; #endif //HASH_SIZE_IS_POWER2 -inline int HASH_REKEY(const int k, const int s) { +inline int HASH_REKEY(igraph_integer_t k, igraph_integer_t s) { assert(k >= 0); if (k == 0) { return s - 1; @@ -124,7 +129,7 @@ inline int HASH_REKEY(const int k, const int s) { return k - 1; } }; -inline int HASH_SIZE(const int x) { +inline int HASH_SIZE(igraph_integer_t x) { if (IS_HASH(x)) { return HASH_EXPAND(x); } else { @@ -133,7 +138,7 @@ inline int HASH_SIZE(const int x) { }; #endif //MACRO_RATHER_THAN_INLINE -inline int HASH_PAIR_KEY(const int x, const int y, const int size) { +inline igraph_integer_t HASH_PAIR_KEY(igraph_integer_t x, igraph_integer_t y, igraph_integer_t size) { return HASH_KEY(x * 1434879443 + y, size); } @@ -143,17 +148,19 @@ inline int HASH_PAIR_KEY(const int x, const int y, const int size) { //_________________________________________________________________________ // 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) { +inline void H_copy(igraph_integer_t *mem, igraph_integer_t *h, igraph_integer_t size) { + for (igraph_integer_t 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) { +inline igraph_integer_t* H_add(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); - int k = HASH_KEY(a, size); + igraph_integer_t k = HASH_KEY(a, size); if (h[k] == HASH_NONE) { return h + k; } @@ -168,8 +175,8 @@ inline int* H_add(int* h, const int size, int a) { } // 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); +inline bool H_better(igraph_integer_t a, igraph_integer_t size, igraph_integer_t currentk, igraph_integer_t newk) { + igraph_integer_t k = HASH_KEY(a, size); if (newk < currentk) { return (k < currentk && k >= newk); } else { @@ -178,13 +185,13 @@ inline bool H_better(const int a, const int size, const int currentk, const int } // removes h[k] -inline void H_rm(int* h, const int size, int k) { +inline void H_rm(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t k) { _HASH_RM_CALL(); - int lasthole = k; + igraph_integer_t lasthole = k; do { _HASH_RM_ITER(); k = HASH_REKEY(k, size); - int next = h[k]; + igraph_integer_t next = h[k]; if (next == HASH_NONE) { break; } @@ -197,11 +204,11 @@ inline void H_rm(int* h, const int size, int k) { } //put a -inline int* H_put(int* h, const int size, const int a) { +inline igraph_integer_t* H_put(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a) { assert(H_add(h, size, a) != NULL); _HASH_PUT_CALL(); _HASH_PUT_ITER(); - int k = HASH_KEY(a, size); + igraph_integer_t k = HASH_KEY(a, size); while (h[k] != HASH_NONE) { k = HASH_REKEY(k, size); _HASH_PUT_ITER(); @@ -212,11 +219,11 @@ inline int* H_put(int* h, const int size, const int a) { } // find A -inline int H_find(int *h, int size, const int a) { +inline igraph_integer_t H_find(igraph_integer_t *h, igraph_integer_t size, igraph_integer_t a) { assert(H_add(h, size, a) == NULL); _HASH_FIND_CALL(); _HASH_FIND_ITER(); - int k = HASH_KEY(a, size); + igraph_integer_t k = HASH_KEY(a, size); while (h[k] != a) { k = HASH_REKEY(k, size); _HASH_FIND_ITER(); @@ -225,10 +232,10 @@ inline int H_find(int *h, int size, const int a) { } // 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) { +inline bool H_pair_insert(igraph_integer_t* h, igraph_integer_t size, igraph_integer_t a, igraph_integer_t b) { _HASH_ADD_CALL(); _HASH_ADD_ITER(); - int k = HASH_PAIR_KEY(a, b, size); + igraph_integer_t k = HASH_PAIR_KEY(a, b, size); if (h[2 * k] == HASH_NONE) { h[2 * k] = a; h[2 * k + 1] = b; @@ -253,7 +260,7 @@ inline bool H_pair_insert(int* h, const int size, int a, int b) { //_________________________________________________________________________ // Look for an element -inline bool H_is(int *mem, const int size, const int elem) { +inline bool H_is(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t elem) { if (IS_HASH(size)) { return (H_add(mem, HASH_EXPAND(size), elem) == NULL); } else { @@ -262,13 +269,13 @@ inline bool H_is(int *mem, const int size, const int elem) { } //pick random location (containing an element) -inline int* H_random(int* mem, int size) { +inline igraph_integer_t* H_random(igraph_integer_t* mem, igraph_integer_t size) { if (!IS_HASH(size)) { return mem + (my_random() % size); } _HASH_RAND_CALL(); size = HASH_EXPAND(size); - int* yo; + igraph_integer_t* yo; do { yo = mem + HASH_KEY(my_random(), size); _HASH_RAND_ITER(); @@ -277,7 +284,7 @@ inline int* H_random(int* mem, int size) { } // replace *k by b -inline int* H_rpl(int *mem, int size, int* k, const int b) { +inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t* k, igraph_integer_t b) { assert(!H_is(mem, size, b)); if (!IS_HASH(size)) { *k = b; @@ -291,7 +298,7 @@ inline int* H_rpl(int *mem, int size, int* k, const int b) { } // replace a by b -inline int* H_rpl(int *mem, int size, const int a, const int b) { +inline igraph_integer_t* H_rpl(igraph_integer_t *mem, igraph_integer_t size, igraph_integer_t a, igraph_integer_t b) { assert(H_is(mem, size, a)); assert(!H_is(mem, size, b)); if (!IS_HASH(size)) { diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h index 50e65d5226b..99e7e14e3ca 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_header.h @@ -27,7 +27,7 @@ namespace gengraph { static KW_RNG::RNG _my_random; -int my_random() { +long my_random() { return _my_random.rand_int31(); } void my_srandom(int x) { diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp index 0417c24ac3a..4147117d72d 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_mr-connected.cpp @@ -22,7 +22,6 @@ #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" @@ -30,11 +29,12 @@ #include "igraph_error.h" #include "core/exceptions.h" +#include "games/degree_sequence_vl/degree_sequence_vl.h" namespace gengraph { // return negative number if program should exit -int parse_options(int &argc, char** &argv); +// int parse_options(int &argc, char** &argv); // options // static const bool MONITOR_TIME = false; @@ -135,58 +135,54 @@ static const int SHUFFLE_TYPE = FINAL_HEURISTICS; using namespace gengraph; -extern "C" { +igraph_error_t igraph_degree_sequence_game_vl(igraph_t *graph, + const igraph_vector_int_t *out_seq, + const igraph_vector_int_t *in_seq) { + IGRAPH_HANDLE_EXCEPTIONS( + igraph_bool_t is_graphical; - 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_int_size(in_seq) != 0) { + IGRAPH_ERROR("The Viger-Latapy sampler support only undirected graphs.", IGRAPH_EINVAL); + } - 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); + } - 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(); - RNG_BEGIN(); + degree_sequence *dd = new degree_sequence(out_seq); - degree_sequence *dd = new degree_sequence(out_seq); + graph_molloy_opt *g = new graph_molloy_opt(*dd); + delete dd; - 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); - } + if (!g->havelhakimi()) { + delete g; + RNG_END(); + IGRAPH_FATAL("g->havelhakimi() failed; please report as a bug."); + } - int *hc = g->hard_copy(); + if (!g->make_connected()) { delete g; - graph_molloy_hash *gh = new graph_molloy_hash(hc); - delete [] hc; + RNG_END(); + IGRAPH_ERROR("Cannot make a connected graph from the given degree sequence.", + IGRAPH_EINVAL); + } - gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); + igraph_integer_t *hc = g->hard_copy(); + delete g; + graph_molloy_hash *gh = new graph_molloy_hash(hc); + delete [] hc; - IGRAPH_CHECK(gh->print(graph)); - delete gh; + gh->shuffle(5 * gh->nbarcs(), 100 * gh->nbarcs(), SHUFFLE_TYPE); - RNG_END(); - ); + IGRAPH_CHECK(gh->print(graph)); + delete gh; - return IGRAPH_SUCCESS; - } + RNG_END(); + ); + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp deleted file mode 100644 index 8ba0c54faa7..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* - * - * 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h deleted file mode 100644 index 57b6b7dd53e..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_powerlaw.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * - * 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h index 6af1f2c8c7c..85df8bc4464 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_qsort.h @@ -21,6 +21,8 @@ #ifndef QSORT_H #define QSORT_H +#include "igraph_types.h" + #include #include @@ -28,17 +30,19 @@ 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) { +inline bool check_zero(igraph_integer_t *mem, igraph_integer_t n) { + for (igraph_integer_t *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) { +inline igraph_integer_t med3(igraph_integer_t a, igraph_integer_t b, igraph_integer_t c) { if (a < b) { if (c < b) { return (a < c) ? c : a; @@ -54,13 +58,13 @@ inline int med3(int a, int b, int c) { } } -inline void isort(int *v, int t) { +inline void isort(igraph_integer_t *v, igraph_integer_t t) { if (t < 2) { return; } - for (int i = 1; i < t; i++) { - int *w = v + i; - int tmp = *w; + for (igraph_integer_t i = 1; i < t; i++) { + igraph_integer_t *w = v + i; + igraph_integer_t tmp = *w; while (w != v && *(w - 1) > tmp) { *w = *(w - 1); w--; @@ -69,9 +73,9 @@ inline void isort(int *v, int t) { } } -inline int partitionne(int *v, int t, int p) { - int i = 0; - int j = t - 1; +inline igraph_integer_t partitionne(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t p) { + igraph_integer_t i = 0; + igraph_integer_t j = t - 1; while (i < j) { while (i <= j && v[i] < p) { i++; @@ -80,7 +84,7 @@ inline int partitionne(int *v, int t, int p) { j--; } if (i < j) { - int tmp = v[i]; + igraph_integer_t tmp = v[i]; v[i++] = v[j]; v[j--] = tmp; } @@ -92,22 +96,22 @@ inline int partitionne(int *v, int t, int p) { return i; } -inline void qsort(int *v, int t) { +inline void qsort(igraph_integer_t *v, igraph_integer_t 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])); + igraph_integer_t 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) { +inline igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t, igraph_integer_t 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])); + igraph_integer_t 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 { @@ -115,7 +119,7 @@ inline int qsort_median(int *v, int t, int pos) { } } -inline int qsort_median(int *v, int t) { +inline igraph_integer_t qsort_median(igraph_integer_t *v, igraph_integer_t t) { return qsort_median(v, t, t / 2); } @@ -138,11 +142,11 @@ inline double med3(double a, double b, double c) { } } -inline void isort(double *v, int t) { +inline void isort(double *v, igraph_integer_t t) { if (t < 2) { return; } - for (int i = 1; i < t; i++) { + for (igraph_integer_t i = 1; i < t; i++) { double *w = v + i; double tmp = *w; while (w != v && *(w - 1) > tmp) { @@ -153,9 +157,9 @@ inline void isort(double *v, int t) { } } -inline int partitionne(double *v, int t, double p) { - int i = 0; - int j = t - 1; +inline igraph_integer_t partitionne(double *v, igraph_integer_t t, double p) { + igraph_integer_t i = 0; + igraph_integer_t j = t - 1; while (i < j) { while (i <= j && v[i] < p) { i++; @@ -176,22 +180,22 @@ inline int partitionne(double *v, int t, double p) { return i; } -inline void qsort(double *v, int t) { +inline void qsort(double *v, igraph_integer_t 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])); + igraph_integer_t 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) { +inline double qsort_median(double *v, igraph_integer_t t, igraph_integer_t 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])); + igraph_integer_t 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 { @@ -199,20 +203,20 @@ inline double qsort_median(double *v, int t, int pos) { } } -inline double qsort_median(double *v, int t) { +inline double qsort_median(double *v, igraph_integer_t 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) { +inline void isort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t t) { if (t < 2) { return; } - for (int i = 1; i < t; i++) { - int vtmp = v[i]; - int tmp = mem[vtmp]; - int j; + for (igraph_integer_t i = 1; i < t; i++) { + igraph_integer_t vtmp = v[i]; + igraph_integer_t tmp = mem[vtmp]; + igraph_integer_t j; for (j = i; j > 0 && tmp < mem[v[j - 1]]; j--) { v[j] = v[j - 1]; } @@ -220,13 +224,13 @@ inline void isort(int *mem, int *v, int t) { } } -inline void qsort(int *mem, int *v, int t) { +inline void qsort(igraph_integer_t *mem, igraph_integer_t *v, igraph_integer_t 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; + igraph_integer_t p = med3(mem[v[t >> 1]], mem[v[(t >> 2) + 3]], mem[v[t - (t >> 1) - 3]]); + igraph_integer_t i = 0; + igraph_integer_t j = t - 1; while (i < j) { while (i <= j && mem[v[i]] < p) { i++; @@ -235,7 +239,7 @@ inline void qsort(int *mem, int *v, int t) { j--; } if (i < j) { - int tmp = v[i]; + igraph_integer_t tmp = v[i]; v[i++] = v[j]; v[j--] = tmp; } @@ -250,13 +254,13 @@ inline void qsort(int *mem, int *v, int t) { } //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; +inline igraph_integer_t *pre_boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t &offset) { + igraph_integer_t *yo; // maximum and minimum - int mx = mem[0]; - int mn = mem[0]; + igraph_integer_t mx = mem[0]; + igraph_integer_t mn = mem[0]; for (yo = mem + n - 1; yo != mem; yo--) { - int x = *yo; + igraph_integer_t x = *yo; if (x > mx) { mx = x; } @@ -265,12 +269,12 @@ inline int *pre_boxsort(int *mem, int n, int &offset) { } } // box - int c = mx - mn + 1; - int *box = new int[c]; + igraph_integer_t c = mx - mn + 1; + igraph_integer_t *box = new igraph_integer_t[c]; for (yo = box + c; yo != box; * (--yo) = 0) { } for (yo = mem + n; yo != mem; box[*(--yo) - mn]++) { } // cumul sum - int sum = 0; + igraph_integer_t sum = 0; for (yo = box + c; yo != box; ) { sum += *(--yo); *yo = sum; @@ -279,16 +283,16 @@ inline int *pre_boxsort(int *mem, int n, int &offset) { return box; } -inline int *boxsort(int *mem, int n, int *buff = NULL) { - int i; +inline igraph_integer_t *boxsort(igraph_integer_t *mem, igraph_integer_t n, igraph_integer_t *buff = NULL) { + igraph_integer_t i; if (n <= 0) { return buff; } - int offset = 0; - int *box = pre_boxsort(mem, n, offset); + igraph_integer_t offset = 0; + igraph_integer_t *box = pre_boxsort(mem, n, offset); // sort if (buff == NULL) { - buff = new int[n]; + buff = new igraph_integer_t[n]; } for (i = 0; i < n; i++) { buff[--box[mem[i] - offset]] = i; @@ -298,267 +302,6 @@ inline int *boxsort(int *mem, int n, int *buff = NULL) { 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/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp index c4d08cb7a74..67fc0a78aaa 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.cpp @@ -28,11 +28,9 @@ // 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. diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h index 90d86b59b17..db77c3af538 100644 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h +++ b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_random.h @@ -44,7 +44,7 @@ class RNG { IGRAPH_UNUSED(jcong_); } long rand_int31() { - return RNG_INT31(); + return RNG_INTEGER(0, 0x7fffffff); } double rand_halfopen01() { // (0,1] return RNG_UNIF01(); diff --git a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h b/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h deleted file mode 100644 index 25b8e84e999..00000000000 --- a/src/vendor/cigraph/src/games/degree_sequence_vl/gengraph_vertex_cover.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * 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/vendor/cigraph/src/games/dotproduct.c b/src/vendor/cigraph/src/games/dotproduct.c index c4bf614fa5b..88587afa5c2 100644 --- a/src/vendor/cigraph/src/games/dotproduct.c +++ b/src/vendor/cigraph/src/games/dotproduct.c @@ -22,9 +22,10 @@ */ #include "igraph_games.h" -#include "igraph_random.h" -#include "igraph_constructors.h" + #include "igraph_blas.h" +#include "igraph_constructors.h" +#include "igraph_random.h" /** * \function igraph_dot_product_game @@ -56,21 +57,21 @@ * for functions to generate the latent vectors. */ -int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, +igraph_error_t 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_integer_t i, j; + igraph_vector_int_t edges; + igraph_bool_t warned_neg = false, warned_big = false; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); RNG_BEGIN(); for (i = 0; i < ncol; i++) { - int from = directed ? 0 : i + 1; + igraph_integer_t from = directed ? 0 : i + 1; igraph_vector_t v1; igraph_vector_view(&v1, &MATRIX(*vecs, 0, i), nrow); for (j = from; j < ncol; j++) { @@ -82,34 +83,33 @@ int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, 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"); + warned_neg = true; + 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)); + warned_big = true; + IGRAPH_WARNING("Greater than 1 connection probability in dot-product graph."); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } } } RNG_END(); - igraph_create(graph, &edges, ncol, directed); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, ncol, directed)); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sample_sphere_surface - * Sample points uniformly from the surface of a sphere + * \brief Sample points uniformly from the surface of a sphere. * * The center of the sphere is at the origin. * @@ -130,7 +130,7 @@ int igraph_dot_product_game(igraph_t *graph, const igraph_matrix_t *vecs, * igraph_sample_dirichlet() for other similar samplers. */ -int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, +igraph_error_t igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, igraph_real_t radius, igraph_bool_t positive, igraph_matrix_t *res) { @@ -138,13 +138,13 @@ int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, if (dim < 2) { IGRAPH_ERROR("Sphere must be at least two dimensional to sample from " - "surface", IGRAPH_EINVAL); + "surface.", IGRAPH_EINVAL); } if (n < 0) { - IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL); } if (radius <= 0) { - IGRAPH_ERROR("Sphere radius must be positive", IGRAPH_EINVAL); + IGRAPH_ERROR("Sphere radius must be positive.", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_matrix_resize(res, dim, n)); @@ -171,12 +171,12 @@ int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, RNG_END(); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sample_sphere_volume - * Sample points uniformly from the volume of a sphere + * \brief Sample points uniformly from the volume of a sphere. * * The center of the sphere is at the origin. * @@ -198,7 +198,7 @@ int igraph_sample_sphere_surface(igraph_integer_t dim, igraph_integer_t n, */ -int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, +igraph_error_t igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, igraph_real_t radius, igraph_bool_t positive, igraph_matrix_t *res) { @@ -221,12 +221,12 @@ int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, RNG_END(); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sample_dirichlet - * Sample points from a Dirichlet distribution + * \brief Sample points from a Dirichlet distribution. * * \param n The number of vectors to sample. * \param alpha The parameters of the Dirichlet distribution. They @@ -245,7 +245,7 @@ int igraph_sample_sphere_volume(igraph_integer_t dim, igraph_integer_t n, * latent vectors. */ -int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, +igraph_error_t igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, igraph_matrix_t *res) { igraph_integer_t len = igraph_vector_size(alpha); @@ -253,16 +253,17 @@ int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, igraph_vector_t vec; if (n < 0) { - IGRAPH_ERROR("Number of samples should be non-negative", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Number of samples should be non-negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, n); } if (len < 2) { - IGRAPH_ERROR("Dirichlet parameter vector too short, must " - "have at least two entries", IGRAPH_EINVAL); + IGRAPH_ERRORF("Dirichlet parameter vector too short, must " + "have at least two entries, got %" IGRAPH_PRId + ".", IGRAPH_EINVAL, len); } if (igraph_vector_min(alpha) <= 0) { - IGRAPH_ERROR("Dirichlet concentration parameters must be positive", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Dirichlet concentration parameters must be positive, got %g.", + IGRAPH_EINVAL, igraph_vector_min(alpha)); } IGRAPH_CHECK(igraph_matrix_resize(res, len, n)); @@ -276,5 +277,5 @@ int igraph_sample_dirichlet(igraph_integer_t n, const igraph_vector_t *alpha, RNG_END(); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/erdos_renyi.c b/src/vendor/cigraph/src/games/erdos_renyi.c index c0b1acf2588..d7a5b71be26 100644 --- a/src/vendor/cigraph/src/games/erdos_renyi.c +++ b/src/vendor/cigraph/src/games/erdos_renyi.c @@ -25,9 +25,11 @@ #include "igraph_constructors.h" #include "igraph_interface.h" -#include "igraph_nongraph.h" #include "igraph_random.h" +#include "random/random_internal.h" +#include "math/safe_intop.h" + /** * \section about_games * @@ -35,32 +37,63 @@ * they generate a different graph every time you call them. */ -int igraph_erdos_renyi_game_gnp( +/** + * \ingroup generators + * \function igraph_erdos_renyi_game_gnp + * \brief Generates a random (Erdős-Rényi) graph with fixed edge probabilities. + * + * In this model, a graph with n vertices is generated such that every possible + * edge is included in the graph with probability p. + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param p The probability of the existence of an edge in the graph. + * \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 n or \p p 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(), + * \ref igraph_erdos_renyi_game_gnm() + * + * \example examples/simple/igraph_erdos_renyi_game_gnp.c + */ +igraph_error_t 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; + /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. + * This is because on a system with 32-bit ints, maxedges will be larger than + * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` + * for tests on large graphs. + */ + igraph_integer_t no_of_nodes = n; + igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; igraph_vector_t s = IGRAPH_VECTOR_NULL; - int retval = 0; - long int vsize; + igraph_integer_t vsize; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } if (p < 0.0 || p > 1.0) { - IGRAPH_ERROR("Invalid probability given", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid probability given.", IGRAPH_EINVAL); } if (p == 0.0 || no_of_nodes == 0) { - IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); + IGRAPH_CHECK(igraph_empty(graph, n, directed)); } else if (p == 1.0) { - IGRAPH_CHECK(retval = igraph_full(graph, n, directed, loops)); + IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); } else { - long int i; - double maxedges = n, last; + igraph_integer_t i; + igraph_real_t maxedges = n, last; + igraph_integer_t maxedges_int; + if (directed && loops) { maxedges *= n; } else if (directed && !loops) { @@ -71,8 +104,12 @@ int igraph_erdos_renyi_game_gnp( maxedges *= (n - 1) / 2.0; } + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &maxedges_int)); + IGRAPH_CHECK(igraph_vector_reserve(&s, maxedges_int)); RNG_BEGIN(); @@ -85,77 +122,107 @@ int igraph_erdos_renyi_game_gnp( RNG_END(); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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); + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; if (from == to) { to = no_of_nodes - 1; } - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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); + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - return retval; + return IGRAPH_SUCCESS; } -int igraph_erdos_renyi_game_gnm( - igraph_t *graph, igraph_integer_t n, igraph_real_t m, +/** + * \ingroup generators + * \function igraph_erdos_renyi_game_gnm + * \brief Generates a random (Erdős-Rényi) graph with a fixed number of edges. + * + * In this model, a graph with n vertices and m edges is generated such that the + * edges are selected uniformly at random. + * + * \param graph Pointer to an uninitialized graph object. + * \param n The number of vertices in the graph. + * \param m The number of edges in the graph. + * \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 n 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(), + * \ref igraph_erdos_renyi_game_gnp() + * + * \example examples/simple/igraph_erdos_renyi_game_gnm.c + */ +igraph_error_t igraph_erdos_renyi_game_gnm( + igraph_t *graph, igraph_integer_t n, igraph_integer_t m, igraph_bool_t directed, igraph_bool_t loops ) { + /* This function uses doubles in its `s` vector, and for `maxedges` and `last`. + * This is because on a system with 32-bit ints, maxedges will be larger than + * IGRAPH_INTEGER_MAX and this will cause overflows when calculating `from` and `to` + * for tests on large graphs. This is also why we need a 'real' version of random_sample. + */ 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_integer_t no_of_edges = m; + igraph_real_t no_of_nodes_real = (igraph_real_t) no_of_nodes; /* for divisions below */ + igraph_vector_int_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); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } - if (m < 0) { - IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); + if (m < 0 || m > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); } if (m == 0.0 || no_of_nodes == 0) { - IGRAPH_CHECK(retval = igraph_empty(graph, n, directed)); + IGRAPH_CHECK(igraph_empty(graph, n, directed)); } else { - long int i; - double maxedges = n; + igraph_integer_t i; + igraph_real_t maxedges = n; if (directed && loops) { maxedges *= n; } else if (directed && !loops) { @@ -167,65 +234,64 @@ int igraph_erdos_renyi_game_gnm( } if (no_of_edges > maxedges) { - IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); } if (maxedges == no_of_edges) { - retval = igraph_full(graph, n, directed, loops); + IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); } else { - long int slen; + igraph_integer_t slen; IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_random_sample(&s, 0, maxedges - 1, - (igraph_integer_t) no_of_edges)); + IGRAPH_CHECK(igraph_random_sample_real(&s, 0, maxedges - 1, no_of_edges)); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&s) * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t to = floor(VECTOR(s)[i] / no_of_nodes_real); + igraph_integer_t from = VECTOR(s)[i] - to * no_of_nodes_real; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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)); + igraph_integer_t from = floor(VECTOR(s)[i] / (no_of_nodes_real - 1)); + igraph_integer_t to = VECTOR(s)[i] - from * (no_of_nodes_real - 1); if (from == to) { to = no_of_nodes - 1; } - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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); + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) - 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to + 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_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_integer_t to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2); + igraph_integer_t from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2; + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); } } igraph_vector_destroy(&s); IGRAPH_FINALLY_CLEAN(1); - retval = igraph_create(graph, &edges, n, directed); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } } - return retval; + return IGRAPH_SUCCESS; } /** @@ -233,6 +299,9 @@ int igraph_erdos_renyi_game_gnm( * \function igraph_erdos_renyi_game * \brief Generates a random (Erdős-Rényi) graph. * + * This function is deprecated; use \ref igraph_erdos_renyi_game_gnm() or + * \ref igraph_erdos_renyi_game_gnp() instead. + * * \param graph Pointer to an uninitialized graph object. * \param type The type of the random graph, possible values: * \clist @@ -264,22 +333,18 @@ int igraph_erdos_renyi_game_gnm( * 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 + * \sa \ref igraph_barabasi_game(), \ref igraph_growing_random_game(), + * \ref igraph_erdos_renyi_game_gnm(), \ref igraph_erdos_renyi_game_gnp() */ -int igraph_erdos_renyi_game(igraph_t *graph, igraph_erdos_renyi_t type, +igraph_error_t 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); + return 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); + return igraph_erdos_renyi_game_gnm(graph, n, (igraph_integer_t) p_or_m, directed, loops); } else { IGRAPH_ERROR("Invalid type", IGRAPH_EINVAL); } - - return retval; } diff --git a/src/vendor/cigraph/src/games/establishment.c b/src/vendor/cigraph/src/games/establishment.c index 552858c7909..9f8cf4ea8e7 100644 --- a/src/vendor/cigraph/src/games/establishment.c +++ b/src/vendor/cigraph/src/games/establishment.c @@ -56,21 +56,21 @@ * 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_error_t 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_int_t *node_type_vec) { + igraph_integer_t i, j; + igraph_vector_int_t edges; igraph_vector_t cumdist; - igraph_vector_t potneis; + igraph_vector_int_t potneis; igraph_real_t maxcum; - igraph_vector_t *nodetypes; + igraph_vector_int_t *nodetypes; /* Argument contracts */ - if(nodes < 0){ + if (nodes < 0) { IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } @@ -90,7 +90,7 @@ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (igraph_is_nan(lo)) { + if (isnan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -101,12 +101,12 @@ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); + igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ 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)) { + if (isnan(lo) || isnan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -115,9 +115,9 @@ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, IGRAPH_ERROR("The preference matrix must be symmetric when generating undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&cumdist, types + 1); - IGRAPH_VECTOR_INIT_FINALLY(&potneis, k); + IGRAPH_VECTOR_INT_INIT_FINALLY(&potneis, k); if (type_dist) { VECTOR(cumdist)[0] = 0; @@ -137,33 +137,33 @@ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, if (node_type_vec) { nodetypes = node_type_vec; - IGRAPH_CHECK(igraph_vector_resize(nodetypes, nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(nodetypes, nodes)); } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); if (! nodetypes) { - IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for establishment_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); } RNG_BEGIN(); for (i = 0; i < nodes; i++) { igraph_real_t uni = RNG_UNIF(0, maxcum); - long int type; + igraph_integer_t 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_integer_t type1 = 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]]; + igraph_integer_t type2 = VECTOR(*nodetypes)[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])); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, VECTOR(potneis)[j])); } } } @@ -171,15 +171,15 @@ int igraph_establishment_game(igraph_t *graph, igraph_integer_t nodes, RNG_END(); if (! node_type_vec) { - igraph_vector_destroy(nodetypes); + igraph_vector_int_destroy(nodetypes); IGRAPH_FREE(nodetypes); IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_destroy(&potneis); + igraph_vector_int_destroy(&potneis); igraph_vector_destroy(&cumdist); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/forestfire.c b/src/vendor/cigraph/src/games/forestfire.c index a4d69bfad11..bb9c12bee75 100644 --- a/src/vendor/cigraph/src/games/forestfire.c +++ b/src/vendor/cigraph/src/games/forestfire.c @@ -22,27 +22,28 @@ */ #include "igraph_games.h" + +#include "igraph_constructors.h" +#include "igraph_dqueue.h" +#include "igraph_interface.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_vector_int_t *inneis; + igraph_vector_int_t *outneis; + igraph_integer_t 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; + igraph_integer_t i; for (i = 0; i < data->no_of_nodes; i++) { - igraph_vector_destroy(data->inneis + i); - igraph_vector_destroy(data->outneis + i); + igraph_vector_int_destroy(data->inneis + i); + igraph_vector_int_destroy(data->outneis + i); } } @@ -78,12 +79,14 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * \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 @@ -95,7 +98,7 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * \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. + 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. @@ -103,17 +106,17 @@ static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { * Time complexity: TODO. */ -int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, +igraph_error_t 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_vector_int_t visited; + igraph_integer_t no_of_nodes = nodes, actnode, i; + igraph_vector_int_t edges; + igraph_vector_int_t *inneis, *outneis; igraph_i_forest_fire_data_t data; - igraph_dqueue_t neiq; - long int ambs = pambs; + igraph_dqueue_int_t neiq; + igraph_integer_t ambs = pambs; igraph_real_t param_geom_out = 1 - fw_prob; igraph_real_t param_geom_in = 1 - fw_prob * bw_factor; @@ -132,19 +135,19 @@ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, if (ambs == 0) { IGRAPH_CHECK(igraph_empty(graph, nodes, directed)); - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); + inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); if (!inneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, inneis); - outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_t); + outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); if (!outneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, outneis); data.inneis = inneis; @@ -152,24 +155,24 @@ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, 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_int_init(inneis + i, 0)); + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_init(&visited, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &visited); + IGRAPH_DQUEUE_INT_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)); \ + if (VECTOR(visited)[(nei)] != actnode+1) { \ + VECTOR(visited)[(nei)] = actnode+1; \ + IGRAPH_CHECK(igraph_dqueue_int_push(&neiq, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, actnode)); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_push_back(outneis+actnode, (nei))); \ + IGRAPH_CHECK(igraph_vector_int_push_back(inneis+(nei), actnode)); \ } IGRAPH_PROGRESS("Forest fire: ", 0.0, NULL); @@ -185,29 +188,29 @@ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, /* Choose ambassador(s) */ for (i = 0; i < ambs; i++) { - long int a = RNG_INTEGER(0, actnode - 1); + igraph_integer_t 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); + while (!igraph_dqueue_int_empty(&neiq)) { + igraph_integer_t actamb = igraph_dqueue_int_pop(&neiq); + igraph_vector_int_t *outv = outneis + actamb; + igraph_vector_int_t *inv = inneis + actamb; + igraph_integer_t no_in = igraph_vector_int_size(inv); + igraph_integer_t no_out = igraph_vector_int_size(outv); + igraph_integer_t neis_out = RNG_GEOM(param_geom_out); + igraph_integer_t neis_in = 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]; + igraph_integer_t nei = VECTOR(*outv)[i]; ADD_EDGE_TO(nei); } } else { - long int oleft = no_out; + igraph_integer_t 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]; + igraph_integer_t which = RNG_INTEGER(0, oleft - 1); + igraph_integer_t nei = VECTOR(*outv)[which]; VECTOR(*outv)[which] = VECTOR(*outv)[oleft - 1]; VECTOR(*outv)[oleft - 1] = nei; if (VECTOR(visited)[nei] != actnode + 1) { @@ -220,14 +223,14 @@ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, /* incoming neighbors */ if (neis_in >= no_in) { for (i = 0; i < no_in; i++) { - long int nei = (long int) VECTOR(*inv)[i]; + igraph_integer_t nei = VECTOR(*inv)[i]; ADD_EDGE_TO(nei); } } else { - long int ileft = no_in; + igraph_integer_t 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]; + igraph_integer_t which = RNG_INTEGER(0, ileft - 1); + igraph_integer_t nei = VECTOR(*inv)[which]; VECTOR(*inv)[which] = VECTOR(*inv)[ileft - 1]; VECTOR(*inv)[ileft - 1] = nei; if (VECTOR(visited)[nei] != actnode + 1) { @@ -248,15 +251,15 @@ int igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, IGRAPH_PROGRESS("Forest fire: ", 100.0, NULL); - igraph_dqueue_destroy(&neiq); - igraph_vector_long_destroy(&visited); + igraph_dqueue_int_destroy(&neiq); + igraph_vector_int_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_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/grg.c b/src/vendor/cigraph/src/games/grg.c index 922a9b254ca..dbd6348a566 100644 --- a/src/vendor/cigraph/src/games/grg.c +++ b/src/vendor/cigraph/src/games/grg.c @@ -55,18 +55,22 @@ * * \example examples/simple/igraph_grg_game.c */ -int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, +igraph_error_t 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_integer_t i; + igraph_vector_t myx, myy, *xx = &myx, *yy = &myy; + igraph_vector_int_t edges; igraph_real_t r2; if (nodes < 0) { IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nodes)); + /* since we only connect nodes strictly closer than radius, * radius < 0 is equivalent to radius == 0 */ if (radius < 0) { @@ -74,9 +78,6 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, } 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)); @@ -105,7 +106,7 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, 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_integer_t j = i + 1; igraph_real_t dx, dy; IGRAPH_ALLOW_INTERRUPTION(); @@ -114,8 +115,8 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } j++; } @@ -124,7 +125,7 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, 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_integer_t j = i + 1; igraph_real_t dx, dy; IGRAPH_ALLOW_INTERRUPTION(); @@ -139,8 +140,8 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } j++; } @@ -153,8 +154,8 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } j++; } @@ -172,7 +173,7 @@ int igraph_grg_game(igraph_t *graph, igraph_integer_t nodes, } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, IGRAPH_UNDIRECTED)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/growing_random.c b/src/vendor/cigraph/src/games/growing_random.c index 68fdefc72d2..cdbfe7fff68 100644 --- a/src/vendor/cigraph/src/games/growing_random.c +++ b/src/vendor/cigraph/src/games/growing_random.c @@ -26,22 +26,24 @@ #include "igraph_constructors.h" #include "igraph_random.h" +#include "math/safe_intop.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 + * \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: @@ -52,41 +54,49 @@ * 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_error_t 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; + igraph_integer_t no_of_nodes = n; + igraph_integer_t no_of_neighbors = m; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - long int resp = 0; + igraph_integer_t resp = 0; - long int i, j; + igraph_integer_t i, j; if (n < 0) { - IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } if (m < 0) { - IGRAPH_ERROR("Invalid number of edges per step (m)", IGRAPH_EINVAL); + 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; + if (no_of_nodes == 0) { + no_of_edges = 0; + } else { + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Number of edges overflows.", IGRAPH_EOVERFLOW); + } + } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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); + igraph_integer_t 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); + igraph_integer_t from = RNG_INTEGER(0, i); + igraph_integer_t to = RNG_INTEGER(1, i); VECTOR(edges)[resp++] = from; VECTOR(edges)[resp++] = to; } @@ -96,8 +106,8 @@ int igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, RNG_END(); IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/islands.c b/src/vendor/cigraph/src/games/islands.c index e9f5ace1466..edececa2be1 100644 --- a/src/vendor/cigraph/src/games/islands.c +++ b/src/vendor/cigraph/src/games/islands.c @@ -26,6 +26,9 @@ #include "igraph_constructors.h" #include "igraph_random.h" +#include "math/safe_intop.h" +#include "random/random_internal.h" + /** * \ingroup generators * \function igraph_simple_interconnected_islands_game @@ -33,15 +36,16 @@ * * All islands are of the same size. Within an island, each edge is generated * with the same probability. A fixed number of additional edges are then - * generated for each unordered pair of islands to connect them. Note that - * the islands themselves will \em not contain multiple edges, but there might - * be multiple edges between the same pair of vertices between islands. + * generated for each unordered pair of islands to connect them. The generated + * graph is guaranteed to be simple. * * \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 within islands. - * \param n_inter The number of edges to create between two islands. + * \param n_inter The number of edges to create between two islands. It may be + * larger than \p islands_size squared, but in this case it is assumed + * to be \p islands_size squared. * * \return Error code: * \c IGRAPH_EINVAL: invalid parameter @@ -51,26 +55,24 @@ * number of vertices plus the number of edges in the graph. * */ -int igraph_simple_interconnected_islands_game( +igraph_error_t 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_int_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; + igraph_integer_t number_of_nodes; + igraph_real_t max_possible_edges_per_island; + igraph_real_t avg_edges_per_island; + igraph_integer_t number_of_inter_island_edges; + igraph_integer_t start_index_of_island, start_index_of_other_island; + igraph_integer_t i, j, is, from, to; + igraph_real_t last; + igraph_integer_t island_ecount; + igraph_real_t nr_edges_reserved; if (islands_n < 0) { IGRAPH_ERRORF("Number of islands cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, islands_n); @@ -85,78 +87,89 @@ int igraph_simple_interconnected_islands_game( IGRAPH_ERRORF("Number of inter-island links cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n_inter); } + number_of_inter_island_edges = islands_size * islands_size; + if (n_inter > number_of_inter_island_edges) { + IGRAPH_ERRORF( + "Too many edges requested between islands, maximum possible " + "is %" IGRAPH_PRId ", got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, number_of_inter_island_edges, 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; + number_of_nodes = islands_n * islands_size; + max_possible_edges_per_island = ((igraph_real_t)islands_size * ((igraph_real_t)islands_size - 1.0)) / 2.0; + avg_edges_per_island = islands_pin * max_possible_edges_per_island; + number_of_inter_island_edges = n_inter * (islands_n * (islands_n - 1)) / 2; + + nr_edges_reserved = 1.1 * avg_edges_per_island * islands_n + number_of_inter_island_edges; + /* The cast of ECOUNT_MAX to double could change its value, which means in theory the size of + the edges vector could still overflow, but only for very rare cases. */ + if (nr_edges_reserved > (double) (IGRAPH_ECOUNT_MAX ) || nr_edges_reserved > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, nr_edges_reserved * 2)); - /* reserve enough space for all the edges */ - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, (long int) maxedges)); + IGRAPH_VECTOR_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_vector_reserve(&s, 1.1 * avg_edges_per_island)); 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, both inclusive */ + start_index_of_island = islands_size * is; - /* 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)); + igraph_vector_clear(&s); last = RNG_GEOM(islands_pin); - while (last < maxpossibleedgesPerIsland) { /* maxedgesPerIsland */ + while (last < max_possible_edges_per_island) { /* avg_edges_per_island */ IGRAPH_CHECK(igraph_vector_push_back(&s, last)); - myrand = RNG_GEOM(islands_pin); - last += myrand; /* RNG_GEOM(islands_pin); */ + last += RNG_GEOM(islands_pin); last += 1; } + island_ecount = igraph_vector_size(&s); + for (i = 0; i < island_ecount; i++) { + to = floor((sqrt(8 * VECTOR(s)[i] + 1) + 1) / 2.0); + from = VECTOR(s)[i] - (((igraph_real_t)to) * (to - 1)) / 2.0; + to += start_index_of_island; + from += start_index_of_island; - - /* 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); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_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 */ + island_ecount = islands_size * islands_size; + number_of_inter_island_edges = n_inter; for (i = is + 1; i < islands_n; i++) { /* for each other island (not the previous ones) */ + IGRAPH_CHECK(igraph_random_sample_real(&s, 0, island_ecount - 1, n_inter)); + start_index_of_other_island = i * islands_size; 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); + from = VECTOR(s)[j] / islands_size; + to = VECTOR(s)[j] - from * islands_size; + from += start_index_of_island; + to += start_index_of_other_island; - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); } - } } + igraph_vector_destroy(&s); + IGRAPH_FINALLY_CLEAN(1); + RNG_END(); /* actually fill the graph object */ - IGRAPH_CHECK(igraph_create(graph, &edges, nbNodes, 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, number_of_nodes, 0)); /* clean remaining things */ - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/k_regular.c b/src/vendor/cigraph/src/games/k_regular.c index ff989ecbcfa..0cebc213ad2 100644 --- a/src/vendor/cigraph/src/games/k_regular.c +++ b/src/vendor/cigraph/src/games/k_regular.c @@ -23,8 +23,6 @@ #include "igraph_games.h" -#include "igraph_interface.h" - /** * \ingroup generators * \function igraph_k_regular_game @@ -36,10 +34,11 @@ * * * 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. + * the \c IGRAPH_DEGSEQ_CONFIGURATION or the \c IGRAPH_DEGSEQ_FAST_SIMPLE + * 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. @@ -57,28 +56,29 @@ * * Time complexity: O(|V|+|E|) if \c multiple is true, otherwise not known. */ -int igraph_k_regular_game(igraph_t *graph, +igraph_error_t 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; + igraph_vector_int_t degseq; + igraph_degseq_t mode = multiple ? IGRAPH_DEGSEQ_CONFIGURATION : IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE; /* 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. */ + * because the VL method is not really good at generating k-regular graphs, + * and it produces only connected graphs. + * Actually, that's why we have added FAST_HEUR_SIMPLE. */ if (no_of_nodes < 0) { - IGRAPH_ERROR("number of nodes must be non-negative", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of nodes must be non-negative.", IGRAPH_EINVAL); } if (k < 0) { - IGRAPH_ERROR("degree must be non-negative", IGRAPH_EINVAL); + IGRAPH_ERROR("Degree must be non-negative.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(°seq, no_of_nodes); - igraph_vector_fill(°seq, k); + IGRAPH_VECTOR_INT_INIT_FINALLY(°seq, no_of_nodes); + igraph_vector_int_fill(°seq, k); IGRAPH_CHECK(igraph_degree_sequence_game(graph, °seq, directed ? °seq : 0, mode)); - igraph_vector_destroy(°seq); + igraph_vector_int_destroy(°seq); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/preference.c b/src/vendor/cigraph/src/games/preference.c index 2148550a4fc..1af55299c9f 100644 --- a/src/vendor/cigraph/src/games/preference.c +++ b/src/vendor/cigraph/src/games/preference.c @@ -26,22 +26,12 @@ #include "igraph_constructors.h" #include "igraph_memory.h" #include "igraph_random.h" +#include "igraph_vector_list.h" #include "core/interruption.h" +#include "math/safe_intop.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); -} +#include /* for sqrt and floor */ /** * \function igraph_preference_game @@ -92,22 +82,23 @@ static void igraph_i_preference_game_free_vids_by_type(igraph_vector_ptr_t *vecs * \ref igraph_establishment_game(), \ref igraph_callaway_traits_game() */ -int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, +igraph_error_t 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_vector_int_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_integer_t i, j, no_reserved_edges; + igraph_vector_int_t edges; + igraph_vector_t s; + igraph_vector_int_t* nodetypes; + igraph_vector_int_list_t vids_by_type; igraph_real_t maxcum, maxedges; - if(nodes < 0){ + if (nodes < 0) { IGRAPH_ERROR("The number of vertices must be non-negative.", IGRAPH_EINVAL); } @@ -127,7 +118,7 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, if (lo < 0) { IGRAPH_ERROR("The vertex type distribution vector must not contain negative values.", IGRAPH_EINVAL); } - if (igraph_is_nan(lo)) { + if (isnan(lo)) { IGRAPH_ERROR("The vertex type distribution vector must not contain NaN.", IGRAPH_EINVAL); } } @@ -138,12 +129,12 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); + igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ 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)) { + if (isnan(lo) || isnan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -159,28 +150,18 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, } if (node_type_vec) { - IGRAPH_CHECK(igraph_vector_resize(node_type_vec, nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(node_type_vec, nodes)); nodetypes = node_type_vec; } else { - nodetypes = IGRAPH_CALLOC(1, igraph_vector_t); + nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); if (nodetypes == 0) { - IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, nodetypes); - IGRAPH_VECTOR_INIT_FINALLY(nodetypes, nodes); + IGRAPH_VECTOR_INT_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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_type, types); RNG_BEGIN(); @@ -202,65 +183,66 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, maxcum = igraph_vector_tail(&cumdist); for (i = 0; i < nodes; i++) { - long int type1; + igraph_integer_t 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_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_type, type1 - 1), i + )); } igraph_vector_destroy(&cumdist); IGRAPH_FINALLY_CLEAN(1); } else { - long int an = 0; + igraph_integer_t 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]; + igraph_integer_t no = VECTOR(*type_dist)[i]; + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, i); for (j = 0; j < no && an < nodes; j++) { VECTOR(*nodetypes)[an] = i; - IGRAPH_CHECK(igraph_vector_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); an++; } } } else { - igraph_integer_t size_of_one_group = (igraph_integer_t) floor( (double)nodes / types); + igraph_integer_t size_of_one_group = nodes / types; igraph_integer_t num_groups_with_one_extra_node = nodes - size_of_one_group * types; for (i = 0; i < types; i++) { - igraph_vector_t *v = VECTOR(vids_by_type)[i]; + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&vids_by_type, i); for (j = 0; j < size_of_one_group; j++) { VECTOR(*nodetypes)[an] = i; - IGRAPH_CHECK(igraph_vector_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); an++; } if (i < num_groups_with_one_extra_node) { VECTOR(*nodetypes)[an] = i; - IGRAPH_CHECK(igraph_vector_push_back(v, an)); + IGRAPH_CHECK(igraph_vector_int_push_back(v, an)); an++; } } } } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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_integer_t k, l, l_x2; igraph_real_t p, last; - igraph_vector_t *v1, *v2; - long int v1_size, v2_size; + igraph_vector_int_t *v1, *v2; + igraph_integer_t 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); + v1 = igraph_vector_int_list_get_ptr(&vids_by_type, i); + v2 = igraph_vector_int_list_get_ptr(&vids_by_type, j); + v1_size = igraph_vector_int_size(v1); + v2_size = igraph_vector_int_size(v2); p = MATRIX(*pref_matrix, i, j); igraph_vector_clear(&s); @@ -269,20 +251,25 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, if (i > j && !directed) { continue; } - maxedges = v1_size * v2_size; + maxedges = ((igraph_real_t) v1_size) * v2_size; } else { if (directed && loops) { - maxedges = v1_size * v1_size; + maxedges = ((igraph_real_t) v1_size) * v1_size; } else if (directed && !loops) { - maxedges = v1_size * (v1_size - 1); + maxedges = ((igraph_real_t) v1_size) * (v1_size - 1); } else if (!directed && loops) { - maxedges = v1_size * (v1_size + 1) / 2; + maxedges = ((igraph_real_t) v1_size) * (v1_size + 1) / 2; } else { - maxedges = v1_size * (v1_size - 1) / 2; + maxedges = ((igraph_real_t) v1_size) * (v1_size - 1) / 2; } } - IGRAPH_CHECK(igraph_vector_reserve(&s, (long int) (maxedges * p * 1.1))); + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); last = RNG_GEOM(p); while (last < maxedges) { @@ -292,48 +279,50 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, } l = igraph_vector_size(&s); - IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2)); + IGRAPH_SAFE_MULT(l, 2, &l_x2); + IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); 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]); + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_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]); + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (VECTOR(s)[k] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_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); + igraph_integer_t to = floor(VECTOR(s)[k] / v1_size); + igraph_integer_t from = (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]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_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]); + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) - 1) / 2); + igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to + 1)) / 2); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_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]); + igraph_integer_t to = floor((sqrt(8 * VECTOR(s)[k] + 1) + 1) / 2); + igraph_integer_t from = (VECTOR(s)[k] - (((igraph_real_t)to) * (to - 1)) / 2); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[to]); } } } @@ -343,17 +332,17 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, RNG_END(); igraph_vector_destroy(&s); - igraph_i_preference_game_free_vids_by_type(&vids_by_type); + igraph_vector_int_list_destroy(&vids_by_type); IGRAPH_FINALLY_CLEAN(2); if (node_type_vec == 0) { - igraph_vector_destroy(nodetypes); + igraph_vector_int_destroy(nodetypes); IGRAPH_FREE(nodetypes); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -397,23 +386,26 @@ int igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, * \sa \ref igraph_preference_game() */ -int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, +igraph_error_t igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t no_out_types, igraph_integer_t no_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_vector_int_t *node_type_out_vec, + igraph_vector_int_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_integer_t i, j, k, no_reserved_edges; + igraph_vector_int_t edges; + igraph_vector_t s; + igraph_vector_t cumdist; + igraph_vector_int_t intersect; + igraph_vector_int_t *nodetypes_in; + igraph_vector_int_t *nodetypes_out; + igraph_vector_int_list_t vids_by_intype, vids_by_outtype; igraph_real_t maxcum, maxedges; - if(nodes < 0){ + if (nodes < 0) { IGRAPH_ERROR("The number of vertices must not be negative.", IGRAPH_EINVAL); } @@ -437,7 +429,7 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, if (lo < 0) { IGRAPH_ERROR("The type distribution matrix must not contain negative values.", IGRAPH_EINVAL); } - if (igraph_is_nan(lo)) { + if (isnan(lo)) { IGRAPH_ERROR("The type distribution matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -449,12 +441,12 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, { igraph_real_t lo, hi; - igraph_matrix_minmax(pref_matrix, &lo, &hi); + igraph_matrix_minmax(pref_matrix, &lo, &hi); /* matrix size is at least 1x1, safe to call minmax */ 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)) { + if (isnan(lo) || isnan(hi)) { IGRAPH_ERROR("The preference matrix must not contain NaN.", IGRAPH_EINVAL); } } @@ -463,47 +455,26 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, if (node_type_in_vec) { nodetypes_in = node_type_in_vec; - IGRAPH_CHECK(igraph_vector_resize(nodetypes_in, nodes)); + IGRAPH_CHECK(igraph_vector_int_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); + nodetypes_in = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes_in, "Insufficient memory for asymmetric preference game."); + IGRAPH_FINALLY(igraph_free, &nodetypes_in); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_in, nodes); } if (node_type_out_vec) { nodetypes_out = node_type_out_vec; - IGRAPH_CHECK(igraph_vector_resize(nodetypes_out, nodes)); + IGRAPH_CHECK(igraph_vector_int_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); + nodetypes_out = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(nodetypes_out, "Insufficient memory for asymmetric preference game."); + IGRAPH_FINALLY(igraph_free, &nodetypes_out); + IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes_out, nodes); } - IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_intype, no_in_types)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_intype); - IGRAPH_CHECK(igraph_vector_ptr_init(&vids_by_outtype, no_out_types)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &vids_by_outtype); - for (i = 0; i < no_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 < no_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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_intype, no_in_types); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vids_by_outtype, no_out_types); VECTOR(cumdist)[0] = 0; k = 0; @@ -524,49 +495,59 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, RNG_BEGIN(); for (i = 0; i < nodes; i++) { - long int in_type, out_type; + igraph_integer_t in_type, out_type; igraph_real_t uni1 = RNG_UNIF(0, maxcum); igraph_vector_binsearch(&cumdist, uni1, &in_type); - out_type = (in_type - 1) % (long int) no_out_types; - in_type = (in_type - 1) / (long int) no_out_types; + out_type = (in_type - 1) % no_out_types; + in_type = (in_type - 1) / no_out_types; VECTOR(*nodetypes_in)[i] = in_type; VECTOR(*nodetypes_out)[i] = out_type; - IGRAPH_CHECK(igraph_vector_push_back( - (igraph_vector_t*)VECTOR(vids_by_intype)[in_type], i)); - IGRAPH_CHECK(igraph_vector_push_back( - (igraph_vector_t*)VECTOR(vids_by_outtype)[out_type], i)); + IGRAPH_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_intype, in_type), i + )); + IGRAPH_CHECK(igraph_vector_int_push_back( + igraph_vector_int_list_get_ptr(&vids_by_outtype, out_type), i + )); } igraph_vector_destroy(&cumdist); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_VECTOR_INIT_FINALLY(&intersect, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&intersect, 0); for (i = 0; i < no_out_types; i++) { for (j = 0; j < no_in_types; j++) { - long int kk, l, c = 0; + igraph_integer_t kk, l, l_x2; + igraph_integer_t c = 0; igraph_real_t p, last; - igraph_vector_t *v1, *v2; - long int v1_size, v2_size; + igraph_vector_int_t *v1, *v2; + igraph_integer_t 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); + v1 = igraph_vector_int_list_get_ptr(&vids_by_outtype, i); + v2 = igraph_vector_int_list_get_ptr(&vids_by_intype, j); + v1_size = igraph_vector_int_size(v1); + v2_size = igraph_vector_int_size(v2); + + maxedges = ((igraph_real_t) v1_size) * v2_size; + + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } - maxedges = v1_size * v2_size; if (!loops) { - IGRAPH_CHECK(igraph_vector_intersect_sorted(v1, v2, &intersect)); - c = igraph_vector_size(&intersect); + IGRAPH_CHECK(igraph_vector_int_intersect_sorted(v1, v2, &intersect)); + c = igraph_vector_int_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))); + + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &no_reserved_edges)); + IGRAPH_CHECK(igraph_vector_reserve(&s, no_reserved_edges)); last = RNG_GEOM(p); while (last < maxedges) { @@ -576,16 +557,19 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, } l = igraph_vector_size(&s); - IGRAPH_CHECK(igraph_vector_reserve(&edges, igraph_vector_size(&edges) + l * 2)); + IGRAPH_SAFE_MULT(l, 2, &l_x2); + IGRAPH_SAFE_ADD(igraph_vector_int_size(&edges), l_x2, &no_reserved_edges); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_reserved_edges)); + 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); + igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); + igraph_integer_t from = (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); + igraph_vector_int_binsearch(&intersect, VECTOR(*v1)[from], &c); from = v1_size - 1; if (VECTOR(*v1)[from] == VECTOR(*v2)[to]) { from--; @@ -597,15 +581,15 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, } } } - igraph_vector_push_back(&edges, VECTOR(*v1)[from]); - igraph_vector_push_back(&edges, VECTOR(*v2)[to]); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_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]); + igraph_integer_t to = floor(VECTOR(s)[kk] / v1_size); + igraph_integer_t from = (VECTOR(s)[kk] - ((igraph_real_t)to) * v1_size); + igraph_vector_int_push_back(&edges, VECTOR(*v1)[from]); + igraph_vector_int_push_back(&edges, VECTOR(*v2)[to]); } } } @@ -614,25 +598,25 @@ int igraph_asymmetric_preference_game(igraph_t *graph, igraph_integer_t nodes, 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_vector_int_destroy(&intersect); + igraph_vector_int_list_destroy(&vids_by_intype); + igraph_vector_int_list_destroy(&vids_by_outtype); IGRAPH_FINALLY_CLEAN(4); if (node_type_out_vec == 0) { - igraph_vector_destroy(nodetypes_out); + igraph_vector_int_destroy(nodetypes_out); IGRAPH_FREE(nodetypes_out); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY_CLEAN(2); } if (node_type_in_vec == 0) { - igraph_vector_destroy(nodetypes_in); + igraph_vector_int_destroy(nodetypes_in); IGRAPH_FREE(nodetypes_in); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY_CLEAN(2); } IGRAPH_CHECK(igraph_create(graph, &edges, nodes, 1)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/games/recent_degree.c b/src/vendor/cigraph/src/games/recent_degree.c index efd896c796e..5564fcaaf0f 100644 --- a/src/vendor/cigraph/src/games/recent_degree.c +++ b/src/vendor/cigraph/src/games/recent_degree.c @@ -29,6 +29,8 @@ #include "igraph_random.h" #include "igraph_interface.h" +#include "math/safe_intop.h" + /** * \function igraph_recent_degree_game * \brief Stochastic graph generator based on the number of incident edges a node has gained recently. @@ -61,32 +63,32 @@ * vertices, |E| is the number of edges in the graph. * */ -int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, +igraph_error_t 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, + const igraph_vector_int_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_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors = 0; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j; igraph_psumtree_t sumtree; - long int edgeptr = 0; + igraph_integer_t edgeptr = 0; igraph_vector_t degree; - igraph_dqueue_t history; - igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + igraph_dqueue_int_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_int_size(outseq) > 0; if (no_of_nodes < 0) { - IGRAPH_ERRORF("Number of vertices cannot be negative, got %ld.", IGRAPH_EINVAL, no_of_nodes); + IGRAPH_ERRORF("Number of vertices cannot be negative, got %" IGRAPH_PRId ".", 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 && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(outseq), no_of_nodes); } if (!have_outseq && m < 0) { IGRAPH_ERRORF("Numer of edges per step cannot be negative, got %" IGRAPH_PRId ".", @@ -106,39 +108,40 @@ int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, if (!have_outseq) { no_of_neighbors = m; - no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); } 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_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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, + IGRAPH_CHECK(igraph_dqueue_int_init(&history, 1.5 * time_window * no_of_edges / no_of_nodes + 10)); - IGRAPH_FINALLY(igraph_dqueue_destroy, &history); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); RNG_BEGIN(); /* first node */ IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); - igraph_dqueue_push(&history, -1); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); /* and the rest */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - long int to; + igraph_integer_t to; if (have_outseq) { - no_of_neighbors = (long int) VECTOR(*outseq)[i]; + no_of_neighbors = VECTOR(*outseq)[i]; } if (i >= time_window) { - while ((j = (long int) igraph_dqueue_pop(&history)) != -1) { + while ((j = igraph_dqueue_int_pop(&history)) != -1) { VECTOR(degree)[j] -= 1; IGRAPH_CHECK(igraph_psumtree_update(&sumtree, j, pow(VECTOR(degree)[j], power) + zero_appeal)); } @@ -156,13 +159,13 @@ int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, VECTOR(degree)[to]++; VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = to; - igraph_dqueue_push(&history, to); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); } - igraph_dqueue_push(&history, -1); + IGRAPH_CHECK(igraph_dqueue_int_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_integer_t nn = VECTOR(edges)[edgeptr - 2 * j - 1]; IGRAPH_CHECK(igraph_psumtree_update(&sumtree, nn, pow(VECTOR(degree)[nn], power) + zero_appeal)); } if (outpref) { @@ -175,16 +178,16 @@ int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, RNG_END(); - igraph_dqueue_destroy(&history); + igraph_dqueue_int_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_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -227,10 +230,10 @@ int igraph_recent_degree_game(igraph_t *graph, igraph_integer_t nodes, * 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_error_t igraph_recent_degree_aging_game(igraph_t *graph, igraph_integer_t nodes, igraph_integer_t m, - const igraph_vector_t *outseq, + const igraph_vector_int_t *outseq, igraph_bool_t outpref, igraph_real_t pa_exp, igraph_real_t aging_exp, @@ -239,28 +242,28 @@ int igraph_recent_degree_aging_game(igraph_t *graph, 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_integer_t no_of_nodes = nodes; + igraph_integer_t no_of_neighbors; + igraph_integer_t binwidth; + igraph_integer_t no_of_edges; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; igraph_psumtree_t sumtree; - long int edgeptr = 0; + igraph_integer_t edgeptr = 0; igraph_vector_t degree; - igraph_dqueue_t history; - igraph_bool_t have_outseq = outseq && igraph_vector_size(outseq) > 0; + igraph_dqueue_int_t history; + igraph_bool_t have_outseq = outseq && igraph_vector_int_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); + IGRAPH_ERRORF("Number of nodes should not be negative, got %" IGRAPH_PRId ".", 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 && igraph_vector_int_size(outseq) != no_of_nodes) { + IGRAPH_ERRORF("Out-degree sequence is specified, but its length (%" IGRAPH_PRId ") does not equal the number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_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); @@ -277,43 +280,44 @@ int igraph_recent_degree_aging_game(igraph_t *graph, if (!have_outseq) { no_of_neighbors = m; - no_of_edges = (no_of_nodes - 1) * no_of_neighbors; + IGRAPH_SAFE_MULT(no_of_nodes - 1, no_of_neighbors, &no_of_edges); } 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_CHECK(igraph_i_safe_vector_int_sum(outseq, &no_of_edges)); + no_of_edges -= VECTOR(*outseq)[0]; + } + /* To ensure the size of the edges vector will not overflow. */ + if (no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); } binwidth = nodes / aging_bins + 1; - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_dqueue_int_init(&history, + 1.5 * time_window * no_of_edges / no_of_nodes + 10)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &history); RNG_BEGIN(); /* first node */ IGRAPH_CHECK(igraph_psumtree_update(&sumtree, 0, zero_appeal)); - igraph_dqueue_push(&history, -1); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, -1)); /* and the rest */ for (i = 1; i < no_of_nodes; i++) { igraph_real_t sum; - long int to; + igraph_integer_t to; if (have_outseq) { - no_of_neighbors = (long int) VECTOR(*outseq)[i]; + no_of_neighbors = VECTOR(*outseq)[i]; } if (i >= time_window) { - while ((j = (long int) igraph_dqueue_pop(&history)) != -1) { - long int age = (i - j) / binwidth; + while ((j = igraph_dqueue_int_pop(&history)) != -1) { + igraph_integer_t age = (i - j) / binwidth; VECTOR(degree)[j] -= 1; IGRAPH_CHECK(igraph_psumtree_update( &sumtree, j, @@ -334,14 +338,14 @@ int igraph_recent_degree_aging_game(igraph_t *graph, VECTOR(degree)[to]++; VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = to; - igraph_dqueue_push(&history, to); + IGRAPH_CHECK(igraph_dqueue_int_push(&history, to)); } - igraph_dqueue_push(&history, -1); + IGRAPH_CHECK(igraph_dqueue_int_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_integer_t n = VECTOR(edges)[edgeptr - 2 * j - 1]; + igraph_integer_t age = (i - n) / binwidth; IGRAPH_CHECK(igraph_psumtree_update( &sumtree, n, (pow(VECTOR(degree)[n], pa_exp) + zero_appeal) * pow(age + 1, aging_exp) @@ -359,9 +363,9 @@ int igraph_recent_degree_aging_game(igraph_t *graph, /* 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_integer_t shnode = i - binwidth * k; + igraph_integer_t deg = VECTOR(degree)[shnode]; + igraph_integer_t age = (i - shnode) / binwidth; IGRAPH_CHECK(igraph_psumtree_update( &sumtree, shnode, (pow(deg, pa_exp) + zero_appeal) * pow(age + 2, aging_exp) @@ -371,14 +375,14 @@ int igraph_recent_degree_aging_game(igraph_t *graph, RNG_END(); - igraph_dqueue_destroy(&history); + igraph_dqueue_int_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_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/sbm.c b/src/vendor/cigraph/src/games/sbm.c index b2d75e88b4a..d8c7a4bee62 100644 --- a/src/vendor/cigraph/src/games/sbm.c +++ b/src/vendor/cigraph/src/games/sbm.c @@ -22,17 +22,18 @@ */ -#include "igraph_interface.h" -#include "igraph_vector.h" +#include "igraph_games.h" + +#include "igraph_constructors.h" #include "igraph_matrix.h" #include "igraph_random.h" -#include "igraph_constructors.h" -#include "igraph_games.h" +#include "igraph_vector.h" #include "core/interruption.h" +#include "math/safe_intop.h" #include /* for DBL_EPSILON */ -#include /* for sqrt */ +#include /* for sqrt and floor */ /** * \function igraph_sbm_game @@ -46,7 +47,7 @@ * Interpretation and evaluation. Social Networks, 14, 5-–61. * * - * The order of the vertex ids in the generated graph corresponds to + * 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 @@ -67,19 +68,24 @@ * 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. + * \sa \ref igraph_erdos_renyi_game_gnp() for a simple Bernoulli graph. * */ -int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, +igraph_error_t 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; +#define IGRAPH_CHECK_MAXEDGES() \ + do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ + }} while (0) + + igraph_integer_t no_blocks = igraph_matrix_nrow(pref_matrix); + igraph_integer_t from, to, fromoff = 0; igraph_real_t minp, maxp; - igraph_vector_t edges; + igraph_vector_int_t edges; /* ------------------------------------------------------------ */ /* Check arguments */ @@ -103,8 +109,8 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, } 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_ERRORF("Block size vector length (%" IGRAPH_PRId ") does not agree with " + "preference matrix size (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_int_size(block_sizes), no_blocks); } @@ -125,14 +131,14 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * guaranteed to be non-negative. This shouldn't be checked separately. */ - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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_integer_t fromsize = VECTOR(*block_sizes)[from]; + igraph_integer_t start = directed ? 0 : from; + igraph_integer_t i, tooff = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -140,79 +146,89 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, tooff += VECTOR(*block_sizes)[i]; } for (to = start; to < no_blocks; to++) { - double tosize = VECTOR(*block_sizes)[to]; + igraph_integer_t tosize = VECTOR(*block_sizes)[to]; igraph_real_t prob = MATRIX(*pref_matrix, from, to); - double maxedges, last = RNG_GEOM(prob); + igraph_real_t maxedges; + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ + igraph_integer_t vfrom, vto; + if (directed && loops) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else if (directed && !loops && from != to) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else if (directed && !loops && from == to) { - maxedges = fromsize * (fromsize - 1); + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0); + IGRAPH_CHECK_MAXEDGES(); while (last < maxedges) { - long int vto = floor(last / fromsize); - long int vfrom = last - (igraph_real_t)vto * fromsize; + vto = floor(last / fromsize); + 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); + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else if (!directed && loops && from != to) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else if (!directed && loops && from == to) { - maxedges = fromsize * (fromsize + 1) / 2.0; + maxedges = ((igraph_real_t) fromsize) * (fromsize + 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor((sqrt(8 * last + 1) - 1) / 2); + vfrom = last - (((igraph_real_t) vto) * (vto + 1.0)) / 2.0; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else if (!directed && !loops && from != to) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor(last / fromsize); + vfrom = last - ((igraph_real_t) vto) * fromsize; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } } else { /*!directed && !loops && from==to */ - maxedges = fromsize * (fromsize - 1) / 2.0; + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); 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); + vto = floor((sqrt(8 * last + 1) + 1) / 2); + vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + igraph_vector_int_push_back(&edges, fromoff + vfrom); + igraph_vector_int_push_back(&edges, tooff + vto); last += RNG_GEOM(prob); last += 1; } @@ -225,11 +241,13 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - igraph_create(graph, &edges, n, directed); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; +#undef IGRAPH_CHECK_MAXEDGES } /** @@ -247,7 +265,7 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * 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. + * \p rho vector. * \param p The Bernoulli rate of connections between * vertices in different blocks. * \return Error code. @@ -256,16 +274,20 @@ int igraph_sbm_game(igraph_t *graph, igraph_integer_t n, * \ref igraph_hsbm_list_game() for a more general version. */ -int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, +igraph_error_t 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); +#define IGRAPH_CHECK_MAXEDGES() \ + do {if (maxedges > IGRAPH_MAX_EXACT_REAL) { \ + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); \ + }} while (0) + igraph_integer_t 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; + igraph_integer_t no_blocks = n / m; + igraph_vector_int_t edges; + igraph_integer_t offset = 0; if (n < 1) { IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL); @@ -273,7 +295,7 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, if (m < 1) { IGRAPH_ERROR("`m' must be positive for HSBM", IGRAPH_EINVAL); } - if ((long) n % (long) m) { + if (n % m) { IGRAPH_ERROR("`n' must be a multiple of `m' for HSBM", IGRAPH_EINVAL); } if (!igraph_vector_isininterval(rho, 0, 1)) { @@ -308,43 +330,45 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, VECTOR(csizes)[i] = round(VECTOR(*rho)[i] * m); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); RNG_BEGIN(); /* Block models first */ for (b = 0; b < no_blocks; b++) { - int from, to, fromoff = 0; + igraph_integer_t from, to, fromoff = 0; for (from = 0; from < k; from++) { - int fromsize = VECTOR(csizes)[from]; - int i, tooff = 0; + igraph_integer_t fromsize = VECTOR(csizes)[from]; + igraph_integer_t 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_integer_t tosize = VECTOR(csizes)[to]; igraph_real_t prob = MATRIX(*C, from, to); igraph_real_t maxedges; - igraph_real_t last = RNG_GEOM(prob); + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ if (from != to) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); 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); + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); last += RNG_GEOM(prob); last += 1; } } else { /* from==to */ - maxedges = fromsize * (fromsize - 1) / 2.0; + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 2.0; + IGRAPH_CHECK_MAXEDGES(); 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); + igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); + igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); last += RNG_GEOM(prob); last += 1; } @@ -361,32 +385,33 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, /* And now the rest, if not a special case */ if (p == 1) { - int fromoff = 0, tooff = m; + igraph_integer_t 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; + igraph_integer_t fromsize = m; + igraph_integer_t tosize = n - tooff; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); } } fromoff += m; tooff += m; } } else if (p > 0) { - int fromoff = 0, tooff = m; + igraph_integer_t 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); + igraph_integer_t fromsize = m; + igraph_integer_t tosize = n - tooff; + igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; + IGRAPH_CHECK_MAXEDGES(); + igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ 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); + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + vto)); last += RNG_GEOM(p); last += 1; } @@ -398,13 +423,14 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - igraph_create(graph, &edges, n, /*directed=*/ 0); + IGRAPH_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&csizes); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; +#undef IGRAPH_CHECK_MAXEDGES } /** @@ -420,7 +446,7 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, * \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 + * one for each block, specifying the Bernoulli rates of connections * within the block. * \param p The Bernoulli rate of connections between * vertices in different blocks. @@ -430,130 +456,132 @@ int igraph_hsbm_game(igraph_t *graph, igraph_integer_t n, * \ref igraph_hsbm_game() for a simpler general version. */ -int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, +igraph_error_t 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, + const igraph_vector_list_t *rholist, + const igraph_matrix_list_t *Clist, igraph_real_t p) { - int i, no_blocks = igraph_vector_ptr_size(rholist); + igraph_integer_t i, no_blocks = igraph_vector_list_size(rholist); igraph_real_t sq_dbl_epsilon = sqrt(DBL_EPSILON); - igraph_vector_t csizes, edges; - int b, offset = 0; + igraph_vector_int_t edges; + igraph_vector_t csizes; + igraph_integer_t b, offset = 0; if (n < 1) { - IGRAPH_ERROR("`n' must be positive for HSBM", IGRAPH_EINVAL); + IGRAPH_ERROR("`n' must be positive for HSBM.", IGRAPH_EINVAL); } if (no_blocks == 0) { - IGRAPH_ERROR("`rholist' empty for HSBM", IGRAPH_EINVAL); + IGRAPH_ERROR("`rholist' empty for HSBM.", IGRAPH_EINVAL); } - if (igraph_vector_ptr_size(Clist) != no_blocks && + if (igraph_matrix_list_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); + "for HSBM.", IGRAPH_EINVAL); } if (p < 0 || p > 1) { - IGRAPH_ERROR("`p' must be a probability for HSBM", IGRAPH_EINVAL); + 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); + 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); + 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]; + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, i); if (!igraph_vector_isininterval(rho, 0, 1)) { - IGRAPH_ERROR("`rho' must be between zero and one for HSBM", + 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); + 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]; + const igraph_matrix_t *C = igraph_matrix_list_get_ptr(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_ERROR("Bernoulli rates 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); + IGRAPH_ERROR("Bernoulli rate matrices must be symmetric.", 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); + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, i); + const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, i); + igraph_integer_t 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_ERROR("All Bernoulli rate matrix 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]; + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, i); igraph_real_t m = VECTOR(*mlist)[i]; - int j, k = igraph_vector_size(rho); + igraph_integer_t 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_ERROR("`rho' * `m' is not integer in HSBM.", IGRAPH_EINVAL); } } } IGRAPH_VECTOR_INIT_FINALLY(&csizes, 0); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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_integer_t from, to, fromoff = 0; + const igraph_vector_t *rho = igraph_vector_list_get_ptr(rholist, b); + const igraph_matrix_t *C = igraph_matrix_list_get_ptr(Clist, b); + igraph_integer_t m = VECTOR(*mlist)[b]; + igraph_integer_t k = igraph_vector_size(rho); - igraph_vector_resize(&csizes, k); + IGRAPH_CHECK(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; + igraph_integer_t fromsize = VECTOR(csizes)[from]; + igraph_integer_t 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_integer_t tosize = VECTOR(csizes)[to]; igraph_real_t prob = MATRIX(*C, from, to); igraph_real_t maxedges; - igraph_real_t last = RNG_GEOM(prob); + igraph_real_t last = RNG_GEOM(prob); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ if (from != to) { - maxedges = fromsize * tosize; + maxedges = ((igraph_real_t) 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); + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); last += RNG_GEOM(prob); last += 1; } } else { /* from==to */ - maxedges = fromsize * (fromsize - 1) / 2.0; + maxedges = ((igraph_real_t) fromsize) * (fromsize - 1.0) / 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); + igraph_integer_t vto = floor((sqrt(8 * last + 1) + 1) / 2); + igraph_integer_t vfrom = last - (((igraph_real_t) vto) * (vto - 1.0)) / 2.0; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, offset + tooff + vto)); last += RNG_GEOM(prob); last += 1; } @@ -570,15 +598,15 @@ int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, /* And now the rest, if not a special case */ if (p == 1) { - int fromoff = 0, tooff = VECTOR(*mlist)[0]; + igraph_integer_t 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; + igraph_integer_t fromsize = VECTOR(*mlist)[b]; + igraph_integer_t tosize = n - tooff; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + to)); } } fromoff += fromsize; @@ -587,17 +615,17 @@ int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, } } } else if (p > 0) { - int fromoff = 0, tooff = VECTOR(*mlist)[0]; + igraph_integer_t 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); + igraph_integer_t fromsize = VECTOR(*mlist)[b]; + igraph_integer_t tosize = n - tooff; + igraph_real_t maxedges = ((igraph_real_t) fromsize) * tosize; + igraph_real_t last = RNG_GEOM(p); /* RNG_GEOM may return NaN so igraph_integer_t is not suitable */ 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); + igraph_integer_t vto = floor(last / fromsize); + igraph_integer_t vfrom = last - ((igraph_real_t) vto) * fromsize; + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, fromoff + vfrom)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tooff + vto)); last += RNG_GEOM(p); last += 1; } @@ -611,11 +639,11 @@ int igraph_hsbm_list_game(igraph_t *graph, igraph_integer_t n, RNG_END(); - igraph_create(graph, &edges, n, /*directed=*/ 0); + IGRAPH_CHECK(igraph_create(graph, &edges, n, /*directed=*/ 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&csizes); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/games/static_fitness.c b/src/vendor/cigraph/src/games/static_fitness.c index 8a7d4c05fe2..3df0aa6d799 100644 --- a/src/vendor/cigraph/src/games/static_fitness.c +++ b/src/vendor/cigraph/src/games/static_fitness.c @@ -31,6 +31,7 @@ #include "igraph_random.h" #include "core/interruption.h" +#include "core/math.h" /* M_SQRT2 */ /** * \ingroup generators @@ -67,8 +68,12 @@ * 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. + * Reference: + * + * + * Goh K-I, Kahng B, Kim D: Universal behaviour of load distribution + * in scale-free networks. Phys Rev Lett 87(27):278701, 2001 + * https://doi.org/10.1103/PhysRevLett.87.278701. * * \param graph Pointer to an uninitialized graph object. * \param fitness_out A numeric vector containing the fitness of each vertex. @@ -88,10 +93,10 @@ * * Time complexity: O(|V| + |E| log |E|). */ -int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, +igraph_error_t 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_vector_int_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; @@ -99,19 +104,17 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, 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_real_t num_steps; igraph_integer_t step_counter = 0; - long int i, from, to, pos; + igraph_integer_t i, from, to, pos; - if (fitness_out == 0) { - IGRAPH_ERROR("fitness_out must not be null.", IGRAPH_EINVAL); - } + IGRAPH_ASSERT(fitness_out != NULL); 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); + no_of_nodes = igraph_vector_size(fitness_out); if (no_of_nodes == 0) { IGRAPH_CHECK(igraph_empty(graph, 0, is_directed)); return IGRAPH_SUCCESS; @@ -181,8 +184,8 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * no_of_edges)); while (no_of_edges > 0) { /* Report progress after every 10000 edges */ @@ -201,8 +204,8 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, continue; } - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); no_of_edges--; } @@ -211,7 +214,7 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed)); /* Clear the edge list */ - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } else { /* Multiple edges are disallowed */ @@ -337,7 +340,7 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, * 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. + * will get back an Erdős-Rényi 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 @@ -355,7 +358,7 @@ int igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_of_edges, * * Time complexity: O(|V| + |E| log |E|). */ -int igraph_static_power_law_game(igraph_t *graph, +igraph_error_t 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, @@ -363,7 +366,7 @@ int igraph_static_power_law_game(igraph_t *graph, igraph_vector_t fitness_out, fitness_in; igraph_real_t alpha_out = 0.0, alpha_in = 0.0; - long int i; + igraph_integer_t i; igraph_real_t j; if (no_of_nodes < 0) { @@ -373,7 +376,7 @@ int igraph_static_power_law_game(igraph_t *graph, /* 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)) { + } else if (isfinite(exponent_out)) { alpha_out = -1.0 / (exponent_out - 1); } else { alpha_out = 0.0; @@ -385,7 +388,7 @@ int igraph_static_power_law_game(igraph_t *graph, 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; + pow(10 * M_SQRT2 * (1 + alpha_out), -1.0 / alpha_out) - 1; } if (j < no_of_nodes) { j = no_of_nodes; @@ -398,7 +401,7 @@ int igraph_static_power_law_game(igraph_t *graph, 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)) { + } else if (isfinite(exponent_in)) { alpha_in = -1.0 / (exponent_in - 1); } else { alpha_in = 0.0; @@ -409,7 +412,7 @@ int igraph_static_power_law_game(igraph_t *graph, 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; + pow(10 * M_SQRT2 * (1 + alpha_in), -1.0 / alpha_in) - 1; } if (j < no_of_nodes) { j = no_of_nodes; diff --git a/src/vendor/cigraph/src/games/tree.c b/src/vendor/cigraph/src/games/tree.c index 805ad5313b4..1f2f422a17e 100644 --- a/src/vendor/cigraph/src/games/tree.c +++ b/src/vendor/cigraph/src/games/tree.c @@ -27,15 +27,17 @@ #include "igraph_interface.h" #include "igraph_random.h" +#include "math/safe_intop.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) { +static igraph_error_t igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { igraph_vector_int_t prufer; - long i; + igraph_integer_t i; if (directed) { IGRAPH_ERROR("The Prufer method for random tree generation does not support directed trees", IGRAPH_EINVAL); @@ -74,19 +76,22 @@ static int igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_t n, igraph 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; +static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { + igraph_vector_int_t edges; igraph_vector_int_t vertices; igraph_vector_bool_t visited; - long i, j, k; + igraph_integer_t i, j, k; + igraph_integer_t no_edges; + + IGRAPH_SAFE_MULT(n - 1, 2, &no_edges); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * (n - 1)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges); 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_CHECK(igraph_vector_int_init_range(&vertices, 0, n)); IGRAPH_FINALLY(igraph_vector_int_destroy, &vertices); RNG_BEGIN(); @@ -138,7 +143,7 @@ static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_in igraph_vector_int_destroy(&vertices); igraph_vector_bool_destroy(&visited); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -179,7 +184,7 @@ static int igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph, igraph_in * */ -int igraph_tree_game(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method) { +igraph_error_t 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; diff --git a/src/vendor/cigraph/src/games/watts_strogatz.c b/src/vendor/cigraph/src/games/watts_strogatz.c index 1a6e730fe86..184286b9df2 100644 --- a/src/vendor/cigraph/src/games/watts_strogatz.c +++ b/src/vendor/cigraph/src/games/watts_strogatz.c @@ -43,8 +43,7 @@ * \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(). + * 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. @@ -52,7 +51,7 @@ * generated graph. * \return Error code. * - * \sa \ref igraph_lattice(), \ref igraph_connect_neighborhood() and + * \sa \ref igraph_square_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. * @@ -60,13 +59,13 @@ * 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_error_t 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; + igraph_vector_int_t dimvector; + igraph_vector_bool_t periodic; if (dim < 1) { IGRAPH_ERROR("WS game: dimension should be at least one", IGRAPH_EINVAL); @@ -82,15 +81,18 @@ int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, /* Create the lattice first */ - IGRAPH_VECTOR_INIT_FINALLY(&dimvector, dim); - for (i = 0; i < dim; i++) { - VECTOR(dimvector)[i] = size; - } + IGRAPH_VECTOR_INT_INIT_FINALLY(&dimvector, dim); + igraph_vector_int_fill(&dimvector, size); - IGRAPH_CHECK(igraph_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED, - 0 /* mutual */, 1 /* circular */)); - igraph_vector_destroy(&dimvector); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&periodic, dim); + igraph_vector_bool_fill(&periodic, true); + + IGRAPH_CHECK(igraph_square_lattice(graph, &dimvector, nei, IGRAPH_UNDIRECTED, + /* mutual */ false, &periodic)); + + igraph_vector_bool_destroy(&periodic); + igraph_vector_int_destroy(&dimvector); + IGRAPH_FINALLY_CLEAN(2); IGRAPH_FINALLY(igraph_destroy, graph); /* Rewire the edges then */ @@ -98,5 +100,5 @@ int igraph_watts_strogatz_game(igraph_t *graph, igraph_integer_t dim, IGRAPH_CHECK(igraph_rewire_edges(graph, p, loops, multiple)); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/graph/adjlist.c b/src/vendor/cigraph/src/graph/adjlist.c index e3da88d09fd..c1ad5a278aa 100644 --- a/src/vendor/cigraph/src/graph/adjlist.c +++ b/src/vendor/cigraph/src/graph/adjlist.c @@ -33,17 +33,34 @@ /** * Helper function that simplifies a sorted adjacency vector by removing * duplicate elements and optionally self-loops. + * + * has_loops and has_multiple are pointers to booleans that will be updated + * to \c true if the function \em finds a loop or a multiple edge. These values will + * \em never be set back to zero by this function. The usage pattern for these + * arguments is that the caller should set them to zero, followed by one or + * multiple calls to this function; at the end of such a sequence the booleans + * will contain whether the function found at least one loop or multiple edge + * in the set of vertices that were investigated. + * + * Note the usage of the word "found" -- it might be the case that the + * function is not interested in loop or multiple edges due to how it is + * parameterized; in this case, we don't spend extra time in investigating the + * existence of loop or multiple edges, so the values of the has_loops and + * has_multiple arguments will stay as is. Therefore, upon exiting the + * function, finding \c false in one of these variables does \em not mean that + * there is no loop or multiple edge, only that the function hasn't found one. */ -static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( +static igraph_error_t 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 + igraph_loops_t loops, igraph_multiple_t multiple, igraph_bool_t *has_loops, + igraph_bool_t *has_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( +static igraph_error_t igraph_i_remove_loops_from_incidence_vector_in_place( igraph_vector_int_t *v, const igraph_t *graph, igraph_loops_t loops ); @@ -99,77 +116,121 @@ static int igraph_i_remove_loops_from_incidence_vector_in_place( * adjacency list contains the state of the graph at the time of its * initialization. * + * + * This function returns each neighbor list in sorted order, just + * like \ref igraph_neighbors(). + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a + * different value from \c IGRAPH_MULTIPLE. + * * \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 + * \param mode Constant specifying whether to include only outgoing + * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), + * or both (\c IGRAPH_ALL) types of neighbors + * in the adjacency list. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE * makes each loop edge appear only once in the adjacency list of the - * corresponding vertex. IGRAPH_LOOPS_TWICE makes loop edges + * corresponding vertex. \c 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. + * but only if the graph is undirected or \p mode is set to + * \c 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 + * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * \c 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. * + * \sa \ref igraph_neighbors() for getting the neighbor lists of individual + * vertices. + * * 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_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Cannot create adjacency list view", IGRAPH_EINVMODE); + 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + /* igraph_degree() is fast when loops=true */ + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ true)); + + al->length = no_of_nodes; al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t); - if (al->adjs == 0) { - IGRAPH_ERROR("Cannot create adjacency list view", IGRAPH_ENOMEM); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating adjacency list view."); + IGRAPH_FINALLY(igraph_adjlist_destroy, al); + + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + multiple = IGRAPH_MULTIPLE; } - IGRAPH_FINALLY(igraph_adjlist_destroy, al); + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + if (mode == IGRAPH_ALL) { + loops = IGRAPH_LOOPS_TWICE; + } else { + loops = IGRAPH_LOOPS_ONCE; + } + } - for (i = 0; i < al->length; i++) { + igraph_bool_t has_loops = false; + igraph_bool_t has_multiple = false; + for (igraph_integer_t 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_vector_int_init(&al->adjs[i], VECTOR(degrees)[i])); + IGRAPH_CHECK(igraph_neighbors(graph, &al->adjs[i], i, mode)); + /* Attention: This function will only set values for has_loops and has_multiple + * if it finds loops/multi-edges. Otherwise they are left at their original value. */ IGRAPH_CHECK(igraph_i_simplify_sorted_int_adjacency_vector_in_place( - &al->adjs[i], i, mode, loops, multiple + &al->adjs[i], i, mode, loops, multiple, &has_loops, &has_multiple )); } + if (has_loops) { + /* If we have found at least one loop above, set the cache to true */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, true); + } else if (loops == IGRAPH_NO_LOOPS) { + /* If we explicitly _checked_ for loops (to remove them) and haven't + * found one, set the cache to false. This is the only case when a + * definite "no" from has_loops really means that there are no loops at + * all */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, false); + } + if (has_multiple) { + /* If we have found at least one multiedge above, set the cache to true */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, true); + } else if (multiple == IGRAPH_NO_MULTIPLE) { + /* If we explicitly _checked_ for multi-edges (to remove them) and + * haven't found one, set the cache to false. This is the only case + * when a definite "no" from has_multiple really means that there are + * no multi-edges at all all */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, false); + } - igraph_vector_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(2); + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_adjlist_destroy */ - return 0; + return IGRAPH_SUCCESS; } /** @@ -179,28 +240,27 @@ int igraph_adjlist_init(const igraph_t *graph, igraph_adjlist_t *al, * 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; +igraph_error_t igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes) { 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_CHECK_OOM(al->adjs, "Insufficient memory for creating adjlist."); IGRAPH_FINALLY(igraph_adjlist_destroy, al); - for (i = 0; i < al->length; i++) { + + for (igraph_integer_t i = 0; i < al->length; i++) { IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], 0)); } + IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -211,28 +271,34 @@ int igraph_adjlist_init_empty(igraph_adjlist_t *al, igraph_integer_t no_of_nodes * 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. + * + * + * This function returns each neighbor list in sorted order. + * * \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 + * (\c IGRAPH_OUT), incoming (\c IGRAPH_IN), + * or both (\c 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. * + * \sa \ref igraph_adjlist_init(), \ref igraph_complementer() + * * Time complexity: O(|V|^2+|E|), quadratic in the number of vertices. */ -int igraph_adjlist_init_complementer(const igraph_t *graph, +igraph_error_t 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; + + igraph_vector_bool_t seen; + igraph_vector_int_t neis; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Cannot create complementer adjlist view", IGRAPH_EINVMODE); + IGRAPH_ERROR("Invalid neighbor mode specified for complementer adjlist view.", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { @@ -241,48 +307,114 @@ int igraph_adjlist_init_complementer(const igraph_t *graph, 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_CHECK_OOM(al->adjs, "Insufficient memory for creating complementer adjlist view."); 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_BOOL_INIT_FINALLY(&seen, al->length); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + for (igraph_integer_t i = 0; i < al->length; i++) { + /* For each vertex, we mark neighbors within the 'seen' bool vector. + * Then we iterate over 'seen' and record non-marked vertices in + * the adjacency list. */ - 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; + + /* Reset neighbor counter and 'seen' vector. */ + igraph_vector_bool_null(&seen); + igraph_integer_t n = al->length; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); + if (!loops) { - seen[i] = 1; + VECTOR(seen)[i] = true; n--; } - for (j = 0; j < igraph_vector_size(&vec); j++) { - if (! seen [ (long int) VECTOR(vec)[j] ] ) { + + igraph_integer_t neis_size = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < neis_size; j++) { + if (! VECTOR(seen)[ VECTOR(neis)[j] ] ) { n--; - seen[ (long int) VECTOR(vec)[j] ] = 1; + VECTOR(seen)[ VECTOR(neis)[j] ] = true; } } + + /* Produce "non-neighbor" list in sorted order. */ IGRAPH_CHECK(igraph_vector_int_init(&al->adjs[i], n)); - for (j = 0, k = 0; k < n; j++) { - if (!seen[j]) { + for (igraph_integer_t j = 0, k = 0; k < n; j++) { + if (!VECTOR(seen)[j]) { VECTOR(al->adjs[i])[k++] = j; } } } - IGRAPH_FREE(seen); - igraph_vector_destroy(&vec); - IGRAPH_FINALLY_CLEAN(3); - return 0; + igraph_vector_bool_destroy(&seen); + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(3); /* +1 for the adjlist itself */ + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_adjlist_init_from_inclist + * \brief Constructs an adjacency list of vertices from an incidence list. + * + * In some algorithms it is useful to have an adjacency list \em and an incidence + * list representation of the same graph, and in many cases it is the most useful + * if they are consistent with each other, i.e. if can be guaranteed that the + * vertex ID in the i-th entry of the adjacency list of vertex v is the + * \em other endpoint of the edge in the i-th entry of the incidence list of the + * same vertex. This function creates such an adjacency list from the corresponding + * incidence list by looking up the endpoints of each edge in the incidence + * list and constructing the corresponding adjacenecy vectors. + * + * + * The adjacency list is independent of the graph or the incidence list after + * creation; in other words, modifications that are made to the graph or the + * incidence list are not reflected in the adjacency list. + * + * \param graph The input graph. + * \param al Pointer to an uninitialized igraph_adjlist_t object. + * \param il Pointer to an \em initialized igraph_inclist_t object + * that will be converted into an adjacency list. + * \return Error code. + * + * Time complexity: O(|V|+|E|), linear in the number of vertices and + * edges. + */ + +igraph_error_t igraph_adjlist_init_from_inclist( + const igraph_t *graph, igraph_adjlist_t *al, const igraph_inclist_t *il) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, num_neis; + + igraph_vector_int_t *neis; + igraph_vector_int_t *incs; + + if (igraph_inclist_size(il) != no_of_nodes) { + IGRAPH_ERRORF( + "Incidence list has %" IGRAPH_PRId " entries but the graph has %" IGRAPH_PRId " vertices.", + IGRAPH_EINVAL, + igraph_inclist_size(il), + no_of_nodes + ); + } + + IGRAPH_CHECK(igraph_adjlist_init_empty(al, no_of_nodes)); + for (i = 0; i < no_of_nodes; i++) { + neis = igraph_adjlist_get(al, i); + incs = igraph_inclist_get(il, i); + + num_neis = igraph_vector_int_size(incs); + IGRAPH_CHECK(igraph_vector_int_resize(neis, num_neis)); + + for (j = 0; j < num_neis; j++) { + VECTOR(*neis)[j] = IGRAPH_OTHER(graph, VECTOR(*incs)[j], i); + } + } + + return IGRAPH_SUCCESS; } /** @@ -295,8 +427,10 @@ int igraph_adjlist_init_complementer(const igraph_t *graph, * Time complexity: depends on memory management. */ void igraph_adjlist_destroy(igraph_adjlist_t *al) { - long int i; + igraph_integer_t i; for (i = 0; i < al->length; i++) { + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_destroy can handle this. */ igraph_vector_int_destroy(&al->adjs[i]); } IGRAPH_FREE(al->adjs); @@ -311,7 +445,7 @@ void igraph_adjlist_destroy(igraph_adjlist_t *al) { * the total number of elements in the adjacency list. */ void igraph_adjlist_clear(igraph_adjlist_t *al) { - long int i; + igraph_integer_t i; for (i = 0; i < al->length; i++) { igraph_vector_int_clear(&al->adjs[i]); } @@ -334,14 +468,19 @@ igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al) { * \function igraph_adjlist_sort * \brief Sorts each vector in an adjacency list. * - * Sorts every vector of the adjacency list. + * Sorts every vector of the adjacency list. Note that + * \ref igraph_adjlist_init() already produces sorted neighbor lists. + * This function is useful when the adjacency list is produced in + * a different manner, or is modified in a way that does not preserve + * the sorted order. + * * \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; + igraph_integer_t i; for (i = 0; i < al->length; i++) { igraph_vector_int_sort(&al->adjs[i]); } @@ -353,24 +492,28 @@ void igraph_adjlist_sort(igraph_adjlist_t *al) { * * Simplifies an adjacency list, i.e. removes loop and multiple edges. * + * + * When the adjacency list is created with \ref igraph_adjlist_init(), + * use the \c loops and \c multiple parameters of that function instead. + * * \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_error_t igraph_adjlist_simplify(igraph_adjlist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; igraph_vector_int_t mark; - igraph_vector_int_init(&mark, n); - IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, n); for (i = 0; i < n; i++) { igraph_vector_int_t *v = &al->adjs[i]; - long int j, l = igraph_vector_int_size(v); + igraph_integer_t 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]; + igraph_integer_t e = VECTOR(*v)[j]; if (VECTOR(mark)[e] != i + 1) { VECTOR(mark)[e] = i + 1; j++; @@ -384,74 +527,40 @@ int igraph_adjlist_simplify(igraph_adjlist_t *al) { 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; + return IGRAPH_SUCCESS; } #ifndef USING_R -int igraph_adjlist_print(const igraph_adjlist_t *al) { - long int i; - long int n = al->length; +igraph_error_t igraph_adjlist_print(const igraph_adjlist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; for (i = 0; i < n; i++) { igraph_vector_int_t *v = &al->adjs[i]; igraph_vector_int_print(v); } - return 0; + return IGRAPH_SUCCESS; } #endif -int igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { - long int i; - long int n = al->length; +igraph_error_t igraph_adjlist_fprint(const igraph_adjlist_t *al, FILE *outfile) { + igraph_integer_t i; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } #define ADJLIST_CANON_EDGE(from, to, directed) \ do { \ igraph_integer_t temp; \ - if((!directed) && from < to) { \ + if ((!directed) && from < to) { \ temp = to; \ to = from; \ from = temp; \ } \ - } while(0); + } 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; @@ -461,40 +570,52 @@ igraph_bool_t igraph_adjlist_has_edge(igraph_adjlist_t* al, igraph_integer_t fro } -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_error_t 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_bool_t found_old, found_new; + igraph_integer_t 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; + found_old = igraph_vector_int_binsearch(oldfromvec, oldto, &oldpos); + if (! found_old) { + IGRAPH_ERROR("Edge to replace does not exist.", IGRAPH_EINVAL); + } + found_new = igraph_vector_int_binsearch(newfromvec, newto, &newpos); + if (found_new) { + IGRAPH_ERROR("New edge already exists.", IGRAPH_EINVAL); } - igraph_vector_int_remove(oldfromvec, oldpos); - if (oldfromvec == newfromvec && oldpos < newpos) { - --newpos; + if (oldfromvec != newfromvec) { + /* grow the new vector first and then remove the item from the old one + * to ensure that we don't end up in a situation where the removal + * succeeds but the addition does not */ + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); + igraph_vector_int_remove(oldfromvec, oldpos); + } else { + /* moving item within the same vector; here we can safely remove first + * and insert afterwards because there is no need to re-allocate memory */ + igraph_vector_int_remove(oldfromvec, oldpos); + if (oldpos < newpos) { + --newpos; + } + IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); } - IGRAPH_CHECK(igraph_vector_int_insert(newfromvec, newpos, newto)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_remove_loops_from_incidence_vector_in_place( +static igraph_error_t 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_integer_t 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 @@ -561,49 +682,26 @@ static int igraph_i_remove_loops_from_incidence_vector_in_place( 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; +igraph_error_t igraph_inclist_print(const igraph_inclist_t *al) { + igraph_integer_t i; + igraph_integer_t n = al->length; for (i = 0; i < n; i++) { igraph_vector_int_t *v = &al->incs[i]; igraph_vector_int_print(v); } - return 0; + return IGRAPH_SUCCESS; } #endif -int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { - long int i; - long int n = al->length; +igraph_error_t igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { + igraph_integer_t i; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /** @@ -625,6 +723,10 @@ int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { * argument to control whether this will be the case (\c IGRAPH_LOOPS_TWICE ) * or not (\c IGRAPH_LOOPS_ONCE or \c IGRAPH_NO_LOOPS). * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE. + * * \param graph The input graph. * \param il Pointer to an uninitialized incidence list. * \param mode Constant specifying whether incoming edges @@ -643,44 +745,37 @@ int igraph_inclist_fprint(const igraph_inclist_t *al, FILE *outfile) { * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. */ -int igraph_inclist_init(const igraph_t *graph, +igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_EINVMODE); + 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + /* igraph_degrees() is fast when loops=true */ + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode, /* loops= */ 1)); + + il->length = no_of_nodes; il->incs = IGRAPH_CALLOC(il->length, igraph_vector_int_t); if (il->incs == 0) { - IGRAPH_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot create incidence list view.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_inclist_destroy, il); - for (i = 0; i < il->length; i++) { - int j, n; - + for (igraph_integer_t i = 0; i < il->length; i++) { 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]; - } + IGRAPH_CHECK(igraph_vector_int_init(&il->incs[i], VECTOR(degrees)[i])); + IGRAPH_CHECK(igraph_incident(graph, &il->incs[i], i, mode)); if (loops != IGRAPH_LOOPS_TWICE) { IGRAPH_CHECK( @@ -689,9 +784,10 @@ int igraph_inclist_init(const igraph_t *graph, } } - igraph_vector_destroy(&tmp); - IGRAPH_FINALLY_CLEAN(2); - return 0; + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(2); /* + igraph_inclist_destroy */ + + return IGRAPH_SUCCESS; } /** @@ -709,13 +805,13 @@ int igraph_inclist_init(const igraph_t *graph, * 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; +igraph_error_t igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { + igraph_integer_t 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_ERROR("Cannot create incidence list view", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_inclist_destroy, il); @@ -724,7 +820,7 @@ int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { } IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -737,10 +833,10 @@ int igraph_inclist_init_empty(igraph_inclist_t *il, igraph_integer_t n) { */ void igraph_inclist_destroy(igraph_inclist_t *il) { - long int i; + igraph_integer_t 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. */ + /* This works if some igraph_vector_int_t's contain NULL, + because igraph_vector_int_destroy can handle this. */ igraph_vector_int_destroy(&il->incs[i]); } IGRAPH_FREE(il->incs); @@ -756,7 +852,7 @@ void igraph_inclist_destroy(igraph_inclist_t *il) { * the total number of elements in the incidence list. */ void igraph_inclist_clear(igraph_inclist_t *il) { - long int i; + igraph_integer_t i; for (i = 0; i < il->length; i++) { igraph_vector_int_clear(&il->incs[i]); } @@ -775,12 +871,22 @@ 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( +/* See the prototype above for a description of this function. */ +static igraph_error_t 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 + igraph_loops_t loops, igraph_multiple_t multiple, igraph_bool_t *has_loops, + igraph_bool_t *has_multiple + ) { - long int i, p = 0; - long int n = igraph_vector_int_size(v); + igraph_bool_t dummy1, dummy2; + if (has_loops == NULL) { + has_loops = &dummy1; + } + if (has_multiple == NULL) { + has_multiple = &dummy2; + } + igraph_integer_t i, p = 0; + igraph_integer_t n = igraph_vector_int_size(v); if ( multiple == IGRAPH_MULTIPLE && @@ -801,6 +907,12 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( (i == n - 1 || VECTOR(*v)[i + 1] != VECTOR(*v)[i])) { VECTOR(*v)[p] = VECTOR(*v)[i]; p++; + } else { + if (VECTOR(*v)[i] == index) { + *has_loops = true; + } else if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { + *has_multiple = true; + } } } } else { @@ -809,6 +921,8 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( if (VECTOR(*v)[i] != index) { VECTOR(*v)[p] = VECTOR(*v)[i]; p++; + } else { + *has_loops = true; } } } @@ -816,11 +930,25 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( 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 if ( + /* If this is not a loop then we have a multigraph. + Else we have at least two loops. + The v vector comes from a call to igraph_neighbors. + This will count loops twice if mode == IGRAPH_ALL. + So if mode != IGRAPH_ALL, + then we have a multigraph. + If mode == IGRAPH_ALL and we have three loops + then we also have a multigraph + */ + (VECTOR(*v)[i] != index) || + (mode != IGRAPH_ALL) || + (mode == IGRAPH_ALL && i < n - 2 && VECTOR(*v)[i + 2] == VECTOR(*v)[i]) + ){ + *has_multiple = true; } } } else { @@ -831,6 +959,7 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( for (i = 0; i < n; i++) { VECTOR(*v)[p] = VECTOR(*v)[i]; if (VECTOR(*v)[i] == index) { + *has_loops = true; /* 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) { @@ -843,12 +972,12 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( } 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 { + *has_multiple = 1; /* 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, @@ -882,41 +1011,76 @@ static int igraph_i_simplify_sorted_int_adjacency_vector_in_place( /** * \function igraph_lazy_adjlist_init - * \brief Initialized a lazy adjacency list. + * \brief Initializes 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. + * but the neighbor vertices are not queried, only at the + * \ref igraph_lazy_adjlist_get() calls. Neighbor lists will be returned + * in sorted order. + * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE or setting \p multiple to a + * different value from \c IGRAPH_MULTIPLE. + * * \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). + * \param mode Constant specifying whether to include only outgoing + * (\c IGRAPH_OUT), only incoming (\c IGRAPH_IN), + * or both (\c IGRAPH_ALL) types of neighbors + * in the adjacency list. It is ignored for undirected graphs. + * \param loops Specifies how to treat loop edges. \c IGRAPH_NO_LOOPS + * removes loop edges from the adjacency list. \c IGRAPH_LOOPS_ONCE + * makes each loop edge appear only once in the adjacency list of the + * corresponding vertex. \c 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 \p mode is set to + * \c IGRAPH_ALL. + * \param multiple Specifies how to treat multiple (parallel) edges. + * \c IGRAPH_NO_MULTIPLE collapses parallel edges into a single one; + * \c 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. * + * \sa \ref igraph_neighbors() for getting the neighbor lists of individual + * vertices. + * * 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_error_t 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); + IGRAPH_ERROR("Cannot create lazy adjacency list view.", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; } + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + multiple = IGRAPH_MULTIPLE; + } + + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + if (mode == IGRAPH_ALL) { + loops = IGRAPH_LOOPS_TWICE; + } else { + loops = IGRAPH_LOOPS_ONCE; + } + } + al->mode = mode; al->loops = loops; al->multiple = multiple; @@ -924,17 +1088,9 @@ int igraph_lazy_adjlist_init(const igraph_t *graph, al->length = igraph_vcount(graph); al->adjs = IGRAPH_CALLOC(al->length, igraph_vector_int_t*); + IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating lazy adjacency list view."); - 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; + return IGRAPH_SUCCESS; } /** @@ -949,7 +1105,6 @@ int igraph_lazy_adjlist_init(const igraph_t *graph, void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { igraph_lazy_adjlist_clear(al); - igraph_vector_destroy(&al->dummy); IGRAPH_FREE(al->adjs); } @@ -962,7 +1117,7 @@ void igraph_lazy_adjlist_destroy(igraph_lazy_adjlist_t *al) { * 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; + igraph_integer_t i, n = al->length; for (i = 0; i < n; i++) { if (al->adjs[i] != 0) { igraph_vector_int_destroy(al->adjs[i]); @@ -984,46 +1139,36 @@ 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; - } +igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, igraph_integer_t no) { + igraph_error_t ret; + if (al->adjs[no] == NULL) { 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; + if (al->adjs[no] == NULL) { + return NULL; } - n = igraph_vector_size(&al->dummy); - ret = igraph_vector_int_init(al->adjs[no], n); - if (ret != 0) { + ret = igraph_vector_int_init(al->adjs[no], 0); + if (ret != IGRAPH_SUCCESS) { IGRAPH_FREE(al->adjs[no]); - igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); - return 0; + return NULL; } - for (i = 0; i < n; i++) { - VECTOR(*al->adjs[no])[i] = VECTOR(al->dummy)[i]; + ret = igraph_neighbors(al->graph, al->adjs[no], no, al->mode); + if (ret != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(al->adjs[no]); + IGRAPH_FREE(al->adjs[no]); + return NULL; } ret = igraph_i_simplify_sorted_int_adjacency_vector_in_place( - al->adjs[no], no, al->mode, al->loops, al->multiple + al->adjs[no], no, al->mode, al->loops, al->multiple, NULL, + NULL ); - if (ret != 0) { + if (ret != IGRAPH_SUCCESS) { igraph_vector_int_destroy(al->adjs[no]); IGRAPH_FREE(al->adjs[no]); - igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); - return 0; + return NULL; } } @@ -1046,6 +1191,10 @@ igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, * 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. * + * + * As of igraph 0.10, there is a small performance cost to setting \p loops + * to a different value than \c IGRAPH_LOOPS_TWICE. + * * \param graph The input graph. * \param al Pointer to an uninitialized incidence list. * \param mode Constant, it gives whether incoming edges @@ -1066,7 +1215,7 @@ igraph_vector_int_t *igraph_i_lazy_adjlist_get_real(igraph_lazy_adjlist_t *al, * also depends on the underlying memory management. */ -int igraph_lazy_inclist_init(const igraph_t *graph, +igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, igraph_lazy_inclist_t *il, igraph_neimode_t mode, igraph_loops_t loops) { @@ -1086,15 +1235,10 @@ int igraph_lazy_inclist_init(const igraph_t *graph, 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_ERROR("Cannot create lazy incidence list view", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - IGRAPH_FINALLY(igraph_free, il->incs); - IGRAPH_CHECK(igraph_vector_init(&il->dummy, 0)); - IGRAPH_FINALLY_CLEAN(1); - - return 0; + return IGRAPH_SUCCESS; } @@ -1110,7 +1254,6 @@ int igraph_lazy_inclist_init(const igraph_t *graph, void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { igraph_lazy_inclist_clear(il); - igraph_vector_destroy(&il->dummy); IGRAPH_FREE(il->incs); } @@ -1124,7 +1267,7 @@ void igraph_lazy_inclist_destroy(igraph_lazy_inclist_t *il) { * 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; + igraph_integer_t i, n = il->length; for (i = 0; i < n; i++) { if (il->incs[i] != 0) { igraph_vector_int_destroy(il->incs[i]); @@ -1146,44 +1289,34 @@ 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; - } +igraph_vector_int_t *igraph_i_lazy_inclist_get_real(igraph_lazy_inclist_t *il, igraph_integer_t no) { + igraph_error_t ret; + if (il->incs[no] == NULL) { 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; + if (il->incs[no] == NULL) { + return NULL; } - n = igraph_vector_size(&il->dummy); - ret = igraph_vector_int_init(il->incs[no], n); - if (ret != 0) { + ret = igraph_vector_int_init(il->incs[no], 0); + if (ret != IGRAPH_SUCCESS) { IGRAPH_FREE(il->incs[no]); - igraph_error("", IGRAPH_FILE_BASENAME, __LINE__, ret); - return 0; + return NULL; } - for (i = 0; i < n; i++) { - VECTOR(*il->incs[no])[i] = VECTOR(il->dummy)[i]; + ret = igraph_incident(il->graph, il->incs[no], no, il->mode); + if (ret != IGRAPH_SUCCESS) { + igraph_vector_int_destroy(il->incs[no]); + IGRAPH_FREE(il->incs[no]); + return NULL; } 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) { + if (ret != IGRAPH_SUCCESS) { igraph_vector_int_destroy(il->incs[no]); IGRAPH_FREE(il->incs[no]); - return 0; + return NULL; } } } diff --git a/src/vendor/cigraph/src/graph/attributes.c b/src/vendor/cigraph/src/graph/attributes.c index afb05208c17..8c1226454bc 100644 --- a/src/vendor/cigraph/src/graph/attributes.c +++ b/src/vendor/cigraph/src/graph/attributes.c @@ -25,22 +25,21 @@ #include "igraph_memory.h" #include "graph/attributes.h" - -#include "config.h" +#include "internal/hacks.h" /* strdup */ #include #include /* Should you ever want to have a thread-local attribute handler table, prepend - * IGRAPH_THREAD_LOCAL to the following declaration */ + * IGRAPH_THREAD_LOCAL to the following declaration and #include "config.h". */ igraph_attribute_table_t *igraph_i_attribute_table = 0; -int igraph_i_attribute_init(igraph_t *graph, void *attr) { +igraph_error_t 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; + return IGRAPH_SUCCESS; } } @@ -50,92 +49,105 @@ 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_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_add_vertices(igraph_t *graph, long int nv, void *attr) { +igraph_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->add_vertices(graph, nv, attr); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_permute_vertices(const igraph_t *graph, +igraph_error_t igraph_i_attribute_permute_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_t *idx) { - + const igraph_vector_int_t *idx) { + /* graph and newgraph may be the same, in which case we need to support + * in-place operations. If they are _not_ the same, it is assumed that the + * new graph has no vertex attributes yet */ if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_vertices(graph, newgraph, idx); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_combine_vertices(const igraph_t *graph, +igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb) { + /* It is assumed that the two graphs are not the same and that the new + * graph has no vertex attributes yet. We cannot assert the latter but we + * can assert the former */ + IGRAPH_ASSERT(graph != newgraph); if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_vertices(graph, newgraph, merges, comb); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_add_edges(igraph_t *graph, - const igraph_vector_t *edges, void *attr) { +igraph_error_t igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_int_t *edges, void *attr) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->add_edges(graph, edges, attr); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_permute_edges(const igraph_t *graph, +igraph_error_t igraph_i_attribute_permute_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_t *idx) { + const igraph_vector_int_t *idx) { + /* graph and newgraph may be the same, in which case we need to support + * in-place operations. If they are _not_ the same, it is assumed that the + * new graph has no edge attributes yet */ if (igraph_i_attribute_table) { return igraph_i_attribute_table->permute_edges(graph, newgraph, idx); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_combine_edges(const igraph_t *graph, +igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb) { + /* It is assumed that the two graphs are not the same and that the new + * graph has no eedge attributes yet. We cannot assert the latter but we + * can assert the former */ + IGRAPH_ASSERT(graph != newgraph); if (igraph_i_attribute_table) { return igraph_i_attribute_table->combine_edges(graph, newgraph, merges, comb); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_info(const igraph_t *graph, +igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_t *gtypes, + igraph_vector_int_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_t *vtypes, + igraph_vector_int_t *vtypes, igraph_strvector_t *enames, - igraph_vector_t *etypes) { + igraph_vector_int_t *etypes) { if (igraph_i_attribute_table) { return igraph_i_attribute_table->get_info(graph, gnames, gtypes, vnames, vtypes, enames, etypes); } else { - return 0; + return IGRAPH_SUCCESS; } } @@ -145,115 +157,115 @@ igraph_bool_t igraph_i_attribute_has_attr(const igraph_t *graph, if (igraph_i_attribute_table) { return igraph_i_attribute_table->has_attr(graph, type, name); } else { - return 0; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_gettype(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_numeric_graph_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_numeric_vertex_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_numeric_edge_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_string_graph_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_string_vertex_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_string_edge_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_bool_graph_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_bool_vertex_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } -int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, +igraph_error_t 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; + return IGRAPH_SUCCESS; } } @@ -266,6 +278,12 @@ int igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, * igraph is compiled in thread-local mode. In the vast majority of cases, * this is not a significant restriction. * + * + * Attribute handlers are normally attached on program startup, and are + * left active for the program's lifetime. This is because a graph object + * created with a given attribute handler must not be manipulated while + * a different attribute handler is active. + * * \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. @@ -288,7 +306,7 @@ igraph_i_set_attribute_table(const igraph_attribute_table_t * table) { } igraph_bool_t igraph_has_attribute_table(void) { - return igraph_i_attribute_table != 0; + return igraph_i_attribute_table != NULL; } @@ -301,7 +319,7 @@ igraph_bool_t igraph_has_attribute_table(void) { * * Time complexity: O(1) */ -int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { +igraph_error_t igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { IGRAPH_CHECK(igraph_vector_ptr_init(&comb->list, 0)); return IGRAPH_SUCCESS; } @@ -316,7 +334,7 @@ int igraph_attribute_combination_init(igraph_attribute_combination_t *comb) { attribute combination list. */ void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) { - long int i, n = igraph_vector_ptr_size(&comb->list); + igraph_integer_t 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) { @@ -339,17 +357,23 @@ void igraph_attribute_combination_destroy(igraph_attribute_combination_t *comb) * \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. + * \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION. This function is called + * by the concrete attribute handler attached to igraph, and its + * calling signature depends completely on the attribute handler. + * For instance, if you are using attributes from C and you have + * attached the C attribute handler, you need to follow the + * documentation of the C attribute handler + * for more details. * \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, +igraph_error_t 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); + igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i = 0; i < n; i++) { @@ -367,20 +391,26 @@ int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, /* 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 (! rec) { + IGRAPH_ERROR("Cannot create attribute combination data.", + IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - if (!name) { + IGRAPH_FINALLY(igraph_free, rec); + if (! name) { rec->name = NULL; } else { rec->name = strdup(name); + if (! rec->name) { + IGRAPH_ERROR("Cannot create attribute combination data.", + IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } } + IGRAPH_FINALLY(igraph_free, (char *) rec->name); /* free() is safe on NULL */ rec->type = type; rec->func = func; IGRAPH_CHECK(igraph_vector_ptr_push_back(&comb->list, rec)); + IGRAPH_FINALLY_CLEAN(2); /* ownership of 'rec' transferred to 'comb->list' */ } @@ -401,9 +431,9 @@ int igraph_attribute_combination_add(igraph_attribute_combination_t *comb, * 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, +igraph_error_t igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, const char *name) { - long int i, n = igraph_vector_ptr_size(&comb->list); + igraph_integer_t i, n = igraph_vector_ptr_size(&comb->list); /* Search, in case it is already there */ for (i = 0; i < n; i++) { @@ -429,11 +459,11 @@ int igraph_attribute_combination_remove(igraph_attribute_combination_t *comb, return IGRAPH_SUCCESS; } -int igraph_attribute_combination_query(const igraph_attribute_combination_t *comb, +igraph_error_t 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); + igraph_integer_t 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]; @@ -442,7 +472,7 @@ int igraph_attribute_combination_query(const igraph_attribute_combination_t *com (name && n && !strcmp(n, name)) ) { *type = rec->type; *func = rec->func; - return 0; + return IGRAPH_SUCCESS; } if (!n) { def = i; @@ -459,18 +489,41 @@ int igraph_attribute_combination_query(const igraph_attribute_combination_t *com *func = rec->func; } - return 0; + return IGRAPH_SUCCESS; } -int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { +/** + * \function igraph_attribute_combination + * \brief Initialize attribute combination list and add records. + * + * \param comb The uninitialized attribute combination list. + * \param ... A list of 'name, type[, func]', where: + * \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. + * The list is closed by setting the name to \c IGRAPH_NO_MORE_ATTRIBUTES. + * \return Error code. + * + * Time complexity: O(n^2), where n is the number attribute + * combinations records to add. + * + * \example examples/simple/igraph_attribute_combination.c + */ +igraph_error_t 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; + while (true) { + igraph_function_pointer_t func = NULL; igraph_attribute_combination_type_t type; const char *name; @@ -480,7 +533,7 @@ int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { break; } - type = (igraph_attribute_combination_type_t)va_arg(ap, int); + type = (igraph_attribute_combination_type_t) va_arg(ap, int); if (type == IGRAPH_ATTRIBUTE_COMBINE_FUNCTION) { func = va_arg(ap, igraph_function_pointer_t); } @@ -489,10 +542,14 @@ int igraph_attribute_combination(igraph_attribute_combination_t *comb, ...) { name = 0; } - IGRAPH_CHECK(igraph_attribute_combination_add(comb, name, type, func)); + igraph_error_t ret = igraph_attribute_combination_add(comb, name, type, func); + if (ret != IGRAPH_SUCCESS) { + va_end(ap); + return ret; + } } va_end(ap); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/graph/attributes.h b/src/vendor/cigraph/src/graph/attributes.h index d0c8426f8f7..ef5838bae81 100644 --- a/src/vendor/cigraph/src/graph/attributes.h +++ b/src/vendor/cigraph/src/graph/attributes.h @@ -20,93 +20,97 @@ #define IGRAPH_GRAPH_ATTRIBUTES_H #include "igraph_attributes.h" +#include "igraph_decls.h" #include "igraph_strvector.h" #include "igraph_types.h" -#include "igraph_vector_ptr.h" + +__BEGIN_DECLS #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; \ + igraph_error_t igraph_i_ret2=IGRAPH_SUCCESS; \ if ((from)->attr) { \ IGRAPH_CHECK(igraph_i_ret2=igraph_i_attribute_copy((to),(from),(ga),(va),(ea))); \ } else { \ - (to)->attr = 0; \ + (to)->attr = NULL; \ } \ - if (igraph_i_ret2 != 0) { \ + if (igraph_i_ret2 != IGRAPH_SUCCESS) { \ IGRAPH_ERROR("", igraph_i_ret2); \ } \ } while(0) -int igraph_i_attribute_init(igraph_t *graph, void *attr); +igraph_error_t 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_error_t 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_error_t igraph_i_attribute_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr); +igraph_error_t 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, + const igraph_vector_int_t *idx); +igraph_error_t igraph_i_attribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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_error_t igraph_i_attribute_add_edges(igraph_t *graph, + const igraph_vector_int_t *edges, void *attr); +igraph_error_t 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, + const igraph_vector_int_t *idx); +igraph_error_t igraph_i_attribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_t *merges, const igraph_attribute_combination_t *comb); -int igraph_i_attribute_get_info(const igraph_t *graph, +igraph_error_t igraph_i_attribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_t *gtypes, + igraph_vector_int_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_t *vtypes, + igraph_vector_int_t *vtypes, igraph_strvector_t *enames, - igraph_vector_t *etypes); + igraph_vector_int_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_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t 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, +igraph_error_t igraph_i_attribute_get_bool_edge_attr(const igraph_t *graph, const char *name, igraph_es_t es, igraph_vector_bool_t *value); +__END_DECLS + #endif /* IGRAPH_GRAPH_ATTRIBUTES_H */ diff --git a/src/vendor/cigraph/src/graph/basic_query.c b/src/vendor/cigraph/src/graph/basic_query.c index 34c4ef48ae5..3681131eb07 100644 --- a/src/vendor/cigraph/src/graph/basic_query.c +++ b/src/vendor/cigraph/src/graph/basic_query.c @@ -29,34 +29,33 @@ /** * \ingroup structural * \function igraph_are_connected - * \brief Decides whether two vertices are connected + * \brief Decides whether two vertices are connected. + * + * This function is of course symmetric for undirected graphs. * * \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. + * \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_error_t 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 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_ERROR("Invalid vertex ID when checking if two vertices are connected.", IGRAPH_EINVVID); } - igraph_get_eid(graph, &eid, v1, v2, /*directed=*/1, /*error=*/ 0); + igraph_get_eid(graph, &eid, v1, v2, IGRAPH_DIRECTED, /*error=*/ false); *res = (eid >= 0); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/graph/caching.c b/src/vendor/cigraph/src/graph/caching.c new file mode 100644 index 00000000000..fb0f52d387a --- /dev/null +++ b/src/vendor/cigraph/src/graph/caching.c @@ -0,0 +1,185 @@ +/* + 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, see . +*/ + +#include "igraph_interface.h" + +#include "graph/caching.h" + +#include + +/****** Strictly internal functions ******/ + +/** + * \brief Initializes a property cache, ensuring that all values are unknown. + */ +igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache) { + IGRAPH_STATIC_ASSERT(IGRAPH_PROP_I_SIZE <= 32); + + memset(cache->value, 0, sizeof(cache->value) / sizeof(cache->value[0])); + cache->known = 0; + return IGRAPH_SUCCESS; +} + +/** + * \brief Copies a property cache. + */ +igraph_error_t igraph_i_property_cache_copy( + igraph_i_property_cache_t *cache, + const igraph_i_property_cache_t *other_cache) { + *cache = *other_cache; + return IGRAPH_SUCCESS; +} + +/** + * \brief Destroys a property cache. + */ +void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache) { + IGRAPH_UNUSED(cache); + /* Nothing to do */ +} + +/***** Developer fuctions, exposed *****/ + +/** + * \brief Returns the value of a cached boolean property. + * + * This function provides valid results only when the property is already + * cached. Use \ref igraph_i_property_cache_has() to retrieve whether the + * property is cached. + * + * \param graph the graph whose cache is to be checked + * \param prop the property to retrieve from the cache + * \return the cached value of the property if the value is in the cache, or + * an undefined value otherwise + */ +igraph_bool_t igraph_i_property_cache_get_bool(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + return graph->cache->value[prop]; +} + +/** + * \brief Returns whether the cache contains a value for the given cached property. + * + * \param graph the graph whose cache is to be checked + * \param prop the property to check in the cache + */ +igraph_bool_t igraph_i_property_cache_has(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + return graph->cache->known & (1 << prop); +} + +/** + * \brief Stores a property value in the cache. + * + * \param graph the graph whose cache is to be modified + * \param prop the property to update in the cache + * \param value the value of the property to add to the cache + */ +void igraph_i_property_cache_set_bool(const igraph_t *graph, igraph_cached_property_t prop, igraph_bool_t value) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + /* Even though graph is const, updating the cache is not considered modification. + * Functions that merely compute graph properties, and thus leave the graph structure + * intact, will often update the cache. */ + graph->cache->value[prop] = value; + graph->cache->known |= (1 << prop); +} + +/** + * \brief Invalidates the cached value of a property in a graph. + * + * \param graph the graph whose cache is to be modified + * \param prop the property to invalidate in the cache + */ +void igraph_i_property_cache_invalidate(const igraph_t *graph, igraph_cached_property_t prop) { + IGRAPH_ASSERT(prop >= 0 && prop < IGRAPH_PROP_I_SIZE); + assert(graph->cache != NULL); + graph->cache->known &= ~(1 << prop); +} + +/** + * \brief Invalidates all cached properties of the graph. + * + * This function is typically called after the graph is modified. + * + * \param graph the graph whose cache is to be invalidated + */ +void igraph_i_property_cache_invalidate_all(const igraph_t *graph) { + assert(graph->cache != NULL); + graph->cache->known = 0; +} + +/** + * \brief Invalidates all but a few cached properties of the graph, subject to specific conditions. + * + * This function is typically called after the graph is modified if we know that + * the modification does not affect certain cached properties in certain cases. + * For instance, adding more vertices does not make a connected graph disconnected, + * so we can keep the cached properties related to graph connectivity if they + * were already cached as true, but we need to invalidate them if they were + * cached as false. + * + * + * Use 1 << IGRAPH_PROP_SOMETHING to encode an individual property + * in the bits of the bitmask used in the arguments of this function. + * + * \param graph the graph whose cache is to be invalidated + * \param keep_always bitmask where the i-th bit corresponds to cached property \em i + * and it should be set to 1 if the property should be \em kept , + * irrespectively of its current cached value. + */ +void igraph_i_property_cache_invalidate_conditionally( + const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, + uint32_t keep_when_true +) { + uint32_t invalidate = ~keep_always; + uint32_t mask; + uint32_t maybe_keep; + igraph_bool_t cached_value; + + assert(graph->cache != NULL); + + /* The bits of maybe_keep are set to 1 for those properties that are: + * + * - currently cached + * - should _probably_ be invalidated + * - _but_ the current cached value of the property may change the decision + */ + maybe_keep = graph->cache->known & invalidate & (keep_when_false | keep_when_true); + + if (maybe_keep) { + for (igraph_cached_property_t prop = (igraph_cached_property_t ) 0; prop < IGRAPH_PROP_I_SIZE; ++prop) { + mask = 1 << prop; + if (maybe_keep & mask) { + /* if we get here, we know that the property is cached; we have + * masked maybe_keep with graph->cache->known */ + cached_value = igraph_i_property_cache_get_bool(graph, prop); + if ( + ((keep_when_false & mask) && !cached_value) || + ((keep_when_true & mask) && cached_value) + ) { + invalidate &= ~mask; + } + } + } + } + + graph->cache->known &= ~invalidate; +} diff --git a/src/vendor/cigraph/src/graph/caching.h b/src/vendor/cigraph/src/graph/caching.h new file mode 100644 index 00000000000..3a3e1491ffa --- /dev/null +++ b/src/vendor/cigraph/src/graph/caching.h @@ -0,0 +1,52 @@ +/* + 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, see . +*/ + +#ifndef IGRAPH_CACHING_H +#define IGRAPH_CACHING_H + +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +#include "internal/hacks.h" + +#include /* memset */ + +__BEGIN_DECLS + +struct igraph_i_property_cache_t { + igraph_bool_t value[IGRAPH_PROP_I_SIZE]; + + /** Bit field that stores which of the properties are cached at the moment */ + uint32_t known; +}; + +igraph_error_t igraph_i_property_cache_init(igraph_i_property_cache_t *cache); +igraph_error_t igraph_i_property_cache_copy( + igraph_i_property_cache_t *cache, + const igraph_i_property_cache_t *other_cache); +void igraph_i_property_cache_destroy(igraph_i_property_cache_t *cache); + +void igraph_i_property_cache_invalidate_conditionally( + const igraph_t *graph, uint32_t keep_always, uint32_t keep_when_false, uint32_t keep_when_true +); + +__END_DECLS + +#endif /* IGRAPH_CACHING_H */ diff --git a/src/vendor/cigraph/src/graph/cattributes.c b/src/vendor/cigraph/src/graph/cattributes.c index 1a458c864d1..2768e186ecf 100644 --- a/src/vendor/cigraph/src/graph/cattributes.c +++ b/src/vendor/cigraph/src/graph/cattributes.c @@ -23,24 +23,24 @@ #include "igraph_attributes.h" #include "igraph_memory.h" -#include "core/math.h" #include "igraph_interface.h" #include "igraph_random.h" +#include "internal/hacks.h" /* strdup */ + #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. */ +/* An attribute is either a numeric vector (vector_t), a boolean vector + * (vector_bool_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 igraph_i_cattribute_t, which contains three vector_ptr_t's, each + * holding pointers to igraph_attribute_record_t objects. */ 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; + const char *name, igraph_integer_t *idx) { + igraph_integer_t i, n = igraph_vector_ptr_size(ptrvec); + igraph_bool_t l = false; for (i = 0; !l && i < n; i++) { igraph_attribute_record_t *rec = VECTOR(*ptrvec)[i]; l = !strcmp(rec->name, name); @@ -51,84 +51,140 @@ static igraph_bool_t igraph_i_cattribute_find(const igraph_vector_ptr_t *ptrvec, return l; } +/* + * Restores attribute vector lengths to their original size after a failure. + * This function assumes that none of the attribute vectors are shorter than origlen. + * Some may be longer due to a partially completed size extension: these will be + * shrunk to their original size. + */ +static void igraph_i_cattribute_revert_attribute_vector_sizes( + igraph_vector_ptr_t *attrlist, igraph_integer_t origlen) { + + igraph_integer_t no_of_attrs = igraph_vector_ptr_size(attrlist); + for (igraph_integer_t i = 0; i < no_of_attrs; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *nvec = (igraph_vector_t *) rec->value; + IGRAPH_ASSERT(igraph_vector_capacity(nvec) >= origlen); + igraph_vector_resize(nvec, origlen); /* shrinks */ + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + igraph_vector_bool_t *bvec = (igraph_vector_bool_t *) rec->value; + IGRAPH_ASSERT(igraph_vector_bool_capacity(bvec) >= origlen); + igraph_vector_bool_resize(bvec, origlen); /* shrinks */ + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *svec = (igraph_strvector_t *) rec->value; + IGRAPH_ASSERT(igraph_strvector_capacity(svec) >= origlen); + igraph_strvector_resize(svec, origlen); /* shrinks */ + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); + } + } +} + 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, +static igraph_error_t 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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newnum); - IGRAPH_CHECK(igraph_vector_copy(newnum, num)); + IGRAPH_CHECK(igraph_vector_init_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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newstr); - IGRAPH_CHECK(igraph_strvector_copy(newstr, str)); + IGRAPH_CHECK(igraph_strvector_init_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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newlog); - IGRAPH_CHECK(igraph_vector_bool_copy(newlog, log)); + IGRAPH_CHECK(igraph_vector_bool_init_copy(newlog, log)); IGRAPH_FINALLY(igraph_vector_bool_destroy, newlog); (*newrec)->value = newlog; } IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } +static void igraph_i_attribute_list_destroy(igraph_vector_ptr_t *attrlist) { + igraph_integer_t i; + igraph_integer_t n = igraph_vector_ptr_size(attrlist); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrlist)[i]; + if (rec) { + if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *num = (igraph_vector_t *) rec->value; + igraph_vector_destroy(num); + IGRAPH_FREE(num); + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *str = (igraph_strvector_t *) rec->value; + igraph_strvector_destroy(str); + IGRAPH_FREE(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(boolvec); + } + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec); + } + } + igraph_vector_ptr_destroy(attrlist); +} -static int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { +static igraph_error_t igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) { igraph_attribute_record_t *attr_rec; - long int i, n; + igraph_integer_t 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_ERROR("Can't init attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, nattr); IGRAPH_CHECK(igraph_vector_ptr_init(&nattr->gal, n)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->gal); + IGRAPH_FINALLY(igraph_i_attribute_list_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); + IGRAPH_FINALLY(igraph_vector_ptr_destroy, &nattr->eal); for (i = 0; i < n; i++) { IGRAPH_CHECK(igraph_i_cattributes_copy_attribute_record( @@ -137,58 +193,30 @@ static int igraph_i_cattribute_init(igraph_t *graph, igraph_vector_ptr_t *attr) } graph->attr = nattr; + IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } 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); - } - } + for (size_t a = 0; a < 3; a++) { + igraph_i_attribute_list_destroy(als[a]); } - 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; + IGRAPH_FREE(graph->attr); /* sets to NULL */ } /* 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_integer_t i, n; igraph_vector_t *num; igraph_strvector_t *str; igraph_vector_bool_t *boolvec; igraph_attribute_record_t *rec; - for (a = 0; a < 3; a++) { + for (size_t a = 0; a < 3; a++) { n = igraph_vector_ptr_size(als[a]); for (i = 0; i < n; i++) { rec = VECTOR(*als[a])[i]; @@ -198,18 +226,18 @@ static void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { if (rec->type == IGRAPH_ATTRIBUTE_NUMERIC) { num = (igraph_vector_t*)rec->value; igraph_vector_destroy(num); - igraph_free(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); + 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(str); } - igraph_free((char*)rec->name); - igraph_free(rec); + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec); } } } @@ -217,17 +245,17 @@ static void igraph_i_cattribute_copy_free(igraph_i_cattributes_t *attr) { /* 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, +static igraph_error_t 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_integer_t i, n; 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_ERROR("Cannot copy attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, attrto); IGRAPH_VECTOR_PTR_INIT_FINALLY(&attrto->gal, 0); @@ -237,7 +265,7 @@ static int igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from, 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++) { + for (size_t a = 0; a < 3; a++) { if (copy[a]) { n = igraph_vector_ptr_size(alfrom[a]); IGRAPH_CHECK(igraph_vector_ptr_resize(alto[a], n)); @@ -252,31 +280,31 @@ static int igraph_i_cattribute_copy(igraph_t *to, const igraph_t *from, } IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, - igraph_vector_ptr_t *nattr) { +static igraph_error_t igraph_i_cattribute_add_vertices_inner(igraph_t *graph, igraph_integer_t 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; + igraph_integer_t length = igraph_vector_ptr_size(val); + igraph_integer_t nattrno = nattr == NULL ? 0 : igraph_vector_ptr_size(nattr); + igraph_integer_t origlen = igraph_vcount(graph) - nv; + igraph_integer_t newattrs = 0, i; + igraph_vector_int_t news; /* First add the new attributes if any */ newattrs = 0; - IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + IGRAPH_VECTOR_INT_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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, nname, &j); if (!l) { newattrs++; - IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != @@ -289,23 +317,23 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, /* 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 *tmp = VECTOR(*nattr)[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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newrec); newrec->type = type; newrec->name = strdup(tmp->name); if (!newrec->name) { - IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); @@ -314,7 +342,7 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); @@ -322,13 +350,12 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newbool); - IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); newrec->value = newbool; - igraph_vector_bool_fill(newbool, 0); + igraph_vector_bool_fill(newbool, false); } IGRAPH_CHECK(igraph_vector_ptr_push_back(val, newrec)); IGRAPH_FINALLY_CLEAN(4); @@ -341,8 +368,8 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, 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; + igraph_integer_t j = -1; + igraph_bool_t l = false; if (nattr) { l = igraph_i_cattribute_find(nattr, name, &j); } @@ -359,29 +386,29 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, 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); + 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_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_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_ERROR("Invalid boolean attribute length.", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); break; default: - IGRAPH_WARNING("Invalid attribute type"); + IGRAPH_WARNING("Invalid attribute type."); break; } } else { @@ -412,14 +439,42 @@ static int igraph_i_cattribute_add_vertices(igraph_t *graph, long int nv, } } - igraph_vector_destroy(&news); + igraph_vector_int_destroy(&news); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static void igraph_i_cattribute_permute_free(igraph_vector_ptr_t *v) { - long int i, n = igraph_vector_ptr_size(v); +static igraph_error_t igraph_i_cattribute_add_vertices(igraph_t *graph, igraph_integer_t nv, + igraph_vector_ptr_t *nattr) { + /* Record information needed to restore attribute vector sizes */ + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_integer_t origlen = igraph_vcount(graph) - nv; + + /* Attempt adding attributes */ + igraph_error_t err = igraph_i_cattribute_add_vertices_inner(graph, nv, nattr); + if (err != IGRAPH_SUCCESS) { + /* If unsuccessful, revert attribute vector sizes. + * The following function assumes that all attributes vectors that + * are present have a length at least as great as origlen. + * This is true at the moment because any new attributes that are + * added to the graph are created directly at 'origlen' instead of + * being created at smaller sizes and resized later. + * + * NOTE: While this ensures that all attribute vector lengths are + * correct, it does not ensure that no extra attributes have + * been added to the graph. However, the presence of extra + * attributes does not make the attribute table inconsistent + * like the incorrect attribute vector lengths would. + */ + igraph_i_cattribute_revert_attribute_vector_sizes(val, origlen); + } + return err; +} + +static void igraph_i_cattribute_clear_attribute_container(igraph_vector_ptr_t *v) { + igraph_integer_t 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); @@ -441,180 +496,360 @@ static void igraph_i_cattribute_permute_free(igraph_vector_ptr_t *v) { igraph_vector_ptr_clear(v); } -static int igraph_i_cattribute_permute_vertices(const igraph_t *graph, - igraph_t *newgraph, - const igraph_vector_t *idx) { +typedef struct { + igraph_vector_t *numeric; + igraph_vector_bool_t *boolean; + igraph_vector_ptr_t *strings; + igraph_integer_t length; +} igraph_i_attribute_permutation_work_area_t; + +static igraph_error_t igraph_i_attribute_permutation_work_area_init( + igraph_i_attribute_permutation_work_area_t *work_area, igraph_integer_t length +) { + work_area->length = length; + work_area->numeric = NULL; + work_area->boolean = NULL; + work_area->strings = NULL; + return IGRAPH_SUCCESS; +} - if (graph == newgraph) { +static void igraph_i_attribute_permutation_work_area_release_stored_strvectors( + igraph_i_attribute_permutation_work_area_t *work_area +) { + if (work_area->strings != NULL) { + igraph_vector_ptr_destroy_all(work_area->strings); + IGRAPH_FREE(work_area->strings); + work_area->strings = NULL; + } +} - 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"); - } +static void igraph_i_attribute_permutation_work_area_destroy( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_i_attribute_permutation_work_area_release_stored_strvectors(work_area); + if (work_area->numeric != NULL) { + igraph_vector_destroy(work_area->numeric); + IGRAPH_FREE(work_area->numeric); + work_area->numeric = NULL; + } + if (work_area->boolean != NULL) { + igraph_vector_bool_destroy(work_area->boolean); + IGRAPH_FREE(work_area->boolean); + work_area->boolean = NULL; + } +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_numeric( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_t* vec = work_area->numeric; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_init(vec, work_area->length)); + work_area->numeric = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_boolean( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_bool_t* vec = work_area->boolean; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_bool_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_bool_init(vec, work_area->length)); + work_area->boolean = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_alloc_for_strings( + igraph_i_attribute_permutation_work_area_t *work_area +) { + igraph_vector_ptr_t* vec = work_area->strings; + + if (vec == NULL) { + vec = IGRAPH_CALLOC(1, igraph_vector_ptr_t); + IGRAPH_CHECK_OOM(vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_ptr_init(vec, 0)); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(vec, igraph_strvector_destroy); + work_area->strings = vec; + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + igraph_i_attribute_permutation_work_area_t *work_area, + const igraph_strvector_t *vec, + const igraph_vector_int_t *idx +) { + igraph_strvector_t *new_vec; + + new_vec = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(new_vec, "Cannot permute attributes"); + IGRAPH_FINALLY(igraph_free, new_vec); + IGRAPH_CHECK(igraph_strvector_init(new_vec, 0)); + IGRAPH_FINALLY(igraph_strvector_destroy, new_vec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(work_area->strings, new_vec)); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_strvector_index(vec, new_vec, idx)); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_permute_vertices_in_place( + igraph_t *graph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *val = &attr->val; + igraph_integer_t valno = igraph_vector_ptr_size(val); + igraph_integer_t i, j; + igraph_attribute_record_t *oldrec; + igraph_vector_t *num, *num_work; + igraph_strvector_t *str, str_work; + igraph_vector_bool_t *oldbool, *bool_work; + igraph_i_attribute_permutation_work_area_t work_area; + igraph_integer_t idx_size = igraph_vector_int_size(idx); + + /* shortcut: don't allocate anything if there are no attributes */ + if (valno == 0) { + return IGRAPH_SUCCESS; + } + + /* do all the allocations that can potentially fail before we actually + * start to permute the vertices to ensure that we will not ever need to + * back out from a permutation once we've started it */ + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); + IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); + break; + + default: + IGRAPH_WARNING("Unknown vertex 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; + /* let's do string attributes first because these might need extra + * allocations that can fail. The strategy is to build new igraph_strvector_t + * instances for the permuted attributes and store them in an + * igraph_vector_ptr_t until we are done with all of them. If any of the + * allocations fail, we can destroy the igraph_vector_ptr_t safely */ + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } - /* 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); + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK( + igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + &work_area, str, idx + ) + ); + } + + /* strings are done, and now all vectors involved in the process are + * as large as they should be (or larger) so the operations below are not + * supposed to fail. We can safely replace the original string attribute + * vectors with the permuted ones, and then proceed to the remaining + * attributes */ + for (i = 0, j = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; } - IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); - IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); + str = (igraph_strvector_t*) oldrec->value; + str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); + *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; + *str = str_work; + j++; + } + igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); - 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; + for (i = 0; i < valno; i++) { + oldrec = VECTOR(*val)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + num_work = work_area.numeric; + IGRAPH_ASSERT(num_work != NULL); + IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); + work_area.numeric = num; + oldrec->value = num_work; + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + bool_work = work_area.boolean; + IGRAPH_ASSERT(bool_work != NULL); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); + work_area.boolean = oldbool; + oldrec->value = bool_work; + break; + case IGRAPH_ATTRIBUTE_STRING: + /* nothing to do */ + break; + default: + /* already warned */ + break; + } + } - /* 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; + igraph_i_attribute_permutation_work_area_destroy(&work_area); + IGRAPH_FINALLY_CLEAN(1); - /* 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"); + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_cattribute_permute_vertices( + const igraph_t *graph, igraph_t *newgraph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; + igraph_vector_ptr_t *val = &attr->val, *new_val = &new_attr->val; + igraph_integer_t i, valno; + + IGRAPH_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_val)); + + /* Handle in-place permutation separately */ + if (graph == newgraph) { + return igraph_i_cattribute_permute_vertices_in_place(newgraph, idx); + } + + /* New vertex attributes */ + valno = igraph_vector_ptr_size(val); + IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, valno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, new_rec); + new_rec->name = strdup(oldrec->name); + if (! new_rec->name) { + IGRAPH_ERROR("Cannot create vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); + new_rec->type = oldrec->type; + + /* 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); + new_rec->value = newnum; + 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); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); + new_rec->value = newbool; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); + IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); + new_rec->value = newstr; + break; + default: + IGRAPH_WARNING("Unknown vertex attribute ignored"); } + + VECTOR(*new_val)[i] = new_rec; + IGRAPH_FINALLY_CLEAN(4); } IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } -typedef int igraph_cattributes_combine_num_t(const igraph_vector_t *input, +typedef igraph_error_t 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, +typedef igraph_error_t 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, +typedef igraph_error_t 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, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); for (j = 0; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + igraph_integer_t x = VECTOR(*idx)[j]; s += VECTOR(*oldv)[x]; } VECTOR(*newv)[i] = s; @@ -623,29 +858,29 @@ static int igraph_i_cattributes_cn_sum(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); for (j = 0; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + igraph_integer_t x = VECTOR(*idx)[j]; s *= VECTOR(*oldv)[x]; } VECTOR(*newv)[i] = s; @@ -654,30 +889,30 @@ static int igraph_i_cattributes_cn_prod(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : nan; for (j = 1; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + igraph_integer_t x = VECTOR(*idx)[j]; igraph_real_t val = VECTOR(*oldv)[x]; if (val < m) { m = val; @@ -689,30 +924,30 @@ static int igraph_i_cattributes_cn_min(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); + igraph_real_t m = n > 0 ? VECTOR(*oldv)[ VECTOR(*idx)[0] ] : nan; for (j = 1; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + igraph_integer_t x = VECTOR(*idx)[j]; igraph_real_t val = VECTOR(*oldv)[x]; if (val > m) { m = val; @@ -724,21 +959,21 @@ static int igraph_i_cattributes_cn_max(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); @@ -746,15 +981,15 @@ static int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldre RNG_BEGIN(); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - long int n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = nan; } else if (n == 1) { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; } else { - long int r = RNG_INTEGER(0, n - 1); - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; + igraph_integer_t r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; } } @@ -763,94 +998,94 @@ static int igraph_i_cattributes_cn_random(const igraph_attribute_record_t *oldre IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_first(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = nan; } else { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_last(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = nan; } else { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_real_t nan = IGRAPH_NAN; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_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]; + igraph_integer_t x = VECTOR(*idx)[j]; s += VECTOR(*oldv)[x]; } if (n > 0) { @@ -862,37 +1097,36 @@ static int igraph_i_cattributes_cn_mean(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); - if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); - } + igraph_vector_t *newv = IGRAPH_CALLOC(1, igraph_vector_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); IGRAPH_FINALLY(igraph_free, newv); IGRAPH_VECTOR_INIT_FINALLY(newv, newlen); + igraph_vector_t values; 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; + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + igraph_integer_t n = igraph_vector_int_size(idx); IGRAPH_CHECK(igraph_vector_resize(&values, n)); - for (j = 0; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; VECTOR(values)[j] = VECTOR(*oldv)[x]; } + + igraph_real_t res; IGRAPH_CHECK(func(&values, &res)); VECTOR(*newv)[i] = res; } @@ -901,37 +1135,36 @@ static int igraph_i_cattributes_cn_func(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_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); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = 0; } else if (n == 1) { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; } else { - long int r = RNG_INTEGER(0, n - 1); - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[r] ]; + igraph_integer_t r = RNG_INTEGER(0, n - 1); + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[r] ]; } } @@ -940,95 +1173,92 @@ static int igraph_i_cattributes_cb_random(const igraph_attribute_record_t *oldre IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_first(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - long int n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = 0; } else { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[0] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[0] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_last(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - long int n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); if (n == 0) { VECTOR(*newv)[i] = 0; } else { - VECTOR(*newv)[i] = VECTOR(*oldv)[ (long int) VECTOR(*idx)[n - 1] ]; + VECTOR(*newv)[i] = VECTOR(*oldv)[ VECTOR(*idx)[n - 1] ]; } } IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + n = igraph_vector_int_size(idx); VECTOR(*newv)[i] = 1; for (j = 0; j < n; j++) { - x = (long int) VECTOR(*idx)[j]; + x = VECTOR(*idx)[j]; if (!VECTOR(*oldv)[x]) { VECTOR(*newv)[i] = 0; break; @@ -1039,31 +1269,30 @@ static int igraph_i_cattributes_cb_all_is_true(const igraph_attribute_record_t * IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + n = igraph_vector_int_size(idx); VECTOR(*newv)[i] = 0; for (j = 0; j < n; j++) { - x = (long int) VECTOR(*idx)[j]; + x = VECTOR(*idx)[j]; if (VECTOR(*oldv)[x]) { VECTOR(*newv)[i] = 1; break; @@ -1074,35 +1303,34 @@ static int igraph_i_cattributes_cb_any_is_true(const igraph_attribute_record_t * IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *oldrec, +static igraph_error_t 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_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i, j, n, x, num_trues; if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); RNG_BEGIN(); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; - n = igraph_vector_size(idx); + n = igraph_vector_int_size(idx); num_trues = 0; for (j = 0; j < n; j++) { - x = (long int) VECTOR(*idx)[j]; + x = VECTOR(*idx)[j]; if (VECTOR(*oldv)[x]) { num_trues++; } @@ -1124,41 +1352,36 @@ static int igraph_i_cattributes_cb_majority(const igraph_attribute_record_t *old IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); - if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); - } + igraph_vector_bool_t *newv = IGRAPH_CALLOC(1, igraph_vector_bool_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_vector_bool_init(newv, newlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newv, newlen); - IGRAPH_CHECK(igraph_vector_bool_init(&values, 0)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newv); + igraph_vector_bool_t values; + IGRAPH_VECTOR_BOOL_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_bool_t res; + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); IGRAPH_CHECK(igraph_vector_bool_resize(&values, n)); - for (j = 0; j < n; j++) { - long int x = (long int) VECTOR(*idx)[j]; + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; VECTOR(values)[j] = VECTOR(*oldv)[x]; } + igraph_bool_t res; IGRAPH_CHECK(func(&values, &res)); VECTOR(*newv)[i] = res; } @@ -1167,39 +1390,38 @@ static int igraph_i_cattributes_cb_func(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges) { + const igraph_vector_int_list_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - long int newlen = igraph_vector_ptr_size(merges); - long int i; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); + igraph_integer_t i; igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); - IGRAPH_FINALLY(igraph_strvector_destroy, newv); + IGRAPH_STRVECTOR_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); - char *tmp; + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_size(idx); + const char *tmp; if (n == 0) { IGRAPH_CHECK(igraph_strvector_set(newv, i, "")); } else if (n == 1) { - igraph_strvector_get(oldv, 0, &tmp); + tmp = igraph_strvector_get(oldv, 0); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } else { - long int r = RNG_INTEGER(0, n - 1); - igraph_strvector_get(oldv, r, &tmp); + igraph_integer_t r = RNG_INTEGER(0, n - 1); + tmp = igraph_strvector_get(oldv, r); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1209,32 +1431,30 @@ static int igraph_i_cattributes_sn_random(const igraph_attribute_record_t *oldre IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges) { + const igraph_vector_int_list_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - long int i, newlen = igraph_vector_ptr_size(merges); + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); - IGRAPH_FINALLY(igraph_strvector_destroy, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - long int n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_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); + const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[0]); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1242,32 +1462,30 @@ static int igraph_i_cattributes_sn_first(const igraph_attribute_record_t *oldrec IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges) { + const igraph_vector_int_list_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - long int i, newlen = igraph_vector_ptr_size(merges); + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); - IGRAPH_FINALLY(igraph_strvector_destroy, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); for (i = 0; i < newlen; i++) { - igraph_vector_t *idx = VECTOR(*merges)[i]; - long int n = igraph_vector_size(idx); + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t n = igraph_vector_int_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); + const char *tmp = igraph_strvector_get(oldv, VECTOR(*idx)[n - 1]); IGRAPH_CHECK(igraph_strvector_set(newv, i, tmp)); } } @@ -1275,41 +1493,41 @@ static int igraph_i_cattributes_sn_last(const igraph_attribute_record_t *oldrec, IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges) { + const igraph_vector_int_list_t *merges) { const igraph_strvector_t *oldv = oldrec->value; - long int i, newlen = igraph_vector_ptr_size(merges); + igraph_integer_t i, newlen = igraph_vector_int_list_size(merges); igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); - IGRAPH_FINALLY(igraph_strvector_destroy, newv); + IGRAPH_STRVECTOR_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_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + igraph_integer_t j, n = igraph_vector_int_size(idx); size_t len = 0; - char *tmp, *tmp2; + const char *tmp; + char *tmp2; for (j = 0; j < n; j++) { - igraph_strvector_get(oldv, j, &tmp); + tmp = igraph_strvector_get(oldv, j); len += strlen(tmp); } tmp2 = IGRAPH_CALLOC(len + 1, char); if (!tmp2) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, tmp2); len = 0; for (j = 0; j < n; j++) { - igraph_strvector_get(oldv, j, &tmp); + tmp = igraph_strvector_get(oldv, j); strcpy(tmp2 + len, tmp); len += strlen(tmp); } @@ -1322,131 +1540,160 @@ static int igraph_i_cattributes_sn_concat(const igraph_attribute_record_t *oldre IGRAPH_FINALLY_CLEAN(2); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, +static igraph_error_t igraph_i_cattributes_sn_func(const igraph_attribute_record_t *oldrec, igraph_attribute_record_t *newrec, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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; + igraph_integer_t newlen = igraph_vector_int_list_size(merges); - if (!newv) { - IGRAPH_ERROR("Cannot combine attributes", IGRAPH_ENOMEM); - } + igraph_strvector_t *newv = IGRAPH_CALLOC(1, igraph_strvector_t); + IGRAPH_CHECK_OOM(newv, "Cannot combine attributes."); IGRAPH_FINALLY(igraph_free, newv); - IGRAPH_CHECK(igraph_strvector_init(newv, newlen)); - IGRAPH_FINALLY(igraph_strvector_destroy, newv); + IGRAPH_STRVECTOR_INIT_FINALLY(newv, newlen); - IGRAPH_CHECK(igraph_strvector_init(newv, 0)); - IGRAPH_FINALLY(igraph_strvector_destroy, &values); + igraph_strvector_t values; + IGRAPH_STRVECTOR_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); - char *res; + for (igraph_integer_t i = 0; i < newlen; i++) { + igraph_vector_int_t *idx = igraph_vector_int_list_get_ptr(merges, i);; + + igraph_integer_t n = igraph_vector_int_size(idx); 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); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t x = VECTOR(*idx)[j]; + const char *elem = igraph_strvector_get(oldv, x); IGRAPH_CHECK(igraph_strvector_set(newv, j, elem)); } + + char *res; 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_FINALLY_CLEAN(1); } igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(3); newrec->value = newv; - return 0; + return IGRAPH_SUCCESS; } +/** + * \section c_attribute_combination_functions + * + * + * The C attribute handler supports combining the attributes of multiple + * vertices of edges into a single attribute during a vertex or edge contraction + * operation via a user-defined function. This is achieved by setting the + * type of the attribute combination to \c IGRAPH_ATTRIBUTE_COMBINE_FUNCTION + * and passing in a pointer to the custom combination function when specifying + * attribute combinations in \ref igraph_attribute_combination() or + * \ref igraph_attribute_combination_add() . For the C attribute handler, the + * signature of the function depends on the type of the underlying attribute. + * For numeric attributes, use: + * \verbatim igraph_error_t function(const igraph_vector_t *input, igraph_real_t *output); \endverbatim + * where \p input will receive a vector containing the value of the attribute + * for all the vertices or edges being combined, and \p output must be filled + * by the function to the combined value. Similarly, for Boolean attributes, the + * function takes a boolean vector in \p input and must return the combined Boolean + * value in \p output: + * \verbatim igraph_error_t function(const igraph_vector_bool_t *input, igraph_bool_t *output); \endverbatim + * For string attributes, the signature is slightly different: + * \verbatim igraph_error_t function(const igraph_strvector_t *input, char **output); \endverbatim + * In case of strings, all strings in the input vector are \em owned by igraph + * and must not be modified or freed in the combination handler. The string + * returned to the caller in \p output remains owned by the caller; igraph will + * make a copy it and store the copy in the appropriate part of the data + * structure holding the vertex or edge attributes. + * + */ -static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, +typedef struct { + igraph_attribute_combination_type_t type; + union { + igraph_function_pointer_t as_void; + igraph_cattributes_combine_num_t *as_num; + igraph_cattributes_combine_str_t *as_str; + igraph_cattributes_combine_bool_t *as_bool; + } func; +} igraph_attribute_combination_todo_item_t; + +static igraph_error_t igraph_i_cattribute_combine_vertices(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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; + igraph_integer_t valno = igraph_vector_ptr_size(val); + igraph_integer_t i, j, keepno = 0; + igraph_attribute_combination_todo_item_t *todo_items; - 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_ASSERT(graph != newgraph); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_val)); + + todo_items = IGRAPH_CALLOC(valno, igraph_attribute_combination_todo_item_t); + if (!todo_items) { IGRAPH_ERROR("Cannot combine vertex attributes", - IGRAPH_ENOMEM); + IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - IGRAPH_FINALLY(igraph_free, funcs); + IGRAPH_FINALLY(igraph_free, todo_items); 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_attribute_combination_type_t type; 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) { + IGRAPH_CHECK(igraph_attribute_combination_query(comb, name, &type, &voidfunc)); + todo_items[i].type = type; + todo_items[i].func.as_void = voidfunc; + if (type != IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { keepno++; } } IGRAPH_CHECK(igraph_vector_ptr_resize(new_val, keepno)); - IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_val); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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) { + igraph_attribute_combination_todo_item_t todo_item = todo_items[i]; + igraph_attribute_type_t attr_type = oldrec->type; + + if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { continue; } newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); if (!newrec) { - IGRAPH_ERROR("Cannot combine vertex attributes", - IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, newrec); newrec->name = strdup(name); - newrec->type = type; - VECTOR(*new_val)[j] = newrec; + if (!newrec->name) { + IGRAPH_ERROR("Cannot combine vertex attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) newrec->name); + newrec->type = attr_type; - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - switch (todo) { + if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, - numfunc)); + todo_item.func.as_num)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); @@ -1485,11 +1732,11 @@ static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { - switch (todo) { + } else if (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, - boolfunc)); + todo_item.func.as_bool)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: case IGRAPH_ATTRIBUTE_COMBINE_MAX: @@ -1521,11 +1768,11 @@ static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - switch (todo) { + } else if (attr_type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, - strfunc)); + todo_item.func.as_str)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); @@ -1571,103 +1818,41 @@ static int igraph_i_cattribute_combine_vertices(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); } + VECTOR(*new_val)[j] = newrec; + IGRAPH_FINALLY_CLEAN(2); /* newrec->name and newrec */ + j++; } - igraph_free(funcs); - igraph_free(TODO); - igraph_i_cattribute_permute_free(new_val); - IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FREE(todo_items); + IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -/* 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) { +static igraph_error_t igraph_i_cattribute_add_edges_inner(igraph_t *graph, const igraph_vector_int_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; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t ne = igraph_vector_int_size(edges) / 2; + igraph_integer_t origlen = igraph_ecount(graph) - ne; + igraph_integer_t nattrno = nattr == 0 ? 0 : igraph_vector_ptr_size(nattr); + igraph_vector_int_t news; + igraph_integer_t newattrs, i; /* First add the new attributes if any */ newattrs = 0; - IGRAPH_VECTOR_INIT_FINALLY(&news, 0); + IGRAPH_VECTOR_INT_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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, nname, &j); if (!l) { newattrs++; - IGRAPH_CHECK(igraph_vector_push_back(&news, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&news, i)); } else { /* check types */ if (nattr_entry->type != @@ -1677,26 +1862,26 @@ static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t } } - /* Add NA/empty string vectors for the existing vertices */ + /* Add NaN/false/"" for the existing vertices for numeric, boolean and string attributes. */ 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 *tmp = VECTOR(*nattr)[ 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newrec); newrec->type = type; newrec->name = strdup(tmp->name); if (!newrec->name) { - IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newnum); IGRAPH_VECTOR_INIT_FINALLY(newnum, origlen); @@ -1705,17 +1890,16 @@ static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newbool); - IGRAPH_CHECK(igraph_vector_bool_init(newbool, origlen)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, origlen); newrec->value = newbool; - igraph_vector_bool_fill(newbool, 0); + igraph_vector_bool_fill(newbool, false); } 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_ERROR("Cannot add attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, newstr); IGRAPH_STRVECTOR_INIT_FINALLY(newstr, origlen); @@ -1730,10 +1914,10 @@ static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t /* 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; + igraph_attribute_record_t *newrec = NULL; const char *name = oldrec->name; - long int j = -1; - igraph_bool_t l = 0; + igraph_integer_t j = -1; + igraph_bool_t l = false; if (nattr) { l = igraph_i_cattribute_find(nattr, name, &j); } @@ -1750,33 +1934,33 @@ static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t 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); + 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_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_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_ERROR("Invalid boolean attribute length.", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_bool_append(oldbool, newbool)); break; default: - IGRAPH_WARNING("Invalid attribute type"); + IGRAPH_WARNING("Invalid attribute type."); break; } } else { - /* No such attribute, append NA's */ + /* No such attribute, append NaN/false/"". */ 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; @@ -1803,261 +1987,316 @@ static int igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_t } } - igraph_vector_destroy(&news); + igraph_vector_int_destroy(&news); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -/* 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) { +static igraph_error_t igraph_i_cattribute_add_edges(igraph_t *graph, const igraph_vector_int_t *edges, + igraph_vector_ptr_t *nattr) { + /* Record information needed to restore attribute vector sizes */ + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_integer_t ne = igraph_vector_int_size(edges) / 2; + igraph_integer_t origlen = igraph_ecount(graph) - ne; + + /* Attempt adding attributes */ + igraph_error_t err = igraph_i_cattribute_add_edges_inner(graph, edges, nattr); + if (err != IGRAPH_SUCCESS) { + /* If unsuccessful, revert attribute vector sizes. + * The following function assumes that all attributes vectors that + * are present have a length at least as great as origlen. + * This is true at the moment because any new attributes that are + * added to the graph are created directly at 'origlen' instead of + * being created at smaller sizes and resized later. + * + * NOTE: While this ensures that all attribute vector lengths are + * correct, it does not ensure that no extra attributes have + * been added to the graph. However, the presence of extra + * attributes does not make the attribute table inconsistent + * like the incorrect attribute vector lengths would. + */ + igraph_i_cattribute_revert_attribute_vector_sizes(eal, origlen); + } + return err; +} - if (graph == newgraph) { +static igraph_error_t igraph_i_cattribute_permute_edges_in_place( + igraph_t *graph, const igraph_vector_int_t *idx +) { + igraph_i_cattributes_t *attr = graph->attr; + igraph_vector_ptr_t *eal = &attr->eal; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t i, j; + igraph_attribute_record_t *oldrec; + igraph_vector_t *num, *num_work; + igraph_strvector_t *str, str_work; + igraph_vector_bool_t *oldbool, *bool_work; + igraph_i_attribute_permutation_work_area_t work_area; + igraph_integer_t idx_size = igraph_vector_int_size(idx); + + /* shortcut: don't allocate anything if there are no attributes */ + if (ealno == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_init(&work_area, idx_size)); + IGRAPH_FINALLY(igraph_i_attribute_permutation_work_area_destroy, &work_area); + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_reserve(num, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_numeric(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + IGRAPH_CHECK(igraph_vector_bool_reserve(oldbool, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_boolean(&work_area)); + break; + + case IGRAPH_ATTRIBUTE_STRING: + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK(igraph_strvector_reserve(str, idx_size)); + IGRAPH_CHECK(igraph_i_attribute_permutation_work_area_alloc_for_strings(&work_area)); + break; + + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); + } + } - 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"); - } + /* let's do string attributes first because these might need extra + * allocations that can fail. The strategy is to build new igraph_strvector_t + * instances for the permuted attributes and store them in an + * igraph_vector_ptr_t until we are done with all of them. If any of the + * allocations fail, we can destroy the igraph_vector_ptr_t safely */ + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; } - } else { + str = (igraph_strvector_t*) oldrec->value; + IGRAPH_CHECK( + igraph_i_attribute_permutation_work_area_permute_and_store_strvector( + &work_area, str, 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; + /* strings are done, and now all vectors involved in the process are + * as large as they should be (or larger) so the operations below are not + * supposed to fail. We can safely replace the original string attribute + * vectors with the permuted ones, and then proceed to the remaining + * attributes */ + for (i = 0, j = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + if (oldrec->type != IGRAPH_ATTRIBUTE_STRING) { + continue; + } - /* 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)); + str = (igraph_strvector_t*) oldrec->value; + str_work = *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]); + *((igraph_strvector_t*) VECTOR(*(work_area.strings))[j]) = *str; + *str = str_work; + j++; + } + igraph_i_attribute_permutation_work_area_release_stored_strvectors(&work_area); - IGRAPH_FINALLY(igraph_i_cattribute_permute_free, new_eal); + /* now all vectors involved in the process are as large as they should be + * (or larger) so the operations below are not supposed to fail -- except + * for string operations that still do some extra allocations and we are + * not prepared for the failures of those. This must still be fixed. */ + for (i = 0; i < ealno; i++) { + oldrec = VECTOR(*eal)[i]; + switch (oldrec->type) { + case IGRAPH_ATTRIBUTE_NUMERIC: + num = (igraph_vector_t*) oldrec->value; + num_work = work_area.numeric; + IGRAPH_ASSERT(num_work != NULL); + IGRAPH_CHECK(igraph_vector_index(num, num_work, idx)); + work_area.numeric = num; + oldrec->value = num_work; + break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + oldbool = (igraph_vector_bool_t*) oldrec->value; + bool_work = work_area.boolean; + IGRAPH_ASSERT(bool_work != NULL); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, bool_work, idx)); + work_area.boolean = oldbool; + oldrec->value = bool_work; + break; + case IGRAPH_ATTRIBUTE_STRING: + /* nothing to do */ + break; + default: + /* already warned */ + break; + } + } - 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; + igraph_i_attribute_permutation_work_area_destroy(&work_area); + IGRAPH_FINALLY_CLEAN(1); - /* 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; + return IGRAPH_SUCCESS; +} - 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"); +static igraph_error_t igraph_i_cattribute_permute_edges(const igraph_t *graph, + igraph_t *newgraph, + const igraph_vector_int_t *idx) { + + igraph_i_cattributes_t *attr = graph->attr, *new_attr = newgraph->attr; + igraph_vector_ptr_t *eal = &attr->eal, *new_eal = &new_attr->eal; + igraph_integer_t i, ealno; + + IGRAPH_ASSERT(graph == newgraph || igraph_vector_ptr_empty(new_eal)); + + if (graph == newgraph) { + return igraph_i_cattribute_permute_edges_in_place(newgraph, idx); + } + + /* New edge attributes */ + ealno = igraph_vector_ptr_size(eal); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); + IGRAPH_CHECK(igraph_vector_ptr_resize(new_eal, ealno)); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, new_rec); + new_rec->name = strdup(oldrec->name); + if (! new_rec->name) { + IGRAPH_ERROR("Cannot create edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) new_rec->name); + new_rec->type = oldrec->type; + + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newnum); + IGRAPH_VECTOR_INIT_FINALLY(newnum, 0); + IGRAPH_CHECK(igraph_vector_index(num, newnum, idx)); + new_rec->value = newnum; + 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); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, newstr); + IGRAPH_STRVECTOR_INIT_FINALLY(newstr, 0); + IGRAPH_CHECK(igraph_strvector_index(str, newstr, idx)); + new_rec->value = newstr; + 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); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, newbool); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(newbool, 0); + IGRAPH_CHECK(igraph_vector_bool_index(oldbool, newbool, idx)); + new_rec->value = newbool; + break; + default: + IGRAPH_WARNING("Unknown edge attribute ignored"); } - IGRAPH_FINALLY_CLEAN(1); + VECTOR(*new_eal)[i] = new_rec; + IGRAPH_FINALLY_CLEAN(4); } + IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_combine_edges(const igraph_t *graph, +static igraph_error_t igraph_i_cattribute_combine_edges(const igraph_t *graph, igraph_t *newgraph, - const igraph_vector_ptr_t *merges, + const igraph_vector_int_list_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; + igraph_integer_t ealno = igraph_vector_ptr_size(eal); + igraph_integer_t i, j, keepno = 0; + igraph_attribute_combination_todo_item_t *todo_items; - 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_ASSERT(graph != newgraph); + IGRAPH_ASSERT(igraph_vector_ptr_empty(new_eal)); + + todo_items = IGRAPH_CALLOC(ealno, igraph_attribute_combination_todo_item_t); + if (!todo_items) { + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - IGRAPH_FINALLY(igraph_free, funcs); + IGRAPH_FINALLY(igraph_free, todo_items); 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; + IGRAPH_CHECK(igraph_attribute_combination_query(comb, name, &todo, &voidfunc)); + todo_items[i].type = todo; + todo_items[i].func.as_void = 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); + IGRAPH_FINALLY(igraph_i_cattribute_clear_attribute_container, 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) { + igraph_attribute_combination_todo_item_t todo_item = todo_items[i]; + igraph_attribute_type_t attr_type = oldrec->type; + + if (todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_DEFAULT || + todo_item.type == IGRAPH_ATTRIBUTE_COMBINE_IGNORE) { continue; } newrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); if (!newrec) { - IGRAPH_ERROR("Cannot combine edge attributes", - IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, newrec); newrec->name = strdup(name); - newrec->type = type; - VECTOR(*new_eal)[j] = newrec; + if (! newrec->name) { + IGRAPH_ERROR("Cannot combine edge attributes", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) newrec->name); + newrec->type = attr_type; - if (type == IGRAPH_ATTRIBUTE_NUMERIC) { - switch (todo) { + if (attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cn_func(oldrec, newrec, merges, - numfunc)); + todo_item.func.as_num)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_CHECK(igraph_i_cattributes_cn_sum(oldrec, newrec, merges)); @@ -2096,11 +2335,11 @@ static int igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (type == IGRAPH_ATTRIBUTE_BOOLEAN) { - switch (todo) { + } else if (attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_cb_func(oldrec, newrec, merges, - boolfunc)); + todo_item.func.as_bool)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: case IGRAPH_ATTRIBUTE_COMBINE_MAX: @@ -2132,11 +2371,11 @@ static int igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); break; } - } else if (type == IGRAPH_ATTRIBUTE_STRING) { - switch (todo) { + } else if (attr_type == IGRAPH_ATTRIBUTE_STRING) { + switch (todo_item.type) { case IGRAPH_ATTRIBUTE_COMBINE_FUNCTION: IGRAPH_CHECK(igraph_i_cattributes_sn_func(oldrec, newrec, merges, - strfunc)); + todo_item.func.as_str)); break; case IGRAPH_ATTRIBUTE_COMBINE_SUM: IGRAPH_ERROR("Cannot sum strings", IGRAPH_EATTRCOMBINE); @@ -2182,41 +2421,43 @@ static int igraph_i_cattribute_combine_edges(const igraph_t *graph, IGRAPH_UNIMPLEMENTED); } + VECTOR(*new_eal)[j] = newrec; + IGRAPH_FINALLY_CLEAN(2); /* newrec and newrc->name */ + j++; } - igraph_free(funcs); - igraph_free(TODO); - IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FREE(todo_items); + IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_info(const igraph_t *graph, +static igraph_error_t igraph_i_cattribute_get_info(const igraph_t *graph, igraph_strvector_t *gnames, - igraph_vector_t *gtypes, + igraph_vector_int_t *gtypes, igraph_strvector_t *vnames, - igraph_vector_t *vtypes, + igraph_vector_int_t *vtypes, igraph_strvector_t *enames, - igraph_vector_t *etypes) { + igraph_vector_int_t *etypes) { igraph_strvector_t *names[3] = { gnames, vnames, enames }; - igraph_vector_t *types[3] = { gtypes, vtypes, etypes }; + igraph_vector_int_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; + igraph_integer_t i, j; for (i = 0; i < 3; i++) { igraph_strvector_t *n = names[i]; - igraph_vector_t *t = types[i]; + igraph_vector_int_t *t = types[i]; igraph_vector_ptr_t *al = attr[i]; - long int len = igraph_vector_ptr_size(al); + igraph_integer_t len = igraph_vector_ptr_size(al); if (n) { IGRAPH_CHECK(igraph_strvector_resize(n, len)); } if (t) { - IGRAPH_CHECK(igraph_vector_resize(t, len)); + IGRAPH_CHECK(igraph_vector_int_resize(t, len)); } for (j = 0; j < len; j++) { @@ -2232,7 +2473,7 @@ static int igraph_i_cattribute_get_info(const igraph_t *graph, } } - return 0; + return IGRAPH_SUCCESS; } static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, @@ -2240,7 +2481,7 @@ static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, 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; + igraph_integer_t attrnum; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -2260,17 +2501,17 @@ static igraph_bool_t igraph_i_cattribute_has_attr(const igraph_t *graph, return igraph_i_cattribute_find(attr[attrnum], name, 0); } -static int igraph_i_cattribute_gettype(const igraph_t *graph, +static igraph_error_t 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_integer_t 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; + igraph_integer_t j; + igraph_bool_t l = false; switch (elemtype) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -2295,21 +2536,21 @@ static int igraph_i_cattribute_gettype(const igraph_t *graph, rec = VECTOR(*al)[j]; *type = rec->type; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*gal)[j]; @@ -2320,21 +2561,21 @@ static int igraph_i_cattribute_get_numeric_graph_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_resize(value, 1)); VECTOR(*value)[0] = VECTOR(*num)[0]; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*gal)[j]; @@ -2345,21 +2586,21 @@ static int igraph_i_cattribute_get_bool_graph_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_bool_resize(value, 1)); VECTOR(*value)[0] = VECTOR(*log)[0]; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The graph attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*gal)[j]; @@ -2370,22 +2611,22 @@ static int igraph_i_cattribute_get_string_graph_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_strvector_resize(value, 1)); IGRAPH_CHECK(igraph_strvector_set(value, 0, STR(*str, 0))); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*val)[j]; @@ -2398,35 +2639,35 @@ static int igraph_i_cattribute_get_numeric_vertex_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_vit_t it; - long int i = 0; + igraph_integer_t 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); + igraph_integer_t v = IGRAPH_VIT_GET(it); VECTOR(*value)[i] = VECTOR(*num)[v]; } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*val)[j]; @@ -2449,22 +2690,22 @@ static int igraph_i_cattribute_get_bool_vertex_attr(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The vertex attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*val)[j]; @@ -2473,40 +2714,39 @@ static int igraph_i_cattribute_get_string_vertex_attr(const igraph_t *graph, } str = (igraph_strvector_t*)rec->value; if (igraph_vs_is_all(&vs)) { - igraph_strvector_resize(value, 0); + igraph_strvector_clear(value); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_vit_t it; - long int i = 0; + igraph_integer_t 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_integer_t v = IGRAPH_VIT_GET(it); + const char *s = igraph_strvector_get(str, v); IGRAPH_CHECK(igraph_strvector_set(value, i, s)); } igraph_vit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*eal)[j]; @@ -2519,34 +2759,34 @@ static int igraph_i_cattribute_get_numeric_edge_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_append(value, num)); } else { igraph_eit_t it; - long int i = 0; + igraph_integer_t 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); + igraph_integer_t e = IGRAPH_EIT_GET(it); VECTOR(*value)[i] = VECTOR(*num)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*eal)[j]; @@ -2555,40 +2795,39 @@ static int igraph_i_cattribute_get_string_edge_attr(const igraph_t *graph, } str = (igraph_strvector_t*)rec->value; if (igraph_es_is_all(&es)) { - igraph_strvector_resize(value, 0); + igraph_strvector_clear(value); IGRAPH_CHECK(igraph_strvector_append(value, str)); } else { igraph_eit_t it; - long int i = 0; + igraph_integer_t 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_integer_t e = IGRAPH_EIT_GET(it); + const char *s = igraph_strvector_get(str, e); IGRAPH_CHECK(igraph_strvector_set(value, i, s)); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph, +static igraph_error_t 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_integer_t 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); + IGRAPH_ERRORF("The edge attribute '%s' does not exist.", IGRAPH_EINVAL, name); } rec = VECTOR(*eal)[j]; @@ -2601,19 +2840,19 @@ static int igraph_i_cattribute_get_bool_edge_attr(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_bool_append(value, log)); } else { igraph_eit_t it; - long int i = 0; + igraph_integer_t 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); + igraph_integer_t e = IGRAPH_EIT_GET(it); VECTOR(*value)[i] = VECTOR(*log)[e]; } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /* -------------------------------------- */ @@ -2624,7 +2863,8 @@ const igraph_attribute_table_t igraph_cattribute_table = { &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_combine_vertices, + &igraph_i_cattribute_add_edges, &igraph_i_cattribute_permute_edges, &igraph_i_cattribute_combine_edges, &igraph_i_cattribute_get_info, @@ -2685,10 +2925,12 @@ const igraph_attribute_table_t igraph_cattribute_table = { /** * \function igraph_cattribute_GAN - * Query a numeric graph attribute. + * \brief Query a numeric graph attribute. * * Returns the value of the given numeric graph attribute. - * The attribute must exist, otherwise an error is triggered. + * If the attribute does not exist, a warning is issued + * and NaN is returned. + * * \param graph The input graph. * \param name The name of the attribute to query. * \return The value of the attribute. @@ -2701,14 +2943,14 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; } rec = VECTOR(*gal)[j]; @@ -2718,10 +2960,12 @@ igraph_real_t igraph_cattribute_GAN(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_GAB - * Query a boolean graph attribute. + * \brief Query a boolean graph attribute. + * + * Returns the value of the given boolean graph attribute. + * If the attribute does not exist, a warning is issued + * and false is returned. * - * 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. @@ -2734,14 +2978,14 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; } rec = VECTOR(*gal)[j]; @@ -2751,11 +2995,13 @@ igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_GAS - * Query a string graph attribute. + * \brief 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. + * specified in \p name. The value must not be modified. + * If the attribute does not exist, a warning is issued and + * an empty string is returned. + * * \param graph The input graph. * \param name The name of the attribute to query. * \return The value of the attribute. @@ -2764,18 +3010,18 @@ igraph_bool_t igraph_cattribute_GAB(const igraph_t *graph, const char *name) { * * Time complexity: O(Ag), the number of graph attributes. */ -const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name) { +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_integer_t 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; + IGRAPH_WARNINGF("Graph attribute '%s' does not exist, returning default string attribute value.", name); + return ""; } rec = VECTOR(*gal)[j]; @@ -2785,9 +3031,12 @@ const char* igraph_cattribute_GAS(const igraph_t *graph, const char *name) { /** * \function igraph_cattribute_VAN - * Query a numeric vertex attribute. + * \brief Query a numeric vertex attribute. + * + * If the attribute does not exist, a warning is issued and + * NaN is returned. See \ref igraph_cattribute_VANV() for + * an error-checked version. * - * 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. @@ -2801,26 +3050,29 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; } rec = VECTOR(*val)[j]; num = (igraph_vector_t*)rec->value; - return VECTOR(*num)[(long int)vid]; + return VECTOR(*num)[vid]; } /** * \function igraph_cattribute_VAB - * Query a boolean vertex attribute. + * \brief Query a boolean vertex attribute. + * + * If the vertex attribute does not exist, a warning is issued + * and false is returned. See \ref igraph_cattribute_VABV() for + * an error-checked version. * - * 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. @@ -2834,26 +3086,31 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; } rec = VECTOR(*val)[j]; log = (igraph_vector_bool_t*)rec->value; - return VECTOR(*log)[(long int)vid]; + return VECTOR(*log)[vid]; } /** * \function igraph_cattribute_VAS - * Query a string vertex attribute. + * \brief Query a string vertex attribute. + * + * Returns a const pointer to the string vertex attribute + * specified in \p name. The value must not be modified. + * If the vertex attribute does not exist, a warning is issued and + * an empty string is returned. See \ref igraph_cattribute_VASV() + * for an error-checked version. * - * 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. @@ -2863,30 +3120,33 @@ igraph_bool_t igraph_cattribute_VAB(const igraph_t *graph, const char *name, * * Time complexity: O(Av), the number of vertex attributes. */ -const char* igraph_cattribute_VAS(const igraph_t *graph, const char *name, +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_integer_t 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; + IGRAPH_WARNINGF("Vertex attribute '%s' does not exist, returning default string attribute value.", name); + return ""; } rec = VECTOR(*val)[j]; str = (igraph_strvector_t*)rec->value; - return STR(*str, (long int)vid); + return STR(*str, vid); } /** * \function igraph_cattribute_EAN - * Query a numeric edge attribute. + * \brief Query a numeric edge attribute. + * + * If the attribute does not exist, a warning is issued and + * NaN is returned. See \ref igraph_cattribute_EANV() for + * an error-checked version. * - * 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. @@ -2900,26 +3160,29 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default numeric attribute value.", name); + return IGRAPH_NAN; } rec = VECTOR(*eal)[j]; num = (igraph_vector_t*)rec->value; - return VECTOR(*num)[(long int)eid]; + return VECTOR(*num)[eid]; } /** * \function igraph_cattribute_EAB - * Query a boolean edge attribute. + * \brief Query a boolean edge attribute. + * + * If the edge attribute does not exist, a warning is issued and + * false is returned. See \ref igraph_cattribute_EABV() for + * an error-checked version. * - * 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. @@ -2933,26 +3196,31 @@ 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_integer_t 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; + IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default boolean attribute value.", name); + return false; } rec = VECTOR(*eal)[j]; log = (igraph_vector_bool_t*)rec->value; - return VECTOR(*log)[(long int)eid]; + return VECTOR(*log)[eid]; } /** * \function igraph_cattribute_EAS - * Query a string edge attribute. + * \brief Query a string edge attribute. + * + * Returns a const pointer to the string edge attribute + * specified in \p name. The value must not be modified. + * If the edge attribute does not exist, a warning is issued and + * an empty string is returned. See \ref igraph_cattribute_EASV() for + * an error-checked version. * - * 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. @@ -2962,28 +3230,28 @@ igraph_bool_t igraph_cattribute_EAB(const igraph_t *graph, const char *name, * * Time complexity: O(Ae), the number of edge attributes. */ -const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, +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_integer_t 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; + IGRAPH_WARNINGF("Edge attribute '%s' does not exist, returning default string attribute value.", name); + return ""; } rec = VECTOR(*eal)[j]; str = (igraph_strvector_t*)rec->value; - return STR(*str, (long int)eid); + return STR(*str, eid); } /** * \function igraph_cattribute_VANV - * Query a numeric vertex attribute for many vertices + * \brief Query a numeric vertex attribute for many vertices. * * \param graph The input graph. * \param name The name of the attribute. @@ -2995,7 +3263,7 @@ const char* igraph_cattribute_EAS(const igraph_t *graph, const char *name, * 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_error_t 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, @@ -3004,7 +3272,7 @@ int igraph_cattribute_VANV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_VABV - * Query a boolean vertex attribute for many vertices + * \brief Query a boolean vertex attribute for many vertices. * * \param graph The input graph. * \param name The name of the attribute. @@ -3016,7 +3284,7 @@ int igraph_cattribute_VANV(const igraph_t *graph, const char *name, * 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_error_t 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, @@ -3025,7 +3293,7 @@ int igraph_cattribute_VABV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EANV - * Query a numeric edge attribute for many edges + * \brief Query a numeric edge attribute for many edges. * * \param graph The input graph. * \param name The name of the attribute. @@ -3037,7 +3305,7 @@ int igraph_cattribute_VABV(const igraph_t *graph, const char *name, * 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_error_t 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, @@ -3046,7 +3314,7 @@ int igraph_cattribute_EANV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EABV - * Query a boolean edge attribute for many edges + * \brief Query a boolean edge attribute for many edges. * * \param graph The input graph. * \param name The name of the attribute. @@ -3058,7 +3326,7 @@ int igraph_cattribute_EANV(const igraph_t *graph, const char *name, * 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_error_t 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, @@ -3067,7 +3335,7 @@ int igraph_cattribute_EABV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_VASV - * Query a string vertex attribute for many vertices + * \brief Query a string vertex attribute for many vertices. * * \param graph The input graph. * \param name The name of the attribute. @@ -3080,7 +3348,7 @@ int igraph_cattribute_EABV(const igraph_t *graph, const char *name, * (We assume that the string attributes have a bounded length.) */ -int igraph_cattribute_VASV(const igraph_t *graph, const char *name, +igraph_error_t 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, @@ -3089,7 +3357,7 @@ int igraph_cattribute_VASV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_EASV - * Query a string edge attribute for many edges + * \brief Query a string edge attribute for many edges. * * \param graph The input graph. * \param name The name of the attribute. @@ -3102,7 +3370,7 @@ int igraph_cattribute_VASV(const igraph_t *graph, const char *name, * 'eids'. (We assume that the string attributes have a bounded length.) */ -int igraph_cattribute_EASV(const igraph_t *graph, const char *name, +igraph_error_t 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, @@ -3111,7 +3379,7 @@ int igraph_cattribute_EASV(const igraph_t *graph, const char *name, /** * \function igraph_cattribute_list - * List all attributes + * \brief List all attributes. * * See \ref igraph_attribute_type_t for the various attribute types. * \param graph The input graph. @@ -3129,23 +3397,23 @@ int igraph_cattribute_EASV(const igraph_t *graph, const char *name, * * 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) { +igraph_error_t igraph_cattribute_list(const igraph_t *graph, + igraph_strvector_t *gnames, igraph_vector_int_t *gtypes, + igraph_strvector_t *vnames, igraph_vector_int_t *vtypes, + igraph_strvector_t *enames, igraph_vector_int_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 + * \brief 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. + * \return Logical value, \c true if the attribute exists, \c false otherwise. * * Time complexity: O(A), the number of (graph, vertex or edge) * attributes, assuming attribute names are not too long. @@ -3158,7 +3426,7 @@ igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, /** * \function igraph_cattribute_GAN_set - * Set a numeric graph attribute + * \brief Set a numeric graph attribute. * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3170,12 +3438,12 @@ igraph_bool_t igraph_cattribute_has_attr(const igraph_t *graph, * * Time complexity: O(1). */ -int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3190,18 +3458,18 @@ int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, num); IGRAPH_VECTOR_INIT_FINALLY(num, 1); @@ -3211,12 +3479,12 @@ int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_GAB_set - * Set a boolean graph attribute + * \brief Set a boolean graph attribute. * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3228,12 +3496,12 @@ int igraph_cattribute_GAN_set(igraph_t *graph, const char *name, * * Time complexity: O(1). */ -int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3248,34 +3516,33 @@ int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, log); - IGRAPH_CHECK(igraph_vector_bool_init(log, 1)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, log); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, 1); VECTOR(*log)[0] = value; rec->value = log; IGRAPH_CHECK(igraph_vector_ptr_push_back(gal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_GAS_set - * Set a string graph attribute. + * \brief Set a string graph attribute. * * \param graph The graph. * \param name Name of the graph attribute. If there is no such @@ -3288,12 +3555,12 @@ int igraph_cattribute_GAB_set(igraph_t *graph, const char *name, * * Time complexity: O(1). */ -int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -3308,18 +3575,18 @@ int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add graph attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, 1); @@ -3329,12 +3596,12 @@ int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAN_set - * Set a numeric vertex attribute + * \brief 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 @@ -3350,12 +3617,12 @@ int igraph_cattribute_GAS_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3364,40 +3631,40 @@ int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num = (igraph_vector_t*)rec->value; - VECTOR(*num)[(long int)vid] = value; + VECTOR(*num)[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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + VECTOR(*num)[vid] = value; rec->value = num; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAB_set - * Set a boolean vertex attribute + * \brief 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 @@ -3413,12 +3680,12 @@ int igraph_cattribute_VAN_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3427,41 +3694,40 @@ int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, 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; + VECTOR(*log)[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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_vcount(graph)); + igraph_vector_bool_fill(log, false); + VECTOR(*log)[vid] = value; rec->value = log; IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAS_set - * Set a string vertex attribute + * \brief 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 @@ -3478,12 +3744,12 @@ int igraph_cattribute_VAB_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -3498,18 +3764,18 @@ int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_vcount(graph)); @@ -3519,12 +3785,12 @@ int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAN_set - * Set a numeric edge attribute + * \brief 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 @@ -3540,12 +3806,12 @@ int igraph_cattribute_VAS_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3554,40 +3820,40 @@ int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, IGRAPH_ERROR("Invalid attribute type", IGRAPH_EINVAL); } else { igraph_vector_t *num = (igraph_vector_t*)rec->value; - VECTOR(*num)[(long int)eid] = value; + VECTOR(*num)[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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + VECTOR(*num)[eid] = value; rec->value = num; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAB_set - * Set a boolean edge attribute + * \brief 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 @@ -3603,12 +3869,12 @@ int igraph_cattribute_EAN_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3617,41 +3883,40 @@ int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, 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; + VECTOR(*log)[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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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; + IGRAPH_VECTOR_BOOL_INIT_FINALLY(log, igraph_ecount(graph)); + igraph_vector_bool_fill(log, false); + VECTOR(*log)[eid] = value; rec->value = log; IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAS_set - * Set a string edge attribute + * \brief 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 @@ -3668,12 +3933,12 @@ int igraph_cattribute_EAB_set(igraph_t *graph, const char *name, * 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_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -3688,18 +3953,18 @@ int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); rec->name = strdup(name); if (!rec->name) { - IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, str); IGRAPH_STRVECTOR_INIT_FINALLY(str, igraph_ecount(graph)); @@ -3709,12 +3974,12 @@ int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAN_setv - * Set a numeric vertex attribute for all vertices. + * \brief Set a numeric vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3728,11 +3993,11 @@ int igraph_cattribute_EAS_set(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices. */ -int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -3754,32 +4019,32 @@ int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, num); rec->value = num; - IGRAPH_CHECK(igraph_vector_copy(num, v)); + IGRAPH_CHECK(igraph_vector_init_copy(num, v)); IGRAPH_FINALLY(igraph_vector_destroy, num); IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAB_setv - * Set a boolean vertex attribute for all vertices. + * \brief Set a boolean vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3793,11 +4058,11 @@ int igraph_cattribute_VAN_setv(igraph_t *graph, const char *name, * Time complexity: O(n), the number of vertices. */ -int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -3819,33 +4084,33 @@ int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, log); rec->value = log; - IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); + IGRAPH_CHECK(igraph_vector_bool_init_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; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_VAS_setv - * Set a string vertex attribute for all vertices. + * \brief Set a string vertex attribute for all vertices. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3859,12 +4124,12 @@ int igraph_cattribute_VAB_setv(igraph_t *graph, const char *name, * 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, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); /* Check length first */ @@ -3886,33 +4151,33 @@ int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, str); rec->value = str; - IGRAPH_CHECK(igraph_strvector_copy(str, sv)); + IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); IGRAPH_FINALLY(igraph_strvector_destroy, str); IGRAPH_CHECK(igraph_vector_ptr_push_back(val, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAN_setv - * Set a numeric edge attribute for all edges. + * \brief Set a numeric edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3925,12 +4190,12 @@ int igraph_cattribute_VAS_setv(igraph_t *graph, const char *name, * * Time complexity: O(e), the number of edges. */ -int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -3952,33 +4217,33 @@ int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, num); rec->value = num; - IGRAPH_CHECK(igraph_vector_copy(num, v)); + IGRAPH_CHECK(igraph_vector_init_copy(num, v)); IGRAPH_FINALLY(igraph_vector_destroy, num); IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAB_setv - * Set a boolean edge attribute for all edges. + * \brief Set a boolean edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -3991,12 +4256,12 @@ int igraph_cattribute_EAN_setv(igraph_t *graph, const char *name, * * Time complexity: O(e), the number of edges. */ -int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -4018,33 +4283,33 @@ int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add edge attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, log); rec->value = log; - IGRAPH_CHECK(igraph_vector_bool_copy(log, v)); + IGRAPH_CHECK(igraph_vector_bool_init_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; + return IGRAPH_SUCCESS; } /** * \function igraph_cattribute_EAS_setv - * Set a string edge attribute for all edges. + * \brief Set a string edge attribute for all edges. * * The attribute will be added if not present yet. * \param graph The graph. @@ -4058,12 +4323,12 @@ int igraph_cattribute_EAB_setv(igraph_t *graph, const char *name, * 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, +igraph_error_t 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); /* Check length first */ @@ -4085,28 +4350,28 @@ int igraph_cattribute_EAS_setv(igraph_t *graph, const char *name, 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_ERROR("Cannot add vertex attribute", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, str); rec->value = str; - IGRAPH_CHECK(igraph_strvector_copy(str, sv)); + IGRAPH_CHECK(igraph_strvector_init_copy(str, sv)); IGRAPH_FINALLY(igraph_strvector_destroy, str); IGRAPH_CHECK(igraph_vector_ptr_push_back(eal, rec)); IGRAPH_FINALLY_CLEAN(4); } - return 0; + return IGRAPH_SUCCESS; } static void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { @@ -4128,7 +4393,7 @@ static void igraph_i_cattribute_free_rec(igraph_attribute_record_t *rec) { /** * \function igraph_cattribute_remove_g - * Remove a graph attribute + * \brief Remove a graph attribute. * * \param graph The graph object. * \param name Name of the graph attribute to remove. @@ -4140,7 +4405,7 @@ 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(gal, name, &j); if (l) { @@ -4153,7 +4418,7 @@ void igraph_cattribute_remove_g(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_v - * Remove a vertex attribute + * \brief Remove a vertex attribute. * * \param graph The graph object. * \param name Name of the vertex attribute to remove. @@ -4165,7 +4430,7 @@ 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(val, name, &j); if (l) { @@ -4178,7 +4443,7 @@ void igraph_cattribute_remove_v(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_e - * Remove an edge attribute + * \brief Remove an edge attribute. * * \param graph The graph object. * \param name Name of the edge attribute to remove. @@ -4190,7 +4455,7 @@ 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_integer_t j; igraph_bool_t l = igraph_i_cattribute_find(eal, name, &j); if (l) { @@ -4203,7 +4468,7 @@ void igraph_cattribute_remove_e(igraph_t *graph, const char *name) { /** * \function igraph_cattribute_remove_all - * Remove all graph/vertex/edge attributes + * \brief Remove all graph/vertex/edge attributes. * * \param graph The graph object. * \param g Boolean, whether to remove graph attributes. @@ -4220,7 +4485,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, if (g) { igraph_vector_ptr_t *gal = &attr->gal; - long int i, n = igraph_vector_ptr_size(gal); + igraph_integer_t i, n = igraph_vector_ptr_size(gal); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*gal)[i]); } @@ -4228,7 +4493,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, } if (v) { igraph_vector_ptr_t *val = &attr->val; - long int i, n = igraph_vector_ptr_size(val); + igraph_integer_t i, n = igraph_vector_ptr_size(val); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*val)[i]); } @@ -4236,7 +4501,7 @@ void igraph_cattribute_remove_all(igraph_t *graph, igraph_bool_t g, } if (e) { igraph_vector_ptr_t *eal = &attr->eal; - long int i, n = igraph_vector_ptr_size(eal); + igraph_integer_t i, n = igraph_vector_ptr_size(eal); for (i = 0; i < n; i++) { igraph_i_cattribute_free_rec(VECTOR(*eal)[i]); } diff --git a/src/vendor/cigraph/src/graph/graph_list.c b/src/vendor/cigraph/src/graph/graph_list.c new file mode 100644 index 00000000000..9d82ec2901a --- /dev/null +++ b/src/vendor/cigraph/src/graph/graph_list.c @@ -0,0 +1,61 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2022 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 + +*/ + +#include "igraph_graph_list.h" + +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_types.h" + +#define GRAPH_LIST +#define BASE_GRAPH +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "../core/typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_GRAPH +#undef GRAPH_LIST + +void igraph_graph_list_set_directed( + igraph_graph_list_t* list, igraph_bool_t directed +) { + IGRAPH_ASSERT(list != 0); + list->directed = directed; +} + +static igraph_error_t igraph_i_graph_list_init_item( + const igraph_graph_list_t* list, igraph_t* item +) { + return igraph_empty(item, 0, list->directed); +} + +static igraph_error_t igraph_i_graph_list_copy_item( + igraph_t* dest, const igraph_t* source +) { + return igraph_copy(dest, source); +} + +static void igraph_i_graph_list_destroy_item(igraph_t* item) { + igraph_destroy(item); +} diff --git a/src/vendor/cigraph/src/graph/neighbors.h b/src/vendor/cigraph/src/graph/internal.h similarity index 55% rename from src/vendor/cigraph/src/graph/neighbors.h rename to src/vendor/cigraph/src/graph/internal.h index 3ff36476795..1e5d2620c1b 100644 --- a/src/vendor/cigraph/src/graph/neighbors.h +++ b/src/vendor/cigraph/src/graph/internal.h @@ -16,26 +16,27 @@ along with this program. If not, see . */ -#ifndef IGRAPH_NEIGHBORS_H -#define IGRAPH_NEIGHBORS_H +#ifndef IGRAPH_GRAPH_INTERNAL_H +#define IGRAPH_GRAPH_INTERNAL_H +#include "igraph_datatype.h" +#include "igraph_decls.h" #include "igraph_constants.h" +#include "igraph_error.h" +#include "igraph_vector.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 igraph_error_t igraph_i_neighbors( + const igraph_t *graph, igraph_vector_int_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_PRIVATE_EXPORT igraph_error_t igraph_i_incident( + const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, + igraph_neimode_t mode, igraph_loops_t loops); + +igraph_error_t igraph_i_reverse(igraph_t *graph); __END_DECLS -#endif /* IGRAPH_NEIGHBORS_H */ +#endif /* IGRAPH_GRAPH_INTERNAL_H */ diff --git a/src/vendor/cigraph/src/graph/iterators.c b/src/vendor/cigraph/src/graph/iterators.c index cdf39888af7..566e1c19cd0 100644 --- a/src/vendor/cigraph/src/graph/iterators.c +++ b/src/vendor/cigraph/src/graph/iterators.c @@ -21,9 +21,12 @@ */ +#include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_iterators.h" #include "igraph_memory.h" #include "igraph_interface.h" +#include "igraph_types.h" #include #include @@ -91,9 +94,9 @@ * Time complexity: O(1). */ -int igraph_vs_all(igraph_vs_t *vs) { +igraph_error_t igraph_vs_all(igraph_vs_t *vs) { vs->type = IGRAPH_VS_ALL; - return 0; + return IGRAPH_SUCCESS; } /** @@ -148,12 +151,12 @@ igraph_vs_t igraph_vss_all(void) { * Time complexity: O(1). */ -int igraph_vs_adj(igraph_vs_t *vs, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -193,12 +196,12 @@ int igraph_vs_adj(igraph_vs_t *vs, * \example examples/simple/igraph_vs_nonadj.c */ -int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -214,9 +217,9 @@ int igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t vid, * Time complexity: O(1). */ -int igraph_vs_none(igraph_vs_t *vs) { +igraph_error_t igraph_vs_none(igraph_vs_t *vs) { vs->type = IGRAPH_VS_NONE; - return 0; + return IGRAPH_SUCCESS; } /** @@ -251,10 +254,10 @@ igraph_vs_t igraph_vss_none(void) { * Time complexity: O(1). */ -int igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { +igraph_error_t igraph_vs_1(igraph_vs_t *vs, igraph_integer_t vid) { vs->type = IGRAPH_VS_1; vs->data.vid = vid; - return 0; + return IGRAPH_SUCCESS; } /** @@ -281,7 +284,7 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * \function igraph_vs_vector * \brief Vertex set based on a vector. * - * This function makes it possible to handle an \type igraph_vector_t + * This function makes it possible to handle an \type igraph_vector_int_t * temporarily as a vertex selector. The vertex selector should be * thought of as a \em view into the vector. If you make changes to * the vector that also affects the vertex selector. Destroying the @@ -292,7 +295,7 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * the vector are valid. * * \param vs Pointer to an uninitialized vertex selector. - * \param v Pointer to a \type igraph_vector_t object. + * \param v Pointer to a \type igraph_vector_int_t object. * \return Error code. * \sa \ref igraph_vss_vector(), \ref igraph_vs_destroy() * @@ -301,11 +304,11 @@ igraph_vs_t igraph_vss_1(igraph_integer_t vid) { * \example examples/simple/igraph_vs_vector.c */ -int igraph_vs_vector(igraph_vs_t *vs, - const igraph_vector_t *v) { +igraph_error_t igraph_vs_vector(igraph_vs_t *vs, + const igraph_vector_int_t *v) { vs->type = IGRAPH_VS_VECTORPTR; vs->data.vecptr = v; - return 0; + return IGRAPH_SUCCESS; } /** @@ -314,7 +317,7 @@ int igraph_vs_vector(igraph_vs_t *vs, * * This is the immediate version of \ref igraph_vs_vector. * - * \param v Pointer to a \type igraph_vector_t object. + * \param v Pointer to a \type igraph_vector_int_t object. * \return A vertex selector object containing the vertices in the * vector. * \sa \ref igraph_vs_vector() @@ -322,7 +325,7 @@ int igraph_vs_vector(igraph_vs_t *vs, * Time complexity: O(1). */ -igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { +igraph_vs_t igraph_vss_vector(const igraph_vector_int_t *v) { igraph_vs_t vecvs; vecvs.type = IGRAPH_VS_VECTORPTR; vecvs.data.vecptr = v; @@ -334,7 +337,7 @@ igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { * \brief Create a vertex set by giving its elements. * * This function can be used to create a vertex selector with a few - * vertices. Do not forget to include a -1 after the + * 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. * @@ -344,24 +347,23 @@ igraph_vs_t igraph_vss_vector(const igraph_vector_t *v) { * large for \type int) vertex IDs here. * * \param vs Pointer to an uninitialized vertex selector object. - * \param ... Additional parameters, these will be the vertex ids to + * \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. + * Time complexity: O(n), the number of vertex IDs supplied. */ -int igraph_vs_vector_small(igraph_vs_t *vs, ...) { +igraph_error_t 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); + igraph_integer_t i, n = 0; + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); + IGRAPH_FINALLY(igraph_free, vec); va_start(ap, vs); while (1) { @@ -373,23 +375,27 @@ int igraph_vs_vector_small(igraph_vs_t *vs, ...) { } va_end(ap); - IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t*)vs->data.vecptr, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); va_start(ap, vs); for (i = 0; i < n; i++) { - VECTOR(*vs->data.vecptr)[i] = (igraph_real_t) va_arg(ap, int); + VECTOR(*vec)[i] = va_arg(ap, int); } va_end(ap); IGRAPH_FINALLY_CLEAN(2); - return 0; + + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = vec; + + return IGRAPH_SUCCESS; } /** * \function igraph_vs_vector_copy * \brief Vertex set based on a vector, with copying. * - * This function makes it possible to handle an \type igraph_vector_t + * This function makes it possible to handle an \type igraph_vector_int_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 @@ -399,79 +405,121 @@ int igraph_vs_vector_small(igraph_vs_t *vs, ...) { * the vertex IDs in the vector are valid. * * \param vs Pointer to an uninitialized vertex selector. - * \param v Pointer to a \type igraph_vector_t object. + * \param v Pointer to a \type igraph_vector_int_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_error_t igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_int_t *v) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create vertex selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); IGRAPH_FINALLY_CLEAN(1); - return 0; + + vs->type = IGRAPH_VS_VECTOR; + vs->data.vecptr = vec; + + return IGRAPH_SUCCESS; } /** - * \function igraph_vs_seq + * \function igraph_vs_range * \brief Vertex set, an interval of vertices. * * Creates a vertex selector containing all vertices with vertex ID - * equal to or bigger than \p from and equal to or smaller than \p - * to. + * equal to or bigger than \p from and smaller than \p to. Note that the + * interval is closed from the left and open from the right, following C + * conventions. * * \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. + * \param start The first vertex ID to be included in the vertex selector. + * \param end The first vertex ID \em not to be included in the vertex selector. * \return Error code. - * \sa \ref igraph_vss_seq(), \ref igraph_vs_destroy() + * \sa \ref igraph_vss_range(), \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; - return 0; +igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end) { + *vs = igraph_vss_range(start, end); + return IGRAPH_SUCCESS; } /** - * \function igraph_vss_seq + * \function igraph_vss_range * \brief An interval of vertices (immediate version). * - * The immediate version of \ref igraph_vs_seq(). + * The immediate version of \ref igraph_vs_range(). * - * \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. + * \param start The first vertex ID to be included in the vertex selector. + * \param end The first vertex ID \em not to be included in the vertex selector. * \return Error code. - * \sa \ref igraph_vs_seq() + * \sa \ref igraph_vs_range() * * Time complexity: O(1). */ -igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { +igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end) { igraph_vs_t vs; - vs.type = IGRAPH_VS_SEQ; - vs.data.seq.from = from; - vs.data.seq.to = to; + vs.type = IGRAPH_VS_RANGE; + vs.data.range.start = start; + vs.data.range.end = end; return vs; } +/** + * \function igraph_vs_seq + * \brief Vertex set, an interval of vertices with inclusive endpoints (deprecated). + * + * Creates a vertex selector containing all vertices with vertex ID + * equal to or bigger than \p from and equal to or smaller than \p to. + * Note that both endpoints are inclusive, contrary to C conventions. + * + * \deprecated-by igraph_vs_range 0.10.0 + * + * \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_vs_range(), \ref igraph_vss_seq(), \ref igraph_vs_destroy() + * + * Time complexity: O(1). + * + * \example examples/simple/igraph_vs_seq.c + */ + +igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to) { + *vs = igraph_vss_range(from, to + 1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_vss_seq + * \brief An interval of vertices with inclusive endpoints (immediate version, deprecated). + * + * The immediate version of \ref igraph_vs_seq(). + * + * \deprecated-by igraph_vss_range 0.10.0 + * + * \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_range(), \ref igraph_vs_seq() + * + * Time complexity: O(1). + */ + +igraph_vs_t igraph_vss_seq(igraph_integer_t from, igraph_integer_t to) { + return igraph_vss_range(from, to + 1); +} + /** * \function igraph_vs_destroy * \brief Destroy a vertex set. @@ -494,11 +542,11 @@ void igraph_vs_destroy(igraph_vs_t *vs) { case IGRAPH_VS_NONE: case IGRAPH_VS_1: case IGRAPH_VS_VECTORPTR: - case IGRAPH_VS_SEQ: + case IGRAPH_VS_RANGE: case IGRAPH_VS_NONADJ: break; case IGRAPH_VS_VECTOR: - igraph_vector_destroy((igraph_vector_t*)vs->data.vecptr); + igraph_vector_int_destroy((igraph_vector_int_t*) vs->data.vecptr); IGRAPH_FREE(vs->data.vecptr); break; default: @@ -514,11 +562,11 @@ void igraph_vs_destroy(igraph_vs_t *vs) { * 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 \c FALSE. + * value will be \c false. * * \param vs Pointer to a vertex selector object. - * \return \c TRUE (1) if the vertex selector contains all vertices and - * \c FALSE (0) otherwise. + * \return \c true if the vertex selector contains all vertices and + * \c false otherwise. * * Time complexity: O(1). */ @@ -527,8 +575,8 @@ 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_error_t igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, + igraph_vector_int_t *v) { igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vs, &vit)); @@ -537,7 +585,7 @@ int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -547,26 +595,32 @@ int igraph_vs_as_vector(const igraph_t *graph, igraph_vs_t vs, * \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) { +igraph_error_t igraph_vs_copy(igraph_vs_t* dest, const igraph_vs_t* src) { + igraph_vector_int_t *vec; + 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)); + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy vertex selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); + dest->data.vecptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + break; + default: break; } - return 0; + + return IGRAPH_SUCCESS; } /** * \function igraph_vs_type * \brief Returns the type of the vertex selector. */ -int igraph_vs_type(const igraph_vs_t *vs) { +igraph_vs_type_t igraph_vs_type(const igraph_vs_t *vs) { return vs->type; } @@ -580,62 +634,62 @@ int igraph_vs_type(const igraph_vs_t *vs) { * \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_error_t igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, igraph_integer_t *result) { - igraph_vector_t vec; + igraph_vector_int_t vec; igraph_bool_t *seen; - long i; + igraph_integer_t i; + igraph_integer_t vec_len; switch (vs->type) { case IGRAPH_VS_NONE: - *result = 0; return 0; + *result = 0; return IGRAPH_SUCCESS; case IGRAPH_VS_1: *result = 0; if (vs->data.vid < igraph_vcount(graph) && vs->data.vid >= 0) { *result = 1; } - return 0; + return IGRAPH_SUCCESS; - case IGRAPH_VS_SEQ: - *result = vs->data.seq.to - vs->data.seq.from + 1; - return 0; + case IGRAPH_VS_RANGE: + *result = vs->data.range.end - vs->data.range.start; + return IGRAPH_SUCCESS; case IGRAPH_VS_ALL: - *result = igraph_vcount(graph); return 0; + *result = igraph_vcount(graph); return IGRAPH_SUCCESS; case IGRAPH_VS_ADJ: - IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + IGRAPH_VECTOR_INT_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); + *result = igraph_vector_int_size(&vec); + igraph_vector_int_destroy(&vec); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_VS_NONADJ: - IGRAPH_VECTOR_INIT_FINALLY(&vec, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs->data.adj.vid, vs->data.adj.mode)); + vec_len = igraph_vector_int_size(&vec); *result = igraph_vcount(graph); seen = IGRAPH_CALLOC(*result, igraph_bool_t); - if (seen == 0) { - IGRAPH_ERROR("Cannot calculate selector length", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(seen, "Cannot calculate vertex selector length."); IGRAPH_FINALLY(igraph_free, seen); - for (i = 0; i < igraph_vector_size(&vec); i++) { - if (!seen[(long int)VECTOR(vec)[i]]) { + for (i = 0; i < vec_len; i++) { + if (!seen[ VECTOR(vec)[i] ]) { (*result)--; - seen[(long int)VECTOR(vec)[i]] = 1; + seen[ VECTOR(vec)[i] ] = 1; } } igraph_free(seen); - igraph_vector_destroy(&vec); + igraph_vector_int_destroy(&vec); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_VS_VECTOR: case IGRAPH_VS_VECTORPTR: - *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)vs->data.vecptr); - return 0; + *result = igraph_vector_int_size(vs->data.vecptr); + return IGRAPH_SUCCESS; } IGRAPH_ERROR("Cannot calculate selector length, invalid selector type", @@ -649,12 +703,12 @@ int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, * \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 + * 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 + * this contains the actual vertex IDs in the graph supplied as a * parameter. * * @@ -670,85 +724,92 @@ int igraph_vs_size(const igraph_t *graph, const igraph_vs_t *vs, * 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_range(), \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 + * 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_error_t igraph_vit_create(const igraph_t *graph, igraph_vs_t vs, igraph_vit_t *vit) { + igraph_vector_int_t vec; + igraph_vector_int_t *vec_int; igraph_bool_t *seen; - long int i, j, n; + igraph_integer_t i, j, n; + igraph_integer_t vec_len; switch (vs.type) { case IGRAPH_VS_ALL: - vit->type = IGRAPH_VIT_SEQ; + vit->type = IGRAPH_VIT_RANGE; vit->pos = 0; vit->start = 0; vit->end = igraph_vcount(graph); break; case IGRAPH_VS_ADJ: + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); + n = igraph_vector_int_size(&vec); + IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); + for (i = 0; i < n; i++) { + VECTOR(*vec_int)[i] = VECTOR(vec)[i]; + } + + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + 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); + vit->vec = vec_int; + vit->end = n; + 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)); + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create vertex iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vec, vs.data.adj.vid, vs.data.adj.mode)); + vec_len = igraph_vector_int_size(&vec); n = igraph_vcount(graph); seen = IGRAPH_CALLOC(n, igraph_bool_t); - if (seen == 0) { - IGRAPH_ERROR("Cannot create iterator", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(seen, "Cannot create vertex iterator."); IGRAPH_FINALLY(igraph_free, seen); - for (i = 0; i < igraph_vector_size(&vec); i++) { - if (! seen [ (long int) VECTOR(vec)[i] ] ) { + for (i = 0; i < vec_len; i++) { + if (! seen [ VECTOR(vec)[i] ] ) { n--; - seen[ (long int) VECTOR(vec)[i] ] = 1; + seen[ VECTOR(vec)[i] ] = 1; } } - IGRAPH_CHECK(igraph_vector_resize((igraph_vector_t*)vit->vec, n)); + IGRAPH_CHECK(igraph_vector_int_resize(vec_int, n)); for (i = 0, j = 0; j < n; i++) { if (!seen[i]) { - VECTOR(*vit->vec)[j++] = i; + VECTOR(*vec_int)[j++] = i; } } IGRAPH_FREE(seen); - igraph_vector_destroy(&vec); - vit->end = n; + igraph_vector_int_destroy(&vec); IGRAPH_FINALLY_CLEAN(4); + + vit->type = IGRAPH_VIT_VECTOR; + vit->pos = 0; + vit->start = 0; + vit->vec = vec_int; + vit->end = n; break; case IGRAPH_VS_NONE: - vit->type = IGRAPH_VIT_SEQ; + vit->type = IGRAPH_VIT_RANGE; vit->pos = 0; vit->start = 0; vit->end = 0; break; case IGRAPH_VS_1: - vit->type = IGRAPH_VIT_SEQ; + vit->type = IGRAPH_VIT_RANGE; vit->pos = vs.data.vid; vit->start = vs.data.vid; vit->end = vs.data.vid + 1; @@ -762,28 +823,33 @@ int igraph_vit_create(const igraph_t *graph, 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)) { + vit->end = igraph_vector_int_size(vit->vec); + if (!igraph_vector_int_isininterval(vit->vec, 0, igraph_vcount(graph) - 1)) { IGRAPH_ERROR("Cannot create iterator, invalid vertex ID.", IGRAPH_EINVVID); } break; - case IGRAPH_VS_SEQ: - if (vs.data.seq.from < 0 || vs.data.seq.from >= igraph_vcount(graph)) { - IGRAPH_ERROR("Cannot create sequence iterator, starting vertex ID out of range.", IGRAPH_EINVAL); - } - if (vs.data.seq.to < 0 || vs.data.seq.to >= igraph_vcount(graph)) { - IGRAPH_ERROR("Cannot create sequence iterator, ending vertex ID out of range.", IGRAPH_EINVAL); + case IGRAPH_VS_RANGE: + { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + if (vs.data.range.start < 0 || + vs.data.range.start > no_of_nodes || + (no_of_nodes > 0 && vs.data.range.start == no_of_nodes)) { + IGRAPH_ERROR("Cannot create range iterator, starting vertex ID out of range.", IGRAPH_EINVAL); + } + if (vs.data.range.end < 0 || vs.data.range.end > no_of_nodes) { + IGRAPH_ERROR("Cannot create range iterator, ending vertex ID out of range.", IGRAPH_EINVAL); + } } - vit->type = IGRAPH_VIT_SEQ; - vit->pos = vs.data.seq.from; - vit->start = vs.data.seq.from; - vit->end = vs.data.seq.to + 1; + vit->type = IGRAPH_VIT_RANGE; + vit->pos = vs.data.range.start; + vit->start = vs.data.range.start; + vit->end = vs.data.range.end; break; default: - IGRAPH_ERROR("Cannot create iterator, invalid selector", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); break; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -801,12 +867,12 @@ int igraph_vit_create(const igraph_t *graph, void igraph_vit_destroy(const igraph_vit_t *vit) { switch (vit->type) { - case IGRAPH_VIT_SEQ: + case IGRAPH_VIT_RANGE: case IGRAPH_VIT_VECTORPTR: break; case IGRAPH_VIT_VECTOR: - igraph_vector_destroy((igraph_vector_t*)vit->vec); - igraph_free((igraph_vector_t*)vit->vec); + igraph_vector_int_destroy((igraph_vector_int_t*) vit->vec); + igraph_free((igraph_vector_int_t*) vit->vec); break; default: /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ @@ -814,14 +880,13 @@ void igraph_vit_destroy(const igraph_vit_t *vit) { } } -int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { - - long int i; +igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t *v) { + igraph_integer_t i; - IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_VIT_SIZE(*vit))); + IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_VIT_SIZE(*vit))); switch (vit->type) { - case IGRAPH_VIT_SEQ: + case IGRAPH_VIT_RANGE: for (i = 0; i < IGRAPH_VIT_SIZE(*vit); i++) { VECTOR(*v)[i] = vit->start + i; } @@ -838,7 +903,7 @@ int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { break; } - return 0; + return IGRAPH_SUCCESS; } /*******************************************************/ @@ -851,7 +916,7 @@ int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { * \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 + * \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 @@ -864,7 +929,7 @@ int igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_t *v) { * Time complexity: O(1). */ -int igraph_es_all(igraph_es_t *es, +igraph_error_t igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order) { switch (order) { case IGRAPH_EDGEORDER_ID: @@ -880,12 +945,12 @@ int igraph_es_all(igraph_es_t *es, IGRAPH_ERROR("Invalid edge order, cannot create selector.", IGRAPH_EINVAL); break; } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_ess_all - * \brief Edge set, all edges (immediate version) + * \brief Edge set, all edges (immediate version). * * The immediate version of the all-edges selector. * @@ -921,12 +986,12 @@ igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order) { * Time complexity: O(1). */ -int igraph_es_incident(igraph_es_t *es, +igraph_error_t 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; + return IGRAPH_SUCCESS; } /** @@ -941,9 +1006,9 @@ int igraph_es_incident(igraph_es_t *es, * Time complexity: O(1). */ -int igraph_es_none(igraph_es_t *es) { +igraph_error_t igraph_es_none(igraph_es_t *es) { es->type = IGRAPH_ES_NONE; - return 0; + return IGRAPH_SUCCESS; } /** @@ -977,10 +1042,10 @@ igraph_es_t igraph_ess_none(void) { * Time complexity: O(1). */ -int igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { +igraph_error_t igraph_es_1(igraph_es_t *es, igraph_integer_t eid) { es->type = IGRAPH_ES_1; es->data.eid = eid; - return 0; + return IGRAPH_SUCCESS; } /** @@ -1019,18 +1084,17 @@ igraph_es_t igraph_ess_1(igraph_integer_t eid) { * Time complexity: O(1). */ -int igraph_es_vector(igraph_es_t *es, - const igraph_vector_t *v) { +igraph_error_t igraph_es_vector(igraph_es_t *es, const igraph_vector_int_t *v) { es->type = IGRAPH_ES_VECTORPTR; es->data.vecptr = v; - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_es_vector_copy * \brief Edge set, based on a vector, with copying. * - * This function makes it possible to handle an \type igraph_vector_t + * This function makes it possible to handle an \type igraph_vector_int_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 @@ -1040,40 +1104,43 @@ int igraph_es_vector(igraph_es_t *es, * whether the edge IDs in the vector are valid. * * \param es Pointer to an uninitialized edge selector. - * \param v Pointer to a \type igraph_vector_t object. + * \param v Pointer to a \type igraph_vector_int_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_error_t igraph_es_vector_copy(igraph_es_t *es, const igraph_vector_int_t *v) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); IGRAPH_FINALLY_CLEAN(1); - return 0; + + es->type = IGRAPH_ES_VECTOR; + es->data.vecptr = vec; + + return IGRAPH_SUCCESS; } /** * \function igraph_ess_vector * \brief Immediate vector view edge selector. * - * This is the immediate version of the vector of edge ids edge + * This is the immediate version of the vector of edge IDs edge * selector. * - * \param v The vector of edge ids. + * \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 igraph_ess_vector(const igraph_vector_int_t *v) { igraph_es_t es; es.type = IGRAPH_ES_VECTORPTR; es.data.vecptr = v; @@ -1081,37 +1148,57 @@ igraph_es_t igraph_ess_vector(const igraph_vector_t *v) { } /** - * \function igraph_es_fromto - * \brief Edge selector, all edges between two vertex sets. + * \function igraph_es_range + * \brief Edge selector, a sequence of edge IDs. * - * This function is not implemented yet. + * Creates an edge selector containing all edges with edge ID + * equal to or bigger than \p from and smaller than \p to. Note that the + * interval is closed from the left and open from the right, following C + * conventions. * - * \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. + * \param vs Pointer to an uninitialized edge selector object. + * \param start The first edge ID to be included in the edge selector. + * \param end The first edge ID \em not to be included in the edge selector. * \return Error code. - * \sa \ref igraph_es_destroy() + * \sa \ref igraph_ess_range(), \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_error_t igraph_es_range(igraph_es_t *es, igraph_integer_t start, igraph_integer_t end) { + *es = igraph_ess_range(start, end); + return IGRAPH_SUCCESS; +} - IGRAPH_UNUSED(es); IGRAPH_UNUSED(from); IGRAPH_UNUSED(to); - IGRAPH_ERROR("igraph_es_fromto not implemented yet.", IGRAPH_UNIMPLEMENTED); - /* TODO */ +/** + * \function igraph_ess_range + * \brief Immediate version of the sequence edge selector. + * + * \param start The first edge ID to be included in the edge selector. + * \param end The first edge ID \em not to be included in the edge selector. + * \return The initialized edge selector. + * \sa \ref igraph_es_range() + * + * Time complexity: O(1). + */ + +igraph_es_t igraph_ess_range(igraph_integer_t start, igraph_integer_t end) { + igraph_es_t es; + es.type = IGRAPH_ES_RANGE; + es.data.range.start = start; + es.data.range.end = end; + return es; } /** * \function igraph_es_seq - * \brief Edge selector, a sequence of edge ids. + * \brief Edge selector, a sequence of edge IDs, with inclusive endpoints (deprecated). * - * All edge ids between \p from and \p to (inclusive) will be + * All edge IDs between \p from and \p to (inclusive) will be * included in the edge selection. * + * \deprecated-by igraph_es_range 0.10.0 + * * \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. @@ -1121,17 +1208,16 @@ int igraph_es_fromto(igraph_es_t *es, * 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; +igraph_error_t igraph_es_seq(igraph_es_t *es, igraph_integer_t from, igraph_integer_t to) { + *es = igraph_ess_range(from, to + 1); + return IGRAPH_SUCCESS; } /** * \function igraph_ess_seq - * \brief Immediate version of the sequence edge selector. + * \brief Immediate version of the sequence edge selector, with inclusive endpoints. + * + * \deprecated-by igraph_ess_range 0.10.0 * * \param from The first edge ID to include. * \param to The last edge ID to include. @@ -1142,11 +1228,7 @@ int igraph_es_seq(igraph_es_t *es, */ 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; + return igraph_ess_range(from, to + 1); } /** @@ -1171,20 +1253,21 @@ igraph_es_t igraph_ess_seq(igraph_integer_t from, igraph_integer_t to) { * \example examples/simple/igraph_es_pairs.c */ -int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, +igraph_error_t igraph_es_pairs(igraph_es_t *es, const igraph_vector_int_t *v, igraph_bool_t directed) { + igraph_vector_int_t* vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + 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)); + es->data.path.ptr = vec; - IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1198,66 +1281,64 @@ int igraph_es_pairs(igraph_es_t *es, const igraph_vector_t *v, * 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. * + * + * 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 es Pointer to an uninitialized edge selector object. * \param directed Whether the graph is directed or not. + * \param ... The additional arguments give the edges to be included in the + * selector, as pairs of vertex IDs. The last argument must be -1. + * The \p first parameter is present for technical reasons and represents + * the first variadic argument. * \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, ...) { +igraph_error_t igraph_es_pairs_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { 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); + igraph_integer_t i, n = 0; + igraph_vector_int_t *vec; + int num; - va_start(ap, directed); - while (1) { - int num = va_arg(ap, int); - if (num == -1) { - break; - } + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + + va_start(ap, first); + num = first; + while (num != -1) { n++; + num = va_arg(ap, int); } va_end(ap); - IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); - va_start(ap, directed); - for (i = 0; i < n; i++) { - VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int); + if (n > 0) { + va_start(ap, first); + VECTOR(*vec)[0] = first; + for (i = 1; i < n; i++) { + VECTOR(*vec)[i] = va_arg(ap, int); + } + va_end(ap); } - 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->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); + es->data.path.ptr = vec; - IGRAPH_CHECK(igraph_vector_copy((igraph_vector_t*) es->data.path.ptr, v)); - - IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_es_path - * \brief Edge selector, edge ids on a 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 @@ -1273,53 +1354,87 @@ int igraph_es_multipairs(igraph_es_t *es, const igraph_vector_t *v, * * Time complexity: O(n), the number of vertices. */ -int igraph_es_path(igraph_es_t *es, const igraph_vector_t *v, +igraph_error_t igraph_es_path(igraph_es_t *es, const igraph_vector_int_t *v, igraph_bool_t directed) { + igraph_vector_int_t *vec; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, v)); + IGRAPH_FINALLY_CLEAN(1); + 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)); + es->data.path.ptr = vec; - IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -int igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, ...) { +igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int first, ...) { 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); + igraph_integer_t i, n = 0; + igraph_vector_int_t *vec; + int num; - va_start(ap, directed); - while (1) { - int num = va_arg(ap, int); - if (num == -1) { - break; - } + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge selector."); + IGRAPH_FINALLY(igraph_free, vec); + + va_start(ap, first); + num = first; + while (num != -1) { n++; + num = va_arg(ap, int); } va_end(ap); - IGRAPH_VECTOR_INIT_FINALLY( (igraph_vector_t*) es->data.path.ptr, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n); - va_start(ap, directed); - for (i = 0; i < n; i++) { - VECTOR(*es->data.path.ptr)[i] = (igraph_real_t) va_arg(ap, int); + if (n > 0) { + va_start(ap, first); + VECTOR(*vec)[0] = first; + for (i = 1; i < n; i++) { + VECTOR(*vec)[i] = va_arg(ap, int); + } + va_end(ap); } - va_end(ap); IGRAPH_FINALLY_CLEAN(2); - return 0; + + es->type = IGRAPH_ES_PATH; + es->data.path.mode = directed; + es->data.path.ptr = vec; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_es_all_between + * \brief Edge selector, all edge IDs between a pair of vertices. + * + * This function takes a pair of vertices and creates a selector that matches + * all edges between those vertices. + * + * \param es Pointer to an uninitialized edge selector object. + * \param from The ID of the source vertex. + * \param to The ID of the target vertex. + * \param direectd 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(1). + */ +IGRAPH_EXPORT igraph_error_t igraph_es_all_between( + igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, + igraph_bool_t directed +) { + es->type = IGRAPH_ES_ALL_BETWEEN; + es->data.between.from = from; + es->data.between.to = to; + es->data.between.directed = directed; + return IGRAPH_SUCCESS; } /** @@ -1344,16 +1459,16 @@ void igraph_es_destroy(igraph_es_t *es) { case IGRAPH_ES_NONE: case IGRAPH_ES_1: case IGRAPH_ES_VECTORPTR: - case IGRAPH_ES_SEQ: + case IGRAPH_ES_RANGE: + case IGRAPH_ES_ALL_BETWEEN: break; case IGRAPH_ES_VECTOR: - igraph_vector_destroy((igraph_vector_t*)es->data.vecptr); + igraph_vector_int_destroy((igraph_vector_int_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_vector_int_destroy((igraph_vector_int_t*)es->data.path.ptr); IGRAPH_FREE(es->data.path.ptr); break; default: @@ -1366,8 +1481,8 @@ void igraph_es_destroy(igraph_es_t *es) { * \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. + * \return \c true if \p es was created with \ref + * igraph_es_all() or \ref igraph_ess_all(), and \c false otherwise. * * Time complexity: O(1). */ @@ -1383,29 +1498,32 @@ igraph_bool_t igraph_es_is_all(const igraph_es_t *es) { * \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) { +igraph_error_t igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { + igraph_vector_int_t *vec; + 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)); + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.vecptr)); + dest->data.vecptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ 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)); + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot copy edge selector."); + IGRAPH_FINALLY(igraph_free, &vec); + IGRAPH_CHECK(igraph_vector_int_init_copy(vec, src->data.path.ptr)); + dest->data.path.ptr = vec; + IGRAPH_FINALLY_CLEAN(1); /* ownership of vec taken by 'dest' */ + break; + default: break; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1423,8 +1541,8 @@ int igraph_es_copy(igraph_es_t* dest, const igraph_es_t* src) { * * 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_error_t igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, + igraph_vector_int_t *v) { igraph_eit_t eit; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -1433,23 +1551,23 @@ int igraph_es_as_vector(const igraph_t *graph, igraph_es_t es, igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_es_type * \brief Returns the type of the edge selector. */ -int igraph_es_type(const igraph_es_t *es) { +igraph_es_type_t igraph_es_type(const igraph_es_t *es) { return es->type; } -static int igraph_i_es_pairs_size(const igraph_t *graph, +static igraph_error_t 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, +static igraph_error_t igraph_i_es_path_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result); +static igraph_error_t igraph_i_es_all_between_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 @@ -1461,35 +1579,35 @@ static int igraph_i_es_multipairs_size(const igraph_t *graph, * \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_error_t igraph_es_size(const igraph_t *graph, const igraph_es_t *es, igraph_integer_t *result) { - igraph_vector_t v; + igraph_vector_int_t v; switch (es->type) { case IGRAPH_ES_ALL: *result = igraph_ecount(graph); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_ALLFROM: *result = igraph_ecount(graph); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_ALLTO: *result = igraph_ecount(graph); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_INCIDENT: - IGRAPH_VECTOR_INIT_FINALLY(&v, 0); + IGRAPH_VECTOR_INT_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); + *result = igraph_vector_int_size(&v); + igraph_vector_int_destroy(&v); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_NONE: *result = 0; - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_1: if (es->data.eid < igraph_ecount(graph) && es->data.eid >= 0) { @@ -1497,28 +1615,28 @@ int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, } else { *result = 0; } - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_VECTOR: case IGRAPH_ES_VECTORPTR: - *result = (igraph_integer_t) igraph_vector_size((igraph_vector_t*)es->data.vecptr); - return 0; + *result = igraph_vector_int_size(es->data.vecptr); + return IGRAPH_SUCCESS; - case IGRAPH_ES_SEQ: - *result = es->data.seq.to - es->data.seq.from + 1; - return 0; + case IGRAPH_ES_RANGE: + *result = es->data.range.end - es->data.range.start; + return IGRAPH_SUCCESS; case IGRAPH_ES_PAIRS: IGRAPH_CHECK(igraph_i_es_pairs_size(graph, es, result)); - return 0; + return IGRAPH_SUCCESS; case IGRAPH_ES_PATH: IGRAPH_CHECK(igraph_i_es_path_size(graph, es, result)); - return 0; + return IGRAPH_SUCCESS; - case IGRAPH_ES_MULTIPAIRS: - IGRAPH_CHECK(igraph_i_es_multipairs_size(graph, es, result)); - return 0; + case IGRAPH_ES_ALL_BETWEEN: + IGRAPH_CHECK(igraph_i_es_all_between_size(graph, es, result)); + return IGRAPH_SUCCESS; default: IGRAPH_ERROR("Cannot calculate selector length, invalid selector type.", @@ -1526,126 +1644,133 @@ int igraph_es_size(const igraph_t *graph, const igraph_es_t *es, } } -static int igraph_i_es_pairs_size(const igraph_t *graph, +static igraph_error_t 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; + igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); 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)) { + if (!igraph_vector_int_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); + *result = 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 from = VECTOR(*es->data.path.ptr)[2 * i]; + igraph_integer_t to = 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, + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es->data.path.mode, /*error=*/ 1)); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_es_path_size(const igraph_t *graph, +static igraph_error_t 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; + igraph_integer_t i, n = igraph_vector_int_size(es->data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); - if (!igraph_vector_isininterval(es->data.path.ptr, 0, no_of_nodes - 1)) { + if (!igraph_vector_int_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); + *result = 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 from = VECTOR(*es->data.path.ptr)[i]; + igraph_integer_t to = 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, + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es->data.path.mode, /*error=*/ 1)); } - return 0; + return IGRAPH_SUCCESS; } -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 igraph_error_t igraph_i_es_all_between_size(const igraph_t *graph, + const igraph_es_t *es, igraph_integer_t *result) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t from = es->data.between.from; + igraph_integer_t to = es->data.between.to; + igraph_bool_t directed = es->data.between.directed; + igraph_vector_int_t vec; + + if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("Cannot calculate selector length.", IGRAPH_EINVVID); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_get_all_eids_between(graph, &vec, from, to, directed)); + *result = igraph_vector_int_size(&vec); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /**************************************************/ -static int igraph_i_eit_create_allfromto(const igraph_t *graph, +static igraph_error_t 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, +static igraph_error_t igraph_i_eit_create_incident(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, +static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit); +static igraph_error_t 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, +static igraph_error_t 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_vector_int_t *vec; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, length; + + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); IGRAPH_FINALLY(igraph_free, vec); - IGRAPH_VECTOR_INIT_FINALLY(vec, 0); - IGRAPH_CHECK(igraph_vector_reserve(vec, no_of_edges)); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(vec, no_of_edges)); if (igraph_is_directed(graph)) { - igraph_vector_t adj; - IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + igraph_vector_int_t adj; + IGRAPH_VECTOR_INT_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_CHECK(igraph_incident(graph, &adj, i, mode)); + igraph_vector_int_append(vec, &adj); /* reserved */ } - igraph_vector_destroy(&adj); + igraph_vector_int_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); } else { - igraph_vector_t adj; + igraph_vector_int_t adj; igraph_bool_t *added; - long int j; - IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + IGRAPH_VECTOR_INT_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_CHECK_OOM(added, "Cannot create edge iterator."); 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_CHECK(igraph_incident(graph, &adj, i, IGRAPH_ALL)); + length = igraph_vector_int_size(&adj); + for (j = 0; j < length; j++) { + if (!added[ VECTOR(adj)[j] ]) { + igraph_vector_int_push_back(vec, VECTOR(adj)[j]); /* reserved */ + added[ VECTOR(adj)[j] ] += 1; } } } - igraph_vector_destroy(&adj); + igraph_vector_int_destroy(&adj); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(2); } @@ -1654,90 +1779,92 @@ static int igraph_i_eit_create_allfromto(const igraph_t *graph, eit->pos = 0; eit->start = 0; eit->vec = vec; - eit->end = igraph_vector_size(eit->vec); + eit->end = igraph_vector_int_size(eit->vec); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eit_pairs(const igraph_t *graph, +static igraph_error_t igraph_i_eit_create_incident(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; + igraph_vector_int_t vec; + igraph_vector_int_t* vec_int; + igraph_integer_t i, n; - 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_incident(graph, &vec, es.data.incident.vid, es.data.incident.mode)); + + vec_int = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec_int, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec_int); + + n = igraph_vector_int_size(&vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec_int, n); + + for (i = 0; i < n; i++) { + VECTOR(*vec_int)[i] = VECTOR(vec)[i]; } + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); + 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; - } + eit->vec = vec_int; + eit->end = igraph_vector_int_size(vec_int); - IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -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); +static igraph_error_t igraph_i_eit_pairs(const igraph_t *graph, + igraph_es_t es, igraph_eit_t *eit) { + igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + igraph_vector_int_t* vec; 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)) { + if (!igraph_vector_int_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); } + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, n / 2); + + for (i = 0; i < n / 2; i++) { + igraph_integer_t from = VECTOR(*es.data.path.ptr)[2 * i]; + igraph_integer_t to = VECTOR(*es.data.path.ptr)[2 * i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + 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)); + eit->vec = vec; - IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eit_path(const igraph_t *graph, +static igraph_error_t 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; + igraph_integer_t n = igraph_vector_int_size(es.data.path.ptr); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, len; + igraph_vector_int_t* vec; - if (!igraph_vector_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { + if (!igraph_vector_int_isininterval(es.data.path.ptr, 0, no_of_nodes - 1)) { IGRAPH_ERROR("Cannot create edge iterator.", IGRAPH_EINVVID); } @@ -1747,30 +1874,59 @@ static int igraph_i_eit_path(const igraph_t *graph, len = n - 1; } + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, len); + + for (i = 0; i < len; i++) { + igraph_integer_t from = VECTOR(*es.data.path.ptr)[i]; + igraph_integer_t to = VECTOR(*es.data.path.ptr)[i + 1]; + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, from, to, es.data.path.mode, + /*error=*/ 1)); + VECTOR(*vec)[i] = eid; + } + + IGRAPH_FINALLY_CLEAN(2); + 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); + eit->vec = vec; - IGRAPH_VECTOR_INIT_FINALLY((igraph_vector_t *)eit->vec, len); + return IGRAPH_SUCCESS; +} - 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; +static igraph_error_t igraph_i_eit_all_between( + const igraph_t *graph, igraph_es_t es, igraph_eit_t *eit +) { + igraph_integer_t from = es.data.between.from; + igraph_integer_t to = es.data.between.to; + igraph_bool_t directed = es.data.between.directed; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t* vec; + + if (from < 0 || from >= no_of_nodes || to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("Cannot create edge iterator", IGRAPH_EINVVID); } + vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(vec, "Cannot create edge iterator."); + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_VECTOR_INT_INIT_FINALLY(vec, 0); + IGRAPH_CHECK(igraph_get_all_eids_between(graph, vec, from, to, directed)); IGRAPH_FINALLY_CLEAN(2); - return 0; + + eit->type = IGRAPH_EIT_VECTOR; + eit->pos = 0; + eit->start = 0; + eit->end = igraph_vector_int_size(vec); + eit->vec = vec; + + return IGRAPH_SUCCESS; } /** @@ -1794,16 +1950,15 @@ static int igraph_i_eit_path(const igraph_t *graph, * * 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 + * \ref igraph_es_1(), \ref igraph_es_vector(), \ref 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) { +igraph_error_t 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->type = IGRAPH_EIT_RANGE; eit->pos = 0; eit->start = 0; eit->end = igraph_ecount(graph); @@ -1815,28 +1970,16 @@ int igraph_eit_create(const igraph_t *graph, 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); + IGRAPH_CHECK(igraph_i_eit_create_incident(graph, es, eit)); break; case IGRAPH_ES_NONE: - eit->type = IGRAPH_EIT_SEQ; + eit->type = IGRAPH_EIT_RANGE; eit->pos = 0; eit->start = 0; eit->end = 0; break; case IGRAPH_ES_1: - eit->type = IGRAPH_EIT_SEQ; + eit->type = IGRAPH_EIT_RANGE; eit->pos = es.data.eid; eit->start = es.data.eid; eit->end = es.data.eid + 1; @@ -1850,37 +1993,42 @@ int igraph_eit_create(const igraph_t *graph, 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)) { + eit->end = igraph_vector_int_size(eit->vec); + if (!igraph_vector_int_isininterval(eit->vec, 0, igraph_ecount(graph) - 1)) { IGRAPH_ERROR("Cannot create iterator, invalid edge ID.", IGRAPH_EINVAL); } break; - case IGRAPH_ES_SEQ: - if (es.data.seq.from < 0 || es.data.seq.from >= igraph_ecount(graph)) { - IGRAPH_ERROR("Cannot create sequence iterator, starting edge ID out of range.", IGRAPH_EINVAL); - } - if (es.data.seq.to < 0 || es.data.seq.to >= igraph_ecount(graph)) { - IGRAPH_ERROR("Cannot create sequence iterator, ending edge ID out of range.", IGRAPH_EINVAL); + case IGRAPH_ES_RANGE: + { + igraph_integer_t no_of_edges = igraph_ecount(graph); + if (es.data.range.start < 0 || + es.data.range.start > no_of_edges || + (no_of_edges > 0 && es.data.range.start == no_of_edges)) { + IGRAPH_ERROR("Cannot create range iterator, starting edge ID out of range.", IGRAPH_EINVAL); + } + if (es.data.range.end < 0 || es.data.range.end > no_of_edges) { + IGRAPH_ERROR("Cannot create range iterator, ending edge ID out of range.", IGRAPH_EINVAL); + } } - eit->type = IGRAPH_EIT_SEQ; - eit->pos = es.data.seq.from; - eit->start = es.data.seq.from; - eit->end = es.data.seq.to + 1; + eit->type = IGRAPH_EIT_RANGE; + eit->pos = es.data.range.start; + eit->start = es.data.range.start; + eit->end = es.data.range.end; 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; + case IGRAPH_ES_ALL_BETWEEN: + IGRAPH_CHECK(igraph_i_eit_all_between(graph, es, eit)); + break; default: IGRAPH_ERROR("Cannot create iterator, invalid selector.", IGRAPH_EINVAL); break; } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1895,12 +2043,12 @@ int igraph_eit_create(const igraph_t *graph, void igraph_eit_destroy(const igraph_eit_t *eit) { switch (eit->type) { - case IGRAPH_EIT_SEQ: + case IGRAPH_EIT_RANGE: case IGRAPH_EIT_VECTORPTR: break; case IGRAPH_EIT_VECTOR: - igraph_vector_destroy((igraph_vector_t*)eit->vec); - igraph_free((igraph_vector_t*)eit->vec); + igraph_vector_int_destroy((igraph_vector_int_t*)eit->vec); + igraph_free((igraph_vector_int_t*)eit->vec); break; default: /* IGRAPH_ERROR("Cannot destroy iterator, unknown type", IGRAPH_EINVAL); */ @@ -1908,14 +2056,14 @@ void igraph_eit_destroy(const igraph_eit_t *eit) { } } -int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) { +igraph_error_t igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_int_t *v) { - long int i; + igraph_integer_t i; - IGRAPH_CHECK(igraph_vector_resize(v, IGRAPH_EIT_SIZE(*eit))); + IGRAPH_CHECK(igraph_vector_int_resize(v, IGRAPH_EIT_SIZE(*eit))); switch (eit->type) { - case IGRAPH_EIT_SEQ: + case IGRAPH_EIT_RANGE: for (i = 0; i < IGRAPH_EIT_SIZE(*eit); i++) { VECTOR(*v)[i] = eit->start + i; } @@ -1932,5 +2080,5 @@ int igraph_eit_as_vector(const igraph_eit_t *eit, igraph_vector_t *v) { break; } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/graph/type_common.c b/src/vendor/cigraph/src/graph/type_common.c new file mode 100644 index 00000000000..da01c834c2e --- /dev/null +++ b/src/vendor/cigraph/src/graph/type_common.c @@ -0,0 +1,207 @@ +/* -*- 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_datatype.h" +#include "igraph_interface.h" + +/* Internal functions */ + +/* The functions in this file are sensible "default" implementations for some + * of the core API functions that simply call other core API functions. If + * you are implementing your own data type, chances are that you can use these + * as is. */ + +/** + * \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/creation.c + */ +igraph_error_t 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_delete_vertices + * \brief Removes some 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 + */ +igraph_error_t igraph_delete_vertices(igraph_t *graph, const igraph_vs_t vertices) { + return igraph_delete_vertices_idx(graph, vertices, /* idx= */ 0, /* invidx= */ 0); +} + +/** + * \function igraph_edge + * \brief Returns 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 (source) of + * the edge will be placed here. + * \param to Pointer to an \type igraph_integer_t. The head (target) of the + * edge will be placed here. + * \return Error code. + * + * \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). + */ +igraph_error_t igraph_edge( + const igraph_t *graph, igraph_integer_t eid, + igraph_integer_t *from, igraph_integer_t *to +) { + + if (eid < 0 || eid >= igraph_ecount(graph)) { + IGRAPH_ERROR("Invalid edge ID when retrieving edge endpoints.", IGRAPH_EINVAL); + } + + 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() 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. + */ +igraph_error_t igraph_edges(const igraph_t *graph, igraph_es_t eids, igraph_vector_int_t *edges) { + igraph_eit_t eit; + igraph_integer_t 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_int_resize(edges, n * 2)); + + if (igraph_is_directed(graph)) { + for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { + igraph_integer_t 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)) { + igraph_integer_t 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; +} + +/** + * \function igraph_invalidate_cache + * \brief Invalidates the internal cache of an igraph graph. + * + * + * igraph graphs cache some basic properties about themselves in an internal + * data structure. This function invalidates the contents of the cache and + * forces a recalculation of the cached properties the next time they are + * needed. + * + * + * You should not need to call this function during normal usage; however, we + * might ask you to call this function explicitly if we suspect that you are + * running into a bug in igraph's cache handling. A tell-tale sign of an invalid + * cache entry is that the result of a cached igraph function (such as + * \ref igraph_is_dag() or \ref igraph_is_simple()) is different before and + * after a cache invalidation. + * + * \param graph The graph whose cache is to be invalidated. + * + * Time complexity: O(1). + */ +void igraph_invalidate_cache(const igraph_t* graph) { + igraph_i_property_cache_invalidate_all(graph); +} diff --git a/src/vendor/cigraph/src/graph/type_indexededgelist.c b/src/vendor/cigraph/src/graph/type_indexededgelist.c index b2ef1408ce0..04689c9430d 100644 --- a/src/vendor/cigraph/src/graph/type_indexededgelist.c +++ b/src/vendor/cigraph/src/graph/type_indexededgelist.c @@ -1,7 +1,7 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2005-2012 Gabor Csardi + Copyright (C) 2005-2021 The igraph development team 334 Harvard street, Cambridge, MA 02139 USA This program is free software; you can redistribute it and/or modify @@ -26,13 +26,15 @@ #include "igraph_memory.h" #include "graph/attributes.h" -#include "graph/neighbors.h" +#include "graph/caching.h" +#include "graph/internal.h" +#include "math/safe_intop.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); +static igraph_error_t igraph_i_create_start_vectors( + igraph_vector_int_t *res, igraph_vector_int_t *el, + igraph_vector_int_t *index, igraph_integer_t nodes); /** * \section about_basic_interface @@ -44,55 +46,32 @@ static int igraph_i_create_start( * 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 + * This section lists all the functions and macros that are considered + * as part of the core API from the point of view of the \em users + * of igraph. Some of these functions and macros have sensible default + * implementations that simply call some other core function (e.g., + * \ref igraph_empty() calls \ref igraph_empty_attrs() with a null attribute + * table pointer). If you wish to experiment with implementing an alternative + * data type, the actual number of functions that you need to replace is lower + * as you can rely on the same default implementations in most cases. */ -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(). + * + * + * This function does not set any vertex attributes. To create a graph which has + * vertex attributes, call this function specifying 0 vertices, then use + * \ref igraph_add_vertices() to add vertices and their attributes. + * * \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. @@ -104,14 +83,19 @@ int igraph_empty(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { * \cli IGRAPH_UNDIRECTED * Create an \em undirected graph. * \endclist - * \param attr The attributes. + * \param attr The graph attributes. Supply \c NULL if not graph attributes + * are to be set. * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * + * \sa \ref igraph_empty() to create an empty graph without attributes; + * \ref igraph_add_vertices() and \ref igraph_add_edges() to add vertices + * and edges, possibly with associated attributes. + * * 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) { +igraph_error_t igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed, void *attr) { if (n < 0) { IGRAPH_ERROR("Number of vertices must not be negative.", IGRAPH_EINVAL); @@ -119,12 +103,19 @@ int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t direct 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->from, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->to, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->oi, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->ii, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->os, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&graph->is, 1); + + /* init cache */ + graph->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(graph->cache, "Cannot create graph."); + IGRAPH_FINALLY(igraph_free, graph->cache); + IGRAPH_CHECK(igraph_i_property_cache_init(graph->cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, graph->cache); VECTOR(graph->os)[0] = 0; VECTOR(graph->is)[0] = 0; @@ -136,8 +127,8 @@ int igraph_empty_attrs(igraph_t *graph, igraph_integer_t n, igraph_bool_t direct /* add the vertices */ IGRAPH_CHECK(igraph_add_vertices(graph, n, 0)); - IGRAPH_FINALLY_CLEAN(6); - return 0; + IGRAPH_FINALLY_CLEAN(8); + return IGRAPH_SUCCESS; } /** @@ -160,12 +151,15 @@ 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); + igraph_i_property_cache_destroy(graph->cache); + IGRAPH_FREE(graph->cache); + + igraph_vector_int_destroy(&graph->from); + igraph_vector_int_destroy(&graph->to); + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); + igraph_vector_int_destroy(&graph->os); + igraph_vector_int_destroy(&graph->is); } /** @@ -194,26 +188,32 @@ void igraph_destroy(igraph_t *graph) { * \example examples/simple/igraph_copy.c */ -int igraph_copy(igraph_t *to, const igraph_t *from) { +igraph_error_t 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; + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->from, &from->from)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->from); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->to, &from->to)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->to); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->oi, &from->oi)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->oi); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->ii, &from->ii)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->ii); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->os, &from->os)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->os); + IGRAPH_CHECK(igraph_vector_int_init_copy(&to->is, &from->is)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &to->is); + + to->cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(to->cache, "Cannot copy graph."); + IGRAPH_FINALLY(igraph_free, to->cache); + IGRAPH_CHECK(igraph_i_property_cache_copy(to->cache, from->cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, to->cache); + + IGRAPH_I_ATTRIBUTE_COPY(to, from, true, true, true); /* does IGRAPH_CHECK */ + + IGRAPH_FINALLY_CLEAN(8); + return IGRAPH_SUCCESS; } /** @@ -228,107 +228,134 @@ int igraph_copy(igraph_t *to, const igraph_t *from) { * 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. + * want to add new vertices, call \ref 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. + * \param attr The attributes of the new edges. You can supply a null pointer + * here if you do not need edge attributes. * \return Error code: - * \c IGRAPH_EINVEVECTOR: invalid (odd) - * edges vector length, \c IGRAPH_EINVVID: - * invalid vertex id in edges vector. + * \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. + * 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 + * \example examples/simple/creation.c */ -int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, +igraph_error_t igraph_add_edges(igraph_t *graph, const igraph_vector_int_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_integer_t no_of_edges = igraph_vector_int_size(&graph->from); + igraph_integer_t edges_to_add = igraph_vector_int_size(edges) / 2; + igraph_integer_t new_no_of_edges; + igraph_integer_t i = 0; + igraph_vector_int_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_int_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); + if (!igraph_vector_int_isininterval(edges, 0, igraph_vcount(graph) - 1)) { + IGRAPH_ERROR("Out-of-range vertex IDs when adding 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)); + IGRAPH_SAFE_ADD(no_of_edges, edges_to_add, &new_no_of_edges); + if (new_no_of_edges > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERRORF("Maximum edge count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, + IGRAPH_ECOUNT_MAX); + } + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->from, no_of_edges + edges_to_add)); + IGRAPH_CHECK(igraph_vector_int_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 */ + igraph_vector_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_int_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 */ + igraph_vector_int_push_back(&graph->to, VECTOR(*edges)[i++]); /* reserved */ + igraph_vector_int_push_back(&graph->from, VECTOR(*edges)[i++]); /* reserved */ } } - /* disable the error handler temporarily */ - oldhandler = igraph_set_error_handler(igraph_error_handler_ignore); + /* If an error occurs while the edges are being added, we make the necessary fixup + * to ensure that the graph is still in a consistent state when this function returns. + * The graph may already be on the finally stack when calling this function. We use + * a separate finally stack level to avoid its destructor from being called on error, + * so that the fixup can succeed. + */ - /* 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)); - } +#define CHECK_ERR(expr) \ + do { \ + igraph_error_t err = (expr); \ + if (err != IGRAPH_SUCCESS) { \ + igraph_vector_int_resize(&graph->from, no_of_edges); /* gets smaller, error safe */ \ + igraph_vector_int_resize(&graph->to, no_of_edges); /* gets smaller, error safe */ \ + IGRAPH_FINALLY_EXIT(); \ + IGRAPH_ERROR("Cannot add edges.", err); \ + } \ + } while (0) - /* 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); + /* oi & ii */ + IGRAPH_FINALLY_ENTER(); + { + CHECK_ERR(igraph_vector_int_init(&newoi, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &newoi); + CHECK_ERR(igraph_vector_int_init(&newii, no_of_edges)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &newii); + CHECK_ERR(igraph_vector_int_pair_order(&graph->from, &graph->to, &newoi, graph->n)); + CHECK_ERR(igraph_vector_int_pair_order(&graph->to, &graph->from, &newii, graph->n)); + + /* Attributes */ + if (graph->attr) { + /* TODO: Does this keep the attribute table in a consistent state upon failure? */ + CHECK_ERR(igraph_i_attribute_add_edges(graph, edges, attr)); } + + /* os & is, its length does not change, error safe */ + igraph_i_create_start_vectors(&graph->os, &graph->from, &newoi, graph->n); + igraph_i_create_start_vectors(&graph->is, &graph->to, &newii, graph->n); + + /* everything went fine */ + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); + IGRAPH_FINALLY_CLEAN(2); + + graph->oi = newoi; + graph->ii = newii; } + IGRAPH_FINALLY_EXIT(); - /* 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); +#undef CHECK_ERR - /* everything went fine */ - igraph_vector_destroy(&graph->oi); - igraph_vector_destroy(&graph->ii); - graph->oi = newoi; - graph->ii = newii; - igraph_set_error_handler(oldhandler); + /* modification successful, clear the cached properties of the graph. + * + * Adding one or more edges cannot make a strongly or weakly connected + * graph disconnected, so we keep those flags if they are cached as true. + * + * Adding one or more edges may turn a DAG into a non-DAG or a forest into + * a non-forest, so we can keep those flags only if they are cached as + * false. + * + * Also, adding one or more edges does not change HAS_LOOP, HAS_MULTI and + * HAS_MUTUAL if they were already true. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_IS_DAG) | (1 << IGRAPH_PROP_IS_FOREST), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) + ); - return 0; + return IGRAPH_SUCCESS; } /** @@ -340,45 +367,101 @@ int igraph_add_edges(igraph_t *graph, const igraph_vector_t *edges, * 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. + * \param nv Non-negative integer specifying the number of vertices to add. + * \param attr The attributes of the new vertices. You can supply a null pointer + * here if you do not need vertex attributes. * \return Error code: - * \c IGRAPH_EINVAL: invalid number of new - * vertices. + * \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. + * Time complexity: O(|V|) where |V| is the number of vertices in the \em new, + * extended graph. * - * \example examples/simple/igraph_add_vertices.c + * \example examples/simple/creation.c */ -int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { - long int ec = igraph_ecount(graph); - long int i; +igraph_error_t igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { + igraph_integer_t ec = igraph_ecount(graph); + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t new_vc; + igraph_integer_t i; if (nv < 0) { - IGRAPH_ERROR("cannot add negative number of vertices", IGRAPH_EINVAL); + 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_SAFE_ADD(graph->n, nv, &new_vc); + if (new_vc > IGRAPH_VCOUNT_MAX) { + IGRAPH_ERRORF("Maximum vertex count (%" IGRAPH_PRId ") exceeded.", IGRAPH_ERANGE, + IGRAPH_VCOUNT_MAX); + } + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->os, new_vc + 1)); + IGRAPH_CHECK(igraph_vector_int_reserve(&graph->is, new_vc + 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++) { + igraph_vector_int_resize(&graph->os, new_vc + 1); /* reserved */ + igraph_vector_int_resize(&graph->is, new_vc + 1); /* reserved */ + for (i = graph->n + 1; i < new_vc + 1; i++) { VECTOR(graph->os)[i] = ec; VECTOR(graph->is)[i] = ec; } graph->n += nv; + /* Add attributes if necessary. This section is protected with + * FINALLY_ENTER/EXIT so that the graph would not be accidentally + * free upon error until it could be restored to a consistant state. */ + if (graph->attr) { - IGRAPH_CHECK(igraph_i_attribute_add_vertices(graph, nv, attr)); + igraph_error_t err; + IGRAPH_FINALLY_ENTER(); + err = igraph_i_attribute_add_vertices(graph, nv, attr); + if (err != IGRAPH_SUCCESS) { + /* Restore original vertex count on failure */ + graph->n = vc; + igraph_vector_int_resize(&graph->os, vc + 1); /* shrinks */ + igraph_vector_int_resize(&graph->is, vc + 1); /* shrinks */ + } + IGRAPH_FINALLY_EXIT(); + if (err != IGRAPH_SUCCESS) { + IGRAPH_ERROR("Cannot add vertices.", err); + } } - return 0; + /* modification successful, clear the cached properties of the graph. + * + * Adding one or more nodes does not change the following cached properties: + * + * - IGRAPH_PROP_HAS_LOOP + * - IGRAPH_PROP_HAS_MULTI + * - IGRAPH_PROP_HAS_MUTUAL + * - IGRAPH_PROP_IS_DAG (adding a node does not create/destroy cycles) + * - IGRAPH_PROP_IS_FOREST (same) + * + * Adding one or more nodes without any edges incident on them is sure to + * make the graph disconnected (weakly or strongly), so we can keep the + * connectivity-related properties if they are currently cached as false. + * (Actually, even if they weren't cached as false, we could still set them + * to false, but we don't have that functionality yet). The only exception + * is when the graph had zero vertices and gained only one vertex, because + * it then becomes connected. That's why we have the condition below in the + * keep_when_false section. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) | + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST), + /* keep_when_false = */ + igraph_vcount(graph) >= 2 ? ( + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED) + ) : 0, + /* keep_when_true = */ + 0 + ); + + return IGRAPH_SUCCESS; } /** @@ -387,11 +470,11 @@ int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { * \brief Removes edges from a graph. * * - * The edges to remove are given as an edge selector. + * The edges to remove are specified as an edge selector. * * - * This function cannot remove vertices, they will be kept, even if - * they lose all their edges. + * This function cannot remove vertices; vertices will be kept even if they lose + * all their edges. * * * This function invalidates all iterators. @@ -399,39 +482,36 @@ int igraph_add_vertices(igraph_t *graph, igraph_integer_t nv, void *attr) { * \param edges The edges to remove. * \return Error code. * - * Time complexity: O(|V|+|E|) where - * |V| - * and |E| are the number of vertices + * 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_error_t igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t edges_to_remove = 0; + igraph_integer_t remaining_edges; igraph_eit_t eit; - igraph_vector_t newfrom, newto, newoi; + igraph_vector_int_t newfrom, newto; + igraph_vector_int_t newoi, newii; - int *mark; - long int i, j; + igraph_bool_t *mark; + igraph_integer_t i, j; - mark = IGRAPH_CALLOC(no_of_edges, int); - if (mark == 0) { - IGRAPH_ERROR("Cannot delete edges", IGRAPH_ENOMEM); - } + mark = IGRAPH_CALLOC(no_of_edges, igraph_bool_t); + IGRAPH_CHECK_OOM(mark, "Cannot delete edges."); 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) { + igraph_integer_t e = IGRAPH_EIT_GET(eit); + if (! mark[e]) { edges_to_remove++; - mark[e]++; + mark[e] = true; } } remaining_edges = no_of_edges - edges_to_remove; @@ -440,12 +520,12 @@ int igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_VECTOR_INIT_FINALLY(&newfrom, remaining_edges); - IGRAPH_VECTOR_INIT_FINALLY(&newto, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newfrom, remaining_edges); + IGRAPH_VECTOR_INT_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) { + if (! mark[i]) { VECTOR(newfrom)[j] = VECTOR(graph->from)[i]; VECTOR(newto)[j] = VECTOR(graph->to)[i]; j++; @@ -453,108 +533,138 @@ int igraph_delete_edges(igraph_t *graph, igraph_es_t edges) { } /* 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newoi, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newii, remaining_edges); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newfrom, &newto, &newoi, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newto, &newfrom, &newii, no_of_nodes)); - /* Edge attributes, we need an index that gives the ids of the + /* 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); + igraph_vector_int_t idx; + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, remaining_edges); for (i = 0, j = 0; i < no_of_edges; i++) { - if (mark[i] == 0) { + if (! mark[i]) { VECTOR(idx)[j++] = i; } } IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, graph, &idx)); - igraph_vector_destroy(&idx); + igraph_vector_int_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); + igraph_vector_int_destroy(&graph->from); + igraph_vector_int_destroy(&graph->to); + igraph_vector_int_destroy(&graph->oi); + igraph_vector_int_destroy(&graph->ii); graph->from = newfrom; graph->to = newto; graph->oi = newoi; - IGRAPH_FINALLY_CLEAN(3); + graph->ii = newii; + IGRAPH_FINALLY_CLEAN(4); 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); + igraph_i_create_start_vectors(&graph->os, &graph->from, &graph->oi, no_of_nodes); + igraph_i_create_start_vectors(&graph->is, &graph->to, &graph->ii, no_of_nodes); + + /* modification successful, clear the cached properties of the graph. + * + * Deleting one or more edges cannot make a directed acyclic graph cyclic, + * or an undirected forest into a cyclic graph, so we keep those flags if + * they are cached as true. + * + * Similarly, deleting one or more edges cannot make a disconnected graph + * connected, so we keep the connectivity flags if they are cached as false. + * + * Also, if the graph had no loop edges before the deletion, it will have + * no loop edges after the deletion either. The same applies to reciprocal + * edges or multiple edges as well. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL) | + (1 << IGRAPH_PROP_IS_STRONGLY_CONNECTED) | + (1 << IGRAPH_PROP_IS_WEAKLY_CONNECTED), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST) + ); /* Nothing to deallocate... */ - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup interface - * \function igraph_delete_vertices - * \brief Removes vertices (with all their edges) from the graph. + * \function igraph_delete_vertices_idx + * \brief Removes some 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 changes the IDs of the vertices (except in some very + * special cases, but these should not be relied on anyway). You can use the + * \c idx argument to obtain the mapping from old vertex IDs to the new ones, + * and the \c newidx argument to obtain the reverse mapping. * * * 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. + * \param vertices The IDs of the vertices to remove, in a vector. The vector + * may contain the same ID more than once. + * \param idx An optional pointer to a vector that provides the mapping from + * the vertex IDs \em before the removal to the vertex IDs \em after + * the removal, \em plus one. Zero is used to represent vertices that were + * removed during the operation. You can supply \c NULL here if you are not + * interested. + * \param invidx An optional pointer to a vector that provides the mapping from + * the vertex IDs \em after the removal to the vertex IDs \em before + * the removal. You can supply \c NULL here if you are not interested. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVVID: invalid vertex ID. * - * Time complexity: O(|V|+|E|), - * |V| and - * |E| are the number of vertices and + * 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_error_t igraph_delete_vertices_idx( + igraph_t *graph, const igraph_vs_t vertices, igraph_vector_int_t *idx, + igraph_vector_int_t *invidx +) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edge_recoding, vertex_recoding; + igraph_vector_int_t *my_vertex_recoding = &vertex_recoding; igraph_vit_t vit; igraph_t newgraph; - long int i, j; - long int remaining_vertices, remaining_edges; + igraph_integer_t i, j; + igraph_integer_t remaining_vertices, remaining_edges; if (idx) { my_vertex_recoding = idx; - IGRAPH_CHECK(igraph_vector_resize(idx, no_of_nodes)); - igraph_vector_null(idx); + IGRAPH_CHECK(igraph_vector_int_resize(idx, no_of_nodes)); + igraph_vector_int_null(idx); } else { - IGRAPH_VECTOR_INIT_FINALLY(&vertex_recoding, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_recoding, no_of_nodes); } - IGRAPH_VECTOR_INIT_FINALLY(&edge_recoding, no_of_edges); + IGRAPH_VECTOR_INT_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); + igraph_integer_t vertex = IGRAPH_VIT_GET(vit); if (vertex < 0 || vertex >= no_of_nodes) { IGRAPH_ERROR("Cannot delete vertices", IGRAPH_EINVVID); } @@ -571,8 +681,8 @@ int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, } /* 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]; + igraph_integer_t from = VECTOR(graph->from)[i]; + igraph_integer_t to = VECTOR(graph->to)[i]; if (VECTOR(*my_vertex_recoding)[from] != 0 && VECTOR(*my_vertex_recoding)[to ] != 0) { VECTOR(edge_recoding)[i] = remaining_edges + 1; @@ -581,51 +691,59 @@ int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, } /* start creating the graph */ - newgraph.n = (igraph_integer_t) remaining_vertices; + newgraph.n = 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.from, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.to, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.oi, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.ii, remaining_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&newgraph.os, remaining_vertices + 1); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t from = VECTOR(graph->from)[i]; + igraph_integer_t to = 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)); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newgraph.from, &newgraph.to, &newgraph.oi, + remaining_vertices)); + IGRAPH_CHECK(igraph_vector_int_pair_order(&newgraph.to, &newgraph.from, &newgraph.ii, + remaining_vertices)); + + IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.os, &newgraph.from, + &newgraph.oi, remaining_vertices)); + IGRAPH_CHECK(igraph_i_create_start_vectors(&newgraph.is, &newgraph.to, + &newgraph.ii, remaining_vertices)); + + newgraph.cache = IGRAPH_CALLOC(1, igraph_i_property_cache_t); + IGRAPH_CHECK_OOM(newgraph.cache, "Cannot delete vertices."); + IGRAPH_FINALLY(igraph_free, newgraph.cache); + IGRAPH_CHECK(igraph_i_property_cache_init(newgraph.cache)); + IGRAPH_FINALLY(igraph_i_property_cache_destroy, newgraph.cache); /* attributes */ IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, /*graph=*/ 1, /*vertex=*/0, /*edge=*/0); - IGRAPH_FINALLY_CLEAN(6); + + /* at this point igraph_destroy can take over the responsibility of + * deallocating the graph */ + IGRAPH_FINALLY_CLEAN(8); /* 2 for the property cache, 6 for the vectors */ IGRAPH_FINALLY(igraph_destroy, &newgraph); if (newgraph.attr) { - igraph_vector_t iidx; - IGRAPH_VECTOR_INIT_FINALLY(&iidx, remaining_vertices); + igraph_vector_int_t iidx; + IGRAPH_VECTOR_INT_INIT_FINALLY(&iidx, remaining_vertices); for (i = 0; i < no_of_nodes; i++) { - long int jj = (long int) VECTOR(*my_vertex_recoding)[i]; + igraph_integer_t jj = VECTOR(*my_vertex_recoding)[i]; if (jj != 0) { VECTOR(iidx)[ jj - 1 ] = i; } @@ -633,20 +751,20 @@ int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, &newgraph, &iidx)); - IGRAPH_CHECK(igraph_vector_resize(&iidx, remaining_edges)); + IGRAPH_CHECK(igraph_vector_int_resize(&iidx, remaining_edges)); for (i = 0; i < no_of_edges; i++) { - long int jj = (long int) VECTOR(edge_recoding)[i]; + igraph_integer_t jj = 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_vector_int_destroy(&iidx); IGRAPH_FINALLY_CLEAN(1); } igraph_vit_destroy(&vit); - igraph_vector_destroy(&edge_recoding); + igraph_vector_int_destroy(&edge_recoding); igraph_destroy(graph); *graph = newgraph; @@ -654,9 +772,9 @@ int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, /* TODO: this is duplicate */ if (invidx) { - IGRAPH_CHECK(igraph_vector_resize(invidx, remaining_vertices)); + IGRAPH_CHECK(igraph_vector_int_resize(invidx, remaining_vertices)); for (i = 0; i < no_of_nodes; i++) { - long int newid = (long int) VECTOR(*my_vertex_recoding)[i]; + igraph_integer_t newid = VECTOR(*my_vertex_recoding)[i]; if (newid != 0) { VECTOR(*invidx)[newid - 1] = i; } @@ -664,11 +782,33 @@ int igraph_delete_vertices_idx(igraph_t *graph, const igraph_vs_t vertices, } if (!idx) { - igraph_vector_destroy(my_vertex_recoding); + igraph_vector_int_destroy(my_vertex_recoding); IGRAPH_FINALLY_CLEAN(1); } - return 0; + /* modification successful, clear the cached properties of the graph. + * + * Deleting one or more vertices cannot make a directed acyclic graph cyclic, + * or an undirected forest into a cyclic graph, so we keep those flags if + * they are cached as true. + * + * Also, if the graph had no loop edges before the deletion, it will have + * no loop edges after the deletion either. The same applies to reciprocal + * edges or multiple edges as well. + */ + igraph_i_property_cache_invalidate_conditionally( + graph, + /* keep_always = */ 0, + /* keep_when_false = */ + (1 << IGRAPH_PROP_HAS_LOOP) | + (1 << IGRAPH_PROP_HAS_MULTI) | + (1 << IGRAPH_PROP_HAS_MUTUAL), + /* keep_when_true = */ + (1 << IGRAPH_PROP_IS_DAG) | + (1 << IGRAPH_PROP_IS_FOREST) + ); + + return IGRAPH_SUCCESS; } /** @@ -696,7 +836,7 @@ igraph_integer_t igraph_vcount(const igraph_t *graph) { * Time complexity: O(1) */ igraph_integer_t igraph_ecount(const igraph_t *graph) { - return (igraph_integer_t) igraph_vector_size(&graph->from); + return igraph_vector_int_size(&graph->from); } /** @@ -707,8 +847,9 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * \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. + * version 0.4 this vector is always sorted, the vertex IDs are + * in increasing order. If one neighbor is connected with multiple + * edges, the neighbor will be returned multiple times. * \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 @@ -721,7 +862,7 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * searched. * This parameter is ignored for undirected graphs. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVVID: invalid vertex ID. * \c IGRAPH_EINVMODE: invalid mode argument. * \c IGRAPH_ENOMEM: not enough memory. * @@ -731,7 +872,7 @@ igraph_integer_t igraph_ecount(const igraph_t *graph) { * * \example examples/simple/igraph_neighbors.c */ -int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, +igraph_error_t igraph_neighbors(const igraph_t *graph, igraph_vector_int_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); @@ -740,7 +881,7 @@ int igraph_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_intege } } -int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_integer_t pnode, +igraph_error_t igraph_i_neighbors(const igraph_t *graph, igraph_vector_int_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) { \ @@ -761,11 +902,11 @@ int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_inte } \ } - long int length = 0, idx = 0; - long int i, j; + igraph_integer_t length = 0, idx = 0; + igraph_integer_t i, j; - long int node = pnode; - igraph_real_t last_added = -1; + igraph_integer_t node = pnode; + igraph_integer_t last_added = -1; igraph_bool_t should_filter_duplicates; if (node < 0 || node > igraph_vcount(graph) - 1) { @@ -794,7 +935,7 @@ int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_inte length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); } - IGRAPH_CHECK(igraph_vector_resize(neis, length)); + IGRAPH_CHECK(igraph_vector_int_resize(neis, length)); /* The loops below produce an ordering what is consistent with the * ordering returned by igraph_neighbors(), and this should be preserved. @@ -816,18 +957,18 @@ int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_inte (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] ]; + j = VECTOR(graph->os)[node + 1]; + for (i = VECTOR(graph->os)[node]; i < j; i++) { + igraph_integer_t to = VECTOR(graph->to)[ 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] ]; + j = VECTOR(graph->is)[node + 1]; + for (i = VECTOR(graph->is)[node]; i < j; i++) { + igraph_integer_t from = VECTOR(graph->from)[ VECTOR(graph->ii)[i] ]; DEDUPLICATE_IF_NEEDED(from, 1); VECTOR(*neis)[idx++] = from; } @@ -836,21 +977,21 @@ int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_inte /* 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; + igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; + igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; + igraph_integer_t i1 = VECTOR(graph->os)[node]; + igraph_integer_t i2 = VECTOR(graph->is)[node]; + igraph_integer_t eid1, eid2; + igraph_integer_t 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]; + eid1 = VECTOR(graph->oi)[i1]; + eid2 = VECTOR(graph->ii)[i2]; + n1 = VECTOR(graph->to)[eid1]; + n2 = VECTOR(graph->from)[eid2]; if (n1 < n2) { i1++; DEDUPLICATE_IF_NEEDED(n1, 1); @@ -877,64 +1018,64 @@ int igraph_i_neighbors(const igraph_t *graph, igraph_vector_t *neis, igraph_inte } while (i1 < j1) { - eid1 = (long int) VECTOR(graph->oi)[i1++]; - igraph_real_t to = (long int) VECTOR(graph->to)[eid1]; + eid1 = VECTOR(graph->oi)[i1++]; + igraph_integer_t to = 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]; + eid2 = VECTOR(graph->ii)[i2++]; + igraph_integer_t from = VECTOR(graph->from)[eid2]; DEDUPLICATE_IF_NEEDED(from, 1); VECTOR(*neis)[idx++] = from; } } - IGRAPH_CHECK(igraph_vector_resize(neis, length)); + IGRAPH_CHECK(igraph_vector_int_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) { +static igraph_error_t igraph_i_create_start_vectors( + igraph_vector_int_t *res, igraph_vector_int_t *el, + igraph_vector_int_t *iindex, igraph_integer_t nodes) { -# define EDGE(i) (VECTOR(*el)[ (long int) VECTOR(*iindex)[(i)] ]) +# define EDGE(i) (VECTOR(*el)[ VECTOR(*iindex)[(i)] ]) - long int no_of_nodes; - long int no_of_edges; - long int i, j, idx; + igraph_integer_t no_of_nodes; + igraph_integer_t no_of_edges; + igraph_integer_t i, j, idx; no_of_nodes = nodes; - no_of_edges = igraph_vector_size(el); + no_of_edges = igraph_vector_int_size(el); /* result */ - IGRAPH_CHECK(igraph_vector_resize(res, nodes + 1)); + IGRAPH_CHECK(igraph_vector_int_resize(res, nodes + 1)); /* create the index */ - if (igraph_vector_size(el) == 0) { + if (no_of_edges == 0) { /* empty graph */ - igraph_vector_null(res); + igraph_vector_int_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])); + igraph_integer_t n = EDGE(i) - EDGE(VECTOR(*res)[idx]); for (j = 0; j < n; j++) { idx++; VECTOR(*res)[idx] = i; } } - j = (long int) EDGE((long int)VECTOR(*res)[idx]); + j = EDGE(VECTOR(*res)[idx]); for (i = 0; i < no_of_nodes - j; i++) { idx++; VECTOR(*res)[idx] = no_of_edges; } @@ -943,7 +1084,7 @@ static int igraph_i_create_start( /* clean */ # undef EDGE - return 0; + return IGRAPH_SUCCESS; } /** @@ -952,8 +1093,8 @@ static int igraph_i_create_start( * \brief Is this a directed graph? * * \param graph The graph. - * \return Logical value, TRUE if the graph is directed, - * FALSE otherwise. + * \return Logical value, \c true if the graph is directed, + * \c false otherwise. * * Time complexity: O(1) * @@ -964,6 +1105,67 @@ igraph_bool_t igraph_is_directed(const igraph_t *graph) { return graph->directed; } +/** + * \ingroup interface + * \function igraph_degree_1 + * \brief The degree of of a single vertex in the graph. + * + * This function calculates the in-, out- or total degree of a single vertex. + * For a single vertex, it is more efficient than calling \ref igraph_degree(). + * + * \param graph The graph. + * \param deg Pointer to the integer where the computed degree will be stored. + * \param vid The vertex for which the degree will be calculated. + * \param mode Defines the type of the degree for directed graphs. 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. + * + * \sa \ref igraph_degree() to compute the degree of several vertices at once. + * + * Time complexity: O(1) if \p loops is \c true, and + * O(d) otherwise, where d is the degree. + */ +igraph_error_t igraph_degree_1(const igraph_t *graph, igraph_integer_t *deg, + igraph_integer_t vid, igraph_neimode_t mode, igraph_bool_t loops) { + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + *deg = 0; + if (mode & IGRAPH_OUT) { + *deg += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); + } + if (mode & IGRAPH_IN) { + *deg += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); + } + if (! loops) { + /* When loops should not be counted, we remove their contribution from the + * previously computed degree. */ + if (mode & IGRAPH_OUT) { + for (igraph_integer_t i = VECTOR(graph->os)[vid]; i < VECTOR(graph->os)[vid + 1]; i++) { + if (VECTOR(graph->to)[ VECTOR(graph->oi)[i] ] == vid) { + *deg -= 1; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t i = VECTOR(graph->is)[vid]; i < VECTOR(graph->is)[vid + 1]; i++) { + if (VECTOR(graph->from)[ VECTOR(graph->ii)[i] ] == vid) { + *deg -= 1; + } + } + } + } + + return IGRAPH_SUCCESS; +} + /** * \ingroup interface * \function igraph_degree @@ -972,12 +1174,18 @@ igraph_bool_t igraph_is_directed(const igraph_t *graph) { * * This function calculates the in-, out- or total degree of the * specified vertices. + * + * + * This function returns the result as a vector of \c igraph_integer_t + * values. In applications where \c igraph_real_t is desired, use + * \ref igraph_strength() with \c NULL weights. + * * \param graph The graph. - * \param res Vector, this will contain the result. It should be + * \param res Integer 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 + * \param vids Vertex selector, giving the vertex IDs of which the degree will * be calculated. - * \param mode Defines the type of the degree. Valid modes are: + * \param mode Defines the type of the degree for directed graphs. Valid modes are: * \c IGRAPH_OUT, out-degree; * \c IGRAPH_IN, in-degree; * \c IGRAPH_ALL, total degree (sum of the @@ -986,35 +1194,33 @@ igraph_bool_t igraph_is_directed(const igraph_t *graph) { * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \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 + * Time complexity: O(v) if \p loops is \c 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. + * edge weights; \ref igraph_degree_1() to efficiently compute the + * degree of a single vertex. * * \example examples/simple/igraph_degree.c */ -int igraph_degree(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_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_integer_t nodes_to_calc; + igraph_integer_t 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); + IGRAPH_ERROR("Invalid mode for degree calculation.", IGRAPH_EINVMODE); } nodes_to_calc = IGRAPH_VIT_SIZE(vit); @@ -1022,15 +1228,15 @@ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, mode = IGRAPH_ALL; } - IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); - igraph_vector_null(res); + IGRAPH_CHECK(igraph_vector_int_resize(res, nodes_to_calc)); + igraph_vector_int_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); + igraph_integer_t vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->os)[vid + 1] - VECTOR(graph->os)[vid]); } } @@ -1038,7 +1244,7 @@ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int vid = IGRAPH_VIT_GET(vit); + igraph_integer_t vid = IGRAPH_VIT_GET(vit); VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); } } @@ -1047,11 +1253,11 @@ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int vid = IGRAPH_VIT_GET(vit); + igraph_integer_t 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]; + for (j = VECTOR(graph->os)[vid]; j < VECTOR(graph->os)[vid + 1]; j++) { - if (VECTOR(graph->to)[ (long int)VECTOR(graph->oi)[j] ] == vid) { + if (VECTOR(graph->to)[ VECTOR(graph->oi)[j] ] == vid) { VECTOR(*res)[i] -= 1; } } @@ -1061,11 +1267,11 @@ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, for (IGRAPH_VIT_RESET(vit), i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int vid = IGRAPH_VIT_GET(vit); + igraph_integer_t 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]; + for (j = VECTOR(graph->is)[vid]; j < VECTOR(graph->is)[vid + 1]; j++) { - if (VECTOR(graph->from)[ (long int)VECTOR(graph->ii)[j] ] == vid) { + if (VECTOR(graph->from)[ VECTOR(graph->ii)[j] ] == vid) { VECTOR(*res)[i] -= 1; } } @@ -1076,172 +1282,111 @@ int igraph_degree(const igraph_t *graph, igraph_vector_t *res, 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; -} +/* These are unsafe macros. Only supply variable names, i.e. no + expressions as parameters, otherwise nasty things can happen. + + BINSEARCH is an inline binary search in the 'edgelist' vector, which is + assumed to be sorted in the order of indices stored in the 'iindex' vector. + (So, [edgelist[iindex[x]] for x in 0..] is assumed to be sorted). 'N' must + be the same as 'end' when invoking the macro but it must be a separate + variable as we want to modify 'end' independently of 'N'. Upon exiting the + macro, 'result' is the index of the _leftmost_ item in the sorted 'edgelist' + (i.e. indexed by 'iindex') where the value was found, if it was found; + otherwise 'pos' is left intact. + + FIND_DIRECTED_EDGE looks for an edge from 'xfrom' to 'xto' in the graph, and + stores the ID of the edge in 'eid' if it is found; otherwise 'eid' is left + intact. + + FIND_UNDIRECTED_EDGE looks for an edge between 'xfrom' and 'xto' in an + undirected graph, swapping them if necessary. It stores the ID of the edge + in 'eid' if it is found; otherwise 'eid' is left intact. + */ + +#define BINSEARCH(start,end,value,iindex,edgelist,N,result,result_pos) \ + do { \ + while ((start) < (end)) { \ + igraph_integer_t mid =(start)+((end)-(start))/2; \ + igraph_integer_t e = VECTOR((iindex))[mid]; \ + if (VECTOR((edgelist))[e] < (value)) { \ + (start) = mid+1; \ + } else { \ + (end) = mid; \ + } \ + } \ + if ((start) < (N)) { \ + igraph_integer_t e = VECTOR((iindex))[(start)]; \ + if (VECTOR((edgelist))[e] == (value)) { \ + *(result) = e; \ + if (result_pos != 0) { *(result_pos) = start; } \ + } \ + } \ + } while (0) -/* 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); \ - } \ +#define FIND_DIRECTED_EDGE(graph,xfrom,xto,eid) \ + do { \ + igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ + igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ + igraph_integer_t N = end; \ + igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ + igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ + igraph_integer_t N2 = end2; \ + igraph_integer_t *nullpointer = NULL; \ + if (end-start < end2-start2) { \ + BINSEARCH(start, end, xto, graph->oi, graph->to, N, eid, nullpointer); \ + } else { \ + BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, eid, nullpointer); \ + } \ } 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); \ +#define FIND_UNDIRECTED_EDGE(graph, from, to, eid) \ + do { \ + igraph_integer_t xfrom1 = from > to ? from : to; \ + igraph_integer_t 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. + * \brief Get the edge ID from the end points of an edge. * - * For undirected graphs \c pfrom and \c pto are exchangeable. + * For undirected graphs \c from and \c to 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 eid Pointer to an integer, the edge ID will be stored here. + * \param from The starting point of the edge. + * \param to 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. + * Note that invalid vertex IDs in input arguments (\p from or \p to) + * always return an error code. * \return Error code. - * \sa \ref igraph_edge() for the opposite operation. + * \sa \ref igraph_edge() for the opposite operation, \ref igraph_get_all_eids_between() + * to retrieve all edge IDs between a pair of vertices. * * 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 + * of \c from and in-degree of \c to 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. + * d2 is the minimum of the out-degree of \c to and the in-degree of \c from. * * \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_error_t igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, + igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed, igraph_bool_t error) { - long int from = pfrom, to = pto; - long int nov = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); - if (from < 0 || to < 0 || from > nov - 1 || to > nov - 1) { - IGRAPH_ERROR("cannot get edge id", IGRAPH_EINVVID); + if (from < 0 || to < 0 || from >= no_of_nodes || to >= no_of_nodes) { + IGRAPH_ERROR("Cannot get edge ID.", IGRAPH_EINVVID); } *eid = -1; @@ -1262,161 +1407,42 @@ int igraph_get_eid(const igraph_t *graph, igraph_integer_t *eid, if (*eid < 0) { if (error) { - IGRAPH_ERROR("Cannot get edge id, no such edge", IGRAPH_EINVAL); + 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. + * Return edge IDs based on the adjacent vertices. * - * - * 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. + * The pairs of vertex IDs for which the edges are looked up are taken + * consecutively from the \c pairs vector, i.e. VECTOR(pairs)[0] + * and VECTOR(pairs)[1] specify the first pair, + * VECTOR(pairs)[2] and VECTOR(pairs)[3] the second + * pair, etc. * * - * 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 you have a sequence of vertex IDs that describe a \em path on the graph, + * use \ref igraph_expand_path_to_pairs() to convert them to a list of vertex + * pairs along the path. * * - * 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 the \c error argument is true, then it is an error to specify pairs + * of vertices that are not connected. Otherwise -1 is reported for vertex pairs + * without at least one edge between them. * * * 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. + * i.e. for a given pair of vertex IDs, igraph always returns the same edge ID, + * even if the pair appears multiple times in \c pairs. * * \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 pairs Vector giving pairs of vertices to fetch the edges for. * \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 @@ -1427,304 +1453,167 @@ int igraph_get_eids_path(const igraph_t *graph, igraph_vector_t *eids, * 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). + * \sa \ref igraph_get_eid() for a single edge. * * \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_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, + const igraph_vector_int_t *pairs, 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 n = pairs ? igraph_vector_int_size(pairs) : 0; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; igraph_integer_t eid = -1; + if (n == 0) { + igraph_vector_int_clear(eids); + return IGRAPH_SUCCESS; + } + if (n % 2 != 0) { - IGRAPH_ERROR("Cannot get edge ids, invalid length of edge ids", + 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); + if (!igraph_vector_int_isininterval(pairs, 0, no_of_nodes - 1)) { + IGRAPH_ERROR("Cannot get edge IDs, invalid vertex ID", IGRAPH_EINVVID); } - IGRAPH_FINALLY(igraph_free, seen); - IGRAPH_CHECK(igraph_vector_resize(eids, n / 2)); + + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t from = VECTOR(*pairs)[2 * i]; + igraph_integer_t to = VECTOR(*pairs)[2 * i + 1]; eid = -1; - FIND_DIRECTED_EDGE(graph, from, to, &eid, seen); + FIND_DIRECTED_EDGE(graph, from, to, &eid); if (!directed && eid < 0) { - FIND_DIRECTED_EDGE(graph, to, from, &eid, seen); + FIND_DIRECTED_EDGE(graph, to, from, &eid); } 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); + 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, 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]; + igraph_integer_t from = VECTOR(*pairs)[2 * i]; + igraph_integer_t to = VECTOR(*pairs)[2 * i + 1]; eid = -1; - FIND_UNDIRECTED_EDGE(graph, from, to, &eid, seen); + FIND_UNDIRECTED_EDGE(graph, from, to, &eid); 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); + if (eid < 0 && error) { + IGRAPH_ERROR("Cannot get edge ID, no such edge", IGRAPH_EINVAL); } } } - IGRAPH_FREE(seen); - IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -#undef BINSEARCH #undef FIND_DIRECTED_EDGE #undef FIND_UNDIRECTED_EDGE +#define FIND_ALL_DIRECTED_EDGES(graph,xfrom,xto,eidvec) \ + do { \ + igraph_integer_t start = VECTOR(graph->os)[xfrom]; \ + igraph_integer_t end = VECTOR(graph->os)[xfrom+1]; \ + igraph_integer_t N = end; \ + igraph_integer_t start2 = VECTOR(graph->is)[xto]; \ + igraph_integer_t end2 = VECTOR(graph->is)[xto+1]; \ + igraph_integer_t N2 = end2; \ + igraph_integer_t eid = -1; \ + igraph_integer_t pos = -1; \ + if (end-start < end2-start2) { \ + BINSEARCH(start, end, xto, graph->oi, graph->to, N, &eid,&pos); \ + while (pos >= 0 && pos < N) { \ + eid = VECTOR(graph->oi)[pos++]; \ + if (VECTOR(graph->to)[eid] != xto) { break; } \ + IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ + } \ + } else { \ + BINSEARCH(start2, end2, xfrom, graph->ii, graph->from, N2, &eid, &pos); \ + while (pos >= 0 && pos < N2) { \ + eid = VECTOR(graph->ii)[pos++]; \ + if (VECTOR(graph->from)[eid] != xfrom) { break; } \ + IGRAPH_CHECK(igraph_vector_int_push_back(eidvec, eid)); \ + } \ + } \ + } while (0) + +#define FIND_ALL_UNDIRECTED_EDGES(graph,from,to,eidvec) \ + do { \ + igraph_integer_t xfrom1 = from > to ? from : to; \ + igraph_integer_t xto1 = from > to ? to : from; \ + FIND_ALL_DIRECTED_EDGES(graph, xfrom1, xto1, eidvec); \ + } while (0) + /** - * \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. + * \function igraph_get_all_eids_between + * \brief Returns all edge IDs between a pair of vertices. * * - * 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. + * For undirected graphs \c source and \c target are exchangeable. * * \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 source The ID of the source vertex + * \param target The ID of the target vertex * \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. + * Time complexity: TODO * - * \sa \ref igraph_get_eid() for a single edge, \ref - * igraph_get_eids() for a faster version that does not handle - * multiple edges. + * \sa \ref igraph_get_eid() for a single edge. */ +igraph_error_t igraph_get_all_eids_between( + const igraph_t *graph, igraph_vector_int_t *eids, + igraph_integer_t source, igraph_integer_t target, igraph_bool_t directed +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); -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); + if (source < 0 || source >= no_of_nodes) { + IGRAPH_ERROR("Cannot get edge IDs, invalid source vertex ID", IGRAPH_EINVVID); } + + if (target < 0 || target >= no_of_nodes) { + IGRAPH_ERROR("Cannot get edge IDs, invalid target vertex ID", IGRAPH_EINVVID); + } + + igraph_vector_int_clear(eids); + + if (igraph_is_directed(graph)) { + /* look in the specified direction first */ + FIND_ALL_DIRECTED_EDGES(graph, source, target, eids); + if (!directed) { + /* look in the reverse direction as well */ + FIND_ALL_DIRECTED_EDGES(graph, target, source, eids); + } + } else { + FIND_ALL_UNDIRECTED_EDGES(graph, source, target, eids); + } + + return IGRAPH_SUCCESS; } +#undef FIND_DIRECTED_EDGE +#undef FIND_UNDIRECTED_EDGE +#undef BINSEARCH + /** * \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 + * \param eids An initialized vector. It will be resized * to hold the result. - * \param pnode A vertex id. + * \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 @@ -1737,7 +1626,7 @@ int igraph_get_eids_multi(const igraph_t *graph, igraph_vector_t *eids, * 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_error_t igraph_incident(const igraph_t *graph, igraph_vector_int_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); @@ -1746,11 +1635,11 @@ int igraph_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer } } -int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integer_t pnode, +igraph_error_t igraph_i_incident(const igraph_t *graph, igraph_vector_int_t *eids, igraph_integer_t pnode, igraph_neimode_t mode, igraph_loops_t loops) { - long int length = 0, idx = 0; - long int i, j; - long int node = pnode; + igraph_integer_t length = 0, idx = 0; + igraph_integer_t i, j; + igraph_integer_t node = pnode; igraph_bool_t directed = igraph_is_directed(graph); if (node < 0 || node > igraph_vcount(graph) - 1) { @@ -1780,7 +1669,7 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ length += (VECTOR(graph->is)[node + 1] - VECTOR(graph->is)[node]); } - IGRAPH_CHECK(igraph_vector_resize(eids, length)); + IGRAPH_CHECK(igraph_vector_int_resize(eids, length)); /* The loops below produce an ordering what is consistent with the * ordering returned by igraph_neighbors(), and this should be preserved. @@ -1793,10 +1682,10 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ /* We did not ask for both directions; this is the easy case */ 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]; + j = VECTOR(graph->os)[node + 1]; + for (i = VECTOR(graph->os)[node]; i < j; i++) { + igraph_integer_t edge = VECTOR(graph->oi)[i]; + igraph_integer_t other = VECTOR(graph->to)[edge]; if (loops == IGRAPH_NO_LOOPS && other == pnode) { length--; } else { @@ -1806,10 +1695,10 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ } 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]; + j = VECTOR(graph->is)[node + 1]; + for (i = VECTOR(graph->is)[node]; i < j; i++) { + igraph_integer_t edge = VECTOR(graph->ii)[i]; + igraph_integer_t other = VECTOR(graph->from)[edge]; if ((loops == IGRAPH_NO_LOOPS || (loops == IGRAPH_LOOPS_ONCE && !directed)) && other == pnode) { length--; } else { @@ -1820,19 +1709,19 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ } 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; + igraph_integer_t j1 = VECTOR(graph->os)[node + 1]; + igraph_integer_t j2 = VECTOR(graph->is)[node + 1]; + igraph_integer_t i1 = VECTOR(graph->os)[node]; + igraph_integer_t i2 = VECTOR(graph->is)[node]; + igraph_integer_t eid1, eid2; + igraph_integer_t n1, n2; igraph_bool_t seen_loop_edge = 0; 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]; + eid1 = VECTOR(graph->oi)[i1]; + eid2 = VECTOR(graph->ii)[i2]; + n1 = VECTOR(graph->to)[eid1]; + n2 = VECTOR(graph->from)[eid2]; if (n1 < n2) { i1++; VECTOR(*eids)[idx++] = eid1; @@ -1876,7 +1765,7 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ VECTOR(*eids)[idx++] = eid2; } } - IGRAPH_CHECK(igraph_vector_resize(eids, length)); + IGRAPH_CHECK(igraph_vector_int_resize(eids, length)); return IGRAPH_SUCCESS; #undef DEDUPLICATE_IF_NEEDED } @@ -1911,15 +1800,15 @@ int igraph_i_incident(const igraph_t *graph, igraph_vector_t *eids, igraph_integ * * Time complexity: O(E), the number of edges in the graphs. * - * \sa igraph_isomorphic() to test if two graphs are isomorphic. + * \sa \ref 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; +igraph_error_t igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *res) { + igraph_integer_t nv1 = igraph_vcount(graph1); + igraph_integer_t nv2 = igraph_vcount(graph2); + igraph_integer_t ne1 = igraph_ecount(graph1); + igraph_integer_t ne2 = igraph_ecount(graph2); + igraph_integer_t i, eid1, eid2; *res = 0; /* Assume that the graphs differ */ @@ -1945,8 +1834,8 @@ int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_ * "target". */ for (i = 0; i < ne1; i++) { - eid1 = (long int) VECTOR(graph1->ii)[i]; - eid2 = (long int) VECTOR(graph2->ii)[i]; + eid1 = VECTOR(graph1->ii)[i]; + eid2 = VECTOR(graph2->ii)[i]; /* Check they have the same source */ if (IGRAPH_FROM(graph1, eid1) != IGRAPH_FROM(graph2, eid2)) { @@ -1962,3 +1851,22 @@ int igraph_is_same_graph(const igraph_t *graph1, const igraph_t *graph2, igraph_ *res = 1; /* No difference was found, graphs are the same */ return IGRAPH_SUCCESS; } + + +/* Reverses the direction of all edges in a directed graph. + * The graph is modified in-place. + * Attributes are preserved. + */ +igraph_error_t igraph_i_reverse(igraph_t *graph) { + + /* Nothing to do for undirected graphs. */ + if (! igraph_is_directed(graph)) { + return IGRAPH_SUCCESS; + } + + igraph_vector_int_swap(&graph->to, &graph->from); + igraph_vector_int_swap(&graph->oi, &graph->ii); + igraph_vector_int_swap(&graph->os, &graph->is); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/graph/visitors.c b/src/vendor/cigraph/src/graph/visitors.c index 82997c74f88..749e1b956ef 100644 --- a/src/vendor/cigraph/src/graph/visitors.c +++ b/src/vendor/cigraph/src/graph/visitors.c @@ -40,7 +40,7 @@ * * 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. + * vertex IDs. * * * Consider using \ref igraph_bfs_simple instead if you set most of the output @@ -65,24 +65,34 @@ * 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 + * 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 + * \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 parents If not a null pointer, then the id of the parent of + * each vertex is stored here. When a vertex was not visited + * during the traversal, -2 will be stored as the ID of its parent. + * When a vertex was visited during the traversal and it was one of + * the roots of the search trees, -1 will be stored as the ID of + * its parent. * \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. + * tree), then -1 is stored as the predecessor of the vertex. + * If the vertex was not visited at all, then -2 is stored for + * the predecessor of the vertex. * \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. + * tree), then -1 is stored as the successor of the vertex. + * If the vertex was not visited at all, then -2 is stored for + * the successor of the vertex. * \param dist If not a null pointer, then the distance from the root of - * the current search tree is stored here. + * the current search tree is stored here for each vertex. If a + * vertex was not reached during the traversal, its distance will + * be -1 in this vector. * \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. @@ -95,62 +105,54 @@ * \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_error_t igraph_bfs(const igraph_t *graph, + igraph_integer_t root, const igraph_vector_int_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, + const igraph_vector_int_t *restricted, + igraph_vector_int_t *order, igraph_vector_int_t *rank, + igraph_vector_int_t *parents, + igraph_vector_int_t *pred, igraph_vector_int_t *succ, + igraph_vector_int_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_error_t ret; + + igraph_dqueue_int_t Q; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t actroot = 0; + igraph_vector_bool_t added; igraph_lazy_adjlist_t adjlist; - long int act_rank = 0; - long int pred_vec = -1; + igraph_integer_t act_rank = 0; + igraph_integer_t pred_vec = -1; - long int rootpos = 0; - long int noroots = roots ? igraph_vector_size(roots) : 1; + igraph_integer_t rootpos = 0; + igraph_integer_t noroots = roots ? igraph_vector_int_size(roots) : 1; if (!roots && (root < 0 || root >= no_of_nodes)) { - IGRAPH_ERROR("Invalid root vertex in BFS", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID); } - 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 (roots && !igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Invalid root vertex in BFS.", IGRAPH_EINVVID); } - 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 (restricted && !igraph_vector_int_isininterval(restricted, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Invalid vertex ID in restricted set.", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + 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_VECTOR_BOOL_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); @@ -159,26 +161,28 @@ int igraph_bfs(const igraph_t *graph, 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); + igraph_integer_t i, n = igraph_vector_int_size(restricted); + igraph_vector_bool_fill(&added, true); for (i = 0; i < n; i++) { - long int v = (long int) VECTOR(*restricted)[i]; - VECTOR(added)[v] = 0; + igraph_integer_t v = VECTOR(*restricted)[i]; + VECTOR(added)[v] = false; } } /* 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); } +# define VINIT(v, initial) \ + if (v) { \ + IGRAPH_CHECK(igraph_vector_int_resize((v), no_of_nodes)); \ + igraph_vector_int_fill((v), initial); \ + } - VINIT(order); - VINIT(rank); - VINIT(father); - VINIT(pred); - VINIT(succ); - VINIT(dist); + VINIT(order, -1); + VINIT(rank, -1); + VINIT(parents, -2); + VINIT(pred, -2); + VINIT(succ, -2); + VINIT(dist, -1); # undef VINIT while (1) { @@ -187,7 +191,7 @@ int igraph_bfs(const igraph_t *graph, if (roots && rootpos < noroots) { /* We are still going through the 'roots' vector */ - actroot = (long int) VECTOR(*roots)[rootpos++]; + actroot = VECTOR(*roots)[rootpos++]; } else if (!roots && rootpos == 0) { /* We have a single root vertex given, and start now */ actroot = root; @@ -209,28 +213,30 @@ int igraph_bfs(const igraph_t *graph, 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; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actroot)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); + VECTOR(added)[actroot] = true; + if (parents) { + VECTOR(*parents)[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); + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actvect = igraph_dqueue_int_pop(&Q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&Q); + igraph_integer_t succ_vec; + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); + igraph_integer_t i, n; + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + n = igraph_vector_int_size(neis); if (pred) { VECTOR(*pred)[actvect] = pred_vec; } if (rank) { - VECTOR(*rank) [actvect] = act_rank; + VECTOR(*rank)[actvect] = act_rank; } if (order) { VECTOR(*order)[act_rank++] = actvect; @@ -240,31 +246,27 @@ int igraph_bfs(const igraph_t *graph, } for (i = 0; i < n; i++) { - long int nei = (long int) VECTOR(*neis)[i]; + igraph_integer_t nei = 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; + VECTOR(added)[nei] = true; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); + if (parents) { + VECTOR(*parents)[nei] = actvect; } } } - succ_vec = igraph_dqueue_empty(&Q) ? -1L : - (long int) igraph_dqueue_head(&Q); + succ_vec = igraph_dqueue_int_empty(&Q) ? -1L : + igraph_dqueue_int_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; + IGRAPH_CHECK_CALLBACK( + callback(graph, actvect, pred_vec, succ_vec, act_rank - 1, actdist, extra), + &ret + ); + + if (ret == IGRAPH_STOP) { + goto cleanup; } } @@ -277,12 +279,14 @@ int igraph_bfs(const igraph_t *graph, } /* for actroot < no_of_nodes */ +cleanup: + igraph_lazy_adjlist_destroy(&adjlist); - igraph_dqueue_destroy(&Q); - igraph_vector_char_destroy(&added); + igraph_dqueue_int_destroy(&Q); + igraph_vector_bool_destroy(&added); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -297,26 +301,26 @@ int igraph_bfs(const igraph_t *graph, * be ignored. * * \param graph The input graph. - * \param vid The id of the root vertex. + * \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 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 + * \param order 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 + * into \c order 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 + * are at distance i from the root, you need to look in the \c order * 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. + * each \em visited node. The values in the vector are set to -2 for + * vertices that were \em not visited, and -1 for the root vertex. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and @@ -324,17 +328,19 @@ int igraph_bfs(const igraph_t *graph, * * \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; +igraph_error_t igraph_bfs_simple( + const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_vector_int_t *order, igraph_vector_int_t *layers, + igraph_vector_int_t *parents +) { + + igraph_dqueue_int_t q; + igraph_integer_t num_visited = 0; + igraph_vector_int_t neis; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; + bool *added; + igraph_integer_t lastlayer = -1; if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; @@ -346,77 +352,79 @@ int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mo } /* temporary storage */ - added = IGRAPH_CALLOC(no_of_nodes, char); + added = IGRAPH_CALLOC(no_of_nodes, bool); if (added == 0) { - IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot calculate BFS", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); - IGRAPH_CHECK(igraph_dqueue_init(&q, 100)); - IGRAPH_FINALLY(igraph_dqueue_destroy, &q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); /* results */ - if (vids) { - igraph_vector_clear(vids); + if (order) { + igraph_vector_int_clear(order); } if (layers) { - igraph_vector_clear(layers); + igraph_vector_int_clear(layers); } if (parents) { - IGRAPH_CHECK(igraph_vector_resize(parents, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + igraph_vector_int_fill(parents, -2); } - /* ok start with vid */ - IGRAPH_CHECK(igraph_dqueue_push(&q, vid)); - IGRAPH_CHECK(igraph_dqueue_push(&q, 0)); + /* ok start with root */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, root)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); if (layers) { - IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); } - if (vids) { - IGRAPH_CHECK(igraph_vector_push_back(vids, vid)); + if (order) { + IGRAPH_CHECK(igraph_vector_int_push_back(order, root)); } if (parents) { - VECTOR(*parents)[(long int)vid] = vid; + VECTOR(*parents)[root] = -1; } num_visited++; - added[(long int)vid] = 1; + added[root] = true; - 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, + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actvect = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, 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; + igraph_integer_t nei_count = igraph_vector_int_size(&neis); + for (i = 0; i < nei_count; i++) { + igraph_integer_t neighbor = VECTOR(neis)[i]; + if (! added[neighbor]) { + added[neighbor] = true; if (parents) { VECTOR(*parents)[neighbor] = actvect; } - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); if (layers && lastlayer != actdist + 1) { - IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); } - if (vids) { - IGRAPH_CHECK(igraph_vector_push_back(vids, neighbor)); + if (order) { + IGRAPH_CHECK(igraph_vector_int_push_back(order, neighbor)); } num_visited++; lastlayer = actdist + 1; } } /* for i in neis */ - } /* while ! dqueue_empty */ + } /* while ! dqueue_int_empty */ if (layers) { - IGRAPH_CHECK(igraph_vector_push_back(layers, num_visited)); + IGRAPH_CHECK(igraph_vector_int_push_back(layers, num_visited)); } - igraph_vector_destroy(&neis); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -432,7 +440,7 @@ int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mo * * 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. + * vertex IDs. * * \param graph The input graph. * \param root The id of the root vertex. @@ -445,15 +453,22 @@ int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mo * 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 + * \param order If not null pointer, then the vertex IDs of the graph are + * stored here, in the same order as they were discovered. The tail of + * the vector will be padded with -1 to ensure that the length of the + * vector is the same as the number of vertices, even if some vertices + * were not visited during the traversal. + * \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. + * their subtree. The tail of the vector will be padded with -1 to ensure + * that the length of the vector is the same as the number of vertices, + * even if some vertices were not visited during the traversal. + * \param parents If not a null pointer, then the id of the parent of + * each vertex is stored here. -1 will be stored for the root of the + * search tree; -2 will be stored for vertices that were not visited. * \param dist If not a null pointer, then the distance from the root of - * the current search tree is stored here. + * the current search tree is stored here. -1 will be stored for vertices + * that were not visited. * \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. @@ -467,23 +482,24 @@ int igraph_bfs_simple(igraph_t *graph, igraph_integer_t vid, igraph_neimode_t mo * edges. */ -int igraph_dfs(const igraph_t *graph, igraph_integer_t root, +igraph_error_t 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_vector_int_t *order, + igraph_vector_int_t *order_out, igraph_vector_int_t *parents, + igraph_vector_int_t *dist, igraph_dfshandler_t *in_callback, igraph_dfshandler_t *out_callback, void *extra) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_lazy_adjlist_t adjlist; - igraph_stack_t stack; + igraph_stack_int_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; + igraph_vector_int_t nptr; + igraph_error_t ret; + igraph_integer_t actroot; + igraph_integer_t act_rank = 0; + igraph_integer_t rank_out = 0; + igraph_integer_t act_dist = 0; if (root < 0 || root >= no_of_nodes) { IGRAPH_ERROR("Invalid root vertex for DFS", IGRAPH_EINVAL); @@ -500,56 +516,56 @@ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, 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_stack_int_init(&stack, 100)); + IGRAPH_FINALLY(igraph_stack_int_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); + IGRAPH_CHECK(igraph_vector_int_init(&nptr, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &nptr); # define FREE_ALL() do { \ - igraph_vector_long_destroy(&nptr); \ + igraph_vector_int_destroy(&nptr); \ igraph_lazy_adjlist_destroy(&adjlist); \ - igraph_stack_destroy(&stack); \ + igraph_stack_int_destroy(&stack); \ igraph_vector_char_destroy(&added); \ IGRAPH_FINALLY_CLEAN(4); } while (0) - /* Resize result vectors and fill them with IGRAPH_NAN */ + /* Resize result vectors and fill them with the initial value */ -# define VINIT(v) if (v) { \ - igraph_vector_resize(v, no_of_nodes); \ - igraph_vector_fill(v, IGRAPH_NAN); } +# define VINIT(v, initial) if (v) { \ + IGRAPH_CHECK(igraph_vector_int_resize(v, no_of_nodes)); \ + igraph_vector_int_fill(v, initial); } - VINIT(order); - VINIT(order_out); - VINIT(father); - VINIT(dist); + VINIT(order, -1); + VINIT(order_out, -1); + VINIT(parents, -2); + VINIT(dist, -1); # undef VINIT - IGRAPH_CHECK(igraph_stack_push(&stack, root)); - VECTOR(added)[(long int)root] = 1; - if (father) { - VECTOR(*father)[(long int)root] = -1; + IGRAPH_CHECK(igraph_stack_int_push(&stack, root)); + VECTOR(added)[root] = 1; + if (parents) { + VECTOR(*parents)[root] = -1; } if (order) { VECTOR(*order)[act_rank++] = root; } if (dist) { - VECTOR(*dist)[(long int)root] = 0; + VECTOR(*dist)[root] = 0; } if (in_callback) { - igraph_bool_t terminate = in_callback(graph, root, 0, extra); - if (terminate) { + IGRAPH_CHECK_CALLBACK(in_callback(graph, root, 0, extra), &ret); + if (ret == IGRAPH_STOP) { FREE_ALL(); - return 0; + return IGRAPH_SUCCESS; } } for (actroot = 0; actroot < no_of_nodes; ) { /* 'root' first, then all other vertices */ - if (igraph_stack_empty(&stack)) { + if (igraph_stack_int_empty(&stack)) { if (!unreachable) { break; } @@ -557,10 +573,10 @@ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, actroot++; continue; } - IGRAPH_CHECK(igraph_stack_push(&stack, actroot)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, actroot)); VECTOR(added)[actroot] = 1; - if (father) { - VECTOR(*father)[actroot] = -1; + if (parents) { + VECTOR(*parents)[actroot] = -1; } if (order) { VECTOR(*order)[act_rank++] = actroot; @@ -570,37 +586,38 @@ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, } if (in_callback) { - igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) actroot, - 0, extra); - if (terminate) { + IGRAPH_CHECK_CALLBACK(in_callback(graph, actroot, 0, extra), &ret); + if (ret == IGRAPH_STOP) { FREE_ALL(); - return 0; + return IGRAPH_SUCCESS; } } + 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); + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t actvect = igraph_stack_int_top(&stack); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, actvect); + igraph_integer_t n = igraph_vector_int_size(neis); + igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, actvect); + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); /* Search for a neighbor that was not yet visited */ - igraph_bool_t any = 0; - long int nei = 0; + igraph_bool_t any = false; + igraph_integer_t nei = 0; while (!any && (*ptr) < n) { - nei = (long int) VECTOR(*neis)[(*ptr)]; + nei = VECTOR(*neis)[(*ptr)]; any = !VECTOR(added)[nei]; (*ptr) ++; } if (any) { /* There is such a neighbor, add it */ - IGRAPH_CHECK(igraph_stack_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); VECTOR(added)[nei] = 1; - if (father) { - VECTOR(*father)[ nei ] = actvect; + if (parents) { + VECTOR(*parents)[ nei ] = actvect; } if (order) { VECTOR(*order)[act_rank++] = nei; @@ -611,30 +628,33 @@ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, } if (in_callback) { - igraph_bool_t terminate = in_callback(graph, (igraph_integer_t) nei, - (igraph_integer_t) act_dist, - extra); - if (terminate) { + IGRAPH_CHECK_CALLBACK( + in_callback(graph, nei, act_dist, extra), + &ret + ); + if (ret == IGRAPH_STOP) { FREE_ALL(); - return 0; + return IGRAPH_SUCCESS; } } } else { /* There is no such neighbor, finished with the subtree */ - igraph_stack_pop(&stack); + igraph_stack_int_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) { + IGRAPH_CHECK_CALLBACK( + out_callback(graph, actvect, act_dist, extra), + &ret + ); + + if (ret == IGRAPH_STOP) { FREE_ALL(); - return 0; + return IGRAPH_SUCCESS; } } } @@ -644,5 +664,5 @@ int igraph_dfs(const igraph_t *graph, igraph_integer_t root, FREE_ALL(); # undef FREE_ALL - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/hrg/dendro.h b/src/vendor/cigraph/src/hrg/dendro.h index a2d092bc93f..113968a5ffd 100644 --- a/src/vendor/cigraph/src/hrg/dendro.h +++ b/src/vendor/cigraph/src/hrg/dendro.h @@ -252,7 +252,7 @@ class dendro { int computeEdgeCount(const int, const short int, const int, const short int); // (consensus tree) counts children - int countChildren(const std::string); + size_t 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 @@ -290,7 +290,7 @@ class dendro { // make single MCMC move bool monteCarloMove(double&, bool&, const double); // record consensus tree from splithist - void recordConsensusTree(igraph_vector_t *parents, + void recordConsensusTree(igraph_vector_int_t *parents, igraph_vector_t *weights); // record D structure void recordDendrogramStructure(igraph_hrg_t *hrg); @@ -303,9 +303,9 @@ class dendro { // reset the dendrograph structures void resetDendrograph(); // sample dendrogram's splits and update the split histogram - bool sampleSplitLikelihoods(int&); + bool sampleSplitLikelihoods(igraph_integer_t&); // reset splits histogram - void resetAllSplits(); + //void resetAllSplits(); }; } // namespace fitHRG diff --git a/src/vendor/cigraph/src/hrg/graph.h b/src/vendor/cigraph/src/hrg/graph.h index f990f9391ba..43e501bee63 100644 --- a/src/vendor/cigraph/src/hrg/graph.h +++ b/src/vendor/cigraph/src/hrg/graph.h @@ -61,6 +61,7 @@ #ifndef IGRAPH_HRG_GRAPH #define IGRAPH_HRG_GRAPH +#include #include "hrg/rbtree.h" #include @@ -144,7 +145,7 @@ class graph { // clear all links from graph void resetLinks(); // allocate edge histograms - void setAdjacencyHistograms(const int); + void setAdjacencyHistograms(const igraph_integer_t); // set name of vertex i bool setName(const int, const std::string); diff --git a/src/vendor/cigraph/src/hrg/hrg.cc b/src/vendor/cigraph/src/hrg/hrg.cc index 58642376e59..ee43f1c4bee 100644 --- a/src/vendor/cigraph/src/hrg/hrg.cc +++ b/src/vendor/cigraph/src/hrg/hrg.cc @@ -21,15 +21,19 @@ */ +#include "hrg/dendro.h" +#include "hrg/graph.h" +#include "hrg/graph_simp.h" + #include "igraph_interface.h" -#include "igraph_memory.h" #include "igraph_attributes.h" #include "igraph_hrg.h" #include "igraph_random.h" +#include "igraph_structural.h" -#include "hrg/dendro.h" -#include "hrg/graph.h" -#include "hrg/graph_simp.h" +#include "core/exceptions.h" + +#include using namespace fitHRG; @@ -79,7 +83,7 @@ struct pblock { }; } -static int markovChainMonteCarlo(dendro *d, unsigned int period, +static igraph_error_t markovChainMonteCarlo(dendro *d, igraph_integer_t period, igraph_hrg_t *hrg) { igraph_real_t bestL = d->getLikelihood(); @@ -98,10 +102,12 @@ static int markovChainMonteCarlo(dendro *d, unsigned int period, // 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++) { + for (igraph_integer_t i = 0; i < period; i++) { // make a MCMC move - IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); + if (!d->monteCarloMove(dL, flag_taken, 1.0)) { + return IGRAPH_FAILURE; + } // get likelihood of this D given G igraph_real_t cl = d->getLikelihood(); @@ -115,13 +121,14 @@ static int markovChainMonteCarlo(dendro *d, unsigned int period, // corrects floating-point errors O(n) d->refreshLikelihood(); - return 0; + return IGRAPH_SUCCESS; } -static int markovChainMonteCarlo2(dendro *d, int num_samples) { +static igraph_error_t markovChainMonteCarlo2(dendro *d, igraph_integer_t 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(); + igraph_integer_t sample_num = 0; + int 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 @@ -144,10 +151,10 @@ static int markovChainMonteCarlo2(dendro *d, int num_samples) { d->refreshLikelihood(); // TODO: less frequently } - return 0; + return IGRAPH_SUCCESS; } -static int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { +static igraph_error_t 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 @@ -165,7 +172,9 @@ static int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { oldMeanL = newMeanL; newMeanL = 0.0; for (int i = 0; i < 65536; i++) { - IGRAPH_CHECK(! d->monteCarloMove(dL, flag_taken, 1.0)); + if (!d->monteCarloMove(dL, flag_taken, 1.0)) { + return IGRAPH_FAILURE; + } Likeli = d->getLikelihood(); newMeanL += Likeli; } @@ -181,27 +190,33 @@ static int MCMCEquilibrium_Find(dendro *d, igraph_hrg_t *hrg) { d->recordDendrogramStructure(hrg); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_hrg_getgraph(const igraph_t *igraph, +static igraph_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(igraph); + igraph_integer_t no_of_edges = igraph_ecount(igraph); + igraph_integer_t i; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for the HRG module.", IGRAPH_EOVERFLOW); + } + // TODO: Can this be relaxed? buildDendrogram() creates a tree with n-2 internal edges, + // i.e. zero internal edges for a 2-vertex graph. This is not handled at the moment. if (no_of_nodes < 3) { IGRAPH_ERROR("Graph must have at least 3 vertices for HRG, got only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL); } // Create graph - d->g = new graph(no_of_nodes); + d->g = new graph((int) 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); + int from = (int) IGRAPH_FROM(igraph, i); + int to = (int) IGRAPH_TO(igraph, i); if (from == to) { continue; } @@ -215,29 +230,34 @@ static int igraph_i_hrg_getgraph(const igraph_t *igraph, d->buildDendrogram(); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_hrg_getsimplegraph(const igraph_t *igraph, +static igraph_error_t igraph_i_hrg_getsimplegraph(const igraph_t *igraph, dendro *d, simpleGraph **sg, - int num_bins) { + igraph_integer_t num_bins) { - int no_of_nodes = igraph_vcount(igraph); - int no_of_edges = igraph_ecount(igraph); - int i; + igraph_integer_t no_of_nodes = igraph_vcount(igraph); + igraph_integer_t no_of_edges = igraph_ecount(igraph); + igraph_integer_t i; + + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for the HRG module.", IGRAPH_EOVERFLOW); + } + // TODO: See analogous TODO item in igraph_i_hrg_getgraph() if (no_of_nodes < 3) { IGRAPH_ERROR("Graph must have at least 3 vertices for HRG, got only %" IGRAPH_PRId " vertices.", IGRAPH_EINVAL); } // Create graphs - d->g = new graph(no_of_nodes, true); + d->g = new graph((int) no_of_nodes, true); d->g->setAdjacencyHistograms(num_bins); - (*sg) = new simpleGraph(no_of_nodes); + (*sg) = new simpleGraph((int) no_of_nodes); for (i = 0; i < no_of_edges; i++) { - int from = IGRAPH_FROM(igraph, i); - int to = IGRAPH_TO(igraph, i); + int from = (int) IGRAPH_FROM(igraph, i); + int to = (int) IGRAPH_TO(igraph, i); if (from == to) { continue; } @@ -257,27 +277,16 @@ static int igraph_i_hrg_getsimplegraph(const igraph_t *igraph, d->buildDendrogram(); - return 0; -} - -static void igraph_i_delete_dendrogram(dendro* d) { - delete d; -} - -static void igraph_i_delete_simple_graph(simpleGraph* g) { - delete g; -} - -static void igraph_i_clear_pblock_array(pblock* arr) { - delete [] arr; + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_init - * Allocate memory for a HRG. + * \brief 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. @@ -286,38 +295,45 @@ static void igraph_i_clear_pblock_array(pblock* arr) { * 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_error_t igraph_hrg_init(igraph_hrg_t *hrg, igraph_integer_t n) { + if (n < 0) { + IGRAPH_ERRORF("Number of vertices should not be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, n); + } + if (n == 0) { + n = 1; + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->left, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->right, n - 1); + IGRAPH_VECTOR_INIT_FINALLY (&hrg->prob, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->edges, n - 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&hrg->vertices, n - 1); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_destroy - * Deallocate memory for an HRG. + * \brief 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_int_destroy(&hrg->left); + igraph_vector_int_destroy(&hrg->right); igraph_vector_destroy(&hrg->prob); - igraph_vector_destroy(&hrg->edges); - igraph_vector_destroy(&hrg->vertices); + igraph_vector_int_destroy(&hrg->edges); + igraph_vector_int_destroy(&hrg->vertices); } /** * \function igraph_hrg_size - * Returns the size of the HRG, the number of leaf nodes. + * \brief 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. @@ -325,13 +341,13 @@ void igraph_hrg_destroy(igraph_hrg_t *hrg) { * Time complexity: O(1). */ -int igraph_hrg_size(const igraph_hrg_t *hrg) { - return igraph_vector_size(&hrg->left) + 1; +igraph_integer_t igraph_hrg_size(const igraph_hrg_t *hrg) { + return igraph_vector_int_size(&hrg->left) + 1; } /** * \function igraph_hrg_resize - * Resize a HRG. + * \brief Resize a HRG. * * \param hrg Pointer to an initialized (see \ref igraph_hrg_init) * HRG. @@ -341,35 +357,43 @@ int igraph_hrg_size(const igraph_hrg_t *hrg) { * 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); +igraph_error_t igraph_hrg_resize(igraph_hrg_t *hrg, igraph_integer_t newsize) { + igraph_integer_t origsize = igraph_hrg_size(hrg); + + /* The data structure must be left in a consistent state if resizing fails. */ + +#define CHECK_ERR(expr) \ + do { \ + igraph_error_t err = (expr); \ + if (err != IGRAPH_SUCCESS) { \ + igraph_vector_int_resize(&hrg->left, origsize); \ + igraph_vector_int_resize(&hrg->right, origsize); \ + igraph_vector_resize(&hrg->prob, origsize); \ + igraph_vector_int_resize(&hrg->edges, origsize); \ + igraph_vector_int_resize(&hrg->vertices, origsize); \ + IGRAPH_FINALLY_EXIT(); \ + IGRAPH_ERROR("Cannot resize HRG.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ + } \ + } while (0) + + IGRAPH_FINALLY_ENTER(); + { + CHECK_ERR(igraph_vector_int_resize(&hrg->left, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->right, newsize - 1)); + CHECK_ERR(igraph_vector_resize(&hrg->prob, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->edges, newsize - 1)); + CHECK_ERR(igraph_vector_int_resize(&hrg->vertices, newsize - 1)); } + IGRAPH_FINALLY_EXIT(); - return 0; +#undef CHECK_ERR + + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_fit - * Fit a hierarchical random graph model to a network + * \brief 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. @@ -378,7 +402,7 @@ int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize) { * 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. + * HRG model. * \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. @@ -387,12 +411,14 @@ int igraph_hrg_resize(igraph_hrg_t *hrg, int newsize) { * Time complexity: TODO. */ -int igraph_hrg_fit(const igraph_t *graph, +igraph_error_t igraph_hrg_fit(const igraph_t *graph, igraph_hrg_t *hrg, igraph_bool_t start, - int steps) { + igraph_integer_t steps) { - int no_of_nodes = igraph_vcount(graph); + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + + igraph_integer_t no_of_nodes = igraph_vcount(graph); RNG_BEGIN(); @@ -401,7 +427,7 @@ int igraph_hrg_fit(const igraph_t *graph, // If we want to start from HRG if (start) { if (igraph_hrg_size(hrg) != no_of_nodes) { - IGRAPH_ERROR("Invalid HRG to start from", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid HRG to start from.", IGRAPH_EINVAL); } // Convert the igraph graph IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, &d)); @@ -422,180 +448,204 @@ int igraph_hrg_fit(const igraph_t *graph, RNG_END(); - return 0; + return IGRAPH_SUCCESS; + IGRAPH_HANDLE_EXCEPTIONS_END } /** * \function igraph_hrg_sample - * Sample from a hierarchical random graph model + * \brief 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. + * This function draws a single sample from a hierarchical random graph model. + * + * \param hrg A HRG model to sample from + * \param sample Pointer to an uninitialized graph; the sample is stored here. * \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) { +igraph_error_t igraph_hrg_sample(const igraph_hrg_t *hrg, igraph_t *sample) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + dendro d; - int i; - dendro *d; + // TODO: error handling - if (no_samples < 0) { - IGRAPH_ERROR("Number of samples must be non-negative", IGRAPH_EINVAL); - } + RNG_BEGIN(); - if (!sample && !samples) { - IGRAPH_ERROR("Give at least one of `sample' and `samples'", - IGRAPH_EINVAL); - } + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + d.makeRandomGraph(); + d.recordGraphStructure(sample); - if (no_samples != 1 && sample) { - IGRAPH_ERROR("Number of samples should be one if `sample' is given", - IGRAPH_EINVAL); - } + RNG_END(); - if (no_samples > 1 && !samples) { - IGRAPH_ERROR("`samples' must be non-null if number of samples " - "is larger than 1", IGRAPH_EINVAL); - } + return IGRAPH_SUCCESS; + IGRAPH_HANDLE_EXCEPTIONS_END +} - if (!start && !input_graph) { - IGRAPH_ERROR("Input graph must be given if initial HRG is not used", - IGRAPH_EINVAL); - } +/** + * \function igraph_hrg_sample_many + * \brief Draw multiple samples from a hierarchical random graph model. + * + * This function draws multiple samples from a hierarchical random graph + * ensemble. The ensemble can be given as a graph (\c input_graph), or as an + * 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 new graph. + * + * \param hrg A HRG model to sample from + * \param samples An initialized graph list that will contain the sampled + * graphs. Note that existing graphs in the graph list are \em not removed + * so make sure you supply an empty list if you do not need the old contents + * of the list. + * \param num_samples The number of samples to generate. + * \return Error code. + * + * Time complexity: TODO. + */ - if (!start) { - IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(input_graph))); - } +igraph_error_t igraph_hrg_sample_many( + const igraph_hrg_t *hrg, igraph_graph_list_t *samples, + igraph_integer_t num_samples +) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + igraph_t g; + dendro d; - if (input_graph && igraph_hrg_size(hrg) != igraph_vcount(input_graph)) { - IGRAPH_ERROR("Invalid HRG size, should match number of nodes", - IGRAPH_EINVAL); + if (num_samples < 0) { + IGRAPH_ERROR("Number of samples must be non-negative.", IGRAPH_EINVAL); } - RNG_BEGIN(); - - d = new dendro; - IGRAPH_FINALLY(igraph_i_delete_dendrogram, d); - - // Need to find equilibrium first? - if (start) { - d->clearDendrograph(); - d->importDendrogramStructure(hrg); - } else { - IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); + if (num_samples == 0) { + return IGRAPH_SUCCESS; } - // TODO: free on error + RNG_BEGIN(); - 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; - } + d.clearDendrograph(); + d.importDendrogramStructure(hrg); + while (num_samples > 0) { + d.makeRandomGraph(); + d.recordGraphStructure(&g); + IGRAPH_FINALLY(igraph_destroy, &g); + IGRAPH_CHECK(igraph_graph_list_push_back(samples, &g)); + IGRAPH_FINALLY_CLEAN(1); } - delete d; - IGRAPH_FINALLY_CLEAN(1); - RNG_END(); - return 0; + return IGRAPH_SUCCESS; + IGRAPH_HANDLE_EXCEPTIONS_END } /** * \function igraph_hrg_game - * Generate a hierarchical random graph + * \brief 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. + * 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. + * \param hrg The hierarchical random graph model to sample from. * \return Error code. * * Time complexity: TODO. */ -int igraph_hrg_game(igraph_t *graph, +igraph_error_t 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); + return igraph_hrg_sample(hrg, graph); +} + +/** + * \function igraph_from_hrg_dendrogram + * \brief Create a graph representation of the dendrogram of a hierarchical random graph model. + * + * Creates the igraph graph equivalent of the dendrogram encoded in an + * \ref igraph_hrg_t data structure. The probabilities associated to the + * nodes are returned in a vector so this function works without an + * attribute handler. + * + * \param graph Pointer to an uninitialized graph, the result is + * stored here. + * \param hrg The hierarchical random graph to convert. + * \param prob Pointer to an \em initialized vector; the probabilities + * associated to the nodes of the dendrogram will be stored here. Leaf nodes + * will have an associated probability of \c IGRAPH_NAN . + * You may set this to \c NULL if you do not need the probabilities. + * \return Error code. + * + * Time complexity: O(n), the number of vertices in the graph. + */ + +igraph_error_t igraph_from_hrg_dendrogram( + igraph_t *graph, const igraph_hrg_t *hrg, igraph_vector_t *prob +) { + igraph_integer_t orig_nodes = igraph_hrg_size(hrg); + igraph_integer_t no_of_nodes = orig_nodes * 2 - 1; + igraph_integer_t no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + igraph_vector_int_t edges; + igraph_integer_t i, idx = 0; + + // Probability labels, for leaf nodes they are IGRAPH_NAN + if (prob) { + IGRAPH_CHECK(igraph_vector_resize(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_INT_INIT_FINALLY(&edges, no_of_edges * 2); + + for (i = 0; i < orig_nodes - 1; i++) { + igraph_integer_t left = VECTOR(hrg->left)[i]; + igraph_integer_t 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, NULL)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, NULL)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(2); // + 1 for graph + + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_dendrogram - * Create a dendrogram from a hierarchical random graph. + * \brief 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. + * + * \deprecated-by igraph_hrg_dendrogram 0.10.5 */ - -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_error_t igraph_hrg_dendrogram(igraph_t *graph, const igraph_hrg_t *hrg) { + igraph_integer_t orig_nodes = igraph_hrg_size(hrg); + igraph_integer_t no_of_nodes = orig_nodes * 2 - 1; + igraph_integer_t no_of_edges = no_of_nodes > 0 ? no_of_nodes - 1 : 0; + igraph_vector_int_t edges; + igraph_integer_t i, idx = 0; igraph_vector_ptr_t vattrs; igraph_vector_t prob; igraph_attribute_record_t rec = { "probability", @@ -612,14 +662,14 @@ int igraph_hrg_dendrogram(igraph_t *graph, VECTOR(prob)[orig_nodes + i] = VECTOR(hrg->prob)[i]; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t left = VECTOR(hrg->left)[i]; + igraph_integer_t right = VECTOR(hrg->right)[i]; VECTOR(edges)[idx++] = orig_nodes + i; VECTOR(edges)[idx++] = left < 0 ? orig_nodes - left - 1 : left; @@ -630,19 +680,19 @@ int igraph_hrg_dendrogram(igraph_t *graph, 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_CHECK(igraph_add_edges(graph, &edges, NULL)); igraph_vector_ptr_destroy(&vattrs); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&prob); IGRAPH_FINALLY_CLEAN(4); // + 1 for graph - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_consensus - * Calculate a consensus tree for a HRG. + * \brief 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. @@ -651,8 +701,8 @@ int igraph_hrg_dendrogram(igraph_t *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. + * 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. @@ -668,23 +718,23 @@ int igraph_hrg_dendrogram(igraph_t *graph, * Time complexity: TODO. */ -int igraph_hrg_consensus(const igraph_t *graph, - igraph_vector_t *parents, +igraph_error_t igraph_hrg_consensus(const igraph_t *graph, + igraph_vector_int_t *parents, igraph_vector_t *weights, igraph_hrg_t *hrg, igraph_bool_t start, - int num_samples) { + igraph_integer_t num_samples) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN dendro *d; if (start && !hrg) { - IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); + IGRAPH_ERROR("`hrg' must be given if `start' is true.", IGRAPH_EINVAL); } RNG_BEGIN(); d = new dendro; - IGRAPH_FINALLY(igraph_i_delete_dendrogram, d); if (start) { IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); @@ -693,7 +743,7 @@ int igraph_hrg_consensus(const igraph_t *graph, } else { IGRAPH_CHECK(igraph_i_hrg_getgraph(graph, d)); if (hrg) { - IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(graph))); + igraph_hrg_resize(hrg, igraph_vcount(graph)); } IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } @@ -703,14 +753,15 @@ int igraph_hrg_consensus(const igraph_t *graph, d->recordConsensusTree(parents, weights); delete d; - IGRAPH_FINALLY_CLEAN(1); RNG_END(); - return 0; + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END } -static int MCMCEquilibrium_Sample(dendro *d, int num_samples) { +static igraph_error_t MCMCEquilibrium_Sample(dendro *d, igraph_integer_t num_samples) { // Because moves in the dendrogram space are chosen (Monte // Carlo) so that we sample dendrograms with probability @@ -725,8 +776,8 @@ static int MCMCEquilibrium_Sample(dendro *d, int num_samples) { double dL; bool flag_taken; - int sample_num = 0; - int t = 1, thresh = 100 * d->g->numNodes(); + igraph_integer_t sample_num = 0; + igraph_integer_t t = 1, thresh = 100 * d->g->numNodes(); double ptest = 1.0 / 10.0 / d->g->numNodes(); while (sample_num < num_samples) { @@ -739,10 +790,10 @@ static int MCMCEquilibrium_Sample(dendro *d, int num_samples) { t++; } - return 0; + return IGRAPH_SUCCESS; } -static int QsortPartition (pblock* array, int left, int right, int index) { +static int QsortPartition (pblock* array, igraph_integer_t left, igraph_integer_t right, igraph_integer_t index) { pblock p_value, temp; p_value.L = array[index].L; p_value.i = array[index].i; @@ -759,8 +810,8 @@ static int QsortPartition (pblock* array, int left, int right, int index) { array[index].i = temp.i; array[index].j = temp.j; - int stored = left; - for (int i = left; i < right; i++) { + igraph_integer_t stored = left; + for (igraph_integer_t i = left; i < right; i++) { if (array[i].L <= p_value.L) { // swap(array[stored], array[i]) temp.L = array[i].L; @@ -789,17 +840,17 @@ static int QsortPartition (pblock* array, int left, int right, int index) { return stored; } -static void QsortMain (pblock* array, int left, int right) { +static void QsortMain (pblock* array, igraph_integer_t left, igraph_integer_t right) { if (right > left) { - int pivot = left; - int part = QsortPartition(array, left, right, pivot); + igraph_integer_t pivot = left; + igraph_integer_t part = QsortPartition(array, left, right, pivot); QsortMain(array, left, part - 1); QsortMain(array, part + 1, right ); } return; } -static int rankCandidatesByProbability(simpleGraph *sg, dendro *d, +static igraph_error_t rankCandidatesByProbability(simpleGraph *sg, dendro *d, pblock *br_list, int mk) { int mkk = 0; int n = sg->getNumNodes(); @@ -818,13 +869,13 @@ static int rankCandidatesByProbability(simpleGraph *sg, dendro *d, // Sort the candidates by their average probability QsortMain(br_list, 0, mk - 1); - return 0; + return IGRAPH_SUCCESS; } -static int recordPredictions(pblock *br_list, igraph_vector_t *edges, +static igraph_error_t recordPredictions(pblock *br_list, igraph_vector_int_t *edges, igraph_vector_t *prob, int mk) { - IGRAPH_CHECK(igraph_vector_resize(edges, mk * 2)); + IGRAPH_CHECK(igraph_vector_int_resize(edges, mk * 2)); IGRAPH_CHECK(igraph_vector_resize(prob, mk)); for (int i = mk - 1, idx = 0, idx2 = 0; i >= 0; i--) { @@ -833,15 +884,16 @@ static int recordPredictions(pblock *br_list, igraph_vector_t *edges, VECTOR(*prob)[idx2++] = br_list[i].L; } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_hrg_predict - * Predict missing edges in a graph, based on HRG models + * \brief 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, @@ -859,28 +911,29 @@ static int recordPredictions(pblock *br_list, igraph_vector_t *edges, * Time complexity: TODO. */ -int igraph_hrg_predict(const igraph_t *graph, - igraph_vector_t *edges, +igraph_error_t igraph_hrg_predict(const igraph_t *graph, + igraph_vector_int_t *edges, igraph_vector_t *prob, igraph_hrg_t *hrg, igraph_bool_t start, - int num_samples, - int num_bins) { + igraph_integer_t num_samples, + igraph_integer_t num_bins) { + IGRAPH_HANDLE_EXCEPTIONS_BEGIN + dendro *d; pblock *br_list; int mk; simpleGraph *sg; if (start && !hrg) { - IGRAPH_ERROR("`hrg' must be given is `start' is true", IGRAPH_EINVAL); + IGRAPH_ERROR("`hrg' must be given if `start' is true.", IGRAPH_EINVAL); } RNG_BEGIN(); - dendro d; + d = new dendro; - IGRAPH_CHECK(igraph_i_hrg_getsimplegraph(graph, &d, &sg, num_bins)); - IGRAPH_FINALLY(igraph_i_delete_simple_graph, sg); + 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]; @@ -889,34 +942,35 @@ int igraph_hrg_predict(const igraph_t *graph, br_list[i].i = -1; br_list[i].j = -1; } - IGRAPH_FINALLY(igraph_i_clear_pblock_array, br_list); if (start) { - d.clearDendrograph(); - d.importDendrogramStructure(hrg); + d->clearDendrograph(); + d->importDendrogramStructure(hrg); } else { if (hrg) { - IGRAPH_CHECK(igraph_hrg_resize(hrg, igraph_vcount(graph))); + igraph_hrg_resize(hrg, igraph_vcount(graph)); } - IGRAPH_CHECK(MCMCEquilibrium_Find(&d, hrg)); + IGRAPH_CHECK(MCMCEquilibrium_Find(d, hrg)); } - IGRAPH_CHECK(MCMCEquilibrium_Sample(&d, num_samples)); - IGRAPH_CHECK(rankCandidatesByProbability(sg, &d, br_list, mk)); + 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; - IGRAPH_FINALLY_CLEAN(2); RNG_END(); - return 0; + return IGRAPH_SUCCESS; + + IGRAPH_HANDLE_EXCEPTIONS_END } /** * \function igraph_hrg_create - * Create a HRG from an igraph graph. + * \brief Create a HRG from an igraph graph. * * \param hrg Pointer to an initialized \ref igraph_hrg_t. The result * is stored here. @@ -924,25 +978,25 @@ int igraph_hrg_predict(const igraph_t *graph, * 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. + * internal nodes of the hierarchical random graph. * \return Error code. * * Time complexity: O(n), the number of vertices in the tree. */ -int igraph_hrg_create(igraph_hrg_t *hrg, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_internal = no_of_nodes > 0 ? (no_of_nodes - 1) / 2 : 0; + igraph_vector_int_t deg, idx; + igraph_integer_t root = 0; + igraph_integer_t d0 = 0, d1 = 0, d2 = 0; + igraph_integer_t ii = 0, il = 0; + igraph_vector_int_t neis; igraph_vector_t path; + igraph_bool_t simple; // -------------------------------------------------------- // CHECKS @@ -950,50 +1004,57 @@ int igraph_hrg_create(igraph_hrg_t *hrg, // At least three vertices are required if (no_of_nodes < 3) { - IGRAPH_ERROR("HRG tree must have at least three vertices", + 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_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); + if (igraph_vector_size(prob) != no_of_nodes / 2) { + IGRAPH_ERRORF("HRG probability vector size (%" IGRAPH_PRId ") should be equal " + "to the number of internal nodes (%" IGRAPH_PRId ").", IGRAPH_EINVAL, + igraph_vector_size(prob), no_of_nodes / 2); } // Must be a directed graph if (!igraph_is_directed(graph)) { - IGRAPH_ERROR("HRG graph must be directed", IGRAPH_EINVAL); + 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_ERROR("Complete HRG graph must have odd number of vertices.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(°, 0); + IGRAPH_CHECK(igraph_is_simple(graph, &simple)); + if (!simple) { + IGRAPH_ERROR("HRG graph must be a simple graph.", IGRAPH_EINVAL); + } + + IGRAPH_VECTOR_INT_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]; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t 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); + "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); + "root vertex.", IGRAPH_EINVAL); } // Every internal vertex must have out-degree two, @@ -1002,13 +1063,13 @@ int igraph_hrg_create(igraph_hrg_t *hrg, 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]; + igraph_integer_t 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); + "degree 0 (leaves).", IGRAPH_EINVAL); } } @@ -1025,10 +1086,10 @@ int igraph_hrg_create(igraph_hrg_t *hrg, // 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); + IGRAPH_VECTOR_INT_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]; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t d = VECTOR(deg)[i]; if (i == root) { continue; } @@ -1041,27 +1102,27 @@ int igraph_hrg_create(igraph_hrg_t *hrg, } IGRAPH_CHECK(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]; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t 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->left )[-ri - 1] = VECTOR(idx)[ VECTOR(neis)[0] ]; + VECTOR(hrg->right)[-ri - 1] = VECTOR(idx)[ 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_int_null(&hrg->edges); + igraph_vector_int_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]; + igraph_integer_t ri = igraph_vector_tail(&path); + igraph_integer_t lc = VECTOR(hrg->left)[-ri - 1]; + igraph_integer_t 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)); @@ -1081,10 +1142,10 @@ int igraph_hrg_create(igraph_hrg_t *hrg, } igraph_vector_destroy(&path); - igraph_vector_destroy(&neis); - igraph_vector_destroy(&idx); - igraph_vector_destroy(°); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&idx); + igraph_vector_int_destroy(°); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/hrg/hrg_types.cc b/src/vendor/cigraph/src/hrg/hrg_types.cc index f2673b6d48f..d8ba2ac8f19 100644 --- a/src/vendor/cigraph/src/hrg/hrg_types.cc +++ b/src/vendor/cigraph/src/hrg/hrg_types.cc @@ -41,6 +41,9 @@ #include "igraph_constructors.h" #include "igraph_random.h" +#include +#include + using namespace std; using namespace fitHRG; @@ -1312,10 +1315,10 @@ int dendro::computeEdgeCount(const int a, const short int atype, // *********************************************************************** -int dendro::countChildren(const string s) { - int len = s.size(); - int numC = 0; - for (int i = 0; i < len; i++) { +size_t dendro::countChildren(const string s) { + size_t len = s.size(); + size_t numC = 0; + for (size_t i = 0; i < len; i++) { if (s[i] == 'C') { numC++; } @@ -1437,7 +1440,13 @@ double dendro::getSplitTotalWeight() { // *********************************************************************** bool dendro::importDendrogramStructure(const igraph_hrg_t *hrg) { - n = igraph_hrg_size(hrg); + igraph_integer_t size = igraph_hrg_size(hrg); + + if (size > INT_MAX) { + throw std::range_error("Hierarchical random graph too large for the HRG module"); + } + + n = (int) size; // allocate memory for G, O(n) leaf = new elementd[n]; @@ -1970,7 +1979,7 @@ int dendro::QsortPartition (block* array, int left, int right, int index) { return stored; } -void dendro::recordConsensusTree(igraph_vector_t *parents, +void dendro::recordConsensusTree(igraph_vector_int_t *parents, igraph_vector_t *weights) { keyValuePairSplit *curr, *prev; @@ -2069,7 +2078,7 @@ void dendro::recordConsensusTree(igraph_vector_t *parents, } // Return the consensus tree - igraph_vector_resize(parents, ii + orig_nodes); + igraph_vector_int_resize(parents, ii + orig_nodes); if (weights) { igraph_vector_resize(weights, ii); } @@ -2098,8 +2107,6 @@ void dendro::recordConsensusTree(igraph_vector_t *parents, VECTOR(*parents)[i] = -1; } } - - } // ********************************************************************** @@ -2117,13 +2124,13 @@ void dendro::recordDendrogramStructure(igraph_hrg_t *hrg) { } void dendro::recordGraphStructure(igraph_t *graph) { - igraph_vector_t edges; + igraph_vector_int_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); + igraph_vector_int_init(&edges, no_of_edges * 2); + IGRAPH_FINALLY(igraph_vector_int_destroy, &edges); for (int i = 0; i < n; i++) { edge *curr = g->getNeighborList(i); @@ -2138,7 +2145,7 @@ void dendro::recordGraphStructure(igraph_t *graph) { igraph_create(graph, &edges, no_of_nodes, /* directed= */ 0); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } @@ -2169,7 +2176,7 @@ list* dendro::reversePathToRoot(const int leafIndex) { // *********************************************************************** -bool dendro::sampleSplitLikelihoods(int &sample_num) { +bool dendro::sampleSplitLikelihoods(igraph_integer_t &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 @@ -2615,7 +2622,7 @@ void graph::resetLinks() { // ********************************************************************** -void graph::setAdjacencyHistograms(const int bin_count) { +void graph::setAdjacencyHistograms(const igraph_integer_t bin_count) { // For all possible adjacencies, setup an edge histograms num_bins = bin_count + 1; bin_resolution = 1.0 / (double)(bin_count); @@ -3119,7 +3126,7 @@ int splittree::returnNodecount() { keyValuePairSplit* splittree::returnTheseSplits(const int target) { keyValuePairSplit *head, *curr, *prev, *newhead, *newtail, *newpair; - int count, len; + size_t count, len; head = returnTreeAsList(); prev = newhead = newtail = newpair = NULL; diff --git a/src/vendor/cigraph/src/internal/glpk_support.c b/src/vendor/cigraph/src/internal/glpk_support.c index 9b0fcae0f7f..7cd89db97b2 100644 --- a/src/vendor/cigraph/src/internal/glpk_support.c +++ b/src/vendor/cigraph/src/internal/glpk_support.c @@ -118,18 +118,19 @@ void igraph_i_glp_delete_prob(glp_prob *p) { } } -int igraph_i_glpk_check(int retval, const char* message) { - char* code = "none"; +igraph_error_t igraph_i_glpk_check(int retval, const char* message) { + const char *code = "none"; char message_and_code[4096]; + igraph_error_t ret; - if (retval == IGRAPH_SUCCESS) { + if (retval == 0) { 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; +#define HANDLE_CODE(c) case c: code = #c; ret = IGRAPH_##c; break; +#define HANDLE_CODE2(c) case c: code = #c; ret = IGRAPH_FAILURE; break; +#define HANDLE_CODE3(c) case c: code = #c; ret = IGRAPH_INTERRUPTED; break; switch (retval) { HANDLE_CODE(GLP_EBOUND); HANDLE_CODE(GLP_EROOT); @@ -149,7 +150,7 @@ int igraph_i_glpk_check(int retval, const char* message) { HANDLE_CODE2(GLP_EITLIM); default: - IGRAPH_ERROR("Unknown GLPK error", IGRAPH_FAILURE); + IGRAPH_ERROR("Unknown GLPK error.", IGRAPH_FAILURE); } #undef HANDLE_CODE #undef HANDLE_CODE2 @@ -157,7 +158,7 @@ int igraph_i_glpk_check(int retval, const char* message) { snprintf(message_and_code, sizeof(message_and_code) / sizeof(message_and_code[0]), "%s (%s)", message, code); - IGRAPH_ERROR(message_and_code, retval); + IGRAPH_ERROR(message_and_code, ret); } #else diff --git a/src/vendor/cigraph/src/internal/glpk_support.h b/src/vendor/cigraph/src/internal/glpk_support.h index 9d0ae73b41b..b8adb4b7599 100644 --- a/src/vendor/cigraph/src/internal/glpk_support.h +++ b/src/vendor/cigraph/src/internal/glpk_support.h @@ -33,9 +33,14 @@ #ifdef HAVE_GLPK +#include "igraph_decls.h" +#include "igraph_error.h" + #include #include +__BEGIN_DECLS + 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 */ @@ -46,15 +51,15 @@ typedef struct igraph_i_glpk_error_info_s { 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); +igraph_error_t 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)) { \ + igraph_error_t igraph_i_ret = igraph_i_glpk_check(func, message); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ return igraph_i_ret; \ } } while (0) @@ -136,6 +141,8 @@ void igraph_i_glp_delete_prob(glp_prob *p); } \ } while (0) -#endif +__END_DECLS + +#endif /* HAVE_GLPK */ -#endif +#endif /* IGRAPH_GLPK_SUPPORT_H */ diff --git a/src/vendor/cigraph/src/internal/hacks.c b/src/vendor/cigraph/src/internal/hacks.c index d6653e05e84..09f42206b62 100644 --- a/src/vendor/cigraph/src/internal/hacks.c +++ b/src/vendor/cigraph/src/internal/hacks.c @@ -26,17 +26,15 @@ #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. */ +/* These are implementations of common C functions that may be missing from some compilers. */ /** * Drop-in replacement for strdup. * Used only in compilers that do not have strdup or _strdup */ -char* igraph_i_strdup(const char *s) { +char *igraph_i_strdup(const char *s) { size_t n = strlen(s) + 1; - char* result = (char*)malloc(sizeof(char) * n); + char *result = malloc(sizeof(char) * n); if (result) { memcpy(result, s, n); } @@ -44,10 +42,21 @@ char* igraph_i_strdup(const char *s) { } /** - * Drop-in replacement for stpcpy. - * Used only in compilers that do not have stpcpy + * Drop-in replacement for strndup. + * Used only in compilers that do not have strndup or _strndup */ -char* igraph_i_stpcpy(char* s1, const char* s2) { - char* result = strcpy(s1, s2); - return result + strlen(s1); +char *igraph_i_strndup(const char *s1, size_t n) { + size_t i; + /* We need to check if the string is shorter than n characters. + * We could use strlen, but that would do more work for long s1 and small n. + * TODO: Maybe memchr would be nicer here. + */ + for (i = 0; s1[i] != '\0' && i < n; i++) {} + n = i; + char *result = malloc(sizeof(char) * (n + 1)); + if (result) { + memcpy(result, s1, n); + result[n] = '\0'; + } + return result; } diff --git a/src/vendor/cigraph/src/internal/hacks.h b/src/vendor/cigraph/src/internal/hacks.h index 559ca40f3e3..6e9810f5d1a 100644 --- a/src/vendor/cigraph/src/internal/hacks.h +++ b/src/vendor/cigraph/src/internal/hacks.h @@ -24,17 +24,11 @@ #ifndef IGRAPH_HACKS_INTERNAL_H #define IGRAPH_HACKS_INTERNAL_H +#include "igraph_decls.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 +#include __BEGIN_DECLS @@ -43,9 +37,9 @@ __BEGIN_DECLS 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); +#ifndef HAVE_STRNDUP + #define strndup igraph_i_strndup + char* igraph_i_strndup(const char *s, size_t n); #endif #ifndef HAVE_STRCASECMP @@ -56,6 +50,19 @@ __BEGIN_DECLS #endif #endif +#ifndef HAVE_STRNCASECMP + #ifdef HAVE__STRNICMP + #define strncasecmp _strnicmp + #else + #error "igraph needs strncasecmp() or _strnicmp()" + #endif +#endif + +/* Magic macro to fail the build if certain condition does not hold. See: + * https://stackoverflow.com/questions/4079243/how-can-i-use-sizeof-in-a-preprocessor-macro + */ +#define IGRAPH_STATIC_ASSERT(condition) ((void)sizeof(char[1 - 2*!(condition)])) + __END_DECLS #endif diff --git a/src/vendor/cigraph/src/internal/lsap.c b/src/vendor/cigraph/src/internal/lsap.c index bdbe96ccd6c..cc38f6ae0ce 100644 --- a/src/vendor/cigraph/src/internal/lsap.c +++ b/src/vendor/cigraph/src/internal/lsap.c @@ -24,30 +24,30 @@ #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 */ + igraph_integer_t n; /* order of problem */ + double **C; /* cost matrix */ + double **c; /* reduced cost matrix */ + igraph_integer_t *s; /* assignment */ + igraph_integer_t *f; /* column i is assigned to f[i] */ + igraph_integer_t na; /* number of assigned items; */ + igraph_integer_t 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(double *t, igraph_integer_t 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 igraph_integer_t ap_assignment(AP *p, igraph_integer_t *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 igraph_error_t ap_hungarian(AP *p); /* static double ap_mincost(AP *p); */ /* static void ap_print_solution(AP *p); */ /* static void ap_show_data(AP *p); */ @@ -60,15 +60,16 @@ static int ap_hungarian(AP *p); /* 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); +static int cover(AP *p, igraph_integer_t *ri, igraph_integer_t *ci); +static void reduce(AP *p, igraph_integer_t *ri, igraph_integer_t *ci); -int ap_hungarian(AP *p) { - int n; /* size of problem */ - int *ri; /* covered rows */ - int *ci; /* covered columns */ +igraph_error_t ap_hungarian(AP *p) { + igraph_integer_t n; /* size of problem */ + igraph_integer_t *ri; /* covered rows */ + igraph_integer_t *ci; /* covered columns */ time_t start, end; /* timer */ - int i, j, ok; + igraph_integer_t i, j; + igraph_integer_t ok; start = time(0); @@ -76,14 +77,14 @@ int ap_hungarian(AP *p) { p->runs = 0; /* allocate memory */ - p->s = calloc(1 + n, sizeof(int)); - p->f = calloc(1 + n, sizeof(int)); + p->s = calloc(1 + n, sizeof(igraph_integer_t)); + p->f = calloc(1 + n, sizeof(igraph_integer_t)); - ri = calloc(1 + n, sizeof(int)); - ci = calloc(1 + n, sizeof(int)); + ri = calloc(1 + n, sizeof(igraph_integer_t)); + ci = calloc(1 + n, sizeof(igraph_integer_t)); if (ri == NULL || ci == NULL || p->s == NULL || p->f == NULL) { - IGRAPH_ERROR("ap_hungarian: could not allocate memory", IGRAPH_ENOMEM); + IGRAPH_ERROR("ap_hungarian: could not allocate memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } preprocess(p); @@ -128,12 +129,12 @@ int ap_hungarian(AP *p) { free(ri); free(ci); - return 0; + return IGRAPH_SUCCESS; } /* abbreviated interface */ -int ap_assignment(AP *p, int *res) { - int i; +igraph_integer_t ap_assignment(AP *p, igraph_integer_t *res) { + igraph_integer_t i; if (p->s == NULL) { ap_hungarian(p); @@ -282,8 +283,8 @@ AP *ap_create_problem_from_matrix(double **t, int n) { #endif /* read data from vector */ -AP *ap_create_problem(double *t, int n) { - int i, j; +AP *ap_create_problem(double *t, igraph_integer_t n) { + igraph_integer_t i, j; AP *p; p = (AP*) malloc(sizeof(AP)); @@ -321,7 +322,7 @@ AP *ap_create_problem(double *t, int n) { /* destructor */ void ap_free(AP *p) { - int i; + igraph_integer_t i; free(p->s); free(p->f); @@ -341,7 +342,7 @@ void ap_free(AP *p) { /* void ap_show_data(AP *p) { - int i, j; + igraph_integer_t i, j; for(i = 1; i <= p->n; i++){ for(j = 1; j <= p->n; j++) @@ -358,7 +359,7 @@ double ap_mincost(AP *p) { return p->cost; } -int ap_size(AP *p) { +igraph_integer_t ap_size(AP *p) { return p->n; } @@ -372,7 +373,7 @@ int ap_iterations(AP *p) { void ap_print_solution(AP *p) { - int i; + igraph_integer_t i; printf("%d itertations, %d secs.\n",p->runs, (int)p->rtime); printf("Min Cost: %10.4f\n",p->cost); @@ -383,7 +384,7 @@ void ap_print_solution(AP *p) } int ap_costmatrix(AP *p, double **m) { - int i, j; + igraph_integer_t i, j; for (i = 0; i < p->n; i++) for (j = 0; j < p->n; j++) { @@ -394,7 +395,7 @@ int ap_costmatrix(AP *p, double **m) { } int ap_datamatrix(AP *p, double **m) { - int i, j; + igraph_integer_t i, j; for (i = 0; i < p->n; i++) for (j = 0; j < p->n; j++) { @@ -420,12 +421,12 @@ void ap_error(char *message) /* by ap_hungarian */ /*************************************************************/ -int cover(AP *p, int *ri, int *ci) { - int *mr, i, r; - int n; +int cover(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { + igraph_integer_t *mr, i, r; + igraph_integer_t n; n = p->n; - mr = calloc(1 + p->n, sizeof(int)); + mr = calloc(1 + p->n, sizeof(igraph_integer_t)); /* reset cover indices */ for (i = 1; i <= n; i++) { @@ -475,8 +476,8 @@ int cover(AP *p, int *ri, int *ci) { return REDUCE; } -void reduce(AP *p, int *ri, int *ci) { - int i, j, n; +void reduce(AP *p, igraph_integer_t *ri, igraph_integer_t *ci) { + igraph_integer_t i, j, n; double min; n = p->n; @@ -505,19 +506,19 @@ void reduce(AP *p, int *ri, int *ci) { } void preassign(AP *p) { - int i, j, min, r, c, n, count; - int *ri, *ci, *rz, *cz; + igraph_integer_t i, j, min, r, c, n, count; + igraph_integer_t *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)); + ri = calloc(1 + n, sizeof(igraph_integer_t)); + ci = calloc(1 + n, sizeof(igraph_integer_t)); /* row and column counts of zeroes */ - rz = calloc(1 + n, sizeof(int)); - cz = calloc(1 + n, sizeof(int)); + rz = calloc(1 + n, sizeof(igraph_integer_t)); + cz = calloc(1 + n, sizeof(igraph_integer_t)); for (i = 1; i <= n; i++) { count = 0; @@ -539,7 +540,7 @@ void preassign(AP *p) { while (TRUE) { /* find unassigned row with least number of zeroes > 0 */ - min = INT_MAX; + min = IGRAPH_INTEGER_MAX; r = 0; for (i = 1; i <= n; i++) if (rz[i] > 0 && rz[i] < min && ri[i] == UNASSIGNED) { @@ -553,7 +554,7 @@ void preassign(AP *p) { /* find unassigned column in row r with least number of zeroes */ c = 0; - min = INT_MAX; + min = IGRAPH_INTEGER_MAX; for (i = 1; i <= n; i++) if (p->c[r][i] == 0 && cz[i] < min && ci[i] == UNASSIGNED) { min = cz[i]; @@ -585,7 +586,7 @@ void preassign(AP *p) { } void preprocess(AP *p) { - int i, j, n; + igraph_integer_t i, j, n; double min; n = p->n; @@ -619,7 +620,7 @@ void preprocess(AP *p) { * \function igraph_solve_lsap * \brief Solve a balanced linear assignment problem. * - * This functions solves a linear assinment problem using the Hungarian + * This functions solves a linear assignment 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 @@ -631,7 +632,7 @@ void preprocess(AP *p) { * * * To solve an unbalanced assignment problem, where the number of agents - * is greater than the number of tasks, an extra task with zero cost + * is greater than the number of tasks, extra tasks with zero costs * should be added. * * \param c The assignment problem, where the number of rows is the @@ -646,18 +647,18 @@ void preprocess(AP *p) { * * 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_error_t igraph_solve_lsap(const igraph_matrix_t *c, igraph_integer_t n, igraph_vector_int_t *p) { AP *ap; - if(n != igraph_matrix_nrow(c)) { + if (n != igraph_matrix_nrow(c)) { IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " - "not equal to number of agents (%ld).", IGRAPH_EINVAL, + "not equal to number of agents (%" IGRAPH_PRId ").", IGRAPH_EINVAL, n, igraph_matrix_nrow(c)); } - if(n != igraph_matrix_ncol(c)) { + if (n != igraph_matrix_ncol(c)) { IGRAPH_ERRORF("n (%" IGRAPH_PRId ") " - "not equal to number of tasks (%ld).", IGRAPH_EINVAL, + "not equal to number of tasks (%" IGRAPH_PRId ").", IGRAPH_EINVAL, n, igraph_matrix_ncol(c)); } IGRAPH_CHECK(igraph_vector_int_resize(p, n)); diff --git a/src/vendor/cigraph/src/internal/pstdint.h b/src/vendor/cigraph/src/internal/pstdint.h deleted file mode 100644 index bd5697f4bc5..00000000000 --- a/src/vendor/cigraph/src/internal/pstdint.h +++ /dev/null @@ -1,817 +0,0 @@ -/* 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/vendor/cigraph/src/internal/qsort.c b/src/vendor/cigraph/src/internal/qsort.c index f877beb73fd..48f3003cd7c 100644 --- a/src/vendor/cigraph/src/internal/qsort.c +++ b/src/vendor/cigraph/src/internal/qsort.c @@ -45,6 +45,11 @@ #define __unused #endif +#if defined(_MSC_VER) && _MSC_VER < 1927 + /* MSVC does not understand restrict before version 19.27 */ + #define restrict __restrict +#endif + #ifndef __unused #define __unused __attribute__ ((unused)) #endif @@ -71,7 +76,7 @@ static inline char *med3(char *, char *, char *, cmp_t *, void *); */ static inline void -swapfunc(char *a, char *b, size_t es) +swapfunc(char * restrict a, char * restrict b, size_t es) { char t; diff --git a/src/vendor/cigraph/src/internal/utils.c b/src/vendor/cigraph/src/internal/utils.c new file mode 100644 index 00000000000..0698e47dfc8 --- /dev/null +++ b/src/vendor/cigraph/src/internal/utils.c @@ -0,0 +1,95 @@ +/* + IGraph library. + Copyright (C) 2023 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_interface.h" + +#include "internal/utils.h" + +/** + * \function igraph_i_matrix_subset_vertices + * \brief Subsets a matrix whose rows/columns correspond to graph vertices. + * + * This is a convenience function to subset a matrix computed from a graph. + * It takes a matrix whose rows and columns correspond to the vertices + * of a graph, and subsets it in-place to retain only some of the vertices. + * + * \param m A square matrix with the same number of rows/columns as the vertex + * count of \p graph. It will be modified in-place, deleting rows \em not present + * in \p from and columns \em not present in \p to. + * \param graph The corresponding graph. m[u,v] is assumed to contain + * a value associated with vertices \c u and \c v of \p graph, e.g. the graph + * distance between them, their similarity, etc. + * \param from Vertex set, these rows of the matrix will be retained. + * \param to Vertex set, these columns of the matrix will be retained. + * \return Error code. + * + * Time complexity: + * O(1) when taking all vertices, + * O(|from|*|to|) otherwise where |from| and |to| denote the size + * of the source and target vertex sets. + */ +igraph_error_t igraph_i_matrix_subset_vertices( + igraph_matrix_t *m, + const igraph_t *graph, + igraph_vs_t from, + igraph_vs_t to) { + + /* Assertion: the size of 'm' agrees with 'graph': */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t ncol = igraph_matrix_ncol(m); + igraph_integer_t nrow = igraph_matrix_nrow(m); + + IGRAPH_ASSERT(nrow == no_of_nodes && nrow == ncol); + + /* When taking all vertices, nothing needs to be done: */ + + if (igraph_vs_is_all(&from) && igraph_vs_is_all(&to)) { + return IGRAPH_SUCCESS; + } + + /* Otherwise, allocate a temporary matrix to copy the data into: */ + + igraph_vit_t fromvit, tovit; + igraph_matrix_t tmp; + + IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); + IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); + + IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); + IGRAPH_FINALLY(igraph_vit_destroy, &tovit); + + IGRAPH_MATRIX_INIT_FINALLY(&tmp, IGRAPH_VIT_SIZE(fromvit), IGRAPH_VIT_SIZE(tovit)); + + for (igraph_integer_t j=0; ! IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { + igraph_integer_t i; + for (IGRAPH_VIT_RESET(fromvit), i=0; ! IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { + MATRIX(tmp, i, j) = MATRIX(*m, IGRAPH_VIT_GET(fromvit), IGRAPH_VIT_GET(tovit)); + } + } + + /* This is O(1) time */ + IGRAPH_CHECK(igraph_matrix_swap(m, &tmp)); + + igraph_matrix_destroy(&tmp); + igraph_vit_destroy(&tovit); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/internal/utils.h b/src/vendor/cigraph/src/internal/utils.h new file mode 100644 index 00000000000..94be451fc3a --- /dev/null +++ b/src/vendor/cigraph/src/internal/utils.h @@ -0,0 +1,32 @@ +/* + 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 . +*/ + +#ifndef IGRAPH_INTERNAL_UTILS_H +#define IGRAPH_INTERNAL_UTILS_H + +#include "igraph_datatype.h" +#include "igraph_iterators.h" +#include "igraph_matrix.h" + +igraph_error_t igraph_i_matrix_subset_vertices( + igraph_matrix_t *m, + const igraph_t *graph, + igraph_vs_t from, + igraph_vs_t to); + +#endif /* IGRAPH_INTERNAL_UTILS_H */ diff --git a/src/vendor/cigraph/src/internal/zeroin.c b/src/vendor/cigraph/src/internal/zeroin.c index 3d1d8d0abd2..89ade546255 100644 --- a/src/vendor/cigraph/src/internal/zeroin.c +++ b/src/vendor/cigraph/src/internal/zeroin.c @@ -89,16 +89,16 @@ #define EPSILON DBL_EPSILON -int igraph_zeroin( /* An estimate of the root */ +igraph_error_t 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 */ + 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 */ + 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) */ + fa, fb, fc; /* f(a), f(b), f(c) */ igraph_real_t tol; int maxit; @@ -111,30 +111,27 @@ int igraph_zeroin( /* An estimate of the root */ *Tol = 0.0; *Maxit = 0; *res = a; - return 0; + return IGRAPH_SUCCESS; } if (fb == 0.0) { *Tol = 0.0; *Maxit = 0; *res = b; - return 0; + return IGRAPH_SUCCESS; } - 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 */ + 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 calculated in the form p/q; */ + igraph_real_t q; /* division operations are 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 */ + /* Swap data for b to be the best approximation */ + a = b; b = c; c = a; fa = fb; fb = fc; fc = fa; } tol_act = 2 * EPSILON * fabs(b) + tol / 2; @@ -144,18 +141,18 @@ int igraph_zeroin( /* An estimate of the root */ *Maxit -= maxit; *Tol = fabs(c - b); *res = b; - return 0; /* Acceptable approx. is found */ + return IGRAPH_SUCCESS; /* 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 */ + * 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 */ + /* points linear interpolation */ t1 = fb / fa; /* can only be applied */ p = cb * t1; q = 1.0 - t1; diff --git a/src/vendor/cigraph/src/io/dimacs.c b/src/vendor/cigraph/src/io/dimacs.c index c615b269604..dcbc3717759 100644 --- a/src/vendor/cigraph/src/io/dimacs.c +++ b/src/vendor/cigraph/src/io/dimacs.c @@ -30,42 +30,83 @@ #include +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_DIMACS_MAX_VERTEX_COUNT (1 << 20) +#define IGRAPH_DIMACS_MAX_EDGE_COUNT (1 << 20) +#else +#define IGRAPH_DIMACS_MAX_VERTEX_COUNT INT32_MAX +#define IGRAPH_DIMACS_MAX_EDGE_COUNT INT32_MAX +#endif + /** * \function igraph_read_graph_dimacs + * \brief Read a graph in DIMACS format (deprecated alias). + * + * \deprecated-by igraph_read_graph_dimacs_flow 0.10.0 + */ +igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed) { + return igraph_read_graph_dimacs_flow( + graph, instream, problem, label, source, target, capacity, directed + ); +} + +#define EXPECT(actual, expected) \ + do { \ + if ((actual) != (expected)) { \ + IGRAPH_ERROR("Reading DIMACS flow problem file failed.", IGRAPH_PARSEERROR); \ + } \ + } while (0) + +/** + * \function igraph_read_graph_dimacs_flow * \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/ + * http://archive.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 + * character is \c c the line is a comment line and it is + * ignored. There is one problem line (\c 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. + * (\c max or \c edge), the number of vertices, + * and number of edges in the graph. In MAX problems, + * exactly two node identification lines are expected + * (\c n), one for the source, and one for the target vertex. + * These have two fields: the ID of the vertex and the type of the + * vertex, either \c s ( = source) or \c t ( = target). + * Arc lines start with \c a and have three fields: the source vertex, + * the target vertex and the edge capacity. In EDGE problems, + * there may be a node line (\c n) for each node. It specifies the + * node index and an integer node label. Nodes for which no explicit + * label was specified will use their index as label. In EDGE problems, + * each edge is specified as an edge line (\c e). * * - * Vertex ids are numbered from 1. + * Within DIMACS files, 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 problem If not \c NULL, it will contain the problem type. + * \param label If not \c NULL, node labels will be stored here for \c edge + * problems. Ignored for \c max problems. + * \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 \c NULL. + * \param target Pointer to an integer, the (igraph) ID of the target + * node will be stored here. It is ignored if \c NULL. * \param capacity Pointer to an initialized vector, the capacity of - * the edges will be stored here if not NULL. + * the edges will be stored here if not \ NULL. * \param directed Boolean, whether to create a directed graph. * \return Error code. * @@ -74,34 +115,36 @@ * * \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; +igraph_error_t igraph_read_graph_dimacs_flow( + igraph_t *graph, FILE *instream, + igraph_strvector_t *problem, + igraph_vector_int_t *label, + igraph_integer_t *source, + igraph_integer_t *target, + igraph_vector_t *capacity, + igraph_bool_t directed) { + + igraph_vector_int_t edges; + igraph_integer_t no_of_nodes = -1; + igraph_integer_t no_of_edges = -1; + igraph_integer_t tsource = -1; + igraph_integer_t ttarget = -1; char prob[21]; char c; - int problem_type = 0; - -#define PROBLEM_EDGE 1 -#define PROBLEM_MAX 2 + enum { + PROBLEM_NONE, + PROBLEM_EDGE, + PROBLEM_MAX + } problem_type = PROBLEM_NONE; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); if (capacity) { igraph_vector_clear(capacity); } while (!feof(instream)) { int read; - char str[3]; + char str[2]; IGRAPH_ALLOW_INTERRUPTION(); @@ -109,12 +152,10 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, if (feof(instream)) { break; } - if (read != 1) { - IGRAPH_ERROR("parsing dimacs file failed", IGRAPH_PARSEERROR); - } + EXPECT(read, 1); switch (str[0]) { - long int tmp, tmp2; - long int from, to; + igraph_integer_t tmp, tmp2; + igraph_integer_t from, to; igraph_real_t cap; case 'c': @@ -123,23 +164,29 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, case 'p': if (no_of_nodes != -1) { - IGRAPH_ERROR("reading dimacs file failed, double 'p' line", + IGRAPH_ERROR("Reading DIMACS file failed, double 'p' line.", IGRAPH_PARSEERROR); } - read = fscanf(instream, "%20s %li %li", prob, + read = fscanf(instream, "%20s %" IGRAPH_PRId " %" IGRAPH_PRId "", prob, &no_of_nodes, &no_of_edges); - if (read != 3) { - IGRAPH_ERROR("reading dimacs file failed", IGRAPH_PARSEERROR); + EXPECT(read, 3); + if (no_of_nodes > IGRAPH_DIMACS_MAX_VERTEX_COUNT) { + IGRAPH_ERROR("Vertex count too large in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_nodes < 0) { + IGRAPH_ERROR("Invalid (negative) vertex count in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_edges > IGRAPH_DIMACS_MAX_EDGE_COUNT) { + IGRAPH_ERROR("Edge count too large in DIMACS file.", IGRAPH_PARSEERROR); + } + if (no_of_edges < 0) { + IGRAPH_ERROR("Invalid (negative) edge count in DIMACS file.", 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; - } + IGRAPH_CHECK(igraph_vector_int_range(label, 1, no_of_nodes+1)); } } else if (!strcmp(prob, "max")) { /* maximum flow problem */ @@ -148,14 +195,14 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, IGRAPH_CHECK(igraph_vector_reserve(capacity, no_of_edges)); } } else { - IGRAPH_ERROR("Unknown problem type, should be 'edge' or 'max'", + 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_strvector_push_back(problem, prob)); } - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); break; case 'n': @@ -163,28 +210,35 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, for EDGE this is a vertex label */ if (problem_type == PROBLEM_MAX) { str[0] = 'x'; - read = fscanf(instream, "%li %1s", &tmp, str); + read = fscanf(instream, "%" IGRAPH_PRId " %1s", &tmp, str); + EXPECT(read, 2); if (str[0] == 's') { if (tsource != -1) { - IGRAPH_ERROR("reading dimacsfile: multiple source vertex line", + IGRAPH_ERROR("Reading DIMACS file: 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_ERROR("Reading DIMACS file: multiple target vertex line.", IGRAPH_PARSEERROR); } else { ttarget = tmp; } } else { - IGRAPH_ERROR("invalid node descriptor line in dimacs file", + IGRAPH_ERROR("Invalid node descriptor line in DIMACS file.", IGRAPH_PARSEERROR); } - } else { - read = fscanf(instream, "%li %li", &tmp, &tmp2); + } else { /* PROBLEM_EDGE */ + read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &tmp, &tmp2); + EXPECT(read, 1); if (label) { + if (tmp < 0 || tmp >= no_of_nodes) { + IGRAPH_ERRORF("Invalid node index %" IGRAPH_PRId " in DIMACS file. " + "Number of nodes was given as %" IGRAPH_PRId".", + IGRAPH_PARSEERROR, tmp, no_of_nodes); + } VECTOR(*label)[tmp] = tmp2; } } @@ -194,15 +248,13 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, 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_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)); + read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %lf", &from, &to, &cap); + EXPECT(read, 3); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to - 1)); if (capacity) { IGRAPH_CHECK(igraph_vector_push_back(capacity, cap)); } @@ -211,19 +263,17 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, case 'e': /* Edge line, only in EDGE */ if (problem_type != PROBLEM_EDGE) { - IGRAPH_ERROR("'e' lines are allowed only in EDGE problem files", + 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)); + read = fscanf(instream, "%" IGRAPH_PRId " %" IGRAPH_PRId "", &from, &to); + EXPECT(read, 2); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from - 1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to - 1)); break; default: - IGRAPH_ERROR("unknown line type in dimacs file", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Unknown line type in DIMACS file.", IGRAPH_PARSEERROR); } /* Go to next line */ @@ -231,23 +281,34 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, } if (source) { - *source = (igraph_integer_t) tsource - 1; + *source = tsource - 1; } if (target) { - *target = (igraph_integer_t) ttarget - 1; + *target = ttarget - 1; } - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_write_graph_dimacs + * \brief Write a graph in DIMACS format (deprecated alias). + * + * \deprecated-by igraph_write_graph_dimacs_flow 0.10.0 + */ +igraph_error_t igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { + return igraph_write_graph_dimacs_flow(graph, outstream, source, target, capacity); +} + +/** + * \function igraph_write_graph_dimacs_flow * \brief Write a graph in DIMACS format. * * This function writes a graph to an output stream in DIMACS format, @@ -256,7 +317,7 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, * * * This file format is discussed in the documentation of \ref - * igraph_read_graph_dimacs(), see that for more information. + * igraph_read_graph_dimacs_flow(), see that for more information. * * \param graph The graph to write to the stream. * \param outstream The stream. @@ -269,16 +330,16 @@ int igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, * * Time complexity: O(|E|), the number of edges in the graph. * - * \sa igraph_read_graph_dimacs() + * \sa \ref igraph_read_graph_dimacs_flow() */ -int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, - long int source, long int target, +igraph_error_t igraph_write_graph_dimacs_flow(const igraph_t *graph, FILE *outstream, + 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_eit_t it; - long int i = 0; + igraph_integer_t i = 0; int ret, ret1, ret2, ret3; if (igraph_vector_size(capacity) != no_of_edges) { @@ -290,7 +351,7 @@ int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, IGRAPH_FINALLY(igraph_eit_destroy, &it); ret = fprintf(outstream, - "c created by igraph\np max %li %li\nn %li s\nn %li t\n", + "c created by igraph\np max %" IGRAPH_PRId " %" IGRAPH_PRId "\nn %" IGRAPH_PRId " s\nn %" IGRAPH_PRId " t\n", no_of_nodes, no_of_edges, source + 1, target + 1); if (ret < 0) { IGRAPH_ERROR("Write error", IGRAPH_EFILE); @@ -302,8 +363,8 @@ int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, 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); + ret1 = fprintf(outstream, "a %" IGRAPH_PRId " %" IGRAPH_PRId " ", + from + 1, to + 1); ret2 = igraph_real_fprintf_precise(outstream, cap); ret3 = fputc('\n', outstream); if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { @@ -314,5 +375,5 @@ int igraph_write_graph_dimacs(const igraph_t *graph, FILE *outstream, igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/dl-header.h b/src/vendor/cigraph/src/io/dl-header.h index fe4d340e57e..b34910957aa 100644 --- a/src/vendor/cigraph/src/io/dl-header.h +++ b/src/vendor/cigraph/src/io/dl-header.h @@ -25,6 +25,14 @@ #include "core/trie.h" +/* TODO: Find out maximum supported vertex count. */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_DL_MAX_VERTEX_COUNT (1 << 20) +#else +#define IGRAPH_DL_MAX_VERTEX_COUNT INT32_MAX +#endif + typedef enum { IGRAPH_DL_MATRIX, IGRAPH_DL_EDGELIST1, IGRAPH_DL_NODELIST1 } igraph_i_dl_type_t; @@ -32,13 +40,14 @@ typedef enum { IGRAPH_DL_MATRIX, typedef struct { void *scanner; int eof; + char errmsg[300]; + igraph_error_t igraph_errno; int mode; - long int n; - long int from, to; - igraph_vector_t edges; + igraph_integer_t n; + igraph_integer_t from, to; + igraph_vector_int_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/vendor/cigraph/src/io/dl-lexer.l b/src/vendor/cigraph/src/io/dl-lexer.l index a1f69740e9a..e4a96c4b993 100644 --- a/src/vendor/cigraph/src/io/dl-lexer.l +++ b/src/vendor/cigraph/src/io/dl-lexer.l @@ -44,7 +44,6 @@ */ -#include "config.h" #include #include @@ -71,6 +70,8 @@ %option reentrant %option bison-bridge %option bison-locations +%option yylineno +%option caseless digit [0-9] whitespace [ \t\v\f] @@ -79,14 +80,13 @@ whitespace [ \t\v\f] %% -<*>\n\r|\r\n|\r|\n { return NEWLINE; } +<*>\n\r|\r\n|\r|\n { return NEWLINE; } -[dD][lL]{whitespace}+ { return DL; } -[nN]{whitespace}*[=]{whitespace}* { - return NEQ; } -{digit}+ { return NUM; } +dl{whitespace}+ { return DL; } +n{whitespace}*[=]{whitespace}* { return NEQ; } +{digit}+ { return NUM; } -[dD][aA][tT][aA][:] { +data: { switch (yyextra->mode) { case 0: BEGIN(FULLMATRIX); break; @@ -97,29 +97,29 @@ whitespace [ \t\v\f] } 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}* { +labels: { BEGIN(LABELM); return LABELS; } +labels{whitespace}+embedded:?{whitespace}* { return LABELSEMBEDDED; } -[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[fF][uU][lL][lL][mM][aA][tT][rR][iI][xX]{whitespace}* { +format{whitespace}*[=]{whitespace}*fullmatrix{whitespace}* { yyextra->mode=0; return FORMATFULLMATRIX; } -[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[eE][dD][gG][eE][lL][iI][sS][tT][1]{whitespace}* { +format{whitespace}*[=]{whitespace}*edgelist1{whitespace}* { yyextra->mode=1; return FORMATEDGELIST1; } -[fF][oO][rR][mM][aA][tT]{whitespace}*[=]{whitespace}*[nN][oO][dD][eE][lL][iI][sS][tT][1]{whitespace}* { +format{whitespace}*[=]{whitespace}*nodelist1{whitespace}* { yyextra->mode=2; return FORMATNODELIST1; } [, ] { /* eaten up */ } -[^, \t\n\r\f\v]+{whitespace}* { return LABEL; } +[^, \t\n\r\f\v\0]+{whitespace}* { return LABEL; } {digit}{whitespace}* { return DIGIT; } -[^ \t\n\r\v\f,]+ { return LABEL; } +[^ \t\n\r\v\f\0,]+ { return LABEL; } {whitespace} { } -\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } -[^ \t\n\r\v\f,]+ { return LABEL; } +(\+|\-)?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } +[^ \t\n\r\v\f\0,]+ { return LABEL; } {whitespace}* { } {digit}+ { return NUM; } -[^ \t\r\n\v\f,]+ { return LABEL; } +[^ \t\r\n\v\f\0,]+ { return LABEL; } {whitespace}* { } {whitespace}+ { /* eaten up */ } diff --git a/src/vendor/cigraph/src/io/dl-parser.y b/src/vendor/cigraph/src/io/dl-parser.y index f62d16230f4..ce58f28cfc7 100644 --- a/src/vendor/cigraph/src/io/dl-parser.y +++ b/src/vendor/cigraph/src/io/dl-parser.y @@ -44,27 +44,22 @@ */ -#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 +#include "io/parse_utils.h" 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, +static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t length, igraph_i_dl_parsedata_t *context); -int igraph_i_dl_add_edge(long int from, long int to, +static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t to, igraph_i_dl_parsedata_t *context); -int igraph_i_dl_add_edge_w(long int from, long int to, +static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t 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); +static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid); #define scanner context->scanner @@ -81,32 +76,40 @@ extern igraph_real_t igraph_pajek_get_number(const char *str, long int len); %lex-param { void* scanner } %union { - long int integer; + igraph_integer_t 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 NUM "number" +%token NEWLINE "end of line" +%token DL "DL" +%token NEQ "n=vertexcount" +%token DATA "data:" +%token LABELS "labels:" +%token LABELSEMBEDDED "labels embedded:" %token FORMATFULLMATRIX %token FORMATEDGELIST1 %token FORMATNODELIST1 -%token DIGIT -%token LABEL +%token DIGIT "binary digit" +%token LABEL "label" %token EOFF %token END 0 "end of file" /* friendly name for $end */ %token ERROR %% -input: DL NEQ integer NEWLINE rest trail eof { context->n=$3; }; +input: DL NEQ integer NEWLINE rest trail eof { + context->n=$3; + if (context->n < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); + } + if (context->n > IGRAPH_DL_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in DL file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->n); + } +}; trail: | trail newline; @@ -128,9 +131,9 @@ fullmatrix: DATA newline fullmatrixdata { } labels: {} /* nothing, empty matrix */ | labels newline LABEL { - igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner), - context); } + IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context)); } ; fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { @@ -141,11 +144,16 @@ fullmatrixdata: {} | fullmatrixdata zerooneseq NEWLINE { zerooneseq: | zerooneseq zeroone { } ; zeroone: DIGIT { - if (igraph_dl_yyget_text(scanner)[0]=='1') { - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + /* TODO: What if the digit is neither 0 or 1? Are multigraphs allowed? */ + char c = igraph_dl_yyget_text(scanner)[0]; + if (c == '1') { + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, context->from)); - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, context->to)); + } else if (c != '0') { + IGRAPH_YY_ERRORF("Unexpected digit '%c' in adjacency matrix in DL file.", + IGRAPH_EINVAL, c); } context->to += 1; } ; @@ -156,9 +164,9 @@ 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); }; +label: LABEL { IGRAPH_YY_CHECK(igraph_i_dl_add_str(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + context)); }; labeledmatrixlines: labeledmatrixline { context->from += 1; @@ -187,39 +195,58 @@ edgelist1data: {} /* nothing, empty graph */ ; edgelist1dataline: integer integer weight NEWLINE { - igraph_i_dl_add_edge_w($1-1, $2-1, $3, context); } + igraph_integer_t from = $1, to = $2; + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_i_dl_add_edge_w(from-1, to-1, $3, context)); } | integer integer NEWLINE { - igraph_i_dl_add_edge($1-1, $2-1, context); + igraph_integer_t from = $1, to = $2; + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_i_dl_add_edge(from-1, to-1, context)); } ; -integer: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner)); }; +integer: NUM { + igraph_integer_t val; + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &val)); + $$=val; +}; labelededgelist1data: {} /* nothing, empty graph */ | labelededgelist1data labelededgelist1dataline {} ; labelededgelist1dataline: elabel elabel weight NEWLINE { - igraph_i_dl_add_edge_w($1, $2, $3, context); } + IGRAPH_YY_CHECK(igraph_i_dl_add_edge_w($1, $2, $3, context)); } | elabel elabel NEWLINE { - igraph_i_dl_add_edge($1, $2, context); + IGRAPH_YY_CHECK(igraph_i_dl_add_edge($1, $2, context)); }; -weight: NUM { $$=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner)); }; +weight: NUM { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &val)); + $$=val; +}; elabel: LABEL { + igraph_integer_t trie_id; + /* Copy label list to trie, if needed */ if (igraph_strvector_size(&context->labels) != 0) { - long int i, id, n=igraph_strvector_size(&context->labels); + igraph_integer_t i, id, n=igraph_strvector_size(&context->labels); for (i=0; itrie, - STR(context->labels, i), &id); + IGRAPH_YY_CHECK(igraph_trie_get(&context->trie, 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), &$$); + IGRAPH_YY_CHECK(igraph_trie_get_len(&context->trie, igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), &trie_id)); + IGRAPH_ASSERT(0 <= trie_id && trie_id < IGRAPH_DL_MAX_VERTEX_COUNT); + $$ = trie_id; }; /*-----------------------------------------------------------*/ @@ -239,13 +266,19 @@ nodelist1data: {} /* nothing, empty graph */ nodelist1dataline: from tolist NEWLINE {} ; -from: NUM { context->from=igraph_pajek_get_number(igraph_dl_yyget_text(scanner), - igraph_dl_yyget_leng(scanner)); } ; +from: NUM { + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_dl_yyget_text(scanner), + igraph_dl_yyget_leng(scanner), + &context->from)); + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(context->from)); +} ; tolist: {} | tolist integer { - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, - context->from-1)); - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2-1)); + igraph_integer_t to = $2; + IGRAPH_YY_CHECK(igraph_i_dl_check_vid(to)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->from-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, to-1)); } ; labelednodelist1data: {} /* nothing, empty graph */ @@ -259,49 +292,63 @@ fromelabel: elabel { }; labeltolist: | labeltolist elabel { - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, - context->from)); - IGRAPH_CHECK(igraph_vector_push_back(&context->edges, $2)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, + context->from)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(&context->edges, $2)); } ; %% -int igraph_dl_yyerror(YYLTYPE* locp, igraph_i_dl_parsedata_t* context, +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); + snprintf(context->errmsg, sizeof(context->errmsg)/sizeof(char)-1, + "Parse error in DL file, line %i (%s)", + locp->first_line, s); return 0; } -int igraph_i_dl_add_str(char *newstr, int length, +static igraph_error_t igraph_i_dl_add_str(char *newstr, yy_size_t 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; + IGRAPH_CHECK(igraph_strvector_push_back_len(&context->labels, newstr, length)); + return IGRAPH_SUCCESS; } -int igraph_i_dl_add_edge(long int from, long int to, +static igraph_error_t igraph_i_dl_add_edge(igraph_integer_t from, igraph_integer_t 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; + //IGRAPH_CHECK(igraph_i_dl_check_vid(from+1)); + //IGRAPH_CHECK(igraph_i_dl_check_vid(to+1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&context->edges, to)); + return IGRAPH_SUCCESS; } -int igraph_i_dl_add_edge_w(long int from, long int to, +static igraph_error_t igraph_i_dl_add_edge_w(igraph_integer_t from, igraph_integer_t 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; + igraph_integer_t n=igraph_vector_size(&context->weights); + igraph_integer_t n2=igraph_vector_int_size(&context->edges)/2; if (n != n2) { - igraph_vector_resize(&context->weights, n2); + IGRAPH_CHECK(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; + return IGRAPH_SUCCESS; +} + +/* Raise an error if the vertex index is invalid in the DL file. + * DL files use 1-based vertex indices. */ +static igraph_error_t igraph_i_dl_check_vid(igraph_integer_t dl_vid) { + if (dl_vid < 1) { + IGRAPH_ERRORF("Invalid vertex index in DL file: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, dl_vid); + } + if (dl_vid > IGRAPH_DL_MAX_VERTEX_COUNT) { + IGRAPH_ERRORF("Vertex index too large in DL file: %" IGRAPH_PRId ".", + IGRAPH_EINVAL, dl_vid); + } + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/dl.c b/src/vendor/cigraph/src/io/dl.c index 8eb868fbce6..3f484b744e5 100644 --- a/src/vendor/cigraph/src/io/dl.c +++ b/src/vendor/cigraph/src/io/dl.c @@ -26,6 +26,7 @@ #include "igraph_interface.h" #include "io/dl-header.h" +#include "io/parsers/dl-parser.h" int igraph_dl_yylex_init_extra (igraph_i_dl_parsedata_t* user_defined, void* scanner); @@ -40,7 +41,7 @@ void igraph_dl_yylex_destroy_wrapper (void *scanner ) { /** * \function igraph_read_graph_dl - * \brief Read a file in the DL format of UCINET + * \brief Reads 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 @@ -63,11 +64,10 @@ void igraph_dl_yylex_destroy_wrapper (void *scanner ) { * \example examples/simple/igraph_read_graph_dl.c */ -int igraph_read_graph_dl(igraph_t *graph, FILE *instream, +igraph_error_t igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_bool_t directed) { - int i; - long int n, n2; + igraph_integer_t n, n2; const igraph_strvector_t *namevec = 0; igraph_vector_ptr_t name, weight; igraph_vector_ptr_t *pname = 0, *pweight = 0; @@ -80,8 +80,10 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, context.n = -1; context.from = 0; context.to = 0; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; - IGRAPH_VECTOR_INIT_FINALLY(&context.edges, 0); + IGRAPH_VECTOR_INT_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); @@ -92,20 +94,39 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_dl_yyset_in(instream, context.scanner); - i = igraph_dl_yyparse(&context); - if (i != 0) { + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_dl_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read DL file", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read DL file.", IGRAPH_PARSEERROR); } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read DL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading DL file.", err); } /* Extend the weight vector, if needed */ n = igraph_vector_size(&context.weights); - n2 = igraph_vector_size(&context.edges) / 2; + n2 = igraph_vector_int_size(&context.edges) / 2; if (n != 0) { - igraph_vector_resize(&context.weights, n2); + IGRAPH_CHECK(igraph_vector_resize(&context.weights, n2)); for (; n < n2; n++) { VECTOR(context.weights)[n] = IGRAPH_NAN; } @@ -113,7 +134,7 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, /* Check number of vertices */ if (n2 > 0) { - n = (long int) igraph_vector_max(&context.edges); + n = igraph_vector_int_max(&context.edges); } else { n = 0; } @@ -122,15 +143,13 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, context.n = n; } - /* OK, everything is ready, create the graph */ - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - IGRAPH_FINALLY(igraph_destroy, graph); + /* Prepare attributes */ /* 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); + namevec = igraph_i_trie_borrow_keys(&context.trie); } if (namevec) { IGRAPH_CHECK(igraph_vector_ptr_init(&name, 1)); @@ -153,7 +172,10 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) context.n, pname)); + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, context.n, pname)); IGRAPH_CHECK(igraph_add_edges(graph, &context.edges, pweight)); if (pweight) { @@ -171,10 +193,10 @@ int igraph_read_graph_dl(igraph_t *graph, FILE *instream, igraph_trie_destroy(&context.trie); igraph_strvector_destroy(&context.labels); - igraph_vector_destroy(&context.edges); + igraph_vector_int_destroy(&context.edges); igraph_vector_destroy(&context.weights); igraph_dl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/dot.c b/src/vendor/cigraph/src/io/dot.c index 1c99a061a7d..e12619bb596 100644 --- a/src/vendor/cigraph/src/io/dot.c +++ b/src/vendor/cigraph/src/io/dot.c @@ -29,24 +29,24 @@ #include "igraph_version.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "internal/hacks.h" /* strcasecmp & strdup */ #include #include -#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write DOT format failed.", IGRAPH_EFILE); } while (0) +#define CHECK(cmd) do { int ret=cmd; if (ret<0) IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_EFILE); } while (0) -static int igraph_i_dot_escape(const char *orig, char **result) { +static igraph_error_t 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; + igraph_integer_t i, j, len = strlen(orig), newlen = 0; + igraph_bool_t need_quote = false, is_number = true; /* first, check whether the string is equal to some reserved word, or empty */ if (!strcasecmp(orig, "graph") || !strcasecmp(orig, "digraph") || !strcasecmp(orig, "node") || !strcasecmp(orig, "edge") || !strcasecmp(orig, "strict") || !strcasecmp(orig, "subgraph") || len == 0) { - need_quote = 1; - is_number = 0; + need_quote = true; + is_number = false; } /* next, check whether we need to escape the string for any other reason. @@ -83,12 +83,12 @@ static int igraph_i_dot_escape(const char *orig, char **result) { if (is_number || !need_quote) { *result = strdup(orig); if (!*result) { - IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } } else { *result = IGRAPH_CALLOC(newlen + 3, char); if (!*result) { - IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } (*result)[0] = '"'; (*result)[newlen + 1] = '"'; @@ -116,7 +116,7 @@ static int igraph_i_dot_escape(const char *orig, char **result) { /** * \function igraph_write_graph_dot - * \brief Write the graph to a stream in DOT format + * \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 @@ -135,14 +135,13 @@ static int igraph_i_dot_escape(const char *orig, char **result) { * * \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); +igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { + igraph_integer_t i, j; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); char edgeop[3]; igraph_strvector_t gnames, vnames, enames; - igraph_vector_t gtypes, vtypes, etypes; + igraph_vector_int_t gtypes, vtypes, etypes; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; @@ -150,9 +149,9 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { 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_VECTOR_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, &gnames, >ypes, &vnames, &vtypes, @@ -174,26 +173,28 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } /* Write the graph attributes */ - if (igraph_vector_size(>ypes) > 0) { + if (igraph_vector_int_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); + for (i = 0; i < igraph_vector_int_size(>ypes); i++) { + const char *name; + char *newname; + name = igraph_strvector_get(&gnames, i); 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])); + if (VECTOR(numv)[0] == (igraph_integer_t)VECTOR(numv)[0]) { + CHECK(fprintf(outstream, " %s=%" IGRAPH_PRId "\n", newname, (igraph_integer_t)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; + const char *s; + char *news; IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); - igraph_strvector_get(&strv, 0, &s); + s = igraph_strvector_get(&strv, 0); IGRAPH_CHECK(igraph_i_dot_escape(s, &news)); CHECK(fprintf(outstream, " %s=%s\n", newname, news)); IGRAPH_FREE(news); @@ -211,12 +212,13 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } /* Write the vertices */ - if (igraph_vector_size(&vtypes) > 0) { + if (igraph_vector_int_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); + CHECK(fprintf(outstream, " %" IGRAPH_PRId " [\n", i)); + for (j = 0; j < igraph_vector_int_size(&vtypes); j++) { + const char *name; + char *newname; + name = igraph_strvector_get(&vnames, j); IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); IGRAPH_FINALLY(igraph_free, newname); if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { @@ -230,14 +232,15 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { 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); + const char *s; + char *news; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); 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)); + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr(graph, name, igraph_vss_1(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 { @@ -250,20 +253,21 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } } else { for (i = 0; i < no_of_nodes; i++) { - CHECK(fprintf(outstream, " %ld;\n", i)); + CHECK(fprintf(outstream, " %" IGRAPH_PRId ";\n", i)); } } CHECK(fprintf(outstream, "\n")); /* Write the edges */ - if (igraph_vector_size(&etypes) > 0) { + if (igraph_vector_int_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_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId " [\n", from, edgeop, to)); + for (j = 0; j < igraph_vector_int_size(&etypes); j++) { + const char *name; + char *newname; + name = igraph_strvector_get(&enames, j); IGRAPH_CHECK(igraph_i_dot_escape(name, &newname)); IGRAPH_FINALLY(igraph_free, newname); if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { @@ -277,16 +281,17 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { CHECK(fputc('\n', outstream)); } } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { - char *s, *news; + const char *s; + char *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); + name, igraph_ess_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); 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)); + name, igraph_ess_1(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 { @@ -299,9 +304,9 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } } 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)); + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + CHECK(fprintf(outstream, " %" IGRAPH_PRId " %s %" IGRAPH_PRId ";\n", from, edgeop, to)); } } CHECK(fprintf(outstream, "}\n")); @@ -309,9 +314,9 @@ int igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { 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_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); igraph_strvector_destroy(&enames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&gnames); diff --git a/src/vendor/cigraph/src/io/edgelist.c b/src/vendor/cigraph/src/io/edgelist.c index 1dea9d62fdc..90797a5daf1 100644 --- a/src/vendor/cigraph/src/io/edgelist.c +++ b/src/vendor/cigraph/src/io/edgelist.c @@ -27,8 +27,7 @@ #include "igraph_iterators.h" #include "core/interruption.h" - -#include +#include "io/parse_utils.h" /** * \section about_loadsave @@ -37,7 +36,8 @@ * from a file. * * They assume that the current locale uses a decimal point and not - * a decimal comma. + * a decimal comma. See \ref igraph_enter_safelocale() and + * \ref igraph_exit_safelocale() for more information. * * Note that as \a igraph uses the traditional C streams, it is * possible to read/write files from/to memory, at least on GNU @@ -54,6 +54,7 @@ * 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 @@ -68,52 +69,44 @@ * * 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. + * reading an integer requires O(1) time. */ -int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, +igraph_error_t 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_int_t edges = IGRAPH_VECTOR_NULL; + igraph_integer_t from, to; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, 100)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 100)); - /* skip all whitespace */ - do { - c = getc (instream); - } while (isspace (c)); - ungetc (c, instream); + for (;;) { + IGRAPH_ALLOW_INTERRUPTION(); - while (!feof(instream)) { - int read; + IGRAPH_CHECK(igraph_i_fskip_whitespace(instream)); - IGRAPH_ALLOW_INTERRUPTION(); + if (feof(instream)) break; - 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_i_fget_integer(instream, &from)); + IGRAPH_CHECK(igraph_i_fget_integer(instream, &to)); + +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* Protect from very large memory allocations when fuzzing. */ +#define IGRAPH_EDGELIST_MAX_VERTEX_COUNT (1L << 20) + if (from > IGRAPH_EDGELIST_MAX_VERTEX_COUNT || to > IGRAPH_EDGELIST_MAX_VERTEX_COUNT) { + IGRAPH_ERROR("Vertex count too large in edgelist file.", IGRAPH_EINVAL); } - 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); +#endif + + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); } IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } /** @@ -122,8 +115,10 @@ int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, * \brief Writes the edge list of a graph to a file. * * + * Edges are represented as pairs of 0-based vertex indices. * 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: @@ -135,7 +130,7 @@ int igraph_read_graph_edgelist(igraph_t *graph, FILE *instream, * integer to the file requires O(1) * time. */ -int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { +igraph_error_t igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { igraph_eit_t it; @@ -147,16 +142,16 @@ int igraph_write_graph_edgelist(const igraph_t *graph, FILE *outstream) { 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); + ret = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", + from, + to); if (ret < 0) { - IGRAPH_ERROR("Write error", IGRAPH_EFILE); + IGRAPH_ERROR("Failed writing edgelist.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/gml-header.h b/src/vendor/cigraph/src/io/gml-header.h index 618db87ea77..b8d590d8068 100644 --- a/src/vendor/cigraph/src/io/gml-header.h +++ b/src/vendor/cigraph/src/io/gml-header.h @@ -1,7 +1,6 @@ /* IGraph library. - Copyright (C) 2011-2012 Gabor Csardi - 334 Harvard street, Cambridge MA, 02139 USA + 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 @@ -15,9 +14,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ #include "igraph_error.h" @@ -26,16 +24,16 @@ typedef struct { void *scanner; - int eof; - int depth; char errmsg[300]; + igraph_error_t igraph_errno; + int depth; 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); +igraph_error_t igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context); /** * Destroys a GML parser context, freeing all memory currently used by the diff --git a/src/vendor/cigraph/src/io/gml-lexer.l b/src/vendor/cigraph/src/io/gml-lexer.l index 0acd8fa5eee..265fb7e3bf4 100644 --- a/src/vendor/cigraph/src/io/gml-lexer.l +++ b/src/vendor/cigraph/src/io/gml-lexer.l @@ -1,7 +1,6 @@ /* IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 334 Harvard st, Cambridge, MA, 02138 USA + 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 @@ -15,9 +14,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ %{ @@ -44,7 +42,6 @@ */ -#include "config.h" #include #include "io/gml-header.h" @@ -70,18 +67,25 @@ %option reentrant %option bison-bridge %option bison-locations +%option yylineno digit [0-9] -whitespace [ \r\n\t] +whitespace [ \r\n\t\v\f] + +/* Use to parse inf/nan as number only when expecting a value, i.e. after a keyword. + * Otherwise they are parsed as a keyword. */ +%s VALUE %% -^#[^\n\r]*[\n]|[\r] { /* comments ignored */ } +^#[^\0\n\r]* { /* comments ignored */ } -\"[^\x00\"]*\" { return STRING; } -\-?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { return NUM; } -[a-zA-Z_][a-zA-Z_0-9]* { return KEYWORD; } +\"[^\0\"]*\" { BEGIN(INITIAL); return STRING; } +(\+|\-)?((?i:nan)|(?i:inf)) { BEGIN(INITIAL); return NUM; } +(\+|\-)?{digit}+(\.{digit}+)?([eE](\+|\-)?{digit}+)? { BEGIN(INITIAL); return NUM; } +[a-zA-Z_][a-zA-Z_0-9]* { BEGIN(VALUE); return KEYWORD; } \[ { + BEGIN(INITIAL); yyextra->depth++; if (yyextra->depth >= 32) { return ERROR; @@ -91,22 +95,9 @@ whitespace [ \r\n\t] } \] { yyextra->depth--; - if (yyextra->depth < 0) { - return ERROR; - } else { - return LISTCLOSE; - } + 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/vendor/cigraph/src/io/gml-parser.y b/src/vendor/cigraph/src/io/gml-parser.y index 040d0f635fb..c52aea0b5d4 100644 --- a/src/vendor/cigraph/src/io/gml-parser.y +++ b/src/vendor/cigraph/src/io/gml-parser.y @@ -1,7 +1,6 @@ /* IGraph library. - Copyright (C) 2009-2012 Gabor Csardi - 334 Harvard st, Cambridge, MA, 02138 USA + Copyright (C) 2009-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 @@ -15,9 +14,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ %{ @@ -44,34 +42,38 @@ */ -#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 */ +#include "io/parse_utils.h" +#include "internal/hacks.h" /* strcasecmp & strndup */ + +#include +#include +#include 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); +static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res); +static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res); +static igraph_error_t igraph_i_gml_make_numeric(const char *name, + int line, + igraph_real_t value, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_string(const char *name, + int line, + char *value, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_list(const char *name, + int line, + igraph_gml_tree_t *list, + igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree); +static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2); #define scanner context->scanner #define USE(x) /*(x)*/ @@ -85,16 +87,14 @@ igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* %defines %locations %error-verbose +%expect 2 /* from list rule */ %parse-param { igraph_i_gml_parsedata_t* context } %lex-param { void *scanner } %union { - struct { - char *s; - int len; - } str; - void *tree; - double real; + char *str; + igraph_gml_tree_t *tree; + igraph_real_t real; } %type list; @@ -103,46 +103,50 @@ igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* %type num; %type string; -%token STRING -%token NUM -%token KEYWORD -%token LISTOPEN -%token LISTCLOSE -%token EOFF +%token STRING "string" +%token NUM "number" +%token KEYWORD "keyword" +%token LISTOPEN "[" +%token LISTCLOSE "]" +/* The following ensures that the special $end token is shown with a friendly name + * even in older Bison versions. + * See https://www.gnu.org/software/bison/manual/bison.html#Token-I18n for more details. */ %token END 0 "end of file" /* friendly name for $end */ %token ERROR -%destructor { IGRAPH_FREE($$.s); } string key KEYWORD; +%destructor { free($$); } string key; %destructor { igraph_gml_tree_destroy($$); } list keyvalue; %% -input: list { context->tree=$1; } - | list EOFF { context->tree=$1; } -; +input: list { context->tree=$1; }; -list: keyvalue { $$=$1; } - | list keyvalue { $$=igraph_i_gml_merge($1, $2); }; +list: /* empty */ { IGRAPH_YY_CHECK(igraph_i_gml_make_empty(&$$)); } + | keyvalue { $$=$1; } /* redundant and causes shift/reduce conflict, but increases performance */ + | list keyvalue { IGRAPH_YY_CHECK(igraph_i_gml_merge($1, $2)); $$ = $1; }; keyvalue: key num - { $$=igraph_i_gml_make_numeric($1.s, $1.len, $2); } + { IGRAPH_YY_CHECK(igraph_i_gml_make_numeric($1, @1.first_line, $2, &$$)); } | key string - { $$=igraph_i_gml_make_string($1.s, $1.len, $2.s, $2.len); } + { IGRAPH_YY_CHECK(igraph_i_gml_make_string($1, @1.first_line, $2, &$$)); } | 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); } + { IGRAPH_YY_CHECK(igraph_i_gml_make_list($1, @1.first_line, $3, &$$)); } ; -key: KEYWORD { igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), +key: KEYWORD { IGRAPH_YY_CHECK(igraph_i_gml_get_keyword(igraph_gml_yyget_text(scanner), + igraph_gml_yyget_leng(scanner), + &$$)); USE($1); }; +num : NUM { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(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)); }; + &val)); + $$=val; +}; -string: STRING { igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), +string: STRING { IGRAPH_YY_CHECK(igraph_i_gml_get_string(igraph_gml_yyget_text(scanner), igraph_gml_yyget_leng(scanner), - &$$); }; + &$$)); }; %% @@ -154,145 +158,112 @@ int igraph_gml_yyerror(YYLTYPE* locp, igraph_i_gml_parsedata_t *context, 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); +static igraph_error_t igraph_i_gml_get_keyword(const char *s, size_t len, char **res) { + *res = strndup(s, len); + if (! *res) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - memcpy(p->s, s, sizeof(char)*len); - p->s[len]='\0'; - p->len=len; + return IGRAPH_SUCCESS; } -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); +static igraph_error_t igraph_i_gml_get_string(const char *s, size_t len, char **res) { + *res = strndup(s+1, len-2); + if (! *res) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - memcpy(p->s, s+1, sizeof(char)*(len-2)); - p->s[len-2]='\0'; - p->len=len-2; + return IGRAPH_SUCCESS; } -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; -} +static igraph_error_t igraph_i_gml_make_numeric(const char *name, + int line, + igraph_real_t value, + igraph_gml_tree_t **tree) { -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; + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - - if (floor(value)==value) { - if (igraph_gml_tree_init_integer(t, s, len, value)) { - free(t); - return 0; - } + IGRAPH_FINALLY(igraph_free, t); + + /* The GML spec only requires support for 32-bit signed integers. + * We treat anything out of that range as real. These values end + * up as igraph_real_t anyway, as igraph does not currently support + * integer-typed attributes. */ + if (floor(value) == value && value >= INT32_MIN && value <= INT32_MAX) { + IGRAPH_CHECK(igraph_gml_tree_init_integer(t, name, line, value)); } else { - if (igraph_gml_tree_init_real(t, s, len, value)) { - free(t); - return 0; - } + IGRAPH_CHECK(igraph_gml_tree_init_real(t, name, line, value)); } - return t; + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ + + return IGRAPH_SUCCESS; } -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]; +static igraph_error_t igraph_i_gml_make_string(const char *name, + int line, + char *value, + igraph_gml_tree_t **tree) { + 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; + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, t); - 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; - } + /* 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 */ + IGRAPH_FINALLY(igraph_free, value); + IGRAPH_CHECK(igraph_gml_tree_init_string(t, name, line, value)); - v[vlen]=tmp; - free(v); + IGRAPH_FINALLY_CLEAN(1); /* value */ - if (t == 0) { - /* no new tree node was created so s has no owner any more */ - free(s); - } + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ - return t; + return IGRAPH_SUCCESS; } -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); +static igraph_error_t igraph_i_gml_make_list(const char *name, + int line, + igraph_gml_tree_t *list, + igraph_gml_tree_t **tree) { + 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; + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, t); - /* 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; - } + IGRAPH_CHECK(igraph_gml_tree_init_tree(t, name, line, list)); - return t; -} + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ -igraph_gml_tree_t *igraph_i_gml_make_list(char* s, int len, - igraph_gml_tree_t *list) { + return IGRAPH_SUCCESS; +} - igraph_gml_tree_t *t=IGRAPH_CALLOC(1, igraph_gml_tree_t); +static igraph_error_t igraph_i_gml_make_empty(igraph_gml_tree_t **tree) { + igraph_gml_tree_t *t = IGRAPH_CALLOC(1, igraph_gml_tree_t); + if (!t) { + IGRAPH_ERROR("Cannot build GML tree.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, t); - if (!t) { - igraph_error("Cannot build GML tree", IGRAPH_FILE_BASENAME, __LINE__, IGRAPH_ENOMEM); - return 0; - } + IGRAPH_CHECK(igraph_gml_tree_init_empty(t)); - if (igraph_gml_tree_init_tree(t, s, len, list)) { - free(t); - return 0; - } + *tree = t; + IGRAPH_FINALLY_CLEAN(1); /* t */ - return t; + return IGRAPH_SUCCESS; } -igraph_gml_tree_t *igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { +static igraph_error_t igraph_i_gml_merge(igraph_gml_tree_t *t1, igraph_gml_tree_t* t2) { - igraph_gml_tree_mergedest(t1, t2); + IGRAPH_CHECK(igraph_gml_tree_mergedest(t1, t2)); IGRAPH_FREE(t2); - return t1; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/gml-tree.c b/src/vendor/cigraph/src/io/gml-tree.c index 8d0fe076a8c..29dbccf561e 100644 --- a/src/vendor/cigraph/src/io/gml-tree.c +++ b/src/vendor/cigraph/src/io/gml-tree.c @@ -1,7 +1,6 @@ /* IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2007-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 @@ -15,9 +14,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ #include "igraph_memory.h" @@ -27,21 +25,23 @@ #include -int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, - const char *name, int namelen, - igraph_integer_t value) { +igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + 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_CHAR_INIT_FINALLY(&t->types, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ - VECTOR(t->names)[0] = (void*)name; + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_INTEGER; @@ -49,84 +49,89 @@ int igraph_gml_tree_init_integer(igraph_gml_tree_t *t, /* children */ p = IGRAPH_CALLOC(1, igraph_integer_t); if (!p) { - IGRAPH_ERROR("Cannot create integer GML tree node", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot create integer GML tree node.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } *p = value; VECTOR(t->children)[0] = p; - IGRAPH_FINALLY_CLEAN(3); - return 0; + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; } -int igraph_gml_tree_init_real(igraph_gml_tree_t *t, - const char *name, int namelen, - igraph_real_t value) { +igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + 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_CHAR_INIT_FINALLY(&t->types, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ VECTOR(t->names)[0] = (void*) name; + /* line number */ + VECTOR(t->lines)[0] = line; + /* 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); + IGRAPH_ERROR("Cannot create real GML tree node.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } *p = value; VECTOR(t->children)[0] = p; - IGRAPH_FINALLY_CLEAN(3); - return 0; + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; } -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_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + const char *value) { 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_CHAR_INIT_FINALLY(&t->types, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ VECTOR(t->names)[0] = (void*) name; + /* line number */ + VECTOR(t->lines)[0] = line; + /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_STRING; /* children */ - VECTOR(t->children)[0] = (void*)value; + VECTOR(t->children)[0] = (void*) value; - IGRAPH_FINALLY_CLEAN(3); - return 0; + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; } -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_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_gml_tree_t *value) { 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_CHAR_INIT_FINALLY(&t->types, 1); IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 1); /* names */ - VECTOR(t->names)[0] = (void*)name; + VECTOR(t->names)[0] = (void*) name; + + /* line number */ + VECTOR(t->lines)[0] = line; /* types */ VECTOR(t->types)[0] = IGRAPH_I_GML_TREE_TREE; @@ -134,32 +139,44 @@ int igraph_gml_tree_init_tree(igraph_gml_tree_t *t, /* children */ VECTOR(t->children)[0] = value; - IGRAPH_FINALLY_CLEAN(3); - return 0; + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; } +igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *t) { + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->names, 0); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&t->types, 0); + IGRAPH_VECTOR_PTR_INIT_FINALLY(&t->children, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&t->lines, 0); + IGRAPH_FINALLY_CLEAN(4); + return IGRAPH_SUCCESS; +} + /* 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); +igraph_error_t igraph_gml_tree_mergedest(igraph_gml_tree_t *t1, igraph_gml_tree_t *t2) { + igraph_integer_t 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_CHECK(igraph_vector_ptr_push_back(&t1->children, VECTOR(t2->children)[i])); + IGRAPH_CHECK(igraph_vector_int_push_back(&t1->lines, VECTOR(t2->lines)[i])); } igraph_vector_ptr_destroy(&t2->names); igraph_vector_char_destroy(&t2->types); igraph_vector_ptr_destroy(&t2->children); - return 0; + igraph_vector_int_destroy(&t2->lines); + + return IGRAPH_SUCCESS; } void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { - long int i, n = igraph_vector_ptr_size(&t->children); + igraph_integer_t i, n = igraph_vector_ptr_size(&t->children); for (i = 0; i < n; i++) { - int type = VECTOR(t->types)[i]; + igraph_i_gml_tree_type_t type = (igraph_i_gml_tree_type_t) VECTOR(t->types)[i]; switch (type) { case IGRAPH_I_GML_TREE_TREE: igraph_gml_tree_destroy(VECTOR(t->children)[i]); @@ -184,17 +201,18 @@ void igraph_gml_tree_destroy(igraph_gml_tree_t *t) { igraph_vector_ptr_destroy(&t->names); igraph_vector_char_destroy(&t->types); igraph_vector_ptr_destroy(&t->children); + igraph_vector_int_destroy(&t->lines); IGRAPH_FREE(t); } -long int igraph_gml_tree_length(const igraph_gml_tree_t *t) { +igraph_integer_t 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); +igraph_integer_t igraph_gml_tree_find( + const igraph_gml_tree_t *t, const char *name, igraph_integer_t from +) { + igraph_integer_t size = igraph_vector_ptr_size(&t->names); while ( from < size && (! VECTOR(t->names)[from] || strcmp(VECTOR(t->names)[from], name)) ) { from++; @@ -206,8 +224,9 @@ long int igraph_gml_tree_find(const igraph_gml_tree_t *t, return from; } -long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, - const char *name, long int from) { +igraph_integer_t igraph_gml_tree_findback( + const igraph_gml_tree_t *t, const char *name, igraph_integer_t from +) { while ( from >= 0 && (! VECTOR(t->names)[from] || strcmp(VECTOR(t->names)[from], name)) ) { from--; @@ -216,39 +235,43 @@ long int igraph_gml_tree_findback(const igraph_gml_tree_t *t, return from; } -int igraph_gml_tree_type(const igraph_gml_tree_t *t, long int pos) { - return VECTOR(t->types)[pos]; +igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos) { + return (igraph_i_gml_tree_type_t) VECTOR(t->types)[pos]; } -const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, long int pos) { +const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos) { return VECTOR(t->names)[pos]; } +igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos) { + return VECTOR(t->lines)[pos]; +} + igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, - long int pos) { + igraph_integer_t 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_integer_t 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) { + igraph_integer_t 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_integer_t 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) { +void igraph_gml_tree_delete(igraph_gml_tree_t *t, igraph_integer_t pos) { if (VECTOR(t->types)[pos] == IGRAPH_I_GML_TREE_TREE) { igraph_gml_tree_destroy(VECTOR(t->children)[pos]); } diff --git a/src/vendor/cigraph/src/io/gml-tree.h b/src/vendor/cigraph/src/io/gml-tree.h index 074721c724a..71aacfeeb6e 100644 --- a/src/vendor/cigraph/src/io/gml-tree.h +++ b/src/vendor/cigraph/src/io/gml-tree.h @@ -1,7 +1,6 @@ /* IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2007-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 @@ -15,9 +14,8 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ #ifndef REST_GML_TREE_H @@ -41,41 +39,48 @@ typedef struct igraph_gml_tree_t { igraph_vector_ptr_t names; igraph_vector_char_t types; igraph_vector_ptr_t children; + igraph_vector_int_t lines; /* line numbers where names appear */ } 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); +igraph_error_t igraph_gml_tree_init_integer(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_integer_t value); +igraph_error_t igraph_gml_tree_init_real(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_real_t value); +igraph_error_t igraph_gml_tree_init_string(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + const char *value); +igraph_error_t igraph_gml_tree_init_tree(igraph_gml_tree_t *t, + const char *name, + igraph_integer_t line, + igraph_gml_tree_t *value); +igraph_error_t igraph_gml_tree_init_empty(igraph_gml_tree_t *t); 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); +void igraph_gml_tree_delete(igraph_gml_tree_t *t, igraph_integer_t pos); +igraph_error_t 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_length(const igraph_gml_tree_t *t); +igraph_integer_t igraph_gml_tree_find(const igraph_gml_tree_t *t, + const char *name, igraph_integer_t from); +igraph_integer_t igraph_gml_tree_findback(const igraph_gml_tree_t *t, + const char *name, igraph_integer_t from); +igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos); +const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos); +igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos); igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, - long int pos); + igraph_integer_t pos); igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, - long int pos); + igraph_integer_t pos); const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, - long int pos); + igraph_integer_t pos); igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, - long int pos); + igraph_integer_t pos); __END_DECLS diff --git a/src/vendor/cigraph/src/io/gml.c b/src/vendor/cigraph/src/io/gml.c index 77c7f054150..5ed255b60e2 100644 --- a/src/vendor/cigraph/src/io/gml.c +++ b/src/vendor/cigraph/src/io/gml.c @@ -1,7 +1,7 @@ /* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2005-2020 The igraph development team + Copyright (C) 2005-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 @@ -17,7 +17,6 @@ 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" @@ -29,23 +28,144 @@ #include "core/trie.h" #include "graph/attributes.h" +#include "internal/hacks.h" /* strdup, strncasecmp */ + #include "io/gml-header.h" +#include "io/parsers/gml-parser.h" #include #include #include -int igraph_gml_yylex_init_extra (igraph_i_gml_parsedata_t* user_defined, - void* scanner); -int 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 ); +int igraph_gml_yylex_init_extra(igraph_i_gml_parsedata_t *user_defined, void *scanner); +int 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); + +/* Checks if a null-terminated string needs encoding or decoding. + * + * Encoding is needed when an " or & character is present. + * + * Decoding is needed when an &xyz; style entity is present, so it's sufficient to look + * for & characters. " characters are never present in the raw strings returned by the + * GML parser, so we can use the same function to detect the need for either encoding + * or decoding. + */ +static igraph_bool_t needs_coding(const char *str) { + while (*str) { + if (*str == '&' || *str == '"') { + return true; + } + str++; + } + return false; +} + +/* Encode & and " character in 'src' to & and " + * '*dest' must be deallocated by the caller. + */ +static igraph_error_t entity_encode(const char *src, char **dest, igraph_bool_t only_quot) { + igraph_integer_t destlen = 0; + const char *s; + char *d; + + for (s = src; *s != '\0'; s++, destlen++) { + switch (*s) { + case '&': /* & */ + if (! only_quot) { + destlen += 4; + } + break; + case '"': /* " */ + destlen += 5; break; + } + } + *dest = IGRAPH_CALLOC(destlen + 1, char); + IGRAPH_CHECK_OOM(dest, "Not enough memory to encode string for GML export."); + for (s = src, d = *dest; *s != '\0'; s++, d++) { + switch (*s) { + case '&': + if (! only_quot) { + strcpy(d, "&"); + d += 4; + } else { + *d = *s; + } + break; + case '"': + strcpy(d, """); d += 5; break; + default: + *d = *s; + } + } + *d = '\0'; + return IGRAPH_SUCCESS; +} + +/* Decode the five standard predefined XML entities. Unknown entities or stray & characters + * will be passed through unchanged. '*dest' must be deallocated by the caller. + * If '*warned' is false, warnings will be issued for unsupported entities and + * '*warned' will be set to true. This is to prevent a flood of warnings in some files. + */ +static igraph_error_t entity_decode(const char *src, char **dest, igraph_bool_t *warned) { + const char *entity_names[] = { + """, "&", "'", "<", ">" + }; + + const char entity_values[] = { + '"', '&', '\'', '<', '>' + }; + + const int entity_count = sizeof entity_values / sizeof entity_values[0]; + + const char *s; + char *d; + size_t len = strlen(src); + *dest = IGRAPH_CALLOC(len+1, char); /* at most as much storage needed as for 'src' */ + IGRAPH_CHECK_OOM(dest, "Not enough memory to decode string during GML import."); + + for (s = src, d = *dest; *s != '\0';) { + if (*s == '&') { + int i; + for (i=0; i < entity_count; i++) { + size_t entity_len = strlen(entity_names[i]); + if (!strncasecmp(s, entity_names[i], entity_len)) { + *d++ = entity_values[i]; + s += entity_len; + break; + } + } + /* None of the known entities match, report warning and pass through unchanged. */ + if (i == entity_count) { + if (! *warned) { + const int max_entity_name_length = 34; + int j = 0; + while (s[j] != '\0' && s[j] != ';' && j < max_entity_name_length) { + j++; + } + if (s[j] == '\0' || j == max_entity_name_length) { + IGRAPH_WARNING("Unterminated entity or stray & character found, will be returned verbatim."); + } else { + IGRAPH_WARNINGF("One or more unknown entities will be returned verbatim (%.*s).", j+1, s); + } + *warned = true; /* warn only once */ + } + *d++ = *s++; + } + } else { + *d++ = *s++; + } + } + *d = '\0'; + + return IGRAPH_SUCCESS; +} static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { - long int i; + igraph_integer_t i; igraph_vector_ptr_t *vec; for (i = 0; i < 3; i++) { - long int j; + igraph_integer_t j; vec = ptr[i]; for (j = 0; j < igraph_vector_ptr_size(vec); j++) { igraph_attribute_record_t *atrec = VECTOR(*vec)[j]; @@ -55,12 +175,14 @@ static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { igraph_vector_destroy(value); IGRAPH_FREE(value); } - } else { + } else if (atrec->type == IGRAPH_ATTRIBUTE_STRING) { igraph_strvector_t *value = (igraph_strvector_t*)atrec->value; if (value != 0) { igraph_strvector_destroy(value); IGRAPH_FREE(value); } + } else { + /* Some empty attribute records may have been created for composite attributes */ } IGRAPH_FREE(atrec->name); IGRAPH_FREE(atrec); @@ -69,39 +191,34 @@ static void igraph_i_gml_destroy_attrs(igraph_vector_ptr_t **ptr) { } } -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); +static igraph_real_t igraph_i_gml_toreal(igraph_gml_tree_t *node, igraph_integer_t pos) { + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); switch (type) { case IGRAPH_I_GML_TREE_INTEGER: - value = igraph_gml_tree_get_integer(node, pos); - break; + return igraph_gml_tree_get_integer(node, pos); case IGRAPH_I_GML_TREE_REAL: - value = igraph_gml_tree_get_real(node, pos); - break; + return igraph_gml_tree_get_real(node, pos); + case IGRAPH_I_GML_TREE_TREE: + return IGRAPH_NAN; /* default value of NaN when composite is ignored */ default: - IGRAPH_ERROR("Internal error while parsing GML file.", IGRAPH_FAILURE); - break; + /* Must never reach here, regardless of the contents of the GML file. */ + IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", + igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ } - - *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]; +static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, igraph_integer_t pos) { + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, pos); + static char tmp[100]; const char *p = tmp; - long int i; + igraph_integer_t 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); + snprintf(tmp, sizeof(tmp) / sizeof(char), "%" IGRAPH_PRId, i); break; case IGRAPH_I_GML_TREE_REAL: d = igraph_gml_tree_get_real(node, pos); @@ -110,24 +227,29 @@ static const char *igraph_i_gml_tostring(igraph_gml_tree_t *node, long int pos) case IGRAPH_I_GML_TREE_STRING: p = igraph_gml_tree_get_string(node, pos); break; - default: + case IGRAPH_I_GML_TREE_TREE: + tmp[0] = '\0'; /* default value of "" when composite is ignored */ break; + default: + /* Must never reach here, regardless of the contents of the GML file. */ + IGRAPH_FATALF("Unexpected node type in GML tree, line %" IGRAPH_PRId ".", + igraph_gml_tree_line(node, pos)); /* LCOV_EXCL_LINE */ } return p; } -int igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t* context) { - context->eof = 0; +igraph_error_t igraph_i_gml_parsedata_init(igraph_i_gml_parsedata_t *context) { context->depth = 0; context->scanner = 0; context->tree = 0; - context->errmsg[0] = 0; + context->errmsg[0] = '\0'; + context->igraph_errno = IGRAPH_SUCCESS; return IGRAPH_SUCCESS; } -void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context) { +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; @@ -139,37 +261,152 @@ void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context) { } } +/* Takes a vector of attribute records and removes those elements + * whose type is unspecified, i.e. IGRAPH_ATTRIBUTE_UNSPECIFIED. */ +static void prune_unknown_attributes(igraph_vector_ptr_t *attrs) { + igraph_integer_t i, j; + for (i = 0, j = 0; i < igraph_vector_ptr_size(attrs); i++) { + igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; + if (atrec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + IGRAPH_FREE(atrec->name); + IGRAPH_FREE(atrec); + } else { + VECTOR(*attrs)[j++] = VECTOR(*attrs)[i]; + } + } + igraph_vector_ptr_resize(attrs, j); /* shrinks */ +} + +/* Converts an integer id to an optionally prefixed string id. */ +static const char *strid(igraph_integer_t id, const char *prefix) { + static char name[100]; + snprintf(name, sizeof(name) / sizeof(char) - 1, "%s%" IGRAPH_PRId, prefix, id); + return name; +} + +/* Creates an empty attribute record or if it exists, updates its type as needed. + * 'name' is the attribute name. 'type' is the current type in the GML tree, + * which will determine the igraph attribute type to use. */ +static igraph_error_t create_or_update_attribute(const char *name, + igraph_i_gml_tree_type_t type, + igraph_trie_t *attrnames, + igraph_vector_ptr_t *attrs) { + + igraph_integer_t trieid, triesize = igraph_trie_size(attrnames); + IGRAPH_CHECK(igraph_trie_get(attrnames, name, &trieid)); + if (trieid == triesize) { + /* new attribute */ + igraph_attribute_record_t *atrec = IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (!atrec) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, atrec); + atrec->name = strdup(name); + if (! atrec->name) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, (char *) atrec->name); + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } else if (type == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } else { + atrec->type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, atrec)); + IGRAPH_FINALLY_CLEAN(2); + } else { + /* already seen, should we update type? */ + igraph_attribute_record_t *atrec = VECTOR(*attrs)[trieid]; + igraph_attribute_type_t type1 = atrec->type; + if (type == IGRAPH_I_GML_TREE_STRING) { + atrec->type = IGRAPH_ATTRIBUTE_STRING; + } else if (type1 == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + if (type == IGRAPH_I_GML_TREE_INTEGER || type == IGRAPH_I_GML_TREE_REAL) { + atrec->type = IGRAPH_ATTRIBUTE_NUMERIC; + } + } + } + + return IGRAPH_SUCCESS; +} + +/* Allocates the contents of attribute records stored in 'attrs'. + * 'no_of_items' is the length of attribute vectors, i.e. no_of_nodes, + * no_of_edges, or 1 for vertex, edge and graph attributes. + * The 'kind' parameter can be "vertex", "edge" or "graph", and + * is used solely for showing better warning messages. */ +static igraph_error_t allocate_attributes(igraph_vector_ptr_t *attrs, + igraph_integer_t no_of_items, + const char *kind) { + + igraph_integer_t i, n = igraph_vector_ptr_size(attrs); + for (i = 0; i < n; i++) { + igraph_attribute_record_t *atrec = VECTOR(*attrs)[i]; + igraph_attribute_type_t type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *p = IGRAPH_CALLOC(1, igraph_vector_t); + if (! p) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_vector_init(p, no_of_items)); + igraph_vector_fill(p, IGRAPH_NAN); /* use NaN as default */ + atrec->value = p; + IGRAPH_FINALLY_CLEAN(1); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *p = IGRAPH_CALLOC(1, igraph_strvector_t); + if (! p) { + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + IGRAPH_FINALLY(igraph_free, p); + IGRAPH_CHECK(igraph_strvector_init(p, no_of_items)); + atrec->value = p; + IGRAPH_FINALLY_CLEAN(1); + } else if (type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + IGRAPH_WARNINGF("Composite %s attribute '%s' ignored in GML file.", kind, atrec->name); + } else { + /* Must never reach here. */ + IGRAPH_FATAL("Unexpected attribute type."); + } + } + return IGRAPH_SUCCESS; +} + /** * \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. + * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 + * for details. * * * Although all syntactically correct GML can be parsed, - * we implement only a subset of this format, some attributes might be + * 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 Only attributes with a simple type are used: integer, real or + * string. If an attribute is composite, i.e. an array or a record, + * then it is ignored. When some values of the attribute are simple and + * some compound, the composite ones are replaced with a default value + * (NaN for numeric, "" for string). + * \oli comment fields are not ignored. They are treated as any + * other field and converted to attributes. * \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 + * \oli There is no maximum line length or maximum keyword length. + * \oli Only the \c quot, \c amp, \c apos, \c lt and \c gt character entities + * are supported. Any other entity is passed through unchanged by the reader + * after issuing a warning, and is expected to be decoded by the user. + * \oli We allow inf, -inf and nan * (not a number) as a real number. This is case insensitive, so - * nan, NaN and NAN are equal. + * nan, NaN and NAN are equivalent. * \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. @@ -181,23 +418,27 @@ void igraph_i_gml_parsedata_destroy(igraph_i_gml_parsedata_t* context) { * * \example examples/simple/gml.c */ -int igraph_read_graph_gml(igraph_t *graph, FILE *instream) { +igraph_error_t igraph_read_graph_gml(igraph_t *graph, FILE *instream) { - long int i, p; - long int no_of_nodes = 0, no_of_edges = 0; + igraph_integer_t i; + igraph_integer_t no_of_nodes = 0, no_of_edges = 0; + igraph_integer_t node_no; igraph_trie_t trie; - igraph_vector_t edges; + igraph_vector_int_t edges; igraph_bool_t directed = IGRAPH_UNDIRECTED; + igraph_bool_t has_directed = false; igraph_gml_tree_t *gtree; - long int gidx; + igraph_integer_t 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; + vattrs = IGRAPH_VECTOR_PTR_NULL, + eattrs = IGRAPH_VECTOR_PTR_NULL; igraph_vector_ptr_t *attrs[3]; - long int edgeptr = 0; + igraph_integer_t edgeptr = 0; igraph_i_gml_parsedata_t context; + igraph_bool_t entity_warned = false; /* used to warn at most once about unsupported entities */ attrs[0] = &gattrs; attrs[1] = &vattrs; attrs[2] = &eattrs; @@ -208,309 +449,383 @@ int igraph_read_graph_gml(igraph_t *graph, FILE *instream) { igraph_gml_yyset_in(instream, context.scanner); - i = igraph_gml_yyparse(&context); - if (i != 0) { + /* Protect 'context' from being destroyed before returning from yyparse() */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_gml_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); } else { IGRAPH_ERROR("Cannot read GML file.", IGRAPH_PARSEERROR); } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read GML file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading GML file.", err); /* LCOV_EXCL_LINE */ } - 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!!!! */ + IGRAPH_WARNINGF("Unknown GML version: %" IGRAPH_PRId ". " + "Parsing will continue assuming GML version 1, but may fail.", + igraph_gml_tree_get_integer(context.tree, i)); } - /* get the graph */ + /* 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); + IGRAPH_ERRORF("Invalid type for 'graph' object in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(context.tree, gidx)); } 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_CHECK(igraph_vector_ptr_init(&gattrs, 0)); + IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); + IGRAPH_CHECK(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. */ + /* Now we go over all objects in the graph to + * - collect the attribute names and types + * - collect node IDs + * - set directedness + * - do some checks which the following code relies on + * + * The 'id' fields of 'node' objects are converted into strings, so that they + * can be inserted into a trie and re-encoded as consecutive integers starting + * at 0. The GML spec allows isolated nodes with no 'id' field. These get a + * generated string id of the form "n123" consisting of "n" and their count + * (i.e. ordinal position) within the GML file. + * + * We use an attribute type value of IGRAPH_ATTRIBUTE_UNSPECIFIED to mark attribute + * records which correspond to composite GML values and must therefore be removed + * before creating the graph. + */ + node_no = 0; 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; + node_no++; 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); + IGRAPH_ERRORF("'node' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); } node = igraph_gml_tree_get_tree(gtree, i); hasid = 0; - for (j = 0; j < igraph_gml_tree_length(node); j++) { + for (igraph_integer_t 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; - } - } + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(node, j); + IGRAPH_CHECK(create_or_update_attribute(name, type, &vattrnames, &vattrs)); /* 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); + if (!strcmp(name, "id")) { + igraph_integer_t id, trie_id; + igraph_integer_t trie_size = igraph_trie_size(&trie); + if (hasid) { + /* A 'node' must not have more than one 'id' field. + * This error cannot be relaxed into a warning because all ids we find are + * added to the trie, and eventually converted to igraph vertex ids. */ + IGRAPH_ERRORF("Node has multiple 'id' fields in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); + } + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); } 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)); + IGRAPH_CHECK(igraph_trie_get(&trie, strid(id, ""), &trie_id)); + if (trie_id != trie_size) { + /* This id has already been seen in a previous node. */ + IGRAPH_ERRORF("Duplicate node id in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(node, j)); + } hasid = 1; } } if (!hasid) { - IGRAPH_ERROR("Node without 'id' while parsing GML file.", IGRAPH_PARSEERROR); + /* Isolated nodes are allowed not to have an id. + * We generate an "n"-prefixed string id to be used in the trie. */ + igraph_integer_t trie_id; + IGRAPH_CHECK(igraph_trie_get(&trie, strid(node_no, "n"), &trie_id)); } } else if (!strcmp(name, "edge")) { igraph_gml_tree_t *edge; - igraph_bool_t has_source = 0, has_target = 0; + igraph_bool_t has_source = false, has_target = false; 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); + IGRAPH_ERRORF("'edge' is not a list in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); } edge = igraph_gml_tree_get_tree(gtree, i); - has_source = has_target = 0; - for (j = 0; j < igraph_gml_tree_length(edge); j++) { + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { const char *name = igraph_gml_tree_name(edge, j); + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(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); + if (has_source) { + /* An edge must not have more than one 'source' field. + * This could be relaxed to a warning, but we keep it as an error + * for consistency with the handling of duplicate node 'id' field, + * and because it indicates a serious corruption in the GML file. */ + IGRAPH_ERRORF("Duplicate 'source' in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); + } + has_source = true; + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer 'source' for an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); } } 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); + if (has_target) { + /* An edge must not have more than one 'target' field. */ + IGRAPH_ERRORF("Duplicate 'target' in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); } - } 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; - } + has_target = true; + if (type != IGRAPH_I_GML_TREE_INTEGER) { + IGRAPH_ERRORF("Non-integer 'target' for an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, + igraph_gml_tree_line(edge, j)); } + } else { + IGRAPH_CHECK(create_or_update_attribute(name, type, &eattrnames, &eattrs)); } } /* for */ if (!has_source) { - IGRAPH_ERROR("No 'source' for edge in GML file.", IGRAPH_PARSEERROR); + IGRAPH_ERRORF("No 'source' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); } if (!has_target) { - IGRAPH_ERROR("No 'target' for edge in GML file.", IGRAPH_PARSEERROR); + IGRAPH_ERRORF("No 'target' for edge in GML file, line %" IGRAPH_PRId ".", IGRAPH_PARSEERROR, + igraph_gml_tree_line(gtree, i)); + } + } else if (! strcmp(name, "directed")) { + /* Set directedness of graph. */ + if (has_directed) { + /* Be tolerant of duplicate entries, but do show a warning. */ + IGRAPH_WARNINGF("Duplicate 'directed' field in 'graph', line %" IGRAPH_PRId ". " + "Ignoring previous 'directed' fields.", + igraph_gml_tree_line(gtree, i)); + } + if (igraph_gml_tree_type(gtree, i) == IGRAPH_I_GML_TREE_INTEGER) { + igraph_integer_t dir = igraph_gml_tree_get_integer(gtree, i); + if (dir != 0 && dir != 1) { + IGRAPH_WARNINGF( + "Invalid value %" IGRAPH_PRId " for 'directed' attribute on line %" IGRAPH_PRId ", should be 0 or 1.", + dir, igraph_gml_tree_line(gtree, i)); + } + if (dir) { + directed = IGRAPH_DIRECTED; + } + has_directed = true; + } else { + IGRAPH_WARNINGF("Invalid type for 'directed' attribute on line %" IGRAPH_PRId ", assuming undirected.", + igraph_gml_tree_line(gtree, i)); } } 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."); + /* Add the rest of items as graph attributes. */ + igraph_i_gml_tree_type_t type = igraph_gml_tree_type(gtree, i); + IGRAPH_CHECK(create_or_update_attribute(name, type, &gattrnames, &gattrs)); } } - 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."); - } - } + /* At this point, all nodes must have an id (from the file or generated) stored + * in the trie. Any condition that violates this should have been caught during + * the preceding checks. */ + IGRAPH_ASSERT(igraph_trie_size(&trie) == no_of_nodes); - /* 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; - } + /* Now we allocate the vectors and strvectors for the attributes */ + IGRAPH_CHECK(allocate_attributes(&vattrs, no_of_nodes, "vertex")); + IGRAPH_CHECK(allocate_attributes(&eattrs, no_of_edges, "edge")); + IGRAPH_CHECK(allocate_attributes(&gattrs, 1, "graph")); - /* and add vertex attributes */ + /* Add edges, edge attributes and vertex attributes */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); + node_no = 0; 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")) { + const char *name; + name = igraph_gml_tree_name(gtree, i); + if (!strcmp(name, "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++) { + igraph_integer_t iidx = igraph_gml_tree_find(node, "id", 0); + igraph_integer_t trie_id; + const char *sid; + node_no++; + if (iidx < 0) { + /* Isolated node with no id field, use n-prefixed generated id */ + sid = strid(node_no, "n"); + } else { + sid = strid(igraph_gml_tree_get_integer(node, iidx), ""); + } + IGRAPH_CHECK(igraph_trie_get(&trie, sid, &trie_id)); + for (igraph_integer_t 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]; + igraph_attribute_type_t type; + igraph_integer_t ai; + IGRAPH_CHECK(igraph_trie_get(&vattrnames, aname, &ai)); + atrec = VECTOR(vattrs)[ai]; 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)); + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[trie_id] = igraph_i_gml_toreal(node, j); } else if (type == IGRAPH_ATTRIBUTE_STRING) { - igraph_strvector_t *v = (igraph_strvector_t *)atrec->value; + 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)); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, trie_id, value)); + } + } else { + /* Ignored composite attribute */ } } + } else if (!strcmp(name, "edge")) { + igraph_gml_tree_t *edge; + igraph_integer_t from, to, fromidx = 0, toidx = 0; + edge = igraph_gml_tree_get_tree(gtree, i); + for (igraph_integer_t j = 0; j < igraph_gml_tree_length(edge); j++) { + const char *aname = igraph_gml_tree_name(edge, j); + if (!strcmp(aname, "source")) { + fromidx = igraph_gml_tree_find(edge, "source", 0); + } else if (!strcmp(aname, "target")) { + toidx = igraph_gml_tree_find(edge, "target", 0); + } else { + igraph_integer_t edgeid = edgeptr / 2; + igraph_integer_t ai; + igraph_attribute_record_t *atrec; + igraph_attribute_type_t type; + IGRAPH_CHECK(igraph_trie_get(&eattrnames, aname, &ai)); + atrec = VECTOR(eattrs)[ai]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[edgeid] = igraph_i_gml_toreal(edge, j); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; + const char *value = igraph_i_gml_tostring(edge, j); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, edgeid, value)); + } + } else { + /* Ignored composite attribute */ + } + } + } + from = igraph_gml_tree_get_integer(edge, fromidx); + to = igraph_gml_tree_get_integer(edge, toidx); + IGRAPH_CHECK(igraph_trie_check(&trie, strid(from, ""), &from)); + if (from < 0) { + IGRAPH_ERRORF("Unknown source node id found in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, fromidx)); + } + IGRAPH_CHECK(igraph_trie_check(&trie, strid(to, ""), &to)); + if (to < 0) { + IGRAPH_ERRORF("Unknown target node id found in an edge in GML file, line %" IGRAPH_PRId ".", + IGRAPH_PARSEERROR, igraph_gml_tree_line(edge, toidx)); + } + VECTOR(edges)[edgeptr++] = from; + VECTOR(edges)[edgeptr++] = to; + } else if (! strcmp(name, "directed")) { + /* Nothing to do for 'directed' field, already handled earlier. */ + } else { + /* Set the rest as graph attributes */ + igraph_integer_t ai; + igraph_attribute_record_t *atrec; + igraph_attribute_type_t type; + IGRAPH_CHECK(igraph_trie_get(&gattrnames, name, &ai)); + atrec = VECTOR(gattrs)[ai]; + type = atrec->type; + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + igraph_vector_t *v = (igraph_vector_t *) atrec->value; + VECTOR(*v)[0] = igraph_i_gml_toreal(gtree, i); + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *v = (igraph_strvector_t *) atrec->value; + const char *value = igraph_i_gml_tostring(gtree, i); + if (needs_coding(value)) { + char *value_decoded; + IGRAPH_CHECK(entity_decode(value, &value_decoded, &entity_warned)); + IGRAPH_FINALLY(igraph_free, value_decoded); + IGRAPH_CHECK(igraph_strvector_set(v, 0, value_decoded)); + IGRAPH_FREE(value_decoded); + IGRAPH_FINALLY_CLEAN(1); + } else { + IGRAPH_CHECK(igraph_strvector_set(v, 0, value)); + } + } else { + /* Ignored composite attribute */ + } } } + /* Remove composite attributes */ + prune_unknown_attributes(&vattrs); + prune_unknown_attributes(&eattrs); + prune_unknown_attributes(&gattrs); + 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_empty_attrs(graph, 0, directed, &gattrs)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, no_of_nodes, &vattrs)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, &eattrs)); + IGRAPH_FINALLY_CLEAN(1); /* do not destroy 'graph', just pop it from the stack */ + igraph_vector_int_destroy(&edges); 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) { +static igraph_error_t igraph_i_gml_convert_to_key(const char *orig, char **key) { char strno[50]; size_t i, len = strlen(orig), newlen = 0, plen = 0; @@ -526,7 +841,7 @@ static int igraph_i_gml_convert_to_key(const char *orig, char **key) { } *key = IGRAPH_CALLOC(newlen + 1, char); if (! *key) { - IGRAPH_ERROR("Writing GML format failed.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Writing GML format failed.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } memcpy(*key, strno, plen * sizeof(char)); for (i = 0; i < len; i++) { @@ -539,19 +854,58 @@ static int igraph_i_gml_convert_to_key(const char *orig, char **key) { return IGRAPH_SUCCESS; } -#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Writing GML format failed.", IGRAPH_EFILE); } while (0) +/* Checks if a vector is free of duplicates. Since NaN == NaN is false, duplicate NaN values + * will not be detected. */ +static igraph_error_t igraph_i_vector_is_duplicate_free(const igraph_vector_t *v, igraph_bool_t *res) { + igraph_vector_t u; + igraph_integer_t n = igraph_vector_size(v); + + if (n < 2) { + *res = true; + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_vector_init_copy(&u, v)); + IGRAPH_FINALLY(igraph_vector_destroy, &u); + igraph_vector_sort(&u); + + *res = true; + for (igraph_integer_t i=1; i < n; i++) { + if (VECTOR(u)[i-1] == VECTOR(u)[i]) { + *res = false; + break; + } + } + + igraph_vector_destroy(&u); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +#define CHECK(cmd) do { int 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 + * \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. + * https://web.archive.org/web/20190207140002/http://www.fim.uni-passau.de/index.php?id=17297%26L=1 + * for details. * - * The graph, vertex and edges attributes are written to the - * file as well, if they are numeric or string. + * + * The graph, vertex and edges attributes are written to the + * file as well, if they are numeric or string. Boolean attributes are converted + * to numeric, with 0 and 1 used for false and true, respectively. + * NaN values of numeric attributes are skipped, as NaN is not part of the GML + * specification and other software may not be able to read files containing them. + * This is consistent with \ref igraph_read_graph_gml(), which produces NaN + * when an attribute value is missing. In contrast with NaN, infinite values + * are retained. Ensure that none of the numeric attributes values are infinite + * to produce a conformant GML file that can be read by other software. * - * As igraph is more forgiving about attribute names, it might + * + * 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 @@ -560,26 +914,46 @@ static int igraph_i_gml_convert_to_key(const char *orig, char **key) { * 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. + * + * The id vertex attribute is treated specially. + * If the id argument is not \c NULL 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 \c NULL and there is a + * numeric id vertex attribute, it will be used instead. If ids + * are not specified in either way then the regular igraph vertex IDs are used. + * If some of the supplied id values are invalid (non-integer or NaN), all supplied + * id are ignored and igraph vertex IDs are used instead. + * + * + * Note that whichever way vertex IDs are specified, their uniqueness is not checked. * - * Note that whichever way vertex ids are specified, their - * uniqueness is not checked. + * + * If the graph has edge attributes that become source + * or target after encoding, or the graph has an attribute that becomes + * directed, they will be ignored with a warning. GML uses these attributes + * to specify the edge endpoints, and the graph directedness, so we cannot write them + * to the file. Rename them before calling this function if you want to preserve them. * - * 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. + * \param options Set of |-combinable boolean flags for writing the GML file. + * \clist + * \cli 0 + * All options turned off. + * \cli IGRAPH_WRITE_GML_DEFAULT_SW + * Default options, currently equivalent to 0. May change in future versions. + * \cli IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW + * Do not encode any other characters than " as entities. Specifically, this + * option prevents the encoding of &. Useful when re-exporting a graph + * that was read from a GML file in which igraph could not interpret all entities, + * and thus passed them through without decoding. + * \endclist + * \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. + * If \c NULL, the igraph version with the current date and time is added. + * If "", the creator line is omitted. Otherwise, the + * supplied string is used verbatim. * \return Error code. * * Time complexity: should be proportional to the number of characters written @@ -591,39 +965,73 @@ static int igraph_i_gml_convert_to_key(const char *orig, char **key) { * \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_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, + igraph_write_gml_sw_t options, + const igraph_vector_t *id, const char *creator) { + igraph_strvector_t gnames, vnames, enames; /* attribute names */ + igraph_vector_int_t gtypes, vtypes, etypes; /* attribute types */ + igraph_integer_t gattr_no, vattr_no, eattr_no; /* attribute counts */ 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_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + /* Each element is a bit field used to prevent showing more + * than one warning for each vertex or edge attribute. */ + igraph_vector_int_t warning_shown; 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 */ + /* Creator line */ + if (creator == NULL) { + 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\"\n", + IGRAPH_VERSION, timestr)); + } else if (creator[0] == '\0') { + /* creator == "", omit Creator line */ + } else { + if (needs_coding(creator)) { + char *d; + IGRAPH_CHECK(entity_encode(creator, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, + "Creator \"%s\"\n", + creator)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + CHECK(fprintf(outstream, + "Creator \"%s\"\n", + creator)); + } + } - CHECK(fprintf(outstream, - "Creator \"igraph version %s %s\"\nVersion 1\ngraph\n[\n", - IGRAPH_VERSION, creator ? creator : timestr)); + /* Version line */ + CHECK(fprintf(outstream, "Version 1\n")); + + /* The graph */ + CHECK(fprintf(outstream, "graph\n[\n")); 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_VECTOR_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&etypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, &gnames, >ypes, &vnames, &vtypes, &enames, &etypes)); + gattr_no = igraph_vector_int_size(>ypes); + vattr_no = igraph_vector_int_size(&vtypes); + eattr_no = igraph_vector_int_size(&etypes); IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); @@ -631,12 +1039,11 @@ int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, /* 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); + igraph_bool_t found = false; + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + const char *n = igraph_strvector_get(&vnames, i); if (!strcmp(n, "id") && VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - found = 1; break; + found = true; break; } } if (found) { @@ -648,71 +1055,162 @@ int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, } } + /* Scan id vector for invalid values. If any are found, all ids are ignored. + * Invalid values may occur as a result of reading a GML file in which some + * nodes did not have an id, or by adding new vertices to a graph with an "id" + * attribute. In this case, the "id" attribute will contain NaN values. + */ + if (myid) { + if (igraph_vector_size(myid) != no_of_nodes) { + IGRAPH_ERROR("Size of id vector must match vertex count.", IGRAPH_EINVAL); + } + for (i = 0; i < no_of_nodes; ++i) { + igraph_real_t val = VECTOR(*myid)[i]; + if (val != (igraph_integer_t) val) { + IGRAPH_WARNINGF("%g is not a valid integer id for GML files, ignoring all supplied ids.", val); + myid = NULL; + break; + } + } + } + + if (myid) { + igraph_bool_t duplicate_free; + IGRAPH_CHECK(igraph_i_vector_is_duplicate_free(myid, &duplicate_free)); + if (! duplicate_free) { + IGRAPH_WARNING("Duplicate id values found, ignoring supplies ids."); + myid = NULL; + } + } + /* 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); + for (i = 0; i < gattr_no; i++) { + const char *name; + char *newname; + name = igraph_strvector_get(&gnames, i); 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"); + if (!strcmp(newname, "directed")) { + IGRAPH_WARNINGF("The graph attribute '%s' was ignored while writing GML format.", name); } else { - IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute ignored"); + if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + IGRAPH_WARNINGF("Infinite value in numeric graph attribute '%s'. " + "Produced GML file will not be conformant.", name); + } + 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) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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); } + /* Macros used to work with the bit fiels in 'warning_shown', + * and avoid showing warnings more than once for each attribute. */ +#define GETBIT(k, i) ((k) & (1 << i)) +#define SETBIT(k, i) ((k) |= (1 << i)) +#define WARN_ONCE(attrno, bit, warn) \ + do { \ + igraph_integer_t *p = &VECTOR(warning_shown)[attrno]; \ + if (! GETBIT(*p, bit)) { \ + warn; \ + SETBIT(*p, bit); \ + } \ + } while (0) + + /* Now come the vertices */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&warning_shown, vattr_no); for (i = 0; i < no_of_nodes; i++) { - long int j; + igraph_integer_t j; CHECK(fprintf(outstream, " node\n [\n")); /* id */ - CHECK(fprintf(outstream, " id %li\n", myid ? (long int)VECTOR(*myid)[i] : i)); + CHECK(fprintf(outstream, " id %" IGRAPH_PRId "\n", myid ? (igraph_integer_t)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); + for (j = 0; j < vattr_no; j++) { + igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(vtypes)[j]; + const char *name; + char *newname; + name = igraph_strvector_get(&vnames, j); if (!strcmp(name, "id")) { + /* No warning, the presence of this attribute is expected, and is handled specially. */ 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"); + if (!strcmp(newname, "id")) { + /* In case an attribute name would conflict with 'id' only after encoding. */ + WARN_ONCE(j, 0, + IGRAPH_WARNINGF("The vertex attribute '%s' was ignored while writing GML format.", name)); } else { - IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, + igraph_vss_1(i), &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + WARN_ONCE(j, 3, + IGRAPH_WARNINGF("Infinite value in numeric vertex attribute '%s'. " + "Produced GML file will not be conformant.", name)); + } + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, + igraph_vss_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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(i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + WARN_ONCE(j, 1, + IGRAPH_WARNINGF("The boolean vertex attribute '%s' was converted to numeric.", name)); + } else { + WARN_ONCE(j, 2, + IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean vertex attribute '%s' was ignored.", name)); + } } IGRAPH_FREE(newname); IGRAPH_FINALLY_CLEAN(1); @@ -721,46 +1219,70 @@ int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, } /* The edges too */ + IGRAPH_CHECK(igraph_vector_int_resize(&warning_shown, eattr_no)); + igraph_vector_int_fill(&warning_shown, 0); 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; + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t 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)); + CHECK(fprintf(outstream, " source %" IGRAPH_PRId "\n", + myid ? (igraph_integer_t)VECTOR(*myid)[from] : from)); + CHECK(fprintf(outstream, " target %" IGRAPH_PRId "\n", + myid ? (igraph_integer_t)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; - } + for (j = 0; j < eattr_no; j++) { + igraph_attribute_type_t type = (igraph_attribute_type_t) VECTOR(etypes)[j]; + const char *name; + char *newname; + name = igraph_strvector_get(&enames, j); 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"); + if (!strcmp(newname, "source") || !strcmp(newname, "target")) { + WARN_ONCE(j, 0, + IGRAPH_WARNINGF("The edge attribute '%s' was ignored while writing GML format.", name)); } else { - IGRAPH_WARNING("A non-numeric, non-string, non-boolean edge attribute was ignored"); + if (type == IGRAPH_ATTRIBUTE_NUMERIC) { + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, + igraph_ess_1(i), &numv)); + /* Treat NaN as missing, skip writing it. GML does not officially support NaN. */ + if (! isnan(VECTOR(numv)[0])) { + if (! isfinite(VECTOR(numv)[0])) { + WARN_ONCE(j, 3, + IGRAPH_WARNINGF("Infinite value in numeric edge attribute '%s'. " + "Produced GML file will not be conformant.", name)); + } + CHECK(fprintf(outstream, " %s ", newname)); + CHECK(igraph_real_fprintf_precise(outstream, VECTOR(numv)[0])); + CHECK(fputc('\n', outstream)); + } + } else if (type == IGRAPH_ATTRIBUTE_STRING) { + const char *s; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, + igraph_ess_1(i), &strv)); + s = igraph_strvector_get(&strv, 0); + if (needs_coding(s)) { + char *d; + IGRAPH_CHECK(entity_encode(s, &d, IGRAPH_WRITE_GML_ENCODE_ONLY_QUOT_SW & options)); + IGRAPH_FINALLY(igraph_free, d); + CHECK(fprintf(outstream, " %s \"%s\"\n", newname, d)); + IGRAPH_FREE(d); + IGRAPH_FINALLY_CLEAN(1); + } else { + 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(i), &boolv)); + CHECK(fprintf(outstream, " %s %d\n", newname, VECTOR(boolv)[0] ? 1 : 0)); + WARN_ONCE(j, 1, + IGRAPH_WARNINGF("The boolean edge attribute '%s' was converted to numeric.", name)); + } else { + WARN_ONCE(j, 2, + IGRAPH_WARNINGF("The non-numeric, non-string, non-boolean edge attribute '%s' was ignored.", name)); + } } IGRAPH_FREE(newname); IGRAPH_FINALLY_CLEAN(1); @@ -770,21 +1292,26 @@ int igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, CHECK(fprintf(outstream, "]\n")); +#undef GETBIT +#undef SETBIT +#undef WARN_ONCE + if (&v_myid == myid) { igraph_vector_destroy(&v_myid); IGRAPH_FINALLY_CLEAN(1); } + igraph_vector_int_destroy(&warning_shown); 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_vector_int_destroy(&etypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(>ypes); igraph_strvector_destroy(&enames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&gnames); - IGRAPH_FINALLY_CLEAN(9); + IGRAPH_FINALLY_CLEAN(10); return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/graphdb.c b/src/vendor/cigraph/src/io/graphdb.c index 21e1d4b95fa..1920e39397a 100644 --- a/src/vendor/cigraph/src/io/graphdb.c +++ b/src/vendor/cigraph/src/io/graphdb.c @@ -23,15 +23,16 @@ #include "igraph_foreign.h" #include "igraph_constructors.h" +#include "core/interruption.h" -#include "core/trie.h" - +/* Read a little-endian encoded 16-bit unsigned word. + * Returns negative value on failure. */ 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) { + if (b1 != EOF && b2 != EOF) { c1 = (unsigned char) b1; c2 = (unsigned char) b2; return c1 | (c2 << 8); } else { @@ -39,13 +40,26 @@ static int igraph_i_read_graph_graphdb_getword(FILE *instream) { } } +/* Determine whether the read failed due to an input error or end-of-file condition. + * Must only be called after a read failure, always returns a non-success error code. */ +static igraph_error_t handle_input_error(FILE *instream) { + if (feof(instream)) { + IGRAPH_ERROR("Unexpected end of file, truncated graphdb file.", IGRAPH_PARSEERROR); + } else { + IGRAPH_ERROR("Cannot read from file.", IGRAPH_EFILE); + } +} + /** * \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: + * This is a binary format, used in the ARG Graph Database + * for isomorphism testing. For more information, see + * https://mivia.unisa.it/datasets/graph-database/arg-database/ + * + * + * From the graph database homepage: * * * \blockquote @@ -62,9 +76,11 @@ static int igraph_i_read_graph_graphdb_getword(FILE *instream) { * first node of the graph has index 0. \endblockquote * * - * Only unlabelled graphs are implemented. + * As of igraph 0.10, only unlabelled graphs are implemented. + * * \param graph Pointer to an uninitialized graph object. - * \param instream The stream to read from. + * \param instream The stream to read from. It should be opened + * in binary mode. * \param directed Logical scalar, whether to create a directed graph. * \return Error code. * @@ -74,45 +90,41 @@ static int igraph_i_read_graph_graphdb_getword(FILE *instream) { * \example examples/simple/igraph_read_graph_graphdb.c */ -int igraph_read_graph_graphdb(igraph_t *graph, FILE *instream, +igraph_error_t 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); + const igraph_integer_t nodes = igraph_i_read_graph_graphdb_getword(instream); if (nodes < 0) { - IGRAPH_ERROR("Can't read from file", IGRAPH_EFILE); + IGRAPH_CHECK(handle_input_error(instream)); } - for (i = 0; !end && i < nodes; i++) { - long int len = igraph_i_read_graph_graphdb_getword(instream); + + igraph_vector_int_t edges; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 100); + igraph_vector_int_clear(&edges); + + for (igraph_integer_t i = 0; i < nodes; i++) { + igraph_integer_t len = igraph_i_read_graph_graphdb_getword(instream); if (len < 0) { - end = 1; - break; + IGRAPH_CHECK(handle_input_error(instream)); } - for (j = 0; ! end && j < len; j++) { - long int to = igraph_i_read_graph_graphdb_getword(instream); + for (igraph_integer_t j = 0; j < len; j++) { + igraph_integer_t to = igraph_i_read_graph_graphdb_getword(instream); if (to < 0) { - end = 1; - break; + IGRAPH_CHECK(handle_input_error(instream)); } - IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_ALLOW_INTERRUPTION(); } } - - if (end) { - IGRAPH_ERROR("Truncated graphdb file", IGRAPH_EFILE); + if (fgetc(instream) != EOF) { + IGRAPH_ERROR("Extra bytes at end of graphdb file.", IGRAPH_PARSEERROR); } - IGRAPH_CHECK(igraph_create(graph, &edges, (igraph_integer_t) nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/graphml.c b/src/vendor/cigraph/src/io/graphml.c index e16683e3e52..b0e587747db 100644 --- a/src/vendor/cigraph/src/io/graphml.c +++ b/src/vendor/cigraph/src/io/graphml.c @@ -22,17 +22,20 @@ */ #include "igraph_foreign.h" + #include "igraph_attributes.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "core/math.h" +#include "core/interruption.h" #include "core/trie.h" #include "graph/attributes.h" -#include "internal/hacks.h" /* strcasecmp */ +#include "internal/hacks.h" /* strcasecmp & strdup */ +#include "io/parse_utils.h" #include "config.h" +#include #include /* isnan */ #include #include /* va_start & co */ @@ -40,7 +43,7 @@ #define GRAPHML_NAMESPACE_URI "http://graphml.graphdrawing.org/xmlns" #if HAVE_LIBXML == 1 -#include +#include #include xmlEntity blankEntityStruct = { @@ -69,23 +72,20 @@ xmlEntity blankEntityStruct = { xmlEntityPtr blankEntity = &blankEntityStruct; -#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) do { \ - if (state->successful) { \ - igraph_i_graphml_sax_handler_error(state, msg); \ - } \ +#define toXmlChar(a) (BAD_CAST(a)) +#define fromXmlChar(a) ((char *)(a)) /* not the most elegant way... */ + +#define GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ + do { \ + if (state->successful) { \ + igraph_i_graphml_sax_handler_error(state, msg); \ + } \ + } while (0) +#define RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code) \ + do { \ + GRAPHML_PARSE_ERROR_WITH_CODE(state, msg, code); \ + return; \ } 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 */ @@ -96,19 +96,22 @@ typedef struct igraph_i_graphml_attribute_record_t { union { igraph_real_t as_numeric; igraph_bool_t as_boolean; - char* as_string; + char *as_string; } default_value; /* Default value of the attribute, if any */ igraph_attribute_record_t record; } igraph_i_graphml_attribute_record_t; +typedef enum { + START, INSIDE_GRAPHML, INSIDE_GRAPH, INSIDE_NODE, INSIDE_EDGE, + INSIDE_KEY, INSIDE_DEFAULT, INSIDE_DATA, FINISH, UNKNOWN, ERROR +} igraph_i_graphml_parser_state_index_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_i_graphml_parser_state_index_t st; igraph_t *g; igraph_trie_t node_trie; igraph_strvector_t edgeids; - igraph_vector_t edgelist; + igraph_vector_int_t edgelist; igraph_vector_int_t prev_state_stack; unsigned int unknown_depth; int index; @@ -125,53 +128,85 @@ struct igraph_i_graphml_parser_state { igraph_attribute_elemtype_t data_type; char *error_message; char *data_char; - long int act_node; + igraph_integer_t 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); + "attribute specifications.", file, line, target); } -static igraph_real_t igraph_i_graphml_parse_numeric(const char* char_data, - igraph_real_t default_value) { - double result; +static igraph_error_t igraph_i_graphml_parse_numeric( + const char* char_data, igraph_real_t* result, igraph_real_t default_value +) { + const char* trimmed; + size_t trimmed_length; if (char_data == 0) { - return default_value; + *result = default_value; + return IGRAPH_SUCCESS; } - if (sscanf(char_data, "%lf", &result) == 0) { - return default_value; + igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); + if (trimmed_length > 0) { + IGRAPH_CHECK(igraph_i_parse_real(trimmed, trimmed_length, result)); + } else { + *result = default_value; } - return result; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_graphml_parse_boolean(const char* char_data, - igraph_bool_t default_value) { - int value; +static igraph_error_t igraph_i_graphml_parse_boolean( + const char* char_data, igraph_bool_t* result, igraph_bool_t default_value +) { + igraph_integer_t value; + const char* trimmed; + size_t trimmed_length; + if (char_data == 0) { - return default_value; + *result = default_value; + return IGRAPH_SUCCESS; } - if (!strcasecmp("true", char_data)) { - return 1; + + igraph_i_trim_whitespace(char_data, strlen(char_data), &trimmed, &trimmed_length); + + if (trimmed_length == 4 && !strncasecmp(trimmed, "true", trimmed_length)) { + *result = 1; + return IGRAPH_SUCCESS; } - if (!strcasecmp("yes", char_data)) { - return 1; + + if (trimmed_length == 3 && !strncasecmp(trimmed, "yes", trimmed_length)) { + *result = 1; + return IGRAPH_SUCCESS; } - if (!strcasecmp("false", char_data)) { - return 0; + + if (trimmed_length == 5 && !strncasecmp(trimmed, "false", trimmed_length)) { + *result = 0; + return IGRAPH_SUCCESS; } - if (!strcasecmp("no", char_data)) { - return 0; + + if (trimmed_length == 2 && !strncasecmp(trimmed, "no", trimmed_length)) { + *result = 0; + return IGRAPH_SUCCESS; } - if (sscanf(char_data, "%d", &value) == 0) { - return default_value; + + if (trimmed_length > 0) { + if (isdigit(trimmed[0])) { + IGRAPH_CHECK(igraph_i_parse_integer(trimmed, trimmed_length, &value)); + } else { + IGRAPH_ERRORF("Cannot parse '%.*s' as Boolean value.", IGRAPH_PARSEERROR, + (int) trimmed_length, trimmed); + } + } else { + *result = default_value; + return IGRAPH_SUCCESS; } - return value != 0; + + *result = value != 0; + return IGRAPH_SUCCESS; } static void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute_record_t* rec) { @@ -193,16 +228,19 @@ static void igraph_i_graphml_attribute_record_destroy(igraph_i_graphml_attribute igraph_vector_bool_destroy((igraph_vector_bool_t*)rec->record.value); IGRAPH_FREE(rec->record.value); } + } else if (rec->record.type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + /* no value was set */ } - if (rec->id != 0) { - IGRAPH_FREE(rec->id); + if (rec->id != NULL) { + xmlFree((void *) rec->id); + rec->id = NULL; } 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) { +static igraph_error_t 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; @@ -229,8 +267,8 @@ static int igraph_i_graphml_parser_state_init(struct igraph_i_graphml_parser_sta 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_vector_int_init(&state->edgelist, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &state->edgelist); IGRAPH_CHECK(igraph_trie_init(&state->node_trie, 1)); IGRAPH_FINALLY(igraph_trie_destroy, &state->node_trie); @@ -257,7 +295,7 @@ static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser 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->edgelist); igraph_vector_int_destroy(&state->prev_state_stack); igraph_vector_ptr_destroy_all(&state->v_attrs); @@ -265,28 +303,25 @@ static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser igraph_vector_ptr_destroy_all(&state->g_attrs); if (state->data_key) { - free(state->data_key); + xmlFree((void *) state->data_key); state->data_key = NULL; } if (state->data_char) { - free(state->data_char); - state->data_char = NULL; + IGRAPH_FREE(state->data_char); } if (state->error_message) { - free(state->error_message); - state->error_message = NULL; + IGRAPH_FREE(state->error_message); } } -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; +static void igraph_i_graphml_parser_state_set_error_from_varargs( + struct igraph_i_graphml_parser_state *state, const char* msg, va_list args +) { const size_t max_error_message_length = 4096; - va_list ap; - - va_start(ap, msg); + state->successful = 0; + state->st = ERROR; if (state->error_message == 0) { /* ownership of state->error_message passed on immediately to @@ -294,32 +329,69 @@ static void igraph_i_graphml_sax_handler_error(void *state0, const char* msg, .. state->error_message = IGRAPH_CALLOC(max_error_message_length, char); } + /* we need to guard against state->error_message == 0, which may happen + * if the memory allocation for the error message itself failed */ + if (state->error_message != 0) { + vsnprintf(state->error_message, max_error_message_length, msg, args); + } +} + +static void igraph_i_graphml_parser_state_set_error_from_xmlerror( + struct igraph_i_graphml_parser_state *state, const xmlErrorPtr error +) { + const size_t max_error_message_length = 4096; + state->successful = 0; state->st = ERROR; - vsnprintf(state->error_message, max_error_message_length, msg, ap); - va_end(ap); + 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); + } + + /* we need to guard against state->error_message == 0, which may happen + * if the memory allocation for the error message itself failed */ + if (state->error_message != 0) { + snprintf(state->error_message, max_error_message_length, "Line %d: %s", + error->line, error->message); + } +} + +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; + va_list args; + va_start(args, msg); + igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); + va_end(args); } static xmlEntityPtr igraph_i_graphml_sax_handler_get_entity(void *state0, const xmlChar* name) { xmlEntityPtr predef = xmlGetPredefinedEntity(name); + const char* entityName; + IGRAPH_UNUSED(state0); if (predef != NULL) { return predef; } - IGRAPH_WARNING("unknown XML entity found\n"); + + entityName = fromXmlChar(name); + IGRAPH_WARNINGF("Unknown XML entity found: '%s'.", entityName); + return blankEntity; } -static void igraph_i_graphml_handle_unknown_start_tag(struct igraph_i_graphml_parser_state *state) { +static igraph_error_t 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); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); state->st = UNKNOWN; state->unknown_depth = 1; } else { state->unknown_depth++; } + return IGRAPH_SUCCESS; } static void igraph_i_graphml_sax_handler_start_document(void *state0) { @@ -335,22 +407,22 @@ static void igraph_i_graphml_sax_handler_start_document(void *state0) { state->ignore_namespaces = 0; } -static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { - long i, l; +static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_parser_state *state) { + igraph_integer_t 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_bool_t already_has_vertex_id = false, already_has_edge_id = false; igraph_vector_ptr_t vattr, eattr, gattr; - long int esize; - const void **tmp; + igraph_integer_t esize; 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_CHECK(igraph_vector_ptr_init(&vattr, igraph_vector_ptr_size(&state->v_attrs) + 1)); /* +1 for 'id' */ IGRAPH_FINALLY(igraph_vector_ptr_destroy, &vattr); + igraph_vector_ptr_resize(&vattr, 0); /* will be filled with push_back() */ esize = igraph_vector_ptr_size(&state->e_attrs); if (igraph_strvector_size(&state->edgeids) != 0) { @@ -358,9 +430,11 @@ static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_ } IGRAPH_CHECK(igraph_vector_ptr_init(&eattr, esize)); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &eattr); + igraph_vector_ptr_resize(&eattr, 0); /* will be filled with push_back() */ IGRAPH_CHECK(igraph_vector_ptr_init(&gattr, igraph_vector_ptr_size(&state->g_attrs))); IGRAPH_FINALLY(igraph_vector_ptr_destroy, &gattr); + igraph_vector_ptr_resize(&gattr, 0); /* will be filled with push_back() */ for (i = 0; i < igraph_vector_ptr_size(&state->v_attrs); i++) { igraph_i_graphml_attribute_record_t *graphmlrec = @@ -368,46 +442,47 @@ static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_ 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 it is then we cannot add the complementary '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_integer_t origsize = igraph_vector_size(vec); + igraph_integer_t 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_integer_t origsize = igraph_strvector_size(strvec); + igraph_integer_t 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_integer_t origsize = igraph_vector_bool_size(boolvec); + igraph_integer_t 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; } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ } - VECTOR(vattr)[i] = rec; + igraph_vector_ptr_push_back(&vattr, rec); /* reserved */ } 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; + idrec.value = igraph_i_trie_borrow_keys(&state->node_trie); + igraph_vector_ptr_push_back(&vattr, &idrec); /* reserved */ } else { - igraph_vector_ptr_pop_back(&vattr); + IGRAPH_WARNING("Could not add vertex ids, there is already an 'id' vertex attribute."); } for (i = 0; i < igraph_vector_ptr_size(&state->e_attrs); i++) { @@ -421,46 +496,46 @@ static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_ 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_integer_t origsize = igraph_vector_size(vec); + igraph_integer_t edges = igraph_vector_int_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_integer_t origsize = igraph_strvector_size(strvec); + igraph_integer_t edges = igraph_vector_int_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_integer_t origsize = igraph_vector_bool_size(boolvec); + igraph_integer_t edges = igraph_vector_int_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; } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ } - VECTOR(eattr)[i] = rec; + igraph_vector_ptr_push_back(&eattr, rec); /* reserved */ } if (igraph_strvector_size(&state->edgeids) != 0) { if (!already_has_edge_id) { - long int origsize = igraph_strvector_size(&state->edgeids); + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, igraph_vector_int_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; + igraph_vector_ptr_push_back(&eattr, &eidrec); /* reserved */ } else { - igraph_vector_ptr_pop_back(&eattr); - IGRAPH_WARNING("Could not add edge ids, " - "there is already an 'id' edge attribute"); + IGRAPH_WARNING("Could not add edge ids, there is already an 'id' edge attribute."); } } @@ -470,32 +545,36 @@ static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_ 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_integer_t 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_integer_t 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_integer_t 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; } + } else if (rec->type == IGRAPH_ATTRIBUTE_UNSPECIFIED) { + continue; /* skipped attribute */ } - VECTOR(gattr)[i] = rec; + igraph_vector_ptr_push_back(&gattr, rec); /* reserved */ } 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_FINALLY(igraph_destroy, state->g); /* because the next two lines may fail as well */ + IGRAPH_CHECK(igraph_add_vertices(state->g, igraph_trie_size(&state->node_trie), &vattr)); IGRAPH_CHECK(igraph_add_edges(state->g, &state->edgelist, &eattr)); + IGRAPH_FINALLY_CLEAN(1); /* graph construction completed successfully */ igraph_vector_ptr_destroy(&vattr); igraph_vector_ptr_destroy(&eattr); @@ -505,36 +584,51 @@ static int igraph_i_graphml_parser_state_finish_parsing(struct igraph_i_graphml_ 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)) +#define XML_ATTR_VALUE(it) *(it+3), (int)((*(it+4))-(*(it+3))) +#define XML_ATTR_VALUE_PF(it) (int)((*(it+4))-(*(it+3))), *(it+3) /* for use in printf-style function with "%.*s" */ + +static igraph_error_t igraph_i_graphml_add_attribute_key( + igraph_i_graphml_attribute_record_t** record, + const xmlChar** attrs, int nb_attrs, + struct igraph_i_graphml_parser_state *state +) { + + /* This function must return in three possible ways: + * + * - a proper newly allocated attribute record is returned in 'record' and + * the function returns IGRAPH_SUCCESS; the parser will process the attribute + * - NULL is returned in 'record' and the function returns an igraph error + * code; the parser will handle the error + * - NULL is returned in 'record', but the function itself returns + * IGRAPH_SUCCESS; the parser will skip the attribute + * + * The caller should be prepared to handle all three cases. + */ -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; + xmlChar *xmlStr; + char *str; 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; + igraph_integer_t id; + int i; + igraph_i_graphml_attribute_record_t *rec = NULL; + igraph_bool_t skip = false; if (!state->successful) { - return 0; + /* Parser is already in an error state */ + goto exit; } 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; + if (rec == NULL) { + IGRAPH_ERROR("Cannot allocate attribute record.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, rec); IGRAPH_FINALLY(igraph_i_graphml_attribute_record_destroy, rec); @@ -550,18 +644,33 @@ static igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key( localname = XML_ATTR_LOCALNAME(it); if (xmlStrEqual(localname, toXmlChar("id"))) { - rec->id = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); + if (xmlStr == NULL) { + IGRAPH_ERROR("Cannot duplicate value of 'id' attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + rec->id = fromXmlChar(xmlStr); + xmlStr = NULL; } else if (xmlStrEqual(localname, toXmlChar("attr.name"))) { - rec->record.name = fromXmlChar(xmlStrndup(XML_ATTR_VALUE(it))); + xmlStr = xmlStrndup(XML_ATTR_VALUE(it)); + if (xmlStr == NULL) { + IGRAPH_ERROR("Cannot duplicate value of 'attr.name' attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + rec->record.name = fromXmlChar(xmlStr); + xmlStr = NULL; } 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))) { + str = strdup(""); + if (!str) { + IGRAPH_ERROR("Cannot allocate new empty string.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } rec->type = I_GRAPHML_STRING; rec->record.type = IGRAPH_ATTRIBUTE_STRING; - rec->default_value.as_string = strdup(""); + rec->default_value.as_string = str; + str = NULL; } else if (!xmlStrncmp(toXmlChar("float"), XML_ATTR_VALUE(it))) { rec->type = I_GRAPHML_FLOAT; rec->record.type = IGRAPH_ATTRIBUTE_NUMERIC; @@ -579,9 +688,8 @@ static igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key( 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; + IGRAPH_ERRORF("Unknown attribute type '%.*s'.", IGRAPH_PARSEERROR, + XML_ATTR_VALUE_PF(it)); } } else if (xmlStrEqual(*it, toXmlChar("for"))) { /* graph, vertex or edge attribute? */ @@ -611,63 +719,64 @@ static igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key( 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; + IGRAPH_ERRORF("Unknown value '%.*s' in the 'for' attribute of a tag.", IGRAPH_PARSEERROR, + XML_ATTR_VALUE_PF(it)); } } } - /* 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; + /* throw an error if there is no ID; this is a clear violation of the GraphML DTD */ + if (rec->id == NULL) { + IGRAPH_ERROR("Found tag with no 'id' attribute.", IGRAPH_PARSEERROR); } /* in case of a missing attr.name attribute, use the id as the attribute name */ - if (rec->record.name == 0) { + if (rec->record.name == NULL) { rec->record.name = strdup(rec->id); + IGRAPH_CHECK_OOM(rec->record.name, "Cannot duplicate attribute ID as name."); } - /* if the attribute type is missing, throw an error */ + /* if the attribute type is missing, ignore the attribute with a warning */ 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); + IGRAPH_WARNINGF("Ignoring because of a missing 'attr.type' attribute.", 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; + IGRAPH_ERROR("Missing 'for' attribute in a tag.", IGRAPH_PARSEERROR); } - /* if the code above requested skipping the attribute, free everything and - * return */ + /* If attribute is skipped, proceed according to the type of the associated graph element. */ if (skip) { - igraph_i_graphml_attribute_record_destroy(rec); - igraph_free(rec); - IGRAPH_FINALLY_CLEAN(2); - return 0; + if (trie == 0) { + /* Attribute was skipped because it is not for a node, edge or the graph. + * Free everything and return. */ + if (rec) { + igraph_i_graphml_attribute_record_destroy(rec); + IGRAPH_FREE(rec); + } + IGRAPH_FINALLY_CLEAN(2); + goto exit; + } else { + /* If the skipped attribute was for a supported graph element, we add it + * as "UNSPECIFIED" so that we can avoid reporting "unknown attribute" warnings + * later. */ + rec->record.type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + } } - /* add to trie, attribues */ - igraph_trie_get(trie, rec->id, &id); + /* add to trie, attributes */ + IGRAPH_CHECK(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; + IGRAPH_ERRORF("Duplicate attribute found: '%s'.", IGRAPH_PARSEERROR, rec->id); } - ret = igraph_vector_ptr_push_back(ptrvector, rec); - if (ret) { - GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot read GraphML file", ret); - return 0; - } + IGRAPH_CHECK(igraph_vector_ptr_push_back(ptrvector, rec)); - /* Ownership of 'rec' is now taken by ptrvector so we can clean the - * finally stack */ - IGRAPH_FINALLY_CLEAN(2); /* rec, destructor + igraph_free */ + /* Ownership of 'rec' is now taken by ptrvector so we are not responsible + * for destroying and freeing it any more */ + IGRAPH_FINALLY_CLEAN(2); /* create the attribute values */ switch (rec->record.type) { @@ -677,45 +786,54 @@ static igraph_i_graphml_attribute_record_t* igraph_i_graphml_add_attribute_key( 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; + IGRAPH_ERROR("Cannot allocate value vector for Boolean attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, boolvec); + IGRAPH_CHECK(igraph_vector_bool_init(boolvec, 0)); rec->record.value = boolvec; - igraph_vector_bool_init(boolvec, 0); + IGRAPH_FINALLY_CLEAN(1); 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; + IGRAPH_ERROR("Cannot allocate value vector for numeric attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, vec); + IGRAPH_CHECK(igraph_vector_init(vec, 0)); rec->record.value = vec; - igraph_vector_init(vec, 0); + IGRAPH_FINALLY_CLEAN(1); 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; + IGRAPH_ERROR("Cannot allocate value vector for string attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } + IGRAPH_FINALLY(igraph_free, strvec); + IGRAPH_CHECK(igraph_strvector_init(strvec, 0)); rec->record.value = strvec; - igraph_strvector_init(strvec, 0); + IGRAPH_FINALLY_CLEAN(1); + break; + case IGRAPH_ATTRIBUTE_UNSPECIFIED: + rec->record.value = NULL; break; - default: break; + default: + IGRAPH_FATAL("Unexpected attribute type."); } - return rec; +exit: + *record = rec; + return IGRAPH_SUCCESS; } -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) { +static igraph_error_t 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; + return IGRAPH_SUCCESS; } for (i = 0, it = (xmlChar**)attrs; i < nb_attrs; i++, it += 5) { @@ -726,52 +844,65 @@ static void igraph_i_graphml_attribute_data_setup(struct igraph_i_graphml_parser if (xmlStrEqual(*it, toXmlChar("key"))) { if (state->data_key) { - free(state->data_key); + xmlFree((void *) state->data_key); + state->data_key = NULL; } state->data_key = xmlStrndup(XML_ATTR_VALUE(it)); + if (state->data_key == 0) { + return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ + } if (state->data_char) { - free(state->data_char); + IGRAPH_FREE(state->data_char); } - state->data_char = NULL; state->data_type = type; } else { /* ignore */ } } + + return IGRAPH_SUCCESS; } -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; +static igraph_error_t igraph_i_graphml_append_to_data_char( + struct igraph_i_graphml_parser_state *state, const xmlChar *data, int len +) { + igraph_integer_t data_char_new_start = 0; + char* new_data_char; if (!state->successful) { - return; + return IGRAPH_SUCCESS; } if (state->data_char) { - data_char_new_start = (long int) strlen(state->data_char); - state->data_char = IGRAPH_REALLOC(state->data_char, + data_char_new_start = strlen(state->data_char); + new_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); + new_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); + + if (new_data_char == 0) { + /* state->data_char is left untouched here so that's good */ + return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ } + + state->data_char = new_data_char; memcpy(state->data_char + data_char_new_start, data, (size_t) len * sizeof(xmlChar)); state->data_char[data_char_new_start + len] = '\0'; + + return IGRAPH_SUCCESS; } -static void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parser_state *state) { +static igraph_error_t 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; + igraph_integer_t recid, id = 0; + igraph_error_t result = IGRAPH_SUCCESS; switch (type) { case IGRAPH_ATTRIBUTE_GRAPH: @@ -787,30 +918,26 @@ static void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parse case IGRAPH_ATTRIBUTE_EDGE: trie = &state->e_names; ptrvector = &state->e_attrs; - id = igraph_vector_size(&state->edgelist) / 2 - 1; /* hack */ + id = igraph_vector_int_size(&state->edgelist) / 2 - 1; /* hack */ break; default: - /* impossible */ - break; + IGRAPH_FATAL("Unexpected attribute element type."); } 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_WARNING("Missing attribute key in a tag, ignoring attribute."); + goto exit; } - igraph_trie_check(trie, key, &recid); + IGRAPH_CHECK(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, + IGRAPH_WARNINGF( + "Unknown attribute key '%s' in a tag, ignoring attribute.", key ); - IGRAPH_FREE(state->data_char); - return; + goto exit; } graphmlrec = VECTOR(*ptrvector)[recid]; @@ -820,49 +947,43 @@ static void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parse igraph_vector_bool_t *boolvec; igraph_vector_t *vec; igraph_strvector_t *strvec; - long int s, i; + igraph_integer_t 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); - } + IGRAPH_CHECK(igraph_vector_bool_resize(boolvec, id + 1)); 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); + IGRAPH_CHECK(igraph_i_graphml_parse_boolean( + state->data_char, VECTOR(*boolvec) + id, 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); - } + IGRAPH_CHECK(igraph_vector_resize(vec, id + 1)); 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); + IGRAPH_CHECK(igraph_i_graphml_parse_numeric( + state->data_char, VECTOR(*vec) + id, + 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); - } + IGRAPH_CHECK(igraph_strvector_resize(strvec, id + 1)); strvalue = graphmlrec->default_value.as_string; for (i = s; i < id; i++) { - igraph_strvector_set(strvec, i, strvalue); + IGRAPH_CHECK(igraph_strvector_set(strvec, i, strvalue)); } } if (state->data_char) { @@ -870,83 +991,85 @@ static void igraph_i_graphml_attribute_data_finish(struct igraph_i_graphml_parse } 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); - } + IGRAPH_CHECK(igraph_strvector_set(strvec, id, strvalue)); break; - default: + case IGRAPH_ATTRIBUTE_UNSPECIFIED: break; + default: + IGRAPH_FATAL("Unexpected attribute type."); } +exit: if (state->data_char) { IGRAPH_FREE(state->data_char); + state->data_char = NULL; } + + return result; } -static void igraph_i_graphml_attribute_default_value_finish( - struct igraph_i_graphml_parser_state *state) { +static igraph_error_t 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; + igraph_error_t result = IGRAPH_SUCCESS; + char* str = 0; - if (graphmlrec == 0) { - IGRAPH_FATAL( - "state->current_attr_record was null where it should have been " - "non-null; please report as a bug." - ); - return; - } + IGRAPH_ASSERT(state->current_attr_record != NULL); if (state->data_char == 0) { - return; + return IGRAPH_SUCCESS; } switch (graphmlrec->record.type) { case IGRAPH_ATTRIBUTE_BOOLEAN: - graphmlrec->default_value.as_boolean = igraph_i_graphml_parse_boolean( - state->data_char, 0); + IGRAPH_CHECK(igraph_i_graphml_parse_boolean( + state->data_char, &graphmlrec->default_value.as_boolean, 0 + )); break; case IGRAPH_ATTRIBUTE_NUMERIC: - graphmlrec->default_value.as_numeric = igraph_i_graphml_parse_numeric( - state->data_char, IGRAPH_NAN); + IGRAPH_CHECK(igraph_i_graphml_parse_numeric( + state->data_char, &graphmlrec->default_value.as_numeric, 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); + str = strdup(state->data_char); + if (str == NULL) { + IGRAPH_ERROR("Cannot allocate memory for string attribute.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + } + + if (graphmlrec->default_value.as_string != 0) { + IGRAPH_FREE(graphmlrec->default_value.as_string); } + graphmlrec->default_value.as_string = str; + str = NULL; break; - default: + case IGRAPH_ATTRIBUTE_UNSPECIFIED: break; + default: + IGRAPH_FATAL("Unexpected attribute type."); } if (state->data_char) { IGRAPH_FREE(state->data_char); } + + return result; } -static void igraph_i_graphml_sax_handler_start_element_ns( - void *state0, const xmlChar* localname, const xmlChar* prefix, +static igraph_error_t igraph_i_graphml_sax_handler_start_element_ns_inner( + struct igraph_i_graphml_parser_state* state, 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; + xmlChar* attr_value = 0; + igraph_integer_t id1, id2; int i; - igraph_bool_t tag_is_unknown = 0; + igraph_bool_t tag_is_unknown = false; 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 @@ -966,8 +1089,8 @@ static void igraph_i_graphml_sax_handler_start_element_ns( } if (tag_is_unknown) { - igraph_i_graphml_handle_unknown_start_tag(state); - return; + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); + goto exit; } switch (state->st) { @@ -980,7 +1103,7 @@ static void igraph_i_graphml_sax_handler_start_element_ns( } state->st = INSIDE_GRAPHML; } else { - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } break; @@ -1009,11 +1132,18 @@ static void igraph_i_graphml_sax_handler_start_element_ns( } state->index--; } else if (xmlStrEqual(localname, toXmlChar("key"))) { - state->current_attr_record = - igraph_i_graphml_add_attribute_key(attributes, nb_attributes, state); + IGRAPH_CHECK( + igraph_i_graphml_add_attribute_key( + &state->current_attr_record, + attributes, nb_attributes, state + ) + ); + /* NULL is okay here for state->current_attr_record -- we should have + * triggered an error in the parser already if we returned NULL, and + * the rest of the code is prepared to handle NULLs */ state->st = INSIDE_KEY; } else { - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } break; @@ -1023,13 +1153,13 @@ static void igraph_i_graphml_sax_handler_start_element_ns( if (state->current_attr_record != NULL && xmlStrEqual(localname, toXmlChar("default"))) { state->st = INSIDE_DEFAULT; } else { - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(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); + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); break; case INSIDE_GRAPH: @@ -1043,31 +1173,54 @@ static void igraph_i_graphml_sax_handler_start_element_ns( 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); + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge source attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); } 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); + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge target attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id2)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); } 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); + igraph_integer_t edges = igraph_vector_int_size(&state->edgelist) / 2 + 1; + igraph_integer_t origsize = igraph_strvector_size(&state->edgeids); + + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of edge ID attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_strvector_resize(&state->edgeids, edges)); + for (; origsize < edges - 1; origsize++) { - igraph_strvector_set(&state->edgeids, origsize, ""); + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, origsize, "")); } - igraph_strvector_set(&state->edgeids, edges - 1, attr_value); - free(attr_value); + + IGRAPH_CHECK(igraph_strvector_set(&state->edgeids, edges - 1, fromXmlChar(attr_value))); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); } } if (id1 >= 0 && id2 >= 0) { - igraph_vector_push_back(&state->edgelist, id1); - igraph_vector_push_back(&state->edgelist, id2); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->edgelist, id2)); } else { - igraph_i_graphml_sax_handler_error(state, "Edge with missing source or target encountered"); - return; + IGRAPH_ERROR("Edge with missing source or target encountered.", IGRAPH_PARSEERROR); } state->st = INSIDE_EDGE; } else if (xmlStrEqual(localname, toXmlChar("node"))) { @@ -1079,9 +1232,16 @@ static void igraph_i_graphml_sax_handler_start_element_ns( 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); + attr_value = xmlStrndup(XML_ATTR_VALUE(it)); + if (attr_value == 0) { + IGRAPH_ERROR("Cannot copy value of node ID attribute.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(xmlFree, attr_value); + + IGRAPH_CHECK(igraph_trie_get(&state->node_trie, fromXmlChar(attr_value), &id1)); + + xmlFree(attr_value); attr_value = NULL; + IGRAPH_FINALLY_CLEAN(1); break; } } @@ -1089,63 +1249,91 @@ static void igraph_i_graphml_sax_handler_start_element_ns( state->act_node = id1; } else { state->act_node = -1; - igraph_i_graphml_sax_handler_error(state, "Node with missing id encountered"); - return; + IGRAPH_ERROR("Node with missing ID encountered.", IGRAPH_PARSEERROR); } 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); + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_GRAPH + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); state->st = INSIDE_DATA; } else { - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(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); + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_VERTEX + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); state->st = INSIDE_DATA; + } else { + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } 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); + IGRAPH_CHECK(igraph_i_graphml_attribute_data_setup( + state, attributes, nb_attributes, IGRAPH_ATTRIBUTE_EDGE + )); + IGRAPH_CHECK(igraph_vector_int_push_back(&state->prev_state_stack, state->st)); state->st = INSIDE_DATA; + } else { + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); } break; case INSIDE_DATA: /* We do not expect any new tags within a tag */ - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); break; case UNKNOWN: - igraph_i_graphml_handle_unknown_start_tag(state); + IGRAPH_CHECK(igraph_i_graphml_handle_unknown_start_tag(state)); break; - default: + case FINISH: break; + + default: + IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); } + +exit: + return IGRAPH_SUCCESS; } -static void igraph_i_graphml_sax_handler_end_element_ns( - void *state0, - const xmlChar* localname, const xmlChar* prefix, - const xmlChar* uri) { +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; + igraph_error_t result; if (!state->successful) { return; } + result = igraph_i_graphml_sax_handler_start_element_ns_inner( + state, localname, prefix, uri, nb_namespaces, namespaces, + nb_attributes, nb_defaulted, attributes + ); + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + } +} + +static igraph_error_t igraph_i_graphml_sax_handler_end_element_ns_inner( + struct igraph_i_graphml_parser_state* state, + const xmlChar* localname, const xmlChar* prefix, + const xmlChar* uri +) { IGRAPH_UNUSED(localname); IGRAPH_UNUSED(prefix); IGRAPH_UNUSED(uri); @@ -1165,7 +1353,7 @@ static void igraph_i_graphml_sax_handler_end_element_ns( break; case INSIDE_DEFAULT: - igraph_i_graphml_attribute_default_value_finish(state); + IGRAPH_CHECK(igraph_i_graphml_attribute_default_value_finish(state)); state->st = INSIDE_KEY; break; @@ -1178,25 +1366,54 @@ static void igraph_i_graphml_sax_handler_end_element_ns( break; case INSIDE_DATA: - igraph_i_graphml_attribute_data_finish(state); - state->st = igraph_vector_int_pop_back(&state->prev_state_stack); + IGRAPH_CHECK(igraph_i_graphml_attribute_data_finish(state)); + IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); + state->st = (igraph_i_graphml_parser_state_index_t) 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); + IGRAPH_ASSERT(!igraph_vector_int_empty(&state->prev_state_stack)); + state->st = (igraph_i_graphml_parser_state_index_t) igraph_vector_int_pop_back(&state->prev_state_stack); } break; - default: + case FINISH: break; + + default: + IGRAPH_FATALF("Unexpected GraphML reader state %d.", (int) state->st); + } + + return IGRAPH_SUCCESS; +} + +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; + igraph_error_t result; + + if (!state->successful) { + return; + } + + result = igraph_i_graphml_sax_handler_end_element_ns_inner( + state, localname, prefix, uri + ); + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); } } 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; + igraph_error_t result = IGRAPH_SUCCESS; if (!state->successful) { return; @@ -1208,13 +1425,17 @@ static void igraph_i_graphml_sax_handler_chars(void* state0, const xmlChar* ch, case INSIDE_DATA: case INSIDE_DEFAULT: - igraph_i_graphml_append_to_data_char(state, ch, len); + result = igraph_i_graphml_append_to_data_char(state, ch, len); break; default: /* just ignore it */ break; } + + if (result != IGRAPH_SUCCESS) { + RETURN_GRAPHML_PARSE_ERROR_WITH_CODE(state, "Cannot parse GraphML file.", result); + } } static xmlSAXHandler igraph_i_graphml_sax_handler = { @@ -1256,9 +1477,10 @@ static xmlSAXHandler igraph_i_graphml_sax_handler = { #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; +static igraph_error_t igraph_i_xml_escape(const char* src, char** dest) { + igraph_integer_t destlen = 0; + const char *s; + char *d; unsigned char ch; for (s = src; *s; s++, destlen++) { @@ -1274,15 +1496,12 @@ static int igraph_i_xml_escape(char* src, char** dest) { } 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); + IGRAPH_ERRORF("Forbidden control character 0x%02X found in igraph_i_xml_escape.", IGRAPH_EINVAL, ch); } } *dest = IGRAPH_CALLOC(destlen + 1, char); if (!*dest) { - IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + IGRAPH_ERROR("Not enough memory.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } for (s = src, d = *dest; *s; s++, d++) { ch = (unsigned char)(*s); @@ -1302,8 +1521,23 @@ static int igraph_i_xml_escape(char* src, char** dest) { } } *d = 0; - return 0; + return IGRAPH_SUCCESS; +} + +#if HAVE_LIBXML == 1 +static void igraph_i_libxml_generic_error_handler(void* ctx, const char* msg, ...) { + struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; + va_list args; + va_start(args, msg); + igraph_i_graphml_parser_state_set_error_from_varargs(state, msg, args); + va_end(args); +} + +static void igraph_i_libxml_structured_error_handler(void* ctx, xmlErrorPtr error) { + struct igraph_i_graphml_parser_state* state = (struct igraph_i_graphml_parser_state*) ctx; + igraph_i_graphml_parser_state_set_error_from_xmlerror(state, error); } +#endif /** * \ingroup loadsave @@ -1315,7 +1549,8 @@ static int igraph_i_xml_escape(char* src, char** dest) { * 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. + * is attached, see \ref igraph_set_attribute_table(). String attrribute values + * are returned in UTF-8 encoding. * * * Graph attribute names are taken from the attr.name attributes of the @@ -1339,10 +1574,14 @@ static int igraph_i_xml_escape(char* src, char** dest) { * * \example examples/simple/graphml.c */ -int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { +igraph_error_t igraph_read_graph_graphml(igraph_t *graph, FILE *instream, igraph_integer_t index) { #if HAVE_LIBXML == 1 xmlParserCtxtPtr ctxt; + xmlGenericErrorFunc libxml_old_generic_error_handler; + void* libxml_old_generic_error_context; + xmlStructuredErrorFunc libxml_old_structured_error_handler; + void* libxml_old_structured_error_context; xmlDocPtr doc; struct igraph_i_graphml_parser_state state; @@ -1352,65 +1591,83 @@ int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { char* error_message; if (index < 0) { - IGRAPH_ERROR("Graph index must be non-negative", IGRAPH_EINVAL); + 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 */ + /* Create a progressive parser context and use the first 4K to detect the + * encoding */ 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_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); + if (res < (int) sizeof buffer && !feof(instream)) { + IGRAPH_ERROR("IO error while reading GraphML data.", IGRAPH_EFILE); } + /* Retrieve the current libxml2 error handlers and temporarily replace them + * with ones that do not print anything to stdout/stderr */ + libxml_old_generic_error_handler = xmlGenericError; + libxml_old_generic_error_context = xmlGenericErrorContext; + libxml_old_structured_error_handler = xmlStructuredError; + libxml_old_structured_error_context = xmlStructuredErrorContext; + xmlSetGenericErrorFunc(&state, &igraph_i_libxml_generic_error_handler); + xmlSetStructuredErrorFunc(&state, &igraph_i_libxml_structured_error_handler); + /* 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); + * survive whatever happens here. So, we put a barrier on the FINALLY stack + * that prevents IGRAPH_ERROR() from freeing the parser state, and then we + * do this ourselves when needed */ + IGRAPH_FINALLY_ENTER(); + { + ctxt = xmlCreatePushParserCtxt(&igraph_i_graphml_sax_handler, + &state, + buffer, + res, + NULL); + if (ctxt) { + if (xmlCtxtUseOptions(ctxt, + XML_PARSE_NOBLANKS | + XML_PARSE_NONET | XML_PARSE_NSCLEAN | + XML_PARSE_NOCDATA | XML_PARSE_HUGE + )) { + xmlFreeParserCtxt(ctxt); + ctxt = NULL; + } + } - /* Do the parsing */ - while ((res = (int) fread(buffer, 1, sizeof(buffer), instream)) > 0) { - xmlParseChunk(ctxt, buffer, res, 0); - if (!state.successful) { - break; + /* Do the parsing */ + if (ctxt) { + while ((res = (int) fread(buffer, 1, sizeof buffer, instream)) > 0) { + xmlParseChunk(ctxt, buffer, res, 0); + if (!state.successful) { + break; + } + IGRAPH_ALLOW_INTERRUPTION(); + } + xmlParseChunk(ctxt, buffer, res, 1); } } - xmlParseChunk(ctxt, buffer, res, 1); + IGRAPH_FINALLY_EXIT(); + + /* Restore error handlers */ + xmlSetGenericErrorFunc(libxml_old_generic_error_context, libxml_old_generic_error_handler); + xmlSetStructuredErrorFunc(libxml_old_structured_error_context, libxml_old_structured_error_handler); /* 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); + if (ctxt) { + 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); + } + } else { + /* We could not create the context earlier so no parsing was done */ + IGRAPH_ERROR("Cannot create XML parser context.", IGRAPH_FAILURE); } /* Extract the error message from the parser state (if any), and make a @@ -1419,28 +1676,28 @@ int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { 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); + IGRAPH_FINALLY(igraph_free, error_message); } /* Trigger the stored error if needed */ if (!parsing_successful) { if (error_message != NULL) { + size_t len = strlen(error_message); + if (error_message[len-1] == '\n') { + error_message[len-1] = '\0'; + } IGRAPH_ERROR(error_message, IGRAPH_PARSEERROR); } else { - IGRAPH_ERROR("Malformed GraphML file", IGRAPH_PARSEERROR); + 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); + IGRAPH_ERROR("Graph index was too large.", IGRAPH_EINVAL); } /* Okay, everything seems good. We can now take the parser state and @@ -1456,20 +1713,22 @@ int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { IGRAPH_UNUSED(instream); IGRAPH_UNUSED(index); - IGRAPH_ERROR("GraphML support is disabled", IGRAPH_UNIMPLEMENTED); + 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 + * \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. * + * + * When a numerical attribute value is NaN, it will be omitted from the file. + * * \param graph The graph to write. * \param outstream The stream object to write to, it should be * writable. @@ -1486,14 +1745,14 @@ int igraph_read_graph_graphml(igraph_t *graph, FILE *instream, int index) { * * \example examples/simple/graphml.c */ -int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, +igraph_error_t 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_int_t gtypes, vtypes, etypes; + igraph_integer_t i; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; @@ -1503,27 +1762,27 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n", GRAPHML_NAMESPACE_URI); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } /* dump the elements if any */ @@ -1535,81 +1794,81 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, 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_VECTOR_INT_INIT_FINALLY(>ypes, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vtypes, 0); + IGRAPH_VECTOR_INT_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); + for (i = 0; i < igraph_vector_int_size(>ypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&gnames, i); 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); + 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); + 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_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); + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&vnames, i); 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); + 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); + 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_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); + for (i = 0; i < igraph_vector_int_size(&etypes); i++) { + const char *name; char *name_escaped; + name = igraph_strvector_get(&enames, i); 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); + 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); + 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_ERROR("Write failed.", IGRAPH_EFILE); } } IGRAPH_FREE(name_escaped); @@ -1617,60 +1876,64 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, ret = fprintf(outstream, " \n", (igraph_is_directed(graph) ? "directed" : "undirected")); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + 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; + for (i = 0; i < igraph_vector_int_size(>ypes); i++) { + const char *name; char *name_escaped; if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_strvector_get(&gnames, i, &name); + name = igraph_strvector_get(&gnames, i); 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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - char *s, *s_escaped; - igraph_strvector_get(&gnames, i, &name); + const char *s; + char *s_escaped; + name = igraph_strvector_get(&gnames, i); 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); + } IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); - igraph_strvector_get(&strv, 0, &s); + s = igraph_strvector_get(&strv, 0); 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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - igraph_strvector_get(&gnames, i, &name); + name = igraph_strvector_get(&gnames, i); 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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } @@ -1678,16 +1941,16 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, /* 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); + const char *name; char *name_escaped; + ret = fprintf(outstream, " \n", l); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } - for (i = 0; i < igraph_vector_size(&vtypes); i++) { + for (i = 0; i < igraph_vector_int_size(&vtypes); i++) { if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_strvector_get(&vnames, i, &name); + name = igraph_strvector_get(&vnames, i); IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, igraph_vss_1(l), &numv)); if (!isnan(VECTOR(numv)[0])) { @@ -1695,39 +1958,43 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, ret = fprintf(outstream, " ", vprefix, name_escaped); IGRAPH_FREE(name_escaped); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - char *s, *s_escaped; - igraph_strvector_get(&vnames, i, &name); + const char *s; + char *s_escaped; + name = igraph_strvector_get(&vnames, i); 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); + } IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr(graph, name, igraph_vss_1(l), &strv)); - igraph_strvector_get(&strv, 0, &s); + s = igraph_strvector_get(&strv, 0); 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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } else if (VECTOR(vtypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - igraph_strvector_get(&vnames, i, &name); + name = igraph_strvector_get(&vnames, i); 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)); @@ -1735,89 +2002,93 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, vprefix, name_escaped, VECTOR(boolv)[0] ? "true" : "false"); IGRAPH_FREE(name_escaped); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } /* Now the edges */ - IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &it)); + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &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); + const char *name; char *name_escaped; + igraph_integer_t edge = IGRAPH_EIT_GET(it); + igraph_edge(graph, edge, &from, &to); + ret = fprintf(outstream, " \n", + from, to); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } - for (i = 0; i < igraph_vector_size(&etypes); i++) { + for (i = 0; i < igraph_vector_int_size(&etypes); i++) { if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { - igraph_strvector_get(&enames, i, &name); + name = igraph_strvector_get(&enames, i); IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, - igraph_ess_1((igraph_integer_t) edge), &numv)); + igraph_ess_1(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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_STRING) { - char *s, *s_escaped; - igraph_strvector_get(&enames, i, &name); + const char *s; + char *s_escaped; + name = igraph_strvector_get(&enames, i); 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); + } 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_ess_1(edge), &strv)); + s = igraph_strvector_get(&strv, 0); 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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } ret = fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } else if (VECTOR(etypes)[i] == IGRAPH_ATTRIBUTE_BOOLEAN) { - igraph_strvector_get(&enames, i, &name); + name = igraph_strvector_get(&enames, i); IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr(graph, name, - igraph_ess_1((igraph_integer_t) edge), &boolv)); + igraph_ess_1(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); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } } } ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -1826,23 +2097,23 @@ int igraph_write_graph_graphml(const igraph_t *graph, FILE *outstream, ret = fprintf(outstream, " \n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } fprintf(outstream, "\n"); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write failed.", IGRAPH_EFILE); } 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_int_destroy(>ypes); + igraph_vector_int_destroy(&vtypes); + igraph_vector_int_destroy(&etypes); igraph_vector_destroy(&numv); igraph_strvector_destroy(&strv); igraph_vector_bool_destroy(&boolv); IGRAPH_FINALLY_CLEAN(9); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/leda.c b/src/vendor/cigraph/src/io/leda.c index b33f2862a27..1191eaa163b 100644 --- a/src/vendor/cigraph/src/io/leda.c +++ b/src/vendor/cigraph/src/io/leda.c @@ -30,7 +30,11 @@ #include -#define CHECK(cmd) do { ret=cmd; if (ret<0) IGRAPH_ERROR("Write failed", IGRAPH_EFILE); } while (0) +#define CHECK(cmd) \ + do { \ + int ret=(cmd); \ + if (ret<0) IGRAPH_ERROR("Writing LEDA format failed.", IGRAPH_EFILE); \ + } while (0) /** * \function igraph_write_graph_leda @@ -47,64 +51,68 @@ * \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. + * are to be stored in the output, or \c NULL if no + * vertex attribute should 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. + * are to be stored in the output, or \c NULL if no + * edge attribute should 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_error_t igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, + const char *vertex_attr_name, + const char *edge_attr_name) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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 i = 0; + igraph_attribute_type_t vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; + igraph_attribute_type_t edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; igraph_integer_t from, to, rev; - IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), - &it)); + 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"); + IGRAPH_WARNINGF("The vertex attribute '%s' does not exist. No vertex values will be written.", + vertex_attr_name); + vertex_attr_name = NULL; } 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"); + vertex_attr_type != IGRAPH_ATTRIBUTE_STRING && + vertex_attr_type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_WARNINGF("The vertex attribute '%s' is not numeric, string or boolean. " + "No vertex values will be written.", + vertex_attr_name); + vertex_attr_name = NULL; vertex_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; } } /* 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"); + IGRAPH_WARNINGF("The edge attribute '%s' does not exist. No edge values will be written.", + edge_attr_name); + edge_attr_name = NULL; } 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"); + edge_attr_type != IGRAPH_ATTRIBUTE_STRING && + edge_attr_type != IGRAPH_ATTRIBUTE_BOOLEAN) { + IGRAPH_WARNINGF("The edge attribute '%s' is not numeric, string or boolean. " + "No edge values will be written.", + edge_attr_name); + edge_attr_name = NULL; edge_attr_type = IGRAPH_ATTRIBUTE_UNSPECIFIED; } } @@ -113,22 +121,28 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, switch (vertex_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: - CHECK(fprintf(outstream, "float\n")); + CHECK(fprintf(outstream, "double\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + CHECK(fprintf(outstream, "bool\n")); + break; default: CHECK(fprintf(outstream, "void\n")); } switch (edge_attr_type) { case IGRAPH_ATTRIBUTE_NUMERIC: - CHECK(fprintf(outstream, "float\n")); + CHECK(fprintf(outstream, "double\n")); break; case IGRAPH_ATTRIBUTE_STRING: CHECK(fprintf(outstream, "string\n")); break; + case IGRAPH_ATTRIBUTE_BOOLEAN: + CHECK(fprintf(outstream, "bool\n")); + break; default: CHECK(fprintf(outstream, "void\n")); } @@ -137,7 +151,7 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, /* Start writing vertices */ CHECK(fprintf(outstream, "# Vertices\n")); - CHECK(fprintf(outstream, "%ld\n", no_of_nodes)); + CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_nodes)); if (vertex_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Vertices with numeric attributes */ @@ -166,9 +180,9 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, graph, vertex_attr_name, igraph_vss_all(), &values)); for (i = 0; i < no_of_nodes; i++) { - const char* str = STR(values, i); + const char *str = STR(values, i); if (strchr(str, '\n') != 0) { - IGRAPH_ERROR("edge attribute values cannot contain newline characters", + IGRAPH_ERROR("Vertex attribute values cannot contain newline characters.", IGRAPH_EINVAL); } CHECK(fprintf(outstream, "|{%s}|\n", str)); @@ -176,6 +190,20 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); + } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Vertices with boolean attributes */ + igraph_vector_bool_t values; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_nodes); + IGRAPH_CHECK(igraph_i_attribute_get_bool_vertex_attr( + graph, vertex_attr_name, igraph_vss_all(), &values)); + + for (i = 0; i < no_of_nodes; i++) { + CHECK(fprintf(outstream, "|{%s|}\n", VECTOR(values)[i] ? "true" : "false")); + } + + igraph_vector_bool_destroy(&values); + IGRAPH_FINALLY_CLEAN(1); } else { /* Vertices with no attributes */ for (i = 0; i < no_of_nodes; i++) { @@ -184,7 +212,7 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, } CHECK(fprintf(outstream, "# Edges\n")); - CHECK(fprintf(outstream, "%ld\n", no_of_edges)); + CHECK(fprintf(outstream, "%" IGRAPH_PRId "\n", no_of_edges)); if (edge_attr_type == IGRAPH_ATTRIBUTE_NUMERIC) { /* Edges with numeric attributes */ @@ -193,15 +221,15 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, 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); + igraph_integer_t eid = IGRAPH_EIT_GET(it); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); 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(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{", + from + 1, to + 1, + rev + 1)); CHECK(igraph_real_fprintf_precise(outstream, VECTOR(values)[eid])); CHECK(fprintf(outstream, "}|\n")); IGRAPH_EIT_NEXT(it); @@ -216,35 +244,59 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, 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); + igraph_integer_t eid = IGRAPH_EIT_GET(it); + const char *str = STR(values, eid); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); if (rev == IGRAPH_EIT_GET(it)) { rev = -1; } if (strchr(str, '\n') != 0) { - IGRAPH_ERROR("edge attribute values cannot contain newline characters", + 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)); + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", + from + 1, to + 1, + rev + 1, str)); IGRAPH_EIT_NEXT(it); } igraph_strvector_destroy(&values); IGRAPH_FINALLY_CLEAN(1); + } else if (vertex_attr_type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Edges with boolean attributes */ + igraph_vector_bool_t values; + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&values, no_of_edges); + IGRAPH_CHECK(igraph_i_attribute_get_bool_edge_attr( + graph, vertex_attr_name, igraph_ess_all(IGRAPH_EDGEORDER_ID), &values)); + + while (!IGRAPH_EIT_END(it)) { + igraph_integer_t eid = IGRAPH_EIT_GET(it); + igraph_edge(graph, eid, &from, &to); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); + if (rev == IGRAPH_EIT_GET(it)) { + rev = -1; + } + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{%s}|\n", + from + 1, to + 1, + rev + 1, + VECTOR(values)[eid] ? "true" : "false")); + IGRAPH_EIT_NEXT(it); + } + + igraph_vector_bool_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); + igraph_get_eid(graph, &rev, to, from, IGRAPH_DIRECTED, false); 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)); + CHECK(fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " %" IGRAPH_PRId " |{}|\n", + from + 1, to + 1, + rev + 1)); IGRAPH_EIT_NEXT(it); } } @@ -252,7 +304,7 @@ int igraph_write_graph_leda(const igraph_t *graph, FILE *outstream, igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } #undef CHECK diff --git a/src/vendor/cigraph/src/io/lgl-header.h b/src/vendor/cigraph/src/io/lgl-header.h index fdb8f587f00..73361d3f152 100644 --- a/src/vendor/cigraph/src/io/lgl-header.h +++ b/src/vendor/cigraph/src/io/lgl-header.h @@ -29,9 +29,10 @@ typedef struct { void *scanner; int eof; char errmsg[300]; - int has_weights; - igraph_vector_t *vector; + igraph_error_t igraph_errno; + igraph_bool_t has_weights; + igraph_vector_int_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; - int actvertex; + igraph_integer_t actvertex; } igraph_i_lgl_parsedata_t; diff --git a/src/vendor/cigraph/src/io/lgl-lexer.l b/src/vendor/cigraph/src/io/lgl-lexer.l index e1ed9acc07e..7dfaa6cf9d2 100644 --- a/src/vendor/cigraph/src/io/lgl-lexer.l +++ b/src/vendor/cigraph/src/io/lgl-lexer.l @@ -44,7 +44,6 @@ */ -#include "config.h" #include #include "io/lgl-header.h" @@ -70,6 +69,7 @@ %option reentrant %option bison-bridge %option bison-locations +%option yylineno alnum [^ \t\r\n\0#] diff --git a/src/vendor/cigraph/src/io/lgl-parser.y b/src/vendor/cigraph/src/io/lgl-parser.y index 88dd4aa567e..c10c369a9ee 100644 --- a/src/vendor/cigraph/src/io/lgl-parser.y +++ b/src/vendor/cigraph/src/io/lgl-parser.y @@ -44,23 +44,21 @@ */ -#include -#include - #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" -#include "config.h" -#include "core/math.h" #include "io/lgl-header.h" #include "io/parsers/lgl-parser.h" #include "io/parsers/lgl-lexer.h" +#include "io/parse_utils.h" #include "internal/hacks.h" +#include +#include + 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 %} @@ -76,17 +74,17 @@ igraph_real_t igraph_lgl_get_number(const char *str, long int len); %lex-param { void *scanner } %union { - long int edgenum; - double weightnum; + igraph_integer_t edgenum; + igraph_real_t weightnum; } %type edgeid %type weight -%token ALNUM -%token NEWLINE -%token HASH "#" -%token END 0 "end of file" /* friendly name for $end */ +%token ALNUM "alphanumeric" +%token NEWLINE "end of line" +%token HASH "#" +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %% @@ -103,26 +101,36 @@ 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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actvertex)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); + IGRAPH_YY_CHECK(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)); } ; +edgeid : ALNUM { + igraph_integer_t trie_id; + IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, + igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &trie_id + )); + $$ = trie_id; +}; + +weight : ALNUM { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_lgl_yyget_text(scanner), + igraph_lgl_yyget_leng(scanner), + &val)); + $$=val; +} ; %% @@ -134,13 +142,3 @@ int igraph_lgl_yyerror(YYLTYPE* locp, igraph_i_lgl_parsedata_t *context, 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/vendor/cigraph/src/io/lgl.c b/src/vendor/cigraph/src/io/lgl.c index 81f7dbee715..ff39e9465ac 100644 --- a/src/vendor/cigraph/src/io/lgl.c +++ b/src/vendor/cigraph/src/io/lgl.c @@ -27,7 +27,8 @@ #include "graph/attributes.h" -#include "lgl-header.h" +#include "io/lgl-header.h" +#include "io/parsers/lgl-parser.h" int igraph_lgl_yylex_init_extra (igraph_i_lgl_parsedata_t* user_defined, void* scanner); @@ -43,9 +44,8 @@ void igraph_lgl_yylex_destroy_wrapper (void *scanner ) { /** * \ingroup loadsave * \function igraph_read_graph_lgl - * \brief Reads a graph from an .lgl file + * \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 @@ -70,7 +70,7 @@ vertex3name [optionalWeight] \endverbatim * 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 + * \param names Logical value, if \c 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 @@ -101,12 +101,13 @@ vertex3name [optionalWeight] \endverbatim * * \example examples/simple/igraph_read_graph_lgl.c */ -int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, +igraph_error_t 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_vector_int_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_t 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; @@ -115,7 +116,7 @@ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, igraph_i_lgl_parsedata_t context; IGRAPH_VECTOR_INIT_FINALLY(&ws, 0); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&trie, names); context.has_weights = 0; @@ -123,32 +124,51 @@ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, context.weights = &ws; context.trie = ≜ context.eof = 0; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; igraph_lgl_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_lgl_yylex_destroy_wrapper, context.scanner); igraph_lgl_yyset_in(instream, context.scanner); - if (igraph_lgl_yyparse(&context)) { + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_lgl_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read LGL file", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_PARSEERROR); } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read LGL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading LGL file.", err); } - IGRAPH_CHECK(igraph_empty(graph, 0, directed)); - IGRAPH_FINALLY(igraph_destroy, graph); + /* Prepare attributes, if needed */ 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; + namerec.value = igraph_i_trie_borrow_keys(&trie); VECTOR(name)[0] = &namerec; } @@ -163,8 +183,10 @@ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - IGRAPH_CHECK(igraph_add_vertices(graph, (igraph_integer_t) - igraph_trie_size(&trie), pname)); + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); + IGRAPH_CHECK(igraph_add_vertices(graph, igraph_trie_size(&trie), pname)); IGRAPH_CHECK(igraph_add_edges(graph, &edges, pweight)); if (pweight) { @@ -176,20 +198,19 @@ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, IGRAPH_FINALLY_CLEAN(1); } igraph_trie_destroy(&trie); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_vector_destroy(&ws); igraph_lgl_yylex_destroy(context.scanner); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** * \ingroup loadsave * \function igraph_write_graph_lgl - * \brief Writes the graph to a file in .lgl format + * \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. * @@ -207,27 +228,25 @@ int igraph_read_graph_lgl(igraph_t *graph, FILE *instream, * \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. + * \param isolates Logical, if \c true isolated vertices are also written + * to the file. If \c 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). + * Time complexity: O(|E|), the number of edges if \p isolates is \c 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, +igraph_error_t 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_integer_t actvertex = -1; igraph_attribute_type_t nametype, weighttype; IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_FROM), @@ -237,51 +256,50 @@ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, /* 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"); + IGRAPH_WARNINGF("Names attribute '%s' does not exists.", names); + names = NULL; } 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; + IGRAPH_WARNINGF("Ignoring names attribute '%s', unknown attribute type.", names); + names = NULL; } } /* 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_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { + IGRAPH_WARNINGF("Weights attribute '%s' does not exists.", weights); + weights = NULL; } 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; + IGRAPH_WARNINGF("Ignoring weights attribute '%s', unknown attribute type.", weights); + weights = NULL; } } - if (names == 0 && weights == 0) { + if (names == NULL && weights == NULL) { /* 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); + ret = fprintf(outstream, "%" IGRAPH_PRId "\n", to); } else { actvertex = from; - ret = fprintf(outstream, "# %li\n%li\n", (long int)from, (long int)to); + ret = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId "\n", from, to); } if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } - } else if (weights == 0) { + } else if (weights == NULL) { /* No weights but use names */ igraph_strvector_t nvec; IGRAPH_CHECK(igraph_strvector_init(&nvec, igraph_vcount(graph))); @@ -293,24 +311,24 @@ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0; - char *str1, *str2; + const char *str1, *str2; igraph_edge(graph, edge, &from, &to); - igraph_strvector_get(&nvec, to, &str2); + str2 = igraph_strvector_get(&nvec, to); if (from == actvertex) { ret = fprintf(outstream, "%s\n", str2); } else { actvertex = from; - igraph_strvector_get(&nvec, from, &str1); + str1 = igraph_strvector_get(&nvec, from); ret = fprintf(outstream, "# %s\n%s\n", str1, str2); } if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } IGRAPH_FINALLY_CLEAN(1); - } else if (names == 0) { + } else if (names == NULL) { /* No names but weights */ igraph_vector_t wvec; IGRAPH_VECTOR_INIT_FINALLY(&wvec, igraph_ecount(graph)); @@ -323,15 +341,15 @@ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, int ret1, ret2, ret3; igraph_edge(graph, edge, &from, &to); if (from == actvertex) { - ret1 = fprintf(outstream, "%li ", (long)to); + ret1 = fprintf(outstream, "%" IGRAPH_PRId " ", to); } else { actvertex = from; - ret1 = fprintf(outstream, "# %li\n%li ", (long)from, (long)to); + ret1 = fprintf(outstream, "# %" IGRAPH_PRId "\n%" IGRAPH_PRId " ", from, to); } - ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); ret3 = fputc('\n', outstream); if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -354,23 +372,23 @@ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0, ret2; - char *str1, *str2; + const char *str1, *str2; igraph_edge(graph, edge, &from, &to); - igraph_strvector_get(&nvec, to, &str2); + str2 = igraph_strvector_get(&nvec, to); if (from == actvertex) { ret = fprintf(outstream, "%s ", str2); } else { actvertex = from; - igraph_strvector_get(&nvec, from, &str1); + str1 = igraph_strvector_get(&nvec, from); ret = fprintf(outstream, "# %s\n%s ", str1, str2); } if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } - ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); ret2 = fputc('\n', outstream); if (ret < 0 || ret2 == EOF) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -380,39 +398,36 @@ int igraph_write_graph_lgl(const igraph_t *graph, FILE *outstream, } if (isolates) { - long int nov = igraph_vcount(graph); - long int i; + igraph_integer_t nov = igraph_vcount(graph); + igraph_integer_t i; int ret = 0; - igraph_vector_t deg; + igraph_integer_t deg; igraph_strvector_t nvec; - char *str; + const 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); + IGRAPH_CHECK(igraph_degree_1(graph, °, i, IGRAPH_ALL, IGRAPH_LOOPS)); + if (deg == 0) { + if (names == NULL) { + ret = fprintf(outstream, "# %" IGRAPH_PRId "\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); + igraph_vss_1(i), &nvec)); + str = igraph_strvector_get(&nvec, 0); ret = fprintf(outstream, "# %s\n", str); } } if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Write LGL file failed.", IGRAPH_EFILE); } } igraph_strvector_destroy(&nvec); - igraph_vector_destroy(°); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); } igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/ncol-header.h b/src/vendor/cigraph/src/io/ncol-header.h index 2ed06f287b5..31e6c03f5b7 100644 --- a/src/vendor/cigraph/src/io/ncol-header.h +++ b/src/vendor/cigraph/src/io/ncol-header.h @@ -29,8 +29,9 @@ typedef struct { void *scanner; int eof; char errmsg[300]; - int has_weights; - igraph_vector_t *vector; + igraph_error_t igraph_errno; + igraph_bool_t has_weights; + igraph_vector_int_t *vector; igraph_vector_t *weights; igraph_trie_t *trie; } igraph_i_ncol_parsedata_t; diff --git a/src/vendor/cigraph/src/io/ncol-lexer.l b/src/vendor/cigraph/src/io/ncol-lexer.l index a2ed8d34f3f..fea437e3088 100644 --- a/src/vendor/cigraph/src/io/ncol-lexer.l +++ b/src/vendor/cigraph/src/io/ncol-lexer.l @@ -44,7 +44,6 @@ */ -#include "config.h" #include #include "io/ncol-header.h" @@ -70,6 +69,7 @@ %option reentrant %option bison-bridge %option bison-locations +%option yylineno alnum [^ \t\n\r\0] diff --git a/src/vendor/cigraph/src/io/ncol-parser.y b/src/vendor/cigraph/src/io/ncol-parser.y index efb32b9be22..4339bde5829 100644 --- a/src/vendor/cigraph/src/io/ncol-parser.y +++ b/src/vendor/cigraph/src/io/ncol-parser.y @@ -44,24 +44,22 @@ */ -#include -#include - #include "igraph_types.h" #include "igraph_memory.h" #include "igraph_error.h" -#include "config.h" -#include "core/math.h" #include "io/ncol-header.h" #include "io/parsers/ncol-parser.h" #include "io/parsers/ncol-lexer.h" +#include "io/parse_utils.h" #include "internal/hacks.h" +#include +#include + 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 %} @@ -77,16 +75,16 @@ igraph_real_t igraph_ncol_get_number(const char *str, long int len); %lex-param { void *scanner } %union { - long int edgenum; - double weightnum; + igraph_integer_t edgenum; + igraph_real_t weightnum; } %type edgeid %type weight -%token ALNUM -%token NEWLINE -%token END 0 "end of file" /* friendly name for $end */ +%token ALNUM "alphanumeric" +%token NEWLINE "end of line" +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %% @@ -97,25 +95,35 @@ input : /* empty */ ; 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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2)); + IGRAPH_YY_CHECK(igraph_vector_push_back(context->weights, 0.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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2)); + IGRAPH_YY_CHECK(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)); } ; +edgeid : ALNUM { + igraph_integer_t trie_id; + IGRAPH_YY_CHECK(igraph_trie_get_len(context->trie, + igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &trie_id + )); + $$ = trie_id; +}; + +weight : ALNUM { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_ncol_yyget_text(scanner), + igraph_ncol_yyget_leng(scanner), + &val)); + $$=val; +} ; %% @@ -127,14 +135,3 @@ int igraph_ncol_yyerror(YYLTYPE* locp, 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/vendor/cigraph/src/io/ncol.c b/src/vendor/cigraph/src/io/ncol.c index d0417c9febb..7bbe42a6f54 100644 --- a/src/vendor/cigraph/src/io/ncol.c +++ b/src/vendor/cigraph/src/io/ncol.c @@ -27,7 +27,8 @@ #include "graph/attributes.h" -#include "ncol-header.h" +#include "io/ncol-header.h" +#include "io/parsers/ncol-parser.h" int igraph_ncol_yylex_init_extra (igraph_i_ncol_parsedata_t* user_defined, void* scanner); @@ -68,12 +69,12 @@ void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { * \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 + * 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 + * \param names Logical value, if \c 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 @@ -102,39 +103,38 @@ void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { * * \sa \ref igraph_read_graph_lgl(), \ref igraph_write_graph_ncol() */ -int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, +igraph_error_t 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_vector_int_t edges; + igraph_vector_t ws; igraph_trie_t trie = IGRAPH_TRIE_NULL; igraph_integer_t no_of_nodes; - long int no_predefined = 0; + igraph_integer_t no_predefined = 0; igraph_vector_ptr_t name, weight; - igraph_vector_ptr_t *pname = 0, *pweight = 0; + igraph_vector_ptr_t *pname = NULL, *pweight = NULL; 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_VECTOR_INT_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; + igraph_integer_t i, id, n; + const 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); + key = igraph_strvector_get(predefnames, i); + IGRAPH_CHECK(igraph_trie_get(&trie, key, &id)); if (id != i) { - IGRAPH_WARNING("reading NCOL file, duplicate entry in predefnames"); + IGRAPH_WARNING("Reading NCOL file, duplicate entry in predefined names."); no_predefined--; } } @@ -145,39 +145,64 @@ int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, context.weights = &ws; context.trie = ≜ context.eof = 0; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; igraph_ncol_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_ncol_yylex_destroy_wrapper, context.scanner); igraph_ncol_yyset_in(instream, context.scanner); - if (igraph_ncol_yyparse(&context)) { + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_ncol_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read NCOL file", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_PARSEERROR); } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read NCOL file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading NCOL file.", err); } if (predefnames != 0 && igraph_trie_size(&trie) != no_predefined) { - IGRAPH_WARNING("unknown vertex/vertices found, predefnames extended"); + IGRAPH_WARNING("Unknown vertex/vertices found in NCOL file, predefined names extended."); } + /* Prepare attributes, if needed */ + 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; + namerec.value = igraph_i_trie_borrow_keys(&trie); 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; @@ -185,28 +210,33 @@ int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, VECTOR(weight)[0] = &weightrec; } - if (igraph_vector_empty(&edges)) { + if (igraph_vector_int_empty(&edges)) { no_of_nodes = 0; } else { - no_of_nodes = igraph_vector_max(&edges) + 1; + no_of_nodes = igraph_vector_int_max(&edges) + 1; } + /* Create graph */ + IGRAPH_CHECK(igraph_empty(graph, 0, directed)); + IGRAPH_FINALLY(igraph_destroy, graph); 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); + IGRAPH_FINALLY_CLEAN(1); } if (pweight) { igraph_vector_ptr_destroy(pweight); + IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&ws); igraph_trie_destroy(&trie); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_ncol_yylex_destroy(context.scanner); - IGRAPH_FINALLY_CLEAN(5); + IGRAPH_FINALLY_CLEAN(5); /* +1 for 'graph' */ - return 0; + return IGRAPH_SUCCESS; } /** @@ -245,42 +275,43 @@ int igraph_read_graph_ncol(igraph_t *graph, FILE *instream, * * \sa \ref igraph_read_graph_ncol(), \ref igraph_write_graph_lgl() */ -int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, +igraph_error_t 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), + IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &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"); + IGRAPH_WARNINGF("Names attribute '%s' does not exists.", names); + names = NULL; } 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; + IGRAPH_WARNINGF("Ignoring names attribute '%s', " + "attribute type is not a string.", names); + names = NULL; } } /* 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_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, weights)) { + IGRAPH_WARNINGF("Weights attribute '%s' does not exists.", weights); + weights = NULL; } 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; + IGRAPH_WARNINGF("Ignoring weights attribute '%s', " + "attribute type is not numeric.", weights); + weights = NULL; } } @@ -290,11 +321,9 @@ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, 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); + ret = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId "\n", from, to); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -310,13 +339,13 @@ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0; - char *str1, *str2; + const char *str1, *str2; igraph_edge(graph, edge, &from, &to); - igraph_strvector_get(&nvec, from, &str1); - igraph_strvector_get(&nvec, to, &str2); + str1 = igraph_strvector_get(&nvec, from); + str2 = igraph_strvector_get(&nvec, to); ret = fprintf(outstream, "%s %s\n", str1, str2); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -334,12 +363,11 @@ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, 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]); + ret1 = fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId " ", from, to); + ret2 = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); ret3 = fputc('\n', outstream); if (ret1 < 0 || ret2 < 0 || ret3 == EOF) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -362,18 +390,18 @@ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_integer_t edge = IGRAPH_EIT_GET(it); igraph_integer_t from, to; int ret = 0, ret2 = 0; - char *str1, *str2; + const char *str1, *str2; igraph_edge(graph, edge, &from, &to); - igraph_strvector_get(&nvec, from, &str1); - igraph_strvector_get(&nvec, to, &str2); + str1 = igraph_strvector_get(&nvec, from); + str2 = igraph_strvector_get(&nvec, to); ret = fprintf(outstream, "%s %s ", str1, str2); if (ret < 0) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); } - ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[(long int)edge]); + ret = igraph_real_fprintf_precise(outstream, VECTOR(wvec)[edge]); ret2 = fputc('\n', outstream); if (ret < 0 || ret2 == EOF) { - IGRAPH_ERROR("Write failed", IGRAPH_EFILE); + IGRAPH_ERROR("Writing NCOL file failed.", IGRAPH_EFILE); } IGRAPH_EIT_NEXT(it); } @@ -384,5 +412,5 @@ int igraph_write_graph_ncol(const igraph_t *graph, FILE *outstream, igraph_eit_destroy(&it); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/pajek-header.h b/src/vendor/cigraph/src/io/pajek-header.h index 92caae45dab..03b7765420a 100644 --- a/src/vendor/cigraph/src/io/pajek-header.h +++ b/src/vendor/cigraph/src/io/pajek-header.h @@ -26,21 +26,35 @@ #include "core/trie.h" +/* According to Pajek's author, limits of the Pajek program as of 2022-1-1 are: + * "At the moment regular Pajek has limit one billion vertices, + * PajekXXL two billions, while Pajek 3XL ten billions." + * Hard-coding the limit INT32_MAX is safe when compiling wiht 32-bit integers, + * and likely sufficient for practical applications. + */ +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION +/* Limit maximum vertex count when using a fuzzer, to avoid out-of-memory failure. */ +#define IGRAPH_PAJEK_MAX_VERTEX_COUNT (1 << 20) +#else +#define IGRAPH_PAJEK_MAX_VERTEX_COUNT INT32_MAX +#endif + typedef struct { void *scanner; int eof; char errmsg[300]; - igraph_vector_t *vector; + igraph_error_t igraph_errno; + igraph_vector_int_t *vector; igraph_bool_t directed; - int vcount, vcount2; - int actfrom; - int actto; + igraph_integer_t vcount, vcount2; + igraph_integer_t actfrom; + igraph_integer_t 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_integer_t vertexid; + igraph_integer_t actvertex; + igraph_integer_t actedge; } igraph_i_pajek_parsedata_t; diff --git a/src/vendor/cigraph/src/io/pajek-lexer.l b/src/vendor/cigraph/src/io/pajek-lexer.l index 4ba29dcf46b..7e533e58b88 100644 --- a/src/vendor/cigraph/src/io/pajek-lexer.l +++ b/src/vendor/cigraph/src/io/pajek-lexer.l @@ -44,7 +44,6 @@ */ -#include "config.h" #include #include "io/pajek-header.h" @@ -70,66 +69,72 @@ %option reentrant %option bison-bridge %option bison-locations +%option yylineno +%option caseless digit [0-9] -word [^ \t\r\n\0] +word [^ \t\v\f\r\n\0] +whitespace [ \t\v\f] + +%x netline %% -[ \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}+)? { +<*>{whitespace}+ { } +%[^\n\r\0]*[\n\r]* { } /* comments */ +\*net { BEGIN(netline); return NETWORKLINE; } +\*network { BEGIN(netline); return NETWORKLINE; } +{whitespace}({word}|{whitespace})* { + return NET_TITLE; } +\*vertices { return VERTICESLINE; } +\*arcs { return ARCSLINE; } +\*edges { return EDGESLINE; } +\*arcslist { return ARCSLISTLINE; } +\*edgeslist { return EDGESLISTLINE; } +\*matrix { return MATRIXLINE; } +<*>[\n\r]+ { BEGIN(INITIAL); yyextra->mode=0; return NEWLINE; } /* skip over multiple newlines */ +\"[^\"\0]*\" { return QSTR; } +\([^\)\0]*\) { 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 +x_fact/[ \t\n\r] { if (yyextra->mode==1) { return VP_X_FACT; } else { return ALNUM; } } +y_fact/[ \t\n\r] { if (yyextra->mode==1) { return VP_Y_FACT; } else { return ALNUM; } } +ic/[ \t\n\r] { if (yyextra->mode==1) { return VP_IC; } else { return ALNUM; } } +bc/[ \t\n\r] { if (yyextra->mode==1) { return VP_BC; } else { return ALNUM; } } +bw/[ \t\n\r] { if (yyextra->mode==1) { return VP_BW; } else { return ALNUM; } } +phi/[ \t\n\r] { if (yyextra->mode==1) { return VP_PHI; } else { return ALNUM; } } +r/[ \t\n\r] { if (yyextra->mode==1) { return VP_R; } else { return ALNUM; } } +q/[ \t\n\r] { if (yyextra->mode==1) { return VP_Q; } else { return ALNUM; } } +font/[ \t\n\r] { if (yyextra->mode==1) { return VP_FONT; } else { return ALNUM; } } +url/[ \t\n\r] { if (yyextra->mode==1) { return VP_URL; } else { return ALNUM; } } + +c/[ \t\n\r] { if (yyextra->mode==2) { return EP_C; } else { return ALNUM; } } +p/[ \t\n\r] { if (yyextra->mode==2) { return EP_P; } else { return ALNUM; } } +s/[ \t\n\r] { if (yyextra->mode==2) { return EP_S; } else { return ALNUM; } } +a/[ \t\n\r] { if (yyextra->mode==2) { return EP_A; } else { return ALNUM; } } +w/[ \t\n\r] { if (yyextra->mode==2) { return EP_W; } else { return ALNUM; } } +h1/[ \t\n\r] { if (yyextra->mode==2) { return EP_H1; } else { return ALNUM; } } +h2/[ \t\n\r] { if (yyextra->mode==2) { return EP_H2; } else { return ALNUM; } } +a1/[ \t\n\r] { if (yyextra->mode==2) { return EP_A1; } else { return ALNUM; } } +a2/[ \t\n\r] { if (yyextra->mode==2) { return EP_A2; } else { return ALNUM; } } +k1/[ \t\n\r] { if (yyextra->mode==2) { return EP_K1; } else { return ALNUM; } } +k2/[ \t\n\r] { if (yyextra->mode==2) { return EP_K2; } else { return ALNUM; } } +ap/[ \t\n\r] { if (yyextra->mode==2) { return EP_AP; } else { return ALNUM; } } +l/[ \t\n\r] { if (yyextra->mode==2) { return EP_L; } else { return ALNUM; } } +lp/[ \t\n\r] { if (yyextra->mode==2) { return EP_LP; } else { return ALNUM; } } + +lphi/[ \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 +lc/[ \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 +lr/[ \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 +la/[ \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 +size/[ \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 +fos/[ \t\n\r] { if (yyextra->mode==1) { return VP_FOS; } else if (yyextra->mode==2) { return EP_FOS; } else { return ALNUM; } } {word}+ { return ALNUM; } @@ -142,6 +147,6 @@ word [^ \t\r\n\0] } } -. { return ERROR; } +<*>. { return ERROR; } %% diff --git a/src/vendor/cigraph/src/io/pajek-parser.y b/src/vendor/cigraph/src/io/pajek-parser.y index 7069a77274e..78201314145 100644 --- a/src/vendor/cigraph/src/io/pajek-parser.y +++ b/src/vendor/cigraph/src/io/pajek-parser.y @@ -44,59 +44,55 @@ */ -#include -#include -#include - -#include "igraph_types.h" -#include "igraph_memory.h" -#include "igraph_error.h" #include "igraph_attributes.h" -#include "config.h" +#include "igraph_error.h" +#include "igraph_memory.h" +#include "igraph_types.h" -#include "core/math.h" #include "io/pajek-header.h" #include "io/parsers/pajek-parser.h" /* it must come first because of YYSTYPE */ #include "io/parsers/pajek-lexer.h" -#include "internal/hacks.h" +#include "io/parse_utils.h" +#include "internal/hacks.h" /* strdup */ + +#include +#include +#include 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, +static igraph_error_t igraph_i_pajek_add_string_vertex_attribute(const char *name, const char *value, - int len, + size_t len, igraph_i_pajek_parsedata_t *context); -int igraph_i_pajek_add_string_edge_attribute(const char *name, +static igraph_error_t igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, - int len, + size_t len, igraph_i_pajek_parsedata_t *context); -int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, +static igraph_error_t 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, +static igraph_error_t 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, +static igraph_error_t igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - long int count, + igraph_integer_t count, const char *attrname, igraph_integer_t vid, igraph_real_t number); -int igraph_i_pajek_add_string_attribute(igraph_trie_t *names, +static igraph_error_t igraph_i_pajek_add_string_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - long int count, + igraph_integer_t 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); + const char *str, + igraph_integer_t str_len); -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; +static igraph_error_t igraph_i_pajek_add_bipartite_type(igraph_i_pajek_parsedata_t *context); +static igraph_error_t igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context); #define scanner context->scanner @@ -113,11 +109,11 @@ extern long int igraph_i_pajek_actedge; %lex-param { void *scanner } %union { - long int intnum; - double realnum; + igraph_integer_t intnum; + igraph_real_t realnum; struct { char *str; - int len; + size_t len; } string; } @@ -132,19 +128,20 @@ extern long int igraph_i_pajek_actedge; %type epwordpar; %type vertex; -%token NEWLINE -%token NUM +%token NEWLINE "end of line" +%token NUM "number" %token ALNUM %token QSTR %token PSTR -%token NETWORKLINE -%token VERTICESLINE -%token ARCSLINE -%token EDGESLINE -%token ARCSLISTLINE -%token EDGESLISTLINE -%token MATRIXLINE -%token END 0 "end of file" /* friendly name for $end */ +%token NETWORKLINE "*network line" +%token NET_TITLE +%token VERTICESLINE "*vertices line" +%token ARCSLINE "*arcs line" +%token EDGESLINE "*edges line" +%token ARCSLISTLINE "*arcslist line" +%token EDGESLISTLINE "*edgeslist line" +%token MATRIXLINE "*matrix line" +%token END 0 "end of file" /* friendly name for $end */ %token ERROR %token VP_X_FACT @@ -191,47 +188,73 @@ input: nethead vertices edgeblock { if (context->vcount2 > 0) { igraph_i_pajek_check_bipartite(context); } }; -nethead: /* empty */ | NETWORKLINE words NEWLINE; +nethead: /* empty */ | NETWORKLINE NEWLINE | NETWORKLINE NET_TITLE NEWLINE ; vertices: verticeshead NEWLINE vertdefs; verticeshead: VERTICESLINE longint { context->vcount=$2; context->vcount2=0; + if (context->vcount < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } } | VERTICESLINE longint longint { context->vcount=$2; context->vcount2=$3; - igraph_i_pajek_add_bipartite_type(context); + if (context->vcount < 0) { + IGRAPH_YY_ERRORF("Invalid vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("Vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount); + } + if (context->vcount2 < 0) { + IGRAPH_YY_ERRORF("Invalid two-mode vertex count in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); + } + if (context->vcount2 > IGRAPH_PAJEK_MAX_VERTEX_COUNT) { + IGRAPH_YY_ERRORF("2-mode vertex count too large in Pajek file (%" IGRAPH_PRId ").", IGRAPH_EINVAL, context->vcount2); + } + IGRAPH_YY_CHECK(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 { + context->actvertex=$1; + if (context->actvertex < 1 || context->actvertex > context->vcount) { + IGRAPH_YY_ERRORF( + "Invalid vertex id (%" IGRAPH_PRId ") in Pajek file. " + "The number of vertices is %" IGRAPH_PRId ".", + IGRAPH_EINVAL, context->actvertex, context->vcount); + } + } 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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("id", $1.str, $1.len, context)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("x", $1, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("y", $2, context)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("shape", $1.str, $1.len, context)); }; params: /* empty */ | params param; @@ -239,76 +262,76 @@ params: /* empty */ | params param; param: vpword | VP_X_FACT number { - igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("xfact", $2, context)); } | VP_Y_FACT number { - igraph_i_pajek_add_numeric_vertex_attribute("yfact", $2, context); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("color-red", $2, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("color-green", $3, context)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framecolor-red", $2, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framecolor-green", $3, context)); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-red", $2, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-green", $3, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labelcolor-blue", $4, context)); } | VP_LR number { - igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldist", $2, context)); } | VP_LPHI number { - igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldegree2", $2, context)); } | VP_BW number { - igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("framewidth", $2, context)); } | VP_FOS number { - igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("fontsize", $2, context)); } | VP_PHI number { - igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("rotation", $2, context)); } | VP_R number { - igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("radius", $2, context)); } | VP_Q number { - igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("diamondratio", $2, context)); } | VP_LA number { - igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_vertex_attribute("labeldegree", $2, context)); } | VP_SIZE number { - igraph_i_pajek_add_numeric_vertex_attribute("vertexsize", $2, context); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_string_vertex_attribute("labelcolor", + $3.str, $3.len, context)); } ; @@ -324,8 +347,14 @@ 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); } + if ($1 < 1) { + IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $1); + } + if ($2 < 1) { + IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $2); + } + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2-1)); } ; arcfrom: longint; @@ -340,8 +369,14 @@ 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); } + if ($1 < 1) { + IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $1); + } + if ($2 < 1) { + IGRAPH_YY_ERRORF("Non-positive vertex ID (%" IGRAPH_PRId ") while reading Pajek file.", IGRAPH_EINVAL, $2); + } + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, $2-1)); } ; edgefrom: longint; @@ -349,7 +384,7 @@ edgefrom: longint; edgeto: longint; weight: /* empty */ | number { - igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); }; edgeparams: /* empty */ | edgeparams edgeparam; @@ -357,76 +392,76 @@ 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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-red", $2, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-green", $3, context)); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("color-blue", $4, context)); } | EP_S number { - igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context)); } | EP_W number { - igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("edgewidth", $2, context)); } | EP_H1 number { - igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("hook1", $2, context)); } | EP_H2 number { - igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("hook2", $2, context)); } | EP_A1 number { - igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("angle1", $2, context)); } | EP_A2 number { - igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("angle2", $2, context)); } | EP_K1 number { - igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("velocity1", $2, context)); } | EP_K2 number { - igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("velocity2", $2, context)); } | EP_AP number { - igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowpos", $2, context)); } | EP_LP number { - igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelpos", $2, context)); } | EP_LR number { - igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelangle", $2, context)); } | EP_LPHI number { - igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("labelangle2", $2, context)); } | EP_LA number { - igraph_i_pajek_add_numeric_edge_attribute("labeldegree", $2, context); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("arrowsize", $2, context)); } | EP_FOS number { - igraph_i_pajek_add_numeric_edge_attribute("fontsize", $2, context); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_string_edge_attribute("color", $3.str, $3.len, context)); } ; @@ -443,8 +478,8 @@ 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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, labs($1)-1)); }; edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; @@ -458,8 +493,8 @@ 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); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, labs($1)-1)); }; /* -----------------------------------------------------*/ @@ -481,15 +516,15 @@ 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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_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); + IGRAPH_YY_CHECK(igraph_i_pajek_add_numeric_edge_attribute("weight", $1, context)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, context->actfrom)); + IGRAPH_YY_CHECK(igraph_vector_int_push_back(context->vector, + context->vcount2+context->actto)); } } context->actto++; @@ -497,13 +532,21 @@ adjmatrixentry: number { /* -----------------------------------------------------*/ -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)); }; +longint: NUM { + igraph_integer_t val; + IGRAPH_YY_CHECK(igraph_i_parse_integer(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner), + &val)); + $$=val; +}; -words: /* empty */ | words word; +number: NUM { + igraph_real_t val; + IGRAPH_YY_CHECK(igraph_i_parse_real(igraph_pajek_yyget_text(scanner), + igraph_pajek_yyget_leng(scanner), + &val)); + $$=val; +}; word: ALNUM { $$.str=igraph_pajek_yyget_text(scanner); $$.len=igraph_pajek_yyget_leng(scanner); } @@ -523,208 +566,197 @@ int igraph_pajek_yyerror(YYLTYPE* locp, 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, +static igraph_error_t igraph_i_pajek_add_numeric_attribute(igraph_trie_t *names, igraph_vector_ptr_t *attrs, - long int count, + igraph_integer_t count, const char *attrname, igraph_integer_t vid, igraph_real_t number) { - long int attrsize=igraph_trie_size(names); - long int id; + igraph_integer_t attrsize = igraph_trie_size(names); + igraph_integer_t id; igraph_vector_t *na; igraph_attribute_record_t *rec; - igraph_trie_get(names, attrname, &id); + IGRAPH_CHECK(igraph_trie_get(names, attrname, &id)); if (id == attrsize) { /* add a new attribute */ rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); + if (! rec) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); na=IGRAPH_CALLOC(1, igraph_vector_t); - igraph_vector_init(na, count); + if (! na) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, na); + IGRAPH_VECTOR_INIT_FINALLY(na, count); rec->name=strdup(attrname); + if (! rec->name) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (void *) rec->name); rec->type=IGRAPH_ATTRIBUTE_NUMERIC; rec->value=na; - igraph_vector_ptr_push_back(attrs, rec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ } 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)); + igraph_integer_t origsize=igraph_vector_size(na); + IGRAPH_CHECK(igraph_vector_resize(na, vid+1)); for (;origsizename=strdup(attrname); + if (! rec->name) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char *) rec->name); rec->type=IGRAPH_ATTRIBUTE_STRING; rec->value=na; - igraph_vector_ptr_push_back(attrs, rec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of rec transferred to attrs */ } 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; + return igraph_i_pajek_add_string_attribute(context->vertex_attribute_names, + context->vertex_attributes, + context->vcount, + name, context->actvertex-1, + value, len); } -int igraph_i_pajek_add_string_edge_attribute(const char *name, +static igraph_error_t igraph_i_pajek_add_string_edge_attribute(const char *name, const char *value, - int len, + size_t 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; + return igraph_i_pajek_add_string_attribute(context->edge_attribute_names, + context->edge_attributes, + context->actedge, + name, context->actedge-1, + value, len); } -int igraph_i_pajek_add_numeric_vertex_attribute(const char *name, +static igraph_error_t 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); + 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, +static igraph_error_t 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); + 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) { +static igraph_error_t 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_integer_t i, n=context->vcount, n1=context->vcount2; + igraph_integer_t attrid, attrsize = igraph_trie_size(names); igraph_attribute_record_t *rec; - igraph_vector_t *na; + igraph_vector_bool_t *na; if (n1 > n) { - IGRAPH_ERROR("Invalid number of vertices in bipartite Pajek file", + 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); - } + IGRAPH_CHECK(igraph_trie_get(names, attrname, &attrid)); + /* It should not be possible for the "type" attribute to be already + * present at this point. */ + IGRAPH_ASSERT(attrid == attrsize); /* add a new attribute */ rec=IGRAPH_CALLOC(1, igraph_attribute_record_t); - na=IGRAPH_CALLOC(1, igraph_vector_t); - igraph_vector_init(na, n); + if (! rec) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, rec); + na=IGRAPH_CALLOC(1, igraph_vector_bool_t); + if (! na) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, na); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(na, n); rec->name=strdup(attrname); - rec->type=IGRAPH_ATTRIBUTE_NUMERIC; + if (! rec->name) { + IGRAPH_ERROR("Out of memory while parsing Pajek file.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, (char *) rec->name); + rec->type=IGRAPH_ATTRIBUTE_BOOLEAN; rec->value=na; - igraph_vector_ptr_push_back(attrs, rec); + IGRAPH_CHECK(igraph_vector_ptr_push_back(attrs, rec)); + IGRAPH_FINALLY_CLEAN(4); /* ownership of 'rec' transferred to 'attrs' */ for (i=0; ivector; - int i, n1=context->vcount2; - int ne=igraph_vector_size(edges); +static igraph_error_t igraph_i_pajek_check_bipartite(igraph_i_pajek_parsedata_t *context) { + const igraph_vector_int_t *edges=context->vector; + igraph_integer_t i, n1=context->vcount2; + igraph_integer_t ne=igraph_vector_int_size(edges); for (i=0; i n1 && v2 > n1) ) { - IGRAPH_WARNING("Invalid edge in bipartite graph"); + IGRAPH_WARNING("Invalid edge in bipartite graph."); } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/pajek.c b/src/vendor/cigraph/src/io/pajek.c index 9667a63ba83..660676f614d 100644 --- a/src/vendor/cigraph/src/io/pajek.c +++ b/src/vendor/cigraph/src/io/pajek.c @@ -29,7 +29,8 @@ #include "graph/attributes.h" -#include "pajek-header.h" +#include "io/pajek-header.h" +#include "io/parsers/pajek-parser.h" #include #include @@ -45,9 +46,35 @@ void igraph_pajek_yylex_destroy_wrapper (void *scanner ) { (void) igraph_pajek_yylex_destroy(scanner); } +void igraph_i_pajek_destroy_attr_vector(igraph_vector_ptr_t *attrs) { + const igraph_integer_t attr_count = igraph_vector_ptr_size(attrs); + for (igraph_integer_t i = 0; i < attr_count; i++) { + igraph_attribute_record_t *rec = VECTOR(*attrs)[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_BOOLEAN) { + igraph_vector_bool_t *vec = (igraph_vector_bool_t*) rec->value; + igraph_vector_bool_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); + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); + } + IGRAPH_FREE(rec->name); + IGRAPH_FREE(rec); + } + igraph_vector_ptr_destroy(attrs); +} + /** * \function igraph_read_graph_pajek - * \brief Reads a file in Pajek format + * \brief Reads a file in Pajek format. * * \param graph Pointer to an uninitialized graph object. * \param file An already opened file handler. @@ -111,7 +138,7 @@ void igraph_pajek_yylex_destroy_wrapper (void *scanner ) { * * * 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 + * 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 @@ -135,23 +162,27 @@ void igraph_pajek_yylex_destroy_wrapper (void *scanner ) { * \example examples/simple/foreign.c */ -int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { +igraph_error_t igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { - igraph_vector_t edges; + igraph_vector_int_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_integer_t i, j; igraph_i_pajek_parsedata_t context; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); IGRAPH_TRIE_INIT_FINALLY(&vattrnames, 1); - IGRAPH_VECTOR_PTR_INIT_FINALLY(&vattrs, 0); + IGRAPH_CHECK(igraph_vector_ptr_init(&vattrs, 0)); + IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &vattrs); + IGRAPH_TRIE_INIT_FINALLY(&eattrnames, 1); - IGRAPH_VECTOR_PTR_INIT_FINALLY(&eattrs, 0); + IGRAPH_CHECK(igraph_vector_ptr_init(&eattrs, 0)); + IGRAPH_FINALLY(igraph_i_pajek_destroy_attr_vector, &eattrs); + context.directed = false; /* assume undirected until an element implying directedness is encountered */ context.vector = &edges; context.mode = 0; context.vcount = -1; @@ -162,90 +193,87 @@ int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { context.edge_attributes = &eattrs; context.actedge = 0; context.eof = 0; + context.errmsg[0] = '\0'; + context.igraph_errno = IGRAPH_SUCCESS; igraph_pajek_yylex_init_extra(&context, &context.scanner); IGRAPH_FINALLY(igraph_pajek_yylex_destroy_wrapper, context.scanner); igraph_pajek_yyset_in(instream, context.scanner); - if (igraph_pajek_yyparse(&context)) { + /* Use ENTER/EXIT to avoid destroying context.scanner before this function returns */ + IGRAPH_FINALLY_ENTER(); + int err = igraph_pajek_yyparse(&context); + IGRAPH_FINALLY_EXIT(); + switch (err) { + case 0: /* success */ + break; + case 1: /* parse error */ if (context.errmsg[0] != 0) { IGRAPH_ERROR(context.errmsg, IGRAPH_PARSEERROR); + } else if (context.igraph_errno != IGRAPH_SUCCESS) { + IGRAPH_ERROR("", context.igraph_errno); } else { - IGRAPH_ERROR("Cannot read Pajek file", IGRAPH_PARSEERROR); + IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_PARSEERROR); } + break; + case 2: /* out of memory */ + IGRAPH_ERROR("Cannot read Pajek file.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + default: /* must never reach here */ + /* Hint: This will usually be triggered if an IGRAPH_CHECK() is used in a Bison + * action instead of an IGRAPH_YY_CHECK(), resulting in an igraph errno being + * returned in place of a Bison error code. + * TODO: What if future Bison versions introduce error codes other than 0, 1 and 2? + */ + IGRAPH_FATALF("Parser returned unexpected error code (%d) when reading Pajek file.", err); } - 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++) { + /* Prepare attributes */ + const igraph_integer_t eattr_count = igraph_vector_ptr_size(&eattrs); + for (i = 0; i < eattr_count; 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); + igraph_integer_t origsize = igraph_vector_size(vec); + IGRAPH_CHECK(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); + } else if (rec->type == IGRAPH_ATTRIBUTE_BOOLEAN) { + /* Boolean attributes are not currently added by the parser. + * This section is here for future-proofing. */ + igraph_vector_bool_t *vec = (igraph_vector_bool_t*)rec->value; + igraph_integer_t origsize = igraph_vector_bool_size(vec); + IGRAPH_CHECK(igraph_vector_bool_resize(vec, context.actedge)); for (j = origsize; j < context.actedge; j++) { - igraph_strvector_set(strvec, j, ""); + VECTOR(*vec)[j] = 0; } + } else if (rec->type == IGRAPH_ATTRIBUTE_STRING) { + igraph_strvector_t *strvec = (igraph_strvector_t*)rec->value; + /* strvector_resize() adds empty strings */ + IGRAPH_CHECK(igraph_strvector_resize(strvec, context.actedge)); + } else { + /* Must never reach here */ + IGRAPH_FATAL("Unknown attribute type encountered."); } } + /* Create graph */ 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_vector_int_destroy(&edges); + igraph_i_pajek_destroy_attr_vector(&eattrs); igraph_trie_destroy(&eattrnames); - igraph_vector_ptr_destroy(&vattrs); + igraph_i_pajek_destroy_attr_vector(&vattrs); igraph_trie_destroy(&vattrnames); igraph_pajek_yylex_destroy(context.scanner); + IGRAPH_FINALLY_CLEAN(7); /* +1 for 'graph' */ - IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } /* Order matters here! */ @@ -306,12 +334,13 @@ int igraph_read_graph_pajek(igraph_t *graph, FILE *instream) { #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; +static igraph_error_t igraph_i_pajek_escape(const char* src, char** dest) { + igraph_integer_t destlen = 0; + igraph_bool_t need_escape = false; /* Determine whether the string contains characters to be escaped */ - char *s, *d; + const char *s; + char *d; for (s = src; *s; s++, destlen++) { if (*s == '\\') { need_escape = 1; @@ -333,7 +362,7 @@ static int igraph_i_pajek_escape(char* src, char** dest) { */ *dest = IGRAPH_CALLOC(destlen + 3, char); if (!*dest) { - IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } d = *dest; @@ -345,7 +374,7 @@ static int igraph_i_pajek_escape(char* src, char** dest) { *dest = IGRAPH_CALLOC(destlen + 3, char); if (!*dest) { - IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); + IGRAPH_ERROR("Not enough memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } d = *dest; @@ -418,12 +447,12 @@ static int igraph_i_pajek_escape(char* src, char** dest) { * \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_error_t igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j; igraph_attribute_type_t vtypes[V_LAST], etypes[E_LAST]; - igraph_bool_t write_vertex_attrs = 0; + igraph_bool_t write_vertex_attrs = false; /* Same order as the #define's */ const char *vnames[] = { "id", "x", "y", "z", "shape", "xfact", "yfact", @@ -480,35 +509,33 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { 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; + igraph_vector_int_t ex_numa; + igraph_vector_int_t ex_stra; + igraph_vector_int_t vx_numa; + igraph_vector_int_t vx_stra; - char *s, *escaped; + const char *s; + char *escaped; - igraph_bool_t bipartite = 0; + igraph_bool_t bipartite = false; igraph_vector_int_t bip_index, bip_index2; igraph_vector_bool_t bvec; - long int notop = 0, nobottom = 0; - - IGRAPH_UNUSED(notop); + igraph_integer_t 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_numa, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ex_stra, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vx_numa, 0); + IGRAPH_VECTOR_INT_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"); + IGRAPH_CHECK(igraph_i_attribute_gettype(graph, &type_type, IGRAPH_ATTRIBUTE_VERTEX, "type")); if (type_type == IGRAPH_ATTRIBUTE_BOOLEAN) { - int bptr = 0, tptr = 0; + igraph_integer_t 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 */ @@ -520,22 +547,22 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { 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)); + "type", igraph_vss_1(i), &bvec)); if (VECTOR(bvec)[0]) { notop++; } else { nobottom++; } } - for (i = 0, bptr = 0, tptr = (int) nobottom; i < no_of_nodes; i++) { + for (i = 0, bptr = 0, tptr = 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)); + "type", igraph_vss_1(i), &bvec)); if (VECTOR(bvec)[0]) { - VECTOR(bip_index)[tptr] = (int) i; + VECTOR(bip_index)[tptr] = i; VECTOR(bip_index2)[i] = tptr; tptr++; } else { - VECTOR(bip_index)[bptr] = (int) i; + VECTOR(bip_index)[bptr] = i; VECTOR(bip_index2)[i] = bptr; bptr++; } @@ -547,12 +574,12 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { /* Write header */ if (bipartite) { - if (fprintf(outstream, "*Vertices %li %li%s", no_of_nodes, nobottom, + if (fprintf(outstream, "*Vertices %" IGRAPH_PRId " %" IGRAPH_PRId "%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) { + if (fprintf(outstream, "*Vertices %" IGRAPH_PRId "%s", no_of_nodes, newline) < 0) { IGRAPH_ERROR("Cannot write pajek file", IGRAPH_EFILE); } } @@ -560,34 +587,31 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { /* 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]); + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vnames[i])) { + IGRAPH_CHECK(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++) { + for (i = 0; i < (igraph_integer_t) (sizeof(vnumnames) / sizeof(vnumnames[0])); 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 (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vnumnames[i])) { + IGRAPH_CHECK(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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vx_numa, i)); } } } - for (i = 0; i < (long int) (sizeof(vstrnames) / sizeof(const char*)); i++) { + for (i = 0; i < (igraph_integer_t) (sizeof(vstrnames) / sizeof(vstrnames[0])); 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 (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_VERTEX, vstrnames[i])) { + IGRAPH_CHECK(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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vx_stra, i)); } } } @@ -595,41 +619,41 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { /* Write vertices */ if (write_vertex_attrs) { for (i = 0; i < no_of_nodes; i++) { - long int id = bipartite ? VECTOR(bip_index)[i] : i; + igraph_integer_t id = bipartite ? VECTOR(bip_index)[i] : i; - /* vertex id */ - fprintf(outstream, "%li", i + 1); + /* vertex ID */ + fprintf(outstream, "%" IGRAPH_PRId, 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); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_ID], igraph_vss_1(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_attribute_get_string_vertex_attr( + graph, vnames[V_ID], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); fprintf(outstream, " %s", escaped); IGRAPH_FREE(escaped); } else { - fprintf(outstream, " \"%li\"", id + 1); + fprintf(outstream, " \"%" IGRAPH_PRId "\"", 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); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_X], igraph_vss_1(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); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnames[V_Y], igraph_vss_1(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); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, vnames[V_Z], + igraph_vss_1(id), &numv)); fputc(' ', outstream); igraph_real_fprintf_precise(outstream, VECTOR(numv)[0]); } @@ -637,29 +661,29 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { /* 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_attribute_get_string_vertex_attr( + graph, vnames[V_SHAPE], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); 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); + for (j = 0; j < igraph_vector_int_size(&vx_numa); j++) { + igraph_integer_t idx = VECTOR(vx_numa)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr( + graph, vnumnames[idx], igraph_vss_1(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); + for (j = 0; j < igraph_vector_int_size(&vx_stra); j++) { + igraph_integer_t idx = VECTOR(vx_stra)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_string_vertex_attr( + graph, vstrnames[idx], igraph_vss_1(id), &strv)); + s = igraph_strvector_get(&strv, 0); IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); fprintf(outstream, " %s %s", vstrnames2[idx], escaped); IGRAPH_FREE(escaped); @@ -684,70 +708,67 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { /* 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]); + if (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, enames[i])) { + IGRAPH_CHECK(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++) { + for (i = 0; i < (igraph_integer_t) (sizeof(enumnames) / sizeof(enumnames[0])); 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 (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, enumnames[i])) { + IGRAPH_CHECK(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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&ex_numa, i)); } } } - for (i = 0; i < (long int) (sizeof(estrnames) / sizeof(const char*)); i++) { + for (i = 0; i < (igraph_integer_t) (sizeof(estrnames) / sizeof(estrnames[0])); 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 (igraph_i_attribute_has_attr(graph, IGRAPH_ATTRIBUTE_EDGE, estrnames[i])) { + IGRAPH_CHECK(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)); + IGRAPH_CHECK(igraph_vector_int_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 edge = IGRAPH_EIT_GET(eit); igraph_integer_t from, to; - igraph_edge(graph, (igraph_integer_t) edge, &from, &to); + igraph_edge(graph, 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); + fprintf(outstream, "%" IGRAPH_PRId " %" IGRAPH_PRId , from + 1, 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); + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( + graph, enames[E_WEIGHT], igraph_ess_1(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); + for (j = 0; j < igraph_vector_int_size(&ex_numa); j++) { + igraph_integer_t idx = VECTOR(ex_numa)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr( + graph, enumnames[idx], igraph_ess_1(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); + for (j = 0; j < igraph_vector_int_size(&ex_stra); j++) { + igraph_integer_t idx = VECTOR(ex_stra)[j]; + IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr( + graph, estrnames[idx], igraph_ess_1(edge), &strv)); + s = igraph_strvector_get(&strv, 0); IGRAPH_CHECK(igraph_i_pajek_escape(s, &escaped)); fprintf(outstream, " %s %s", estrnames2[idx], escaped); IGRAPH_FREE(escaped); @@ -767,12 +788,12 @@ int igraph_write_graph_pajek(const igraph_t *graph, FILE *outstream) { 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_vector_int_destroy(&ex_numa); + igraph_vector_int_destroy(&ex_stra); + igraph_vector_int_destroy(&vx_numa); + igraph_vector_int_destroy(&vx_stra); igraph_strvector_destroy(&strv); igraph_vector_destroy(&numv); IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/io/parse_utils.c b/src/vendor/cigraph/src/io/parse_utils.c new file mode 100644 index 00000000000..6fc4b3d1b66 --- /dev/null +++ b/src/vendor/cigraph/src/io/parse_utils.c @@ -0,0 +1,381 @@ + +#include "parse_utils.h" + +#include "igraph_foreign.h" +#include "igraph_memory.h" + +#include "config.h" + +#include +#include +#include +#include + +#if defined(HAVE_XLOCALE) +/* On some systems, xlocale.h exists, but uselocale() is still in locale.h. + * Thus we include both. */ +#include +#include +#else +#include +#endif + +/* Trims whitespace from the beginning and the end of a string with a specified length. + * A pointer to the first character of the result substring, as well as its length, are returned. + * + * If you have a null-terminated string, call this function as + * + * igraph_i_trim_whitespace(str, strlen(str), &res, &len); + * + * This does not carry a performance penalty, as the end of the string would need to be + * determined anyway. + */ +void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len) { + const char *beg = str, *end = str + str_len; + while (beg < end && isspace(beg[0]) ) beg++; + while (end > beg && isspace(end[-1])) end--; + *res = beg; + *res_len = end - beg; +} + + +/* TODO: Support for reporting line number where parse error occurred. */ + +/* Converts a string to an integer. Throws an error if the result is not representable. + * + * The input is a not-necessarily-null-terminated string that must contain only the number. + * Any additional characters at the end of the string, such as whitespace, will trigger + * a parsing error. + * + * An error is returned if the input is an empty string. + */ +igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value) { + char buffer[128]; + char *tmp, *end; + char last_char; + igraph_bool_t out_of_range, dynamic_alloc; + long long val; + + if (length == 0) { + IGRAPH_ERROR("Cannot parse integer from empty string.", IGRAPH_PARSEERROR); + } + + dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); + + if (dynamic_alloc) { + tmp = IGRAPH_CALLOC(length+1, char); + IGRAPH_CHECK_OOM(tmp, "Failed to parse integer."); + } else { + tmp = buffer; + } + + strncpy(tmp, str, length); + tmp[length]='\0'; + + /* To avoid having to choose the appropriate strto?() function based on + * the definition of igraph_integer_t, we first use a long long variable + * which should be at least as large as igraph_integer_t on any platform. */ + errno = 0; + val = strtoll(tmp, &end, 10); + out_of_range = errno == ERANGE; + *value = (igraph_integer_t) val; + last_char = *end; + if (*value != val) { + out_of_range = 1; + } + + /* Free memory before raising any errors. */ + if (dynamic_alloc) { + IGRAPH_FREE(tmp); + } + + if (out_of_range) { + IGRAPH_ERROR("Failed to parse integer.", val > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); + } + + /* Did we parse to the end of the string? */ + if (last_char) { + IGRAPH_ERRORF("Unexpected character '%c' while parsing integer.", IGRAPH_PARSEERROR, last_char); + } + + return IGRAPH_SUCCESS; +} + + +/* Converts a string to a real number. Throws an error if the result is not representable. + * + * The input is a not-necessarily-null-terminated string that must contain only the number. + * Any additional characters at the end of the string, such as whitespace, will trigger + * a parsing error. + * + * NaN and Inf are supported. An error is returned if the input is an empty string. + */ +igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value) { + char buffer[128]; + char *tmp, *end; + char last_char; + igraph_bool_t out_of_range, dynamic_alloc; + + if (length == 0) { + IGRAPH_ERROR("Cannot parse real number from empty string.", IGRAPH_PARSEERROR); + } + + dynamic_alloc = length+1 > sizeof(buffer) / sizeof(buffer[0]); + + if (dynamic_alloc) { + tmp = IGRAPH_CALLOC(length+1, char); + IGRAPH_CHECK_OOM(tmp, "Failed to parse real number."); + } else { + tmp = buffer; + } + + strncpy(tmp, str, length); + tmp[length]='\0'; + + errno = 0; + *value = strtod(tmp, &end); + out_of_range = errno == ERANGE; /* This does not trigger when reading +-Inf. */ + last_char = *end; + + /* Free memory before raising any errors. */ + if (dynamic_alloc) { + IGRAPH_FREE(tmp); + } + + if (out_of_range) { + IGRAPH_ERROR("Failed to parse real number.", *value > 0 ? IGRAPH_EOVERFLOW : IGRAPH_EUNDERFLOW); + } + + /* Did we parse to the end of the string? */ + if (last_char) { + IGRAPH_ERRORF("Unexpected character '%c' while parsing real number.", IGRAPH_PARSEERROR, last_char); + } + + return IGRAPH_SUCCESS; +} + + +/* Skips all whitespace in a file. */ +igraph_error_t igraph_i_fskip_whitespace(FILE *file) { + int ch; + + do { + ch = fgetc(file); + } while (isspace(ch)); + if (ferror(file)) { + IGRAPH_ERROR("Error reading file.", IGRAPH_EFILE); + } + ungetc(ch, file); + + return IGRAPH_SUCCESS; +} + + +/* Reads an integer from a file. Throws an error if the result is not representable. + * + * Any initial whitespace is skipped. If no number is found, an error is raised. + * + * This function assumes that the number is followed by whitespace or the end of the file. + * If this is not the case, an error will be raised. + */ +igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value) { + /* The value requiring the most characters on 64-bit is -2^63, i.e. "-9223372036854775808". + * This is 20 characters long, plus one for the null terminator, requiring a buffer of + * at least 21 characters. We use a slightly larger buffer to allow for leading zeros and + * clearer error messages. + * + * Note: The string held in this buffer is not null-terminated. + */ + char buf[32]; + int ch; + + IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); + + int i = 0; /* must be 'int' due to use in printf format specifier */ + while (1) { + ch = fgetc(file); + if (ch == EOF) break; + if (isspace(ch)) { + ungetc(ch, file); + break; + } + if (i == sizeof(buf)) { + /* Reached the end of the buffer. */ + IGRAPH_ERRORF("'%.*s' is not a valid integer value.", IGRAPH_PARSEERROR, i, buf); + } + buf[i++] = ch; + } + if (ferror(file)) { + IGRAPH_ERROR("Error while reading integer.", IGRAPH_EFILE); + } + + if (i == 0) { + IGRAPH_ERROR("Integer expected, reached end of file instead.", IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_i_parse_integer(buf, i, value)); + + return IGRAPH_SUCCESS; +} + + +/* Reads a real number from a file. Throws an error if the result is not representable. + * + * Any initial whitespace is skipped. If no number is found, an error is raised. + * + * This function assumes that the number is followed by whitespace or the end of the file. + * If this is not the case, an error will be raised. + */ +igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value) { + /* The value requiring the most characters with an IEEE-754 double is the smallest + * representable number, with signs added, "-2.2250738585072014e-308" + * + * This is 24 characters long, plus one for the null terminator, requiring a buffer of + * at least 25 characters. This is 17 mantissa digits for lossless representation, + * 3 exponent digits, "e", and up to two minus signs. We use a larger buffer as some + * files may have more digits specified than necessary for exact representation. + * + * Note: The string held in this buffer is not null-terminated. + */ + char buf[64]; + int ch; + + IGRAPH_CHECK(igraph_i_fskip_whitespace(file)); + + int i = 0; /* must be 'int' due to use in printf format specifier */ + while (1) { + ch = fgetc(file); + if (ch == EOF) break; + if (isspace(ch)) { + ungetc(ch, file); + break; + } + if (i == sizeof(buf)) { + /* Reached the end of the buffer. */ + IGRAPH_ERRORF("'%.*s' is not a valid real value.", IGRAPH_PARSEERROR, i, buf); + } + buf[i++] = ch; + } + if (ferror(file)) { + IGRAPH_ERROR("Error while reading real number.", IGRAPH_EFILE); + } + + if (i == 0) { + IGRAPH_ERROR("Real number expected, reached end of file instead.", IGRAPH_PARSEERROR); + } + + IGRAPH_CHECK(igraph_i_parse_real(buf, i, value)); + + return IGRAPH_SUCCESS; +} + + +/* igraph_i_safelocale() and igraph_i_unsafelocale() will set the numeric locale to "C" + * and re-set it to its original value. This is to ensure that parsing and writing + * numbers uses a decimal point instead of a comma. + * + * These functions attempt to set the locale only for the current thread on a best-effort + * basis. On some platforms this is not possible, so the global locale will be changed. + * This is not safe to do in multi-threaded programs (not even if igraph runs only in + * a single thread). + */ + +struct igraph_safelocale_s { +#ifdef HAVE_USELOCALE + locale_t original_locale; + locale_t c_locale; +#else + char *original_locale; +# ifdef HAVE__CONFIGTHREADLOCALE + int per_thread_locale; +# endif +#endif +}; + +/** + * \function igraph_enter_safelocale + * \brief Temporarily set the C locale. + * + * \experimental + * + * igraph's foreign format readers and writers require a locale that uses a + * decimal point instead of a decimal comma. This is a convenience function + * that temporarily sets the C locale so that readers and writers would work + * correctly. It \em must be paired with a call to \ref igraph_exit_safelocale(), + * otherwise a memory leak will occur. + * + * + * This function tries to set the locale for the current thread only on a + * best-effort basis. Restricting the locale change to a single thread is not + * supported on all platforms. In these cases, this function falls back to using + * the standard setlocale() function, which affects the entire process + * and is not safe to use from concurrent threads. + * + * + * It is generally recommended to run igraph within a thread that has been + * permanently set to the C locale using system-specific means. This is a convenience + * function for situations when this is not easily possible because the programmer + * is not in control of the process, such as when developing plugins/extensions. + * Note that processes start up in the C locale by default, thus nothing needs to + * be done unless the locale has been changed away from the default. + * + * \param loc Pointer to a variable of type \c igraph_safelocale_t. The current + * locale will be stored here, so that it can be restored using + * \ref igraph_exit_safelocale(). + * \return Error code. + * + * \example examples/simple/safelocale.c + */ + +igraph_error_t igraph_enter_safelocale(igraph_safelocale_t *loc) { + *loc = IGRAPH_CALLOC(1, struct igraph_safelocale_s); + IGRAPH_CHECK_OOM(loc, "Could not set C locale."); + igraph_safelocale_t l = *loc; +#ifdef HAVE_USELOCALE + l->c_locale = newlocale(LC_NUMERIC_MASK, "C", NULL); + if (! l->c_locale) { + IGRAPH_ERROR("Could not set C locale.", IGRAPH_FAILURE); + } + l->original_locale = uselocale(l->c_locale); +#else + l->original_locale = strdup(setlocale(LC_NUMERIC, NULL)); + IGRAPH_CHECK_OOM(l->original_locale, "Not enough memory."); +# ifdef HAVE__CONFIGTHREADLOCALE + /* On Windows, we can enable per-thread locale */ + l->per_thread_locale = _configthreadlocale(0); + _configthreadlocale(_ENABLE_PER_THREAD_LOCALE); +# endif + setlocale(LC_NUMERIC, "C"); +#endif + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_exit_safelocale + * \brief Temporarily set the C locale. + * + * \experimental + * + * Restores a locale saved by \ref igraph_enter_safelocale() and deallocates + * all associated data. This function \em must be paired with a call to + * \ref igraph_enter_safelocale(). + * + * \param loc A variable of type \c igraph_safelocale_t, originally set + * by \ref igraph_enter_safelocale(). + */ + +void igraph_exit_safelocale(igraph_safelocale_t *loc) { + igraph_safelocale_t l = *loc; +#ifdef HAVE_USELOCALE + uselocale(l->original_locale); + freelocale(l->c_locale); +#else + setlocale(LC_NUMERIC, l->original_locale); + IGRAPH_FREE(l->original_locale); +# ifdef HAVE__CONFIGTHREADLOCALE + /* Restore per-thread locale setting on Windows */ + _configthreadlocale(l->per_thread_locale); +# endif +#endif + IGRAPH_FREE(*loc); +} diff --git a/src/vendor/cigraph/src/io/parse_utils.h b/src/vendor/cigraph/src/io/parse_utils.h new file mode 100644 index 00000000000..49a23d03a2a --- /dev/null +++ b/src/vendor/cigraph/src/io/parse_utils.h @@ -0,0 +1,41 @@ + +#ifndef IGRAPH_PARSE_UTILS_H +#define IGRAPH_PARSE_UTILS_H + +#include "igraph_error.h" +#include "igraph_types.h" + +/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ +#define IGRAPH_YY_CHECK(expr) \ + do { \ + igraph_error_t igraph_i_ret = (expr); \ + if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS)) { \ + context->igraph_errno = igraph_i_ret; \ + yyerror(&yylloc, context, "failed"); \ + YYABORT; \ + } \ + } while (0) + +/* This macro must be used only in Bison actions, in place of IGRAPH_CHECK(). */ +/* Note: + * Don't name macro argument 'igraph_errno' due to use of context->igraph_errno, + * or 'errno' due to use of #include in parse_utils.c. */ +#define IGRAPH_YY_ERRORF(reason, error_code, ...) \ + do { \ + igraph_errorf(reason, IGRAPH_FILE_BASENAME, __LINE__, \ + error_code, __VA_ARGS__) ; \ + context->igraph_errno = error_code; \ + YYABORT; \ + } while (0) + +void igraph_i_trim_whitespace(const char *str, size_t str_len, const char **res, size_t *res_len); + +igraph_error_t igraph_i_fskip_whitespace(FILE *file); + +igraph_error_t igraph_i_parse_integer(const char *str, size_t length, igraph_integer_t *value); +igraph_error_t igraph_i_parse_real(const char *str, size_t length, igraph_real_t *value); + +igraph_error_t igraph_i_fget_integer(FILE *file, igraph_integer_t *value); +igraph_error_t igraph_i_fget_real(FILE *file, igraph_real_t *value); + +#endif /* IGRAPH_PARSE_UTILS_H */ diff --git a/src/vendor/cigraph/src/isomorphism/bliss.cc b/src/vendor/cigraph/src/isomorphism/bliss.cc index 2439f629b84..d304ff531e9 100644 --- a/src/vendor/cigraph/src/isomorphism/bliss.cc +++ b/src/vendor/cigraph/src/isomorphism/bliss.cc @@ -25,10 +25,12 @@ #include "igraph_interrupt.h" #include "igraph_memory.h" #include "igraph_vector.h" -#include "igraph_vector_ptr.h" #include "core/exceptions.h" +#include +#include + using namespace bliss; using namespace std; @@ -74,21 +76,28 @@ using namespace std; 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); + igraph_integer_t nof_vertices = igraph_vcount(graph); + igraph_integer_t nof_edges = igraph_ecount(graph); + + if (nof_vertices > UINT_MAX || nof_edges > UINT_MAX) { + throw std::runtime_error("Graph too large for BLISS"); + } AbstractGraph *g; if (igraph_is_directed(graph)) { - g = new Digraph(nof_vertices); + g = new Digraph(static_cast(nof_vertices)); } else { - g = new Graph(nof_vertices); + g = new Graph(static_cast(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)); + for (unsigned int i = 0; i < static_cast(nof_edges); i++) { + g->add_edge( + static_cast(IGRAPH_FROM(graph, i)), + static_cast(IGRAPH_TO(graph, i)) + ); } return g; @@ -100,7 +109,7 @@ void bliss_free_graph(AbstractGraph *g) { } -inline int bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { +inline igraph_error_t bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { if (directed) { Digraph::SplittingHeuristic gsh = Digraph::shs_fsm; switch (sh) { @@ -130,7 +139,7 @@ inline int bliss_set_sh(AbstractGraph *g, igraph_bliss_sh_t sh, bool directed) { } -inline int bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { +inline igraph_error_t bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) { if (colors == NULL) { return IGRAPH_SUCCESS; } @@ -139,13 +148,17 @@ inline int bliss_set_colors(AbstractGraph *g, const igraph_vector_int_t *colors) IGRAPH_ERROR("Invalid vertex color vector length.", IGRAPH_EINVAL); } for (int i = 0; i < n; ++i) { - g->change_color(i, VECTOR(*colors)[i]); + igraph_integer_t color = VECTOR(*colors)[i]; + if (color < INT_MIN || color > INT_MAX) { + IGRAPH_ERRORF("Invalid vertex color index %" IGRAPH_PRId " for vertex %d.", IGRAPH_EOVERFLOW, color, i); + } + g->change_color(i, static_cast(color)); } return IGRAPH_SUCCESS; } -inline int bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { +inline igraph_error_t bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { if (info) { size_t group_size_strlen; @@ -162,7 +175,7 @@ inline int bliss_info_to_igraph(igraph_bliss_info_t *info, const Stats &stats) { 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); + IGRAPH_ERROR("Insufficient memory to retrieve automotphism group size.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } mpz_get_str(info->group_size, /* base */ 10, group_size); mpz_clear(group_size); @@ -190,24 +203,24 @@ struct AbortChecker { // 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; + igraph_vector_int_list_t *generators; public: - AutCollector(igraph_vector_ptr_t *generators_) : generators(generators_) { } + AutCollector(igraph_vector_int_list_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) { + igraph_vector_int_t newvector; + igraph_error_t err; + + err = igraph_vector_int_init(&newvector, n); + if (err != IGRAPH_SUCCESS) { 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) { + + copy(aut, aut + n, VECTOR(newvector)); // takes care of unsigned int -> igraph_integer_t conversion + + err = igraph_vector_int_list_push_back(generators, &newvector); + if (err != IGRAPH_SUCCESS) { throw bad_alloc(); } } @@ -241,12 +254,12 @@ class AutCollector { * when no longer needed, see \ref igraph_bliss_info_t. * \return Error code. * - * \sa igraph_is_same_graph() + * \sa \ref 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_error_t igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_vector_int_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); @@ -262,7 +275,7 @@ int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_ return IGRAPH_INTERRUPTED; } - IGRAPH_CHECK(igraph_vector_resize(labeling, N)); + IGRAPH_CHECK(igraph_vector_int_resize(labeling, N)); for (unsigned int i = 0; i < N; i++) { VECTOR(*labeling)[i] = cl[i]; } @@ -278,14 +291,23 @@ int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_ /** * \function igraph_automorphisms + * \brief Number of automorphisms using Bliss (deprecated alias). + * + * \deprecated-by igraph_count_automorphisms 0.10.5 + */ +igraph_error_t igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *colors, + igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { + return igraph_count_automorphisms(graph, colors, sh, info); +} + +/** + * \function igraph_count_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. + * for relatively small graphs. 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. @@ -300,7 +322,7 @@ int igraph_canonical_permutation(const igraph_t *graph, const igraph_vector_int_ * * 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_error_t igraph_count_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); @@ -339,7 +361,7 @@ int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *color * \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 + * contain pointers to \ref igraph_vector_int_t objects * representing generators of the automorphism group. * \param sh The splitting heuristics to be used in Bliss. See \ref * igraph_bliss_sh_t. @@ -350,8 +372,8 @@ int igraph_automorphisms(const igraph_t *graph, const igraph_vector_int_t *color * * 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_error_t igraph_automorphism_group( + const igraph_t *graph, const igraph_vector_int_t *colors, igraph_vector_int_list_t *generators, igraph_bliss_sh_t sh, igraph_bliss_info_t *info) { IGRAPH_HANDLE_EXCEPTIONS( AbstractGraph *g = bliss_from_igraph(graph); @@ -361,7 +383,7 @@ int igraph_automorphism_group( IGRAPH_CHECK(bliss_set_colors(g, colors)); Stats stats; - igraph_vector_ptr_resize(generators, 0); + igraph_vector_int_list_clear(generators); AutCollector collector(generators); AbortChecker checker; g->find_automorphisms(stats, collector, checker); @@ -441,20 +463,20 @@ int igraph_automorphism_group( * * Time complexity: exponential, but in practice it is quite fast. */ -int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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_bool_t *iso, igraph_vector_int_t *map12, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph1); + igraph_integer_t no_of_edges = igraph_ecount(graph1); + igraph_vector_int_t perm1, perm2; + igraph_vector_int_t vmap12, *mymap12 = &vmap12; + igraph_vector_int_t from, to, index; + igraph_vector_int_t from2, to2, index2; igraph_bool_t directed; - long int i, j; + igraph_integer_t i, j; *iso = 0; if (info1) { @@ -481,80 +503,80 @@ int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { if (map12) { - igraph_vector_clear(map12); + igraph_vector_int_clear(map12); } if (map21) { - igraph_vector_clear(map21); + igraph_vector_int_clear(map21); } - return 0; + return IGRAPH_SUCCESS; } if (map12) { mymap12 = map12; } else { - IGRAPH_VECTOR_INIT_FINALLY(mymap12, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(mymap12, 0); } - IGRAPH_VECTOR_INIT_FINALLY(&perm1, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&perm2, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&perm1, no_of_nodes); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_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; + VECTOR(*mymap12)[ 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] ]; + VECTOR(perm2)[i] = VECTOR(*mymap12)[ VECTOR(perm1)[i] ]; } /* Copy it to mymap12 */ - igraph_vector_update(mymap12, &perm2); + IGRAPH_CHECK(igraph_vector_int_update(mymap12, &perm2)); - igraph_vector_destroy(&perm1); - igraph_vector_destroy(&perm2); + igraph_vector_int_destroy(&perm1); + igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&from, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&from2, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&to2, no_of_edges); + IGRAPH_VECTOR_INT_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) ]; + VECTOR(from)[i] = VECTOR(*mymap12)[ IGRAPH_FROM(graph1, i) ]; + VECTOR(to)[i] = VECTOR(*mymap12)[ IGRAPH_TO (graph1, i) ]; if (! directed && VECTOR(from)[i] < VECTOR(to)[i]) { - igraph_real_t tmp = VECTOR(from)[i]; + igraph_integer_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_vector_int_pair_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]; + igraph_integer_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); + igraph_vector_int_resize(&from2, no_of_edges); + igraph_vector_int_pair_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]; + igraph_integer_t i1 = VECTOR(index)[i]; + igraph_integer_t i2 = VECTOR(index2)[i]; if (VECTOR(from)[i1] != VECTOR(from2)[i2] || VECTOR(to)[i1] != VECTOR(to2)[i2]) { *iso = 0; @@ -567,42 +589,42 @@ int igraph_isomorphic_bliss(const igraph_t *graph1, const igraph_t *graph2, if (*iso && colors1 != NULL) { for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(*colors1)[i] != VECTOR(*colors2)[(long int) VECTOR(*mymap12)[i] ]) { + if (VECTOR(*colors1)[i] != VECTOR(*colors2)[ 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_vector_int_destroy(&index2); + igraph_vector_int_destroy(&to2); + igraph_vector_int_destroy(&from2); + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&to); + igraph_vector_int_destroy(&from); IGRAPH_FINALLY_CLEAN(6); if (*iso) { /* The inverse of mymap12 */ if (map21) { - IGRAPH_CHECK(igraph_vector_resize(map21, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(map21, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - VECTOR(*map21)[ (long int) VECTOR(*mymap12)[i] ] = i; + VECTOR(*map21)[ VECTOR(*mymap12)[i] ] = i; } } } else { if (map12) { - igraph_vector_clear(map12); + igraph_vector_int_clear(map12); } if (map21) { - igraph_vector_clear(map21); + igraph_vector_int_clear(map21); } } if (!map12) { - igraph_vector_destroy(mymap12); + igraph_vector_int_destroy(mymap12); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/isomorphism/bliss/CMakeLists.txt b/src/vendor/cigraph/src/isomorphism/bliss/CMakeLists.txt index 276bf80d809..9de271afb6b 100644 --- a/src/vendor/cigraph/src/isomorphism/bliss/CMakeLists.txt +++ b/src/vendor/cigraph/src/isomorphism/bliss/CMakeLists.txt @@ -31,7 +31,7 @@ endif() if(NOT GMP_IS_VENDORED) target_link_libraries( bliss - PUBLIC + PRIVATE ${GMP_LIBRARY} ) endif() diff --git a/src/vendor/cigraph/src/isomorphism/bliss/graph.hh b/src/vendor/cigraph/src/isomorphism/bliss/graph.hh index c4d30b3f66e..5a60389628a 100644 --- a/src/vendor/cigraph/src/isomorphism/bliss/graph.hh +++ b/src/vendor/cigraph/src/isomorphism/bliss/graph.hh @@ -514,7 +514,9 @@ protected: unsigned int color; std::vector edges; - unsigned int nof_edges() const {return edges.size(); } + unsigned int nof_edges() const { + return static_cast(edges.size()); + } }; std::vector vertices; void sort_edges(); @@ -610,7 +612,9 @@ public: /** * Return the number of vertices in the graph. */ - unsigned int get_nof_vertices() const {return vertices.size(); } + unsigned int get_nof_vertices() const { + return static_cast(vertices.size()); + } /** * \copydoc AbstractGraph::permute(const unsigned int* const perm) const @@ -712,8 +716,8 @@ protected: 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(); } + unsigned int nof_edges_in() const { return static_cast(edges_in.size()); } + unsigned int nof_edges_out() const { return static_cast(edges_out.size()); } }; std::vector vertices; void remove_duplicate_edges(); @@ -819,7 +823,7 @@ public: /** * Return the number of vertices in the graph. */ - unsigned int get_nof_vertices() const {return vertices.size(); } + unsigned int get_nof_vertices() const { return static_cast(vertices.size()); } /** * Add a new vertex with color 'color' in the graph and return its index. diff --git a/src/vendor/cigraph/src/isomorphism/isoclasses.c b/src/vendor/cigraph/src/isomorphism/isoclasses.c index d2a4eca5040..27162e903f1 100644 --- a/src/vendor/cigraph/src/isomorphism/isoclasses.c +++ b/src/vendor/cigraph/src/isomorphism/isoclasses.c @@ -2525,6 +2525,7 @@ const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, * (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. + * These values can also be retrieved using \ref igraph_graph_count(). * For more information, see https://oeis.org/A000273 and https://oeis.org/A000088. * * @@ -2547,10 +2548,10 @@ const unsigned int igraph_i_classedges_6u[] = { 4, 5, 3, 5, 2, 5, 1, 5, 0, 5, 3, * * 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); +igraph_error_t igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { + igraph_integer_t e; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); unsigned int idx, mul; const unsigned int *arr_idx, *arr_code; unsigned int code; @@ -2625,8 +2626,8 @@ int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { * 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 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. @@ -2636,18 +2637,18 @@ int igraph_isoclass(const igraph_t *graph, igraph_integer_t *isoclass) { * 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_error_t igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_int_t *vids, igraph_integer_t *isoclass) { - int subgraph_size = (int) igraph_vector_size(vids); - igraph_vector_t neis; + igraph_integer_t subgraph_size = igraph_vector_int_size(vids); + igraph_vector_int_t neis; unsigned int mul, idx; const unsigned int *arr_idx, *arr_code; unsigned int code = 0; - long int i, j, s; + igraph_integer_t i, j, s; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); if (igraph_is_directed(graph)) { switch (subgraph_size) { @@ -2694,20 +2695,20 @@ int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, } 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); + igraph_integer_t from = VECTOR(*vids)[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); + s = igraph_vector_int_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)) { + igraph_integer_t nei = VECTOR(neis)[j], to; + if (igraph_vector_int_search(vids, 0, nei, &to)) { idx = (mul * i + to); code |= arr_idx[idx]; } } } - *isoclass = (igraph_integer_t) arr_code[code]; - igraph_vector_destroy(&neis); + *isoclass = arr_code[code]; + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -2745,22 +2746,22 @@ int igraph_isoclass_subgraph(const igraph_t *graph, const igraph_vector_t *vids, * 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_error_t igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, igraph_integer_t number, igraph_bool_t directed) { - igraph_vector_t edges; + igraph_vector_int_t edges; const unsigned int *classedges; - long int graphcount; - long int power; - long int pos; + igraph_integer_t graphcount; + igraph_integer_t pos; + unsigned int power; unsigned int code; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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) + "Isoclass %" IGRAPH_PRId " requested, but there are only %" \ + IGRAPH_PRId " %s graphs of size %" IGRAPH_PRId ".", IGRAPH_EINVAL, \ + number, graphcount, directed ? "directed" : "undirected", size) if (directed) { switch (size) { @@ -2772,7 +2773,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_3[ (long int) number]; + code = igraph_i_isographs_3[number]; power = 32; break; @@ -2785,7 +2786,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_4[ (long int) number]; + code = igraph_i_isographs_4[number]; power = 2048; break; @@ -2805,7 +2806,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_3u[ (long int) number]; + code = igraph_i_isographs_3u[number]; power = 4; break; @@ -2818,7 +2819,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_4u[ (long int) number]; + code = igraph_i_isographs_4u[number]; power = 32; break; @@ -2831,7 +2832,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_5u[ (long int) number]; + code = igraph_i_isographs_5u[number]; power = 512; break; @@ -2844,7 +2845,7 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, CHECK_ISOCLASS(number, directed, size, graphcount); } - code = igraph_i_isographs_6u[ (long int) number]; + code = igraph_i_isographs_6u[number]; power = 16384; break; @@ -2860,8 +2861,8 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, 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])); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, classedges[2 * pos])); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, classedges[2 * pos + 1])); code -= power; } power /= 2; @@ -2869,8 +2870,64 @@ int igraph_isoclass_create(igraph_t *graph, igraph_integer_t size, } IGRAPH_CHECK(igraph_create(graph, &edges, size, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } + +/* https://oeis.org/A000088 */ +static igraph_integer_t undirected_graph_counts[] = { + 1, 1, 2, 4, 11, 34, 156, 1044, 12346, 274668, 12005168, 1018997864, +#if IGRAPH_INTEGER_SIZE == 64 + 165091172592, 50502031367952, 29054155657235488 +#endif +}; + +/* https://oeis.org/A000273 */ +static igraph_integer_t directed_graph_counts[] = { + 1, 1, 3, 16, 218, 9608, 1540944, 882033440, +#if IGRAPH_INTEGER_SIZE == 64 + 1793359192848, 13027956824399552 +#endif +}; + +/** + * \function igraph_graph_count + * \brief The number of unlabelled graphs on the given number of vertices. + * + * Gives the number of unlabelled \em simple graphs on the specified number of vertices. + * The "isoclass" of a graph of this size is at most one less than this value. + * + * + * This function is meant to be used in conjunction with isoclass and motif finder + * functions. It will only work for small \p n values for which the result is + * represetable in an \type igraph_integer_t. For larger \p n values, an overflow + * error is raised. + * + * \param n The number of vertices. + * \param directed Boolean, whether to consider directed graphs. + * \param count Pointer to an integer, the result will be stored here. + * \return Error code. + * + * \sa \ref igraph_isoclass(), \ref igraph_motifs_randesu_callback(). + * + * Time complexity: O(1). + */ +igraph_error_t igraph_graph_count(igraph_integer_t n, igraph_bool_t directed, igraph_integer_t *count) { + if (n < 0) { + IGRAPH_ERROR("Graph size must not be negative.", IGRAPH_EINVAL); + } + if (directed) { + if (n >= (igraph_integer_t) (sizeof directed_graph_counts / sizeof directed_graph_counts[0])) { + IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); + } + *count = directed_graph_counts[n]; + } else { + if (n >= (igraph_integer_t) (sizeof undirected_graph_counts / sizeof undirected_graph_counts[0])) { + IGRAPH_ERRORF("Graph size of % " IGRAPH_PRId " too large.", IGRAPH_EOVERFLOW, n); + } + *count = undirected_graph_counts[n]; + } + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c b/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c index 235e5e73c31..2b8ff8e693c 100644 --- a/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c +++ b/src/vendor/cigraph/src/isomorphism/isomorphism_misc.c @@ -52,24 +52,24 @@ * \sa \ref igraph_simplify(), \ref igraph_isomorphic_vf2(), \ref igraph_subisomorphic_vf2() * */ -int igraph_simplify_and_colorize( +igraph_error_t 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_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t pto = -1, pfrom = -1; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); IGRAPH_CHECK(igraph_vector_int_resize(vertex_color, no_of_nodes)); igraph_vector_int_null(vertex_color); @@ -79,9 +79,9 @@ int igraph_simplify_and_colorize( 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); + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); if (to == from) { VECTOR(*vertex_color)[to]++; @@ -91,8 +91,8 @@ int igraph_simplify_and_colorize( if (to == pto && from == pfrom) { VECTOR(*edge_color)[i]++; } else { - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); i++; VECTOR(*edge_color)[i] = 1; } @@ -108,7 +108,7 @@ int igraph_simplify_and_colorize( IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/isomorphism/lad.c b/src/vendor/cigraph/src/isomorphism/lad.c index d6be6657f51..fbb9518db07 100644 --- a/src/vendor/cigraph/src/isomorphism/lad.c +++ b/src/vendor/cigraph/src/isomorphism/lad.c @@ -57,23 +57,17 @@ #include "core/interruption.h" +#include #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_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ } \ IGRAPH_FINALLY(igraph_free, VAR); \ } @@ -83,7 +77,7 @@ #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_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ } \ IGRAPH_FINALLY(igraph_free, VAR); \ IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR)); \ @@ -95,15 +89,15 @@ /* ---------------------------------------------------------*/ typedef struct { - long int nbVertices; /* Number of vertices */ - igraph_vector_t nbSucc; + igraph_integer_t nbVertices; /* Number of vertices */ + igraph_vector_int_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); +static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { + igraph_integer_t i, j, n; + igraph_integer_t no_of_nodes = igraph_vcount(igraph); igraph_vector_int_t *neis; graph->nbVertices = no_of_nodes; @@ -111,7 +105,7 @@ static int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { 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); + IGRAPH_VECTOR_INT_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)); } @@ -123,7 +117,7 @@ static int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { 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]; + igraph_integer_t v = VECTOR(*neis)[j]; if (MATRIX(graph->isEdge, i, v)) { IGRAPH_ERROR("LAD functions do not support graphs with multi-edges.", IGRAPH_EINVAL); } @@ -139,7 +133,7 @@ static int igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { static void igraph_i_lad_destroyGraph(Tgraph *graph) { igraph_matrix_char_destroy(&graph->isEdge); igraph_adjlist_destroy(&graph->succ); - igraph_vector_destroy(&graph->nbSucc); + igraph_vector_int_destroy(&graph->nbSucc); } @@ -157,17 +151,17 @@ typedef struct { /* 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_integer_t 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 + igraph_integer_t 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 + igraph_integer_t 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 */ @@ -192,11 +186,11 @@ static void igraph_i_lad_resetToFilter(Tdomain *D) { } -static int igraph_i_lad_nextToFilter(Tdomain* D, int size) { +static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t size) { /* precondition: emptyToFilter = false remove a node from toFilter (FIFO) unmark this node and return it */ - int u = VECTOR(D->toFilter)[D->nextOutToFilter]; + igraph_integer_t u = VECTOR(D->toFilter)[D->nextOutToFilter]; VECTOR(D->markedToFilter)[u] = false; if (D->nextOutToFilter == D->lastInToFilter) { /* u was the last node in tofilter */ @@ -209,7 +203,7 @@ static int igraph_i_lad_nextToFilter(Tdomain* D, int size) { return u; } -static void igraph_i_lad_addToFilter(int u, Tdomain* D, int size) { +static void igraph_i_lad_addToFilter(igraph_integer_t u, Tdomain* D, igraph_integer_t size) { /* if u is not marked, then add it to toFilter and mark it */ if (VECTOR(D->markedToFilter)[u]) { return; @@ -226,29 +220,29 @@ static void igraph_i_lad_addToFilter(int u, Tdomain* D, int size) { VECTOR(D->toFilter)[D->lastInToFilter] = u; } -static bool igraph_i_lad_isInD(int u, int v, Tdomain* D) { +static bool igraph_i_lad_isInD(igraph_integer_t u, igraph_integer_t 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) { +static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D, igraph_integer_t 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; + igraph_integer_t *fifo, *pred; bool *marked; - int nextIn = 0; - int nextOut = 0; - int i, v, v2, u2; + igraph_integer_t nextIn = 0; + igraph_integer_t nextOut = 0; + igraph_integer_t i, v, v2, u2; *result = false; /* Allocate memory */ - ALLOC_ARRAY(fifo, nbV, int); - ALLOC_ARRAY(pred, nbV, int); + ALLOC_ARRAY(fifo, nbV, igraph_integer_t); + ALLOC_ARRAY(pred, nbV, igraph_integer_t); ALLOC_ARRAY(marked, nbV, bool); for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { @@ -297,21 +291,21 @@ static int igraph_i_lad_augmentingPath(int u, Tdomain* D, int nbV, bool* result) igraph_free(marked); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lad_removeAllValuesButOne(int u, int v, Tdomain* D, Tgraph* Gp, +static igraph_error_t igraph_i_lad_removeAllValuesButOne(igraph_integer_t u, igraph_integer_t 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_integer_t j, oldPos, newPos; igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); - int n = (int) igraph_vector_int_size(uneis); + igraph_integer_t n = 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)); + igraph_i_lad_addToFilter(VECTOR(*uneis)[j], D, + Gp->nbVertices); } /* remove all values but v from D[u] */ oldPos = MATRIX(D->posInVal, u, v); @@ -326,27 +320,27 @@ static int igraph_i_lad_removeAllValuesButOne(int u, int v, Tdomain* D, Tgraph* 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)); + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, Gt->nbVertices, result)); } else { *result = true; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lad_removeValue(int u, int v, Tdomain* D, Tgraph* Gp, +static igraph_error_t igraph_i_lad_removeValue(igraph_integer_t u, igraph_integer_t 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_integer_t j; igraph_vector_int_t *uneis = igraph_adjlist_get(&Gp->succ, u); - int n = (int) igraph_vector_int_size(uneis); - int oldPos, newPos; + igraph_integer_t n = igraph_vector_int_size(uneis); + igraph_integer_t 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)); + igraph_i_lad_addToFilter(VECTOR(*uneis)[j], D, + Gp->nbVertices); } /* remove v from D[u] */ oldPos = MATRIX(D->posInVal, u, v); @@ -361,17 +355,17 @@ static int igraph_i_lad_removeValue(int u, int v, Tdomain* D, Tgraph* Gp, 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)); + IGRAPH_CHECK(igraph_i_lad_augmentingPath(u, D, Gt->nbVertices, result)); } else { *result = true; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, +static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vector_int_t* toBeMatched, bool induced, Tdomain* D, Tgraph* Gp, - Tgraph* Gt, int *invalid) { + Tgraph* Gt, igraph_integer_t *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 @@ -379,7 +373,7 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, 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_integer_t j, u, v, u2, oldNbVal; igraph_vector_int_t *vneis; bool result = false; @@ -394,7 +388,8 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, 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; + *invalid = 1; + return IGRAPH_SUCCESS; } } if (MATRIX(Gp->isEdge, u, u2)) { @@ -406,7 +401,8 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { - *invalid = 1; return 0; + *invalid = 1; + return IGRAPH_SUCCESS; } } } @@ -420,16 +416,18 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); if (!result) { - *invalid = 1; return 0; + *invalid = 1; + return IGRAPH_SUCCESS; } } } } 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 (igraph_i_lad_isInD(u2, VECTOR(*vneis)[j], D)) { + IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(*vneis)[j], D, Gp, Gt, &result)); if (!result) { - *invalid = 1; return 0; + *invalid = 1; + return IGRAPH_SUCCESS; } } } @@ -437,7 +435,7 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, } if (VECTOR(D->nbVal)[u2] == 0) { *invalid = 1; /* D[u2] is empty */ - return 0; + return IGRAPH_SUCCESS; } if ((VECTOR(D->nbVal)[u2] == 1) && (oldNbVal > 1)) { VECTOR(*toBeMatched)[nb++] = u2; @@ -446,13 +444,13 @@ static int igraph_i_lad_matchVertices(int nb, igraph_vector_int_t* toBeMatched, } } *invalid = 0; - return 0; + return IGRAPH_SUCCESS; } -static bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp, +static bool igraph_i_lad_matchVertex(igraph_integer_t u, bool induced, Tdomain* D, Tgraph* Gp, Tgraph *Gt) { - int invalid; + igraph_integer_t 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) @@ -460,8 +458,7 @@ static bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&toBeMatched, Gp->nbVertices); VECTOR(toBeMatched)[0] = u; IGRAPH_CHECK(igraph_i_lad_matchVertices(1, &toBeMatched, induced, D, Gp, Gt, &invalid)); @@ -474,17 +471,22 @@ static bool igraph_i_lad_matchVertex(int u, bool induced, Tdomain* D, Tgraph* Gp 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; + igraph_integer_t pa = ((*((igraph_integer_t*)a) - *((igraph_integer_t*)b))); + if (pa < 0) { + return -1; + } else if (pa > 0) { + return 1; + } + return 0; } -static bool igraph_i_lad_compare(int size_mu, int* mu, int size_mv, int* mv) { +static bool igraph_i_lad_compare(igraph_integer_t size_mu, igraph_integer_t* mu, igraph_integer_t size_mv, igraph_integer_t* 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); + igraph_integer_t i, j; + igraph_qsort(mu, (size_t) size_mu, sizeof(mu[0]), igraph_i_lad_qcompare); + igraph_qsort(mv, (size_t) size_mv, sizeof(mv[0]), igraph_i_lad_qcompare); i = size_mv - 1; for (j = size_mu - 1; j >= 0; j--) { if (mu[j] > mv[i]) { @@ -495,22 +497,22 @@ static bool igraph_i_lad_compare(int size_mu, int* mu, int size_mv, int* mv) { 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) { +static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, + const igraph_vector_int_list_t *domains, Tdomain *D, + const Tgraph *Gp, const Tgraph *Gt, igraph_integer_t *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; + igraph_integer_t *val; bool *dom; - int *mu, *mv; - int matchingSize, u, v, i, j; - igraph_vector_t *vec; + igraph_integer_t *mu, *mv; + igraph_integer_t matchingSize, u, v, i, j; + igraph_vector_int_t *vec; - ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, int); + ALLOC_ARRAY(val, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); ALLOC_ARRAY(dom, Gt->nbVertices, bool); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); @@ -544,11 +546,11 @@ static int igraph_i_lad_initDomains(bool initialDomains, 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); + vec = igraph_vector_int_list_get_ptr(domains, u); + i = igraph_vector_int_size(vec); memset(dom, false, sizeof(bool) * (size_t)(Gt->nbVertices)); for (j = 0; j < i; j++) { - v = (int) VECTOR(*vec)[j]; + v = VECTOR(*vec)[j]; dom[v] = true; } } @@ -559,42 +561,42 @@ static int igraph_i_lad_initDomains(bool initialDomains, 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); + MATRIX(D->posInVal, u, v) = 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); + mu = IGRAPH_CALLOC(VECTOR(Gp->nbSucc)[u], igraph_integer_t); if (mu == 0) { igraph_free(val); igraph_free(dom); - IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); + IGRAPH_ERROR("cannot allocate 'mu' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - mv = IGRAPH_CALLOC((long int) VECTOR(Gt->nbSucc)[v], int); + mv = IGRAPH_CALLOC(VECTOR(Gt->nbSucc)[v], igraph_integer_t); 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); + IGRAPH_ERROR("cannot allocate 'mv' array in igraph_i_lad_initDomains", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { - mu[i] = (int) VECTOR(Gp->nbSucc)[(long int) VECTOR(*Gp_uneis)[i]]; + mu[i] = VECTOR(Gp->nbSucc)[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]]; + mv[i] = VECTOR(Gt->nbSucc)[VECTOR(*Gt_vneis)[i]]; } - if (igraph_i_lad_compare((int) VECTOR(Gp->nbSucc)[u], mu, - (int) VECTOR(Gt->nbSucc)[v], mv) == 1) { + if (igraph_i_lad_compare(VECTOR(Gp->nbSucc)[u], mu, + 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); + 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); + VECTOR(D->firstVal)[u] + Gt->nbVertices; } } } @@ -622,7 +624,7 @@ static int igraph_i_lad_initDomains(bool initialDomains, igraph_vector_int_fill(&D->matching, -1); D->nextOutToFilter = 0; - D->lastInToFilter = (int) (Gp->nbVertices - 1); + D->lastInToFilter = Gp->nbVertices - 1; *empty = 0; @@ -659,19 +661,19 @@ static void igraph_i_lad_destroyDomains(Tdomain *D) { #define toBeDeleted 3 #define deleted 4 -static void igraph_i_lad_addToDelete(int u, int* list, int* nb, int* marked) { +static void igraph_i_lad_addToDelete(igraph_integer_t u, igraph_integer_t* list, igraph_integer_t* nb, igraph_integer_t* marked) { if (marked[u] < toBeDeleted) { list[(*nb)++] = u; marked[u] = toBeDeleted; } } -static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, +static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igraph_integer_t sizeOfV, igraph_vector_int_t *degree, igraph_vector_int_t *firstAdj, igraph_vector_int_t *adj, igraph_vector_int_t * matchedWithU, - int *invalid) { + igraph_integer_t *invalid) { /* input: sizeOfU = number of vertices in U sizeOfV = number of vertices in V @@ -686,53 +688,53 @@ static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, 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 + igraph_integer_t *matchedWithV; /* matchedWithV[matchedWithU[u]]=u */ + igraph_integer_t *nbPred; /* nbPred[i] = nb of predecessors of the ith vertex of V in the DAG */ - int *pred; /* pred[i][j] = jth predecessor the ith + igraph_integer_t *pred; /* pred[i][j] = jth predecessor the ith vertex of V in the DAG */ - int *nbSucc; /* nbSucc[i] = nb of successors of the ith + igraph_integer_t *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 + igraph_integer_t *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; + igraph_integer_t *listV, *listU, *listDV, *listDU; + igraph_integer_t nbV, nbU, nbDV, nbDU; + igraph_integer_t i, j, k, stop, u, v; + igraph_integer_t *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_integer_t nbUnmatched = 0; /* number of vertices of U that are not matched */ + igraph_integer_t *unmatched; /* vertices of U that are not matched */ + igraph_integer_t *posInUnmatched; /* unmatched[posInUnmatched[u]]=u */ igraph_vector_int_t path; if (sizeOfU > sizeOfV) { *invalid = 1; /* trivial case of infeasibility */ - return 0; + return IGRAPH_SUCCESS; } - 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); + ALLOC_ARRAY(matchedWithV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(nbPred, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(pred, sizeOfV * sizeOfU, igraph_integer_t); + ALLOC_ARRAY(nbSucc, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(succ, sizeOfU * sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(listDV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(listDU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(markedV, sizeOfV, igraph_integer_t); + ALLOC_ARRAY(markedU, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(unmatched, sizeOfU, igraph_integer_t); + ALLOC_ARRAY(posInUnmatched, sizeOfU, igraph_integer_t); 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)); + memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(matchedWithV[0])); for (u = 0; u < sizeOfU; u++) { if (VECTOR(*matchedWithU)[u] >= 0) { matchedWithV[VECTOR(*matchedWithU)[u]] = u; @@ -762,10 +764,10 @@ static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, 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)); + memset(markedU, white, (size_t) sizeOfU * sizeof(markedU[0])); + memset(nbSucc, 0, (size_t) sizeOfU * sizeof(nbSucc[0])); + memset(markedV, white, (size_t) sizeOfV * sizeof(markedV[0])); + memset(nbPred, 0, (size_t) sizeOfV * sizeof(nbPred[0])); /* first layer of the DAG from the free nodes of U */ nbV = 0; for (j = 0; j < nbUnmatched; j++) { @@ -869,7 +871,6 @@ static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, 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++) { } @@ -915,12 +916,12 @@ static int igraph_i_lad_updateMatching(int sizeOfU, int sizeOfV, igraph_free(nbPred); igraph_free(matchedWithV); IGRAPH_FINALLY_CLEAN(14); - return 0; + return IGRAPH_SUCCESS; } -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) { +static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t u, bool* marked, igraph_integer_t* nbSucc, + igraph_integer_t* succ, igraph_vector_int_t * matchedWithU, + igraph_integer_t* order, igraph_integer_t* nb) { /* perform a depth first search, starting from u, in the bipartite graph Go=(U, V, E) such that U = vertices of Gp @@ -931,8 +932,8 @@ static void igraph_i_lad_DFS(int nbU, int nbV, int u, bool* marked, int* nbSucc, 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 */ + igraph_integer_t i; + igraph_integer_t 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++) { @@ -946,9 +947,9 @@ static void igraph_i_lad_DFS(int nbU, int nbV, int u, bool* marked, int* nbSucc, 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, +static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t* numV, igraph_integer_t* numU, + igraph_integer_t* nbSucc, igraph_integer_t* succ, + igraph_integer_t* nbPred, igraph_integer_t* pred, igraph_vector_int_t * matchedWithU, igraph_vector_int_t * matchedWithV) { /* postrelation: numV[v]==numU[u] iff they belong to the same @@ -961,15 +962,15 @@ static int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU, Given a vertex v of Gt, nbSucc[v]=number of sucessors of v and succ[v]=list of successors of v */ - int *order; + igraph_integer_t *order; bool *marked; - int *fifo; - int u, v, i, j, k, nbSCC, nb; + igraph_integer_t *fifo; + igraph_integer_t u, v, i, j, k, nbSCC, nb; /* Allocate memory */ - ALLOC_ARRAY(order, nbU, int); + ALLOC_ARRAY(order, nbU, igraph_integer_t); ALLOC_ARRAY(marked, nbU, bool); - ALLOC_ARRAY(fifo, nbV, int); + ALLOC_ARRAY(fifo, nbV, igraph_integer_t); /* Order vertices of Gp wrt DFS */ nb = nbU - 1; @@ -982,8 +983,8 @@ static int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU, /* 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)); + memset(numU, -1, (size_t) nbU * sizeof(numU[0])); + memset(numV, -1, (size_t) nbV * sizeof(numV[0])); for (i = 0; i < nbU; i++) { u = order[i]; v = VECTOR(*matchedWithU)[u]; @@ -1017,12 +1018,12 @@ static int igraph_i_lad_SCC(int nbU, int nbV, int* numV, int* numU, igraph_free(order); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, - Tdomain* D, int *invalid) { +static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, + Tdomain* D, igraph_integer_t *invalid) { /* precondition: D->globalMatchingP is an all different matching of the pattern vertices postcondition: filter domains wrt GAC(allDiff) @@ -1033,29 +1034,29 @@ static int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, 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 + igraph_integer_t *nbPred; /* nbPred[u] = nb of predecessors of u in Go */ + igraph_integer_t *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 + igraph_integer_t *nbSucc; /* nbSucc[v] = nb of successors of v in Go */ + igraph_integer_t *succ; /* succ[v][i] = ith successor of v in Go */ - int u, v, i, w, oldNbVal, nbToMatch; - int *numV, *numU; + igraph_integer_t u, v, i, w, oldNbVal, nbToMatch; + igraph_integer_t *numV, *numU; igraph_vector_int_t toMatch; bool *used; - int *list; - int nb = 0; + igraph_integer_t *list; + igraph_integer_t 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(nbPred, Gp->nbVertices, igraph_integer_t); + ALLOC_ARRAY(pred, Gp->nbVertices * Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(nbSucc, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(succ, Gt->nbVertices * Gp->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numV, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numU, Gp->nbVertices, igraph_integer_t); ALLOC_ARRAY(used, Gp->nbVertices * Gt->nbVertices, bool); - ALLOC_ARRAY(list, Gt->nbVertices, int); + ALLOC_ARRAY(list, Gt->nbVertices, igraph_integer_t); IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices)); IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); @@ -1096,7 +1097,7 @@ static int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, /* look for strongly connected components in Go */ IGRAPH_CHECK( - igraph_i_lad_SCC((int)(Gp->nbVertices), (int)(Gt->nbVertices), numV, numU, + igraph_i_lad_SCC(Gp->nbVertices, 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 @@ -1141,38 +1142,38 @@ static int igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tgraph* Gt, igraph_free(nbPred); IGRAPH_FINALLY_CLEAN(9); - return 0; + return IGRAPH_SUCCESS; } /* ---------------------------------------------------------*/ /* Coming from lad.c */ /* ---------------------------------------------------------*/ -static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* Gt, +static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t 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_integer_t u2, v2, i, j; + igraph_integer_t nbMatched = 0; igraph_vector_int_t *Gp_uneis = igraph_adjlist_get(&Gp->succ, u); - int *num, *numInv; + igraph_integer_t *num, *numInv; igraph_vector_int_t nbComp; igraph_vector_int_t firstComp; igraph_vector_int_t comp; - int nbNum = 0; - int posInComp = 0; + igraph_integer_t nbNum = 0; + igraph_integer_t posInComp = 0; igraph_vector_int_t matchedWithU; - int invalid; + igraph_integer_t 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 */ + u2 = 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; + return IGRAPH_SUCCESS; } /* look for a support of edge (u, u2) for v */ for (i = VECTOR(D->firstVal)[u2]; @@ -1181,18 +1182,18 @@ static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* G VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = VECTOR(D->val)[i]; *result = true; - return 0; + return IGRAPH_SUCCESS; } } *result = false; - return 0; + return IGRAPH_SUCCESS; } /* 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]; + u2 = 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++; @@ -1200,32 +1201,32 @@ static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* G } if (nbMatched == VECTOR(Gp->nbSucc)[u]) { *result = true; - return 0; + return IGRAPH_SUCCESS; } /* The matching still covers adj(u) */ /* Allocate memory */ - ALLOC_ARRAY(num, Gt->nbVertices, int); - ALLOC_ARRAY(numInv, Gt->nbVertices, int); + ALLOC_ARRAY(num, Gt->nbVertices, igraph_integer_t); + ALLOC_ARRAY(numInv, Gt->nbVertices, igraph_integer_t); /* 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_CHECK(igraph_vector_int_init(&nbComp, 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_CHECK(igraph_vector_int_init(&firstComp, 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] * + IGRAPH_CHECK(igraph_vector_int_init(&comp, (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_CHECK(igraph_vector_int_init(&matchedWithU, VECTOR(Gp->nbSucc)[u])); IGRAPH_FINALLY(igraph_vector_int_destroy, &matchedWithU); - memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(int)); + memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(num[0])); for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { - u2 = (int) VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */ + u2 = 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; @@ -1245,7 +1246,7 @@ static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* G } 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 */ + v2 = 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; @@ -1270,7 +1271,7 @@ static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* G } /* Call Hopcroft Karp to update the matching */ IGRAPH_CHECK( - igraph_i_lad_updateMatching((int) VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, + igraph_i_lad_updateMatching(VECTOR(Gp->nbSucc)[u], nbNum, &nbComp, &firstComp, &comp, &matchedWithU, &invalid) ); if (invalid) { @@ -1292,24 +1293,24 @@ static int igraph_i_lad_checkLAD(int u, int v, Tdomain* D, Tgraph* Gp, Tgraph* G igraph_vector_int_destroy(&nbComp); IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } /* ---------------------------------------------------------*/ /* Coming from main.c */ /* ---------------------------------------------------------*/ -static int igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, +static igraph_error_t 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; + igraph_integer_t u, v, i, oldNbVal; + igraph_integer_t invalid; bool result2; while (!igraph_i_lad_toFilterEmpty(D)) { while (!igraph_i_lad_toFilterEmpty(D)) { - u = igraph_i_lad_nextToFilter(D, (int) (Gp->nbVertices)); + u = igraph_i_lad_nextToFilter(D, Gp->nbVertices); oldNbVal = VECTOR(D->nbVal)[u]; i = VECTOR(D->firstVal)[u]; while (i < VECTOR(D->firstVal)[u] + VECTOR(D->nbVal)[u]) { @@ -1323,36 +1324,37 @@ static int igraph_i_lad_filter(bool induced, Tdomain* D, Tgraph* Gp, Tgraph* Gt, IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result2)); if (!result2) { *result = false; - return 0; + return IGRAPH_SUCCESS; } } } if ((VECTOR(D->nbVal)[u] == 1) && (oldNbVal > 1) && (!igraph_i_lad_matchVertex(u, induced, D, Gp, Gt))) { - *result = false; return 0; + *result = false; + return IGRAPH_SUCCESS; } if (VECTOR(D->nbVal)[u] == 0) { *result = false; - return 0; + return IGRAPH_SUCCESS; } } igraph_i_lad_ensureGACallDiff(induced, Gp, Gt, D, &invalid); if (invalid) { *result = false; - return 0; + return IGRAPH_SUCCESS; } } *result = true; - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, +static igraph_error_t igraph_i_lad_solve(igraph_integer_t 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, + igraph_integer_t *invalid, igraph_bool_t *iso, + igraph_vector_int_t *vec, igraph_vector_int_t *map, igraph_vector_int_list_t *maps, + igraph_integer_t *nbNodes, igraph_integer_t *nbFail, igraph_integer_t *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; @@ -1360,12 +1362,11 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, return false if CPU time limit exceeded before the search is completed, return true otherwise */ - int u, v, minDom, i; - int* nbVal; - int* globalMatching; + igraph_integer_t u, v, minDom, i; + igraph_integer_t* nbVal; + igraph_integer_t* globalMatching; clock_t end = clock(); - igraph_vector_t *vec; - int* val; + igraph_integer_t* val; bool result; (*nbNodes)++; @@ -1376,8 +1377,8 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, } /* Allocate memory */ - ALLOC_ARRAY_IN_HISTORY(nbVal, Gp->nbVertices, int, alloc_history); - ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, int, alloc_history); + ALLOC_ARRAY_IN_HISTORY(nbVal, Gp->nbVertices, igraph_integer_t, alloc_history); + ALLOC_ARRAY_IN_HISTORY(globalMatching, Gp->nbVertices, igraph_integer_t, alloc_history); IGRAPH_CHECK(igraph_i_lad_filter(induced, D, Gp, Gt, &result)); if (!result) { @@ -1406,25 +1407,18 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, *iso = 1; } (*nbSol)++; - if (map && igraph_vector_size(map) == 0) { - IGRAPH_CHECK(igraph_vector_resize(map, Gp->nbVertices)); + if (map && igraph_vector_int_size(map) == 0) { + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_resize(vec, Gp->nbVertices)); 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_CHECK(igraph_vector_int_list_push_back_copy(maps, vec)); } igraph_i_lad_resetToFilter(D); *invalid = 0; @@ -1432,7 +1426,7 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, } /* save the domain of minDom to iterate on its values */ - ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], int, alloc_history); + ALLOC_ARRAY_IN_HISTORY(val, VECTOR(D->nbVal)[minDom], igraph_integer_t, alloc_history); for (i = 0; i < VECTOR(D->nbVal)[minDom]; i++) { val[i] = VECTOR(D->val)[ VECTOR(D->firstVal)[minDom] + i ]; } @@ -1448,7 +1442,7 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, igraph_i_lad_resetToFilter(D); } else { IGRAPH_CHECK(igraph_i_lad_solve(timeLimit, firstSol, induced, - D, Gp, Gt, invalid, iso, map, maps, + D, Gp, Gt, invalid, iso, vec, map, maps, nbNodes, nbFail, nbSol, begin, alloc_history)); } @@ -1471,7 +1465,7 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, igraph_free(nbVal); igraph_vector_ptr_pop_back(alloc_history); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1515,20 +1509,20 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, * \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 + * vector, then it must contain pointers to \ref igraph_vector_int_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 + * 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. + * pointer, then the boolean is set to \c true if a subgraph + * isomorphism is found, and to \c false 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. + * (in vertex ID order) from the pattern graph. + * \param maps Pointer to a list of integer vectors or a null pointer. If not + * a null pointer, then all subgraph isomorphisms are stored in the + * vector list, in \ref igraph_vector_int_t objects. * \param induced Boolean, whether to search for induced matching * subgraphs. * \param time_limit Processor time limit in seconds. Supply zero @@ -1543,32 +1537,34 @@ static int igraph_i_lad_solve(int timeLimit, bool firstSol, bool induced, * \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) { +igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, + const igraph_vector_int_list_t *domains, + igraph_bool_t *iso, igraph_vector_int_t *map, + igraph_vector_int_list_t *maps, + igraph_bool_t induced, igraph_integer_t time_limit) { bool firstSol = maps == 0; bool initialDomains = domains != 0; Tgraph Gp, Gt; Tdomain D; - int invalidDomain; - int u, nbToMatch = 0; + igraph_integer_t invalidDomain; + igraph_integer_t u, nbToMatch = 0; igraph_vector_int_t toMatch; + /* Helper vector in which we build the current subisomorphism mapping */ + igraph_vector_int_t vec; /* Number of nodes in the search tree */ - int nbNodes = 0; + igraph_integer_t nbNodes = 0; /* number of failed nodes in the search tree */ - int nbFail = 0; + igraph_integer_t nbFail = 0; /* number of solutions found */ - int nbSol = 0; + igraph_integer_t 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_ERROR("Please specify at least one of `iso', `map' or `maps'", IGRAPH_EINVAL); } @@ -1577,17 +1573,17 @@ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, "or vice versa", IGRAPH_EINVAL); } if (time_limit <= 0) { - time_limit = INT_MAX; + time_limit = IGRAPH_INTEGER_MAX; } if (iso) { *iso = (igraph_vcount(pattern) == 0); } if (map) { - igraph_vector_clear(map); + igraph_vector_int_clear(map); } if (maps) { - igraph_vector_ptr_clear(maps); + igraph_vector_int_list_clear(maps); } if (igraph_vcount(pattern) == 0) { @@ -1595,6 +1591,8 @@ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, return IGRAPH_SUCCESS; } + IGRAPH_VECTOR_INT_INIT_FINALLY(&vec, 0); + IGRAPH_CHECK(igraph_i_lad_createGraph(pattern, &Gp)); IGRAPH_FINALLY(igraph_i_lad_destroyGraph, &Gp); @@ -1612,8 +1610,8 @@ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, goto exit2; } - IGRAPH_CHECK(igraph_i_lad_updateMatching((int) (Gp.nbVertices), - (int) (Gt.nbVertices), + IGRAPH_CHECK(igraph_i_lad_updateMatching(Gp.nbVertices, + Gt.nbVertices, &D.nbVal, &D.firstVal, &D.val, &D.globalMatchingP, &invalidDomain)); @@ -1651,7 +1649,7 @@ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, 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, + &Gp, &Gt, &invalidDomain, iso, &vec, map, maps, &nbNodes, &nbFail, &nbSol, &begin, &alloc_history)); @@ -1668,7 +1666,8 @@ int igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t *target, igraph_i_lad_destroyGraph(&Gt); igraph_i_lad_destroyGraph(&Gp); - IGRAPH_FINALLY_CLEAN(2); + igraph_vector_int_destroy(&vec); + IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/isomorphism/queries.c b/src/vendor/cigraph/src/isomorphism/queries.c index 2f55cab7706..1337fd8e3a7 100644 --- a/src/vendor/cigraph/src/isomorphism/queries.c +++ b/src/vendor/cigraph/src/isomorphism/queries.c @@ -46,18 +46,21 @@ * 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(). - * + * Finally, the isomorphism classes of all directed graphs with three and + * four vertices and all undirected graphs with 3-6 vertices are precomputed + * and stored in igraph, so for these small graphs there is a separate fast + * path in the code that does not use more complex, generic isomorphism + * algorithms. */ +static igraph_error_t igraph_i_isomorphic_small( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +); + /** * \function igraph_isomorphic - * \brief Decides whether two graphs are isomorphic + * \brief Are two graphs 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 @@ -66,19 +69,19 @@ * 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 one of the graphs has multi-edges then both graphs are + * simplified and colorized using \ref igraph_simplify_and_colorize() and sent to VF2. * \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. + * and edges it returns with \c false. + * \oli Otherwise, if the \ref igraph_isoclass() function supports both + * graphs (which is true for directed graphs with 3 and 4 vertices, and + * undirected graphs with 3-6 vertices), an O(1) algorithm is used with + * precomputed data. * \oli Otherwise Bliss is used, see \ref igraph_isomorphic_bliss(). * \endolist * @@ -87,57 +90,90 @@ * * \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. + * \param iso Pointer to a logical variable, will be set to \c true + * if the two graphs are isomorphic, and \c false 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_error_t 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_integer_t nodes1 = igraph_vcount(graph1), nodes2 = igraph_vcount(graph2); + igraph_integer_t 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; + if (dir1 != dir2) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs for isomorphism.", IGRAPH_EINVAL); + } + 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); + igraph_t r1; + igraph_t r2; + igraph_vector_int_t vc1; + igraph_vector_int_t vc2; + igraph_vector_int_t ec1; + igraph_vector_int_t ec2; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vc1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vc2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ec1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ec2, 0); + IGRAPH_CHECK(igraph_simplify_and_colorize(graph1, &r1, &vc1, &ec1)); + IGRAPH_FINALLY(igraph_destroy, &r1); + IGRAPH_CHECK(igraph_simplify_and_colorize(graph2, &r2, &vc2, &ec2)); + IGRAPH_FINALLY(igraph_destroy, &r2); + IGRAPH_CHECK(igraph_isomorphic_vf2(&r1, &r2, &vc1, &vc2, &ec1, &ec2, iso, + NULL, NULL, NULL, NULL, NULL)); + igraph_destroy(&r2); + igraph_destroy(&r1); + igraph_vector_int_destroy(&ec2); + igraph_vector_int_destroy(&ec1); + igraph_vector_int_destroy(&vc2); + igraph_vector_int_destroy(&vc1); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; } - 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) { + if (nodes1 != nodes2 || edges1 != edges2) { + *iso = false; + } else if (nodes1 >= 3 && nodes1 <= (dir1 ? 4 : 6)) { 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)); + IGRAPH_CHECK(igraph_i_isomorphic_small(graph1, graph2, iso)); } else { IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, - 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 0, 0)); + NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL)); } } else { IGRAPH_CHECK(igraph_isomorphic_bliss(graph1, graph2, NULL, NULL, iso, - 0, 0, /*sh=*/ IGRAPH_BLISS_FL, 0, 0)); + NULL, NULL, /*sh=*/ IGRAPH_BLISS_FL, NULL, NULL)); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_isomorphic_34 - * Graph isomorphism for 3-4 vertices + * \brief Graph isomorphism for 3-4 vertices (deprecated). + * + * \deprecated-by igraph_isomorphic 0.10.0 + * + * If you really care about performance and you \em know for sure that your + * input graphs are simple and have either 3 or 4 vertices for directed graphs, + * or 3-6 vertices for undirected graphs, you can compare their isomorphism + * classes obtained from \ref igraph_isoclass() directly instead of calling + * \ref igraph_isomorphic(); this saves the cost of checking whether the graphs + * do not contain multiple edges or self-loops. * - * 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. @@ -146,14 +182,37 @@ int igraph_isomorphic(const igraph_t *graph1, const igraph_t *graph2, * * Time complexity: O(1). */ -int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, - igraph_bool_t *iso) { +igraph_error_t igraph_isomorphic_34( + const igraph_t *graph1, const igraph_t *graph2, igraph_bool_t *iso +) { + return igraph_i_isomorphic_small(graph1, graph2, iso); +} +/** + * \function igraph_i_isomorphic_small + * \brief Graph isomorphism for small graphs. + * + * This function uses precomputed indices to decide isomorphism + * problems for directed graphs with only 3 or 4 vertices, or for undirected + * graphs with 3, 4, 5 or 6 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). + */ +igraph_error_t igraph_i_isomorphic_small( + 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; + return IGRAPH_SUCCESS; } /** @@ -177,7 +236,7 @@ int igraph_isomorphic_34(const igraph_t *graph1, const igraph_t *graph2, * * Time complexity: exponential. */ -int igraph_subisomorphic(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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/vendor/cigraph/src/isomorphism/vf2.c b/src/vendor/cigraph/src/isomorphism/vf2.c index ade96f50f8b..50f991ef383 100644 --- a/src/vendor/cigraph/src/isomorphism/vf2.c +++ b/src/vendor/cigraph/src/isomorphism/vf2.c @@ -25,8 +25,8 @@ #include "igraph_adjlist.h" #include "igraph_interface.h" -#include "igraph_memory.h" #include "igraph_stack.h" +#include "igraph_structural.h" #include "core/interruption.h" @@ -53,8 +53,35 @@ * */ +static igraph_error_t igraph_i_perform_vf2_pre_checks( + const igraph_t* graph1, const igraph_t* graph2 +) { + igraph_bool_t has_loops; + + if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { + IGRAPH_ERROR("Cannot compare directed and undirected graphs", + IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_has_loop(graph1, &has_loops)); + if (!has_loops) { + IGRAPH_CHECK(igraph_has_loop(graph2, &has_loops)); + } + + if (has_loops) { + IGRAPH_ERROR("The VF2 algorithm does not support graphs with loop edges.", + IGRAPH_EINVAL); + } + + /* TODO: VF2 does not support graphs with multiple edges either, but we + * don't check for this as the check would be complex, comparable to + * the runtime of the algorithm itself */ + + return IGRAPH_SUCCESS; +} + /** - * \function igraph_isomorphic_function_vf2 + * \function igraph_get_isomorphisms_vf2_callback * The generic VF2 interface * * @@ -67,8 +94,11 @@ * \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 + * callback returns \c IGRAPH_SUCCESS, then the search is continued, + * otherwise it stops. \c IGRAPH_STOP as a return value can be used to + * indicate normal premature termination; any other return value will be + * treated as an igraph error code, making the caller function return the + * same error code as well. 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. @@ -105,37 +135,31 @@ * 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_error_t igraph_get_isomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph1); + igraph_integer_t no_of_edges = igraph_ecount(graph1); + igraph_vector_int_t mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_int_t in_1, in_2, out_1, out_2; + igraph_integer_t 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_integer_t matched_nodes = 0; + igraph_integer_t depth; + igraph_integer_t cand1, cand2; + igraph_integer_t last1, last2; + igraph_stack_int_t path; igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; - igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; - long int vsize; + igraph_vector_int_t indeg1, indeg2, outdeg1, outdeg2; + igraph_integer_t vsize; - if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { - IGRAPH_ERROR("Cannot compare directed and undirected graphs", - IGRAPH_EINVAL); - } + IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { IGRAPH_WARNING("Only one graph is vertex-colored, vertex colors will be ignored"); @@ -149,7 +173,7 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph if (no_of_nodes != igraph_vcount(graph2) || no_of_edges != igraph_ecount(graph2)) { - return 0; + return IGRAPH_SUCCESS; } if (vertex_color1) { @@ -168,11 +192,11 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph /* Check color distribution */ if (vertex_color1) { - int ret = 0; + igraph_bool_t ret = false; igraph_vector_int_t tmp1, tmp2; - IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, vertex_color1)); + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, vertex_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); - IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, vertex_color2)); + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp2, vertex_color2)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); igraph_vector_int_sort(&tmp1); igraph_vector_int_sort(&tmp2); @@ -181,17 +205,17 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { - return 0; + return IGRAPH_SUCCESS; } } /* Check edge color distribution */ if (edge_color1) { - int ret = 0; + igraph_bool_t ret = false; igraph_vector_int_t tmp1, tmp2; - IGRAPH_CHECK(igraph_vector_int_copy(&tmp1, edge_color1)); + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp1, edge_color1)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp1); - IGRAPH_CHECK(igraph_vector_int_copy(&tmp2, edge_color2)); + IGRAPH_CHECK(igraph_vector_int_init_copy(&tmp2, edge_color2)); IGRAPH_FINALLY(igraph_vector_int_destroy, &tmp2); igraph_vector_int_sort(&tmp1); igraph_vector_int_sort(&tmp2); @@ -200,32 +224,32 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph igraph_vector_int_destroy(&tmp2); IGRAPH_FINALLY_CLEAN(2); if (ret) { - return 0; + return IGRAPH_SUCCESS; } } if (map12) { core_1 = map12; - IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(core_1, no_of_nodes)); } else { - IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes); } - igraph_vector_fill(core_1, -1); + igraph_vector_int_fill(core_1, -1); if (map21) { core_2 = map21; - IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes)); - igraph_vector_null(core_2); + IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes)); + igraph_vector_int_null(core_2); } else { - IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes); + IGRAPH_VECTOR_INT_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_vector_int_fill(core_2, -1); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_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)); @@ -234,12 +258,12 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph 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_VECTOR_INT_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); - IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes * 2)); + IGRAPH_CHECK(igraph_stack_int_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(), @@ -251,7 +275,7 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph depth = 0; last1 = -1; last2 = -1; while (depth >= 0) { - long int i; + igraph_integer_t i; IGRAPH_ALLOW_INTERRUPTION(); @@ -333,8 +357,8 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph /**************************************************************/ /* 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); + last2 = igraph_stack_int_pop(&path); + last1 = igraph_stack_int_pop(&path); matched_nodes -= 1; VECTOR(*core_1)[last1] = -1; VECTOR(*core_2)[last2] = -1; @@ -352,37 +376,48 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph out_2_size += 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); + inneis_1 = igraph_lazy_adjlist_get(&inadj1, last1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = 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); + + outneis_1 = igraph_lazy_adjlist_get(&outadj1, last1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = 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); + + inneis_2 = igraph_lazy_adjlist_get(&inadj2, last2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = 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); + + outneis_2 = igraph_lazy_adjlist_get(&outadj2, last2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = VECTOR(*outneis_2)[i]; if (VECTOR(out_2)[node] == depth) { VECTOR(out_2)[node] = 0; out_2_size -= 1; @@ -396,48 +431,50 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph } 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); + igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = false; + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + if (VECTOR(indeg1)[cand1] != VECTOR(indeg2)[cand2] || VECTOR(outdeg1)[cand1] != VECTOR(outdeg2)[cand2]) { - end = 1; + end = true; } if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { - end = 1; + end = true; } - if (node_compat_fn && !node_compat_fn(graph1, graph2, - (igraph_integer_t) cand1, - (igraph_integer_t) cand2, arg)) { - end = 1; + if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { + end = true; } vsize = igraph_vector_int_size(inneis_1); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = VECTOR(*inneis_1)[i]; if (VECTOR(*core_1)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_1)[node]; + igraph_integer_t node2 = VECTOR(*core_1)[node]; /* check if there is a node2->cand2 edge */ if (!igraph_vector_int_binsearch2(inneis_2, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, node, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node2, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -451,27 +488,25 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph } vsize = igraph_vector_int_size(outneis_1); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = VECTOR(*outneis_1)[i]; if (VECTOR(*core_1)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_1)[node]; + igraph_integer_t node2 = VECTOR(*core_1)[node]; /* check if there is a cand2->node2 edge */ if (!igraph_vector_int_binsearch2(outneis_2, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, cand1, node, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -485,27 +520,25 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph } vsize = igraph_vector_int_size(inneis_2); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = VECTOR(*inneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_2)[node]; + igraph_integer_t node2 = VECTOR(*core_2)[node]; /* check if there is a node2->cand1 edge */ if (!igraph_vector_int_binsearch2(inneis_1, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -519,27 +552,25 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph } vsize = igraph_vector_int_size(outneis_2); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = VECTOR(*outneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_2)[node]; + igraph_integer_t node2 = VECTOR(*core_2)[node]; /* check if there is a cand1->node2 edge */ if (!igraph_vector_int_binsearch2(outneis_1, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -555,8 +586,8 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph 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)); + IGRAPH_CHECK(igraph_stack_int_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_int_push(&path, cand2)); matched_nodes += 1; VECTOR(*core_1)[cand1] = cand2; VECTOR(*core_2)[cand2] = cand1; @@ -575,42 +606,54 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph out_2_size -= 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = 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); + + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = 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); + + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = 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); + + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = 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; @@ -620,36 +663,58 @@ int igraph_isomorphic_function_vf2(const igraph_t *graph1, const igraph_t *graph } if (matched_nodes == no_of_nodes && isohandler_fn) { - if (!isohandler_fn(core_1, core_2, arg)) { + igraph_error_t ret; + IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); + if (ret == IGRAPH_STOP) { break; } } } - igraph_vector_destroy(&outdeg2); - igraph_vector_destroy(&outdeg1); - igraph_vector_destroy(&indeg2); - igraph_vector_destroy(&indeg1); + igraph_vector_int_destroy(&outdeg2); + igraph_vector_int_destroy(&outdeg1); + igraph_vector_int_destroy(&indeg2); + igraph_vector_int_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_stack_int_destroy(&path); + igraph_vector_int_destroy(&out_2); + igraph_vector_int_destroy(&out_1); + igraph_vector_int_destroy(&in_2); + igraph_vector_int_destroy(&in_1); IGRAPH_FINALLY_CLEAN(13); if (!map21) { - igraph_vector_destroy(core_2); + igraph_vector_int_destroy(core_2); IGRAPH_FINALLY_CLEAN(1); } if (!map12) { - igraph_vector_destroy(core_1); + igraph_vector_int_destroy(core_1); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_isomorphic_function_vf2 + * \brief The generic VF2 interface (deprecated alias). + * + * \deprecated-by igraph_get_isomorphisms_vf2_callback 0.10.0 + */ +igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + return igraph_get_isomorphisms_vf2_callback( + graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, + map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg + ); } typedef struct { @@ -677,23 +742,24 @@ static igraph_bool_t igraph_i_isocompat_edge_cb( 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) { +static igraph_error_t igraph_i_isomorphic_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 */ + return IGRAPH_STOP; } /** * \function igraph_isomorphic_vf2 - * \brief Isomorphism via VF2 + * \brief Isomorphism via VF2. * * * This function performs the VF2 algorithm via calling \ref - * igraph_isomorphic_function_vf2(). + * igraph_get_isomorphisms_vf2_callback(). * * Note that this function cannot be used for * deciding subgraph isomorphism, use \ref igraph_subisomorphic_vf2() @@ -742,13 +808,13 @@ static igraph_bool_t igraph_i_isomorphic_vf2(igraph_vector_t *map12, * \example examples/simple/igraph_isomorphic_vf2.c */ -int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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_bool_t *iso, igraph_vector_int_t *map12, + igraph_vector_int_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -757,42 +823,42 @@ int igraph_isomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, 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, + IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, map12, map21, - (igraph_isohandler_t*) - igraph_i_isomorphic_vf2, + (igraph_isohandler_t*) igraph_i_isomorphic_vf2_cb, ncb, ecb, &data)); if (! *iso) { if (map12) { - igraph_vector_clear(map12); + igraph_vector_int_clear(map12); } if (map21) { - igraph_vector_clear(map21); + igraph_vector_int_clear(map21); } } - return 0; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_count_isomorphisms_vf2( - const igraph_vector_t *map12, - const igraph_vector_t *map21, - void *arg) { +static igraph_error_t igraph_i_count_isomorphisms_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 */ + return IGRAPH_SUCCESS; } /** * \function igraph_count_isomorphisms_vf2 - * Number of isomorphisms via VF2 + * \brief 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() + * graphs. It uses the generic \ref igraph_get_isomorphisms_vf2_callback() * 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. @@ -819,10 +885,12 @@ static igraph_bool_t igraph_i_count_isomorphisms_vf2( * \p edge_compat_fn. * \return Error code. * + * \sa igraph_count_automorphisms() + * * Time complexity: exponential. */ -int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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, @@ -838,50 +906,23 @@ int igraph_count_isomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2 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, + IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, - (igraph_isohandler_t*) - igraph_i_count_isomorphisms_vf2, + (igraph_isohandler_t*) igraph_i_count_isomorphisms_vf2_cb, 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); - } + return IGRAPH_SUCCESS; } -static int igraph_i_get_isomorphisms_vf2_inner( - const igraph_vector_t *map12, - const igraph_vector_t *map21, - void *arg) { +static igraph_error_t igraph_i_store_mapping_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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_vector_int_list_t *ptrvector = data->arg; 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; + return igraph_vector_int_list_push_back_copy(ptrvector, map21); } /** @@ -889,7 +930,7 @@ static igraph_bool_t igraph_i_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() + * graphs. It uses the \ref igraph_get_isomorphisms_vf2_callback() * 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. @@ -907,15 +948,10 @@ static igraph_bool_t igraph_i_get_isomorphisms_vf2( * 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 maps Pointer to a list of integer vectors. On return it is empty if + * the input graphs are not isomorphic. Otherwise it contains pointers to + * \ref igraph_vector_int_t objects, each vector is an + * isomorphic mapping of \p graph2 to \p graph1. * \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. @@ -929,13 +965,13 @@ static igraph_bool_t igraph_i_get_isomorphisms_vf2( * Time complexity: exponential. */ -int igraph_get_isomorphisms_vf2(const igraph_t *graph1, +igraph_error_t 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_vector_int_list_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -944,24 +980,21 @@ int igraph_get_isomorphisms_vf2(const igraph_t *graph1, 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, + igraph_vector_int_list_clear(maps); + IGRAPH_CHECK(igraph_get_isomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, NULL, NULL, - (igraph_isohandler_t*) - igraph_i_get_isomorphisms_vf2, + (igraph_isohandler_t*) igraph_i_store_mapping_vf2_cb, ncb, ecb, &data)); - IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } /** - * \function igraph_subisomorphic_function_vf2 - * Generic VF2 function for subgraph isomorphism problems + * \function igraph_get_subisomorphisms_vf2_callback + * \brief Generic VF2 function for subgraph isomorphism problems. * - * This function is the pair of \ref igraph_isomorphic_function_vf2(), + * This function is the pair of \ref igraph_get_isomorphisms_vf2_callback(), * 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. @@ -991,9 +1024,10 @@ int igraph_get_isomorphisms_vf2(const igraph_t *graph1, * 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. + * isomorphism is found. If the function returns \c IGRAPH_SUCCESS, + * then the search is continued. If the function returns \c IGRAPH_STOP, + * the search is terminated normally. Any other value is treated as an + * igraph error code. * \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. @@ -1007,44 +1041,36 @@ int igraph_get_isomorphisms_vf2(const igraph_t *graph1, * 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), +igraph_error_t igraph_get_subisomorphisms_vf2_callback( + 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + + igraph_integer_t no_of_nodes1 = igraph_vcount(graph1), no_of_nodes2 = igraph_vcount(graph2); - long int no_of_edges1 = igraph_ecount(graph1), + igraph_integer_t 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 mycore_1, mycore_2, *core_1 = &mycore_1, *core_2 = &mycore_2; + igraph_vector_int_t in_1, in_2, out_1, out_2; + igraph_integer_t 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_integer_t matched_nodes = 0; + igraph_integer_t depth; + igraph_integer_t cand1, cand2; + igraph_integer_t last1, last2; + igraph_stack_int_t path; igraph_lazy_adjlist_t inadj1, inadj2, outadj1, outadj2; - igraph_vector_t indeg1, indeg2, outdeg1, outdeg2; - long int vsize; + igraph_vector_int_t indeg1, indeg2, outdeg1, outdeg2; + igraph_integer_t vsize; - if (igraph_is_directed(graph1) != igraph_is_directed(graph2)) { - IGRAPH_ERROR("Cannot compare directed and undirected graphs", - IGRAPH_EINVAL); - } + IGRAPH_CHECK(igraph_i_perform_vf2_pre_checks(graph1, graph2)); - if (no_of_nodes1 < no_of_nodes2 || - no_of_edges1 < no_of_edges2) { - return 0; + if (no_of_nodes1 < no_of_nodes2 || no_of_edges1 < no_of_edges2) { + return IGRAPH_SUCCESS; } if ( (vertex_color1 && !vertex_color2) || (!vertex_color1 && vertex_color2) ) { @@ -1083,24 +1109,24 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, if (map12) { core_1 = map12; - IGRAPH_CHECK(igraph_vector_resize(core_1, no_of_nodes1)); + IGRAPH_CHECK(igraph_vector_int_resize(core_1, no_of_nodes1)); } else { - IGRAPH_VECTOR_INIT_FINALLY(core_1, no_of_nodes1); + IGRAPH_VECTOR_INT_INIT_FINALLY(core_1, no_of_nodes1); } - igraph_vector_fill(core_1, -1); + igraph_vector_int_fill(core_1, -1); if (map21) { core_2 = map21; - IGRAPH_CHECK(igraph_vector_resize(core_2, no_of_nodes2)); + IGRAPH_CHECK(igraph_vector_int_resize(core_2, no_of_nodes2)); } else { - IGRAPH_VECTOR_INIT_FINALLY(core_2, no_of_nodes2); + IGRAPH_VECTOR_INT_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_vector_int_fill(core_2, -1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_1, no_of_nodes1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&in_2, no_of_nodes2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_1, no_of_nodes1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&out_2, no_of_nodes2); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_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)); @@ -1109,12 +1135,12 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, 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_VECTOR_INT_INIT_FINALLY(&indeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&indeg2, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outdeg2, 0); - IGRAPH_CHECK(igraph_stack_reserve(&path, no_of_nodes2 * 2)); + IGRAPH_CHECK(igraph_stack_int_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(), @@ -1126,7 +1152,7 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, depth = 0; last1 = -1; last2 = -1; while (depth >= 0) { - long int i; + igraph_integer_t i; IGRAPH_ALLOW_INTERRUPTION(); @@ -1208,8 +1234,8 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, /**************************************************************/ /* 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); + last2 = igraph_stack_int_pop(&path); + last1 = igraph_stack_int_pop(&path); matched_nodes -= 1; VECTOR(*core_1)[last1] = -1; VECTOR(*core_2)[last2] = -1; @@ -1227,37 +1253,48 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, out_2_size += 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) last1); + inneis_1 = igraph_lazy_adjlist_get(&inadj1, last1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = 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); + + outneis_1 = igraph_lazy_adjlist_get(&outadj1, last1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = 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); + + inneis_2 = igraph_lazy_adjlist_get(&inadj2, last2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = 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); + + outneis_2 = igraph_lazy_adjlist_get(&outadj2, last2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = VECTOR(*outneis_2)[i]; if (VECTOR(out_2)[node] == depth) { VECTOR(out_2)[node] = 0; out_2_size -= 1; @@ -1271,28 +1308,32 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, } 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); + igraph_integer_t xin1 = 0, xin2 = 0, xout1 = 0, xout2 = 0; + igraph_bool_t end = false; + + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + if (VECTOR(indeg1)[cand1] < VECTOR(indeg2)[cand2] || VECTOR(outdeg1)[cand1] < VECTOR(outdeg2)[cand2]) { - end = 1; + end = true; } if (vertex_color1 && VECTOR(*vertex_color1)[cand1] != VECTOR(*vertex_color2)[cand2]) { - end = 1; + end = true; } - if (node_compat_fn && !node_compat_fn(graph1, graph2, - (igraph_integer_t) cand1, - (igraph_integer_t) cand2, arg)) { - end = 1; + if (node_compat_fn && !node_compat_fn(graph1, graph2, cand1, cand2, arg)) { + end = true; } vsize = igraph_vector_int_size(inneis_1); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = VECTOR(*inneis_1)[i]; if (VECTOR(*core_1)[node] < 0) { if (VECTOR(in_1)[node] != 0) { xin1++; @@ -1304,7 +1345,7 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, } vsize = igraph_vector_int_size(outneis_1); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = VECTOR(*outneis_1)[i]; if (VECTOR(*core_1)[node] < 0) { if (VECTOR(in_1)[node] != 0) { xin1++; @@ -1316,27 +1357,25 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, } vsize = igraph_vector_int_size(inneis_2); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = VECTOR(*inneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_2)[node]; + igraph_integer_t node2 = VECTOR(*core_2)[node]; /* check if there is a node2->cand1 edge */ if (!igraph_vector_int_binsearch2(inneis_1, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, node2, cand1, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, node, cand2, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -1350,27 +1389,25 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, } vsize = igraph_vector_int_size(outneis_2); for (i = 0; !end && i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = VECTOR(*outneis_2)[i]; if (VECTOR(*core_2)[node] >= 0) { - long int node2 = (long int) VECTOR(*core_2)[node]; + igraph_integer_t node2 = VECTOR(*core_2)[node]; /* check if there is a cand1->node2 edge */ if (!igraph_vector_int_binsearch2(outneis_1, node2)) { - end = 1; + end = true; } 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; + igraph_get_eid(graph1, &eid1, cand1, node2, IGRAPH_DIRECTED, + /*error=*/ true); + igraph_get_eid(graph2, &eid2, cand2, node, IGRAPH_DIRECTED, + /*error=*/ true); + if (edge_color1 && VECTOR(*edge_color1)[eid1] != + VECTOR(*edge_color2)[eid2]) { + end = true; } if (edge_compat_fn && !edge_compat_fn(graph1, graph2, eid1, eid2, arg)) { - end = 1; + end = true; } } } else { @@ -1386,8 +1423,8 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, 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)); + IGRAPH_CHECK(igraph_stack_int_push(&path, cand1)); + IGRAPH_CHECK(igraph_stack_int_push(&path, cand2)); matched_nodes += 1; VECTOR(*core_1)[cand1] = cand2; VECTOR(*core_2)[cand2] = cand1; @@ -1406,42 +1443,54 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, out_2_size -= 1; } - inneis_1 = igraph_lazy_adjlist_get(&inadj1, (igraph_integer_t) cand1); + inneis_1 = igraph_lazy_adjlist_get(&inadj1, cand1); + IGRAPH_CHECK_OOM(inneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_1)[i]; + igraph_integer_t node = 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); + + outneis_1 = igraph_lazy_adjlist_get(&outadj1, cand1); + IGRAPH_CHECK_OOM(outneis_1, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_1); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_1)[i]; + igraph_integer_t node = 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); + + inneis_2 = igraph_lazy_adjlist_get(&inadj2, cand2); + IGRAPH_CHECK_OOM(inneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(inneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*inneis_2)[i]; + igraph_integer_t node = 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); + + outneis_2 = igraph_lazy_adjlist_get(&outadj2, cand2); + IGRAPH_CHECK_OOM(outneis_2, "Failed to query neighbors."); + vsize = igraph_vector_int_size(outneis_2); for (i = 0; i < vsize; i++) { - long int node = (long int) VECTOR(*outneis_2)[i]; + igraph_integer_t node = 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; @@ -1451,47 +1500,69 @@ int igraph_subisomorphic_function_vf2(const igraph_t *graph1, } if (matched_nodes == no_of_nodes2 && isohandler_fn) { - if (!isohandler_fn(core_1, core_2, arg)) { + igraph_error_t ret; + IGRAPH_CHECK_CALLBACK(isohandler_fn(core_1, core_2, arg), &ret); + if (ret == IGRAPH_STOP) { break; } } } - igraph_vector_destroy(&outdeg2); - igraph_vector_destroy(&outdeg1); - igraph_vector_destroy(&indeg2); - igraph_vector_destroy(&indeg1); + igraph_vector_int_destroy(&outdeg2); + igraph_vector_int_destroy(&outdeg1); + igraph_vector_int_destroy(&indeg2); + igraph_vector_int_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_stack_int_destroy(&path); + igraph_vector_int_destroy(&out_2); + igraph_vector_int_destroy(&out_1); + igraph_vector_int_destroy(&in_2); + igraph_vector_int_destroy(&in_1); IGRAPH_FINALLY_CLEAN(13); if (!map21) { - igraph_vector_destroy(core_2); + igraph_vector_int_destroy(core_2); IGRAPH_FINALLY_CLEAN(1); } if (!map12) { - igraph_vector_destroy(core_1); + igraph_vector_int_destroy(core_1); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_subisomorphic_vf2( - const igraph_vector_t *map12, - const igraph_vector_t *map21, - void *arg) { +static igraph_error_t igraph_i_subisomorphic_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 */ + return IGRAPH_STOP; +} + +/** + * \function igraph_subisomorphic_function_vf2 + * \brief Generic VF2 function for subgraph isomorphism problems (deprecated alias). + * + * \deprecated-by igraph_get_subisomorphisms_vf2_callback 0.10.0 + */ +igraph_error_t 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_int_t *map12, igraph_vector_int_t *map21, + igraph_isohandler_t *isohandler_fn, igraph_isocompat_t *node_compat_fn, + igraph_isocompat_t *edge_compat_fn, void *arg +) { + return igraph_get_subisomorphisms_vf2_callback( + graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, + map12, map21, isohandler_fn, node_compat_fn, edge_compat_fn, arg + ); } /** @@ -1499,7 +1570,7 @@ static igraph_bool_t igraph_i_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(). + * graph2. It uses \ref igraph_get_subisomorphisms_vf2_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 @@ -1537,13 +1608,13 @@ static igraph_bool_t igraph_i_subisomorphic_vf2( * Time complexity: exponential. */ -int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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_bool_t *iso, igraph_vector_int_t *map12, + igraph_vector_int_t *map21, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -1553,33 +1624,32 @@ int igraph_subisomorphic_vf2(const igraph_t *graph1, const igraph_t *graph2, igraph_isocompat_t *ecb = edge_compat_fn ? igraph_i_isocompat_edge_cb : 0; *iso = 0; - IGRAPH_CHECK(igraph_subisomorphic_function_vf2(graph1, graph2, + IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, map12, map21, - (igraph_isohandler_t *) - igraph_i_subisomorphic_vf2, + (igraph_isohandler_t *) igraph_i_subisomorphic_vf2_cb, ncb, ecb, &data)); if (! *iso) { if (map12) { - igraph_vector_clear(map12); + igraph_vector_int_clear(map12); } if (map21) { - igraph_vector_clear(map21); + igraph_vector_int_clear(map21); } } - return 0; + return IGRAPH_SUCCESS; } -static igraph_bool_t igraph_i_count_subisomorphisms_vf2( - const igraph_vector_t *map12, - const igraph_vector_t *map21, - void *arg) { +static igraph_error_t igraph_i_count_subisomorphisms_vf2_cb( + const igraph_vector_int_t *map12, const igraph_vector_int_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 */ + return IGRAPH_SUCCESS; } /** @@ -1587,8 +1657,7 @@ static igraph_bool_t igraph_i_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(). + * \p graph2. This function uses \ref igraph_get_subisomorphisms_vf2_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 @@ -1621,7 +1690,7 @@ static igraph_bool_t igraph_i_count_subisomorphisms_vf2( * Time complexity: exponential. */ -int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *graph2, +igraph_error_t 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, @@ -1637,59 +1706,22 @@ int igraph_count_subisomorphisms_vf2(const igraph_t *graph1, const igraph_t *gra 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, + IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, 0, 0, - (igraph_isohandler_t*) - igraph_i_count_subisomorphisms_vf2, + (igraph_isohandler_t*) igraph_i_count_subisomorphisms_vf2_cb, 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. + * igraph_get_subisomorphisms_vf2_callback() 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 @@ -1707,14 +1739,9 @@ static igraph_bool_t igraph_i_get_subisomorphisms_vf2( * 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 maps Pointer to a list of integer vectors. On return it contains + * pointers to \ref igraph_vector_int_t objects, each vector is an isomorphic + * mapping of \p graph2 to a subgraph of \p graph1. * \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. @@ -1728,13 +1755,13 @@ static igraph_bool_t igraph_i_get_subisomorphisms_vf2( * Time complexity: exponential. */ -int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, +igraph_error_t 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_vector_int_list_t *maps, igraph_isocompat_t *node_compat_fn, igraph_isocompat_t *edge_compat_fn, void *arg) { @@ -1743,15 +1770,13 @@ int igraph_get_subisomorphisms_vf2(const igraph_t *graph1, 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, + igraph_vector_int_list_clear(maps); + IGRAPH_CHECK(igraph_get_subisomorphisms_vf2_callback(graph1, graph2, vertex_color1, vertex_color2, edge_color1, edge_color2, NULL, NULL, - (igraph_isohandler_t*) - igraph_i_get_subisomorphisms_vf2, + (igraph_isohandler_t*) igraph_i_store_mapping_vf2_cb, ncb, ecb, &data)); - IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/circular.c b/src/vendor/cigraph/src/layout/circular.c index dc2019a147b..ed1bad318ca 100644 --- a/src/vendor/cigraph/src/layout/circular.c +++ b/src/vendor/cigraph/src/layout/circular.c @@ -31,26 +31,24 @@ /** * \ingroup layout * \function igraph_layout_circle - * \brief Places the vertices uniformly on a circle, in the order of vertex ids. + * \brief Places the vertices uniformly on a circle in arbitrary order. * * \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. + * \ref igraph_vss_all() here to place vertices in the + * order of their vertex IDs. * \return Error code. * - * Time complexity: O(|V|), the - * number of vertices. + * Time complexity: O(|V|), the number of vertices. */ -int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t order) { - long int no_of_nodes = igraph_vcount(graph); + const igraph_integer_t 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)); @@ -58,16 +56,16 @@ int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, 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_CHECK(igraph_vit_create(graph, order, &vit)); + for (igraph_integer_t 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); + igraph_integer_t idx = IGRAPH_VIT_GET(vit); MATRIX(*res, idx, 0) = cos(phi); MATRIX(*res, idx, 1) = sin(phi); } igraph_vit_destroy(&vit); - return 0; + return IGRAPH_SUCCESS; } /** @@ -83,26 +81,22 @@ int igraph_layout_circle(const igraph_t *graph, igraph_matrix_t *res, * minus 1. * \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. + * 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) { +igraph_error_t igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t center, const igraph_vector_int_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; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); if (no_of_nodes > 0 && (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) { + if (order && igraph_vector_int_size(order) != no_of_nodes) { IGRAPH_ERROR("Invalid order vector length.", IGRAPH_EINVAL); } @@ -111,34 +105,40 @@ int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, if (no_of_nodes == 1) { MATRIX(*res, 0, 0) = MATRIX(*res, 0, 1) = 0.0; } else if (no_of_nodes > 1) { - 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; + igraph_real_t phi = 0.0; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t node = order ? 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) { + if (node != center) { MATRIX(*res, node, 0) = cos(phi); MATRIX(*res, node, 1) = sin(phi); - phi += step; + phi += 2.0 * M_PI / (no_of_nodes - 1); } else { MATRIX(*res, node, 0) = MATRIX(*res, node, 1) = 0.0; } } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_layout_sphere * \brief Places vertices (more or less) uniformly on a sphere. * + * The vertices are placed with approximately equal spacing on a spiral + * wrapped around a sphere, in the order of their vertex IDs. Vertices + * with consecutive vertex IDs are placed near each other. + * * * 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. + * 5--11. https://doi.org/10.1007/BF03024331 * * \param graph Pointer to an initialized graph object. * \param res Pointer to an initialized matrix object. This will @@ -150,39 +150,39 @@ int igraph_layout_star(const igraph_t *graph, igraph_matrix_t *res, * * Time complexity: O(|V|), the number of vertices in the graph. */ -int igraph_layout_sphere(const igraph_t *graph, igraph_matrix_t *res) { +igraph_error_t 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; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_real_t sqrt_no_of_nodes = sqrt(no_of_nodes); 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; - } + igraph_real_t phi = 0; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t r, z; + + /* The first and last point are handled separately to avoid + * division by zero or 1-z*z becoming slightly negative due + * to roundoff errors. */ + if (i == 0) { + z = -1; r = 0; + } else if (i == no_of_nodes-1) { + z = 1; r = 0; + } else { + z = -1.0 + 2.0 * i / (no_of_nodes - 1); + r = sqrt(1 - z*z); + phi += 3.6 / (sqrt_no_of_nodes*r); + } + + igraph_real_t x = r*cos(phi); + igraph_real_t y = r*sin(phi); - 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; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/davidson_harel.c b/src/vendor/cigraph/src/layout/davidson_harel.c index 57b706f3bdc..b0ac699cc8b 100644 --- a/src/vendor/cigraph/src/layout/davidson_harel.c +++ b/src/vendor/cigraph/src/layout/davidson_harel.c @@ -26,45 +26,45 @@ #include "igraph_interface.h" #include "igraph_random.h" -#include "core/math.h" #include "core/interruption.h" +#include "core/math.h" #include "layout/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; +igraph_bool_t igraph_i_layout_segments_intersect(igraph_real_t p0_x, igraph_real_t p0_y, + igraph_real_t p1_x, igraph_real_t p1_y, + igraph_real_t p2_x, igraph_real_t p2_y, + igraph_real_t p3_x, igraph_real_t p3_y) { + igraph_real_t s1_x = p1_x - p0_x; + igraph_real_t s1_y = p1_y - p0_y; + igraph_real_t s2_x = p3_x - p2_x; + igraph_real_t s2_y = p3_y - p2_y; + + igraph_real_t 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; + return false; } 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; + return s >= 0 && s <= 1 && t >= 0 && t <= 1; } /* 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; +igraph_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real_t v_y, + igraph_real_t u1_x, igraph_real_t u1_y, + igraph_real_t u2_x, igraph_real_t u2_y) { + + igraph_real_t dx = u2_x - u1_x; + igraph_real_t dy = u2_y - u1_y; + igraph_real_t l2 = dx * dx + dy * dy; + igraph_real_t 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); } @@ -81,12 +81,13 @@ float igraph_i_layout_point_segment_dist2(float v_x, float v_y, /** * \function igraph_layout_davidson_harel - * Davidson-Harel layout algorithm + * \brief 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. + * https://doi.org/10.1145/234535.234538 * * * The algorithm uses simulated annealing and a sophisticated @@ -113,7 +114,8 @@ float igraph_i_layout_point_segment_dist2(float v_x, float v_y, * \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. + * value is max(10, log2(n)) where \c 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. @@ -138,7 +140,7 @@ float igraph_i_layout_point_segment_dist2(float v_x, float v_y, * */ -int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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, @@ -148,62 +150,58 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, 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_real_t 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_bool_t fine_tuning = false; + igraph_vector_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_real_t move_radius = width / 2; + igraph_real_t fine_tuning_factor = 0.01; + igraph_vector_int_t neis; + igraph_real_t 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); - } + igraph_real_t w_node_dist = weight_node_dist ; /* 1.0 */ + igraph_real_t w_borderlines = weight_border; /* 0.0 */ + igraph_real_t w_edge_lengths = weight_edge_lengths; /* 0.0001; */ + igraph_real_t w_edge_crossings = weight_edge_crossings; /* 1.0 */ + igraph_real_t w_node_edge_dist = weight_node_edge_dist; /* 0.2 */ + if (maxiter < 0) { - IGRAPH_ERROR("Number of iterations must be non-negative in " - "Davidson-Harel layout", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of iterations must not be negative for the 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); + IGRAPH_ERROR("Number of fine tuning iterations must not be negative for the 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); + IGRAPH_ERROR("Cooling factor must be in (0,1) for the Davidson-Harel layout.", IGRAPH_EINVAL); + } + if (use_seed) { + if (igraph_matrix_nrow(res) != no_nodes || igraph_matrix_ncol(res) != 2) { + IGRAPH_ERROR("Invalid start position matrix size in Davidson-Harel layout.", IGRAPH_EINVAL); + } + } else { + IGRAPH_CHECK(igraph_matrix_resize(res, no_nodes, 2)); } if (no_nodes == 0) { - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_CHECK(igraph_vector_int_init_seq(&perm, 0, no_nodes - 1)); + IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); 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_VECTOR_INIT_FINALLY(&try_x, no_tries); + IGRAPH_VECTOR_INIT_FINALLY(&try_y, no_tries); + IGRAPH_CHECK(igraph_vector_int_init_range(&try_idx, 0, no_tries)); IGRAPH_FINALLY(igraph_vector_int_destroy, &try_idx); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 100); + IGRAPH_VECTOR_INT_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; + for (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t 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) { @@ -220,9 +218,9 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } 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); + for (igraph_integer_t i = 0; i < no_nodes; i++) { + igraph_real_t x = MATRIX(*res, i, 0); + igraph_real_t y = MATRIX(*res, i, 1); if (x < min_x) { min_x = x; } else if (x > max_x) { @@ -236,39 +234,37 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } } - for (i = 0; i < no_tries; i++) { - float phi = 2 * M_PI / no_tries * i; + for (igraph_integer_t i = 0; i < no_tries; i++) { + double 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); - + for (igraph_integer_t round = 0; round < maxiter + fineiter; round++) { IGRAPH_ALLOW_INTERRUPTION(); + igraph_vector_int_shuffle(&perm); + 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); + igraph_real_t fx = fine_tuning_factor * (max_x - min_x); + igraph_real_t 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; + for (igraph_integer_t p = 0; p < no_nodes; p++) { 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]; + for (igraph_integer_t t = 0; t < no_tries; t++) { + igraph_real_t diff_energy = 0.0; + igraph_integer_t 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]; + igraph_real_t old_x = MATRIX(*res, v, 0); + igraph_real_t old_y = MATRIX(*res, v, 1); + igraph_real_t new_x = old_x + move_radius * VECTOR(try_x)[ti]; + igraph_real_t new_y = old_y + move_radius * VECTOR(try_y)[ti]; if (new_x < -width / 2) { new_x = -width / 2 - 1e-6; @@ -284,9 +280,8 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } if (w_node_dist != 0) { - igraph_integer_t u; - for (u = 0; u < no_nodes; u++) { - float odx, ody, odist2, dx, dy, dist2; + for (igraph_integer_t u = 0; u < no_nodes; u++) { + igraph_real_t odx, ody, odist2, dx, dy, dist2; if (u == v) { continue; } @@ -301,10 +296,10 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } 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; + igraph_real_t odx1 = width / 2 - old_x, odx2 = old_x + width / 2; + igraph_real_t ody1 = height / 2 - old_y, ody2 = old_y + height / 2; + igraph_real_t dx1 = width / 2 - new_x, dx2 = new_x + width / 2; + igraph_real_t dy1 = height / 2 - new_y, dy2 = new_y + height / 2; if (odx1 < 0) { odx1 = 2; } if (odx2 < 0) { @@ -334,34 +329,34 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } 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_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t len = igraph_vector_int_size(&neis); + for (igraph_integer_t 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; + igraph_real_t odx = old_x - MATRIX(*res, u, 0); + igraph_real_t ody = old_y - MATRIX(*res, u, 1); + igraph_real_t odist2 = odx * odx + ody * ody; + igraph_real_t dx = new_x - MATRIX(*res, u, 0); + igraph_real_t dy = new_y - MATRIX(*res, u, 1); + igraph_real_t 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 no = 0; + + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t len = igraph_vector_int_size(&neis); + for (igraph_integer_t 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_real_t u_x = MATRIX(*res, u, 0); + igraph_real_t 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; + igraph_real_t u1_x, u1_y, u2_x, u2_y; if (u1 == v || u2 == v || u1 == u || u2 == u) { continue; } @@ -379,13 +374,11 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, } 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++) { + for (igraph_integer_t 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; + igraph_real_t u1_x, u1_y, u2_x, u2_y, d_ev; if (u1 == v || u2 == v) { continue; } @@ -393,35 +386,34 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, 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); + 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); + 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_CHECK(igraph_incident(graph, &neis, v, IGRAPH_ALL)); + igraph_integer_t no = igraph_vector_int_size(&neis); + for (igraph_integer_t 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; + igraph_real_t u_x = MATRIX(*res, u, 0); + igraph_real_t u_y = MATRIX(*res, u, 1); + for (igraph_integer_t w = 0; w < no_nodes; w++) { + igraph_real_t 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); + 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); + 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; } } @@ -453,12 +445,12 @@ int igraph_layout_davidson_harel(const igraph_t *graph, igraph_matrix_t *res, RNG_END(); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); igraph_vector_int_destroy(&try_idx); - igraph_vector_float_destroy(&try_x); - igraph_vector_float_destroy(&try_y); + igraph_vector_destroy(&try_x); + igraph_vector_destroy(&try_y); igraph_vector_int_destroy(&perm); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp b/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp index 1678db37b7a..dd17d34aa74 100644 --- a/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp +++ b/src/vendor/cigraph/src/layout/drl/DensityGrid.cpp @@ -35,10 +35,10 @@ #include "drl_Node.h" #include "DensityGrid.h" -#include "igraph_error.h" -#include #include +#include +#include using namespace std; @@ -65,20 +65,9 @@ DensityGrid::~DensityGrid () { 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 - } + Density = new float[GRID_SIZE][GRID_SIZE]; + fall_off = new float[RADIUS * 2 + 1][RADIUS * 2 + 1]; + Bins = new deque[GRID_SIZE * GRID_SIZE]; // Clear Grid int i; @@ -188,9 +177,7 @@ void DensityGrid::Subtract(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, - __LINE__, IGRAPH_EDRL); - return; + throw runtime_error("Exceeded density grid in DrL."); #endif } @@ -232,9 +219,7 @@ void DensityGrid::Add(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, - __LINE__, IGRAPH_EDRL); - return; + throw runtime_error("Exceeded density grid in DrL."); #endif } diff --git a/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp b/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp index 0f02f1537a8..19569cde1c1 100644 --- a/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/DensityGrid_3d.cpp @@ -35,10 +35,10 @@ #include "drl_Node_3d.h" #include "DensityGrid_3d.h" -#include "igraph_error.h" -#include #include +#include +#include using namespace std; @@ -65,20 +65,9 @@ DensityGrid::~DensityGrid () { 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 - } + 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]; // Clear Grid int i; @@ -203,9 +192,7 @@ void DensityGrid::Subtract(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, - __LINE__, IGRAPH_EDRL); - return; + throw runtime_error("Exceeded density grid in DrL."); #endif } @@ -252,9 +239,7 @@ void DensityGrid::Add(Node &N) { #ifdef MUSE_MPI MPI_Abort ( MPI_COMM_WORLD, 1 ); #else - igraph_error("Exceeded density grid in DrL", IGRAPH_FILE_BASENAME, - __LINE__, IGRAPH_EDRL); - return; + throw runtime_error("Exceeded density grid in DrL."); #endif } diff --git a/src/vendor/cigraph/src/layout/drl/drl_Node.h b/src/vendor/cigraph/src/layout/drl/drl_Node.h index bc894c23f7b..387bbb293c8 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_Node.h +++ b/src/vendor/cigraph/src/layout/drl/drl_Node.h @@ -33,6 +33,8 @@ #ifndef __NODE_H__ #define __NODE_H__ +#include + // The node class contains information about a given node for // use by the density server process. @@ -46,16 +48,16 @@ class Node { public: bool fixed; // if true do not change the - // position of this node - int id; + igraph_integer_t id; + // position of this node float x, y; float sub_x, sub_y; float energy; public: - Node( int node_id ) { + Node( igraph_integer_t node_id ) { x = y = 0.0; fixed = false; id = node_id; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h b/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h index e373d9a65e9..250e934a366 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h +++ b/src/vendor/cigraph/src/layout/drl/drl_Node_3d.h @@ -33,6 +33,8 @@ #ifndef __NODE_H__ #define __NODE_H__ +#include + // The node class contains information about a given node for // use by the density server process. @@ -46,16 +48,16 @@ class Node { public: bool fixed; // if true do not change the - // position of this node - int id; + igraph_integer_t id; + // position of this node float x, y, z; float sub_x, sub_y, sub_z; float energy; public: - Node( int node_id ) { + Node( igraph_integer_t node_id ) { x = y = z = 0.0; fixed = false; id = node_id; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph.cpp b/src/vendor/cigraph/src/layout/drl/drl_graph.cpp index e96bb80331a..f764409ec62 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_graph.cpp @@ -175,11 +175,11 @@ graph::graph(const igraph_t *igraph, // 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++) { + igraph_integer_t no_of_edges = igraph_ecount(igraph); + for (igraph_integer_t i = 0; i < num_nodes; i++) { id_catalog[i] = 1; } - map< int, int>::iterator cat_iter; + map::iterator cat_iter; for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++) { cat_iter->second = cat_iter->first; @@ -194,9 +194,9 @@ graph::graph(const igraph_t *igraph, } // read .int file for graph info - long int node_1, node_2; - double weight; - for (long int i = 0; i < no_of_edges; i++) { + igraph_integer_t node_1, node_2; + igraph_real_t weight; + for (igraph_integer_t 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 ; @@ -395,17 +395,17 @@ void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { 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; + igraph_integer_t 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; + real_iterations = (igraph_integer_t)real_parm; } else if ( real_parm == 1) { real_iterations = full_comp_iters + simmer.iterations + 100; } else { - real_iterations = (int)(real_parm * full_comp_iters); + real_iterations = (igraph_integer_t)(real_parm * full_comp_iters); } tot_iterations = 0; @@ -488,13 +488,12 @@ void graph::init_parms(const igraph_layout_drl_options_t *options) { // 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++) { +int graph::read_real(const igraph_matrix_t *real_mat) { + igraph_integer_t n = igraph_matrix_nrow(real_mat); + for (igraph_integer_t 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; + positions[id_catalog[i]].fixed = false; if ( real_iterations > 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); @@ -819,7 +818,7 @@ int graph::ReCompute( ) { void graph::update_nodes ( ) { - vector node_indices; // node list of nodes currently being updated + 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 @@ -832,9 +831,9 @@ void graph::update_nodes ( ) { // 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 )); + igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); - for ( int i = myid; i < square_num_nodes; i += num_procs ) { + for ( igraph_integer_t i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); @@ -845,7 +844,7 @@ void graph::update_nodes ( ) { if ( i < num_nodes ) { // advance random sequence according to myid - for ( int j = 0; j < 2 * myid; j++ ) { + for ( size_t j = 0; j < 2 * myid; j++ ) { RNG_UNIF01(); } // rand(); @@ -856,7 +855,7 @@ void graph::update_nodes ( ) { } // advance random sequence for next iteration - for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + for ( size_t j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { RNG_UNIF01(); } // rand(); @@ -864,7 +863,7 @@ void graph::update_nodes ( ) { } else { // advance random sequence according to use by // the other processors - for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { + for ( size_t j = 0; j < 2 * (node_indices.size()); j++ ) { RNG_UNIF01(); } //rand(); @@ -872,7 +871,7 @@ void graph::update_nodes ( ) { // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; - for ( unsigned int j = 0; j < node_indices.size (); j++ ) + for ( size_t j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { all_fixed = false; } @@ -899,7 +898,7 @@ void graph::update_nodes ( ) { */ // compute node list for next update - for ( unsigned int j = 0; j < node_indices.size(); j++ ) { + for ( size_t j = 0; j < node_indices.size(); j++ ) { node_indices [j] += num_procs; } @@ -920,11 +919,11 @@ void graph::update_nodes ( ) { // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. -void graph::get_positions ( vector &node_indices, +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++) { + for (size_t 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; } @@ -936,7 +935,7 @@ void graph::get_positions ( vector &node_indices, // of active processes at this level for use by the random number // generators. -void graph::update_node_pos ( int node_ind, +void graph::update_node_pos ( igraph_integer_t node_ind, float old_positions[2 * MAX_PROCS], float new_positions[2 * MAX_PROCS] ) { @@ -1008,13 +1007,13 @@ void graph::update_node_pos ( int node_ind, // updates the positions by subtracting the old positions and adding the // new positions to the density grid. -void graph::update_density ( vector &node_indices, +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++ ) { + for ( size_t 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]], @@ -1034,13 +1033,13 @@ void graph::update_density ( vector &node_indices, * original code by B. Wylie. * *********************************************/ -float graph::Compute_Node_Energy( int node_ind ) { +float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { /* Want to expand 4th power range of attraction */ float attraction_factor = attraction * attraction * attraction * attraction * 2e-2; - map ::iterator EI; + map ::iterator EI; float x_dis, y_dis; float energy_distance, weight; float node_energy = 0; @@ -1091,9 +1090,9 @@ float graph::Compute_Node_Energy( int node_ind ) { * originally written by B. Wylie * *********************************************/ -void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) { +void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_y ) { - map ::iterator EI; + map ::iterator EI; float total_weight = 0; float x_dis, y_dis, x_cen = 0, y_cen = 0; float x = 0, y = 0, dis; @@ -1134,7 +1133,7 @@ void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y ) { float num_connections = sqrt((double)neighbors[node_ind].size()); float maxLength = 0; - map::iterator maxIndex; + map::iterator maxIndex; // Go through nodes edges... cutting if necessary for (EI = maxIndex = neighbors[node_ind].begin(); @@ -1292,9 +1291,9 @@ int graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } - long int n = positions.size(); + igraph_integer_t n = positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 2)); - for (long int i = 0; i < n; i++) { + for (size_t i = 0; i < n; i++) { MATRIX(*res, i, 0) = positions[i].x; MATRIX(*res, i, 1) = positions[i].y; } diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph.h b/src/vendor/cigraph/src/layout/drl/drl_graph.h index 1a2804dba59..df1424f9432 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph.h +++ b/src/vendor/cigraph/src/layout/drl/drl_graph.h @@ -45,7 +45,7 @@ namespace drl { // layout schedule information struct layout_schedule { - int iterations; + igraph_integer_t iterations; float temperature; float attraction; float damping_mult; @@ -61,8 +61,7 @@ class graph { 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); + int read_real ( const igraph_matrix_t *real_mat ); void scan_int ( char *filename ); void read_int ( char *file_name ); void draw_graph ( int int_out, char *coord_file ); @@ -83,13 +82,13 @@ class graph { // 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 Compute_Node_Energy ( igraph_integer_t node_ind ); + void Solve_Analytic ( igraph_integer_t 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, + void update_node_pos ( igraph_integer_t node_ind, float old_positions[2 * MAX_PROCS], float new_positions[2 * MAX_PROCS] ); @@ -97,17 +96,18 @@ class graph { int myid, num_procs; // graph decomposition information - int num_nodes; // number of nodes in graph + igraph_integer_t 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. + 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; + int STAGE; + igraph_integer_t 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; @@ -123,9 +123,9 @@ class graph { 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 + igraph_integer_t real_iterations; // number of iterations to hold .real input fixed + igraph_integer_t tot_iterations; + igraph_integer_t tot_expected_iterations; // for progress bar bool real_fixed; }; diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp index fe9d21a103d..27be46161e4 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.cpp @@ -98,11 +98,11 @@ graph::graph(const igraph_t *igraph, // 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++) { + igraph_integer_t no_of_edges = igraph_ecount(igraph); + for (igraph_integer_t i = 0; i < num_nodes; i++) { id_catalog[i] = 1; } - map< int, int>::iterator cat_iter; + map< igraph_integer_t, igraph_integer_t>::iterator cat_iter; for ( cat_iter = id_catalog.begin(); cat_iter != id_catalog.end(); cat_iter++) { cat_iter->second = cat_iter->first; @@ -117,9 +117,9 @@ graph::graph(const igraph_t *igraph, } // read .int file for graph info - long int node_1, node_2; - double weight; - for (long int i = 0; i < no_of_edges; i++) { + igraph_integer_t node_1, node_2; + igraph_real_t weight; + for (igraph_integer_t 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 ; @@ -156,7 +156,7 @@ void graph::init_parms ( int rand_seed, float edge_cut, float real_parm ) { 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; + igraph_integer_t full_comp_iters; full_comp_iters = liquid.iterations + expansion.iterations + cooldown.iterations + crunch.iterations + 3; @@ -200,14 +200,13 @@ void graph::init_parms(const igraph_layout_drl_options_t *options) { 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++) { +int graph::read_real(const igraph_matrix_t *real_mat) { + igraph_integer_t n = igraph_matrix_nrow(real_mat); + for (igraph_integer_t 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; + positions[id_catalog[i]].fixed = false; if ( real_iterations > 0 ) { density_server.Add ( positions[id_catalog[i]], fineDensity ); @@ -470,7 +469,7 @@ int graph::ReCompute( ) { void graph::update_nodes ( ) { - vector node_indices; // node list of nodes currently being updated + 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 @@ -483,9 +482,9 @@ void graph::update_nodes ( ) { // 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 )); + igraph_integer_t square_num_nodes = (igraph_integer_t)(num_procs + num_procs * floor ((float)(num_nodes - 1) / (float)num_procs )); - for ( int i = myid; i < square_num_nodes; i += num_procs ) { + for ( igraph_integer_t i = myid; i < square_num_nodes; i += num_procs ) { // get old positions get_positions ( node_indices, old_positions ); @@ -496,7 +495,7 @@ void graph::update_nodes ( ) { if ( i < num_nodes ) { // advance random sequence according to myid - for ( int j = 0; j < 2 * myid; j++ ) { + for ( size_t j = 0; j < 2 * myid; j++ ) { RNG_UNIF01(); } // rand(); @@ -507,7 +506,7 @@ void graph::update_nodes ( ) { } // advance random sequence for next iteration - for ( unsigned int j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { + for ( size_t j = 2 * myid; j < 2 * (node_indices.size() - 1); j++ ) { RNG_UNIF01(); } // rand(); @@ -515,7 +514,7 @@ void graph::update_nodes ( ) { } else { // advance random sequence according to use by // the other processors - for ( unsigned int j = 0; j < 2 * (node_indices.size()); j++ ) { + for ( size_t j = 0; j < 2 * (node_indices.size()); j++ ) { RNG_UNIF01(); } //rand(); @@ -523,7 +522,7 @@ void graph::update_nodes ( ) { // check if anything was actually updated (e.g. everything was fixed) all_fixed = true; - for ( unsigned int j = 0; j < node_indices.size (); j++ ) + for ( size_t j = 0; j < node_indices.size (); j++ ) if ( !(positions [ node_indices[j] ].fixed && real_fixed) ) { all_fixed = false; } @@ -543,14 +542,14 @@ void graph::update_nodes ( ) { if ( myid == 0 ) { // output node list (for debugging) - for ( unsigned int j = 0; j < node_indices.size(); j++ ) + for ( size_t 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++ ) { + for ( size_t j = 0; j < node_indices.size(); j++ ) { node_indices [j] += num_procs; } @@ -571,11 +570,11 @@ void graph::update_nodes ( ) { // The get_positions function takes the node_indices list // and returns the corresponding positions in an array. -void graph::get_positions ( vector &node_indices, +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++) { + for (size_t 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; @@ -588,7 +587,7 @@ void graph::get_positions ( vector &node_indices, // of active processes at this level for use by the random number // generators. -void graph::update_node_pos ( int node_ind, +void graph::update_node_pos ( igraph_integer_t node_ind, float old_positions[3 * MAX_PROCS], float new_positions[3 * MAX_PROCS] ) { @@ -666,13 +665,13 @@ void graph::update_node_pos ( int node_ind, // updates the positions by subtracting the old positions and adding the // new positions to the density grid. -void graph::update_density ( vector &node_indices, +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++ ) { + for ( size_t 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]; @@ -694,13 +693,13 @@ void graph::update_density ( vector &node_indices, * original code by B. Wylie. * *********************************************/ -float graph::Compute_Node_Energy( int node_ind ) { +float graph::Compute_Node_Energy( igraph_integer_t node_ind ) { /* Want to expand 4th power range of attraction */ float attraction_factor = attraction * attraction * attraction * attraction * 2e-2; - map ::iterator EI; + map ::iterator EI; float x_dis, y_dis, z_dis; float energy_distance, weight; float node_energy = 0; @@ -752,10 +751,10 @@ float graph::Compute_Node_Energy( int node_ind ) { * originally written by B. Wylie * *********************************************/ -void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y, +void graph::Solve_Analytic( igraph_integer_t node_ind, float &pos_x, float &pos_y, float &pos_z) { - map ::iterator EI; + 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; @@ -796,7 +795,7 @@ void graph::Solve_Analytic( int node_ind, float &pos_x, float &pos_y, float num_connections = (float)sqrt((float)neighbors[node_ind].size()); float maxLength = 0; - map::iterator maxIndex; + map::iterator maxIndex; // Go through nodes edges... cutting if necessary for (EI = maxIndex = neighbors[node_ind].begin(); @@ -858,9 +857,9 @@ int graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } - long int n = positions.size(); + size_t n = positions.size(); IGRAPH_CHECK(igraph_matrix_resize(res, n, 3)); - for (long int i = 0; i < n; i++) { + for (size_t 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; diff --git a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h index c61612ca962..b87ad86ce7d 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h +++ b/src/vendor/cigraph/src/layout/drl/drl_graph_3d.h @@ -45,7 +45,7 @@ namespace drl3d { // layout schedule information struct layout_schedule { - int iterations; + igraph_integer_t iterations; float temperature; float attraction; float damping_mult; @@ -59,8 +59,7 @@ class graph { // 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 read_real ( const igraph_matrix_t *real_mat ); int draw_graph (igraph_matrix_t *res); float get_tot_energy ( ); @@ -75,13 +74,13 @@ class graph { // 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 Compute_Node_Energy ( igraph_integer_t node_ind ); + void Solve_Analytic ( igraph_integer_t 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, + void update_node_pos ( igraph_integer_t node_ind, float old_positions[3 * MAX_PROCS], float new_positions[3 * MAX_PROCS] ); @@ -89,17 +88,18 @@ class graph { int myid, num_procs; // graph decomposition information - int num_nodes; // number of nodes in graph + igraph_integer_t 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. + 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; + int STAGE; + igraph_integer_t 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; @@ -115,9 +115,9 @@ class graph { 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 + igraph_integer_t real_iterations; // number of iterations to hold .real input fixed + igraph_integer_t tot_iterations; + igraph_integer_t tot_expected_iterations; // for progress bar bool real_fixed; }; diff --git a/src/vendor/cigraph/src/layout/drl/drl_layout.cpp b/src/vendor/cigraph/src/layout/drl/drl_layout.cpp index 2ea5e862ffa..d31c71489ac 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_layout.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_layout.cpp @@ -248,7 +248,7 @@ namespace drl { * Time complexity: O(1). */ -int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, +igraph_error_t igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, igraph_layout_drl_default_t templ) { options->edge_cut = 32.0 / 40.0; @@ -438,25 +438,15 @@ int igraph_layout_drl_options_init(igraph_layout_drl_options_t *options, * \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_error_t 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 igraph_vector_t *weights) { const char msg[] = "Damping multipliers cannot be negative, got %g."; if (options->init_damping_mult < 0) { @@ -495,7 +485,7 @@ int igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); - neighbors.read_real(res, fixed); + neighbors.read_real(res); } neighbors.draw_graph(res); diff --git a/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp b/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp index b5a6e2d5d60..6e953a8dae6 100644 --- a/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp +++ b/src/vendor/cigraph/src/layout/drl/drl_layout_3d.cpp @@ -86,11 +86,6 @@ using namespace drl3d; * \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: ???. @@ -98,11 +93,10 @@ using namespace drl3d; * \sa \ref igraph_layout_drl() for the standard 2d version. */ -int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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) { + const igraph_vector_t *weights) { const char msg[] = "Damping multipliers cannot be negative, got %g."; @@ -142,7 +136,7 @@ int igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, neighbors.init_parms(options); if (use_seed) { IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); - neighbors.read_real(res, fixed); + neighbors.read_real(res); } neighbors.draw_graph(res); diff --git a/src/vendor/cigraph/src/layout/fruchterman_reingold.c b/src/vendor/cigraph/src/layout/fruchterman_reingold.c index f7f14fc6062..69bcd967508 100644 --- a/src/vendor/cigraph/src/layout/fruchterman_reingold.c +++ b/src/vendor/cigraph/src/layout/fruchterman_reingold.c @@ -31,7 +31,7 @@ #include "core/interruption.h" #include "layout/layout_internal.h" -static int igraph_layout_i_fr(const igraph_t *graph, +static igraph_error_t igraph_layout_i_fr(const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t use_seed, igraph_integer_t niter, @@ -45,15 +45,15 @@ static int igraph_layout_i_fr(const igraph_t *graph, 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_vector_t dispx, dispy; igraph_real_t temp = start_temp; igraph_real_t difftemp = start_temp / niter; - igraph_bool_t conn = 1; - float C = 0; + igraph_bool_t conn = true; + igraph_real_t C = 0; IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { - C = no_nodes * sqrtf(no_nodes); + C = no_nodes * sqrt(no_nodes); } RNG_BEGIN(); @@ -62,10 +62,8 @@ static int igraph_layout_i_fr(const igraph_t *graph, igraph_i_layout_random_bounded(graph, res, minx, maxx, miny, maxy); } - 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_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); for (i = 0; i < niter; i++) { igraph_integer_t v, u, e; @@ -74,14 +72,14 @@ static int igraph_layout_i_fr(const igraph_t *graph, /* calculate repulsive forces, we have a special version for unconnected graphs */ - igraph_vector_float_null(&dispx); - igraph_vector_float_null(&dispy); + igraph_vector_null(&dispx); + igraph_vector_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; + 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 dlen = dx * dx + dy * dy; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); @@ -98,9 +96,9 @@ static int igraph_layout_i_fr(const igraph_t *graph, } 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; + 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 dlen, rdlen; dlen = dx * dx + dy * dy; while (dlen == 0) { @@ -127,7 +125,7 @@ static int igraph_layout_i_fr(const igraph_t *graph, 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; + 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); @@ -169,14 +167,14 @@ static int igraph_layout_i_fr(const igraph_t *graph, RNG_END(); - igraph_vector_float_destroy(&dispx); - igraph_vector_float_destroy(&dispy); + igraph_vector_destroy(&dispx); + igraph_vector_destroy(&dispy); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_layout_i_grid_fr( +static igraph_error_t 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, @@ -186,14 +184,14 @@ static int igraph_layout_i_grid_fr( igraph_integer_t no_nodes = igraph_vcount(graph); igraph_integer_t no_edges = igraph_ecount(graph); - float width = sqrtf(no_nodes), height = width; + igraph_real_t width = sqrt(no_nodes), height = width; igraph_2dgrid_t grid; - igraph_vector_float_t dispx, dispy; + igraph_vector_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; + const igraph_real_t cellsize = 2.0; RNG_BEGIN(); @@ -211,26 +209,24 @@ static int igraph_layout_i_grid_fr( 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); + IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); 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); + igraph_vector_null(&dispx); + igraph_vector_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; + 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 dlen = dx * dx + dy * dy; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); dy = RNG_UNIF(-1e-9, 1e-9); @@ -252,7 +248,7 @@ static int igraph_layout_i_grid_fr( 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; + 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); @@ -291,11 +287,11 @@ static int igraph_layout_i_grid_fr( temp -= difftemp; } - igraph_vector_float_destroy(&dispx); - igraph_vector_float_destroy(&dispy); + igraph_vector_destroy(&dispx); + igraph_vector_destroy(&dispy); igraph_2dgrid_destroy(&grid); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -347,7 +343,7 @@ static int igraph_layout_i_grid_fr( * 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, + * \param weights Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * Weights must be positive. * It will be ignored if it is a null-pointer. @@ -368,13 +364,13 @@ static int igraph_layout_i_grid_fr( * vertices in the graph. */ -int igraph_layout_fruchterman_reingold(const igraph_t *graph, +igraph_error_t 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 *weights, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -394,10 +390,10 @@ int igraph_layout_fruchterman_reingold(const igraph_t *graph, "Fruchterman-Reingold layout.", IGRAPH_EINVAL); } - if (weight && igraph_vector_size(weight) != no_edges) { + if (weights && igraph_vector_size(weights) != no_edges) { IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } - if (weight && no_edges > 0 && igraph_vector_min(weight) <= 0) { + if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", IGRAPH_EINVAL); } @@ -430,10 +426,10 @@ int igraph_layout_fruchterman_reingold(const igraph_t *graph, if (grid == IGRAPH_LAYOUT_GRID) { return igraph_layout_i_grid_fr(graph, res, use_seed, niter, start_temp, - weight, minx, maxx, miny, maxy); + weights, minx, maxx, miny, maxy); } else { return igraph_layout_i_fr(graph, res, use_seed, niter, start_temp, - weight, minx, maxx, miny, maxy); + weights, minx, maxx, miny, maxy); } } @@ -456,7 +452,7 @@ int igraph_layout_fruchterman_reingold(const igraph_t *graph, * 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, + * \param weights Pointer to a vector containing edge weights, * the attraction along the edges will be multiplied by these. * Weights must be positive. * It will be ignored if it is a null-pointer. @@ -485,12 +481,12 @@ int igraph_layout_fruchterman_reingold(const igraph_t *graph, * */ -int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, +igraph_error_t 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 *weights, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, @@ -501,11 +497,11 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, const igraph_integer_t no_nodes = igraph_vcount(graph); const igraph_integer_t no_edges = igraph_ecount(graph); igraph_integer_t i; - igraph_vector_float_t dispx, dispy, dispz; + igraph_vector_t dispx, dispy, dispz; igraph_real_t temp = start_temp; igraph_real_t difftemp = start_temp / niter; - igraph_bool_t conn = 1; - float C = 0; + igraph_bool_t conn = true; + igraph_real_t C = 0; if (niter < 0) { IGRAPH_ERROR("Number of iterations must be non-negative in " @@ -518,10 +514,10 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, "Fruchterman-Reingold layout", IGRAPH_EINVAL); } - if (weight && igraph_vector_size(weight) != igraph_ecount(graph)) { + if (weights && igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); } - if (weight && no_edges > 0 && igraph_vector_min(weight) <= 0) { + if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("Weights must be positive for Fruchterman-Reingold layout.", IGRAPH_EINVAL); } @@ -555,7 +551,7 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK)); if (!conn) { - C = no_nodes * sqrtf(no_nodes); + C = no_nodes * sqrt(no_nodes); } RNG_BEGIN(); @@ -564,12 +560,9 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, igraph_i_layout_random_bounded_3d(graph, res, minx, maxx, miny, maxy, minz, maxz); } - 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); + IGRAPH_VECTOR_INIT_FINALLY(&dispx, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispy, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&dispz, no_nodes); for (i = 0; i < niter; i++) { igraph_integer_t v, u, e; @@ -578,16 +571,16 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, /* 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); + igraph_vector_null(&dispx); + igraph_vector_null(&dispy); + igraph_vector_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; + 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 dlen = dx * dx + dy * dy + dz * dz; while (dlen == 0) { dx = RNG_UNIF(-1e-9, 1e-9); @@ -607,10 +600,10 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, } 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; + 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 dlen, rdlen; dlen = dx * dx + dy * dy + dz * dz; while (dlen == 0) { @@ -640,7 +633,7 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, 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 w = weights ? VECTOR(*weights)[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); @@ -694,10 +687,10 @@ int igraph_layout_fruchterman_reingold_3d(const igraph_t *graph, RNG_END(); - igraph_vector_float_destroy(&dispx); - igraph_vector_float_destroy(&dispy); - igraph_vector_float_destroy(&dispz); + igraph_vector_destroy(&dispx); + igraph_vector_destroy(&dispy); + igraph_vector_destroy(&dispz); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/gem.c b/src/vendor/cigraph/src/layout/gem.c index 21f35eb91c4..991ea0d6f05 100644 --- a/src/vendor/cigraph/src/layout/gem.c +++ b/src/vendor/cigraph/src/layout/gem.c @@ -25,13 +25,15 @@ #include "igraph_interface.h" #include "igraph_random.h" +#include "igraph_structural.h" -#include "core/math.h" #include "core/interruption.h" +#include "core/math.h" /** * \ingroup layout * \function igraph_layout_gem + * \brief Layout graph according to GEM algorithm. * * The GEM layout algorithm, as described in Arne Frick, Andreas Ludwig, * Heiko Mehldau: A Fast Adaptive Layout Algorithm for Undirected Graphs, @@ -64,74 +66,76 @@ * performed. */ -int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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_vector_t impulse_x, impulse_y, temp, skew_gauge; igraph_integer_t i; - float temp_global; + igraph_real_t temp_global; igraph_integer_t perm_pointer = 0; - float barycenter_x = 0, barycenter_y = 0; + igraph_real_t 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; + igraph_vector_int_t neis; + const igraph_real_t elen_des2 = 128 * 128; + const igraph_real_t gamma = 1 / 16.0; + const igraph_real_t alpha_o = M_PI; + const igraph_real_t alpha_r = M_PI / 3.0; + const igraph_real_t sigma_o = 1.0 / 3.0; + const igraph_real_t sigma_r = 1.0 / 2.0 / no_nodes; if (maxiter < 0) { - IGRAPH_ERROR("Number of iterations must be non-negative in GEM layout", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Number of iterations must be non-negative in GEM layout, " + "got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, maxiter); } - 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 (use_seed && igraph_matrix_nrow(res) != no_nodes) { + IGRAPH_ERRORF("In GEM layout, seed matrix number of rows should equal number of nodes (%" IGRAPH_PRId "), got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, no_nodes, igraph_matrix_nrow(res)); + } + if (use_seed && igraph_matrix_ncol(res) != 2) { + IGRAPH_ERRORF("In GEM layout, seed matrix number of columns should be 2, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, igraph_matrix_ncol(res)); } if (temp_max <= 0) { - IGRAPH_ERROR("Maximum temperature should be positive in GEM layout", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Maximum temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_max); } if (temp_min <= 0) { - IGRAPH_ERROR("Minimum temperature should be positive in GEM layout", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Minimum temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_min); } if (temp_init <= 0) { - IGRAPH_ERROR("Initial temperature should be positive in GEM layout", - IGRAPH_EINVAL); + IGRAPH_ERRORF("Initial temperature should be positive in GEM layout, got %g.", + IGRAPH_EINVAL, temp_init); } if (temp_max < temp_init || temp_init < temp_min) { - IGRAPH_ERROR("Minimum <= Initial <= Maximum temperature is required " - "in GEM layout", IGRAPH_EINVAL); + IGRAPH_ERRORF("Minimum <= Initial <= Maximum temperature is required " + "in GEM layout, but %g is not larger than %g and smaller than %g.", IGRAPH_EINVAL, + temp_init, temp_min, temp_max); } if (no_nodes == 0) { - return 0; + return IGRAPH_SUCCESS; } - 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_VECTOR_INIT_FINALLY(&impulse_x, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&impulse_y, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&temp, no_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&skew_gauge, no_nodes); + IGRAPH_CHECK(igraph_vector_int_init_range(&perm, 0, no_nodes)); IGRAPH_FINALLY(igraph_vector_int_destroy, &perm); IGRAPH_VECTOR_INIT_FINALLY(&phi, no_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 10); RNG_BEGIN(); /* Initialization */ - igraph_degree(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS); + IGRAPH_CHECK(igraph_strength(graph, &phi, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, /* weights = */ 0)); + 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)); @@ -149,12 +153,12 @@ int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, VECTOR(phi)[i] *= (VECTOR(phi)[i] / 2.0 + 1.0); } } - igraph_vector_float_fill(&temp, temp_init); + igraph_vector_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_real_t px, py, pvx, pvy; IGRAPH_ALLOW_INTERRUPTION(); @@ -172,7 +176,7 @@ int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, py += RNG_UNIF(-32.0, 32.0); for (u = 0; u < no_nodes; u++) { - float dx, dy, dist2; + igraph_real_t dx, dy, dist2; if (u == v) { continue; } @@ -186,19 +190,19 @@ int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, } IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - nlen = igraph_vector_size(&neis); + nlen = igraph_vector_int_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; + 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 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); + igraph_real_t plen = sqrt(px * px + py * py); px *= VECTOR(temp)[v] / plen; py *= VECTOR(temp)[v] / plen; MATRIX(*res, v, 0) += px; @@ -209,19 +213,19 @@ int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, 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]; + igraph_real_t beta = atan2(pvy - py, pvx - px); + igraph_real_t sin_beta = sin(beta); + igraph_real_t sign_sin_beta = (sin_beta > 0) ? 1 : ((sin_beta < 0) ? -1 : 0); + igraph_real_t cos_beta = cos(beta); + igraph_real_t abs_cos_beta = fabs(cos_beta); + igraph_real_t 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)) { + if (abs_cos_beta >= cos(alpha_o / 2.0)) { VECTOR(temp)[v] *= sigma_o * cos_beta; } - VECTOR(temp)[v] *= (1 - fabsf(VECTOR(skew_gauge)[v])); + VECTOR(temp)[v] *= (1 - fabs(VECTOR(skew_gauge)[v])); if (VECTOR(temp)[v] > temp_max) { VECTOR(temp)[v] = temp_max; } @@ -237,14 +241,14 @@ int igraph_layout_gem(const igraph_t *graph, igraph_matrix_t *res, RNG_END(); - igraph_vector_destroy(&neis); + igraph_vector_int_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_vector_destroy(&skew_gauge); + igraph_vector_destroy(&temp); + igraph_vector_destroy(&impulse_y); + igraph_vector_destroy(&impulse_x); IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/graphopt.c b/src/vendor/cigraph/src/layout/graphopt.c index 1668932fa05..432cb641c0c 100644 --- a/src/vendor/cigraph/src/layout/graphopt.c +++ b/src/vendor/cigraph/src/layout/graphopt.c @@ -33,43 +33,43 @@ static igraph_real_t igraph_i_distance_between( const igraph_matrix_t *c, - long int a, long int b); + igraph_integer_t a, igraph_integer_t b); -static int igraph_i_determine_electric_axal_forces( +static void 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); + igraph_integer_t other_node, + igraph_integer_t this_node); -static int igraph_i_apply_electrical_force( +static void 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_integer_t other_node, igraph_integer_t this_node, igraph_real_t node_charge, igraph_real_t distance); -static int igraph_i_determine_spring_axal_forces( +static void 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); + igraph_integer_t other_node, + igraph_integer_t this_node); -static int igraph_i_apply_spring_force( +static void 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_integer_t other_node, + igraph_integer_t this_node, igraph_real_t spring_length, igraph_real_t spring_constant); -static int igraph_i_move_nodes( +static void igraph_i_move_nodes( igraph_matrix_t *pos, const igraph_vector_t *pending_forces_x, const igraph_vector_t *pending_forces_y, @@ -78,19 +78,19 @@ static int igraph_i_move_nodes( static igraph_real_t igraph_i_distance_between( const igraph_matrix_t *c, - long int a, long int b) { + igraph_integer_t a, igraph_integer_t 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 ); + return sqrt(diffx*diffx + diffy*diffy); } -static int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos, +static void 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) { + igraph_integer_t other_node, + igraph_integer_t this_node) { // We know what the directed force is. We now need to translate it // into the appropriate x and y components. @@ -133,15 +133,13 @@ static int igraph_i_determine_electric_axal_forces(const igraph_matrix_t *pos, if (MATRIX(*pos, other_node, 1) < MATRIX(*pos, this_node, 1)) { *y = *y * -1; } - - return 0; } -static int igraph_i_apply_electrical_force( +static void 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_integer_t other_node, igraph_integer_t this_node, igraph_real_t node_charge, igraph_real_t distance) { @@ -157,17 +155,15 @@ static int igraph_i_apply_electrical_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( +static void 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) { + igraph_integer_t other_node, igraph_integer_t this_node) { // if the spring is just the right size, the forces will be 0, so we can // skip the computation. @@ -196,16 +192,14 @@ static int igraph_i_determine_spring_axal_forces( *x = 0.5 * *x; *y = 0.5 * *y; } - - return 0; } -static int igraph_i_apply_spring_force( +static void 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_integer_t other_node, + igraph_integer_t this_node, igraph_real_t spring_length, igraph_real_t spring_constant) { // determined using Hooke's Law: @@ -222,7 +216,7 @@ static int igraph_i_apply_spring_force( // 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; + return; } displacement = distance - spring_length; @@ -243,11 +237,9 @@ static int igraph_i_apply_spring_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( +static void igraph_i_move_nodes( igraph_matrix_t *pos, const igraph_vector_t *pending_forces_x, const igraph_vector_t *pending_forces_y, @@ -268,7 +260,7 @@ static int igraph_i_move_nodes( // velocity = force / mass // displacement = force / mass - long int this_node, no_of_nodes = igraph_vector_size(pending_forces_x); + igraph_integer_t this_node, no_of_nodes = igraph_vector_size(pending_forces_x); for (this_node = 0; this_node < no_of_nodes; this_node++) { @@ -292,19 +284,16 @@ static int igraph_i_move_nodes( 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 version 0.4.1 was rewritten in C, the support for + * layers was removed and the code was reorganized to avoid some + * unnecessary steps when the node charge (see below) is zero. * * * Graphopt uses physical analogies for defining attracting and repelling @@ -314,6 +303,7 @@ static int igraph_i_move_nodes( * * * 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 @@ -346,7 +336,7 @@ static int igraph_i_move_nodes( * |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_error_t 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, @@ -354,16 +344,16 @@ int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t this_node, other_node, edge; igraph_real_t distance; - long int i; + igraph_integer_t i; IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_x, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&pending_forces_y, no_of_nodes); @@ -422,8 +412,8 @@ int igraph_layout_graphopt(const igraph_t *graph, igraph_matrix_t *res, // 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); + igraph_integer_t tthis_node = IGRAPH_FROM(graph, edge); + igraph_integer_t 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, diff --git a/src/vendor/cigraph/src/layout/kamada_kawai.c b/src/vendor/cigraph/src/layout/kamada_kawai.c index e5f155c48a6..59f52cb0bf5 100644 --- a/src/vendor/cigraph/src/layout/kamada_kawai.c +++ b/src/vendor/cigraph/src/layout/kamada_kawai.c @@ -46,6 +46,10 @@ * far-apart veritces will have a smaller effect on the layout. * * + * Disconnected graphs are handled by assuming that the graph distance between + * vertices in different components is the same as the graph diameter. + * + * * This layout works particularly well for locally connected spatial networks * such as lattices. * @@ -99,7 +103,7 @@ * graph. */ -int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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, @@ -115,43 +119,43 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t i, j, m; if (maxiter < 0) { - IGRAPH_ERROR("Number of iterations must be non-negatice in " - "Kamada-Kawai layout", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of iterations must be non-negative in " + "Kamada-Kawai layout.", IGRAPH_EINVAL); } if (kkconst <= 0) { - IGRAPH_ERROR("`K' constant must be positive in Kamada-Kawai layout", + 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); + "Kamada-Kawai layout.", IGRAPH_EINVAL); } if (weights && igraph_vector_size(weights) != no_edges) { - IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } if (weights && no_edges > 0 && igraph_vector_min(weights) <= 0) { IGRAPH_ERROR("Weights must be positive for Kamada-Kawai layout.", IGRAPH_EINVAL); } if (minx && igraph_vector_size(minx) != no_nodes) { - IGRAPH_ERROR("Invalid minx vector length", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid minx vector length.", IGRAPH_EINVAL); } if (maxx && igraph_vector_size(maxx) != no_nodes) { - IGRAPH_ERROR("Invalid maxx vector length", IGRAPH_EINVAL); + 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); + 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); + IGRAPH_ERROR("Invalid miny vector length.", IGRAPH_EINVAL); } if (maxy && igraph_vector_size(maxy) != no_nodes) { - IGRAPH_ERROR("Invalid maxy vector length", IGRAPH_EINVAL); + 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); + IGRAPH_ERROR("miny must not be greater than maxy.", IGRAPH_EINVAL); } if (!use_seed) { @@ -168,21 +172,21 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, } if (no_nodes <= 1) { - return 0; + return IGRAPH_SUCCESS; } 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)); + IGRAPH_CHECK(igraph_distances_dijkstra(graph, &dij, igraph_vss_all(), + igraph_vss_all(), weights, IGRAPH_ALL)); + /* Find largest finite distance */ 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))) { + if (!isfinite(MATRIX(dij, i, j))) { continue; } if (MATRIX(dij, i, j) > max_dij) { @@ -190,6 +194,9 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, } } } + + /* Replace infinite distances by the largest finite distance, + * effectively making the graph connected. */ for (i = 0; i < no_nodes; i++) { for (j = 0; j < no_nodes; j++) { if (MATRIX(dij, i, j) > max_dij) { @@ -222,7 +229,7 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, } 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); + 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); } @@ -262,7 +269,7 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, } dx = old_x - MATRIX(*res, i, 0); dy = old_y - MATRIX(*res, i, 1); - dist = sqrt(dx * dx + dy * dy); + 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; @@ -317,10 +324,10 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, } 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); + 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); + 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); @@ -349,7 +356,7 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, igraph_matrix_destroy(&dij); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** @@ -406,7 +413,7 @@ int igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t *res, * graph. */ -int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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, @@ -484,21 +491,19 @@ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, } if (no_nodes <= 1) { - return 0; + return IGRAPH_SUCCESS; } 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)); + IGRAPH_CHECK(igraph_distances_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))) { + if (!isfinite(MATRIX(dij, i, j))) { continue; } if (MATRIX(dij, i, j) > max_dij) { @@ -693,5 +698,5 @@ int igraph_layout_kamada_kawai_3d(const igraph_t *graph, igraph_matrix_t *res, igraph_matrix_destroy(&dij); IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/large_graph.c b/src/vendor/cigraph/src/layout/large_graph.c index bd63dc1093d..0f10b690a0c 100644 --- a/src/vendor/cigraph/src/layout/large_graph.c +++ b/src/vendor/cigraph/src/layout/large_graph.c @@ -34,7 +34,7 @@ #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)); + igraph_real_t len = sqrt(*x * *x + *y * *y); if (len != 0) { *x /= len; *y /= len; @@ -47,14 +47,14 @@ static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { * * * This is a layout generator similar to the Large Graph Layout - * algorithm and program - * (http://lgl.sourceforge.net/). But unlike LGL, this + * 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 graph The (initialized) graph object to place. It must be connnected; + * disconnected graphs are not handled by the algorithm. * \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 @@ -88,24 +88,24 @@ static void igraph_i_norm2d(igraph_real_t *x, igraph_real_t *y) { * in the same grid cell. */ -int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t root; + igraph_integer_t no_of_layers, actlayer = 0; + igraph_vector_int_t vids; + igraph_vector_int_t layers; + igraph_vector_int_t parents; + igraph_vector_int_t edges; igraph_2dgrid_t grid; - igraph_vector_t eids; + igraph_vector_int_t eids; igraph_vector_t forcex; igraph_vector_t forcey; @@ -158,22 +158,27 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, } /* Assign the layers */ - IGRAPH_VECTOR_INIT_FINALLY(&vids, 0); - IGRAPH_VECTOR_INIT_FINALLY(&layers, 0); - IGRAPH_VECTOR_INIT_FINALLY(&parents, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&layers, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&parents, 0); if (no_of_nodes > 0) { - IGRAPH_CHECK(igraph_bfs_simple(&mst, (igraph_integer_t) root, IGRAPH_ALL, &vids, - &layers, &parents)); + IGRAPH_CHECK(igraph_bfs_simple(&mst, root, IGRAPH_ALL, &vids, &layers, &parents)); + } + no_of_layers = igraph_vector_int_size(&layers) - 1; + + /* Check whether we have reached all the nodes -- if not, the graph is + * disconnected */ + if (no_of_nodes > 0 && igraph_vector_int_min(&parents) <= -2) { + IGRAPH_WARNING("LGL layout does not support disconnected graphs yet."); } - 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); IGRAPH_VECTOR_INIT_FINALLY(&forcex, no_of_nodes); IGRAPH_VECTOR_INIT_FINALLY(&forcey, no_of_nodes); @@ -197,14 +202,15 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, for (actlayer = 1; actlayer < no_of_layers; actlayer++) { igraph_real_t c = 1; - long int i, j; + igraph_integer_t i, j; igraph_real_t massx, massy; igraph_real_t px, py; igraph_real_t sx, sy; - long int it = 0; + igraph_integer_t it = 0; igraph_real_t epsilon = 10e-6; igraph_real_t maxchange = epsilon + 1; + /* igraph_integer_t pairs; */ igraph_real_t sconst = sqrt(area / M_PI) / H_n; igraph_2dgrid_iterator_t vidit; @@ -216,12 +222,18 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, RNG_BEGIN(); - j = (long int) VECTOR(layers)[actlayer]; - for (i = (long int) VECTOR(layers)[actlayer - 1]; + j = VECTOR(layers)[actlayer]; + for (i = 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_integer_t vid = VECTOR(vids)[i]; + igraph_integer_t par = VECTOR(parents)[vid]; + + if (par < 0) { + /* this is either the root vertex or an unreachable node */ + continue; + } + IGRAPH_ALLOW_INTERRUPTION(); igraph_2dgrid_getcenter(&grid, &massx, &massy); igraph_i_norm2d(&massx, &massy); @@ -232,8 +244,7 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, 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) { + while (j < VECTOR(layers)[actlayer + 1] && VECTOR(parents)[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); @@ -246,7 +257,7 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, 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); + igraph_2dgrid_add(&grid, VECTOR(vids)[j], sx + rx, sy + ry); j++; } } @@ -257,20 +268,18 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, /* Step 2: add the edges of the next layer */ /*-----------------------------------------*/ - for (j = (long int) VECTOR(layers)[actlayer]; + for (j = VECTOR(layers)[actlayer]; j < VECTOR(layers)[actlayer + 1]; j++) { - long int vid = (long int) VECTOR(vids)[j]; - long int k; + igraph_integer_t vid = VECTOR(vids)[j]; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_incident(graph, &eids, vid, IGRAPH_ALL)); + for (k = 0; k < igraph_vector_int_size(&eids); k++) { + igraph_integer_t eid = VECTOR(eids)[k]; + igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); if ((from != vid && igraph_2dgrid_in(&grid, from)) || (to != vid && igraph_2dgrid_in(&grid, to))) { - igraph_vector_push_back(&edges, eid); + igraph_vector_int_push_back(&edges, eid); } } } @@ -281,12 +290,12 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, 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_integer_t jj; + igraph_real_t t = maxdelta * pow((maxit - it) / (igraph_real_t) maxit, coolexp); + igraph_integer_t 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))), + 100.0 * ((actlayer - 1.0) / (no_of_layers - 1.0) + (it) / (maxit * (no_of_layers - 1.0))), 0); /* init */ @@ -295,58 +304,58 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, maxchange = 0; /* attractive "forces" along the edges */ - for (jj = 0; jj < igraph_vector_size(&edges); jj++) { - igraph_integer_t from, to; + for (jj = 0; jj < igraph_vector_int_size(&edges); jj++) { + igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(edges)[jj]); + igraph_integer_t to = IGRAPH_TO(graph, VECTOR(edges)[jj]); 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); + xd = MATRIX(*res, from, 0) - MATRIX(*res, to, 0); + yd = MATRIX(*res, from, 1) - MATRIX(*res, 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; + VECTOR(forcex)[from] -= xd * force; + VECTOR(forcex)[to] += xd * force; + VECTOR(forcey)[from] -= yd * force; + VECTOR(forcey)[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 xd = MATRIX(*res, vid, 0) - MATRIX(*res, nei, 0); + igraph_real_t yd = MATRIX(*res, vid, 1) - MATRIX(*res, 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; + VECTOR(forcex)[vid] += xd * force; + VECTOR(forcex)[nei] -= xd * force; + VECTOR(forcey)[vid] += yd * force; + VECTOR(forcey)[nei] -= yd * force; } } } /* printf("verties: %li iterations: %li\n", */ - /* (long int) VECTOR(layers)[actlayer+1], pairs); */ + /* 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_integer_t vvid = 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); + igraph_real_t ded = sqrt(fx*fx + fy*fy); if (ded > t) { ded = t / ded; fx *= ded; fy *= ded; @@ -366,15 +375,15 @@ int igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, 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_vector_int_destroy(&vids); + igraph_vector_int_destroy(&layers); + igraph_vector_int_destroy(&parents); + igraph_vector_int_destroy(&edges); igraph_2dgrid_destroy(&grid); - igraph_vector_destroy(&eids); + igraph_vector_int_destroy(&eids); igraph_vector_destroy(&forcex); igraph_vector_destroy(&forcey); IGRAPH_FINALLY_CLEAN(9); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/layout_bipartite.c b/src/vendor/cigraph/src/layout/layout_bipartite.c index ebd4540ed0c..92d018b4cda 100644 --- a/src/vendor/cigraph/src/layout/layout_bipartite.c +++ b/src/vendor/cigraph/src/layout/layout_bipartite.c @@ -49,23 +49,23 @@ * * \sa \ref igraph_layout_sugiyama(). */ -int igraph_layout_bipartite(const igraph_t *graph, +igraph_error_t 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) { + igraph_real_t vgap, igraph_integer_t maxiter) { - long int i, no_of_nodes = igraph_vcount(graph); - igraph_vector_t layers; + igraph_integer_t i, no_of_nodes = igraph_vcount(graph); + igraph_vector_int_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_ERRORF("The vertex type vector size (%" IGRAPH_PRId ") should be equal to the number of nodes (%" IGRAPH_PRId ").", 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_ERRORF("The horizontal gap cannot be negative, got %g.", IGRAPH_EINVAL, hgap); } - IGRAPH_VECTOR_INIT_FINALLY(&layers, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&layers, no_of_nodes); for (i = 0; i < no_of_nodes; i++) { VECTOR(layers)[i] = VECTOR(*types)[i] ? 0 : 1; } @@ -74,7 +74,7 @@ int igraph_layout_bipartite(const igraph_t *graph, /*extd_to_orig_eids=*/ 0, &layers, hgap, vgap, maxiter, /*weights=*/ 0)); - igraph_vector_destroy(&layers); + igraph_vector_int_destroy(&layers); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/layout/layout_grid.c b/src/vendor/cigraph/src/layout/layout_grid.c index 144fdb65de2..16ec85b88fe 100644 --- a/src/vendor/cigraph/src/layout/layout_grid.c +++ b/src/vendor/cigraph/src/layout/layout_grid.c @@ -41,14 +41,14 @@ * * 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_error_t igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t width) { + igraph_integer_t 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)); + width = ceil(sqrt(no_of_nodes)); } x = y = 0; @@ -60,7 +60,7 @@ int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int wid } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -81,19 +81,19 @@ int igraph_layout_grid(const igraph_t *graph, igraph_matrix_t *res, long int wid * * 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_error_t igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, + igraph_integer_t width, igraph_integer_t height) { + igraph_integer_t 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)); + width = height = ceil(pow(no_of_nodes, 1.0 / 3)); } else if (width <= 0) { - width = (long int) ceil(sqrt(no_of_nodes / (double)height)); + width = ceil(sqrt(no_of_nodes / (double)height)); } else if (height <= 0) { - height = (long int) ceil(sqrt(no_of_nodes / (double)width)); + height = ceil(sqrt(no_of_nodes / (double)width)); } x = y = z = 0; @@ -109,5 +109,5 @@ int igraph_layout_grid_3d(const igraph_t *graph, igraph_matrix_t *res, } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/layout_internal.h b/src/vendor/cigraph/src/layout/layout_internal.h index b3dd8730005..f4af93e1cbd 100644 --- a/src/vendor/cigraph/src/layout/layout_internal.h +++ b/src/vendor/cigraph/src/layout/layout_internal.h @@ -25,39 +25,45 @@ #include "igraph_datatype.h" #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" +#include "igraph_matrix.h" #include "layout/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_PRIVATE_EXPORT igraph_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + igraph_integer_t 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_PRIVATE_EXPORT igraph_error_t 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_PRIVATE_EXPORT igraph_error_t 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_real_t igraph_i_layout_point_segment_dist2(igraph_real_t v_x, igraph_real_t v_y, + igraph_real_t u1_x, igraph_real_t u1_y, + igraph_real_t u2_x, igraph_real_t 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); +IGRAPH_PRIVATE_EXPORT igraph_bool_t igraph_i_layout_segments_intersect(igraph_real_t p0_x, igraph_real_t p0_y, + igraph_real_t p1_x, igraph_real_t p1_y, + igraph_real_t p2_x, igraph_real_t p2_y, + igraph_real_t p3_x, igraph_real_t p3_y); -int igraph_i_layout_random_bounded( +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, + igraph_real_t *a_p, + igraph_real_t *b_p); + +igraph_error_t igraph_i_layout_random_bounded( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy); -int igraph_i_layout_random_bounded_3d( +igraph_error_t igraph_i_layout_random_bounded_3d( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, diff --git a/src/vendor/cigraph/src/layout/layout_random.c b/src/vendor/cigraph/src/layout/layout_random.c index 13120021050..31cad0008df 100644 --- a/src/vendor/cigraph/src/layout/layout_random.c +++ b/src/vendor/cigraph/src/layout/layout_random.c @@ -42,10 +42,10 @@ * Time complexity: O(|V|), the * number of vertices. */ -int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { +igraph_error_t igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { - long int no_of_nodes = igraph_vcount(graph); - long int i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); @@ -58,7 +58,7 @@ int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { RNG_END(); - return 0; + return IGRAPH_SUCCESS; } /** @@ -79,10 +79,10 @@ int igraph_layout_random(const igraph_t *graph, igraph_matrix_t *res) { * * Time complexity: O(|V|), the number of vertices. */ -int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { +igraph_error_t igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { - long int no_of_nodes = igraph_vcount(graph); - long int i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i; IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 3)); @@ -103,7 +103,7 @@ int igraph_layout_random_3d(const igraph_t *graph, igraph_matrix_t *res) { /* The following functions generate suitable initial random layouts for * the Fruchterman-Reingold and Kamada-Kawai algorithms. */ -int igraph_i_layout_random_bounded( +igraph_error_t igraph_i_layout_random_bounded( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, @@ -160,16 +160,16 @@ int igraph_i_layout_random_bounded( igraph_real_t x2 = maxx ? VECTOR(*maxx)[i] : dmaxx; igraph_real_t y1 = miny ? VECTOR(*miny)[i] : dminy; igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; - if (!igraph_finite(x1)) { + if (!isfinite(x1)) { x1 = -width / 2; } - if (!igraph_finite(x2)) { + if (!isfinite(x2)) { x2 = width / 2; } - if (!igraph_finite(y1)) { + if (!isfinite(y1)) { y1 = -height / 2; } - if (!igraph_finite(y2)) { + if (!isfinite(y2)) { y2 = height / 2; } MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); @@ -179,7 +179,7 @@ int igraph_i_layout_random_bounded( return IGRAPH_SUCCESS; } -int igraph_i_layout_random_bounded_3d( +igraph_error_t igraph_i_layout_random_bounded_3d( const igraph_t *graph, igraph_matrix_t *res, const igraph_vector_t *minx, const igraph_vector_t *maxx, const igraph_vector_t *miny, const igraph_vector_t *maxy, @@ -257,22 +257,22 @@ int igraph_i_layout_random_bounded_3d( igraph_real_t y2 = maxy ? VECTOR(*maxy)[i] : dmaxy; igraph_real_t z1 = minz ? VECTOR(*minz)[i] : dminz; igraph_real_t z2 = maxz ? VECTOR(*maxz)[i] : dmaxz; - if (!igraph_finite(x1)) { + if (!isfinite(x1)) { x1 = -width / 2; } - if (!igraph_finite(x2)) { + if (!isfinite(x2)) { x2 = width / 2; } - if (!igraph_finite(y1)) { + if (!isfinite(y1)) { y1 = -height / 2; } - if (!igraph_finite(y2)) { + if (!isfinite(y2)) { y2 = height / 2; } - if (!igraph_finite(z1)) { + if (!isfinite(z1)) { z1 = -depth / 2; } - if (!igraph_finite(z2)) { + if (!isfinite(z2)) { z2 = depth / 2; } MATRIX(*res, i, 0) = RNG_UNIF(x1, x2); diff --git a/src/vendor/cigraph/src/layout/mds.c b/src/vendor/cigraph/src/layout/mds.c index bf6e12dca79..288e4f71c98 100644 --- a/src/vendor/cigraph/src/layout/mds.c +++ b/src/vendor/cigraph/src/layout/mds.c @@ -33,33 +33,43 @@ #include "igraph_random.h" #include "igraph_structural.h" -static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, +#include + +static igraph_error_t 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 igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, igraph_integer_t dim); -static int igraph_i_layout_mds_step(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; + IGRAPH_CHECK(igraph_blas_dgemv_array(0, 1, matrix, from, 0, to)); + return IGRAPH_SUCCESS; } /* 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) { +igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, + igraph_matrix_t *dist, igraph_integer_t dim) { - long int no_of_nodes = igraph_vcount(graph); - long int nev = dim; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t nev = dim; igraph_matrix_t vectors; igraph_vector_t values, row_means; igraph_real_t grand_mean; - long int i, j, k; + igraph_integer_t i, j, k; igraph_eigen_which_t which; + if (no_of_nodes > INT_MAX) { + IGRAPH_ERROR("Graph too large for eigenvector calculations", IGRAPH_EOVERFLOW); + } + + if (nev > INT_MAX) { + IGRAPH_ERROR("Dimensionality too large for eigenvector calculations", IGRAPH_EOVERFLOW); + } + /* Handle the trivial cases */ if (no_of_nodes == 1) { IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim)); @@ -90,7 +100,7 @@ int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, /* 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); + IGRAPH_CHECK(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++) { @@ -166,8 +176,8 @@ int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, * 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. + * case, the undirected 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. @@ -178,9 +188,9 @@ int igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t *res, * 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_error_t igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, + const igraph_matrix_t *dist, igraph_integer_t dim) { + igraph_integer_t i, no_of_nodes = igraph_vcount(graph); igraph_matrix_t m; igraph_bool_t conn; @@ -202,12 +212,10 @@ int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, /* 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)); + IGRAPH_MATRIX_INIT_FINALLY(&m, no_of_nodes, no_of_nodes); + IGRAPH_CHECK(igraph_distances(graph, &m, igraph_vss_all(), igraph_vss_all(), IGRAPH_ALL)); } else { - IGRAPH_CHECK(igraph_matrix_copy(&m, dist)); + IGRAPH_CHECK(igraph_matrix_init_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++) { @@ -222,27 +230,27 @@ int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, 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_matrix_list_t layouts; + igraph_vector_int_t vertex_order; + igraph_vector_int_t comp; igraph_t subgraph; - igraph_matrix_t *layout; + igraph_matrix_t layout; igraph_matrix_t dist_submatrix; igraph_bool_t *seen_vertices; - long int j, n, processed_vertex_count = 0; + igraph_integer_t j, n, processed_vertex_count = 0; - IGRAPH_VECTOR_INIT_FINALLY(&comp, 0); - IGRAPH_VECTOR_INIT_FINALLY(&vertex_order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&comp, 0); + IGRAPH_VECTOR_INT_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_MATRIX_LIST_INIT_FINALLY(&layouts, 0); + IGRAPH_MATRIX_INIT_FINALLY(&layout, 0, 0); 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_ERROR("cannot calculate MDS layout", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, seen_vertices); @@ -258,29 +266,19 @@ int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, 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); + IGRAPH_CHECK(igraph_matrix_select_rows_cols(&m, &dist_submatrix, &comp, &comp)); /* Lay out the subgraph */ - IGRAPH_CHECK(igraph_i_layout_mds_single(&subgraph, layout, &dist_submatrix, dim)); + 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 */ + IGRAPH_CHECK(igraph_matrix_list_push_back_copy(&layouts, &layout)); /* 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); + n = igraph_vector_int_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++; + seen_vertices[VECTOR(comp)[j]] = 1; + VECTOR(vertex_order)[VECTOR(comp)[j]] = processed_vertex_count++; } } /* Merge the layouts - reusing dist_submatrix here */ @@ -290,10 +288,11 @@ int igraph_layout_mds(const igraph_t* graph, igraph_matrix_t *res, 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); + igraph_matrix_destroy(&layout); + igraph_matrix_list_destroy(&layouts); + igraph_vector_int_destroy(&vertex_order); + igraph_vector_int_destroy(&comp); + IGRAPH_FINALLY_CLEAN(6); } RNG_END(); diff --git a/src/vendor/cigraph/src/layout/merge_dla.c b/src/vendor/cigraph/src/layout/merge_dla.c index 34e3cc46cf0..7082540aacf 100644 --- a/src/vendor/cigraph/src/layout/merge_dla.c +++ b/src/vendor/cigraph/src/layout/merge_dla.c @@ -22,10 +22,10 @@ */ #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 "layout/merge_grid.h" @@ -33,60 +33,62 @@ /** * \function igraph_layout_merge_dla - * \brief Merge multiple layouts by using a DLA algorithm + * \brief Merges multiple layouts by using a DLA algorithm. + * + * \experimental * - * * 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 coords List of matrices 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. + * Added in version 0.2. * * * 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_error_t igraph_layout_merge_dla( + const igraph_vector_ptr_t *thegraphs, const igraph_matrix_list_t *coords, + igraph_matrix_t *res +) { + igraph_integer_t coords_len = igraph_matrix_list_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_integer_t allnodes = 0; + igraph_integer_t i, j; + igraph_integer_t actg; igraph_i_layout_mergegrid_t grid; - long int jpos = 0; + igraph_integer_t jpos = 0; igraph_real_t minx, maxx, miny, maxy; igraph_real_t area = 0; igraph_real_t maxr = 0; - long int respos; + igraph_integer_t 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); + IGRAPH_VECTOR_INIT_FINALLY(&sizes, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&x, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&y, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&r, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&nx, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&ny, coords_len); + IGRAPH_VECTOR_INIT_FINALLY(&nr, coords_len); 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); + for (i = 0; i < coords_len; i++) { + igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); + igraph_integer_t size = igraph_matrix_nrow(mat); if (igraph_matrix_ncol(mat) != 2) { IGRAPH_ERROR("igraph_layout_merge_dla works for 2D layouts only", @@ -103,10 +105,9 @@ int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, } 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_get_ptr(&nx, i), + igraph_vector_get_ptr(&ny, i), + igraph_vector_get_ptr(&nr, i)); } igraph_vector_order2(&sizes); /* largest first */ @@ -120,20 +121,20 @@ int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, /* fprintf(stderr, "Ok, starting DLA\n"); */ /* 1. place the largest */ - actg = (long int) VECTOR(sizes)[jpos++]; + actg = 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) { + while (jpos < coords_len) { IGRAPH_ALLOW_INTERRUPTION(); /* fprintf(stderr, "comp: %li", jpos); */ - IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / graphs, NULL); + IGRAPH_PROGRESS("Merging layouts via DLA", (100.0 * jpos) / coords_len, NULL); - actg = (long int) VECTOR(sizes)[jpos++]; + actg = 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), + igraph_vector_get_ptr(&x, actg), + igraph_vector_get_ptr(&y, actg), VECTOR(r)[actg], 0, 0, maxx, maxx + 5); @@ -146,12 +147,12 @@ int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, /* 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]); + for (i = 0; i < coords_len; i++) { + igraph_matrix_t *mat = igraph_matrix_list_get_ptr(coords, i); + igraph_integer_t size = igraph_matrix_nrow(mat); 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; @@ -176,14 +177,14 @@ int igraph_layout_merge_dla(const igraph_vector_ptr_t *thegraphs, igraph_vector_destroy(&ny); igraph_vector_destroy(&nr); IGRAPH_FINALLY_CLEAN(8); - return 0; + return IGRAPH_SUCCESS; } -int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, +igraph_error_t 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_integer_t nodes = igraph_matrix_nrow(coords); + igraph_integer_t i; igraph_real_t xmin, xmax, ymin, ymax; xmin = xmax = MATRIX(*coords, 0, 0); @@ -206,16 +207,16 @@ int igraph_i_layout_sphere_2d(igraph_matrix_t *coords, *x = (xmin + xmax) / 2; *y = (ymin + ymax) / 2; - *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) ) / 2; + *r = sqrt((xmax - xmin)*(xmax - xmin) + (ymax - ymin)*(ymax - ymin)) / 2; - return 0; + return IGRAPH_SUCCESS; } -int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, +igraph_error_t 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_integer_t nodes = igraph_matrix_nrow(coords); + igraph_integer_t i; igraph_real_t xmin, xmax, ymin, ymax, zmin, zmax; xmin = xmax = MATRIX(*coords, 0, 0); @@ -249,16 +250,16 @@ int igraph_i_layout_sphere_3d(igraph_matrix_t *coords, *r = sqrt( (xmax - xmin) * (xmax - xmin) + (ymax - ymin) * (ymax - ymin) + (zmax - zmin) * (zmax - zmin) ) / 2; - return 0; + return IGRAPH_SUCCESS; } #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_error_t igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, + igraph_integer_t 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_integer_t sp = -1; igraph_real_t angle, len; /* The graph is not used, only its coordinates */ @@ -287,6 +288,5 @@ int igraph_i_layout_merge_dla(igraph_i_layout_mergegrid_t *grid, } } - /* fprintf(stderr, "%li ", steps); */ - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/layout/merge_grid.c b/src/vendor/cigraph/src/layout/merge_grid.c index 5b7d94be641..38e36d54f1e 100644 --- a/src/vendor/cigraph/src/layout/merge_grid.c +++ b/src/vendor/cigraph/src/layout/merge_grid.c @@ -20,19 +20,20 @@ */ +#include "layout/merge_grid.h" + #include "igraph_memory.h" -#include "layout/merge_grid.h" -static int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, +static igraph_error_t 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) { + igraph_integer_t *x, igraph_integer_t *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)); + *x = floor((xc - (grid->minx)) / (grid->deltax)); } if (yc <= grid->miny) { @@ -40,15 +41,15 @@ static int igraph_i_layout_mergegrid_which(igraph_i_layout_mergegrid_t *grid, } else if (yc >= grid->maxy) { *y = grid->stepsy - 1; } else { - *y = (long int) floor((yc - (grid->miny)) / (grid->deltay)); + *y = floor((yc - (grid->miny)) / (grid->deltay)); } - return 0; + return IGRAPH_SUCCESS; } -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_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, + igraph_real_t miny, igraph_real_t maxy, igraph_integer_t stepsy) { grid->minx = minx; grid->maxx = maxx; grid->stepsx = stepsx; @@ -58,11 +59,11 @@ int igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, grid->stepsy = stepsy; grid->deltay = (maxy - miny) / stepsy; - grid->data = IGRAPH_CALLOC(stepsx * stepsy, long int); + grid->data = IGRAPH_CALLOC(stepsx * stepsy, igraph_integer_t); if (grid->data == 0) { - IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot create grid", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } - return 0; + return IGRAPH_SUCCESS; } void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { @@ -72,11 +73,11 @@ void igraph_i_layout_mergegrid_destroy(igraph_i_layout_mergegrid_t *grid) { #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_error_t 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_integer_t id) { + igraph_integer_t cx, cy; + igraph_integer_t i, j; igraph_i_layout_mergegrid_which(grid, x, y, &cx, &cy); @@ -124,13 +125,13 @@ int igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, #undef DIST #undef DIST2 - return 0; + return IGRAPH_SUCCESS; } -long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, +igraph_integer_t 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; + igraph_integer_t cx, cy; + igraph_integer_t res; if (x <= grid->minx || x >= grid->maxx || y <= grid->miny || y >= grid->maxy) { @@ -145,11 +146,11 @@ long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, #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_integer_t 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; + igraph_integer_t cx, cy; + igraph_integer_t i, j; + igraph_integer_t ret; if (x - r <= grid->minx || x + r >= grid->maxx || y - r <= grid->miny || y + r >= grid->maxy) { @@ -206,7 +207,7 @@ long int igraph_i_layout_mergegrid_get_sphere(igraph_i_layout_mergegrid_t *grid, } /* int print_grid(igraph_i_layout_mergegrid_t *grid) { */ -/* long int i,j; */ +/* igraph_integer_t i,j; */ /* for (i=0; istepsx; i++) { */ /* for (j=0; jstepsy; j++) { */ diff --git a/src/vendor/cigraph/src/layout/merge_grid.h b/src/vendor/cigraph/src/layout/merge_grid.h index 70fcfeed01c..82c65184a95 100644 --- a/src/vendor/cigraph/src/layout/merge_grid.h +++ b/src/vendor/cigraph/src/layout/merge_grid.h @@ -24,6 +24,7 @@ #define IGRAPH_LAYOUT_MERGE_GRID_H #include "igraph_decls.h" +#include "igraph_error.h" #include "igraph_types.h" __BEGIN_DECLS @@ -31,26 +32,26 @@ __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_integer_t *data; + igraph_integer_t 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 igraph_error_t igraph_i_layout_mergegrid_init(igraph_i_layout_mergegrid_t *grid, + igraph_real_t minx, igraph_real_t maxx, igraph_integer_t stepsx, + igraph_real_t miny, igraph_real_t maxy, igraph_integer_t 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); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_merge_place_sphere(igraph_i_layout_mergegrid_t *grid, + igraph_real_t x, igraph_real_t y, igraph_real_t r, + igraph_integer_t id); -long int igraph_i_layout_mergegrid_get(igraph_i_layout_mergegrid_t *grid, +igraph_integer_t 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_integer_t 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 diff --git a/src/vendor/cigraph/src/layout/reingold_tilford.c b/src/vendor/cigraph/src/layout/reingold_tilford.c index 7d101803e91..607368e1522 100644 --- a/src/vendor/cigraph/src/layout/reingold_tilford.c +++ b/src/vendor/cigraph/src/layout/reingold_tilford.c @@ -34,53 +34,53 @@ #include "core/math.h" -static int igraph_i_layout_reingold_tilford_unreachable( +static igraph_error_t 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_integer_t real_root, + igraph_integer_t no_of_nodes, + igraph_vector_int_t *pnewedges) { + + igraph_integer_t no_of_newedges; + igraph_vector_bool_t visited; + igraph_integer_t i, j, n; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; igraph_adjlist_t allneis; igraph_vector_int_t *neis; - igraph_vector_resize(pnewedges, 0); + igraph_vector_int_clear(pnewedges); /* 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_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); + IGRAPH_DQUEUE_INT_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); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, real_root)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); neis = igraph_adjlist_get(&allneis, actnode); n = igraph_vector_int_size(neis); - VECTOR(visited)[actnode] = 1; + VECTOR(visited)[actnode] = true; 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)); + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (!VECTOR(visited)[neighbor]) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); } } } for (j = 0; j < no_of_nodes; j++) { - no_of_newedges += 1 - VECTOR(visited)[j]; + no_of_newedges += VECTOR(visited)[j] ? 0 : 1; } /* 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); + igraph_vector_int_resize(pnewedges, no_of_newedges * 2); j = 0; for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(visited)[i]) { @@ -96,9 +96,9 @@ static int igraph_i_layout_reingold_tilford_unreachable( } } - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); igraph_adjlist_destroy(&allneis); - igraph_vector_destroy(&visited); + igraph_vector_bool_destroy(&visited); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -107,50 +107,50 @@ static int igraph_i_layout_reingold_tilford_unreachable( /* 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_integer_t parent; /* Parent node index */ + igraph_integer_t level; /* Level of the node */ igraph_real_t offset; /* X offset from parent node */ - long int left_contour; /* Next left node of the contour + igraph_integer_t 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 + igraph_integer_t 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_integer_t left_extreme; /* Leftmost node on the deepest layer of the subtree rooted at this node */ + igraph_integer_t 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); +static void igraph_i_layout_reingold_tilford_postorder(struct igraph_i_reingold_tilford_vertex *vdata, + igraph_integer_t node, igraph_integer_t vcount); +static void igraph_i_layout_reingold_tilford_calc_coords(struct igraph_i_reingold_tilford_vertex *vdata, + igraph_matrix_t *res, igraph_integer_t node, + igraph_integer_t 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, +static igraph_error_t 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_integer_t root) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, n, j; + igraph_dqueue_int_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_DQUEUE_INT_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_ERROR("igraph_layout_reingold_tilford failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, vdata); @@ -172,22 +172,22 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, 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); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, root)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_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]; + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); vdata[neighbor].parent = actnode; vdata[neighbor].level = actdist + 1; } @@ -200,7 +200,7 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, /* 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_dqueue_int_destroy(&q); igraph_adjlist_destroy(&allneis); igraph_free(vdata); IGRAPH_FINALLY_CLEAN(3); @@ -210,14 +210,14 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, #ifdef LAYOUT_RT_DEBUG for (i = 0; i < no_of_nodes; i++) { printf( - "%3ld: offset = %.2f, contours = [%ld, %ld], contour offsets = [%.2f, %.2f]\n", + "%3" IGRAPH_PRId ": offset = %.2f, contours = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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", + " extrema = [%" IGRAPH_PRId ", %" IGRAPH_PRId "], 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 ); @@ -225,16 +225,16 @@ static int igraph_i_layout_reingold_tilford(const igraph_t *graph, } #endif - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_layout_reingold_tilford_calc_coords( +static void 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; + igraph_matrix_t *res, igraph_integer_t node, + igraph_integer_t vcount, igraph_real_t xpos) { + MATRIX(*res, node, 0) = xpos; - for (i = 0; i < vcount; i++) { + for (igraph_integer_t i = 0; i < vcount; i++) { if (i == node) { continue; } @@ -243,23 +243,23 @@ static int igraph_i_layout_reingold_tilford_calc_coords( xpos + vdata[i].offset); } } - return 0; } -static int igraph_i_layout_reingold_tilford_postorder( +static void 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; + igraph_integer_t node, igraph_integer_t vcount) { + + igraph_integer_t childcount, leftroot, leftrootidx; const igraph_real_t minsep = 1; igraph_real_t avg; #ifdef LAYOUT_RT_DEBUG - printf("Starting visiting node %ld\n", node); + printf("Starting visiting node %" IGRAPH_PRId "\n", node); #endif /* Check whether this node is a leaf node */ childcount = 0; - for (i = 0; i < vcount; i++) { + for (igraph_integer_t i = 0; i < vcount; i++) { if (i == node) { continue; } @@ -271,7 +271,7 @@ static int igraph_i_layout_reingold_tilford_postorder( } if (childcount == 0) { - return 0; + return; } /* Here we can assume that all of the subtrees have been placed and their @@ -285,9 +285,9 @@ static int igraph_i_layout_reingold_tilford_postorder( leftroot = leftrootidx = -1; avg = 0.0; #ifdef LAYOUT_RT_DEBUG - printf("Visited node %ld and arranged its subtrees\n", node); + printf("Visited node %" IGRAPH_PRId " and arranged its subtrees\n", node); #endif - for (i = 0, j = 0; i < vcount; i++) { + for (igraph_integer_t i = 0, j = 0; i < vcount; i++) { if (i == node) { continue; } @@ -295,11 +295,11 @@ static int igraph_i_layout_reingold_tilford_postorder( 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_integer_t 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); + printf(" Placing child %" IGRAPH_PRId " on level %" IGRAPH_PRId ", to the right of %" IGRAPH_PRId "\n", i, vdata[i].level, leftroot); #endif lnode = leftroot; rnode = i; rootsep = vdata[leftroot].offset + minsep; @@ -311,7 +311,7 @@ static int igraph_i_layout_reingold_tilford_postorder( vdata[node].offset_to_right_contour = rootsep; #ifdef LAYOUT_RT_DEBUG - printf(" Contour: [%ld, %ld], offsets: [%lf, %lf], rootsep: %lf\n", + printf(" Contour: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep); #endif while ((lnode >= 0) && (rnode >= 0)) { @@ -341,8 +341,8 @@ static int igraph_i_layout_reingold_tilford_postorder( 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); + printf(" Left subtree ended earlier, continuing left subtree's left and right contour on right subtree (node %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, vdata[rnode].left_contour); + printf(" New contour following offset for node %" IGRAPH_PRId " is %lf\n", auxnode, vdata[auxnode].offset_to_left_contour); #endif } else { /* Both subtrees are ending at the same time; the @@ -376,14 +376,14 @@ static int igraph_i_layout_reingold_tilford_postorder( * 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); + printf(" Right subtree ended earlier, continuing right subtree's left and right contour on left subtree (node %" IGRAPH_PRId " gets connected to node %" IGRAPH_PRId ")\n", auxnode, lnode); + printf(" New contour following offset for node %" IGRAPH_PRId " 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", + printf(" Contour: [%" IGRAPH_PRId ", %" IGRAPH_PRId "], offsets: [%lf, %lf], rootsep: %lf\n", lnode, rnode, loffset, roffset, rootsep); #endif @@ -399,7 +399,7 @@ static int igraph_i_layout_reingold_tilford_postorder( } #ifdef LAYOUT_RT_DEBUG - printf(" Offset of subtree with root node %ld will be %lf\n", i, rootsep); + printf(" Offset of subtree with root node %" IGRAPH_PRId " will be %lf\n", i, rootsep); #endif vdata[i].offset = rootsep; vdata[node].offset_to_right_contour = rootsep; @@ -407,10 +407,10 @@ static int igraph_i_layout_reingold_tilford_postorder( 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 */ + /* 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); + printf(" Placing child %" IGRAPH_PRId " on level %" IGRAPH_PRId " as first child\n", i, vdata[i].level); #endif leftrootidx = j; leftroot = i; @@ -428,13 +428,13 @@ static int igraph_i_layout_reingold_tilford_postorder( } } #ifdef LAYOUT_RT_DEBUG - printf("Shifting node %ld to be centered above children. Shift amount: %lf\n", node, avg); + printf("Shifting node %" IGRAPH_PRId " 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++) { + for (igraph_integer_t i = 0; i < vcount; i++) { if (i == node) { continue; } @@ -442,19 +442,17 @@ static int igraph_i_layout_reingold_tilford_postorder( 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( +igraph_error_t igraph_i_layout_reingold_tilford_cluster_degrees_directed( const igraph_t *graph, - const igraph_vector_t *membership, + const igraph_vector_int_t *membership, igraph_integer_t no_comps, igraph_neimode_t mode, - igraph_vector_t *degrees) { + igraph_vector_int_t *degrees) { igraph_eit_t eit; @@ -462,8 +460,8 @@ int igraph_i_layout_reingold_tilford_cluster_degrees_directed( IGRAPH_ERROR("Directed graph expected.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_vector_resize(degrees, no_comps)); - igraph_vector_null(degrees); + IGRAPH_CHECK(igraph_vector_int_resize(degrees, no_comps)); + igraph_vector_int_null(degrees); IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(IGRAPH_EDGEORDER_ID), &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); @@ -499,33 +497,83 @@ int igraph_i_layout_reingold_tilford_cluster_degrees_directed( * 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 + * either lowest eccentricity (if 'use_eccentricity' is true) or based on * highest degree (out- or in-degree in directed mode). */ -int igraph_i_layout_reingold_tilford_select_roots( + +/** + * \function igraph_roots_for_tree_layout + * \brief Roots suitable for a nice tree layout. + * + * This function chooses a root, or a set of roots suitable for visualizing a tree, + * or a tree-like graph. It is typically used with \ref igraph_layout_reingold_tilford(). + * 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, vertices are prioritized based on the given \p heuristic. + * + * \param graph The graph, typically a tree, but any graph is accepted. + * \param mode Whether to interpret the input as undirected, a directed out-tree or in-tree. + * \param roots An initialized integer vector, the roots will be returned here. + * \param heuristic The heuristic to use for breaking ties when multiple root + * choices are possible. + * \clist + * \cli IGRAPH_ROOT_CHOICE_DEGREE + * Choose the vertices with the highest degree (out- or in-degree + * in directed mode). This simple heuristic is fast even in large graphs. + * \cli IGRAPH_ROOT_CHOICE_ECCENTRICITY + * Choose the vertices with the lowest eccentricity. This usually results + * in a "wide and shallow" tree layout. While this heuristic produces + * high-quality results, it is slow for large graphs: computing the + * eccentricities has quadratic complexity in the number of vertices. + * \endclist + * \return Error code. + * + * Time complexity: depends on the heuristic. + */ +igraph_error_t igraph_roots_for_tree_layout( const igraph_t *graph, igraph_neimode_t mode, - igraph_vector_t *roots, - igraph_bool_t use_eccentricity) { + igraph_vector_int_t *roots, + igraph_root_choice_t heuristic) { igraph_integer_t no_of_nodes = igraph_vcount(graph); - igraph_vector_t order, membership; + igraph_vector_int_t order, membership; igraph_integer_t no_comps; - long int i, j; + igraph_integer_t i, j; + igraph_bool_t use_eccentricity; + + switch (heuristic) { + case IGRAPH_ROOT_CHOICE_DEGREE: + use_eccentricity = false; break; + case IGRAPH_ROOT_CHOICE_ECCENTRICITY: + use_eccentricity = true; break; + default: + IGRAPH_ERROR("Invalid root choice heuristic given.", IGRAPH_EINVAL); + } if (! igraph_is_directed(graph)) { mode = IGRAPH_ALL; } - IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); + if (no_of_nodes == 0) { + igraph_vector_int_clear(roots); + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); if (use_eccentricity) { - /* Sort vertices by decreasing eccenticity. */ + /* Sort vertices by decreasing eccentricity. */ 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_CHECK(igraph_vector_qsort_ind(&ecc, &order, IGRAPH_ASCENDING)); igraph_vector_destroy(&ecc); IGRAPH_FINALLY_CLEAN(1); @@ -536,12 +584,14 @@ int igraph_i_layout_reingold_tilford_select_roots( 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_VECTOR_INT_INIT_FINALLY(&membership, no_of_nodes); + IGRAPH_CHECK(igraph_connected_components( + 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 */ + IGRAPH_CHECK(igraph_vector_int_resize(roots, no_comps)); + igraph_vector_int_fill(roots, -1); /* -1 signifies a not-yet-determined root for a component */ if (mode != IGRAPH_ALL) { /* Directed case: @@ -551,9 +601,9 @@ int igraph_i_layout_reingold_tilford_select_roots( * 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_int_t cluster_degrees; - IGRAPH_VECTOR_INIT_FINALLY(&cluster_degrees, no_of_nodes); + IGRAPH_VECTOR_INT_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 */ @@ -563,14 +613,14 @@ int igraph_i_layout_reingold_tilford_select_roots( * 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]; + igraph_integer_t v = VECTOR(order)[i]; + igraph_integer_t cl = VECTOR(membership)[v]; if (VECTOR(cluster_degrees)[cl] == 0 && VECTOR(*roots)[cl] == -1) { VECTOR(*roots)[cl] = v; } } - igraph_vector_destroy(&cluster_degrees); + igraph_vector_int_destroy(&cluster_degrees); IGRAPH_FINALLY_CLEAN(1); /* Remove remaining -1 indices. These correspond to components that @@ -581,7 +631,7 @@ int igraph_i_layout_reingold_tilford_select_roots( } VECTOR(*roots)[j++] = VECTOR(*roots)[i]; } - igraph_vector_resize(roots, j); + igraph_vector_int_resize(roots, j); } else { /* Undirected case: @@ -589,11 +639,11 @@ int igraph_i_layout_reingold_tilford_select_roots( * Select the highest degree node from each component. */ - long int no_seen = 0; + igraph_integer_t no_seen = 0; for (i=0; i < no_of_nodes; ++i) { - long int v = VECTOR(order)[i]; - long int cl = VECTOR(membership)[v]; + igraph_integer_t v = VECTOR(order)[i]; + igraph_integer_t cl = VECTOR(membership)[v]; if (VECTOR(*roots)[cl] == -1) { no_seen += 1; VECTOR(*roots)[cl] = v; @@ -605,8 +655,8 @@ int igraph_i_layout_reingold_tilford_select_roots( } } - igraph_vector_destroy(&membership); - igraph_vector_destroy(&order); + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -614,7 +664,7 @@ int igraph_i_layout_reingold_tilford_select_roots( /** * \function igraph_layout_reingold_tilford - * \brief Reingold-Tilford layout for tree graphs + * \brief Reingold-Tilford layout for tree graphs. * * * Arranges the nodes in a tree where the given node is used as the root. @@ -641,13 +691,14 @@ int igraph_i_layout_reingold_tilford_select_roots( * 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 + * Simply put, in the undirected 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. + * selection prefers low eccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. + * The root selection heuristic may change without notice. To ensure a consistent + * output, please specify the roots manually. The \ref igraph_roots_for_tree_layout() + * function gives more control over automatic root selection. * \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 @@ -657,26 +708,25 @@ int igraph_i_layout_reingold_tilford_select_roots( * * Added in version 0.2. * - * \sa \ref igraph_layout_reingold_tilford_circular(). + * \sa \ref igraph_layout_reingold_tilford_circular(), \ref igraph_roots_for_tree_layout() * * \example examples/simple/igraph_layout_reingold_tilford.c */ -int igraph_layout_reingold_tilford(const igraph_t *graph, +igraph_error_t 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) { + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel) { - long int no_of_nodes_orig = igraph_vcount(graph); - long int no_of_nodes = no_of_nodes_orig; - long int real_root; + const igraph_integer_t no_of_nodes_orig = igraph_vcount(graph); + igraph_integer_t no_of_nodes = no_of_nodes_orig; + igraph_integer_t real_root; igraph_t extended; const igraph_t *pextended = graph; - igraph_vector_t myroots; - const igraph_vector_t *proots = roots; + igraph_vector_int_t myroots; + const igraph_vector_int_t *proots = roots; + igraph_vector_int_t newedges; - 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 @@ -685,61 +735,60 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, */ /* at various steps it might be necessary to add edges to the graph */ - IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0); + IGRAPH_VECTOR_INT_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 ) { + if ( (!roots || igraph_vector_int_size(roots) == 0) && + rootlevel && igraph_vector_int_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) { + if (!roots || igraph_vector_int_size(roots) == 0) { - IGRAPH_VECTOR_INIT_FINALLY(&myroots, 0); - igraph_i_layout_reingold_tilford_select_roots(graph, mode, &myroots, no_of_nodes < 500); + IGRAPH_VECTOR_INT_INIT_FINALLY(&myroots, 0); + igraph_roots_for_tree_layout(graph, mode, &myroots, + no_of_nodes < 500 ? IGRAPH_ROOT_CHOICE_DEGREE : IGRAPH_ROOT_CHOICE_ECCENTRICITY); proots = &myroots; - } else if (rootlevel && igraph_vector_size(rootlevel) > 0 && - igraph_vector_size(roots) > 1) { + } else if (rootlevel && igraph_vector_int_size(rootlevel) > 0 && + igraph_vector_int_size(roots) > 1) { /* ----------------------------------------------------------------------- */ /* Many roots were given to us, check 'rootlevel' */ - long int plus_levels = 0; - long int i; + igraph_integer_t plus_levels = 0; - if (igraph_vector_size(roots) != igraph_vector_size(rootlevel)) { + if (igraph_vector_int_size(roots) != igraph_vector_int_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++) { + for (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { plus_levels += VECTOR(*rootlevel)[i]; } /* make copy of graph, add vertices/edges */ if (plus_levels != 0) { - long int edgeptr = 0; + igraph_integer_t 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_CHECK(igraph_add_vertices(&extended, plus_levels, 0)); - igraph_vector_resize(&newedges, plus_levels * 2); + igraph_vector_int_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; + for (igraph_integer_t i = 0; i < igraph_vector_int_size(roots); i++) { + igraph_integer_t rl = VECTOR(*rootlevel)[i]; + igraph_integer_t rn = VECTOR(*roots)[i]; + igraph_integer_t j; /* zero-level roots don't get anything special */ if (rl == 0) { @@ -803,15 +852,15 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, 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 (igraph_vector_int_size(proots) == 1) { + real_root = VECTOR(*proots)[0]; if (real_root < 0 || real_root >= no_of_nodes) { - IGRAPH_ERROR("Invalid vertex id.", IGRAPH_EINVVID); + IGRAPH_ERROR("Invalid vertex ID.", IGRAPH_EINVVID); } /* else, we need to make real_root */ } else { - long int no_of_newedges; + igraph_integer_t no_of_newedges; /* Make copy of the graph unless it exists already */ if (pextended == graph) { @@ -826,9 +875,9 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, 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++) { + no_of_newedges = igraph_vector_int_size(proots); + igraph_vector_int_resize(&newedges, no_of_newedges * 2); + for (igraph_integer_t i = 0; i < no_of_newedges; i++) { VECTOR(newedges)[2 * i] = no_of_nodes - 1; VECTOR(newedges)[2 * i + 1] = VECTOR(*proots)[i]; } @@ -839,7 +888,7 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, /* 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) { + if (igraph_vector_int_size(&newedges) != 0) { /* Make copy of the graph unless it exists already */ if (pextended == graph) { pextended = &extended; @@ -849,7 +898,7 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, IGRAPH_CHECK(igraph_add_edges(&extended, &newedges, 0)); } - igraph_vector_destroy(&newedges); + igraph_vector_int_destroy(&newedges); IGRAPH_FINALLY_CLEAN(1); /* ----------------------------------------------------------------------- */ @@ -862,7 +911,7 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, IGRAPH_CHECK(igraph_matrix_remove_row(res, no_of_nodes_orig)); } else { igraph_matrix_t tmp; - long int i; + igraph_integer_t 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); @@ -881,18 +930,17 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, /* Remove the roots vector if it was created by us */ if (proots != roots) { - igraph_vector_destroy(&myroots); + igraph_vector_int_destroy(&myroots); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_layout_reingold_tilford_circular - * \brief Circular Reingold-Tilford layout for trees + * \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. * @@ -907,12 +955,12 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, * 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 + * Simply put, in the undirected 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 + * selection prefers low eccentricity vertices in graphs with fewer than + * 500 vertices, and high degree vertices (according to \p mode) in larger graphs. + * The root selection 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 @@ -923,14 +971,13 @@ int igraph_layout_reingold_tilford(const igraph_t *graph, * * \sa \ref igraph_layout_reingold_tilford(). */ -int igraph_layout_reingold_tilford_circular(const igraph_t *graph, +igraph_error_t 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) { + const igraph_vector_int_t *roots, + const igraph_vector_int_t *rootlevel) { - long int no_of_nodes = igraph_vcount(graph); - long int i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t ratio; igraph_real_t minx, maxx; @@ -943,7 +990,7 @@ int igraph_layout_reingold_tilford_circular(const igraph_t *graph, 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++) { + for (igraph_integer_t i = 1; i < no_of_nodes; i++) { if (MATRIX(*res, i, 0) > maxx) { maxx = MATRIX(*res, i, 0); } @@ -954,7 +1001,7 @@ int igraph_layout_reingold_tilford_circular(const igraph_t *graph, if (maxx > minx) { ratio /= (maxx - minx); } - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t 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); diff --git a/src/vendor/cigraph/src/layout/sugiyama.c b/src/vendor/cigraph/src/layout/sugiyama.c index a046e11c79e..37eaa740e33 100644 --- a/src/vendor/cigraph/src/layout/sugiyama.c +++ b/src/vendor/cigraph/src/layout/sugiyama.c @@ -23,7 +23,7 @@ */ #include "igraph_layout.h" -#include "igraph_centrality.h" + #include "igraph_components.h" #include "igraph_constants.h" #include "igraph_constructors.h" @@ -155,38 +155,29 @@ static void debug(const char* fmt, ...) { * Data structure to store a layering of the graph. */ typedef struct { - igraph_vector_ptr_t layers; + igraph_vector_int_list_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; +static igraph_error_t igraph_i_layering_init(igraph_i_layering_t* layering, + const igraph_vector_int_t* membership) { + igraph_integer_t i, n, num_layers; - if (igraph_vector_size(membership) == 0) { + if (igraph_vector_int_size(membership) == 0) { num_layers = 0; } else { - num_layers = (long int) igraph_vector_max(membership) + 1; + num_layers = igraph_vector_int_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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&layering->layers, num_layers); - n = igraph_vector_size(membership); + n = igraph_vector_int_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_integer_t l = VECTOR(*membership)[i]; + igraph_vector_int_t* vec = igraph_vector_int_list_get_ptr(&layering->layers, l); + IGRAPH_CHECK(igraph_vector_int_push_back(vec, i)); } IGRAPH_FINALLY_CLEAN(1); @@ -198,22 +189,22 @@ static int igraph_i_layering_init(igraph_i_layering_t* layering, * Destroys a layering. */ static void igraph_i_layering_destroy(igraph_i_layering_t* layering) { - igraph_vector_ptr_destroy_all(&layering->layers); + igraph_vector_int_list_destroy(&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); +static igraph_integer_t igraph_i_layering_num_layers(const igraph_i_layering_t* layering) { + return igraph_vector_int_list_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]; +static igraph_vector_int_t* igraph_i_layering_get(const igraph_i_layering_t* layering, + igraph_integer_t index) { + return igraph_vector_int_list_get_ptr(&layering->layers, index); } @@ -221,12 +212,12 @@ static igraph_vector_t* igraph_i_layering_get(const igraph_i_layering_t* layerin * 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, +static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_int_t* membership); +static igraph_error_t 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_integer_t maxiter); +static igraph_error_t 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); @@ -307,57 +298,56 @@ static INLINE igraph_real_t igraph_i_median_4(igraph_real_t x1, * 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_error_t igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, + igraph_t *extd_graph, igraph_vector_int_t *extd_to_orig_eids, + const igraph_vector_int_t* layers, igraph_real_t hgap, igraph_real_t vgap, + igraph_integer_t maxiter, const igraph_vector_t *weights) { + igraph_integer_t i, j, k, l, m, nei; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t comp_idx; + igraph_integer_t 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_vector_int_t membership; /* components of the original graph */ + igraph_vector_int_t extd_edgelist; /* edge list of the extended graph */ + igraph_vector_int_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) { + if (layers && igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&extd_edgelist, 0); if (extd_to_orig_eids != 0) { - igraph_vector_clear(extd_to_orig_eids); + igraph_vector_int_clear(extd_to_orig_eids); } } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); - IGRAPH_VECTOR_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_vector_int_init_copy(&layers_own, layers)); + IGRAPH_FINALLY(igraph_vector_int_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; + igraph_vector_int_t inds; + IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 0); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&layers_own, &inds, IGRAPH_ASCENDING)); + j = -1; dx = VECTOR(layers_own)[VECTOR(inds)[0]] - 1; for (i = 0; i < no_of_nodes; i++) { - k = (long int)VECTOR(inds)[i]; + k = VECTOR(inds)[i]; if (VECTOR(layers_own)[k] > dx) { /* New layer starts here */ dx = VECTOR(layers_own)[k]; @@ -366,38 +356,37 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, } VECTOR(layers_own)[k] = j; } - igraph_vector_destroy(&inds); + igraph_vector_int_destroy(&inds); IGRAPH_FINALLY_CLEAN(1); } /* 2. Find the connected components. */ - IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, - IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(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 */ + igraph_integer_t component_size, next_new_vertex_id; + igraph_vector_int_t old2new_vertex_ids; + igraph_vector_int_t new2old_vertex_ids; + igraph_vector_int_t new_layers; + igraph_vector_int_t edgelist; + igraph_vector_int_t neis; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new2old_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&old2new_vertex_ids, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layers, 0); + + igraph_vector_int_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])); + IGRAPH_CHECK(igraph_vector_int_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++; @@ -414,11 +403,10 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, /* 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); + IGRAPH_CHECK(igraph_incident(graph, &neis, i, IGRAPH_OUT)); + j = igraph_vector_int_size(&neis); for (k = 0; k < j; k++) { - long int eid = (long int) VECTOR(neis)[k]; + igraph_integer_t eid = VECTOR(neis)[k]; if (directed) { nei = IGRAPH_TO(graph, eid); } else { @@ -431,68 +419,68 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, /* 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) { - IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_int_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, + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, VECTOR(old2new_vertex_ids)[nei])); - for (l = (long int) VECTOR(layers_own)[nei] + 1; + for (l = 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_int_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); } - IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_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; + for (l = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id - m)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(extd_to_orig_eids, eid)); } } - IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) { - IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); } } } else { /* Edge goes downwards */ - IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, VECTOR(old2new_vertex_ids)[i])); - for (l = (long int) VECTOR(layers_own)[i] + 1; + for (l = 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_int_push_back(&new_layers, l)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edgelist, next_new_vertex_id++)); } - IGRAPH_CHECK(igraph_vector_push_back(&edgelist, + IGRAPH_CHECK(igraph_vector_int_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; + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, i)); + for (l = 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++)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, next_extd_vertex_id)); + IGRAPH_CHECK(igraph_vector_int_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_int_push_back(extd_to_orig_eids, eid)); } } - IGRAPH_CHECK(igraph_vector_push_back(&extd_edgelist, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&extd_edgelist, nei)); if (extd_to_orig_eids != 0) { - IGRAPH_CHECK(igraph_vector_push_back(extd_to_orig_eids, eid)); + IGRAPH_CHECK(igraph_vector_int_push_back(extd_to_orig_eids, eid)); } } } @@ -508,13 +496,12 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, 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_CHECK(igraph_create(&subgraph, &edgelist, next_new_vertex_id, 1)); IGRAPH_FINALLY(igraph_destroy, &subgraph); /* - igraph_vector_print(&edgelist); - igraph_vector_print(&new_layers); + igraph_vector_int_print(&edgelist); + igraph_vector_int_print(&new_layers); */ /* Assign the vertical coordinates */ @@ -533,7 +520,7 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, /* 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)); + &layering, hgap, 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 */ @@ -542,16 +529,16 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *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]; + l = 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)]; + MATRIX(*res, l, 1) = VECTOR(layer_to_y)[(igraph_integer_t) 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)]; + MATRIX(*res, k, 1) = VECTOR(layer_to_y)[(igraph_integer_t) MATRIX(layout, i, 1)]; if (dx2 < MATRIX(*res, k, 0)) { dx2 = MATRIX(*res, k, 0); } @@ -565,37 +552,36 @@ int igraph_layout_sugiyama(const igraph_t *graph, igraph_matrix_t *res, 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_vector_int_destroy(&new_layers); + igraph_vector_int_destroy(&old2new_vertex_ids); + igraph_vector_int_destroy(&new2old_vertex_ids); + igraph_vector_int_destroy(&edgelist); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(5); } - igraph_vector_destroy(&layers_own); + igraph_vector_int_destroy(&layers_own); igraph_vector_destroy(&layer_to_y); - igraph_vector_destroy(&membership); + igraph_vector_int_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_CHECK(igraph_create(extd_graph, &extd_edgelist, next_extd_vertex_id, igraph_is_directed(graph))); + igraph_vector_int_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)); +static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph, + const igraph_vector_t* weights, igraph_vector_int_t* membership) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); if (no_of_edges == 0) { - igraph_vector_fill(membership, 0); + igraph_vector_int_fill(membership, 0); return IGRAPH_SUCCESS; } @@ -603,32 +589,37 @@ static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph 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; + igraph_integer_t i, j; + igraph_vector_t outdegs, indegs; + igraph_vector_int_t feedback_edges; glp_prob *ip; glp_smcp parm; + if (no_of_edges > INT_MAX) { + IGRAPH_ERROR("Number of edges in graph too large for GLPK.", IGRAPH_EOVERFLOW); + } + /* 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_INT_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); + igraph_vector_int_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); + j = igraph_vector_int_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); + igraph_integer_t eid = VECTOR(feedback_edges)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); VECTOR(outdegs)[from] -= weights ? VECTOR(*weights)[eid] : 1; VECTOR(indegs)[to] -= weights ? VECTOR(*weights)[eid] : 1; } @@ -654,13 +645,13 @@ static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph /* Add constraints */ glp_add_rows(ip, (int) no_of_edges); - IGRAPH_CHECK(igraph_vector_push_back(&feedback_edges, -1)); + IGRAPH_CHECK(igraph_vector_int_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; + ind[1] = (int) IGRAPH_FROM(graph, i) + 1; + ind[2] = (int) IGRAPH_TO(graph, i) + 1; if (ind[1] == ind[2]) { if (VECTOR(feedback_edges)[j] == i) { @@ -690,7 +681,7 @@ static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph } glp_delete_prob(ip); - igraph_vector_destroy(&feedback_edges); + igraph_vector_int_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)); @@ -708,36 +699,35 @@ static int igraph_i_layout_sugiyama_place_nodes_vertically(const igraph_t* graph 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, +static igraph_error_t igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, + const igraph_i_layering_t* layering, igraph_integer_t 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_integer_t i, j, m, n; + igraph_vector_int_t* layer_members = igraph_i_layering_get(layering, layer_index); + igraph_vector_int_t neis; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - n = igraph_vector_size(layer_members); + n = igraph_vector_int_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, VECTOR(*layer_members)[i], direction)); + m = igraph_vector_int_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] += MATRIX(*layout, (igraph_integer_t) VECTOR(neis)[j], 0); } VECTOR(*barycenters)[i] /= m; } } - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -748,33 +738,37 @@ static int igraph_i_layout_sugiyama_calculate_barycenters(const igraph_t* graph, * 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, +static igraph_error_t 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_integer_t maxiter) { + igraph_integer_t i, n, nei; + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t iter, layer_index; + igraph_vector_int_t* layer_members; + igraph_vector_int_t new_layer_members; + igraph_vector_int_t neis; + igraph_vector_t barycenters; + igraph_vector_int_t 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); + igraph_integer_t *xs = IGRAPH_CALLOC(no_of_layers, igraph_integer_t); if (xs == 0) { - IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); + IGRAPH_ERROR("cannot order nodes horizontally", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } for (i = 0; i < no_of_vertices; i++) { - MATRIX(*layout, i, 0) = xs[(long int)MATRIX(*layout, i, 1)]++; + MATRIX(*layout, i, 0) = xs[(igraph_integer_t)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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_layer_members, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sort_indices, 0); /* Start the effective part of the Sugiyama algorithm */ iter = 0; changed = 1; @@ -786,27 +780,27 @@ static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* gra /* 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); + n = igraph_vector_int_size(layer_members); + IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); 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("Vertices: "); igraph_vector_int_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif - IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, - &sort_indices, 0)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); for (i = 0; i < n; i++) { - nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; - VECTOR(barycenters)[i] = nei; + nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; + VECTOR(new_layer_members)[i] = nei; MATRIX(*layout, nei, 0) = i; } - if (!igraph_vector_all_e(layer_members, &barycenters)) { - IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); + if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { + IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); #ifdef SUGIYAMA_DEBUG - printf("New vertex order: "); igraph_vector_print(layer_members); + printf("New vertex order: "); igraph_vector_int_print(layer_members); #endif changed = 1; } else { @@ -819,28 +813,28 @@ static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* gra /* 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); + n = igraph_vector_int_size(layer_members); + IGRAPH_CHECK(igraph_vector_int_resize(&new_layer_members, n)); 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("Vertices: "); igraph_vector_int_print(layer_members); printf("Barycenters: "); igraph_vector_print(&barycenters); #endif - IGRAPH_CHECK((int) igraph_vector_qsort_ind(&barycenters, - &sort_indices, 0)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&barycenters, &sort_indices, IGRAPH_ASCENDING)); for (i = 0; i < n; i++) { - nei = (long)VECTOR(*layer_members)[(long)VECTOR(sort_indices)[i]]; - VECTOR(barycenters)[i] = nei; + nei = VECTOR(*layer_members)[VECTOR(sort_indices)[i]]; + VECTOR(new_layer_members)[i] = nei; MATRIX(*layout, nei, 0) = i; } - if (!igraph_vector_all_e(layer_members, &barycenters)) { - IGRAPH_CHECK(igraph_vector_update(layer_members, &barycenters)); + if (!igraph_vector_int_all_e(layer_members, &new_layer_members)) { + IGRAPH_CHECK(igraph_vector_int_update(layer_members, &new_layer_members)); #ifdef SUGIYAMA_DEBUG - printf("New vertex order: "); igraph_vector_print(layer_members); + printf("New vertex order: "); igraph_vector_int_print(layer_members); #endif changed = 1; } else { @@ -858,9 +852,10 @@ static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* gra } igraph_vector_destroy(&barycenters); - igraph_vector_destroy(&neis); - igraph_vector_destroy(&sort_indices); - IGRAPH_FINALLY_CLEAN(3); + igraph_vector_int_destroy(&new_layer_members); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&sort_indices); + IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; } @@ -869,30 +864,30 @@ static int igraph_i_layout_sugiyama_order_nodes_horizontally(const igraph_t* gra #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, +static igraph_error_t 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, +static igraph_error_t 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, +static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t 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_vector_int_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, +static igraph_error_t 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_integer_t i, j, k, l, n; + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t neis1, neis2; igraph_vector_t xs[4]; igraph_vector_t roots, align; igraph_vector_t vertex_to_the_left; @@ -900,16 +895,16 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra /* { - igraph_vector_t edgelist; - IGRAPH_VECTOR_INIT_FINALLY(&edgelist, 0); + igraph_vector_int_t edgelist; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgelist, 0); IGRAPH_CHECK(igraph_get_edgelist(graph, &edgelist, 0)); - igraph_vector_print(&edgelist); - igraph_vector_destroy(&edgelist); + igraph_vector_int_print(&edgelist); + igraph_vector_int_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_vector_int_t* layer = igraph_i_layering_get(layering, i); + igraph_vector_int_print(layer); } } */ @@ -918,8 +913,8 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INT_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 @@ -927,29 +922,28 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra * 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); + igraph_vector_int_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_int_size(vertices); /* Find all the edges from this layer to the next */ - igraph_vector_clear(&neis1); + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_neighbors(graph, &neis2, VECTOR(*vertices)[j], IGRAPH_OUT)); + IGRAPH_CHECK(igraph_vector_int_append(&neis1, &neis2)); } /* Consider all pairs of edges and check whether they are in a type 1 * conflict */ - n = igraph_vector_size(&neis1); + n = igraph_vector_int_size(&neis1); for (j = 0; j < n; j++) { - long int u = IGRAPH_FROM(graph, j); - long int v = IGRAPH_TO(graph, j); + igraph_integer_t u = IGRAPH_FROM(graph, j); + igraph_integer_t 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); + igraph_integer_t w = IGRAPH_FROM(graph, k); + igraph_integer_t x = IGRAPH_TO(graph, k); if (IS_INNER_SEGMENT(w, x) == j_inner) { continue; } @@ -973,8 +967,8 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra } } - igraph_vector_destroy(&neis1); - igraph_vector_destroy(&neis2); + igraph_vector_int_destroy(&neis1); + igraph_vector_int_destroy(&neis2); IGRAPH_FINALLY_CLEAN(2); /* @@ -983,16 +977,16 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra * 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); + igraph_vector_int_t* vertices = igraph_i_layering_get(layering, i); + n = igraph_vector_int_size(vertices); if (n == 0) { continue; } - k = l = (long int)VECTOR(*vertices)[0]; + k = l = VECTOR(*vertices)[0]; VECTOR(vertex_to_the_left)[k] = k; for (j = 1; j < n; j++) { - k = (long int)VECTOR(*vertices)[j]; + k = VECTOR(*vertices)[j]; VECTOR(vertex_to_the_left)[k] = l; l = k; } @@ -1010,7 +1004,7 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra 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, + /* reverse = */ 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])); @@ -1075,20 +1069,22 @@ static int igraph_i_layout_sugiyama_place_nodes_horizontally(const igraph_t* gra return IGRAPH_SUCCESS; } -static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, +static igraph_error_t 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_integer_t i, j, k, n, di, dj, i_limit, j_limit, r; + igraph_integer_t no_of_layers = igraph_i_layering_num_layers(layering); + igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_neimode_t neimode = (reverse ? IGRAPH_OUT : IGRAPH_IN); - igraph_vector_t neis, xs, inds; + igraph_vector_int_t neis; + igraph_vector_t xs; + igraph_vector_int_t inds; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); IGRAPH_VECTOR_INIT_FINALLY(&xs, 0); - IGRAPH_VECTOR_INIT_FINALLY(&inds, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inds, 0); IGRAPH_CHECK(igraph_vector_resize(roots, no_of_nodes)); IGRAPH_CHECK(igraph_vector_resize(align, no_of_nodes)); @@ -1106,19 +1102,19 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, 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); + igraph_vector_int_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; + r = align_right ? IGRAPH_INTEGER_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; + j = align_right ? (igraph_vector_int_size(layer) - 1) : 0; dj = align_right ? -1 : 1; - j_limit = align_right ? -1 : igraph_vector_size(layer); + j_limit = align_right ? -1 : igraph_vector_int_size(layer); for (; j != j_limit; j += dj) { - long int medians[2]; - long int vertex = (long int) VECTOR(*layer)[j]; - long int pos; + igraph_integer_t medians[2]; + igraph_integer_t vertex = VECTOR(*layer)[j]; + igraph_integer_t pos; if (VECTOR(*align)[vertex] != vertex) /* This vertex is already aligned with some other vertex, @@ -1128,10 +1124,9 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, } /* Find the neighbors of vertex j in layer i */ - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) vertex, - neimode)); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, vertex, neimode)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); if (n == 0) /* No neighbors in this direction, continue */ { @@ -1139,30 +1134,30 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, } if (n == 1) { /* Just one neighbor; the median is trivial */ - medians[0] = (long int) VECTOR(neis)[0]; + medians[0] = 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]); + VECTOR(xs)[k] = X_POS(VECTOR(neis)[k]); } - IGRAPH_CHECK((int) igraph_vector_qsort_ind(&xs, &inds, 0)); + IGRAPH_CHECK(igraph_vector_qsort_ind(&xs, &inds, IGRAPH_ASCENDING)); 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[0] = VECTOR(neis)[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]]; + medians[0] = VECTOR(neis)[VECTOR(inds)[n / 2]]; + medians[1] = VECTOR(neis)[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]]; + medians[0] = VECTOR(neis)[VECTOR(inds)[n / 2 - 1]]; + medians[1] = VECTOR(neis)[VECTOR(inds)[n / 2]]; } } } @@ -1179,13 +1174,12 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, } /* 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]) { + IGRAPH_CHECK(igraph_get_eid(graph, &eid, vertex, medians[k], IGRAPH_UNDIRECTED, /* error= */ true)); + if (VECTOR(*ignored_edges)[eid]) { continue; } /* Okay, align with the median if possible */ - pos = (long int) X_POS(medians[k]); + pos = 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]]; @@ -1196,8 +1190,8 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, } } - igraph_vector_destroy(&inds); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&inds); + igraph_vector_int_destroy(&neis); igraph_vector_destroy(&xs); IGRAPH_FINALLY_CLEAN(3); @@ -1214,27 +1208,27 @@ static int igraph_i_layout_sugiyama_vertical_alignment(const igraph_t* graph, * `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, +static igraph_error_t 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_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_t shifts, old_xs; + igraph_vector_int_t sinks; igraph_real_t shift; /* Initialization */ - IGRAPH_VECTOR_INIT_FINALLY(&sinks, no_of_nodes); + IGRAPH_CHECK(igraph_vector_int_init_range(&sinks, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sinks); + IGRAPH_VECTOR_INIT_FINALLY(&shifts, no_of_nodes); + igraph_vector_fill(&shifts, IGRAPH_INFINITY); + 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 @@ -1259,15 +1253,15 @@ static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, /* 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]; + igraph_integer_t root = VECTOR(*roots)[i]; VECTOR(*xs)[i] = VECTOR(old_xs)[root]; - shift = VECTOR(shifts)[(long int)VECTOR(sinks)[root]]; + shift = VECTOR(shifts)[VECTOR(sinks)[root]]; if (shift < IGRAPH_INFINITY) { VECTOR(*xs)[i] += shift; } } - igraph_vector_destroy(&sinks); + igraph_vector_int_destroy(&sinks); igraph_vector_destroy(&shifts); igraph_vector_destroy(&old_xs); IGRAPH_FINALLY_CLEAN(3); @@ -1275,13 +1269,13 @@ static int igraph_i_layout_sugiyama_horizontal_compaction(const igraph_t* graph, return IGRAPH_SUCCESS; } -static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v, +static igraph_error_t igraph_i_layout_sugiyama_horizontal_compaction_place_block(igraph_integer_t 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_vector_int_t* sinks, igraph_vector_t* shifts, igraph_real_t hgap, igraph_vector_t* xs) { - long int u, w; - long int u_sink, v_sink; + igraph_integer_t u, w; + igraph_integer_t u_sink, v_sink; if (VECTOR(*xs)[v] >= 0) { return IGRAPH_SUCCESS; @@ -1292,18 +1286,18 @@ static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v w = v; do { /* Check whether vertex w is the leftmost in its own layer */ - u = (long int) VECTOR(*vertex_to_the_left)[w]; + u = 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]; + u = 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]; + u_sink = VECTOR(*sinks)[u]; + v_sink = 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; @@ -1327,7 +1321,7 @@ static int igraph_i_layout_sugiyama_horizontal_compaction_place_block(long int v } /* Follow the alignment */ - w = (long int) VECTOR(*align)[w]; + w = VECTOR(*align)[w]; } while (w != v); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/layout/umap.c b/src/vendor/cigraph/src/layout/umap.c new file mode 100644 index 00000000000..d1f334313e0 --- /dev/null +++ b/src/vendor/cigraph/src/layout/umap.c @@ -0,0 +1,1260 @@ +/* -*- 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_layout.h" + +#include "igraph_interface.h" +#include "igraph_lapack.h" +#include "igraph_matrix.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" +#include "igraph_vector_list.h" + +#include "layout/layout_internal.h" +#include "core/interruption.h" + +#include + +/* This file contains the implementation of the UMAP algorithm. + * + * UMAP is typically used as a to reduce dimensionality of vectors, embedding them in + * 2D (or, less commonly, in 3D). Despite this geometric flair, UMAP heavily relies on + * graphs as intermediate data structures and is therefore a useful graph layout + * algorithm in its own right. Conceptually, there are three steps: + * + * 1. Compute a sparse graph with edges connecting similar vectors, e.g. a k-nearest + * neighbor graph. A vector of distances is associated with the graph edges. This + * file does *not* perform this part of the computation since there are many + * libraries out there that can compute knn or other sparse graphs efficiently + * starting from vector spaces (e.g. faiss). + * 2. Convert the distances into weights, which are weights between 0 and 1 + * that are larger for short-distance edges. This step is exposed via + * igraph_layout_umap_compute_weights. + * 3. Compute a layout for the graph, using its associated weights as edge + * weights. This step is exposed via igraph_layout_umap and its 3D counterpart. + * These two fuctions can also compute steps 2 and 3 in one go, since that's the + * most common use case: the argument "distances_are_weights" should be + * set to false. + * + * A few more details w/r/t steps 2 and 3, since they are computed in detail below. + * + * STEP 2 + * For each vertex, the distance to its closest neighbor, called rho, is "forfeited": + * that edge begets weight 1 (in principle, at least). Farther neighbors beget + * lower weights according to an exponential decay. The scale factor of this + * decay is called sigma and is computed from the graph itself. + * + * STEP 3 + * The layout is computed via stochastic gradient descent, i.e. applying stochastic + * forces along high-weight edges and, more rarely, low-weight edges. + * To compute the stochastic forces, one needs a smooth function that approximates + * weights but in the embedded space: + * Q(d) = ( 1 + a*d^2b )^-1 + * where d is the 2D/3D distance between the vertices and a and b are constants that + * are computed globally based on a user-chosen fudge parameter called min_dist. + * Smaller min_dist will give rise to slightly more compact embeddings. We find a + * and b via gradient descent, which is implemented de novo below. + * + * Repulsion is computed via negative sampling, typically a few nodes are picked + * at random as repulsive sources each time an attractive force is computed. + * + * During the stochastic gradient descent, the learning rate - a multiplicative factor + * on top of the stochastic forces themselves - is reduced linearly from 1 to 0. At + * the end, the stochastic forces can be strong but their effect is reduced to almost + * nothing by the small learning rate. Notice that UMAP does not formally converge: + * instead, we reduce the forces' impact steadily to a trickle and finally quench it + * altogether. + * + * FINAL COMMENTS + * This implementation uses a few more tricks to improve the result: + * - a few constants are defined to limit the force applied to vertices at each step + * and other geometric corrections + * - the layout is centered at the end of the computation. + * - a seed layout can be used. Notice that since UMAP runs for an essentially fixed + * time rather than until convergence, using a good/bad seed does not affect + * runtimes significantly. + * */ +#define UMAP_FORCE_LIMIT 4 +#define UMAP_MIN_DISTANCE_ATTRACTION 0.0001 +#define UMAP_CORRECT_DISTANCE_REPULSION 0.01 + +/* Find sigma for this vertex by binary search */ +static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, + const igraph_vector_int_t *eids, + igraph_real_t rho, igraph_real_t *sigma_p, igraph_real_t target) { + + igraph_real_t sigma = 1; + igraph_real_t sum; + igraph_real_t tol = 0.01; + igraph_integer_t maxiter = 100; + igraph_integer_t no_of_neis = igraph_vector_int_size(eids); + igraph_integer_t eid; + igraph_real_t step = sigma; + igraph_integer_t seen_max = 0; + + /* Binary search */ + for (igraph_integer_t iter = 0; iter < maxiter; iter++) { + sum = 0; + for (igraph_integer_t j = 0; j < no_of_neis; j++) { + eid = VECTOR(*eids)[j]; + sum += exp(-(VECTOR(*distances)[eid] - rho) / sigma); + } + +#ifdef UMAP_DEBUG + printf("SIGMA function (no_of_neis = %" IGRAPH_PRId ")- sum: %g, " + "target: %g, rho: %g, sigma: %g\n", no_of_neis, sum, target, rho, sigma); +#endif + + if (sum < target) { + /* going back up after having seen an upper bound */ + if (seen_max == 1) { + step /= 2; + /* we need to go up but have not seen an upper bound yet + * first iteration we want to increase by sigma, else we must come from + * below, so we are sitting at 2 * step, we want to move to 4 * step */ + } else if (iter > 0) { + step *= 2; + } + sigma += step; + /* overshooting, we have definitely seen the max */ + } else { + seen_max = 1; + step /= 2; + sigma -= step; + } + + /* Check for convergence */ + if (fabs(sum - target) < tol) { + break; + } + } + + *sigma_p = sigma; + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_layout_umap_compute_weights + * \brief Compute weights for a UMAP layout starting from distances. + * + * \experimental + * + * UMAP is used to embed high-dimensional vectors in a low-dimensional space + * (most commonly 2D). It uses a distance graph as an intermediate data structure, + * making it also a useful graph layout algorithm. See \ref igraph_layout_umap() + * for more information. + * + * + * + * An early step in UMAP is to compute exponentially decaying "weights" from the + * distance graph. Connectivities can also be viewed as edge weights that quantify + * similarity between two vertices. This function computes weights from the + * distance graph. To compute the layout from precomputed weights, call + * \ref igraph_layout_umap() with the \p distances_are_weights argument set to \c true. + * + * + * + * While the distance graph can be directed (e.g. in a k-nearest neighbors, it is + * clear *who* you are a neighbor of), the weights are usually undirected. Whenever two + * vertices are doubly connected in the distance graph, the resulting weight W is set as: + * + * W = W1 + W2 - W1 * W2 + * + * Because UMAP weights are interpreted as probabilities, this is just the probability + * that either edge is present, without double counting. It is called "fuzzy union" in + * the original UMAP implementation and is the default. One could also require that both + * edges are there, i.e. W = W1 * W2: this would represent the fuzzy intersection and is + * not implemented in igraph. As a consequence of this symmetrization, information is lost, + * i.e. one needs fewer weights than one had distances. To keep things efficient, here + * we set the weight for one of the two edges as above and the weight for its opposite edge + * as 0, so that it will be skipped in the UMAP gradient descent later on. + * + * + * + * Technical note: For each vertex, this function computes its scale factor (sigma), + * its connectivity correction (rho), and finally the weights themselves. + * + * + * References: + * + * + * Leland McInnes, John Healy, and James Melville. https://arxiv.org/abs/1802.03426 + * + * \param graph Pointer to the distance graph. This can be directed (e.g. connecting + * each vertex to its neighbors in a k-nearest neighbor) or undirected, but must + * have no loops nor parallel edges. The only exception is: if the graph is directed, + * having pairs of edges with opposite direction is accepted. + * \param distances Pointer to the vector with the vertex-to-vertex distance associated with + * each edge. This argument can be NULL, in which case all edges are assumed to have the + * same distance. + * \param weights Pointer to an initialized vector where the result will be stored. If the + * input graph is directed, the weights represent a symmetrized version which contains + * less information. Therefore, whenever two edges between the same vertices and opposite + * direction are present in the input graph, only one of the weights is set and the other + * is fixed to zero. That format is accepted by \ref igraph_layout_umap(), which skips + * all zero-weight edges from the layout optimization. + * + * \return Error code. + */ +igraph_error_t igraph_layout_umap_compute_weights( + const igraph_t *graph, + const igraph_vector_t *distances, + igraph_vector_t *weights) { + + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_neis, eid, i, j, k, l; + igraph_vector_int_t eids; + igraph_vector_int_list_t neighbors_seen; + igraph_vector_list_t weights_seen; + igraph_vector_int_t* neighbors_seen_elt; + igraph_vector_t* weights_seen_elt; + igraph_real_t rho, dist_max, dist, sigma, weight, weight_inv, sigma_target, dist_min; + + /* reserve memory for the weights */ + IGRAPH_CHECK(igraph_vector_resize(weights, no_of_edges)); + + /* UMAP is sometimes used on unweighted graphs, otherwise check distance vector. */ + if (distances != NULL) { + if (igraph_vector_size(distances) != no_of_edges) { + IGRAPH_ERROR("Distances must be the same number as the edges in the graph.", IGRAPH_EINVAL); + } + if (no_of_edges > 0) { + dist_min = igraph_vector_min(distances); + if (dist_min < 0) { + IGRAPH_ERROR("Distance values must not be negative.", IGRAPH_EINVAL); + } else if (isnan(dist_min)) { + IGRAPH_ERROR("Distance values must not be NaN.", IGRAPH_EINVAL); + } + } + } + + /* Initialize auxiliary vectors */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&neighbors_seen, no_of_vertices); + IGRAPH_VECTOR_LIST_INIT_FINALLY(&weights_seen, no_of_vertices); + + /* Iterate over vertices x, like in the paper */ + for (i = 0; i < no_of_vertices; i++) { + /* Edges out of this vertex, e.g. to its k-nearest neighbors */ + IGRAPH_CHECK(igraph_incident(graph, &eids, i, IGRAPH_OUT)); + no_of_neis = igraph_vector_int_size(&eids); + + /* Vertex has no neighbors */ + if (no_of_neis == 0) { + continue; + } + + /* Find rho for this vertex, i.e. the minimal non-self distance */ + if (distances != NULL) { + rho = VECTOR(*distances)[VECTOR(eids)[0]]; + dist_max = rho; + for (j = 1; j < no_of_neis; j++) { + eid = VECTOR(eids)[j]; + dist = VECTOR(*distances)[eid]; + rho = fmin(rho, dist); + dist_max = fmax(dist_max, dist); + } + } else { + rho = dist_max = 0; + } + + /* If the maximal distance is rho, all neighbors are identical to + * each other. This can happen e.g. if distances == NULL. */ + if (dist_max == rho) { + /* This is a special flag for later on */ + sigma = -1; + + /* Else, find sigma for this vertex, from its rho plus binary search */ + } else { + sigma_target = log2(no_of_neis); + IGRAPH_CHECK(igraph_i_umap_find_sigma(distances, + &eids, rho, &sigma, + sigma_target)); + } + + /* Convert to weights */ + for (j = 0; j < no_of_neis; j++) { + eid = VECTOR(eids)[j]; + + /* Basically, nodes closer than rho have probability 1, the rest is + * exponentially penalized keeping rough cardinality */ + weight = sigma < 0 ? 1 : exp(-(VECTOR(*distances)[eid] - rho) / sigma); + + #ifdef UMAP_DEBUG + if (distances != NULL) + printf("distance: %g\n", VECTOR(*distances)[eid]); + printf("weight: %g\n", weight); + #endif + + /* Store in vector lists for later symmetrization */ + k = IGRAPH_OTHER(graph, eid, i); + if (k == i) { + IGRAPH_ERROR("Input graph must contain no self-loops.", IGRAPH_EINVAL); + } + + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); + IGRAPH_CHECK(igraph_vector_int_push_back(neighbors_seen_elt, k)); + + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); + IGRAPH_CHECK(igraph_vector_push_back(weights_seen_elt, weight)); + } + + } + + /* Symmetrize the weights. UMAP weights are probabilities of that edge being a + * "real" connection. Unlike the distances, which can represent a directed graph, + * weights are usually symmetric. We symmetrize via fuzzy union. */ + for (eid=0; eid < no_of_edges; eid++) { + i = IGRAPH_FROM(graph, eid); + k = IGRAPH_TO(graph, eid); + + /* Direct weight, if found */ + /* NOTE: this and the subsequent loop could be faster if we sorted the vectors + * beforehand. Probably not such a big deal. */ + weight = 0; + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, i); + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, i); + no_of_neis = igraph_vector_int_size(neighbors_seen_elt); + for (l=0; l < no_of_neis; l++) { + if (VECTOR(*neighbors_seen_elt)[l] == k) { + weight = VECTOR(*weights_seen_elt)[l]; + /* Tag this weight so we can ignore it later on if the opposite + * directed edge is found. It's ok to retag */ + VECTOR(*weights_seen_elt)[l] = -1; + break; + } + } + + /* The opposite edge has already been union-ed, set this one to -1 */ + if (weight < 0) { + VECTOR(*weights)[eid] = 0; + continue; + } + + /* Weight of the opposite edge, if found */ + weight_inv = 0; + neighbors_seen_elt = igraph_vector_int_list_get_ptr(&neighbors_seen, k); + weights_seen_elt = igraph_vector_list_get_ptr(&weights_seen, k); + no_of_neis = igraph_vector_int_size(neighbors_seen_elt); + for (l=0; l < no_of_neis; l++) { + if (VECTOR(*neighbors_seen_elt)[l] == i) { + weight_inv = VECTOR(*weights_seen_elt)[l]; + /* Tag this weight so we can ignore it later on if the opposite + * directed edge is found. It's ok to retag */ + VECTOR(*weights_seen_elt)[l] = -1; + break; + } + } + + /* The opposite edge has already been union-ed, set this one to -1 */ + if (weight_inv < 0) { + VECTOR(*weights)[eid] = 0; + continue; + } + + /* First time this edge or its opposite are seen, set the W */ + VECTOR(*weights)[eid] = weight + weight_inv - weight * weight_inv; + } + + igraph_vector_list_destroy(&weights_seen); + igraph_vector_int_list_destroy(&neighbors_seen); + igraph_vector_int_destroy(&eids); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Helper function to compute a and b parameters (smoothing probability metric in embedding space) */ +static igraph_error_t igraph_i_umap_get_ab_residuals(igraph_vector_t *residuals, + igraph_real_t *squared_sum_res, igraph_integer_t nr_points, igraph_real_t a, + igraph_real_t b, igraph_vector_t *powb, const igraph_vector_t *x, igraph_real_t min_dist) +{ + igraph_real_t tmp; + + *squared_sum_res = 0; + for (igraph_integer_t i = 0; i < nr_points; i++) { + /* The ideal probability is: + * + * P(d) = d < min_dist ? 1 : e^{-(d - min_dist)} + * + * which is the same as the high-dimensional probability, except + * min_dist plays the role of rho and sigma is fixed at 1. However, + * this function has a kink at min_dist (first derivative is not + * continuous). So we smoothen it with: + * + * Q(d) = ( 1 + a*d^2b )^-1 + * + * which is quite similar throughout for appropriate a and b. Notice + * that we do not need to smoothen the high-dimensional probability + * function because the vertices are not moved in the high-dimensional + * space, so there is no need for differentiating that function. + * + * The residual is of course: + * + * Q(d) - P(d) = ( 1 + a*d^2b )^-1 - [ d < min_dist ? 1 : e^{-(d - min_dist)} ] + * + * This function also sets the auxiliary vector powb. + * */ + VECTOR(*powb)[i] = pow(VECTOR(*x)[i], 2 * b); + tmp = 1 / (1 + a * VECTOR(*powb)[i]); + tmp -= VECTOR(*x)[i] <= min_dist ? 1 : exp(-(VECTOR(*x)[i] - min_dist)); + VECTOR(*residuals)[i] = tmp; + *squared_sum_res += tmp * tmp; + } + return IGRAPH_SUCCESS; +} + +/* UMAP minimizes the cross-entropy between probability of being a true edge in + * high and low dimensions. For the low-dimensional computation, it uses a smooth + * function of the Euclidean distance between two vertices: + * + * P(d) = (1 + a*d^2b)^-1 + * + * where d is the distance and a and b are hyperparameters that basically determine + * the cutoff distance at which the probability starts to decrease. + * + * We fit these two parameters using nonlinear least squares (Gauss-Newton + line search) + * on a grid of artificial distances. There is only one user-chosen input argument that + * determines this fit, called min_dist, which is approximately the cutoff distance we + * are trying to achieve. + * + * ADVANCED NOTE: + * In a way, the whole UMAP layout is invariant upon scaling transformations, of course, + * so min_dist is basically meaningless. Another way to see this is that for any pair + * (a,b) that minimize the least squares for dist_min, we can easily find a solution for + * a new dist_min2 := alpha * dist_min: + * + * P(d, a, b) = (1 + a*d^2b)^-1 + * + * P(alpha * d, a', b') = (1 + a'*(alpha * d)^2b' )^-1 + * + * that is: + * + * a*d^2b = a'*alpha^2b'*d^2b' for each d >= 0. + * + * So for d = 1 -> a = a'*alpha^2b' + * and for d = sqrt(2) -> a*2^b = a'*alpha^2b'*2^b' + * + * which solves as: + * + * b' = b + * a' = a / alpha^2b + * + * For instance, if b = 1, a -> 0.01*a moves the fit a decade towards larger min_dist, + * and a -> 100*a moves the fit a decade towards smaller min_dist. + * */ +igraph_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, igraph_real_t *a_p, igraph_real_t *b_p) +{ + /* Grid points */ + igraph_vector_t x; + /* Make a lattice from 0 to 3 * sigma with 300 points. This is what + * umap.umap_.fit_ab_params does, but sigma is fixed to 1.0 here since + * that's the default value used in scanpy and by virtually everyone */ + igraph_integer_t nr_points = 300; + igraph_real_t end_point = 3.0; + /* Initial values takes as reasonable assumptions from typical min_dist values */ + igraph_real_t b = 0.8; + igraph_real_t a = 1.8; + /* deltas */ + igraph_real_t da, db; + /* Residuals */ + igraph_vector_t residuals; + igraph_real_t squared_sum_res, squared_sum_res_old, squared_sum_res_tmp; + /* Needed for the Gauss-Newton search */ + igraph_matrix_t jacobian, jTj, jTr; + igraph_real_t tol = 0.001; + igraph_real_t maxiter = 100; + /* Auxiliary vars */ + igraph_real_t tmp; + igraph_vector_t powb; + int lapack_info; + + /* Distance lattice */ + IGRAPH_VECTOR_INIT_FINALLY(&x, nr_points); + /* Residuals */ + IGRAPH_VECTOR_INIT_FINALLY(&residuals, nr_points); + /* First derivatives, for the fitting (direction) */ + IGRAPH_MATRIX_INIT_FINALLY(&jacobian, nr_points, 2); + /* Composite matrices/vectors for linear least squares at each iteration */ + IGRAPH_MATRIX_INIT_FINALLY(&jTj, 2, 2); + IGRAPH_MATRIX_INIT_FINALLY(&jTr, 2, 1); + /* Auxiliary vars for convenience */ + IGRAPH_VECTOR_INIT_FINALLY(&powb, nr_points); + + /* Distance |x-y| (this is a lattice, there are no actual x and y) */ + for (igraph_integer_t i = 0; i < nr_points; i++) { + VECTOR(x)[i] = (end_point / nr_points) * i + 0.001; /* added a 0.001 to prevent NaNs */ + } + + /* Initialize squared_sum_res_old to a dummy value to prevent some compilers + * from complaining about uninitialized values */ + squared_sum_res_old = IGRAPH_INFINITY; + +#ifdef UMAP_DEBUG + printf("start fit_ab\n"); +#endif + for (igraph_integer_t iter = 0; iter < maxiter; iter++) { + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a, b, + &powb, &x, min_dist)); + + /* break if good fit (conergence to truth) */ + if (squared_sum_res < tol * tol) { +#ifdef UMAP_DEBUG + printf("convergence to zero (wow!)\n"); +#endif + break; + } + /* break if no change (convergence) */ + if ((iter > 0) && fabs(sqrt(squared_sum_res_old) - sqrt(squared_sum_res)) < tol) { +#ifdef UMAP_DEBUG + printf("no-change absolute convergence\n"); +#endif + break; + } + + /* Jacobian (first derivatives) of squared residuals at (a, b) */ + for (igraph_integer_t i = 0; i < nr_points; i++) { + tmp = 1 + a * VECTOR(powb)[i]; + MATRIX(jacobian, i, 0) = - 2 * VECTOR(powb)[i] / tmp / tmp; + MATRIX(jacobian, i, 1) = MATRIX(jacobian, i, 0) * a * log(VECTOR(x)[i]) * 2; + } + + /* At each iteration, we want to minimize the linear approximation of the sum of squared + * residuals: + * + * sum_i (Ji @ d(a,b) -r_i)^2 + * + * Putting the first derivative to zero results in a linear system of 2 equations + * (for a and b): + * + * sum_i J_i^T @ J_i @ d(a,b) = sum_i J_i^T r_i + * * + * or more compactly: + * + * J^T @ J @ d(a,b) = J^T @ r + * + * where J_T is the transpose of the Jacobian. Defining A := J^T @ J, B = J^T @ r: + * + * A @ d(a,b) = B + * + * This can be solved for d(a,b) using LAPACK within igraph + * */ + /* Compute A and B, i.e. J^T @ J and J^T @ r */ + MATRIX(jTj, 0, 0) = MATRIX(jTj, 0, 1) = MATRIX(jTj, 1, 0) = MATRIX(jTj, 1, 1) = 0; + MATRIX(jTr, 0, 0) = MATRIX(jTr, 1, 0) = 0; + for (igraph_integer_t i = 0; i < nr_points; i++) { + for (igraph_integer_t j1 = 0; j1 < 2; j1++) { + for (igraph_integer_t j2 = 0; j2 < 2; j2++) { + MATRIX(jTj, j1, j2) += MATRIX(jacobian, i, j1) * MATRIX(jacobian, i, j2); + } + MATRIX(jTr, j1, 0) += MATRIX(jacobian, i, j1) * VECTOR(residuals)[i]; + } + } + /* LAPACK puts solution into jTr */ + IGRAPH_CHECK(igraph_lapack_dgesv(&jTj, 0, &jTr, &lapack_info)); + + /* This might go wrong, in which case we should fail graciously */ + if (lapack_info != 0) { + IGRAPH_ERROR("Singular matrix in the estimation of a and b for UMAP", IGRAPH_EINVAL); + } + + da = -MATRIX(jTr, 0, 0); + db = -MATRIX(jTr, 1, 0); + + /* Improvement over GN: rough exponential line search for best delta + * start from largest change, and keep shrinking as long as we are going down + * */ + squared_sum_res_old = squared_sum_res; + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, a + da, + b + db, &powb, &x, min_dist)); + +#ifdef UMAP_DEBUG + printf("start line search, SSR before delta: %g, current SSR:, %g\n", squared_sum_res_old, + squared_sum_res); +#endif + for (igraph_integer_t k = 0; k < 30; k++) { + /* Try new parameters */ + da /= 2.0; + db /= 2.0; + squared_sum_res_tmp = squared_sum_res; + IGRAPH_CHECK(igraph_i_umap_get_ab_residuals(&residuals, &squared_sum_res, nr_points, + a + da, b + db, &powb, &x, min_dist)); + + /* Compare and if we are going back uphill, undo last step and break */ +#ifdef UMAP_DEBUG + printf("during line search, k = %d, old SSR:, %g, new SSR (half a,b):, %g\n", k, + squared_sum_res_tmp, squared_sum_res); +#endif + if (squared_sum_res > squared_sum_res_tmp - tol) { + da *= 2; + db *= 2; + break; + } + } +#ifdef UMAP_DEBUG + printf("end of line search and iteration, squared_sum_res: %g \n\n", squared_sum_res_tmp); +#endif + + /* assign a, b*/ + a += da; + b += db; + + } + + /* Free memory and tidy up stack */ + igraph_vector_destroy(&x); + igraph_vector_destroy(&residuals); + igraph_matrix_destroy(&jacobian); + igraph_matrix_destroy(&jTj); + igraph_matrix_destroy(&jTr); + igraph_vector_destroy(&powb); + IGRAPH_FINALLY_CLEAN(6); + +#ifdef UMAP_DEBUG + printf("a, b: %g %g\n", a, b); +#endif + + *a_p = a; + *b_p = b; + + return IGRAPH_SUCCESS; + +} + +/* cross-entropy */ +#ifdef UMAP_DEBUG +static igraph_error_t igraph_i_umap_compute_cross_entropy(const igraph_t *graph, + const igraph_vector_t *umap_weights, const igraph_matrix_t *layout, igraph_real_t a, igraph_real_t b, + igraph_real_t *cross_entropy) { + + igraph_real_t mu, nu, xd, yd, sqd; + igraph_integer_t from, to; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_vertices = igraph_vcount(graph); + igraph_matrix_t edge_seen; + + IGRAPH_MATRIX_INIT_FINALLY(&edge_seen, no_of_vertices, no_of_vertices); + + /* Measure the (variable part of the) cross-entropy terms for debugging: + * 1. - sum_edge_e mu(e) * log(nu(e)) + * 2. - sum_edge_e (1 - mu(e)) * log(1 - nu(e)) + * NOTE: the sum goes over the whole adjacency matrix, i.e. all potential edges, + * not just the actual edges. That is because otherwise there's no benefit from + * repelling unconnected edges. + * */ + *cross_entropy = 0; + for (igraph_integer_t eid = 0; eid < no_of_edges; eid++) { + mu = VECTOR(*umap_weights)[eid]; + + /* Find vertices */ + from = IGRAPH_FROM(graph, eid); + to = IGRAPH_TO(graph, eid); + /* Find distance in layout space */ + xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); + yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); + sqd = xd * xd + yd * yd; + /* Find probability associated with distance using fitted Phi */ + nu = 1.0 / (1 + a * pow(sqd, b)); + + /* Term 1: entropy from the edges */ + if (mu > 0) + *cross_entropy -= mu * log(nu); + /* Term 2: entropy from the missing edges */ + if (mu < 1) + *cross_entropy -= (1 - mu) * log(1 - nu); + + MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; + } + /* Add the entropy from the missing edges */ + for (igraph_integer_t from = 0; from < no_of_vertices; from++) { + for (igraph_integer_t to = 0; to < from; to++) { + if (MATRIX(edge_seen, from, to) > 0) { + continue; + } + + /* Find distance in layout space */ + xd = (MATRIX(*layout, from, 0) - MATRIX(*layout, to, 0)); + yd = (MATRIX(*layout, from, 1) - MATRIX(*layout, to, 1)); + sqd = xd * xd + yd * yd; + + /* Find probability associated with distance using fitted Phi */ + nu = 1.0 / (1 + a * pow(sqd, b)); + + /* Term 2*/ + *cross_entropy -= log(1 - nu); + + MATRIX(edge_seen, from, to) = MATRIX(edge_seen, to, from) = 1; + } + } + + igraph_matrix_destroy(&edge_seen); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} +#endif /* UMAP_DEBUG */ + + +/* clip forces to avoid too rapid shifts */ +static igraph_real_t igraph_i_umap_clip_force(igraph_real_t force, igraph_real_t limit) { + return force > limit ? limit : (force < -limit ? -limit : force); +} + +static igraph_real_t igraph_i_umap_attract( + igraph_real_t dsq, + igraph_real_t a, + igraph_real_t b) +{ + return - (2 * a * b * pow(dsq, b - 1.)) / (1. + a * pow(dsq, b)); +} + +static igraph_real_t igraph_i_umap_repel( + igraph_real_t dsq, + igraph_real_t a, + igraph_real_t b) +{ + igraph_real_t dsq_min = UMAP_CORRECT_DISTANCE_REPULSION * UMAP_CORRECT_DISTANCE_REPULSION; + + return (2 * b) / (dsq_min + dsq) / (1. + a * pow(dsq, b)); +} + +static igraph_error_t igraph_i_umap_apply_forces( + const igraph_t *graph, + const igraph_vector_t *umap_weights, + igraph_matrix_t *layout, + igraph_real_t a, + igraph_real_t b, + igraph_real_t learning_rate, + igraph_bool_t avoid_neighbor_repulsion, + igraph_integer_t negative_sampling_rate, + igraph_integer_t epoch, + igraph_integer_t epochs, + igraph_vector_t *next_epoch_sample_per_edge) +{ + igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); + igraph_integer_t ndim = igraph_matrix_ncol(layout); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t from, to, nneis, eid; + igraph_vector_t from_emb, to_emb, delta; + igraph_real_t force = 0, dsq, force_d; + /* The following is only used for small graphs, to avoid repelling your neighbors + * For large sparse graphs, it's not necessary. For large dense graphs, you should + * not be doing UMAP. + * */ + igraph_vector_int_t neis, negative_vertices; + igraph_integer_t n_negative_vertices = (no_of_vertices - 1 < negative_sampling_rate) ? (no_of_vertices - 1) : negative_sampling_rate; + + /* Initialize vectors */ + IGRAPH_VECTOR_INIT_FINALLY(&from_emb, ndim); + IGRAPH_VECTOR_INIT_FINALLY(&to_emb, ndim); + IGRAPH_VECTOR_INIT_FINALLY(&delta, ndim); + + if (avoid_neighbor_repulsion) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&negative_vertices, 0); + + /* Iterate over edges. Stronger edges are sampled more often */ + for (eid = 0; eid < no_of_edges; eid++) { + /* Zero-weight edges do not affect vertex positions. They can + * also emerge during the weight symmetrization. */ + if (VECTOR(*umap_weights)[eid] <= 0) { + continue; + } + + /* We sample all and only edges that are supposed to be moved at this time */ + if ((VECTOR(*next_epoch_sample_per_edge)[eid] - epoch) >= 1) { + continue; + } + + /* set next epoch at which this edge will be sampled */ + VECTOR(*next_epoch_sample_per_edge)[eid] += 1.0 / VECTOR(*umap_weights)[eid]; + + /* we move all vertices on one end of the edges, then we come back for + * the vertices on the other end. This way we don't move both ends at the + * same time, which is almost a wasted move since they attract each other */ + int swapflag = (int)(RNG_UNIF01() > 0.5); + int swapflag_end = swapflag + 2; + for (; swapflag < swapflag_end; swapflag++) { + + /* half the time, swap the from/to, otherwise some vertices are never moved. + * This has to do with the graph representation within igraph */ + if (swapflag % 2) { + from = IGRAPH_FROM(graph, eid); + to = IGRAPH_TO(graph, eid); + } else { + to = IGRAPH_FROM(graph, eid); + from = IGRAPH_TO(graph, eid); + } + + + /* Current coordinates of both vertices */ + dsq = 0; + for (igraph_integer_t d = 0; d != ndim; d++) { + VECTOR(from_emb)[d] = MATRIX(*layout, from, d); + VECTOR(to_emb)[d] = MATRIX(*layout, to, d); + VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); + dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; + } + + /* Apply attractive force since they are neighbors */ + /* NOTE: If they are already together, no force needed */ + if (dsq >= UMAP_MIN_DISTANCE_ATTRACTION * UMAP_MIN_DISTANCE_ATTRACTION) { + force = igraph_i_umap_attract(dsq, a, b); + for (igraph_integer_t d = 0; d != ndim; d++) { + force_d = force * VECTOR(delta)[d]; + /* clip force to avoid too rapid change */ + force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); + + #ifdef UMAP_DEBUG + fprintf(stderr, "force attractive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); + #endif + + MATRIX(*layout, from, d) += learning_rate * force_d; + } + } + + /* Random other nodes repel the focal vertex */ + IGRAPH_CHECK(igraph_random_sample(&negative_vertices, + 0, no_of_vertices - 2, n_negative_vertices)); + for (igraph_integer_t j = 0; j < n_negative_vertices; j++) { + + IGRAPH_ALLOW_INTERRUPTION(); + + /* Get random neighbor */ + to = VECTOR(negative_vertices)[j]; + /* obviously you cannot repel yourself */ + if (to >= from) { + to++; + } + + /* do not repel neighbors for small graphs, for big graphs this + * does not matter as long as the k in knn << number of vertices */ + if (avoid_neighbor_repulsion) { + /* NOTE: the efficiency of this step could be improved but it + * should be only used for small graphs anyway, so it's fine */ + igraph_bool_t skip = 0; + IGRAPH_CHECK(igraph_incident(graph, &neis, from, IGRAPH_ALL)); + nneis = igraph_vector_int_size(&neis); + for (igraph_integer_t k = 0; k < nneis; k++) { + igraph_integer_t eid2 = VECTOR(neis)[k]; + igraph_integer_t from2, to2; + from2 = IGRAPH_FROM(graph, eid2); + to2 = IGRAPH_TO(graph, eid2); + if (((from2 == from) && (to2 == to)) || ((from2 == to) && (from == to2))) { + skip = 1; + break; + } + } + if (skip == 1) { + continue; + } + } + + /* Get layout of random neighbor and gradient in embedding */ + dsq = 0; + for (igraph_integer_t d = 0; d != ndim; d++) { + VECTOR(to_emb)[d] = MATRIX(*layout, to, d); + VECTOR(delta)[d] = MATRIX(*layout, from, d) - MATRIX(*layout, to, d); + dsq += VECTOR(delta)[d] * VECTOR(delta)[d]; + } + + /* This repels the other vertex assuming it's a negative example + * that is no weight, no edge */ + force = igraph_i_umap_repel(dsq, a, b); + /* The repulsive force is already *away* from the other (non-neighbor) vertex */ + for (igraph_integer_t d = 0; d != ndim; d++) { + force_d = force * VECTOR(delta)[d]; + + /* clip force to avoid too rapid change */ + force_d = igraph_i_umap_clip_force(force_d, UMAP_FORCE_LIMIT); + + #ifdef UMAP_DEBUG + fprintf(stderr, "force repulsive: delta[%ld] = %g, forces[%ld] = %g\n", d, VECTOR(delta)[d], d, force_d); + #endif + + MATRIX(*layout, from, d) += learning_rate * force_d; + } + } + } + } + + /* Free vectors */ + igraph_vector_int_destroy(&negative_vertices); + igraph_vector_destroy(&from_emb); + igraph_vector_destroy(&to_emb); + igraph_vector_destroy(&delta); + IGRAPH_FINALLY_CLEAN(4); + + /* Free vector of neighbors if needed */ + if (avoid_neighbor_repulsion) { + igraph_vector_int_destroy(&neis); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/* Edges with heavier weight/higher probability should be sampled more often. In + * other words, vertices at each end of those edges should be moved more often. If the + * edge weight is 1.0, which happens to each nearest neighbor due to the correction via + * rho, that vertices at the end of that edge are moved each single epoch. Conversely, + * vertices at the end of weak edges can be moved only once in a while. */ +static igraph_error_t igraph_i_umap_optimize_layout_stochastic_gradient( + const igraph_t *graph, + const igraph_vector_t *umap_weights, + igraph_real_t a, + igraph_real_t b, + igraph_matrix_t *layout, + igraph_integer_t epochs, + igraph_integer_t negative_sampling_rate) { + + igraph_real_t learning_rate = 1; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_t next_epoch_sample_per_edge; + +#ifdef UMAP_DEBUG + igraph_real_t cross_entropy, cross_entropy_old; +#endif + + IGRAPH_VECTOR_INIT_FINALLY(&next_epoch_sample_per_edge, no_of_edges); + + /* Explicit avoidance of neighbor repulsion, only useful in small graphs + * which are never very sparse. This is because negative sampling as implemented + * relies on an approximation that only works if the graph is sparse, which is never + * quite true for small graphs (i.e. |V| << |E| << |V|^2 is hard to judge if + * |V| is small) */ + igraph_bool_t avoid_neighbor_repulsion = 0; + if (igraph_vcount(graph) < 100) { + avoid_neighbor_repulsion = 1; + } + + /* Measure the (variable part of the) cross-entropy terms for debugging: + * 1. - sum_edge_e mu(e) * log(nu(e)) + * 2. + sum_edge_e (1 - mu(e)) * log(1 - nu(e)) + * The latter is approximated by negative sampling as: + * 2b. + sum_random_ij 1 * log(1 - nu_ij) + * whereby the mu = 0 because we assume there's no edge between i and j, and nu_ij + * is basically their distance in embedding space, lensed through the probability + * function Phi. + * */ +#ifdef UMAP_DEBUG + igraph_umap_compute_cross_entropy( + graph, umap_weights, layout, a, b, &cross_entropy); +#endif + + for (igraph_integer_t e = 0; e < epochs; e++) { + /* Apply (stochastic) forces */ + igraph_i_umap_apply_forces( + graph, + umap_weights, + layout, + a, b, + learning_rate, + avoid_neighbor_repulsion, + negative_sampling_rate, + e, + epochs, + &next_epoch_sample_per_edge); + +#ifdef UMAP_DEBUG + /* Recompute CE and check how it's going*/ + cross_entropy_old = cross_entropy; + igraph_umap_compute_cross_entropy( + graph, umap_weights, layout, a, b, &cross_entropy); + + printf("Cross-entropy before shift: %g, after shift: %g\n", cross_entropy_old, cross_entropy); +#endif + + /* Adjust learning rate */ + learning_rate = 1.0 - (igraph_real_t)(e + 1) / epochs; + } + + igraph_vector_destroy(&next_epoch_sample_per_edge); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/* Center layout around (0,0) at the end, just for convenience */ +static igraph_error_t igraph_i_umap_center_layout(igraph_matrix_t *layout) { + igraph_integer_t no_of_vertices = igraph_matrix_nrow(layout); + igraph_real_t xm = 0, ym = 0; + + /* Compute center */ + xm = 0; + ym = 0; + for (igraph_integer_t i = 0; i < no_of_vertices; i++) { + xm += MATRIX(*layout, i, 0); + ym += MATRIX(*layout, i, 1); + } + xm /= no_of_vertices; + ym /= no_of_vertices; + + /* Shift vertices */ + for (igraph_integer_t i = 0; i < no_of_vertices; i++) { + MATRIX(*layout, i, 0) -= xm; + MATRIX(*layout, i, 1) -= ym; + } + + return IGRAPH_SUCCESS; +} + + +/* This is the main function that works for any dimensionality of the embedding + * (currently hard-constrained to 2 or 3 ONLY in the initialization). */ +static igraph_error_t igraph_i_layout_umap( + const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_integer_t ndim, + igraph_bool_t distances_are_weights) { + + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_vertices = igraph_vcount(graph); + /* probabilities of each edge being a real connection */ + igraph_vector_t weights; + igraph_vector_t *weightsp; + /* The smoothing parameters given min_dist */ + igraph_real_t a, b; + /* How many repulsions for each attraction */ + igraph_integer_t negative_sampling_rate = 5; + + /* Check input arguments */ + if (min_dist < 0) { + IGRAPH_ERRORF("Minimum distance must not be negative, got %g.", + IGRAPH_EINVAL, min_dist); + } + + if (epochs < 0) { + IGRAPH_ERRORF("Number of epochs must be non-negative, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, epochs); + } + + if ((ndim != 2) && (ndim != 3)) { + IGRAPH_ERRORF("Number of dimensions must be 2 or 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, ndim); + + } + + /* Compute weights (exponential weights) from distances if required. + * If the weights have already been computed, they are stored in + * the "distances" vector and we can recycle the pointer. */ + if (distances_are_weights) { + weightsp = (igraph_vector_t *) distances; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_edges); + IGRAPH_CHECK(igraph_layout_umap_compute_weights( + graph, distances, &weights)); + weightsp = &weights; + } + /* From now on everything lives in probability space, it does not matter whether + * the original graph was weighted/distanced or unweighted */ + + /* Compute initial layout if required. If a seed layout is used, then just + * check that the dimensions of the layout make sense. */ + if (use_seed) { + if ((igraph_matrix_nrow(res) != no_of_vertices) || (igraph_matrix_ncol(res) != ndim)) { + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + IGRAPH_ERRORF("Seed layout should have %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions, got %" IGRAPH_PRId " points in %" IGRAPH_PRId " dimensions.", + IGRAPH_EINVAL, no_of_vertices, ndim, + igraph_matrix_nrow(res), + igraph_matrix_ncol(res)); + } + + /* Trivial graphs (0 or 1 nodes) with seed - do nothing */ + if (no_of_vertices <= 1) { + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + return IGRAPH_SUCCESS; + } + } else { + /* Trivial graphs (0 or 1 nodes) beget trivial - but valid - layouts */ + if (no_of_vertices <= 1) { + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_vertices, ndim)); + igraph_matrix_null(res); + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + return IGRAPH_SUCCESS; + } + + /* Skip spectral embedding for now (see #1971), initialize at random */ + if (ndim == 2) { + igraph_layout_random(graph, res); + } else { + igraph_layout_random_3d(graph, res); + } + } + + RNG_BEGIN(); + + /* Fit a and b parameter to find smooth approximation to + * probability distribution in embedding space */ + IGRAPH_CHECK(igraph_i_umap_fit_ab(min_dist, &a, &b)); + + /* Minimize cross-entropy between high-d and low-d probability + * distributions */ + IGRAPH_CHECK(igraph_i_umap_optimize_layout_stochastic_gradient( + graph, + weightsp, + a, b, + res, + epochs, + negative_sampling_rate)); + + if (!distances_are_weights) { + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(1); + } + RNG_END(); + + /* Center layout */ + IGRAPH_CHECK(igraph_i_umap_center_layout(res)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_layout_umap + * \brief Layout using Uniform Manifold Approximation and Projection (UMAP). + * + * \experimental + * + * UMAP is mostly used to embed high-dimensional vectors in a low-dimensional space + * (most commonly 2D). The algorithm is probabilistic and introduces nonlinearities, + * unlike e.g. PCA and similar to T-distributed Stochastic Neighbor Embedding (t-SNE). + * Nonlinearity helps "cluster" very similar vectors together without imposing a + * global geometry on the embedded space (e.g. a rigid rotation + compression in PCA). + * UMAP uses graphs as intermediate data structures, hence it can be used as a + * graph layout algorithm as well. + * + * + * + * The general UMAP workflow is to start from vectors, compute a sparse distance + * graph that only contains edges between simiar points (e.g. a k-nearest neighbors + * graph), and then convert these distances into exponentially decaying weights + * between 0 and 1 that are larger for points that are closest neighbors in the + * distance graph. If a graph without any distances associated to the edges is used, + * all weights will be set to 1. + * + * + * + * If you are trying to use this function to embed high-dimensional vectors, you should + * first compute a k-nearest neighbors graph between your vectors and compute the + * associated distances, and then call this function on that graph. If you already + * have a distance graph, or you have a graph with no distances, you can call this + * function directly. If you already have a graph with meaningful weights + * associated to each edge, you can also call this function, but set the argument + * distances_are_weights to true. To compute weights from distances + * without computing the layout, see \ref igraph_layout_umap_compute_weights(). + * + * + * References: + * + * + * Leland McInnes, John Healy, and James Melville. https://arxiv.org/abs/1802.03426 + + * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is + * typically a sparse graph with only edges for the shortest distances stored, e.g. + * a k-nearest neighbors graph. + * \param res Pointer to the n by 2 matrix where the layout coordinates will be stored. + * \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 distances Pointer to a vector of distances associated with the graph edges. + * If this argument is \c NULL, all weights will be set to 1. + * \param min_dist A fudge parameter that decides how close two unconnected vertices + * can be in the embedding before feeling a repulsive force. It must not be + * negative. Typical values are between 0 and 1. + * \param epochs Number of iterations of the main stochastic gradient descent loop on + * the cross-entropy. Typical values are between 30 and 500. + * \param distances_are_weights Whether to use precomputed weights. If + * true, the "distances" vector contains precomputed weights. If false (the + * typical use case), this function will compute weights from distances and + * then use them to compute the layout. + * \return Error code. + */ +igraph_error_t igraph_layout_umap(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights) { + return igraph_i_layout_umap(graph, res, use_seed, + distances, min_dist, epochs, 2, distances_are_weights); +} + + +/** + * \function igraph_layout_umap_3d + * \brief 3D layout using UMAP. + * + * \experimental + * + * + * + * This is the 3D version of the UMAP algorithm + * (see \ref igraph_layout_umap() for the 2D version). + * + * \param graph Pointer to the graph to find a layout for (i.e. to embed). This is + * typically a directed, sparse graph with only edges for the shortest distances + * stored, e.g. a k-nearest neighbors graph with the edges going from each focal + * vertex to its neighbors. However, it can also be an undirected graph. If the + * \p distances_are_weights is \c true, this is treated as an undirected graph. + * \param res Pointer to the n by 3 matrix where the layout coordinates will be stored. + * \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 distances Pointer to a vector of distances associated with the graph edges. + * If this argument is \c NULL, all edges are assumed to have the same distance. + * \param min_dist A fudge parameter that decides how close two unconnected vertices + * can be in the embedding before feeling a repulsive force. It must not be + * negative. Typical values are between 0 and 1. + * \param epochs Number of iterations of the main stochastic gradient descent loop on + * the cross-entropy. Typical values are between 30 and 500. + * \param distances_are_weights Whether to use precomputed weights. If \c false (the + * typical use case), this function will compute weights from distances and + * then use them to compute the layout. If \c true, the "distances" vector contains + * precomputed weights, including possibly some weights equal to zero that are + * inconsequential for the layout optimization. + * \return Error code. + */ +igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, + igraph_matrix_t *res, + igraph_bool_t use_seed, + const igraph_vector_t *distances, + igraph_real_t min_dist, + igraph_integer_t epochs, + igraph_bool_t distances_are_weights) { + return igraph_i_layout_umap(graph, res, use_seed, + distances, min_dist, epochs, 3, distances_are_weights); +} diff --git a/src/vendor/cigraph/src/linalg/arpack.c b/src/vendor/cigraph/src/linalg/arpack.c index 1b9f3a57da5..12d70f8dc49 100644 --- a/src/vendor/cigraph/src/linalg/arpack.c +++ b/src/vendor/cigraph/src/linalg/arpack.c @@ -23,19 +23,21 @@ */ #include "igraph_arpack.h" -#include "igraph_memory.h" -#include "igraph_random.h" #include "core/interruption.h" #include "linalg/arpack_internal.h" +#include "igraph_memory.h" +#include "igraph_random.h" + #include #include #include +#include /* The ARPACK example file dssimp.f is used as a template */ -static int igraph_i_arpack_err_dsaupd(int error) { +static igraph_error_t igraph_i_arpack_err_dsaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; @@ -57,7 +59,7 @@ static int igraph_i_arpack_err_dsaupd(int error) { } } -static int igraph_i_arpack_err_dseupd(int error) { +static igraph_error_t igraph_i_arpack_err_dseupd(int error) { switch (error) { case -1: return IGRAPH_ARPACK_NPOS; case -2: return IGRAPH_ARPACK_NEVNPOS; @@ -79,7 +81,7 @@ static int igraph_i_arpack_err_dseupd(int error) { } -static int igraph_i_arpack_err_dnaupd(int error) { +static igraph_error_t igraph_i_arpack_err_dnaupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_MAXIT; case 3: return IGRAPH_ARPACK_NOSHIFT; @@ -100,7 +102,7 @@ static int igraph_i_arpack_err_dnaupd(int error) { } } -static int igraph_i_arpack_err_dneupd(int error) { +static igraph_error_t igraph_i_arpack_err_dneupd(int error) { switch (error) { case 1: return IGRAPH_ARPACK_REORDER; case -1: return IGRAPH_ARPACK_NPOS; @@ -121,9 +123,50 @@ static int igraph_i_arpack_err_dneupd(int error) { } } +/* Pristine ARPACK options object that is not exposed to the user; this is used + * as a template for \c igraph_i_arpack_options_default when the user requests + * a pointer to the default object */ +const static igraph_arpack_options_t igraph_i_arpack_options_pristine = { + /* .bmat = */ { 'I' }, + /* .n = */ 0, + /* .which = */ { 'X', 'X' }, + /* .nev = */ 1, + /* .tol = */ 0, + /* .ncv = */ 0, /* 0 means "automatic" */ + /* .ldv = */ 0, + /* .ishift = */ 1, + /* .mxiter = */ 3000, + /* .nb = */ 1, + /* .mode = */ 1, + /* .start = */ 0, + /* .lworl = */ 0, + /* .sigma = */ 0, + /* .sigmai = */ 0, + /* .info = */ 0, + /* .ierr = */ 0, + /* .noiter = */ 0, + /* .nconv = */ 0, + /* .numop = */ 0, + /* .numopb = */ 0, + /* .numreo = */ 0, + /* .iparam = */ { + /* same as ishift: */ 1, + 0, + /* same as mxiter: */ 3000, + /* same as nb: */ 1, + 0, + 0, + /* same as mode: */ 1 + /* the rest are all zeros */ + }, + /* .ipntr = */ { 0 /* the rest are all zeros */ } +}; + +static IGRAPH_THREAD_LOCAL igraph_arpack_options_t igraph_i_arpack_options_default; + /** * \function igraph_arpack_options_init - * Initialize ARPACK options + * \brief Initialize ARPACK options. * * Initializes ARPACK options, set them to default values. * You can always pass the initialized \ref igraph_arpack_options_t @@ -132,16 +175,20 @@ static int igraph_i_arpack_err_dneupd(int error) { * 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 = igraph_i_arpack_options_pristine; + o->bmat[0] = 'I'; o->n = 0; /* needs to be updated! */ o->which[0] = 'X'; o->which[1] = 'X'; @@ -164,9 +211,29 @@ void igraph_arpack_options_init(igraph_arpack_options_t *o) { o->iparam[8] = 0; o->iparam[9] = 0; o->iparam[10] = 0; } +/** + * \function igraph_arpack_options_get_default + * \brief Returns a pointer to a "default" ARPACK options object. + * + * This function is used by other igraph functions taking an \ref igraph_arpack_options_t + * object as an argument to get a reference to a pre-initialized "default" + * ARPACK options object when the user passes \c NULL instead of a real ARPACK + * options object. The object returned from this function is reset to a pristine + * state with every call to \c igraph_arpack_options_get_default(). + * + * + * The object returned from this function must \em not be destroyed. + * + * Time complexity: O(1). + */ +igraph_arpack_options_t* igraph_arpack_options_get_default(void) { + igraph_i_arpack_options_default = igraph_i_arpack_options_pristine; + return &igraph_i_arpack_options_default; +} + /** * \function igraph_arpack_storage_init - * Initialize ARPACK storage + * \brief Initialize ARPACK storage. * * You only need this function if you want to run multiple eigenvalue * calculations using ARPACK, and want to spare the memory @@ -175,9 +242,10 @@ void igraph_arpack_options_init(igraph_arpack_options_t *o) { * 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. + * + * 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. @@ -190,18 +258,28 @@ void igraph_arpack_options_init(igraph_arpack_options_t *o) { * 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_error_t igraph_arpack_storage_init(igraph_arpack_storage_t *s, igraph_integer_t maxn, + igraph_integer_t maxncv, igraph_integer_t maxldv, igraph_bool_t symm) { /* TODO: check arguments */ + if (maxn > INT_MAX) { + IGRAPH_ERROR("Maximum order of matrices too large for ARPACK.", IGRAPH_EOVERFLOW); + } + if (maxncv > INT_MAX) { + IGRAPH_ERROR("Maximum NCV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); + } + if (maxldv > INT_MAX) { + IGRAPH_ERROR("Maximum LDV parameter too large for ARPACK.", IGRAPH_EOVERFLOW); + } + 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_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -226,12 +304,12 @@ int igraph_arpack_storage_init(igraph_arpack_storage_t *s, long int maxn, #undef CHECKMEM IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_arpack_storage_destroy - * Deallocate ARPACK storage + * \brief Deallocate ARPACK storage. * * \param s The \ref igraph_arpack_storage_t object for which the * memory will be deallocated. @@ -261,7 +339,7 @@ void igraph_arpack_storage_destroy(igraph_arpack_storage_t *s) { * "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, +static igraph_error_t 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; @@ -294,7 +372,7 @@ static int igraph_i_arpack_rssolve_1x1(igraph_arpack_function_t *fun, void *extr * "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, +static igraph_error_t 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; @@ -327,7 +405,7 @@ static int igraph_i_arpack_rnsolve_1x1(igraph_arpack_function_t *fun, void *extr * "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, +static igraph_error_t 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]; @@ -335,8 +413,8 @@ static int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extr 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; + igraph_bool_t swap_evals = false; + igraph_bool_t complex_evals = false; int nev = options->nev; if (nev <= 0) { @@ -392,7 +470,7 @@ static int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extr 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)); + swap_evals = (igraph_complex_abs(eval1) > igraph_complex_abs(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)); @@ -405,7 +483,7 @@ static int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extr } 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)); + swap_evals = (igraph_complex_abs(eval1) < igraph_complex_abs(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)); @@ -475,7 +553,7 @@ static int igraph_i_arpack_rnsolve_2x2(igraph_arpack_function_t *fun, void *extr * "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, +static igraph_error_t 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]; @@ -560,9 +638,9 @@ static int igraph_i_arpack_rssolve_2x2(igraph_arpack_function_t *fun, void *extr 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_error_t 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]; @@ -585,9 +663,15 @@ int igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors, sort[0] = 'L'; sort[1] = 'M'; } else if (which('B', 'E')) { sort[0] = 'L'; sort[1] = 'A'; + } else { + /* None of the above, no sorting. These 'X' values are + * ignored by ARPACK, but we set them anyway in order to + * avoid an uninitialized 'sort' which would trigger + * checkers such as MemorySanitizer. */ + sort[0] = 'X'; sort[1] = 'X'; } - IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1)); + IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); IGRAPH_FINALLY(igraph_vector_destroy, &order); #ifdef HAVE_GFORTRAN igraphdsortr_(sort, &apply, &nconv, d, VECTOR(order), /*which_len=*/ 2); @@ -639,10 +723,10 @@ int igraph_arpack_rssort(igraph_vector_t *values, igraph_matrix_t *vectors, igraph_vector_destroy(&order); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -int igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, +igraph_error_t 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) { @@ -670,11 +754,17 @@ int igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, sort[0] = 'S'; sort[1] = 'I'; } else if (which('S', 'I')) { sort[0] = 'L'; sort[1] = 'I'; + } else { + /* None of the above, no sorting. These 'X' values are + * ignored by ARPACK, but we set them anyway in order to + * avoid an uninitialized 'sort' which would trigger + * checkers such as MemorySanitizer. */ + sort[0] = 'X'; sort[1] = 'X'; } #undef which - IGRAPH_CHECK(igraph_vector_init_seq(&order, 0, nconv - 1)); + IGRAPH_CHECK(igraph_vector_init_range(&order, 0, nconv)); IGRAPH_FINALLY(igraph_vector_destroy, &order); #ifdef HAVE_GFORTRAN igraphdsortc_(sort, &apply, &nconv, dr, di, VECTOR(order), /*which_len=*/ 2); @@ -756,7 +846,7 @@ int igraph_arpack_rnsort(igraph_matrix_t *values, igraph_matrix_t *vectors, } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -807,7 +897,7 @@ static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* /** * \function igraph_arpack_rssolve - * \brief ARPACK solver for symmetric matrices + * \brief ARPACK solver for symmetric matrices. * * This is the ARPACK solver for symmetric matrices. Please use * \ref igraph_arpack_rnsolve() for non-symmetric matrices. @@ -839,13 +929,13 @@ static void igraph_i_arpack_report_no_convergence(const igraph_arpack_options_t* * are found in O(n) time as well. */ -int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, +igraph_error_t 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; + igraph_bool_t free_them = false; int *select, i; int ido = 0; @@ -908,7 +998,7 @@ int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, #define CHECKMEM(x) \ if (!x) { \ - IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -1065,12 +1155,12 @@ int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, IGRAPH_FREE(v); IGRAPH_FINALLY_CLEAN(7); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_arpack_rnsolve - * \brief ARPACK solver for non-symmetric matrices + * \brief ARPACK solver for non-symmetric matrices. * * Please always consider calling \ref igraph_arpack_rssolve() if your * matrix is symmetric, it is much faster. @@ -1115,13 +1205,13 @@ int igraph_arpack_rssolve(igraph_arpack_function_t *fun, void *extra, * are found in O(n) time as well. */ -int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, +igraph_error_t 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; + igraph_bool_t free_them = false; int *select, i; int ido = 0; @@ -1187,7 +1277,7 @@ int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, #define CHECKMEM(x) \ if (!x) { \ - IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); \ + IGRAPH_ERROR("Cannot allocate memory for ARPACK", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ } \ IGRAPH_FINALLY(igraph_free, x); @@ -1354,12 +1444,12 @@ int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, IGRAPH_FREE(v); IGRAPH_FINALLY_CLEAN(8); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_arpack_unpack_complex - * \brief Make the result of the non-symmetric ARPACK solver more readable + * \brief Makes 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 @@ -1390,22 +1480,21 @@ int igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, * matrix. */ -int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, - long int nev) { +igraph_error_t igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *values, + igraph_integer_t 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_integer_t nodes = igraph_matrix_nrow(vectors); + igraph_integer_t no_evs = igraph_matrix_nrow(values); + igraph_integer_t i, j; + igraph_integer_t new_vector_pos, vector_pos; igraph_matrix_t new_vectors; /* Error checks */ if (nev < 0) { - IGRAPH_ERROR("`nev' cannot be negative", IGRAPH_EINVAL); + 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_ERROR("`nev' too large, we don't have that many in `values'.", IGRAPH_EINVAL); } @@ -1455,7 +1544,7 @@ int igraph_arpack_unpack_complex(igraph_matrix_t *vectors, igraph_matrix_t *valu } } igraph_matrix_destroy(vectors); - IGRAPH_CHECK(igraph_matrix_copy(vectors, &new_vectors)); + IGRAPH_CHECK(igraph_matrix_init_copy(vectors, &new_vectors)); igraph_matrix_destroy(&new_vectors); IGRAPH_FINALLY_CLEAN(1); diff --git a/src/vendor/cigraph/src/linalg/arpack_internal.h b/src/vendor/cigraph/src/linalg/arpack_internal.h index f318371e66b..9be196c90c0 100644 --- a/src/vendor/cigraph/src/linalg/arpack_internal.h +++ b/src/vendor/cigraph/src/linalg/arpack_internal.h @@ -28,9 +28,12 @@ include this header. */ +#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" +__BEGIN_DECLS + #ifndef INTERNAL_ARPACK #define igraphdsaupd_ dsaupd_ #define igraphdseupd_ dseupd_ @@ -226,4 +229,6 @@ int igraphdsortc_(char *which, int *apply, int* n, igraph_real_t *xreal, #endif +__END_DECLS + #endif /* ARPACK_INTERNAL_H */ diff --git a/src/vendor/cigraph/src/linalg/blas.c b/src/vendor/cigraph/src/linalg/blas.c index 8a4d017a030..8835e6fb25d 100644 --- a/src/vendor/cigraph/src/linalg/blas.c +++ b/src/vendor/cigraph/src/linalg/blas.c @@ -22,11 +22,11 @@ */ -#include "igraph_error.h" #include "igraph_blas.h" - #include "linalg/blas_internal.h" +#include + /** * \function igraph_blas_dgemv * \brief Matrix-vector multiplication using BLAS, vector version. @@ -45,18 +45,24 @@ * * Time complexity: O(nk) if the matrix is of size n x k * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_SUCCESS otherwise. * \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) { +igraph_error_t 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; + if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); + } + m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); @@ -70,6 +76,86 @@ void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, VECTOR(*x), &inc, &beta, VECTOR(*y), &inc); #endif + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_blas_dgemm + * \brief Matrix-matrix multiplication using BLAS. + * + * This function is a somewhat more user-friendly interface to + * the \c dgemm function in BLAS. \c dgemm calculates + * alpha*a*b + beta*c, where a, b and c are matrices, of which a and b + * can be transposed. + * + * \param transpose_a whether to transpose the matrix \p a + * \param transpose_b whether to transpose the matrix \p b + * \param alpha the constant \c alpha + * \param a the matrix \c a + * \param b the matrix \c b + * \param beta the constant \c beta + * \param c the matrix \c c. The result will also be stored here. + * If beta is zero, c will be resized to fit the result. + * + * Time complexity: O(n m k) where matrix a is of size n × k, and matrix b is of + * size k × m. + * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_EINVAL if the matrices have incompatible sizes, + * \c IGRAPH_SUCCESS otherwise. + * + * \example examples/simple/blas_dgemm.c + */ +igraph_error_t igraph_blas_dgemm(igraph_bool_t transpose_a, igraph_bool_t transpose_b, + igraph_real_t alpha, const igraph_matrix_t *a, const igraph_matrix_t *b, + igraph_real_t beta, igraph_matrix_t *c) { + char trans_a = transpose_a ? 'T' : 'N'; + char trans_b = transpose_b ? 'T' : 'N'; + int m, n, k, lda, ldb, ldc; + igraph_integer_t nrow_oa = transpose_a ? igraph_matrix_ncol(a) : igraph_matrix_nrow(a); + igraph_integer_t ncol_oa = transpose_a ? igraph_matrix_nrow(a) : igraph_matrix_ncol(a); + igraph_integer_t nrow_ob = transpose_b ? igraph_matrix_ncol(b) : igraph_matrix_nrow(b); + igraph_integer_t ncol_ob = transpose_b ? igraph_matrix_nrow(b) : igraph_matrix_ncol(b); + + if (ncol_oa != nrow_ob) { + IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId + " matrices cannot be multiplied, incompatible dimensions.", IGRAPH_EINVAL, + nrow_oa, ncol_oa, nrow_ob, ncol_ob); + } + if (beta != 0 && (ncol_oa != igraph_matrix_ncol(c) || nrow_oa != igraph_matrix_nrow(c))) { + IGRAPH_ERRORF("%" IGRAPH_PRId "-by-%" IGRAPH_PRId " and %" IGRAPH_PRId "-by-%" IGRAPH_PRId + " matrices cannot be added, incompatible dimensions.", IGRAPH_EINVAL, + nrow_oa, ncol_ob, igraph_matrix_nrow(c), igraph_matrix_ncol(c)); + } + if (nrow_oa > INT_MAX || ncol_oa > INT_MAX) { + IGRAPH_ERROR("Matrix A too large for BLAS.", IGRAPH_EOVERFLOW); + } + if (ncol_ob > INT_MAX) { + IGRAPH_ERROR("Matrix B too large for BLAS.", IGRAPH_EOVERFLOW); + } + if (beta == 0) { + IGRAPH_CHECK(igraph_matrix_resize(c, nrow_oa, ncol_ob)); + } + + m = (int) nrow_oa; + k = (int) ncol_oa; + n = (int) ncol_ob; + lda = (int) igraph_matrix_nrow(a); + ldb = (int) igraph_matrix_nrow(b); + ldc = (int) igraph_matrix_nrow(c); + + +#ifdef HAVE_GFORTRAN + igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), + &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc, + /*trans_a_len*/ 1, /*trans_b_len*/ 1); +#else + igraphdgemm_(&trans_a, &trans_b, &m, &n, &k, &alpha, VECTOR(a->data), + &lda, VECTOR(b->data), &ldb, &beta, VECTOR(c->data), &ldc); +#endif + + return IGRAPH_SUCCESS; } /** @@ -91,16 +177,23 @@ void igraph_blas_dgemv(igraph_bool_t transpose, igraph_real_t alpha, * * Time complexity: O(nk) if the matrix is of size n x k * + * \return \c IGRAPH_EOVERFLOW if the matrix is too large for BLAS, + * \c IGRAPH_SUCCESS otherwise. + * * \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, +igraph_error_t 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; + if (igraph_matrix_nrow(a) > INT_MAX || igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Matrix too large for BLAS", IGRAPH_EOVERFLOW); + } + m = (int) igraph_matrix_nrow(a); n = (int) igraph_matrix_ncol(a); @@ -111,6 +204,8 @@ void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, igraphdgemv_(&trans, &m, &n, &alpha, VECTOR(a->data), &m, (igraph_real_t*)x, &inc, &beta, y, &inc); #endif + + return IGRAPH_SUCCESS; } /** @@ -123,7 +218,11 @@ void igraph_blas_dgemv_array(igraph_bool_t transpose, igraph_real_t alpha, * 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); + if (igraph_vector_size(v) > INT_MAX) { + IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); + } + + int n = (int) igraph_vector_size(v); int one = 1; return igraphdnrm2_(&n, VECTOR(*v), &one); } @@ -140,10 +239,14 @@ igraph_real_t igraph_blas_dnrm2(const igraph_vector_t *v) { * * \example examples/simple/blas.c */ -int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, +igraph_error_t igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, igraph_real_t *res) { - int n = igraph_vector_size(v1); + if (igraph_vector_size(v1) > INT_MAX) { + IGRAPH_ERROR("Vector too large for BLAS", IGRAPH_EOVERFLOW); + } + + int n = (int) igraph_vector_size(v1); int one = 1; if (igraph_vector_size(v2) != n) { @@ -153,5 +256,5 @@ int igraph_blas_ddot(const igraph_vector_t *v1, const igraph_vector_t *v2, *res = igraphddot_(&n, VECTOR(*v1), &one, VECTOR(*v2), &one); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/linalg/blas_internal.h b/src/vendor/cigraph/src/linalg/blas_internal.h index 71810b05699..63f8d4434d0 100644 --- a/src/vendor/cigraph/src/linalg/blas_internal.h +++ b/src/vendor/cigraph/src/linalg/blas_internal.h @@ -29,9 +29,12 @@ include this header. */ +#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" +__BEGIN_DECLS + #ifndef INTERNAL_BLAS #define igraphdaxpy_ daxpy_ #define igraphdger_ dger_ @@ -87,4 +90,6 @@ double igraphdnrm2_(int *n, double *x, int *incx); double igraphddot_(int *n, double *dx, int *incx, double *dy, int *incy); +__END_DECLS + #endif diff --git a/src/vendor/cigraph/src/linalg/eigen.c b/src/vendor/cigraph/src/linalg/eigen.c index f42dc86cd52..52ee6c711bd 100644 --- a/src/vendor/cigraph/src/linalg/eigen.c +++ b/src/vendor/cigraph/src/linalg/eigen.c @@ -28,11 +28,12 @@ #include "igraph_interface.h" #include "igraph_adjlist.h" +#include #include #include #include -static int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, +static igraph_error_t igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, int n, void *extra, igraph_matrix_t *res) { @@ -54,18 +55,23 @@ static int igraph_i_eigen_arpackfun_to_mat(igraph_arpack_function_t *fun, igraph_vector_destroy(&v); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_matrix_t *A, +static igraph_error_t 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; + int n; + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); @@ -132,20 +138,25 @@ static int igraph_i_eigen_matrix_symmetric_lapack_lm(const igraph_matrix_t *A, igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A, +static igraph_error_t 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); + int i, w = 0, n; igraph_real_t small; int p1, p2, pr = 0; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + IGRAPH_VECTOR_INIT_FINALLY(&val, 0); if (vectors) { @@ -208,15 +219,18 @@ static int igraph_i_eigen_matrix_symmetric_lapack_sm(const igraph_matrix_t *A, igraph_vector_destroy(&val); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A, +static igraph_error_t 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? */ + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } int n = (int) igraph_matrix_nrow(A); int il = n - which->howmany + 1; @@ -225,10 +239,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_la(const igraph_matrix_t *A, /*il=*/ il, /*iu=*/ n, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A, +static igraph_error_t 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) { @@ -241,10 +255,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_sa(const igraph_matrix_t *A, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, +static igraph_error_t 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) { @@ -253,9 +267,14 @@ static int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, igraph_matrix_t vec1, vec2; igraph_vector_t val1, val2; - int n = (int) igraph_matrix_nrow(A); + int n; int p1 = 0, p2 = which->howmany / 2, pr = 0; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); + IGRAPH_VECTOR_INIT_FINALLY(&val1, 0); IGRAPH_VECTOR_INIT_FINALLY(&val2, 0); @@ -320,10 +339,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_be(const igraph_matrix_t *A, igraph_vector_destroy(&val1); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, +static igraph_error_t igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, igraph_vector_t *values, igraph_matrix_t *vectors) { @@ -333,10 +352,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_all(const igraph_matrix_t *A, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A, +static igraph_error_t 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) { @@ -348,10 +367,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_iv(const igraph_matrix_t *A, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A, +static igraph_error_t 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) { @@ -362,10 +381,10 @@ static int igraph_i_eigen_matrix_symmetric_lapack_sel(const igraph_matrix_t *A, /*abstol=*/ 1e-14, values, vectors, /*support=*/ 0)); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, +static igraph_error_t 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, @@ -379,9 +398,15 @@ static int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, /* First we need to create a dense square matrix */ if (A) { - n = (int) igraph_matrix_nrow(A); + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_matrix_nrow(A); /* TODO: n isn't used after this assignment */ } else if (sA) { - n = (int) igraph_sparsemat_nrow(sA); + if (igraph_sparsemat_nrow(sA) > INT_MAX) { + IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + n = (int) igraph_sparsemat_nrow(sA); /* TODO: n isn't used after this assignment */ IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &mA); IGRAPH_CHECK(igraph_sparsemat_as_matrix(&mA, sA)); @@ -438,7 +463,7 @@ static int igraph_i_eigen_matrix_symmetric_lapack(const igraph_matrix_t *A, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { @@ -446,7 +471,7 @@ typedef struct igraph_i_eigen_matrix_sym_arpack_data_t { 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, +static igraph_error_t igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { @@ -454,8 +479,8 @@ static int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, (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); + IGRAPH_CHECK(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); @@ -463,10 +488,10 @@ static int igraph_i_eigen_matrix_sym_arpack_cb(igraph_real_t *to, igraph_vector_null(&vto); igraph_sparsemat_gaxpy(data->sA, &vfrom, &vto); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A, +static igraph_error_t 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, @@ -538,10 +563,10 @@ static int igraph_i_eigen_matrix_symmetric_arpack_be(const igraph_matrix_t *A, igraph_vector_destroy(&tmpvalues); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A, +static igraph_error_t 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, @@ -617,7 +642,7 @@ static int igraph_i_eigen_matrix_symmetric_arpack(const igraph_matrix_t *A, IGRAPH_CHECK(igraph_arpack_rssolve(fun, extra, options, storage, values, vectors)); - return 0; + return IGRAPH_SUCCESS; } } @@ -649,7 +674,8 @@ typedef struct igraph_i_eml_cmp_t { 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_m = VECTOR(*myextra->mag)[*aa]; igraph_real_t b_m = VECTOR(*myextra->mag)[*bb]; @@ -694,7 +720,8 @@ static int igraph_i_eigen_matrix_lapack_cmp_lm(void *extra, const void *a, 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_m = VECTOR(*myextra->mag)[*aa]; igraph_real_t b_m = VECTOR(*myextra->mag)[*bb]; @@ -738,7 +765,8 @@ 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; @@ -777,7 +805,8 @@ 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_r = VECTOR(*myextra->real)[*aa]; igraph_real_t b_r = VECTOR(*myextra->real)[*bb]; @@ -814,7 +843,8 @@ 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; @@ -852,7 +882,8 @@ 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_integer_t *aa = (igraph_integer_t*) a; + igraph_integer_t *bb = (igraph_integer_t*) b; igraph_real_t a_i = VECTOR(*myextra->imag)[*aa]; igraph_real_t b_i = VECTOR(*myextra->imag)[*bb]; @@ -897,7 +928,7 @@ static int igraph_i_eigen_matrix_lapack_cmp_si(void *extra, const void *a, } \ } while (0) -static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, +static igraph_error_t igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, const igraph_vector_t *imag, const igraph_matrix_t *compressed, const igraph_eigen_which_t *which, @@ -905,22 +936,24 @@ static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, igraph_matrix_complex_t *vectors) { igraph_vector_int_t idx; igraph_vector_t mag; - igraph_bool_t hasmag = 0; - int nev = (int) igraph_vector_size(real); + igraph_bool_t hasmag = false; + int nev; int howmany = 0, start = 0; - int i; + igraph_integer_t i; igraph_i_eigen_matrix_lapack_cmp_t cmpfunc = 0; igraph_i_eml_cmp_t vextra; void *extra; + if (igraph_vector_size(real) > INT_MAX) { + IGRAPH_ERROR("Number of eigenvalues too large for LAPACK.", IGRAPH_EOVERFLOW); + } + nev = (int) igraph_vector_size(real); + vextra.mag = &mag; vextra.real = real; vextra.imag = imag; extra = &vextra; - IGRAPH_CHECK(igraph_vector_int_init(&idx, nev)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &idx); - switch (which->pos) { case IGRAPH_EIGEN_LM: INITMAG(); @@ -966,9 +999,8 @@ static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, break; } - for (i = 0; i < nev; i++) { - VECTOR(idx)[i] = i; - } + IGRAPH_CHECK(igraph_vector_int_init_range(&idx, 0, nev)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &idx); igraph_qsort_r(VECTOR(idx), (size_t) nev, sizeof(VECTOR(idx)[0]), extra, cmpfunc); @@ -981,17 +1013,17 @@ static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, if (values) { IGRAPH_CHECK(igraph_vector_complex_resize(values, howmany)); for (i = 0; i < howmany; i++) { - int x = VECTOR(idx)[start + i]; + igraph_integer_t 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_integer_t n = 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]; + igraph_integer_t j, x = VECTOR(idx)[start + i]; if (VECTOR(*imag)[x] == 0) { /* real eigenvalue */ for (j = 0; j < n; j++) { @@ -1017,17 +1049,17 @@ static int igraph_i_eigen_matrix_lapack_reorder(const igraph_vector_t *real, igraph_vector_int_destroy(&idx); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t *A, +static igraph_error_t 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); + igraph_integer_t n = igraph_matrix_nrow(A); int info = 1; IGRAPH_VECTOR_INIT_FINALLY(&valuesreal, n); @@ -1052,68 +1084,11 @@ static int igraph_i_eigen_matrix_lapack_common(const igraph_matrix_t *A, 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); -} + return IGRAPH_SUCCESS; -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, +static igraph_error_t igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -1127,8 +1102,14 @@ static int igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, /* We need to create a dense square matrix first */ if (A) { + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } n = (int) igraph_matrix_nrow(A); } else if (sA) { + if (igraph_sparsemat_nrow(sA) > INT_MAX) { + IGRAPH_ERROR("Number of rows in sparse matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } n = (int) igraph_sparsemat_nrow(sA); IGRAPH_CHECK(igraph_matrix_init(&mA, 0, 0)); IGRAPH_FINALLY(igraph_matrix_destroy, &mA); @@ -1139,54 +1120,19 @@ static int igraph_i_eigen_matrix_lapack(const igraph_matrix_t *A, 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; - } + IGRAPH_CHECK(igraph_i_eigen_matrix_lapack_common(myA, which, + values, + vectors)); if (!A) { igraph_matrix_destroy(&mA); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_checks(const igraph_matrix_t *A, +static igraph_error_t igraph_i_eigen_checks(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n) { @@ -1205,7 +1151,7 @@ static int igraph_i_eigen_checks(const igraph_matrix_t *A, } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1214,7 +1160,7 @@ static int igraph_i_eigen_checks(const igraph_matrix_t *A, * \example examples/simple/igraph_eigen_matrix_symmetric.c */ -int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, +igraph_error_t igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -1238,35 +1184,31 @@ int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } - switch (algorithm) { - case IGRAPH_EIGEN_AUTO: + if (algorithm == 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)); + algorithm = IGRAPH_EIGEN_LAPACK; } else { - IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, - extra, which, - options, storage, - values, vectors)); + algorithm = IGRAPH_EIGEN_ARPACK; } - break; + } + + switch (algorithm) { case IGRAPH_EIGEN_LAPACK: IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_lapack(A, sA, fun, n, extra, - which, values, - vectors)); + which, values, vectors)); break; case IGRAPH_EIGEN_ARPACK: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } IGRAPH_CHECK(igraph_i_eigen_matrix_symmetric_arpack(A, sA, fun, n, extra, - which, options, - storage, - values, vectors)); + which, options, storage, values, vectors)); break; default: IGRAPH_ERROR("Unknown 'algorithm'", IGRAPH_EINVAL); } - return 0; + return IGRAPH_SUCCESS; } /** @@ -1274,7 +1216,7 @@ int igraph_eigen_matrix_symmetric(const igraph_matrix_t *A, * */ -int igraph_eigen_matrix(const igraph_matrix_t *A, +igraph_error_t igraph_eigen_matrix(const igraph_matrix_t *A, const igraph_sparsemat_t *sA, igraph_arpack_function_t *fun, int n, void *extra, @@ -1336,30 +1278,30 @@ int igraph_eigen_matrix(const igraph_matrix_t *A, IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_adjacency_arpack_sym_cb(igraph_real_t *to, +static igraph_error_t 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; + igraph_integer_t 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]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] += from[nei]; } } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, +static igraph_error_t 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, @@ -1373,7 +1315,7 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, igraph_adjlist_t adjlist; void *extra = (void*) &adjlist; - int n = igraph_vcount(graph); + igraph_integer_t n = igraph_vcount(graph); if (!options) { IGRAPH_ERROR("`options' must be given for ARPACK algorithm", @@ -1396,6 +1338,9 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, IGRAPH_ERROR("ARPACK adjacency eigensolver does not implement " "`ALL' eigenvalues", IGRAPH_UNIMPLEMENTED); } + if (n > INT_MAX) { + IGRAPH_ERROR("Graph has too many vertices for ARPACK.", IGRAPH_EOVERFLOW); + } switch (which->pos) { case IGRAPH_EIGEN_LM: @@ -1416,7 +1361,7 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, break; case IGRAPH_EIGEN_ALL: options->which[0] = 'L'; options->which[1] = 'M'; - options->nev = n; + options->nev = (int) n; break; case IGRAPH_EIGEN_BE: IGRAPH_ERROR("Eigenvectors from both ends with ARPACK", @@ -1438,8 +1383,8 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, break; } - options->n = n; - options->ncv = 2 * options->nev < n ? 2 * options->nev : n; + options->n = (int) n; + options->ncv = 2 * options->nev < options->n ? 2 * options->nev : options->n; IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); @@ -1450,7 +1395,7 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1458,7 +1403,7 @@ static int igraph_i_eigen_adjacency_arpack(const igraph_t *graph, * */ -int igraph_eigen_adjacency(const igraph_t *graph, +igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, @@ -1479,34 +1424,38 @@ int igraph_eigen_adjacency(const igraph_t *graph, IGRAPH_ERROR("Invalid 'pos' position in 'which'", IGRAPH_EINVAL); } + if (algorithm == IGRAPH_EIGEN_AUTO) { + /* Select ARPACK unconditionally because nothing else is implemented yet */ + algorithm = IGRAPH_EIGEN_ARPACK; + } else if (algorithm == IGRAPH_EIGEN_COMP_AUTO) { + /* Select ARPACK unconditionally because nothing else is implemented yet */ + algorithm = IGRAPH_EIGEN_COMP_ARPACK; + } + 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: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } 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: + if (options == 0) { + options = igraph_arpack_options_get_default(); + } IGRAPH_ERROR("'COMP_ARPACK' algorithm not implemented yet", IGRAPH_UNIMPLEMENTED); /* TODO */ @@ -1515,8 +1464,7 @@ int igraph_eigen_adjacency(const igraph_t *graph, IGRAPH_ERROR("Unknown `algorithm'", IGRAPH_EINVAL); } - - return 0; + return IGRAPH_SUCCESS; } /** @@ -1524,7 +1472,7 @@ int igraph_eigen_adjacency(const igraph_t *graph, * */ -int igraph_eigen_laplacian(const igraph_t *graph, +igraph_error_t igraph_eigen_laplacian(const igraph_t *graph, igraph_eigen_algorithm_t algorithm, const igraph_eigen_which_t *which, igraph_arpack_options_t *options, diff --git a/src/vendor/cigraph/src/linalg/lapack.c b/src/vendor/cigraph/src/linalg/lapack.c index 5652caf78e0..c50fe55cd56 100644 --- a/src/vendor/cigraph/src/linalg/lapack.c +++ b/src/vendor/cigraph/src/linalg/lapack.c @@ -22,9 +22,54 @@ */ #include "igraph_lapack.h" - #include "linalg/lapack_internal.h" +#include + +#define BASE_FORTRAN_INT +#include "igraph_pmt.h" +#include "igraph_vector_type.h" +#include "igraph_vector_pmt.h" +#include "../core/vector.pmt" +#include "igraph_pmt_off.h" +#undef BASE_FORTRAN_INT + +/* Converts a Fortran integer vector to an igraph vector */ +static igraph_error_t igraph_vector_int_update_from_fortran( + igraph_vector_int_t* vec, const igraph_vector_fortran_int_t* fortran_vec +) { + igraph_integer_t size = igraph_vector_fortran_int_size(fortran_vec); + + IGRAPH_CHECK(igraph_vector_int_resize(vec, size)); + + for (igraph_integer_t i = 0; i < size; i++) { + VECTOR(*vec)[i] = VECTOR(*fortran_vec)[i]; + } + + return IGRAPH_SUCCESS; +} + +/* Allocates a Fortran integer vector from the contents of an igraph vector */ +static igraph_error_t igraph_vector_int_copy_to_fortran( + const igraph_vector_int_t* vec, igraph_vector_fortran_int_t* fortran_vec +) { + igraph_integer_t i, size = igraph_vector_int_size(vec); + + IGRAPH_CHECK(igraph_vector_fortran_int_resize(fortran_vec, size)); + + for (i = 0; i < size; i++) { + if (VECTOR(*vec)[i] > INT_MAX) { + IGRAPH_ERROR( + "Overflow error while copying an igraph integer vector to a " + "Fortran integer vector.", IGRAPH_EOVERFLOW + ); + } + VECTOR(*fortran_vec)[i] = (int) VECTOR(*vec)[i]; + } + + return IGRAPH_SUCCESS; +} + /** * \function igraph_lapack_dgetrf * \brief LU factorization of a general M-by-N matrix. @@ -53,22 +98,30 @@ * Time complexity: TODO. */ -int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, +igraph_error_t 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)); + int m; + int n; + size_t num_elts; + int lda; + igraph_vector_fortran_int_t vipiv; + + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + + m = (int) igraph_matrix_nrow(a); + n = (int) igraph_matrix_ncol(a); + num_elts = m < n ? m : n; + lda = m > 0 ? m : 1; - igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(*myipiv), info); + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, num_elts)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + + igraphdgetrf_(&m, &n, VECTOR(a->data), &lda, VECTOR(vipiv), info); if (*info > 0) { IGRAPH_WARNING("LU: factor is exactly singular."); @@ -98,12 +151,14 @@ int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, } } - if (!ipiv) { - igraph_vector_int_destroy(&vipiv); - IGRAPH_FINALLY_CLEAN(1); + if (ipiv) { + IGRAPH_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); } - return 0; + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /** @@ -120,8 +175,8 @@ int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, * 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 + * \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. @@ -130,14 +185,27 @@ int igraph_lapack_dgetrf(igraph_matrix_t *a, igraph_vector_int_t *ipiv, * Time complexity: TODO. */ -int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, +igraph_error_t 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 n; + int nrhs; + int lda; + int ldb; int info; + igraph_vector_fortran_int_t vipiv; + + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + + n = (int) igraph_matrix_nrow(a); + nrhs = (int) igraph_matrix_ncol(b); + lda = n > 0 ? n : 1; + ldb = n > 0 ? n : 1; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); @@ -145,19 +213,23 @@ int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, 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) { + if (! igraph_vector_int_isininterval(ipiv, 1, n)) { 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), + + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, igraph_vector_int_size(ipiv))); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); + IGRAPH_CHECK(igraph_vector_int_copy_to_fortran(ipiv, &vipiv)); + + igraphdgetrs_(&trans, &n, &nrhs, VECTOR(a->data), &lda, VECTOR(vipiv), VECTOR(b->data), &ldb, &info); + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + if (info < 0) { switch (info) { case -1: @@ -193,12 +265,12 @@ int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_lapack_dgesv - * Solve system of linear equations with LU factorization + * \brief 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 @@ -210,6 +282,7 @@ int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, * 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. @@ -231,14 +304,20 @@ int igraph_lapack_dgetrs(igraph_bool_t transpose, const igraph_matrix_t *a, * \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) { +igraph_error_t igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, + igraph_matrix_t *b, int *info) { + if (igraph_matrix_nrow(a) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } + if (igraph_matrix_ncol(a) > INT_MAX) { + IGRAPH_ERROR("Number of columns in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } 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; + igraph_vector_fortran_int_t vipiv; if (n != igraph_matrix_ncol(a)) { IGRAPH_ERROR("Cannot LU solve matrix.", IGRAPH_NONSQUARE); @@ -247,13 +326,10 @@ int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, 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; - } + IGRAPH_CHECK(igraph_vector_fortran_int_init(&vipiv, n)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &vipiv); - igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(*myipiv), + igraphdgesv_(&n, &nrhs, VECTOR(a->data), &lda, VECTOR(vipiv), VECTOR(b->data), &ldb, info); if (*info > 0) { @@ -290,24 +366,28 @@ int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, } } - if (!ipiv) { - igraph_vector_int_destroy(&vipiv); - IGRAPH_FINALLY_CLEAN(1); + if (ipiv) { + IGRAPH_CHECK(igraph_vector_int_update_from_fortran(ipiv, &vipiv)); } - return 0; + igraph_vector_fortran_int_destroy(&vipiv); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } /** * \function igraph_lapack_dsyevr - * Selected eigenvalues and optionally eigenvectors of a symmetric matrix + * \brief 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. + * + * 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. @@ -358,7 +438,7 @@ int igraph_lapack_dgesv(igraph_matrix_t *a, igraph_vector_int_t *ipiv, * \example examples/simple/igraph_lapack_dsyevr.c */ -int igraph_lapack_dsyevr(const igraph_matrix_t *A, +igraph_error_t 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, @@ -367,12 +447,15 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, igraph_matrix_t Acopy; char jobz = vectors ? 'V' : 'N', range, uplo = 'U'; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } 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_fortran_int_t mysupport; igraph_vector_t work; - igraph_vector_int_t iwork; + igraph_vector_fortran_int_t iwork; int lwork = -1, liwork = -1; if (n != igraph_matrix_ncol(A)) { @@ -387,43 +470,41 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, IGRAPH_ERROR("Invalid 'il' and/or 'iu' values.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_init_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); + IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, 1)); + IGRAPH_FINALLY(igraph_vector_fortran_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_fortran_int_init(&mysupport, 0)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &mysupport); 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)); + IGRAPH_CHECK(igraph_vector_fortran_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)); + IGRAPH_CHECK(igraph_vector_fortran_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))); + IGRAPH_CHECK(igraph_vector_fortran_int_resize(&mysupport, 2 * (iu - il + 1))); if (vectors) { IGRAPH_CHECK(igraph_matrix_resize(vectors, n, iu - il + 1)); } @@ -432,7 +513,7 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, 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), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); if (info != 0) { @@ -442,11 +523,11 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, lwork = (int) VECTOR(work)[0]; liwork = VECTOR(iwork)[0]; IGRAPH_CHECK(igraph_vector_resize(&work, lwork)); - IGRAPH_CHECK(igraph_vector_int_resize(&iwork, liwork)); + IGRAPH_CHECK(igraph_vector_fortran_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), + vectors ? &MATRIX(*vectors, 0, 0) : 0, &ldz, VECTOR(mysupport), VECTOR(work), &lwork, VECTOR(iwork), &liwork, &info); if (info != 0) { @@ -460,29 +541,29 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, IGRAPH_CHECK(igraph_matrix_resize(vectors, n, m)); } if (support) { + IGRAPH_CHECK(igraph_vector_int_update_from_fortran(support, &mysupport)); IGRAPH_CHECK(igraph_vector_int_resize(support, m)); } - if (!support) { - igraph_vector_int_destroy(&vsupport); - IGRAPH_FINALLY_CLEAN(1); - } + igraph_vector_fortran_int_destroy(&mysupport); + IGRAPH_FINALLY_CLEAN(1); + if (!values) { igraph_vector_destroy(&vvalues); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&iwork); + igraph_vector_fortran_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_lapack_dgeev - * Eigenvalues and optionally eigenvectors of a non-symmetric matrix + * \brief 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 @@ -493,8 +574,8 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, * 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). + * 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 @@ -538,7 +619,7 @@ int igraph_lapack_dsyevr(const igraph_matrix_t *A, * \example examples/simple/igraph_lapack_dgeev.c */ -int igraph_lapack_dgeev(const igraph_matrix_t *A, +igraph_error_t igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, igraph_matrix_t *vectorsleft, @@ -547,6 +628,11 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, char jobvl = vectorsleft ? 'V' : 'N'; char jobvr = vectorsright ? 'V' : 'N'; + igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ + + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } int n = (int) igraph_matrix_nrow(A); int lda = n, ldvl = n, ldvr = n, lwork = -1; igraph_vector_t work; @@ -558,7 +644,7 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, IGRAPH_ERROR("Cannot calculate eigenvalues (dgeev).", IGRAPH_NONSQUARE); } - IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&work, 1); @@ -584,8 +670,8 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, 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, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, VECTOR(work), &lwork, info); lwork = (int) VECTOR(work)[0]; @@ -593,8 +679,8 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, 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, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, VECTOR(work), &lwork, info); if (*info < 0) { @@ -620,12 +706,12 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_lapack_dgeevx - * Eigenvalues/vectors of nonsymmetric matrices, expert mode + * \brief 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. @@ -659,7 +745,8 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, * 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. + * Users' Guide. Note that the eigenvectors obtained for the balanced + * matrix are backtransformed to those of \p A. * * \param balance Scalar that indicated, whether the input matrix * should be balanced. Possible values: @@ -690,12 +777,14 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, * 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. + * and (j+1)-th columns contain the real and imaginary parts of the + * 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 + * \param ihi if not NULL, \p ilo and \p ihi point to 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 @@ -741,7 +830,7 @@ int igraph_lapack_dgeev(const igraph_matrix_t *A, * \example examples/simple/igraph_lapack_dgeevx.c */ -int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, +igraph_error_t igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, const igraph_matrix_t *A, igraph_vector_t *valuesreal, igraph_vector_t *valuesimag, @@ -757,15 +846,27 @@ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, char jobvl = vectorsleft ? 'V' : 'N'; char jobvr = vectorsright ? 'V' : 'N'; char sense; + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } 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_vector_fortran_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; + igraph_real_t dummy; /* to prevent some Clang sanitizer warnings */ + int ilo_dummy; + int ihi_dummy; + if (ilo == NULL) { + ilo = &ilo_dummy; + } + if (ihi == NULL) { + ihi = &ihi_dummy; + } if (igraph_matrix_ncol(A) != n) { IGRAPH_ERROR("Cannot calculate eigenvalues (dgeevx).", IGRAPH_NONSQUARE); } @@ -798,12 +899,12 @@ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, sense = 'B'; } - IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_init_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); + IGRAPH_CHECK(igraph_vector_fortran_int_init(&iwork, n)); + IGRAPH_FINALLY(igraph_vector_fortran_int_destroy, &iwork); if (!valuesreal) { IGRAPH_VECTOR_INIT_FINALLY(&vreal, n); @@ -832,11 +933,11 @@ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, 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, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, - rconde ? VECTOR(*rconde) : 0, - rcondv ? VECTOR(*rcondv) : 0, + rconde ? VECTOR(*rconde) : &dummy, + rcondv ? VECTOR(*rcondv) : &dummy, VECTOR(work), &lwork, VECTOR(iwork), info); lwork = (int) VECTOR(work)[0]; @@ -844,11 +945,11 @@ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, 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, + vectorsleft ? &MATRIX(*vectorsleft, 0, 0) : &dummy, &ldvl, + vectorsright ? &MATRIX(*vectorsright, 0, 0) : &dummy, &ldvr, ilo, ihi, VECTOR(*myscale), abnrm, - rconde ? VECTOR(*rconde) : 0, - rcondv ? VECTOR(*rcondv) : 0, + rconde ? VECTOR(*rconde) : &dummy, + rcondv ? VECTOR(*rcondv) : &dummy, VECTOR(work), &lwork, VECTOR(iwork), info); if (*info < 0) { @@ -876,18 +977,21 @@ int igraph_lapack_dgeevx(igraph_lapack_dgeevx_balance_t balance, IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_int_destroy(&iwork); + igraph_vector_fortran_int_destroy(&iwork); igraph_vector_destroy(&work); igraph_matrix_destroy(&Acopy); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } -int igraph_lapack_dgehrd(const igraph_matrix_t *A, +igraph_error_t igraph_lapack_dgehrd(const igraph_matrix_t *A, int ilo, int ihi, igraph_matrix_t *result) { + if (igraph_matrix_nrow(A) > INT_MAX) { + IGRAPH_ERROR("Number of rows in matrix too large for LAPACK.", IGRAPH_EOVERFLOW); + } int n = (int) igraph_matrix_nrow(A); int lda = n; int lwork = -1; @@ -908,10 +1012,10 @@ int igraph_lapack_dgehrd(const igraph_matrix_t *A, if (n <= 1) { IGRAPH_CHECK(igraph_matrix_update(result, A)); - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_CHECK(igraph_matrix_copy(&Acopy, A)); + IGRAPH_CHECK(igraph_matrix_init_copy(&Acopy, A)); IGRAPH_FINALLY(igraph_matrix_destroy, &Acopy); IGRAPH_VECTOR_INIT_FINALLY(&tau, n - 1); @@ -950,5 +1054,5 @@ int igraph_lapack_dgehrd(const igraph_matrix_t *A, } } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/linalg/lapack_internal.h b/src/vendor/cigraph/src/linalg/lapack_internal.h index 0ab117e96f8..198faf32ad5 100644 --- a/src/vendor/cigraph/src/linalg/lapack_internal.h +++ b/src/vendor/cigraph/src/linalg/lapack_internal.h @@ -29,9 +29,12 @@ include this header. */ +#include "igraph_decls.h" #include "igraph_types.h" #include "config.h" +__BEGIN_DECLS + #ifndef INTERNAL_LAPACK #define igraphdgeevx_ dgeevx_ #define igraphdgeev_ dgeev_ @@ -180,4 +183,6 @@ int igraphdgehrd_(int *n, int *ilo, int *ihi, igraph_real_t *A, int *lda, igraph_real_t igraphddot_(int *n, igraph_real_t *dx, int *incx, igraph_real_t *dy, int *incy); +__END_DECLS + #endif diff --git a/src/vendor/cigraph/src/math/bfgs.c b/src/vendor/cigraph/src/math/bfgs.c deleted file mode 100644 index a272029849c..00000000000 --- a/src/vendor/cigraph/src/math/bfgs.c +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- 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/vendor/cigraph/src/math/complex.c b/src/vendor/cigraph/src/math/complex.c index e47b1685d73..e895ab3059a 100644 --- a/src/vendor/cigraph/src/math/complex.c +++ b/src/vendor/cigraph/src/math/complex.c @@ -22,7 +22,7 @@ */ #include "igraph_complex.h" -#include "core/math.h" + #include /** @@ -43,6 +43,10 @@ igraph_complex_t igraph_complex_polar(igraph_real_t r, igraph_real_t theta) { return res; } +/** + * Deprecated in favour of igraph_complex_almost_equals(), which uses relative + * tolerances. Will be removed in 0.11. + */ igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, igraph_complex_t z2, igraph_real_t tol) { @@ -53,12 +57,6 @@ igraph_bool_t igraph_complex_eq_tol(igraph_complex_t z1, 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); @@ -194,6 +192,7 @@ igraph_complex_t igraph_complex_inv(igraph_complex_t z) { } igraph_real_t igraph_complex_abs(igraph_complex_t z) { + /* hypot() avoids overflow at intermediate stages of the calculation */ return hypot(IGRAPH_REAL(z), IGRAPH_IMAG(z)); } diff --git a/src/vendor/cigraph/src/math/safe_intop.c b/src/vendor/cigraph/src/math/safe_intop.c new file mode 100644 index 00000000000..0c8b168f55e --- /dev/null +++ b/src/vendor/cigraph/src/math/safe_intop.c @@ -0,0 +1,156 @@ +/* + IGraph library. + Copyright (C) 2022 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 +*/ + +#include "math/safe_intop.h" + +/* Use IGRAPH_SAFE_ADD() instead unless there is a need to intercept errors. */ +igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { + IGRAPH_SAFE_ADD(a, b, res); + return IGRAPH_SUCCESS; +} + +/* Use IGRAPH_SAFE_MULT() instead unless there is a need to intercept errors. */ +igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res) { + IGRAPH_SAFE_MULT(a, b, res); + return IGRAPH_SUCCESS; +} + +/* Overflow-safe sum of integer vector elements. */ +igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res) { + igraph_integer_t i, n = igraph_vector_int_size(vec); + igraph_integer_t sum = 0; + for (i=0; i < n; ++i) { + IGRAPH_SAFE_ADD(sum, VECTOR(*vec)[i], &sum); + } + *res = sum; + return IGRAPH_SUCCESS; +} + +/* Overflow-safe product of integer vector elements. */ +igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res) { + igraph_integer_t i, n = igraph_vector_int_size(vec); + igraph_integer_t prod = 1; + for (i=0; i < n; ++i) { + IGRAPH_SAFE_MULT(prod, VECTOR(*vec)[i], &prod); + } + *res = prod; + return IGRAPH_SUCCESS; +} + +/** + * Rounds up an integer to the next power of 2, with overflow check. + * The result for 2, 3 and 4, respectively, would be 2, 4, and 4. + * This function must not be called with negative input. + * Based on https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + */ +igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res) { + IGRAPH_ASSERT(k >= 0); + if (k == 0) { + *res = 0; + return IGRAPH_SUCCESS; + } + k--; + k |= k >> 1; + k |= k >> 2; + k |= k >> 4; + k |= k >> 8; + k |= k >> 16; +#if IGRAPH_INTEGER_SIZE == 32 + /* Nothing else to do. */ +#elif IGRAPH_INTEGER_SIZE == 64 + k |= k >> 32; +#else + /* If values other than 32 or 64 become allowed, + * this code will need to be updated. */ +# error "Unexpected IGRAPH_INTEGER_SIZE value." +#endif + if (k < IGRAPH_INTEGER_MAX) { + *res = k+1; + return IGRAPH_SUCCESS; + } else { + IGRAPH_ERRORF("Overflow when computing next power of 2 for %" IGRAPH_PRId ".", + IGRAPH_EOVERFLOW, k); + } +} + +/** + * Computes 2^k as an integer, with overflow check. + * This function must not be called with negative input. + */ +igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res) { + IGRAPH_ASSERT(k >= 0); + if (k > IGRAPH_INTEGER_SIZE-2) { + IGRAPH_ERRORF("Overflow when raising 2 to power %" IGRAPH_PRId ".", + IGRAPH_EOVERFLOW, k); + } + *res = (igraph_integer_t) 1 << k; + return IGRAPH_SUCCESS; +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is assumed to have no + * fractional part. + */ +static igraph_error_t igraph_i_safe_real_to_int(igraph_real_t value, igraph_integer_t *result) { + /* IGRAPH_INTEGER_MAX is one less than a power of 2, and may not be representable as + * a floating point number. Thus we cannot safely check that value <= IGRAPH_INTEGER_MAX, + * as this would convert IGRAPH_INTEGER_MAX to floating point, potentially chaning its value. + * Instead, we compute int_max_plus_1 = IGRAPH_INTEGER_MAX + 1, which is exactly representable + * since it is a power of 2, and check that value < int_max_plus_1. + * + * IGRAPH_INTEGER_MIN is a negative power of 2, so there is no such issue. + */ + const igraph_real_t int_max_plus_1 = 2.0 * (IGRAPH_INTEGER_MAX / 2 + 1); + const igraph_real_t int_min = (igraph_real_t) IGRAPH_INTEGER_MIN; + if (int_min <= value && value < int_max_plus_1) { + *result = (igraph_integer_t) value; + return IGRAPH_SUCCESS; + } else { + /* %.f ensures exact printing, %g would not */ + IGRAPH_ERRORF("Cannot convert %.f to integer, outside of representable range.", IGRAPH_EOVERFLOW, value); + } +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with ceil(). + */ +igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t *result) { + return igraph_i_safe_real_to_int(ceil(value), result); +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with floor(). + */ +igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t *result) { + return igraph_i_safe_real_to_int(floor(value), result); +} + +/** + * Converts an igraph_real_t into an igraph_integer_t with range checks to + * protect from undefined behaviour. The input value is converted into an + * integer with round(). + */ +igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result) { + return igraph_i_safe_real_to_int(round(value), result); +} diff --git a/src/vendor/cigraph/src/math/safe_intop.h b/src/vendor/cigraph/src/math/safe_intop.h new file mode 100644 index 00000000000..94360c16de3 --- /dev/null +++ b/src/vendor/cigraph/src/math/safe_intop.h @@ -0,0 +1,136 @@ +/* + 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 _safe_a copy of the GNU General Public 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_SAFE_INTOP_H +#define IGRAPH_MATH_SAFE_INTOP_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +#include "config.h" + +#include + +__BEGIN_DECLS + +/* Largest positive value for igraph_real_t that can safely represent integers. */ +#define IGRAPH_MAX_EXACT_REAL ((double)(1LL << DBL_MANT_DIG)) + +/* These macros raise an error if the operation would result in an overflow. + * They must only be used in functions that return an igraph_error_t. + * + * This code is based on the recommendation of + * https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard + */ + +#ifdef HAVE_BUILTIN_OVERFLOW + +#define IGRAPH_SAFE_ADD(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_sum; \ + if (__builtin_add_overflow(_safe_a, _safe_b, &_safe_sum)) { \ + IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + *(res) = _safe_sum; \ + } while (0) + +#define IGRAPH_SAFE_MULT(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_prod; \ + if (__builtin_mul_overflow(_safe_a, _safe_b, &_safe_prod)) { \ + IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + *(res) = _safe_prod; \ + } while (0) + +#else + +#define IGRAPH_SAFE_ADD(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_sum; \ + if (((_safe_b > 0) && (_safe_a > (IGRAPH_INTEGER_MAX - _safe_b))) || \ + ((_safe_b < 0) && (_safe_a < (IGRAPH_INTEGER_MIN - _safe_b)))) { \ + IGRAPH_ERRORF("Overflow when adding %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + _safe_sum = _safe_a+_safe_b; \ + *(res) = _safe_sum; \ + } while (0) + +#define IGRAPH_SAFE_MULT(a, b, res) \ + do { \ + igraph_integer_t _safe_a = (a), _safe_b = (b); \ + igraph_integer_t _safe_prod; \ + int err=0; \ + if (_safe_a > 0) { /* _safe_a is positive */ \ + if (_safe_b > 0) { /* _safe_a and _safe_b are positive */ \ + if (_safe_a > (IGRAPH_INTEGER_MAX / _safe_b)) { \ + err=1; \ + } \ + } else { /* _safe_a positive, _safe_b nonpositive */ \ + if (_safe_b < (IGRAPH_INTEGER_MIN / _safe_a)) { \ + err=1; \ + } \ + } /* _safe_a positive, _safe_b nonpositive */ \ + } else { /* _safe_a is nonpositive */ \ + if (_safe_b > 0) { /* _safe_a is nonpositive, _safe_b is positive */ \ + if (_safe_a < (IGRAPH_INTEGER_MIN / _safe_b)) { \ + err=1; \ + } \ + } else { /* _safe_a and _safe_b are nonpositive */ \ + if ( (_safe_a != 0) && (_safe_b < (IGRAPH_INTEGER_MAX / _safe_a))) { \ + err=1; \ + } \ + } /* End if _safe_a and _safe_b are nonpositive */ \ + } /* End if _safe_a is nonpositive */ \ + if (err) { \ + IGRAPH_ERRORF("Overflow when multiplying %" IGRAPH_PRId " and %" IGRAPH_PRId ".", IGRAPH_EOVERFLOW, _safe_a, _safe_b); \ + } \ + _safe_prod = _safe_a*_safe_b; \ + *(res) = _safe_prod; \ + } while (0) + +#endif /* HAVE_BUILTIN_OVERFLOW */ + +/* Overflow-safe calculation of "n choose 2" = n*(n-1) / 2, assuming that n >= 0. */ +#define IGRAPH_SAFE_N_CHOOSE_2(n, res) \ + do { \ + igraph_integer_t _safe_n = (n); \ + if (_safe_n % 2 == 0) IGRAPH_SAFE_MULT(_safe_n / 2, _safe_n - 1, res); \ + else IGRAPH_SAFE_MULT(_safe_n, (_safe_n - 1) / 2, res); \ + } while (0) + +igraph_error_t igraph_i_safe_ceil(igraph_real_t value, igraph_integer_t* result); +igraph_error_t igraph_i_safe_floor(igraph_real_t value, igraph_integer_t* result); +igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result); + +igraph_error_t igraph_i_safe_next_pow_2(igraph_integer_t k, igraph_integer_t *res); +igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_add(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph_integer_t *res); +igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res); +igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res); + +__END_DECLS + +#endif /* IGRAPH_MATH_SAFE_INTOP_H */ diff --git a/src/vendor/cigraph/src/math/utils.c b/src/vendor/cigraph/src/math/utils.c index abf175c8326..7c0be5baf4f 100644 --- a/src/vendor/cigraph/src/math/utils.c +++ b/src/vendor/cigraph/src/math/utils.c @@ -21,222 +21,17 @@ */ +#include "igraph_complex.h" +#include "igraph_nongraph.h" #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 IGRAPH_THREAD_LOCAL int nlnrel = 0; - static IGRAPH_THREAD_LOCAL 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); } @@ -255,22 +50,22 @@ int igraph_is_neginf(double x) { /** * \function igraph_almost_equals - * Compare two double-precision floats with a tolerance + * \brief 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 + * \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 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 + * \return True if the two floats are nearly equal to each other within + * the given level of tolerance, false otherwise. */ -int igraph_almost_equals(double a, double b, double eps) { - return igraph_cmp_epsilon(a, b, eps) == 0 ? 1 : 0; +igraph_bool_t igraph_almost_equals(double a, double b, double eps) { + return igraph_cmp_epsilon(a, b, eps) == 0; } /* Use value-safe floating point math for igraph_cmp_epsilon() with @@ -291,20 +86,34 @@ int igraph_almost_equals(double a, double b, double eps) { /** * \function igraph_cmp_epsilon - * Compare two double-precision floats with a tolerance + * \brief 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 + * + * The function supports infinities and NaN values. NaN values are considered + * not equal to any other value (even another NaN), but the ordering is + * arbitrary; in other words, we only guarantee that comparing a NaN with + * any other value will not return zero. Positive infinity is considered to + * be greater than any finite value with any tolerance. Negative infinity is + * considered to be smaller than any finite value with any tolerance. + * Positive infinity is considered to be equal to another positive infinity + * with any tolerance. Negative infinity is considered to be equal to another + * negative infinity with any tolerance. + * + * \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 abs(a-b) / (abs(a) + abs(b)). The * two numbers are considered equal if this is less than \c eps. + * Negative epsilon values are not allowed; the returned value will + * be undefined in this case. Zero means to do an exact comparison + * without tolerance. * - * \return zero if the two floats are nearly equal to each other within + * \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 + * larger, negative number if the second float is larger. */ int igraph_cmp_epsilon(double a, double b, double eps) { double diff; @@ -334,6 +143,40 @@ int igraph_cmp_epsilon(double a, double b, double eps) { } } +/** + * \function igraph_complex_almost_equals + * \brief Compare two complex numbers with a tolerance. + * + * Determines whether two complex numbers are "almost equal" + * to each other with a given level of tolerance on the relative error. + * + * \param a The first complex number. + * \param b The second complex number. + * \param eps The level of tolerance on the relative error. The relative + * error is defined as abs(a-b) / (abs(a) + abs(b)). The + * two numbers are considered equal if this is less than \c eps. + * + * \return True if the two complex numbers are nearly equal to each other within + * the given level of tolerance, false otherwise. + */ +igraph_bool_t igraph_complex_almost_equals(igraph_complex_t a, + igraph_complex_t b, + igraph_real_t eps) { + + igraph_real_t a_abs = igraph_complex_abs(a); + igraph_real_t b_abs = igraph_complex_abs(b); + igraph_real_t sum = a_abs + b_abs; + igraph_real_t abs_diff = igraph_complex_abs(igraph_complex_sub(a, b)); + + if (a_abs == 0 || b_abs == 0 || sum < DBL_MIN) { + return abs_diff < eps * DBL_MIN; + } else if (! isfinite(sum)) { + return abs_diff < (eps * a_abs + eps * b_abs); + } else { + return abs_diff/ sum < eps; + } +} + #ifdef __INTEL_COMPILER #pragma float_control(pop) #endif diff --git a/src/vendor/cigraph/src/misc/bipartite.c b/src/vendor/cigraph/src/misc/bipartite.c index e26ceb489e0..7c0a3f8a538 100644 --- a/src/vendor/cigraph/src/misc/bipartite.c +++ b/src/vendor/cigraph/src/misc/bipartite.c @@ -31,6 +31,7 @@ #include "igraph_nongraph.h" #include "graph/attributes.h" +#include "math/safe_intop.h" /** * \section about_bipartite Bipartite networks in igraph @@ -67,13 +68,17 @@ * \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. + * vertices in the first projection is stored here. May be \c NULL + * if not needed. * \param ecount1 Pointer to an \c igraph_integer_t, the number of - * edges in the first projection is stored here. + * edges in the first projection is stored here. May be \c NULL + * if not needed. * \param vcount2 Pointer to an \c igraph_integer_t, the number of - * vertices in the second projection is stored here. + * vertices in the second projection is stored here. May be \c NULL + * if not needed. * \param ecount2 Pointer to an \c igraph_integer_t, the number of - * edges in the second projection is stored here. + * edges in the second projection is stored here. May be \c NULL + * if not needed. * \return Error code. * * \sa \ref igraph_bipartite_projection() to calculate the actual @@ -82,33 +87,30 @@ * 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, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vc1 = 0, ec1 = 0, vc2 = 0, ec2 = 0; igraph_adjlist_t adjlist; - igraph_vector_long_t added; - long int i; + igraph_vector_int_t added; + igraph_integer_t i; - IGRAPH_CHECK(igraph_vector_long_init(&added, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_long_destroy, &added); + IGRAPH_VECTOR_INT_INIT_FINALLY(&added, no_of_nodes); 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; + igraph_integer_t neilen1, j; + igraph_integer_t *ecptr; if (VECTOR(*types)[i]) { vc2++; ecptr = &ec2; @@ -119,7 +121,7 @@ int igraph_bipartite_projection_size(const igraph_t *graph, 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_integer_t k, neilen2, nei = 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", @@ -127,7 +129,7 @@ int igraph_bipartite_projection_size(const igraph_t *graph, } neilen2 = igraph_vector_int_size(neis2); for (k = 0; k < neilen2; k++) { - long int nei2 = (long int) VECTOR(*neis2)[k]; + igraph_integer_t nei2 = VECTOR(*neis2)[k]; if (nei2 <= i) { continue; } @@ -140,45 +142,57 @@ int igraph_bipartite_projection_size(const igraph_t *graph, } } - *vcount1 = (igraph_integer_t) vc1; - *ecount1 = (igraph_integer_t) ec1; - *vcount2 = (igraph_integer_t) vc2; - *ecount2 = (igraph_integer_t) ec2; + if (vcount1) { + *vcount1 = vc1; + } + + if (ecount1) { + *ecount1 = ec1; + } + + if (vcount2) { + *vcount2 = vc2; + } + + if (ecount2) { + *ecount2 = ec2; + } igraph_adjlist_destroy(&adjlist); - igraph_vector_long_destroy(&added); + igraph_vector_int_destroy(&added); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_bipartite_projection(const igraph_t *graph, +static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, const igraph_vector_bool_t *types, igraph_t *proj, int which, - igraph_vector_t *multiplicity) { + igraph_vector_int_t *multiplicity) { - long int no_of_nodes = igraph_vcount(graph); - long int i, j, k; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, k; igraph_integer_t remaining_nodes = 0; - igraph_vector_t vertex_perm, vertex_index; - igraph_vector_t edges; + igraph_vector_int_t vertex_perm, vertex_index; + igraph_vector_int_t edges; igraph_adjlist_t adjlist; igraph_vector_int_t *neis1, *neis2; - long int neilen1, neilen2; - igraph_vector_long_t added; + igraph_integer_t neilen1, neilen2; + igraph_vector_int_t added; igraph_vector_t mult; if (which < 0) { - return 0; + return IGRAPH_SUCCESS; } - 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_VECTOR_INT_INIT_FINALLY(&vertex_perm, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&vertex_perm, no_of_nodes)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_index, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); @@ -186,24 +200,24 @@ static int igraph_i_bipartite_projection(const igraph_t *graph, * 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); + igraph_vector_int_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); + VECTOR(vertex_index)[i] = remaining_nodes++; + igraph_vector_int_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; + igraph_integer_t new_i = VECTOR(vertex_index)[i]; + igraph_integer_t 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]; + igraph_integer_t nei = VECTOR(*neis1)[j]; if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { IGRAPH_ERROR("Non-bipartite edge found in bipartite projection", IGRAPH_EINVAL); @@ -211,7 +225,7 @@ static int igraph_i_bipartite_projection(const igraph_t *graph, 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; + igraph_integer_t nei2 = VECTOR(*neis2)[k], new_nei2; if (nei2 <= i) { continue; } @@ -227,28 +241,28 @@ static int igraph_i_bipartite_projection(const igraph_t *graph, } iedges++; - IGRAPH_CHECK(igraph_vector_push_back(&edges, new_i)); + IGRAPH_CHECK(igraph_vector_int_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)); + old vertex IDs here and rewrite it later */ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei2)); } else { - new_nei2 = (long int) VECTOR(vertex_index)[nei2] - 1; - IGRAPH_CHECK(igraph_vector_push_back(&edges, new_nei2)); + new_nei2 = VECTOR(vertex_index)[nei2]; + IGRAPH_CHECK(igraph_vector_int_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; + igraph_integer_t now = igraph_vector_int_size(&edges); + igraph_integer_t 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]; + igraph_integer_t nei2 = VECTOR(edges)[j + 1]; + igraph_integer_t new_nei2 = VECTOR(vertex_index)[nei2]; + igraph_integer_t m = VECTOR(mult)[nei2]; VECTOR(edges)[j + 1] = new_nei2; - IGRAPH_CHECK(igraph_vector_push_back(multiplicity, m)); + IGRAPH_CHECK(igraph_vector_int_push_back(multiplicity, m)); } } } /* if VECTOR(*type)[i] == which */ @@ -256,23 +270,23 @@ static int igraph_i_bipartite_projection(const igraph_t *graph, igraph_vector_destroy(&mult); igraph_adjlist_destroy(&adjlist); - igraph_vector_long_destroy(&added); - igraph_vector_destroy(&vertex_index); + igraph_vector_int_destroy(&added); + igraph_vector_int_destroy(&vertex_index); IGRAPH_FINALLY_CLEAN(4); IGRAPH_CHECK(igraph_create(proj, &edges, remaining_nodes, /*directed=*/ 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_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_vector_int_destroy(&vertex_perm); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** @@ -314,19 +328,17 @@ static int igraph_i_bipartite_projection(const igraph_t *graph, * 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, +igraph_error_t 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_vector_int_t *multiplicity1, + igraph_vector_int_t *multiplicity2, igraph_integer_t probe1) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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 */ @@ -345,7 +357,7 @@ int igraph_bipartite_projection(const igraph_t *graph, } if (probe1 >= 0) { - t1 = VECTOR(*types)[(long int)probe1]; + t1 = VECTOR(*types)[probe1]; if (proj2) { t2 = 1 - t1; } else { @@ -361,7 +373,7 @@ int igraph_bipartite_projection(const igraph_t *graph, IGRAPH_CHECK(igraph_i_bipartite_projection(graph, types, proj2, t2, multiplicity2)); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } @@ -407,7 +419,7 @@ int igraph_bipartite_projection(const igraph_t *graph, * \sa \ref igraph_full() for non-bipartite full graphs. */ -int igraph_full_bipartite(igraph_t *graph, +igraph_error_t igraph_full_bipartite(igraph_t *graph, igraph_vector_bool_t *types, igraph_integer_t n1, igraph_integer_t n2, igraph_bool_t directed, @@ -415,10 +427,10 @@ int igraph_full_bipartite(igraph_t *graph, 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; + igraph_vector_int_t edges; + igraph_integer_t no_of_edges; + igraph_integer_t ptr = 0; + igraph_integer_t i, j; if (!directed) { no_of_edges = nn1 * nn2; @@ -428,7 +440,7 @@ int igraph_full_bipartite(igraph_t *graph, no_of_edges = nn1 * nn2 * 2; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); if (!directed || mode == IGRAPH_OUT) { @@ -461,7 +473,7 @@ int igraph_full_bipartite(igraph_t *graph, } IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, graph); @@ -475,7 +487,7 @@ int igraph_full_bipartite(igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -493,7 +505,7 @@ int igraph_full_bipartite(igraph_t *graph, * \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 + * 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. @@ -505,34 +517,29 @@ int igraph_full_bipartite(igraph_t *graph, * \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_error_t igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, + const igraph_vector_int_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; + igraph_integer_t no_of_nodes = igraph_vector_bool_size(types); + igraph_integer_t no_of_edges = igraph_vector_int_size(edges); + igraph_integer_t 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); + if (! igraph_vector_int_isininterval(edges, 0, no_of_nodes-1)) { + 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]; + igraph_integer_t from = VECTOR(*edges)[i]; + igraph_integer_t to = VECTOR(*edges)[i + 1]; + igraph_bool_t t1 = VECTOR(*types)[from]; + igraph_bool_t t2 = VECTOR(*types)[to]; if ( (t1 && t2) || (!t1 && !t2) ) { IGRAPH_ERROR("Invalid edges, not a bipartite graph", IGRAPH_EINVAL); } @@ -543,37 +550,53 @@ int igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, IGRAPH_CHECK(igraph_add_edges(graph, edges, 0)); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_incidence - * \brief Creates a bipartite graph from an incidence matrix. + * \brief Creates a bipartite graph from a bipartite adjacency matrix (deprecated alias). + * + * \deprecated-by igraph_biadjacency 0.10.5 + */ + +igraph_error_t 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 +) { + return igraph_biadjacency(graph, types, incidence, directed, mode, multiple); +} + +/** + * \function igraph_biadjacency + * \brief Creates a bipartite graph from a bipartite adjacency 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 always connect vertices of different types. A bipartite adjacency + * matrix is an \em n x \em m matrix, \em n and \em 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 + * \p multiple argument. If it is \c false, then a single edge is + * created for every non-zero element in the bipartite adjacency matrix. If \p + * multiple is \c true, 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. + * \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 + * \param input The bipartite adjacency matrix that serves as an input + * to this function. + * \param directed Specifies 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 @@ -581,32 +604,39 @@ int igraph_create_bipartite(igraph_t *graph, const igraph_vector_bool_t *types, * 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. + * \param multiple How to interpret the matrix elements. See details below. * \return Error code. * - * Time complexity: O(n*m), the size of the incidence matrix. + * Time complexity: O(n*m), the size of the bipartite adjacency 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_error_t igraph_biadjacency( + igraph_t *graph, igraph_vector_bool_t *types, + const igraph_matrix_t *input, 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 n1 = igraph_matrix_nrow(input); + igraph_integer_t n2 = igraph_matrix_ncol(input); igraph_integer_t no_of_nodes = n1 + n2; - igraph_vector_t edges; - long int i, j, k; + igraph_vector_int_t edges; + igraph_integer_t i, j, k; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + + if (n1 > 0 && n2 > 0 && igraph_matrix_min(input) < 0) { + IGRAPH_ERRORF( + "Bipartite adjacencey matrix elements should be non-negative, found %g.", + IGRAPH_EINVAL, igraph_matrix_min(input) + ); + } 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; + igraph_integer_t elem = ceil(MATRIX(*input, i, j)); + igraph_integer_t from, to; if (!elem) { continue; @@ -622,15 +652,15 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); } } } @@ -640,9 +670,9 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, for (i = 0; i < n1; i++) { for (j = 0; j < n2; j++) { - long int from, to; + igraph_integer_t from, to; - if (MATRIX(*incidence, i, j) != 0) { + if (MATRIX(*input, i, j) != 0) { if (mode == IGRAPH_IN) { from = n1 + j; to = i; @@ -651,13 +681,13 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, to = n1 + j; } if (mode != IGRAPH_ALL || !directed) { - IGRAPH_CHECK(igraph_vector_push_back(&edges, from)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_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_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); } } } @@ -666,7 +696,7 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, } IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, graph); @@ -679,12 +709,27 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, } IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_get_incidence - * \brief Convert a bipartite graph into an incidence matrix. + * \brief Convert a bipartite graph into a bipartite adjacency matrix (deprecated alias). + * + * \deprecated-by igraph_get_biadjacency 0.10.5 + */ + +igraph_error_t igraph_get_incidence(const igraph_t *graph, + const igraph_vector_bool_t *types, + igraph_matrix_t *res, + igraph_vector_int_t *row_ids, + igraph_vector_int_t *col_ids) { + return igraph_get_biadjacency(graph, types, res, row_ids, col_ids); +} + +/** + * \function igraph_get_biadjacency + * \brief Convert a bipartite graph into a bipartite adjacency matrix. * * \param graph The input graph, edge directions are ignored. * \param types Boolean vector containing the vertex types. All vertices @@ -695,44 +740,44 @@ int igraph_incidence(igraph_t *graph, igraph_vector_bool_t *types, * 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 + * 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 + * 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. + * \sa \ref igraph_biadjacency() 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) { +igraph_error_t igraph_get_biadjacency( + const igraph_t *graph, const igraph_vector_bool_t *types, + igraph_matrix_t *res, igraph_vector_int_t *row_ids, + igraph_vector_int_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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t n1 = 0, n2 = 0, i; + igraph_vector_int_t perm; + igraph_integer_t p1, p2; + igraph_integer_t 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_ERRORF("Vertex type vector size (%" IGRAPH_PRId ") not equal to number of vertices (%" IGRAPH_PRId ").", 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; + n1 += VECTOR(*types)[i] == false ? 1 : 0; } n2 = no_of_nodes - n1; - IGRAPH_VECTOR_INIT_FINALLY(&perm, no_of_nodes); + IGRAPH_VECTOR_INT_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++; @@ -741,10 +786,10 @@ int igraph_get_incidence(const igraph_t *graph, 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]; + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_integer_t from2 = VECTOR(perm)[from]; + igraph_integer_t to2 = VECTOR(perm)[to]; if (VECTOR(*types)[from] == VECTOR(*types)[to]) { ignored_edges++; } else if (! VECTOR(*types)[from]) { @@ -754,33 +799,34 @@ int igraph_get_incidence(const igraph_t *graph, } } if (ignored_edges) { - IGRAPH_WARNINGF("%ld edges running within partitions were ignored.", ignored_edges); + IGRAPH_WARNINGF("%" IGRAPH_PRId " edges running within partitions were ignored.", ignored_edges); } if (row_ids) { - IGRAPH_CHECK(igraph_vector_resize(row_ids, n1)); + IGRAPH_CHECK(igraph_vector_int_resize(row_ids, n1)); } if (col_ids) { - IGRAPH_CHECK(igraph_vector_resize(col_ids, n2)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t i2 = VECTOR(perm)[i]; VECTOR(*row_ids)[i2] = i; } } else { if (col_ids) { - long int i2 = (long int) VECTOR(perm)[i]; + igraph_integer_t i2 = VECTOR(perm)[i]; VECTOR(*col_ids)[i2 - n1] = i; } } } } - igraph_vector_destroy(&perm); + igraph_vector_int_destroy(&perm); IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } @@ -814,7 +860,7 @@ int igraph_get_incidence(const igraph_t *graph, * edges. */ -int igraph_is_bipartite(const igraph_t *graph, +igraph_error_t igraph_is_bipartite(const igraph_t *graph, igraph_bool_t *res, igraph_vector_bool_t *types) { @@ -826,52 +872,50 @@ int igraph_is_bipartite(const igraph_t *graph, 2 means type 2. */ - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_dqueue_int_t Q; + igraph_vector_int_t neis; + igraph_bool_t bi = true; - 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); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&seen, no_of_nodes); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - for (i = 0; bi && i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; bi && i < no_of_nodes; i++) { if (VECTOR(seen)[i]) { continue; } - IGRAPH_CHECK(igraph_dqueue_push(&Q, i)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (bi && !igraph_dqueue_int_empty(&Q)) { + igraph_integer_t n, j; + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); char acttype = VECTOR(seen)[actnode]; IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = VECTOR(neis)[j]; if (VECTOR(seen)[nei]) { - long int neitype = VECTOR(seen)[nei]; + igraph_integer_t neitype = VECTOR(seen)[nei]; if (neitype == acttype) { - bi = 0; + bi = false; break; } } else { VECTOR(seen)[nei] = 3 - acttype; - IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); } } } } - igraph_vector_destroy(&neis); - igraph_dqueue_destroy(&Q); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&Q); IGRAPH_FINALLY_CLEAN(2); if (res) { @@ -880,7 +924,7 @@ int igraph_is_bipartite(const igraph_t *graph, if (types && bi) { IGRAPH_CHECK(igraph_vector_bool_resize(types, no_of_nodes)); - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { VECTOR(*types)[i] = VECTOR(seen)[i] - 1; } } @@ -888,17 +932,16 @@ int igraph_is_bipartite(const igraph_t *graph, igraph_vector_char_destroy(&seen); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, +igraph_error_t 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; + igraph_vector_int_t edges, s; + igraph_integer_t i; if (p < 0.0 || p > 1.0) { IGRAPH_ERROR("Invalid connection probability", IGRAPH_EINVAL); @@ -913,92 +956,99 @@ int igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t *types, } if (p == 0 || n1 * n2 < 1) { - IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); + IGRAPH_CHECK(igraph_empty(graph, n1 + n2, directed)); } else if (p == 1.0) { - IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, directed, + IGRAPH_CHECK(igraph_full_bipartite(graph, types, n1, n2, directed, mode)); } else { - long int to, from, slen; - double maxedges, last; + igraph_integer_t to, from, slen; + igraph_real_t n1_real = n1; /* for divisions below */ + igraph_real_t n2_real = n2; /* for divisions below */ + igraph_real_t maxedges, last; + igraph_integer_t maxedges_int; + if (!directed || mode != IGRAPH_ALL) { - maxedges = (double) n1 * (double) n2; + maxedges = n1_real * n2_real; } else { - maxedges = 2.0 * (double) n1 * (double) n2; + maxedges = 2.0 * n1_real * n2_real; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&s, 0); - IGRAPH_CHECK(igraph_vector_reserve(&s, (long) (maxedges * p * 1.1))); + if (maxedges > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&s, 0); + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &maxedges_int)); + IGRAPH_CHECK(igraph_vector_int_reserve(&s, maxedges_int)); RNG_BEGIN(); last = RNG_GEOM(p); while (last < maxedges) { - IGRAPH_CHECK(igraph_vector_push_back(&s, last)); + IGRAPH_CHECK(igraph_vector_int_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)); + slen = igraph_vector_int_size(&s); + IGRAPH_CHECK(igraph_vector_int_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 = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; to += n1; } else { - long int n1n2 = n1 * n2; + igraph_integer_t 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 = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; to += n1; } else { - to = (long) floor( (VECTOR(s)[i] - n1n2) / n2); - from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2); + to = floor((VECTOR(s)[i] - n1n2) / n2_real); + from = VECTOR(s)[i] - n1n2 - to * n2_real; from += n1; } } if (mode != IGRAPH_IN) { - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); } else { - igraph_vector_push_back(&edges, to); - igraph_vector_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); } } - igraph_vector_destroy(&s); + igraph_vector_int_destroy(&s); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } - return retval; + return IGRAPH_SUCCESS; } -int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, +igraph_error_t 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; + igraph_vector_int_t edges; + igraph_vector_int_t s; if (n1 < 0 || n2 < 0) { - IGRAPH_ERROR("Invalid number of vertices", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } if (m < 0) { - IGRAPH_ERROR("Invalid number of edges", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid number of edges.", IGRAPH_EINVAL); } if (types) { - long int i; + igraph_integer_t i; IGRAPH_CHECK(igraph_vector_bool_resize(types, n1 + n2)); igraph_vector_bool_null(types); for (i = n1; i < n1 + n2; i++) { @@ -1008,71 +1058,73 @@ int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, if (m == 0 || n1 * n2 == 0) { if (m > 0) { - IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); } - IGRAPH_CHECK(retval = igraph_empty(graph, n1 + n2, directed)); + IGRAPH_CHECK(igraph_empty(graph, n1 + n2, directed)); } else { + igraph_integer_t i; + igraph_real_t maxedges; - - long int i; - double maxedges; if (!directed || mode != IGRAPH_ALL) { - maxedges = (double) n1 * (double) n2; + maxedges = (igraph_real_t) n1 * (igraph_real_t) n2; } else { - maxedges = 2.0 * (double) n1 * (double) n2; + maxedges = 2.0 * (igraph_real_t) n1 * (igraph_real_t) n2; } if (m > maxedges) { - IGRAPH_ERROR("Invalid number (too large) of edges", IGRAPH_EINVAL); + IGRAPH_ERROR("Too many edges requested compared to the number of vertices.", IGRAPH_EINVAL); } if (maxedges == m) { - IGRAPH_CHECK(retval = igraph_full_bipartite(graph, types, n1, n2, + IGRAPH_CHECK(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_integer_t to, from; + igraph_real_t n1_real = (igraph_real_t) n1; /* for divisions below */ + igraph_real_t n2_real = (igraph_real_t) n2; /* for divisions below */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_int_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 = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; to += n1; } else { - long int n1n2 = n1 * n2; + igraph_integer_t 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 = floor(VECTOR(s)[i] / n1_real); + from = VECTOR(s)[i] - to * n1_real; to += n1; } else { - to = (long) floor( (VECTOR(s)[i] - n1n2) / n2); - from = (long) (VECTOR(s)[i] - n1n2 - ((igraph_real_t) to) * n2); + to = floor((VECTOR(s)[i] - n1n2) / n2_real); + from = VECTOR(s)[i] - n1n2 - to * n2_real; from += n1; } } if (mode != IGRAPH_IN) { - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); } else { - igraph_vector_push_back(&edges, to); - igraph_vector_push_back(&edges, from); + igraph_vector_int_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); } } - igraph_vector_destroy(&s); + igraph_vector_int_destroy(&s); IGRAPH_FINALLY_CLEAN(1); - IGRAPH_CHECK(retval = igraph_create(graph, &edges, n1 + n2, directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); } } - return retval; + return IGRAPH_SUCCESS; } /** @@ -1082,7 +1134,7 @@ int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, * 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 + * bottom vertices is realized with probability p, independently of the * rest of the edges. In G(n,m), we uniformly choose m edges to * realize. * @@ -1105,7 +1157,7 @@ int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, * probability p. * \endclist * \param n1 The number of bottom vertices. - * \param n2 The number of top verices. + * \param n2 The number of top vertices. * \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 @@ -1128,7 +1180,7 @@ int igraph_bipartite_game_gnm(igraph_t *graph, igraph_vector_bool_t *types, * edges. */ -int igraph_bipartite_game(igraph_t *graph, igraph_vector_bool_t *types, +igraph_error_t 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, diff --git a/src/vendor/cigraph/src/misc/chordality.c b/src/vendor/cigraph/src/misc/chordality.c index fe57ff12eaf..7cbcffc2105 100644 --- a/src/vendor/cigraph/src/misc/chordality.c +++ b/src/vendor/cigraph/src/misc/chordality.c @@ -18,7 +18,7 @@ */ #include "igraph_structural.h" -#include "igraph_error.h" + #include "igraph_adjlist.h" #include "igraph_interface.h" @@ -65,45 +65,41 @@ * \sa \ref igraph_is_chordal(). */ -int igraph_maximum_cardinality_search(const igraph_t *graph, - igraph_vector_t *alpha, - igraph_vector_t *alpham1) { +igraph_error_t igraph_maximum_cardinality_search(const igraph_t *graph, + igraph_vector_int_t *alpha, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t size; + igraph_vector_int_t head, next, prev; /* doubly linked list with head */ + igraph_integer_t i; igraph_adjlist_t adjlist; /***************/ /* local j, v; */ /***************/ - long int j, v; + igraph_integer_t j, v; if (no_of_nodes == 0) { - igraph_vector_clear(alpha); + igraph_vector_int_clear(alpha); if (alpham1) { - igraph_vector_clear(alpham1); + igraph_vector_int_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_VECTOR_INT_INIT_FINALLY(&size, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&head, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&next, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&prev, no_of_nodes); 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)); + IGRAPH_CHECK(igraph_vector_int_resize(alpha, no_of_nodes)); if (alpham1) { - IGRAPH_CHECK(igraph_vector_resize(alpham1, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(alpham1, no_of_nodes)); } /***********************************************/ @@ -135,7 +131,7 @@ int igraph_maximum_cardinality_search(const igraph_t *graph, /**************/ while (i >= 1) { - long int x, k, len; + igraph_integer_t x, k, len; igraph_vector_int_t *neis; /********************************/ @@ -166,16 +162,16 @@ int igraph_maximum_cardinality_search(const igraph_t *graph, 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]; + igraph_integer_t w = VECTOR(*neis)[k]; + igraph_integer_t 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]; + igraph_integer_t nw = VECTOR(next)[w]; + igraph_integer_t pw = VECTOR(prev)[w]; if (nw != 0) { VECTOR(prev)[nw - 1] = pw; } @@ -226,10 +222,10 @@ int igraph_maximum_cardinality_search(const igraph_t *graph, } 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_vector_int_destroy(&prev); + igraph_vector_int_destroy(&next); + igraph_vector_int_destroy(&head); + igraph_vector_int_destroy(&size); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; @@ -276,37 +272,37 @@ int igraph_maximum_cardinality_search(const igraph_t *graph, * \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_error_t igraph_is_chordal(const igraph_t *graph, + const igraph_vector_int_t *alpha, + const igraph_vector_int_t *alpham1, igraph_bool_t *chordal, - igraph_vector_t *fill_in, + igraph_vector_int_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; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_vector_int_t *my_alpha = alpha, *my_alpham1 = alpham1; + igraph_vector_int_t v_alpha, v_alpham1; + igraph_vector_int_t f, index; + igraph_integer_t i; igraph_adjlist_t adjlist; - igraph_vector_long_t mark; + igraph_vector_int_t mark; igraph_bool_t calc_edges = fill_in || newgraph; - igraph_vector_t *my_fill_in = fill_in, v_fill_in; + igraph_vector_int_t *my_fill_in = fill_in, v_fill_in; /*****************/ /* local v, w, x */ /*****************/ - long int v, w, x; + igraph_integer_t 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 (alpha && (igraph_vector_int_size(alpha) != no_of_nodes)) { + IGRAPH_ERRORF("Alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_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 (alpham1 && (igraph_vector_int_size(alpham1) != no_of_nodes)) { + IGRAPH_ERRORF("Inverse alpha vector size (%" IGRAPH_PRId ") not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(alpham1), no_of_nodes); } if (!chordal && !calc_edges) { @@ -315,50 +311,47 @@ int igraph_is_chordal(const igraph_t *graph, } if (!alpha && !alpham1) { - IGRAPH_VECTOR_INIT_FINALLY(&v_alpha, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); my_alpha = &v_alpha; - IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); + IGRAPH_VECTOR_INT_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)); + (igraph_vector_int_t*) my_alpha, + (igraph_vector_int_t*) my_alpham1)); } else if (alpha && !alpham1) { - long int v; - IGRAPH_VECTOR_INIT_FINALLY(&v_alpham1, no_of_nodes); + igraph_integer_t v; + IGRAPH_VECTOR_INT_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]; + igraph_integer_t i = 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); + igraph_integer_t i; + IGRAPH_VECTOR_INT_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]; + igraph_integer_t v = VECTOR(*my_alpham1)[i]; VECTOR(*my_alpha)[v] = i; } } if (!fill_in && newgraph) { - IGRAPH_VECTOR_INIT_FINALLY(&v_fill_in, 0); + IGRAPH_VECTOR_INT_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_VECTOR_INT_INIT_FINALLY(&f, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, no_of_nodes); if (my_fill_in) { - igraph_vector_clear(my_fill_in); + igraph_vector_int_clear(my_fill_in); } if (chordal) { - *chordal = 1; + *chordal = true; } /*********************/ @@ -367,13 +360,13 @@ int igraph_is_chordal(const igraph_t *graph, for (i = 0; i < no_of_nodes; i++) { igraph_vector_int_t *neis; - long int j, len; + igraph_integer_t j, len; /**********************************************/ /* w := alpham1(i); f(w) := w; index(w) := i; */ /**********************************************/ - w = (long int) VECTOR(*my_alpham1)[i]; + w = VECTOR(*my_alpham1)[i]; VECTOR(f)[w] = w; VECTOR(index)[w] = i; @@ -384,12 +377,12 @@ int igraph_is_chordal(const igraph_t *graph, neis = igraph_adjlist_get(&adjlist, w); len = igraph_vector_int_size(neis); for (j = 0; j < len; j++) { - v = (long int) VECTOR(*neis)[j]; + v = VECTOR(*neis)[j]; VECTOR(mark)[v] = w + 1; } for (j = 0; j < len; j++) { - v = (long int) VECTOR(*neis)[j]; + v = VECTOR(*neis)[j]; if (VECTOR(*my_alpha)[v] >= i) { continue; } @@ -419,12 +412,12 @@ int igraph_is_chordal(const igraph_t *graph, if (VECTOR(mark)[x] != w + 1) { if (chordal) { - *chordal = 0; + *chordal = false; } if (my_fill_in) { - IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, x)); - IGRAPH_CHECK(igraph_vector_push_back(my_fill_in, w)); + IGRAPH_CHECK(igraph_vector_int_push_back(my_fill_in, x)); + IGRAPH_CHECK(igraph_vector_int_push_back(my_fill_in, w)); } if (!calc_edges) { @@ -453,10 +446,10 @@ int igraph_is_chordal(const igraph_t *graph, } } - igraph_vector_long_destroy(&mark); + igraph_vector_int_destroy(&mark); igraph_adjlist_destroy(&adjlist); - igraph_vector_long_destroy(&index); - igraph_vector_long_destroy(&f); + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&f); IGRAPH_FINALLY_CLEAN(4); if (newgraph) { @@ -467,19 +460,19 @@ int igraph_is_chordal(const igraph_t *graph, } if (!fill_in && newgraph) { - igraph_vector_destroy(&v_fill_in); + igraph_vector_int_destroy(&v_fill_in); IGRAPH_FINALLY_CLEAN(1); } if (!alpha && !alpham1) { - igraph_vector_destroy(&v_alpham1); - igraph_vector_destroy(&v_alpha); + igraph_vector_int_destroy(&v_alpham1); + igraph_vector_int_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(2); } else if (alpha && !alpham1) { - igraph_vector_destroy(&v_alpham1); + igraph_vector_int_destroy(&v_alpham1); IGRAPH_FINALLY_CLEAN(1); } else if (!alpha && alpham1) { - igraph_vector_destroy(&v_alpha); + igraph_vector_int_destroy(&v_alpha); IGRAPH_FINALLY_CLEAN(1); } diff --git a/src/vendor/cigraph/src/misc/cocitation.c b/src/vendor/cigraph/src/misc/cocitation.c index 6f9c787c798..c1210ec42e7 100644 --- a/src/vendor/cigraph/src/misc/cocitation.c +++ b/src/vendor/cigraph/src/misc/cocitation.c @@ -23,6 +23,7 @@ */ #include "igraph_cocitation.h" + #include "igraph_memory.h" #include "igraph_adjlist.h" #include "igraph_interface.h" @@ -31,7 +32,7 @@ #include -int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, +static igraph_error_t igraph_i_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, igraph_vs_t vids, igraph_neimode_t mode, igraph_vector_t *weights); @@ -40,21 +41,21 @@ int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, * \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 + * 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 + * \param vids The vertex IDs of the vertices for which the * calculation will be done. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVVID: invalid vertex ID. * * Time complexity: O(|V|d^2), |V| is * the number of vertices in the graph, @@ -66,9 +67,9 @@ int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, * \example examples/simple/igraph_cocitation.c */ -int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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); + return igraph_i_cocitation_real(graph, res, vids, IGRAPH_OUT, NULL); } /** @@ -76,21 +77,21 @@ int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, * \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 + * 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 + * \param vids The vertex IDs of the vertices for which the * calculation will be done. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVVID: invalid vertex ID. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in @@ -102,9 +103,9 @@ int igraph_cocitation(const igraph_t *graph, igraph_matrix_t *res, * \example examples/simple/igraph_cocitation.c */ -int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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); + return igraph_i_cocitation_real(graph, res, vids, IGRAPH_IN, NULL); } /** @@ -112,7 +113,6 @@ int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * \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 @@ -126,13 +126,14 @@ int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * * 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. + * https://doi.org/10.1016/S0378-8733(03)00009-1 * * \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 + * 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 + * \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: @@ -149,7 +150,7 @@ int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * degree. * \endclist * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \c IGRAPH_EINVVID: invalid vertex ID. * * Time complexity: O(|V|d^2), * |V| is the number of vertices in @@ -159,11 +160,12 @@ int igraph_bibcoupling(const igraph_t *graph, igraph_matrix_t *res, * \example examples/simple/igraph_similarity.c */ -int igraph_similarity_inverse_log_weighted(const igraph_t *graph, +igraph_error_t 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_vector_int_t degrees; igraph_neimode_t mode0; - long int i, no_of_nodes; + igraph_integer_t no_of_nodes = igraph_vcount(graph); switch (mode) { case IGRAPH_OUT: mode0 = IGRAPH_IN; break; @@ -171,32 +173,34 @@ int igraph_similarity_inverse_log_weighted(const igraph_t *graph, 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++) { + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), mode0, true)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(weights)[i] = VECTOR(degrees)[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_CHECK(igraph_i_cocitation_real(graph, res, vids, mode0, &weights)); + igraph_vector_int_destroy(°rees); igraph_vector_destroy(&weights); - IGRAPH_FINALLY_CLEAN(1); - return 0; + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; } -int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, +static igraph_error_t igraph_i_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_vids; + igraph_integer_t from, i, j; + igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; + igraph_vector_int_t vid_reverse_index; igraph_vit_t vit; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); @@ -206,38 +210,40 @@ int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, /* 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vid_reverse_index, no_of_nodes); + igraph_vector_int_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); + igraph_integer_t v = IGRAPH_VIT_GET(vit); if (v < 0 || v >= no_of_nodes) { - IGRAPH_ERROR("invalid vertex ID in vertex selector", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid vertex ID in vertex selector.", IGRAPH_EINVVID); } VECTOR(vid_reverse_index)[v] = i; } - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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_real_t weight; IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, - (igraph_integer_t) from, mode)); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, mode)); + igraph_integer_t nei_count = igraph_vector_int_size(&neis); if (weights) { weight = VECTOR(*weights)[from]; + } else { + weight = 1; } - 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]; + for (i = 0; i < nei_count - 1; i++) { + igraph_integer_t u = VECTOR(neis)[i]; + igraph_integer_t k = VECTOR(vid_reverse_index)[u]; + for (j = i + 1; j < nei_count; j++) { + igraph_integer_t v = VECTOR(neis)[j]; + igraph_integer_t l = VECTOR(vid_reverse_index)[v]; if (k != -1) { MATRIX(*res, k, v) += weight; } @@ -249,20 +255,21 @@ int igraph_cocitation_real(const igraph_t *graph, igraph_matrix_t *res, } /* Clean up */ - igraph_vector_destroy(&neis); - igraph_vector_destroy(&vid_reverse_index); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&vid_reverse_index); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } -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) { +static igraph_error_t igraph_i_neisets_intersect( + const igraph_vector_int_t *v1, const igraph_vector_int_t *v2, + igraph_integer_t *len_union, igraph_integer_t *len_intersection +) { /* ASSERT: v1 and v2 are sorted */ - long int i, j, i0, jj0; + igraph_integer_t 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; @@ -276,7 +283,7 @@ static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, j++; } } - return 0; + return IGRAPH_SUCCESS; } /** @@ -284,7 +291,6 @@ static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, * \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 @@ -293,8 +299,8 @@ static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, * \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 + * 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: @@ -314,7 +320,7 @@ static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -328,13 +334,14 @@ static int igraph_i_neisets_intersect(const igraph_vector_int_t *v1, * * \example examples/simple/igraph_similarity.c */ -int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t 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_integer_t i, j; + igraph_integer_t len_union, len_intersection; igraph_vector_int_t *v1, *v2; + igraph_integer_t k; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -349,9 +356,10 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, 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); + v1 = igraph_lazy_adjlist_get(&al, i); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); if (!igraph_vector_int_binsearch(v1, i, &k)) { - igraph_vector_int_insert(v1, k, i); + IGRAPH_CHECK(igraph_vector_int_insert(v1, k, i)); } } } @@ -364,9 +372,13 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, if (j <= i) { continue; } + v1 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit)); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); v2 = igraph_lazy_adjlist_get(&al, IGRAPH_VIT_GET(vit2)); - igraph_i_neisets_intersect(v1, v2, &len_union, &len_intersection); + IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); + + IGRAPH_CHECK(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 { @@ -381,7 +393,7 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, igraph_vit_destroy(&vit2); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -389,7 +401,6 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, * \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 @@ -421,7 +432,7 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -436,17 +447,16 @@ int igraph_similarity_jaccard(const igraph_t *graph, igraph_matrix_t *res, * * \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_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_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_integer_t u, v; + igraph_integer_t len_union, len_intersection; igraph_vector_int_t *v1, *v2; - igraph_bool_t *seen; - k = igraph_vector_size(pairs); + igraph_integer_t k = igraph_vector_int_size(pairs); if (k % 2 != 0) { - IGRAPH_ERROR("number of elements in `pairs' must be even", IGRAPH_EINVAL); + IGRAPH_ERROR("Number of elements in `pairs' must be even.", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_vector_resize(res, k / 2)); @@ -455,41 +465,42 @@ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, 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]) { + igraph_vector_bool_t seen; + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen, igraph_vcount(graph)); + + for (igraph_integer_t i = 0; i < k; i++) { + igraph_integer_t j = VECTOR(*pairs)[i]; + if (VECTOR(seen)[j]) { continue; } - seen[j] = 1; - v1 = igraph_lazy_adjlist_get(&al, (igraph_integer_t) j); + VECTOR(seen)[j] = true; + v1 = igraph_lazy_adjlist_get(&al, j); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); if (!igraph_vector_int_binsearch(v1, j, &u)) { - igraph_vector_int_insert(v1, u, j); + IGRAPH_CHECK(igraph_vector_int_insert(v1, u, j)); } } - IGRAPH_FREE(seen); + igraph_vector_bool_destroy(&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]; + for (igraph_integer_t i = 0, j = 0; i < k; i += 2, j++) { + u = VECTOR(*pairs)[i]; + v = 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); + v1 = igraph_lazy_adjlist_get(&al, u); + IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); + v2 = igraph_lazy_adjlist_get(&al, v); + IGRAPH_CHECK_OOM(v2, "Failed to query neighbors."); + + IGRAPH_CHECK(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 { @@ -500,7 +511,7 @@ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, igraph_lazy_adjlist_destroy(&al); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -508,7 +519,6 @@ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, * \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 @@ -539,7 +549,7 @@ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -555,28 +565,15 @@ int igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vector_t *res, * * \example examples/simple/igraph_similarity.c */ -int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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_vector_int_t pairs; - 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_VECTOR_INT_INIT_FINALLY(&pairs, 0); + IGRAPH_CHECK(igraph_edges(graph, es, &pairs)); + IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, &pairs, mode, loops)); + igraph_vector_int_destroy(&pairs); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -587,16 +584,15 @@ int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, * \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 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 + * 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: @@ -616,30 +612,30 @@ int igraph_similarity_jaccard_es(const igraph_t *graph, igraph_vector_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * 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. + * where |V| is the number of vertices in the vertex iterator given, and + * 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_error_t igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t vids, + igraph_neimode_t mode, igraph_bool_t loops) { 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_integer_t nr = igraph_matrix_nrow(res); + igraph_integer_t nc = igraph_matrix_ncol(res); + for (igraph_integer_t i = 0; i < nr; i++) { + for (igraph_integer_t j = 0; j < nc; j++) { igraph_real_t x = MATRIX(*res, i, j); MATRIX(*res, i, j) = 2 * x / (1 + x); } @@ -653,7 +649,6 @@ int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, * \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. @@ -684,7 +679,7 @@ int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -699,13 +694,12 @@ int igraph_similarity_dice(const igraph_t *graph, igraph_matrix_t *res, * * \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_error_t igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, + const igraph_vector_int_t *pairs, igraph_neimode_t mode, igraph_bool_t loops) { IGRAPH_CHECK(igraph_similarity_jaccard_pairs(graph, res, pairs, mode, loops)); - n = igraph_vector_size(res); - for (i = 0; i < n; i++) { + igraph_integer_t n = igraph_vector_size(res); + for (igraph_integer_t i = 0; i < n; i++) { igraph_real_t x = VECTOR(*res)[i]; VECTOR(*res)[i] = 2 * x / (1 + x); } @@ -718,7 +712,6 @@ int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, * \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 @@ -748,7 +741,7 @@ int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * invalid vertex ID passed. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -764,13 +757,12 @@ int igraph_similarity_dice_pairs(const igraph_t *graph, igraph_vector_t *res, * * \example examples/simple/igraph_similarity.c */ -int igraph_similarity_dice_es(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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_integer_t n = igraph_vector_size(res); + for (igraph_integer_t i = 0; i < n; i++) { igraph_real_t x = VECTOR(*res)[i]; VECTOR(*res)[i] = 2 * x / (1 + x); } diff --git a/src/vendor/cigraph/src/misc/coloring.c b/src/vendor/cigraph/src/misc/coloring.c index 39b0ff9509b..e8952139e8a 100644 --- a/src/vendor/cigraph/src/misc/coloring.c +++ b/src/vendor/cigraph/src/misc/coloring.c @@ -19,18 +19,21 @@ */ #include "igraph_coloring.h" -#include "igraph_interface.h" + #include "igraph_adjlist.h" +#include "igraph_interface.h" +#include "core/genheap.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); +/* COLORED_NEIGHBORS: Choose vertices based on the number of already coloured neighbours. */ + +static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t *colors) { + igraph_integer_t i, vertex, maxdeg; + igraph_integer_t 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_vector_int_t neighbors, nei_colors; IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); igraph_vector_int_fill(colors, 0); @@ -43,64 +46,70 @@ static int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vect 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_vector_int_t degree; - IGRAPH_CHECK(igraph_vector_init(°ree, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, °ree); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, 0)); - vertex = igraph_vector_which_max(°ree); + vertex = igraph_vector_int_which_max(°ree); maxdeg = VECTOR(degree)[vertex]; - igraph_vector_destroy(°ree); + igraph_vector_int_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_VECTOR_INT_INIT_FINALLY(&nei_colors, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&nei_colors, maxdeg)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&neighbors, maxdeg)); + /* two-way indexed heap holding number of already colored neighbors of yet-uncolored vertices */ IGRAPH_CHECK(igraph_2wheap_init(&cn, vc)); IGRAPH_FINALLY(igraph_2wheap_destroy, &cn); - for (i = 0; i < vc; ++i) + 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 */ + 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); + /* Within this loop, a color of 0 means "uncolored", and valid color indices start at 1. + * At the beginning, all vertices are set as "uncolored", see the vector_int_fill() call above. + * Colors will be decremented to start at 0 later. */ + while (true) { + IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, vertex, IGRAPH_ALL)); + igraph_integer_t nei_count = igraph_vector_int_size(&neighbors); - /* colour current vertex */ + /* Colour current vertex by finding smallest available non-0 color. + * Note that self-loops are effectively skipped as they merely prevent + * the current vertex from being colored with the color value it presently + * has, which is 0 (meaning uncolored). */ { 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_CHECK(igraph_vector_int_resize(&nei_colors, nei_count)); + for (i = 0; i < nei_count; ++i) { + VECTOR(nei_colors)[i] = VECTOR(*colors)[ VECTOR(neighbors)[i] ]; } - igraph_vector_int_sort(&neigh_colors); + igraph_vector_int_sort(&nei_colors); i = 0; col = 0; do { - while (i < neigh_count && VECTOR(neigh_colors)[i] == col) { + while (i < nei_count && VECTOR(nei_colors)[i] == col) { i++; } col++; - } while (i < neigh_count && VECTOR(neigh_colors)[i] == col); + } while (i < nei_count && VECTOR(nei_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]; + for (i = 0; i < nei_count; ++i) { + igraph_integer_t idx = VECTOR(neighbors)[i]; if (igraph_2wheap_has_elem(&cn, idx)) { igraph_2wheap_modify(&cn, idx, igraph_2wheap_get(&cn, idx) + 1); } @@ -120,42 +129,174 @@ static int igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vect igraph_vector_int_add_constant(colors, -1); /* free data structures */ - igraph_vector_int_destroy(&neigh_colors); - igraph_adjlist_destroy(&adjlist); + igraph_vector_int_destroy(&neighbors); + igraph_vector_int_destroy(&nei_colors); igraph_2wheap_destroy(&cn); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } +/* DSATUR: Choose vertices based on the number of adjacent colours, i.e. "saturation degree" */ + +typedef struct { + igraph_integer_t saturation_degree; /* number of colors used by neighbors */ + igraph_integer_t edge_degree; /* degree in the subgraph induced by uncolored vertices */ +} dsatur_t; + +static int dsatur_t_compare(const void *left, const void *right) { + const dsatur_t *left_d = left; + const dsatur_t *right_d = right; + if (left_d->saturation_degree == right_d->saturation_degree) { + return left_d->edge_degree - right_d->edge_degree; + } + return left_d->saturation_degree - right_d->saturation_degree; +} + +static igraph_bool_t dsatur_is_color_used_by_neighbour( + const igraph_vector_int_t *colors, igraph_integer_t color, + const igraph_vector_int_t *neighbors +) { + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (VECTOR(*colors)[nei] == color) { + return true; + } + } + + return false; +} + +static void dsatur_update_heap( + const igraph_adjlist_t *adjlist, igraph_gen2wheap_t *node_degrees_heap, + const igraph_vector_int_t *neighbors, const igraph_vector_int_t *colors, + igraph_integer_t color +) { + igraph_gen2wheap_delete_max(node_degrees_heap); + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (!igraph_gen2wheap_has_elem(node_degrees_heap, nei)) { + continue; + } + dsatur_t deg_data = *((dsatur_t*) igraph_gen2wheap_get(node_degrees_heap, nei)); + if (!dsatur_is_color_used_by_neighbour(colors, color, igraph_adjlist_get(adjlist, nei))) { + deg_data.saturation_degree++; + } + deg_data.edge_degree--; + igraph_gen2wheap_modify(node_degrees_heap, nei, °_data); + } +} + +static igraph_integer_t dsatur_get_first_viable_color(const igraph_vector_int_t *used_colors_sorted) { + igraph_integer_t color_count = igraph_vector_int_size(used_colors_sorted); + igraph_integer_t i = 0; + igraph_integer_t col = 0; + while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { + while (i < color_count && VECTOR(*used_colors_sorted)[i] == col) { + i++; + } + col++; + } + return col; +} + +static igraph_error_t igraph_i_vertex_coloring_dsatur( + const igraph_t *graph, igraph_vector_int_t *colors +) { + igraph_integer_t vcount = igraph_vcount(graph); + IGRAPH_CHECK(igraph_vector_int_resize(colors, vcount)); + + if (vcount == 0) { + return IGRAPH_SUCCESS; + } + + if (vcount == 1) { + VECTOR(*colors)[0] = 0; + return IGRAPH_SUCCESS; + } + + igraph_vector_int_fill(colors, -1); /* -1 as a color means uncolored */ + + /* Multi-edges and self-loops are removed from the adjacency list in order to ensure the correct + * updating of a vertex's neighbors' saturation degrees when that vertex is colored. */ + igraph_adjlist_t adjlist; + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + igraph_gen2wheap_t node_degrees_heap; + IGRAPH_CHECK(igraph_gen2wheap_init(&node_degrees_heap, dsatur_t_compare, sizeof(dsatur_t), vcount)); + IGRAPH_FINALLY(igraph_gen2wheap_destroy, &node_degrees_heap); + + for (igraph_integer_t vertex = 0; vertex < vcount; vertex++) { + dsatur_t dsatur; + dsatur.saturation_degree = 0; + dsatur.edge_degree = igraph_vector_int_size(igraph_adjlist_get(&adjlist, vertex)); + IGRAPH_CHECK(igraph_gen2wheap_push_with_index(&node_degrees_heap, vertex, &dsatur)); + } + + igraph_vector_int_t used_colors_sorted; + IGRAPH_VECTOR_INT_INIT_FINALLY(&used_colors_sorted, 0); + + while (! igraph_gen2wheap_empty(&node_degrees_heap)) { + igraph_integer_t node_to_color = igraph_gen2wheap_max_index(&node_degrees_heap); + igraph_vector_int_t *neighbors = igraph_adjlist_get(&adjlist, node_to_color); + igraph_integer_t nei_count = igraph_vector_int_size(neighbors); + igraph_vector_int_clear(&used_colors_sorted); + for (igraph_integer_t i=0; i < nei_count; i++) { + igraph_integer_t nei = VECTOR(*neighbors)[i]; + if (VECTOR(*colors)[nei] != -1) { + IGRAPH_CHECK(igraph_vector_int_push_back(&used_colors_sorted, VECTOR(*colors)[nei])); + } + } + igraph_vector_int_sort(&used_colors_sorted); + igraph_integer_t color = dsatur_get_first_viable_color(&used_colors_sorted); + dsatur_update_heap(&adjlist, &node_degrees_heap, neighbors, colors, color); + VECTOR(*colors)[node_to_color] = color; + + IGRAPH_ALLOW_INTERRUPTION(); + } + + igraph_vector_int_destroy(&used_colors_sorted); + igraph_gen2wheap_destroy(&node_degrees_heap); + igraph_adjlist_destroy(&adjlist); + 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, ... + * Vertices are colored greedily, one by one, always choosing the smallest color + * index that differs from that of already colored neighbors. Vertices are picked + * in an order determined by the speified heuristic. + * Colors are represented by 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 + * \param heuristic The vertex ordering heuristic to use during greedy coloring. + * See \ref igraph_coloring_greedy_t for more information. * * \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) { +igraph_error_t 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); + case IGRAPH_COLORING_GREEDY_DSATUR: + return igraph_i_vertex_coloring_dsatur(graph, colors); default: - return IGRAPH_EINVAL; + IGRAPH_ERROR("Invalid heuristic for greedy vertex coloring.", IGRAPH_EINVAL); } } diff --git a/src/vendor/cigraph/src/misc/conversion.c b/src/vendor/cigraph/src/misc/conversion.c index b16a70e835a..85495ab0e87 100644 --- a/src/vendor/cigraph/src/misc/conversion.c +++ b/src/vendor/cigraph/src/misc/conversion.c @@ -33,20 +33,25 @@ #include "core/fixed_vectorlist.h" #include "graph/attributes.h" -#include "misc/conversion_internal.h" +#include "math/safe_intop.h" + +#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1) /** * \ingroup conversion * \function igraph_get_adjacency - * \brief Returns the adjacency matrix of a graph + * \brief 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. + * contains the number of edges connecting vertex i to vertex j in the unweighted + * case, or the total weight of edges connecting vertex i to vertex j in the + * weighted case. + * * \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 + * \param type Constant specifying the type of the adjacency matrix to * create for undirected graphs. It is ignored for directed * graphs. Possible values: * \clist @@ -58,131 +63,111 @@ * 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.) + * \param weights An optional vector containing the weight of each edge + * in the graph. Supply a null pointer here to make all edges have + * the same weight of 1. + * \param loops Constant specifying how loop edges should be handled. + * Possible values: + * \clist + * \cli IGRAPH_NO_LOOPS + * loop edges are ignored and the diagonal of the matrix will contain + * zeros only + * \cli IGRAPH_LOOPS_ONCE + * loop edges are counted once, i.e. a vertex with a single unweighted + * loop edge will have 1 in the corresponding diagonal entry + * \cli IGRAPH_LOOPS_TWICE + * loop edges are counted twice in \em undirected graphs, i.e. a vertex + * with a single unweighted loop edge in an undirected graph will have + * 2 in the corresponding diagonal entry. Loop edges in directed graphs + * are still counted as 1. Essentially, this means that the function is + * counting the incident edge \em stems , which makes more sense when + * using the adjacency matrix in linear algebra. + * \endclist * \return Error code: * \c IGRAPH_EINVAL invalid type argument. * - * \sa igraph_get_adjacency_sparse if you want a sparse matrix representation + * \sa \ref 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. + * 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_error_t igraph_get_adjacency( + const igraph_t *graph, igraph_matrix_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); - int retval = 0; - long int from, to; - igraph_integer_t ffrom, fto; + igraph_integer_t i, 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(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; + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (from != to || loops != IGRAPH_NO_LOOPS) { + MATRIX(*res, from, to) += WEIGHT_OF(i); } - 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; + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); if (to < from) { - if (eids) { - MATRIX(*res, to, from) = edge + 1; - } else { - MATRIX(*res, to, from) += 1; - } + MATRIX(*res, to, from) += WEIGHT_OF(i); } else { - if (eids) { - MATRIX(*res, from, to) = edge + 1; - } else { - MATRIX(*res, from, to) += 1; - } + MATRIX(*res, from, to) += WEIGHT_OF(i); + } + if (to == from && loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, to) += WEIGHT_OF(i); } - 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; + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); if (to < from) { - if (eids) { - MATRIX(*res, from, to) = edge + 1; - } else { - MATRIX(*res, from, to) += 1; - } + MATRIX(*res, from, to) += WEIGHT_OF(i); } else { - if (eids) { - MATRIX(*res, to, from) = edge + 1; - } else { - MATRIX(*res, to, from) += 1; - } + MATRIX(*res, to, from) += WEIGHT_OF(i); + } + if (to == from && loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, to) += WEIGHT_OF(i); } - 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; - } + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + MATRIX(*res, from, to) += WEIGHT_OF(i); + if (from != to || loops == IGRAPH_LOOPS_TWICE) { + MATRIX(*res, to, from) += WEIGHT_OF(i); } - IGRAPH_EIT_NEXT(edgeit); } } else { IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); } - igraph_eit_destroy(&edgeit); - IGRAPH_FINALLY_CLEAN(1); - return retval; + /* Erase the diagonal if we don't need loop edges */ + if (loops == IGRAPH_NO_LOOPS) { + for (i = 0; i < no_of_nodes; i++) { + MATRIX(*res, i, i) = 0; + } + } + + return IGRAPH_SUCCESS; } /** - * \ingroup conversion * \function igraph_get_adjacency_sparse - * \brief Returns the adjacency matrix of a graph in sparse matrix format. + * \brief Returns the adjacency matrix of a graph in a 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 + * \param graph The input graph. + * \param res Pointer to an \em initialized sparse matrix. The result + * will be stored here. The matrix will be resized as needed. + * \param type Constant specifying the type of the adjacency matrix to * create for undirected graphs. It is ignored for directed * graphs. Possible values: * \clist @@ -191,91 +176,147 @@ int igraph_get_adjacency(const igraph_t *graph, igraph_matrix_t *res, * \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. + * the whole matrix is used, a symmetric matrix is returned + * if the graph is undirected. * \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 ) + * \sa \ref igraph_get_adjacency(), the dense version of this function. * - * Time complexity: O(|V||V|), - * |V| is the - * number of vertices in the graph. + * Time complexity: TODO. */ -int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, - igraph_get_adjacency_t type) { +igraph_error_t igraph_get_adjacency_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_get_adjacency_t type, + const igraph_vector_t *weights, igraph_loops_t loops +) { - igraph_eit_t edgeit; - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); - long int from, to; - igraph_integer_t ffrom, fto; + igraph_integer_t nzmax = directed ? no_of_edges : no_of_edges * 2; + igraph_integer_t i, from, to; - 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); + IGRAPH_CHECK(igraph_sparsemat_resize(res, no_of_nodes, no_of_nodes, nzmax)); 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); + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (from != to || loops != IGRAPH_NO_LOOPS) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + } } } 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; + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); if (to < from) { - igraph_spmatrix_add_e(res, to, from, 1); + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); + } else if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } } else { - igraph_spmatrix_add_e(res, from, to, 1); + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); } - 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); + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to < from) { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + } else if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } } else { - igraph_spmatrix_add_e(res, from, to, 1); + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); } - 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); + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + if (to == from) { + switch (loops) { + case IGRAPH_LOOPS_ONCE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, WEIGHT_OF(i))); + break; + case IGRAPH_LOOPS_TWICE: + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, to, 2 * WEIGHT_OF(i))); + break; + case IGRAPH_NO_LOOPS: + default: + break; + } + } else { + IGRAPH_CHECK(igraph_sparsemat_entry(res, from, to, WEIGHT_OF(i))); + IGRAPH_CHECK(igraph_sparsemat_entry(res, to, from, WEIGHT_OF(i))); } - IGRAPH_EIT_NEXT(edgeit); } } else { - IGRAPH_ERROR("Invalid type argument.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid type argument", IGRAPH_EINVAL); } - igraph_eit_destroy(&edgeit); - IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } +#undef WEIGHT_OF + +/** + * \function igraph_get_sparsemat + * \brief Converts an igraph graph to a sparse matrix (deprecated). + * + * If the graph is undirected, then a symmetric matrix is created. + * + * + * This function is deprecated in favour of \ref igraph_get_adjacency_sparse(), + * but does not work in an identical way. This function takes an \em uninitialized + * \c igraph_sparsemat_t while \ref igraph_get_adjacency_sparse() takes + * an already initialized one. + * + * \param graph The input graph. + * \param res Pointer to an \em uninitialized sparse matrix. The result + * will be stored here. + * \return Error code. + * + * \deprecated-by igraph_get_adjacency_sparse 0.10.0 + */ + +igraph_error_t igraph_get_sparsemat(const igraph_t *graph, igraph_sparsemat_t *res) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges; + IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax)); + return igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, NULL, IGRAPH_LOOPS_ONCE); +} + /** * \ingroup conversion * \function igraph_get_edgelist - * \brief Returns the list of edges in a graph + * \brief The list of edges in a graph. + * + * The order of the edges is given by the edge IDs. * - * 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. @@ -285,20 +326,19 @@ int igraph_get_adjacency_sparse(const igraph_t *graph, igraph_spmatrix_t *res, * res[1]->res[|E|+1], etc. * \return Error code. * - * \sa \ref igraph_edges() to return the result only for some edge ids. + * \sa \ref igraph_edges() to return the result only for some edge IDs. * - * Time complexity: O(|E|), the - * number of edges in the graph. + * 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_error_t igraph_get_edgelist(const igraph_t *graph, igraph_vector_int_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 no_of_edges = igraph_ecount(graph); + igraph_integer_t vptr = 0; igraph_integer_t from, to; - IGRAPH_CHECK(igraph_vector_resize(res, no_of_edges * 2)); + IGRAPH_CHECK(igraph_vector_int_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); @@ -322,15 +362,15 @@ int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool igraph_eit_destroy(&edgeit); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_to_directed - * \brief Convert an undirected graph to a directed one + * \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: @@ -356,10 +396,10 @@ int igraph_get_edgelist(const igraph_t *graph, igraph_vector_t *res, igraph_bool * of edges. */ -int igraph_to_directed(igraph_t *graph, +igraph_error_t 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); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); if (igraph_is_directed(graph)) { return IGRAPH_SUCCESS; @@ -371,19 +411,18 @@ int igraph_to_directed(igraph_t *graph, case IGRAPH_TO_DIRECTED_ACYCLIC: { igraph_t newgraph; - igraph_vector_t edges; - long int size = no_of_edges * 2; - long int i; + igraph_vector_int_t edges; + igraph_integer_t size = no_of_edges * 2; - IGRAPH_VECTOR_INIT_FINALLY(&edges, size); + IGRAPH_VECTOR_INT_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) { + for (igraph_integer_t i=0; i < no_of_edges; ++i) { if (RNG_INTEGER(0,1)) { - igraph_real_t temp = VECTOR(edges)[2*i]; + igraph_integer_t temp = VECTOR(edges)[2*i]; VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; VECTOR(edges)[2*i+1] = temp; } @@ -398,9 +437,9 @@ int igraph_to_directed(igraph_t *graph, 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) { + for (igraph_integer_t 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]; + igraph_integer_t temp = VECTOR(edges)[2*i]; VECTOR(edges)[2*i] = VECTOR(edges)[2*i+1]; VECTOR(edges)[2*i+1] = temp; } @@ -408,12 +447,12 @@ int igraph_to_directed(igraph_t *graph, } IGRAPH_CHECK(igraph_create(&newgraph, &edges, - (igraph_integer_t) no_of_nodes, + 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_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(2); igraph_destroy(graph); @@ -424,31 +463,32 @@ int igraph_to_directed(igraph_t *graph, 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_vector_int_t edges; + igraph_vector_int_t index; + igraph_integer_t size; + + IGRAPH_SAFE_MULT(no_of_edges, 4, &size); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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++) { + IGRAPH_CHECK(igraph_vector_int_resize(&edges, size)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_edges * 2); + for (igraph_integer_t 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, + 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_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges=*/false); IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, &newgraph, &index)); - igraph_vector_destroy(&index); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); igraph_destroy(graph); @@ -457,7 +497,7 @@ int igraph_to_directed(igraph_t *graph, break; } default: - IGRAPH_ERROR("Cannot direct graph, invalid mode", IGRAPH_EINVAL); + IGRAPH_ERROR("Cannot direct graph, invalid mode.", IGRAPH_EINVAL); } return IGRAPH_SUCCESS; @@ -496,13 +536,13 @@ int igraph_to_directed(igraph_t *graph, * \example examples/simple/igraph_to_undirected.c */ -int igraph_to_undirected(igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; igraph_t newgraph; igraph_bool_t attr = edge_comb && igraph_has_attribute_table(); @@ -513,27 +553,25 @@ int igraph_to_undirected(igraph_t *graph, } if (!igraph_is_directed(graph)) { - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_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_vector_int_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_integer_t edge = IGRAPH_EIT_GET(eit); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_FROM(graph, edge))); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, IGRAPH_TO(graph, edge))); IGRAPH_EIT_NEXT(eit); } @@ -542,52 +580,49 @@ int igraph_to_undirected(igraph_t *graph, IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - (igraph_integer_t) no_of_nodes, + no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1); + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, true); 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; + igraph_vector_int_t inadj, outadj; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge = 0; if (attr) { - IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INT_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); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t n_out, n_in; + igraph_integer_t p1 = -1, p2 = -1; + igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0, last; + IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT)); + IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN)); + n_out = igraph_vector_int_size(&outadj); + n_in = igraph_vector_int_size(&inadj); #define STEPOUT() if ( (++p1) < n_out) { \ - e1 = (long int) VECTOR(outadj)[p1]; \ + e1 = VECTOR(outadj)[p1]; \ n1 = IGRAPH_TO(graph, e1); \ } #define STEPIN() if ( (++p2) < n_in) { \ - e2 = (long int) VECTOR(inadj )[p2]; \ + e2 = 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)); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); \ + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, last)); \ } #define MERGE_INTO_CURRENT_EDGE(which) { \ if (attr) { \ @@ -638,26 +673,24 @@ int igraph_to_undirected(igraph_t *graph, #undef STEPOUT #undef STEPIN - igraph_vector_destroy(&outadj); - igraph_vector_destroy(&inadj); + igraph_vector_int_destroy(&outadj); + igraph_vector_int_destroy(&inadj); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - (igraph_integer_t) no_of_nodes, + no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 0); /* no edge attributes */ + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */ if (attr) { igraph_fixed_vectorlist_t vl; - IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, - actedge)); + 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_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb)); igraph_fixed_vectorlist_destroy(&vl); IGRAPH_FINALLY_CLEAN(1); @@ -668,41 +701,40 @@ int igraph_to_undirected(igraph_t *graph, *graph = newgraph; if (attr) { - igraph_vector_destroy(&mergeinto); + igraph_vector_int_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; + igraph_vector_int_t inadj, outadj; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge = 0; if (attr) { - IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); - igraph_vector_fill(&mergeinto, -1); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); + igraph_vector_int_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); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inadj, 0); + IGRAPH_VECTOR_INT_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, + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t n_out, n_in; + igraph_integer_t p1 = -1, p2 = -1; + igraph_integer_t e1 = 0, e2 = 0, n1 = 0, n2 = 0; + IGRAPH_CHECK(igraph_incident(graph, &outadj, i, IGRAPH_OUT)); - IGRAPH_CHECK(igraph_incident(graph, &inadj, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_incident(graph, &inadj, i, IGRAPH_IN)); - n_out = igraph_vector_size(&outadj); - n_in = igraph_vector_size(&inadj); + n_out = igraph_vector_int_size(&outadj); + n_in = igraph_vector_int_size(&inadj); #define STEPOUT() if ( (++p1) < n_out) { \ - e1 = (long int) VECTOR(outadj)[p1]; \ + e1 = VECTOR(outadj)[p1]; \ n1 = IGRAPH_TO(graph, e1); \ } #define STEPIN() if ( (++p2) < n_in) { \ - e2 = (long int) VECTOR(inadj )[p2]; \ + e2 = VECTOR(inadj )[p2]; \ n2 = IGRAPH_FROM(graph, e2); \ } @@ -711,8 +743,8 @@ int igraph_to_undirected(igraph_t *graph, 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, n1)); if (attr) { VECTOR(mergeinto)[e1] = actedge; VECTOR(mergeinto)[e2] = actedge; @@ -731,26 +763,24 @@ int igraph_to_undirected(igraph_t *graph, #undef STEPOUT #undef STEPIN - igraph_vector_destroy(&outadj); - igraph_vector_destroy(&inadj); + igraph_vector_int_destroy(&outadj); + igraph_vector_int_destroy(&inadj); IGRAPH_FINALLY_CLEAN(2); IGRAPH_CHECK(igraph_create(&newgraph, &edges, - (igraph_integer_t) no_of_nodes, + no_of_nodes, IGRAPH_UNDIRECTED)); IGRAPH_FINALLY(igraph_destroy, &newgraph); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph); - IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 0); /* no edge attributes */ + IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, true, true, /*edges*/ false); /* no edge attributes */ if (attr) { igraph_fixed_vectorlist_t vl; - IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, - actedge)); + 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_CHECK(igraph_i_attribute_combine_edges(graph, &newgraph, &vl.vecs, edge_comb)); igraph_fixed_vectorlist_destroy(&vl); IGRAPH_FINALLY_CLEAN(1); @@ -761,144 +791,158 @@ int igraph_to_undirected(igraph_t *graph, *graph = newgraph; if (attr) { - igraph_vector_destroy(&mergeinto); + igraph_vector_int_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(1); } } - return 0; + return IGRAPH_SUCCESS; } +#define WEIGHT_OF(eid) (weights ? VECTOR(*weights)[eid] : 1) + /** * \function igraph_get_stochastic - * Stochastic adjacency matrix of a graph + * \brief Stochastic adjacency matrix of a graph. * * Stochastic matrix of a graph. The stochastic matrix of a graph is * its adjacency matrix, normalized row-wise or column-wise, such that * the sum of each row (or column) is one. + * * \param graph The input graph. - * \param sparsemat Pointer to an initialized matrix, the - * result is stored here. - * \param column_wise Whether to normalize column-wise. For undirected - * graphs this argument does not have any effect. + * \param res Pointer to an initialized matrix, the result is stored here. + * It will be resized as needed. + * \param column_wise Whether to normalize column-wise. * \return Error code. * - * Time complexity: O(|V||V|), quadratic in the number of vertices. + * Time complexity: O(|V||V|), |V| is the number of vertices in the graph. * - * \sa igraph_get_stochastic_sparsemat(), the sparse version of this + * \sa \ref igraph_get_stochastic_sparse(), the sparse version of this * function. */ -int igraph_get_stochastic(const igraph_t *graph, - igraph_matrix_t *matrix, - igraph_bool_t column_wise) { - - int no_of_nodes = igraph_vcount(graph); +igraph_error_t igraph_get_stochastic( + const igraph_t *graph, igraph_matrix_t *res, igraph_bool_t column_wise, + const igraph_vector_t *weights +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_integer_t i, from, to; + igraph_vector_t sums; igraph_real_t sum; - int i, j; - - IGRAPH_CHECK(igraph_get_adjacency(graph, matrix, - IGRAPH_GET_ADJACENCY_BOTH, /*eids=*/ 0)); - - if (!column_wise) { - for (i = 0; i < no_of_nodes; i++) { - sum = 0.0; - for (j = 0; j < no_of_nodes; j++) { - sum += MATRIX(*matrix, i, j); - } - for (j = 0; j < no_of_nodes; j++) { - MATRIX(*matrix, i, j) /= sum; - } - } - } else { - for (i = 0; i < no_of_nodes; i++) { - sum = 0.0; - for (j = 0; j < no_of_nodes; j++) { - sum += MATRIX(*matrix, j, i); - } - for (j = 0; j < no_of_nodes; j++) { - MATRIX(*matrix, j, i) /= sum; - } - } - } - - return 0; -} + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); -int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, - igraph_bool_t column_wise) { - igraph_vector_t sum; - int no_of_nodes = (int) igraph_sparsemat_nrow(sparsemat); - int i; + IGRAPH_VECTOR_INIT_FINALLY(&sums, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&sum, no_of_nodes); + if (directed) { + IGRAPH_CHECK(igraph_strength( + graph, &sums, igraph_vss_all(), + column_wise ? IGRAPH_IN : IGRAPH_OUT, + /* loops = */ true, weights + )); - if (!column_wise) { - IGRAPH_CHECK(igraph_sparsemat_rowsums(sparsemat, &sum)); - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(sum)[i] == 0.0) { - IGRAPH_ERROR("Zero out-degree vertices not allowed", - IGRAPH_EINVAL); - } - VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + sum = VECTOR(sums)[column_wise ? to : from]; + MATRIX(*res, from, to) += WEIGHT_OF(i) / sum; } - IGRAPH_CHECK(igraph_sparsemat_scale_rows(sparsemat, &sum)); } else { - IGRAPH_CHECK(igraph_sparsemat_colsums(sparsemat, &sum)); - for (i = 0; i < no_of_nodes; i++) { - if (VECTOR(sum)[i] == 0.0) { - IGRAPH_ERROR("Zero out-degree vertices not allowed", - IGRAPH_EINVAL); - } - VECTOR(sum)[i] = 1.0 / VECTOR(sum)[i]; + IGRAPH_CHECK(igraph_strength( + graph, &sums, igraph_vss_all(), IGRAPH_ALL, + /* loops = */ true, weights + )); + + for (i = 0; i < no_of_edges; i++) { + from = IGRAPH_FROM(graph, i); + to = IGRAPH_TO(graph, i); + MATRIX(*res, from, to) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? to : from]; + MATRIX(*res, to, from) += WEIGHT_OF(i) / VECTOR(sums)[column_wise ? from: to]; } - IGRAPH_CHECK(igraph_sparsemat_scale_cols(sparsemat, &sum)); } - igraph_vector_destroy(&sum); + igraph_vector_destroy(&sums); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } +#undef WEIGHT_OF + /** - * \function igraph_get_stochastic_sparsemat - * \brief Stochastic adjacency matrix of a graph + * \function igraph_get_stochastic_sparse + * \brief The stochastic adjacency matrix of a graph. * * Stochastic matrix of a graph. The stochastic matrix of a graph is * its adjacency matrix, normalized row-wise or column-wise, such that * the sum of each row (or column) is one. + * * \param graph The input graph. - * \param sparsemat Pointer to an uninitialized sparse matrix, the - * result is stored here. - * \param column_wise Whether to normalize column-wise. For undirected - * graphs this argument does not have any effect. + * \param res Pointer to an \em initialized sparse matrix, the + * result is stored here. The matrix will be resized as needed. + * \param column_wise Whether to normalize column-wise. * \return Error code. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. * - * \sa igraph_get_stochastic(), the dense version of this function. + * \sa \ref igraph_get_stochastic(), the dense version of this function. */ -int igraph_get_stochastic_sparsemat(const igraph_t *graph, - igraph_sparsemat_t *sparsemat, - igraph_bool_t column_wise) { +igraph_error_t igraph_get_stochastic_sparse( + const igraph_t *graph, igraph_sparsemat_t *res, igraph_bool_t column_wise, + const igraph_vector_t *weights +) { + IGRAPH_CHECK(igraph_get_adjacency_sparse(graph, res, IGRAPH_GET_ADJACENCY_BOTH, weights, IGRAPH_LOOPS_TWICE)); - IGRAPH_CHECK(igraph_get_sparsemat(graph, sparsemat)); - IGRAPH_FINALLY(igraph_sparsemat_destroy, sparsemat); - IGRAPH_CHECK(igraph_i_normalize_sparsemat(sparsemat, column_wise)); - IGRAPH_FINALLY_CLEAN(1); + if (column_wise) { + IGRAPH_CHECK(igraph_sparsemat_normalize_cols(res, /* allow_zeros = */ 0)); + } else { + IGRAPH_CHECK(igraph_sparsemat_normalize_rows(res, /* allow_zeros = */ 0)); + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_get_stochastic_sparsemat + * \brief Stochastic adjacency matrix of a graph (deprecated). + * + * This function is deprecated in favour of \ref igraph_get_stochastic_sparse(), + * but does not work in an identical way. This function takes an \em uninitialized + * \c igraph_sparsemat_t while \ref igraph_get_stochastic_sparse() takes + * an already initialized one. + * + * \param graph The input graph. + * \param res Pointer to an \em uninitialized sparse matrix, the + * result is stored here. The matrix will be resized as needed. + * \param column_wise Whether to normalize column-wise. For undirected + * graphs this argument does not have any effect. + * \return Error code. + * + * \deprecated-by igraph_get_stochastic_sparse 0.10.0 + */ + +igraph_error_t igraph_get_stochastic_sparsemat(const igraph_t *graph, + igraph_sparsemat_t *res, + igraph_bool_t column_wise) { - return 0; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nzmax = igraph_is_directed(graph) ? no_of_edges : 2*no_of_edges; + IGRAPH_CHECK(igraph_sparsemat_init(res, no_of_nodes, no_of_nodes, nzmax)); + return igraph_get_stochastic_sparse(graph, res, column_wise, NULL); } /** * \ingroup conversion * \function igraph_to_prufer - * \brief Converts a tree to its Prüfer sequence + * \brief Converts a tree to its Prüfer sequence. * * A Prüfer sequence is a unique sequence of integers associated * with a labelled tree. A tree on n >= 2 vertices can be represented by a @@ -919,19 +963,20 @@ int igraph_get_stochastic_sparsemat(const igraph_t *graph, * \sa \ref igraph_from_prufer() * */ -int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { +igraph_error_t 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 + 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_vector_int_t degrees; + igraph_vector_int_t neighbors; igraph_integer_t prufer_index = 0; igraph_integer_t n = igraph_vcount(graph); - igraph_bool_t is_tree = 0; + igraph_bool_t is_tree = false; IGRAPH_CHECK(igraph_is_tree(graph, &is_tree, NULL, IGRAPH_ALL)); @@ -944,8 +989,8 @@ int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { } IGRAPH_CHECK(igraph_vector_int_resize(prufer, n - 2)); - IGRAPH_VECTOR_INIT_FINALLY(°rees, n); - IGRAPH_VECTOR_INIT_FINALLY(&neighbors, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, n); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 1); IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); @@ -954,7 +999,6 @@ int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { 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; @@ -963,8 +1007,8 @@ int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { 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_count = igraph_vector_int_size(&neighbors); + for (igraph_integer_t i = 0; i < neighbor_count; i++) { neighbor = VECTOR(neighbors)[i]; if (VECTOR(degrees)[neighbor] > 0) { break; @@ -985,8 +1029,8 @@ int igraph_to_prufer(const igraph_t *graph, igraph_vector_int_t* prufer) { } } - igraph_vector_destroy(°rees); - igraph_vector_destroy(&neighbors); + igraph_vector_int_destroy(°rees); + igraph_vector_int_destroy(&neighbors); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/misc/cycle_bases.c b/src/vendor/cigraph/src/misc/cycle_bases.c new file mode 100644 index 00000000000..425a62cf5dd --- /dev/null +++ b/src/vendor/cigraph/src/misc/cycle_bases.c @@ -0,0 +1,540 @@ +/* + IGraph library. + Copyright (C) 2021-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, see . +*/ + +#include "igraph_cycles.h" + +#include "igraph_adjlist.h" +#include "igraph_components.h" +#include "igraph_dqueue.h" +#include "igraph_error.h" +#include "igraph_interface.h" + +#include "core/interruption.h" +#include "misc/order_cycle.h" + +/**** Fundamental cycles *****/ + +/* Computes fundamental cycles for the connected component containing 'start_vid', + * and appends them to 'result'. + * + * 'visited' must be a vector of length igraph_vcount(graph). + * visited[u] will be set to mark+1 or mark+2 for each visited vertex 'u'. + * No elements of 'visited' must have these values when calling this function. + * 'mark' can be specified in order to be able to re-use a 'visited' vector + * multiple times without having to re-set all its elements. + * + * During the operation of the function, mark+1 indicates that a vertex has been + * queued for processing, but not processed yet. mark+2 indicates that it has + * been processed. + */ +static igraph_error_t +igraph_i_fundamental_cycles_bfs( + const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_inclist_t *inclist, + igraph_vector_int_t *visited, + igraph_integer_t mark /* mark used in 'visited' */) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vector_int_t pred_edge; + igraph_vector_int_t u_back, v_back; + + if (start_vid < 0 || start_vid >= no_of_nodes) { + IGRAPH_ERROR("Invalid starting vertex id.", IGRAPH_EINVAL); + } + + if (mark > IGRAPH_INTEGER_MAX - 2) { + IGRAPH_ERROR("Graph too large for cycle basis.", IGRAPH_EOVERFLOW); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&pred_edge, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&u_back, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&v_back, 0); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 0); + + IGRAPH_CHECK(igraph_dqueue_int_push(&q, start_vid)); /* vertex id */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); /* distance from start_vid*/ + VECTOR(*visited)[start_vid] = mark + 1; /* mark as seen */ + VECTOR(pred_edge)[start_vid] = -1; /* non-valid predecessor edge id for root vertex */ + + while (! igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); + igraph_integer_t vdist = igraph_dqueue_int_pop(&q); + + igraph_vector_int_t *incs = igraph_inclist_get(inclist, v); + igraph_integer_t n = igraph_vector_int_size(incs); + igraph_integer_t i, j; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (i=0; i < n; ++i) { + igraph_integer_t e = VECTOR(*incs)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + + if (e == VECTOR(pred_edge)[v]) { + /* do not follow the edge through which we came to v */ + continue; + } + + if (VECTOR(*visited)[u] == mark + 2) { + /* u has already been processed */ + continue; + } else if (VECTOR(*visited)[u] == mark + 1) { + /* u has been seen but not yet processed */ + + /* Found cycle edge u-v. Now we walk back up the BFS tree + * in order to find the common ancestor of u and v. We exploit + * that the distance of u from the start vertex is either the + * same as that of v, or one greater. */ + + igraph_integer_t up = u, vp = v; + igraph_integer_t u_back_len, v_back_len; + igraph_vector_int_t cycle; + + IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, e)); + for (;;) { + igraph_integer_t upe, vpe; + + if (up == vp) { + break; + } + + upe = VECTOR(pred_edge)[up]; + IGRAPH_CHECK(igraph_vector_int_push_back(&u_back, upe)); + up = IGRAPH_OTHER(graph, upe, up); + + if (up == vp) { + break; + } + + vpe = VECTOR(pred_edge)[vp]; + IGRAPH_CHECK(igraph_vector_int_push_back(&v_back, vpe)); + vp = IGRAPH_OTHER(graph, vpe, vp); + } + + u_back_len = igraph_vector_int_size(&u_back); + v_back_len = igraph_vector_int_size(&v_back); + IGRAPH_VECTOR_INT_INIT_FINALLY(&cycle, u_back_len + v_back_len); + + for (j=0; j < v_back_len; ++j) { + VECTOR(cycle)[j] = VECTOR(v_back)[j]; + } + for (j=0; j < u_back_len; ++j) { + VECTOR(cycle)[v_back_len + j] = VECTOR(u_back)[u_back_len - j - 1]; + } + + igraph_vector_int_clear(&v_back); + igraph_vector_int_clear(&u_back); + + IGRAPH_CHECK(igraph_vector_int_list_push_back(result, &cycle)); + IGRAPH_FINALLY_CLEAN(1); /* pass ownership of 'cycle' to 'result' */ + } else { + /* encountering u for the first time, queue it for processing */ + + /* Only queue vertices with distance at most 'bfs_cutoff' from the root. */ + /* Negative 'bfs_cutoff' indicates no cutoff. */ + if (bfs_cutoff < 0 || vdist < bfs_cutoff) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, u)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, vdist + 1)); + VECTOR(*visited)[u] = mark + 1; + VECTOR(pred_edge)[u] = e; + } + } + } + + VECTOR(*visited)[v] = mark + 2; /* mark v as processed */ + } /* ! igraph_dqueue_int_empty(&q) */ + + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&v_back); + igraph_vector_int_destroy(&u_back); + igraph_vector_int_destroy(&pred_edge); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_fundamental_cycles + * \brief Finds a fundamental cycle basis. + * + * \experimental + * + * This function computes a fundamental cycle basis associated with a breadth-first + * search tree of the graph. + * + * + * Edge directions are ignored. Multi-edges and self-loops are supported. + * + * \param graph The graph object. + * \param result An initialized integer vector list. The result will be stored here, + * each vector containing the edge IDs of a basis element. + * \param start_vid If negative, a complete fundamental cycle basis is returned. + * If a vertex ID, the fundamental cycles associated with the BFS tree rooted + * in that vertex will be returned, only for the weakly connected component + * containing that vertex. + * \param bfs_cutoff If negative, a complete cycle basis is returned. Otherwise, only + * cycles of length 2*bfs_cutoff + 1 or shorter are included. \p bfs_cutoff + * is used to limit the depth of the BFS tree when searching for cycle edges. + * \param weights Currently unused. + * \return Error code. + * + * Time complexity: O(|V| + |E|). + */ +igraph_error_t igraph_fundamental_cycles(const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t start_vid, + igraph_integer_t bfs_cutoff, + const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t estimated_rank; + igraph_integer_t i; + igraph_inclist_t inclist; + igraph_vector_int_t visited; /* see comments before igraph_i_fundamental_cycles_bfs() */ + + IGRAPH_UNUSED(weights); + + if (start_vid >= no_of_nodes) { + IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); + + /* Compute cycle rank assuming that the graph is connected. */ + estimated_rank = no_of_edges - no_of_nodes + 1; + estimated_rank = estimated_rank < 0 ? 0 : estimated_rank; + + igraph_vector_int_list_clear(result); + IGRAPH_CHECK(igraph_vector_int_list_reserve(result, estimated_rank)); + + if (start_vid < 0) { + for (i=0; i < no_of_nodes; ++i) { + if (! VECTOR(visited)[i]) { + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, i, bfs_cutoff, &inclist, + &visited, /* mark */ 0)); + } + } + } else { + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs(graph, result, start_vid, bfs_cutoff, &inclist, + &visited, /* mark */ 0)); + } + + igraph_vector_int_destroy(&visited); + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/***** Minimum weight cycle basis *****/ + +/* In this implementation, the cycle vectors (basis elements) are stored as a sparse representation: + * a sorted list of edge indices. */ + + +/* qsort-compatible comparison for sparse cycle vectors: shorter ones come first, use lexicographic + * order for equal length ones. Lexicographic order helps keep row insertion into the reduced matrix + * efficient during Gaussian elimination, by ensuring that insertions usually happen near the end. */ +static int cycle_cmp(const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); + + if (n1 < n2) { + return -1; + } else if (n1 > n2) { + return 1; + } else { + return igraph_vector_int_lex_cmp(v1, v2); + } +} + +/* Adding cycle vectors produces the symmetric difference of the corresponding edge sets. */ +static igraph_error_t cycle_add(const igraph_vector_int_t *a, const igraph_vector_int_t *b, igraph_vector_int_t *res) { + igraph_integer_t na = igraph_vector_int_size(a), nb = igraph_vector_int_size(b); + const igraph_integer_t *pa = VECTOR(*a), *pb = VECTOR(*b); + const igraph_integer_t *pa_end = pa + na, *pb_end = pb + nb; + + igraph_vector_int_clear(res); + for (;;) { + while (pa != pa_end && pb != pb_end && *pa < *pb) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); + pa++; + } + while (pa != pa_end && pb != pb_end && *pa == *pb) { + pa++; pb++; + } + while (pa != pa_end && pb != pb_end && *pb < *pa) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); + pb++; + } + if (pa == pa_end) { + while (pb != pb_end) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pb)); + pb++; + } + break; + } + if (pb == pb_end) { + while (pa != pa_end) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, *pa)); + pa++; + } + break; + } + } + + return IGRAPH_SUCCESS; +} + + +#define MATROW(m, i) (&VECTOR(m)[i]) +#define MATEL(m, i, j) VECTOR(*MATROW(m, i))[j] + +/* Gaussian elimination for sparse cycle vectors. 'reduced_matrix' is always maintained + * in row-echelon form. This function decides if 'cycle' is linearly independent of this + * matrix, and if not, it adds it to the matrix. */ +static igraph_error_t gaussian_elimination(igraph_vector_int_list_t *reduced_matrix, + const igraph_vector_int_t *cycle, + igraph_bool_t *independent) { + + const igraph_integer_t nrow = igraph_vector_int_list_size(reduced_matrix); + igraph_integer_t i; + + igraph_vector_int_t work, tmp; + + IGRAPH_CHECK(igraph_vector_int_init_copy(&work, cycle)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &work); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + + for (i=0; i < nrow; ++i) { + igraph_vector_int_t *row = MATROW(*reduced_matrix, i); + + if ( VECTOR(*row)[0] < VECTOR(work)[0] ) { + continue; + } else if ( VECTOR(*row)[0] == VECTOR(work)[0] ) { + IGRAPH_CHECK(cycle_add(row, &work, &tmp)); + if (igraph_vector_int_empty(&tmp)) { + *independent = false; + igraph_vector_int_destroy(&work); + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; + } + IGRAPH_CHECK(igraph_vector_int_swap(&work, &tmp)); + } else { /* VECTOR(*row)[0] > VECTOR(work)[0] */ + break; + } + } + + /* 'cycle' was linearly independent, insert new row into matrix */ + *independent = true; + IGRAPH_CHECK(igraph_vector_int_list_insert(reduced_matrix, i, &work)); /* transfers ownership */ + + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(2); /* +1, transferring ownership of 'work' to 'reduced_matrix' */ + + return IGRAPH_SUCCESS; +} + +#undef MATEL +#undef MATROW + + +/** + * \function igraph_minimum_cycle_basis + * \brief Computes a minimum weight cycle basis. + * + * \experimental + * + * This function computes a minimum weight cycle basis of a graph. Currently, + * a modified version of Horton's algorithm is used that allows for cutoffs. + * + * + * Edge directions are ignored. Multi-edges and self-loops are supported. + * + * + * References: + * + * + * Horton, J. D. (1987) + * A polynomial-time algorithm to find the shortest cycle basis of a graph, + * SIAM Journal on Computing, 16 (2): 358–366. + * https://doi.org/10.1137%2F0216026 + * + * \param graph The graph object. + * \param result An initialized integer vector list, the elements of the cycle + * basis will be stored here as vectors of edge IDs. + * \param bfs_cutoff If negative, an exact minimum cycle basis is returned. Otherwise + * only those cycles in the result will be part of some minimum cycle basis which + * are of size 2*bfs_cutoff + 1 or smaller. Cycles longer than this limit + * may not be of the smallest possible size. + * \p bfs_cutoff is used to limit the depth of the BFS tree when computing candidate + * cycles. Specifying a bfs_cutoff can speed up the computation substantially. + * \param complete Boolean value. Used only when \p bfs_cutoff was given. + * If true, a complete basis is returned. If false, only cycles not greater + * than 2*bfs_cutoff + 1 are returned. This may save computation + * time, however, the result will not span the entire cycle space. + * \param use_cycle_order If true, each cycle is returned in natural order: + * the edge IDs will appear ordered along the cycle. This comes at a small + * performance cost. If false, no guarantees are given about the ordering + * of edge IDs within cycles. This parameter exists solely to control + * performance tradeoffs. + * \param weights Currently unused. + * \return Error code. + * + * Time complexity: TODO. + */ +igraph_error_t igraph_minimum_cycle_basis(const igraph_t *graph, + igraph_vector_int_list_t *result, + igraph_integer_t bfs_cutoff, + igraph_bool_t complete, + igraph_bool_t use_cycle_order, + const igraph_vector_t *weights) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t rank; + igraph_vector_int_list_t candidates; + + IGRAPH_UNUSED(weights); + + /* Compute candidate elements for the minimum weight basis. */ + { + igraph_inclist_t inclist; + igraph_vector_int_t visited; /* visited[v] % 3 is zero for unvisited vertices, see igraph_i_fundamental_cycles_bfs() */ + igraph_vector_int_t degrees; + igraph_integer_t no_of_comps; + igraph_integer_t mark; + + /* We use the degrees to avoid doing a BFS from vertices with d < 3, except in special cases. + * Degrees cannot be computed from the inclist because there we use IGRAPH_LOOPS_ONCE. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_connected_components(graph, NULL, NULL, &no_of_comps, IGRAPH_WEAK)); + rank = no_of_edges - no_of_nodes + no_of_comps; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&visited, no_of_nodes); + + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + + /* TODO: estimate space to reserve. 'rank' is a lower bound only. */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&candidates, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(&candidates, rank)); + + mark = 0; + for (igraph_integer_t i=0; i < no_of_nodes; ++i) { + igraph_integer_t degree = VECTOR(degrees)[i]; + igraph_bool_t vis = VECTOR(visited)[i] % 3 != 0; /* was vertex i visited already? */ + + /* Generally, we only need to run a BFS from vertices of degree 3 or greater. + * The exception is a connected component which is itself a cycle, and therefore + * only contains vertices of degree 2. Thus from unvisited vertices we always run + * a full BFS while from already visited ones only if their degree is at least 3. */ + + /* TODO: mark entire component as visited, not just vertex. */ + if (degree <= 1 || (vis && degree < 3)) { + continue; + } + + /* TODO: BFS is only necessary from a feedback vertex set, find fast FVS approximation algorithm. */ + + IGRAPH_CHECK(igraph_i_fundamental_cycles_bfs( + graph, &candidates, i, (vis || !complete) ? bfs_cutoff : -1, &inclist, &visited, mark)); + mark += 3; + } + + igraph_inclist_destroy(&inclist); + igraph_vector_int_destroy(&visited); + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(3); + } + + /* Sort candidates by size (= weight) and remove duplicates. */ + { + igraph_integer_t cand_count = igraph_vector_int_list_size(&candidates); + + for (igraph_integer_t i=0; i < cand_count; ++i) { + igraph_vector_int_sort(igraph_vector_int_list_get_ptr(&candidates, i)); + } + igraph_vector_int_list_sort(&candidates, &cycle_cmp); + igraph_vector_int_list_remove_consecutive_duplicates(&candidates, igraph_vector_int_all_e); + } + + igraph_vector_int_list_clear(result); + IGRAPH_CHECK(igraph_vector_int_list_reserve(result, rank)); + + /* Find a complete basis, starting with smallest elements. */ + /* This is typically the slowest part of the algorithm. */ + { + igraph_integer_t cand_len = igraph_vector_int_list_size(&candidates); + igraph_vector_int_list_t reduced_matrix; + igraph_bool_t independent; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&reduced_matrix, 0); + + for (igraph_integer_t i=0; i < cand_len; ++i) { + const igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(&candidates, i); + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(gaussian_elimination(&reduced_matrix, cycle, &independent)); + if (independent) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(result, cycle)); + } + + if (igraph_vector_int_list_size(&reduced_matrix) == rank) { + /* We have a complete basis. */ + break; + } + } + + igraph_vector_int_list_destroy(&reduced_matrix); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_list_destroy(&candidates); + IGRAPH_FINALLY_CLEAN(1); + + if (use_cycle_order) { + igraph_integer_t result_size = igraph_vector_int_list_size(result); + igraph_vector_int_t tmp; + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + for (igraph_integer_t i=0; i < result_size; ++i) { + igraph_vector_int_t *cycle = igraph_vector_int_list_get_ptr(result, i); + IGRAPH_CHECK(igraph_vector_int_update(&tmp, cycle)); + IGRAPH_CHECK(igraph_i_order_cycle(graph, &tmp, cycle)); + } + igraph_vector_int_destroy(&tmp); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/misc/degree_sequence.cpp b/src/vendor/cigraph/src/misc/degree_sequence.cpp index fcc11305bd4..1debe31d473 100644 --- a/src/vendor/cigraph/src/misc/degree_sequence.cpp +++ b/src/vendor/cigraph/src/misc/degree_sequence.cpp @@ -18,7 +18,9 @@ */ #include "igraph_constructors.h" -#include "igraph_interface.h" + +#include "core/exceptions.h" +#include "math/safe_intop.h" #include #include @@ -34,10 +36,10 @@ // (vertex, degree) pair struct vd_pair { - long vertex; + igraph_integer_t vertex; igraph_integer_t degree; - vd_pair(long vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} + vd_pair(igraph_integer_t vertex, igraph_integer_t degree) : vertex(vertex), degree(degree) {} }; // (indegree, outdegree) @@ -45,10 +47,10 @@ typedef std::pair bidegree; // (vertex, bidegree) pair struct vbd_pair { - long vertex; + igraph_integer_t vertex; bidegree degree; - vbd_pair(long vertex, bidegree degree) : vertex(vertex), degree(degree) {} + vbd_pair(igraph_integer_t vertex, bidegree degree) : vertex(vertex), degree(degree) {} }; // Comparison function for vertex-degree pairs. @@ -69,14 +71,14 @@ template inline bool degree_less(const T &a, const T &b) { // 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); +static igraph_error_t igraph_i_havel_hakimi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool largest) { + igraph_integer_t n = igraph_vector_int_size(deg); - long ec = 0; // number of edges added so far + igraph_integer_t ec = 0; // number of edges added so far std::vector vertices; vertices.reserve(n); - for (int i = 0; i < n; ++i) { + for (igraph_integer_t i = 0; i < n; ++i) { vertices.push_back(vd_pair(i, VECTOR(*deg)[i])); } @@ -100,7 +102,7 @@ static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *ed } if (largest) { - for (int i = 0; i < vd.degree; ++i) { + for (igraph_integer_t i = 0; i < vd.degree; ++i) { if (--(vertices[vertices.size() - 1 - i].degree) < 0) { goto fail; } @@ -111,7 +113,7 @@ static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *ed } 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) { + for (igraph_integer_t i = 0; i < vd.degree; ++i) { vertices[i].degree--; VECTOR(*edges)[2 * (ec + i)] = vd.vertex; @@ -130,34 +132,34 @@ static int igraph_i_havel_hakimi(const igraph_vector_t *deg, igraph_vector_t *ed // 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); +static igraph_error_t igraph_i_havel_hakimi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges) { + igraph_integer_t n = igraph_vector_int_size(deg); - long ec = 0; // number of edges added so far + igraph_integer_t ec = 0; // number of edges added so far typedef std::list vlist; vlist vertices; - for (int i = 0; i < n; ++i) { + for (igraph_integer_t 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) { + for (auto it = vertices.begin(); it != vertices.end(); ++it) { pointers.push_back(it); } - for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + for (const auto &pt : pointers) { vertices.sort(degree_greater); - vd_pair vd = **pt; - vertices.erase(*pt); + vd_pair vd = *pt; + vertices.erase(pt); if (vd.degree == 0) { continue; } - int k; + igraph_integer_t k; vlist::iterator it; for (it = vertices.begin(), k = 0; k != vd.degree && it != vertices.end(); @@ -213,23 +215,23 @@ static void bubble_up(It first, It last, Compare comp) { // 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); +static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops, bool largest) { + igraph_integer_t vcount = igraph_vector_int_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]; + for (igraph_integer_t i = 0; i < vcount; ++i) { + igraph_integer_t 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; + igraph_integer_t ec = 0; while (! vertices.empty()) { // Remove any zero degrees, and error on negative ones. @@ -245,7 +247,7 @@ static int igraph_i_realize_undirected_multi(const igraph_vector_t *deg, igraph_ // or throw an error, depending on the 'loops' setting. if (vertices.size() == 1) { if (loops) { - for (long i=0; i < w.degree/2; ++i) { + for (igraph_integer_t i=0; i < w.degree/2; ++i) { VECTOR(*edges)[2*ec] = w.vertex; VECTOR(*edges)[2*ec+1] = w.vertex; ec++; @@ -287,39 +289,39 @@ static int igraph_i_realize_undirected_multi(const igraph_vector_t *deg, igraph_ } -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); +static igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vector_int_t *deg, igraph_vector_int_t *edges, bool loops) { + igraph_integer_t vcount = igraph_vector_int_size(deg); if (vcount == 0) return IGRAPH_SUCCESS; typedef std::list vlist; vlist vertices; - for (int i = 0; i < vcount; ++i) { + for (igraph_integer_t 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) { + for (auto 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); + igraph_integer_t ec = 0; + for (const auto &pt : pointers) { + vd_pair vd = *pt; + vertices.erase(pt); while (vd.degree > 0) { - vlist::iterator uit = vertices.begin(); + auto 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) { + for (igraph_integer_t i=0; i < vd.degree/2; ++i) { VECTOR(*edges)[2*ec] = vd.vertex; VECTOR(*edges)[2*ec+1] = vd.vertex; ec++; @@ -341,7 +343,7 @@ static int igraph_i_realize_undirected_multi_index(const igraph_vector_t *deg, i // 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; + auto wit = uit; ++wit; if (wit->degree > uit->degree) { @@ -369,14 +371,14 @@ inline bool is_nonzero_outdeg(const vbd_pair &vd) { // 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 +static igraph_error_t igraph_i_kleitman_wang(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges, bool smallest) { + igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices - long ec = 0; // number of edges added so far + igraph_integer_t ec = 0; // number of edges added so far std::vector vertices; vertices.reserve(n); - for (int i = 0; i < n; ++i) { + for (igraph_integer_t i = 0; i < n; ++i) { vertices.push_back(vbd_pair(i, bidegree(VECTOR(*indeg)[i], VECTOR(*outdeg)[i]))); } @@ -406,13 +408,13 @@ static int igraph_i_kleitman_wang(const igraph_vector_t *outdeg, const igraph_ve } // are there a sufficient number of other vertices to connect to? - if (static_cast(vertices.size()) - 1 < vdp->degree.second) { + if (static_cast(vertices.size()) - 1 < vdp->degree.second) { goto fail; } // create the connections - int k = 0; - for (std::vector::iterator it = vertices.begin(); + igraph_integer_t k = 0; + for (auto it = vertices.begin(); k < vdp->degree.second; ++it) { if (it->vertex == vdp->vertex) { @@ -440,36 +442,36 @@ static int igraph_i_kleitman_wang(const igraph_vector_t *outdeg, const igraph_ve // 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 +static igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *outdeg, const igraph_vector_int_t *indeg, igraph_vector_int_t *edges) { + igraph_integer_t n = igraph_vector_int_size(indeg); // number of vertices - long ec = 0; // number of edges added so far + igraph_integer_t ec = 0; // number of edges added so far typedef std::list vlist; vlist vertices; - for (int i = 0; i < n; ++i) { + for (igraph_integer_t 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) { + for (auto it = vertices.begin(); it != vertices.end(); ++it) { pointers.push_back(it); } - for (std::vector::iterator pt = pointers.begin(); pt != pointers.end(); ++pt) { + for (const auto &pt : pointers) { // 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; + vbd_pair &vd = *pt; if (vd.degree.second == 0) { continue; } - int k = 0; + igraph_integer_t k = 0; vlist::iterator it; for (it = vertices.begin(); k != vd.degree.second && it != vertices.end(); @@ -506,27 +508,29 @@ static int igraph_i_kleitman_wang_index(const igraph_vector_t *outdeg, const igr /***** Main functions *****/ /**************************/ -static int igraph_i_realize_undirected_degree_sequence( +static igraph_error_t igraph_i_realize_undirected_degree_sequence( igraph_t *graph, - const igraph_vector_t *deg, + const igraph_vector_int_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)); + igraph_integer_t node_count = igraph_vector_int_size(deg); + igraph_integer_t deg_sum; + + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(deg, °_sum)); 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) { + if (node_count > 0 && igraph_vector_int_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); + igraph_vector_int_t edges; + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, deg_sum); + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; 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) { @@ -588,34 +592,39 @@ static int igraph_i_realize_undirected_degree_sequence( * so no explanatory error message for now. */ return IGRAPH_UNIMPLEMENTED; } + IGRAPH_HANDLE_EXCEPTIONS_END; - igraph_create(graph, &edges, igraph_integer_t(node_count), false); + IGRAPH_CHECK(igraph_create(graph, &edges, node_count, false)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static int igraph_i_realize_directed_degree_sequence( +static igraph_error_t igraph_i_realize_directed_degree_sequence( igraph_t *graph, - const igraph_vector_t *outdeg, - const igraph_vector_t *indeg, + const igraph_vector_int_t *outdeg, + const igraph_vector_int_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)); + igraph_integer_t node_count = igraph_vector_int_size(outdeg); + igraph_integer_t edge_count, edge_count2, indeg_sum; - if (igraph_vector_size(indeg) != node_count) { + IGRAPH_CHECK(igraph_i_safe_vector_int_sum(outdeg, &edge_count)); + + if (igraph_vector_int_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_CHECK(igraph_i_safe_vector_int_sum(indeg, &indeg_sum)); + if (indeg_sum != 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)) { + if (node_count > 0 && (igraph_vector_int_min(outdeg) < 0 || igraph_vector_int_min(indeg) < 0)) { IGRAPH_ERROR("Vertex degrees must be non-negative.", IGRAPH_EINVAL); } @@ -624,10 +633,11 @@ static int igraph_i_realize_directed_degree_sequence( 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); + igraph_vector_int_t edges; + IGRAPH_SAFE_MULT(edge_count, 2, &edge_count2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, edge_count2); + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; switch (method) { case IGRAPH_REALIZE_DEGSEQ_SMALLEST: IGRAPH_CHECK(igraph_i_kleitman_wang(outdeg, indeg, &edges, true)); @@ -641,10 +651,11 @@ static int igraph_i_realize_directed_degree_sequence( default: IGRAPH_ERROR("Invalid directed degree sequence realization method.", IGRAPH_EINVAL); } + IGRAPH_HANDLE_EXCEPTIONS_END; - igraph_create(graph, &edges, igraph_integer_t(node_count), true); + IGRAPH_CHECK(igraph_create(graph, &edges, node_count, true)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -690,7 +701,7 @@ static int igraph_i_realize_directed_degree_sequence( * 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 + * https://www.jstor.org/stable/2098770 * * * D. J. Kleitman and D. L. Wang, @@ -700,8 +711,8 @@ static int igraph_i_realize_directed_degree_sequence( * * * Sz. Horvát and C. D. Modes, - * Connectivity matters: Construction and exact random sampling of connected graphs (2020). - * https://arxiv.org/abs/2009.03747 + * Connectedness matters: construction and exact random sampling of connected networks (2021). + * https://doi.org/10.1088/2632-072X/abced5 * * \param graph Pointer to an uninitialized graph object. * \param outdeg The degree sequence of an undirected graph @@ -728,7 +739,7 @@ static int igraph_i_realize_directed_degree_sequence( * 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 + * provided that a connected realization exists (see Horvát and Modes, 2021, 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. @@ -758,28 +769,20 @@ static int igraph_i_realize_directed_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. * + * \example examples/simple/igraph_realize_degree_sequence.c */ -int igraph_realize_degree_sequence( +igraph_error_t igraph_realize_degree_sequence( igraph_t *graph, - const igraph_vector_t *outdeg, const igraph_vector_t *indeg, + const igraph_vector_int_t *outdeg, const igraph_vector_int_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; + bool directed = indeg != NULL; - 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); + 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); } } diff --git a/src/vendor/cigraph/src/misc/embedding.c b/src/vendor/cigraph/src/misc/embedding.c index d873ff3c07f..3c4a31496bb 100644 --- a/src/vendor/cigraph/src/misc/embedding.c +++ b/src/vendor/cigraph/src/misc/embedding.c @@ -25,11 +25,14 @@ #include "igraph_adjlist.h" #include "igraph_blas.h" -#include "igraph_centrality.h" #include "igraph_interface.h" #include "igraph_random.h" #include "igraph_structural.h" +#include "core/math.h" + +#include + typedef struct { const igraph_t *graph; const igraph_vector_t *cvec; @@ -42,13 +45,13 @@ typedef struct { /* Adjacency matrix, unweighted, undirected. Eigendecomposition is used */ -static int igraph_i_asembeddingu(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; + igraph_integer_t i, j, nlen; /* to = (A+cD) from */ for (i = 0; i < n; i++) { @@ -56,18 +59,18 @@ static int igraph_i_asembeddingu(igraph_real_t *to, const igraph_real_t *from, nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] += from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Adjacency matrix, weighted, undirected. Eigendecomposition is used. */ -static int igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; @@ -75,7 +78,7 @@ static int igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from, const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = (A+cD) from */ for (i = 0; i < n; i++) { @@ -83,19 +86,19 @@ static int igraph_i_asembeddinguw(igraph_real_t *to, const igraph_real_t *from, 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /* Adjacency matrix, unweighted, directed. SVD. */ -static int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; @@ -103,7 +106,7 @@ static int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, const igraph_vector_t *cvec = data->cvec; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* tmp = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -111,7 +114,7 @@ static int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, 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]; + igraph_integer_t nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += from[nei]; } VECTOR(*tmp)[i] += VECTOR(*cvec)[i] * from[i]; @@ -123,23 +126,23 @@ static int igraph_i_asembedding(igraph_real_t *to, const igraph_real_t *from, nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } to[i] += VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Adjacency matrix, unweighted, directed. SVD, right eigenvectors */ -static int igraph_i_asembedding_right(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; + igraph_integer_t i, j, nlen; /* to = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -147,17 +150,17 @@ static int igraph_i_asembedding_right(igraph_real_t *to, const igraph_real_t *fr nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] += from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Adjacency matrix, weighted, directed. SVD. */ -static int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; @@ -167,7 +170,7 @@ static int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *incs; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* tmp = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -175,8 +178,8 @@ static int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); igraph_real_t w = VECTOR(*weights)[edge]; VECTOR(*tmp)[i] += w * from[nei]; } @@ -189,19 +192,19 @@ static int igraph_i_asembeddingw(igraph_real_t *to, const igraph_real_t *from, 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /* Adjacency matrix, weighted, directed. SVD, right eigenvectors. */ -static int igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; @@ -209,7 +212,7 @@ static int igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *f const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = (A+cD)' from */ for (i = 0; i < n; i++) { @@ -217,25 +220,25 @@ static int igraph_i_asembeddingw_right(igraph_real_t *to, const igraph_real_t *f 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /* Laplacian D-A, unweighted, undirected. Eigendecomposition. */ -static int igraph_i_lsembedding_da(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; + igraph_integer_t i, j, nlen; /* to = (D-A) from */ for (i = 0; i < n; i++) { @@ -243,17 +246,17 @@ static int igraph_i_lsembedding_da(igraph_real_t *to, const igraph_real_t *from, nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] -= from[nei]; } to[i] += VECTOR(*cvec)[i] * from[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian D-A, weighted, undirected. Eigendecomposition. */ -static int igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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; @@ -261,7 +264,7 @@ static int igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from const igraph_vector_t *weights = data->weights; const igraph_t *graph = data->graph; igraph_vector_int_t *incs; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = (D-A) from */ for (i = 0; i < n; i++) { @@ -269,19 +272,19 @@ static int igraph_i_lsembedding_daw(igraph_real_t *to, const igraph_real_t *from 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t 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; + return IGRAPH_SUCCESS; } /* Laplacian DAD, unweighted, undirected. Eigendecomposition. */ -static int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_asembedding_data_t *data = extra; @@ -289,7 +292,7 @@ static int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from const igraph_vector_t *cvec = data->cvec; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = D^1/2 from */ for (i = 0; i < n; i++) { @@ -302,7 +305,7 @@ static int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from 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]; + igraph_integer_t nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -312,10 +315,10 @@ static int igraph_i_lsembedding_dad(igraph_real_t *to, const igraph_real_t *from to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_asembedding_data_t *data = extra; @@ -325,7 +328,7 @@ static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *fro const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *incs; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = D^-1/2 from */ for (i = 0; i < n; i++) { @@ -338,8 +341,8 @@ static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *fro 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); igraph_real_t w = VECTOR(*weights)[edge]; VECTOR(*tmp)[i] += w * to[nei]; } @@ -356,8 +359,8 @@ static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *fro 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_integer_t edge = VECTOR(*incs)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, i); igraph_real_t w = VECTOR(*weights)[edge]; VECTOR(*tmp)[i] += w * to[nei]; } @@ -368,37 +371,34 @@ static int igraph_i_lsembedding_dadw(igraph_real_t *to, const igraph_real_t *fro to[i] = VECTOR(*cvec)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian I-DAD, unweighted, undirected. Eigendecomposition. */ -static int igraph_i_lsembedding_idad(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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++) { + for (int i = 0; i < n; i++) { to[i] = from[i] - to[i]; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lsembedding_idadw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t 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++) { + for (int i = 0; i < n; i++) { to[i] = from[i] - to[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian OAP, unweighted, directed. SVD. */ -static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_asembedding_data_t *data = extra; @@ -408,7 +408,7 @@ static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *fro const igraph_vector_t *deg_out = data->cvec2; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* tmp = O' from */ for (i = 0; i < n; i++) { @@ -421,7 +421,7 @@ static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *fro nlen = igraph_vector_int_size(neis); to[i] = 0.0; for (j = 0; j < nlen; j++) { - int nei = VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; to[i] += VECTOR(*tmp)[nei]; } } @@ -442,7 +442,7 @@ static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *fro nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - int nei = VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -452,11 +452,11 @@ static int igraph_i_lseembedding_oap(igraph_real_t *to, const igraph_real_t *fro to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian OAP, unweighted, directed. SVD, right eigenvectors. */ -static int igraph_i_lseembedding_oap_right(igraph_real_t *to, +static igraph_error_t 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; @@ -465,7 +465,7 @@ static int igraph_i_lseembedding_oap_right(igraph_real_t *to, const igraph_vector_t *deg_out = data->cvec2; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; /* to = O' from */ for (i = 0; i < n; i++) { @@ -478,7 +478,7 @@ static int igraph_i_lseembedding_oap_right(igraph_real_t *to, nlen = igraph_vector_int_size(neis); VECTOR(*tmp)[i] = 0.0; for (j = 0; j < nlen; j++) { - int nei = VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; VECTOR(*tmp)[i] += to[nei]; } } @@ -488,11 +488,11 @@ static int igraph_i_lseembedding_oap_right(igraph_real_t *to, to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian OAP, weighted, directed. SVD. */ -static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *from, +static igraph_error_t igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *from, int n, void *extra) { igraph_i_asembedding_data_t *data = extra; @@ -504,7 +504,9 @@ static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *fr const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; + igraph_integer_t edge, nei; + igraph_real_t w; /* tmp = O' from */ for (i = 0; i < n; i++) { @@ -517,9 +519,9 @@ static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *fr 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]; + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + w = VECTOR(*weights)[edge]; to[i] += w * VECTOR(*tmp)[nei]; } } @@ -540,9 +542,9 @@ static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *fr 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]; + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + w = VECTOR(*weights)[edge]; VECTOR(*tmp)[i] += w * to[nei]; } } @@ -552,11 +554,11 @@ static int igraph_i_lseembedding_oapw(igraph_real_t *to, const igraph_real_t *fr to[i] = VECTOR(*deg_out)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } /* Laplacian OAP, weighted, directed. SVD, right eigenvectors. */ -static int igraph_i_lseembedding_oapw_right(igraph_real_t *to, +static igraph_error_t 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; @@ -567,7 +569,9 @@ static int igraph_i_lseembedding_oapw_right(igraph_real_t *to, const igraph_t *graph = data->graph; igraph_vector_t *tmp = data->tmp; igraph_vector_int_t *neis; - int i, j, nlen; + igraph_integer_t i, j, nlen; + igraph_integer_t edge, nei; + igraph_real_t w; /* to = O' from */ for (i = 0; i < n; i++) { @@ -580,9 +584,9 @@ static int igraph_i_lseembedding_oapw_right(igraph_real_t *to, 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]; + edge = VECTOR(*neis)[j]; + nei = IGRAPH_OTHER(graph, edge, i); + w = VECTOR(*weights)[edge]; VECTOR(*tmp)[i] += w * to[nei]; } } @@ -592,10 +596,10 @@ static int igraph_i_lseembedding_oapw_right(igraph_real_t *to, to[i] = VECTOR(*deg_in)[i] * VECTOR(*tmp)[i]; } - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_spectral_embedding(const igraph_t *graph, +static igraph_error_t igraph_i_spectral_embedding(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -616,7 +620,7 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, igraph_vector_t tmp; igraph_adjlist_t outlist, inlist; igraph_inclist_t eoutlist, einlist; - int i, j, cveclen = igraph_vector_size(cvec); + igraph_integer_t i, j, cveclen = igraph_vector_size(cvec); igraph_i_asembedding_data_t data; igraph_vector_t tmpD; @@ -654,6 +658,14 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, "the number of vertices or scalar", IGRAPH_EINVAL); } + if (vc > INT_MAX) { + IGRAPH_ERROR("Graph too large for ARPACK", IGRAPH_EOVERFLOW); + } + + if (no > INT_MAX) { + IGRAPH_ERROR("Too many eigenvectors requested from ARPACK", IGRAPH_EOVERFLOW); + } + IGRAPH_CHECK(igraph_matrix_resize(X, vc, no)); if (Y) { IGRAPH_CHECK(igraph_matrix_resize(Y, vc, no)); @@ -665,11 +677,10 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, if (Y) { igraph_matrix_null(Y); } - return 0; + return IGRAPH_SUCCESS; } - igraph_vector_init(&tmp, vc); - IGRAPH_FINALLY(igraph_vector_destroy, &tmp); + IGRAPH_VECTOR_INIT_FINALLY(&tmp, vc); if (!weights) { IGRAPH_CHECK(igraph_adjlist_init(graph, &outlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &outlist); @@ -687,9 +698,9 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, } IGRAPH_VECTOR_INIT_FINALLY(&tmpD, no); - options->n = vc; + options->n = (int) vc; options->start = 1; /* no random start vector */ - options->nev = no; + options->nev = (int) no; switch (which) { case IGRAPH_EIGEN_LM: options->which[0] = 'L'; options->which[1] = 'M'; @@ -703,9 +714,9 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, default: break; } - options->ncv = no + 3; - if (options->ncv > vc) { - options->ncv = vc; + options->ncv = options->nev + 3; + if (options->ncv > options->n) { + options->ncv = options->n; } /* We provide a random start vector to ARPACK on our own to ensure that @@ -724,7 +735,7 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, 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); + callback_right(&MATRIX(*Y, 0, i), &MATRIX(*X, 0, i), (int) vc, &data); igraph_vector_view(&v, &MATRIX(*Y, 0, i), vc); norm = 1.0 / igraph_blas_dnrm2(&v); igraph_vector_scale(&v, norm); @@ -795,7 +806,7 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -856,15 +867,15 @@ static int igraph_i_spectral_embedding(const igraph_t *graph, * 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. + * for details. Supply \c NULL to use the defaults. 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_error_t igraph_adjacency_spectral_embedding(const igraph_t *graph, igraph_integer_t n, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -887,6 +898,10 @@ int igraph_adjacency_spectral_embedding(const igraph_t *graph, callback_right = 0; } + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + return igraph_i_spectral_embedding(graph, n, weights, which, scaled, X, Y, D, cvec, /* deg2=*/ 0, options, callback, callback_right, @@ -894,7 +909,7 @@ int igraph_adjacency_spectral_embedding(const igraph_t *graph, /*eigen=*/ !directed, /*zapsmall=*/ 1); } -static int igraph_i_lse_und(const igraph_t *graph, +static igraph_error_t igraph_i_lse_und(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -925,15 +940,14 @@ static int igraph_i_lse_und(const igraph_t *graph, } IGRAPH_VECTOR_INIT_FINALLY(°, 0); - igraph_strength(graph, °, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1, - weights); + IGRAPH_CHECK(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(°); + igraph_integer_t i, n = igraph_vector_size(°); for (i = 0; i < n; i++) { VECTOR(deg)[i] = 1.0 / sqrt(VECTOR(deg)[i]); } @@ -951,10 +965,10 @@ static int igraph_i_lse_und(const igraph_t *graph, igraph_vector_destroy(°); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_lse_dir(const igraph_t *graph, +static igraph_error_t igraph_i_lse_dir(const igraph_t *graph, igraph_integer_t no, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -971,7 +985,7 @@ static int igraph_i_lse_dir(const igraph_t *graph, weights ? igraph_i_lseembedding_oapw_right : igraph_i_lseembedding_oap_right; igraph_vector_t deg_in, deg_out; - int i, n = igraph_vcount(graph); + igraph_integer_t i, n = igraph_vcount(graph); if (type != IGRAPH_EMBEDDING_OAP) { IGRAPH_ERROR("Invalid Laplacian spectral embedding type", IGRAPH_EINVAL); @@ -979,10 +993,8 @@ static int igraph_i_lse_dir(const igraph_t *graph, 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); + IGRAPH_CHECK(igraph_strength(graph, °_in, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1, weights)); + IGRAPH_CHECK(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]); @@ -999,7 +1011,7 @@ static int igraph_i_lse_dir(const igraph_t *graph, igraph_vector_destroy(°_out); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** @@ -1052,17 +1064,17 @@ static int igraph_i_lse_dir(const igraph_t *graph, * 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. + * for details. Supply \c NULL to use the defaults. 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_error_t igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_integer_t n, const igraph_vector_t *weights, igraph_eigen_which_position_t which, @@ -1073,6 +1085,10 @@ int igraph_laplacian_spectral_embedding(const igraph_t *graph, igraph_vector_t *D, igraph_arpack_options_t *options) { + if (options == 0) { + options = igraph_arpack_options_get_default(); + } + if (igraph_is_directed(graph)) { return igraph_i_lse_dir(graph, n, weights, which, type, scaled, X, Y, D, options); @@ -1084,7 +1100,7 @@ int igraph_laplacian_spectral_embedding(const igraph_t *graph, /** * \function igraph_dim_select - * Dimensionality selection + * \brief Dimensionality selection. * * Dimensionality selection for singular values using * profile likelihood. @@ -1117,9 +1133,9 @@ int igraph_laplacian_spectral_embedding(const igraph_t *graph, * \sa \ref igraph_adjacency_spectral_embedding(). */ -int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { +igraph_error_t igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { - int i, n = igraph_vector_size(sv); + igraph_integer_t 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; @@ -1133,7 +1149,7 @@ int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { if (n == 1) { *dim = 1; - return 0; + return IGRAPH_SUCCESS; } for (i = 0; i < n; i++) { @@ -1143,7 +1159,7 @@ int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { } for (i = 0; i < n - 1; i++) { - int n1 = i + 1, n2 = n - i - 1, n1m1 = n1 - 1, n2m1 = n2 - 1; + igraph_integer_t 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; @@ -1181,5 +1197,5 @@ int igraph_dim_select(const igraph_vector_t *sv, igraph_integer_t *dim) { *dim = n; } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/misc/feedback_arc_set.c b/src/vendor/cigraph/src/misc/feedback_arc_set.c index 5f753a36e45..1cead4cf002 100644 --- a/src/vendor/cigraph/src/misc/feedback_arc_set.c +++ b/src/vendor/cigraph/src/misc/feedback_arc_set.c @@ -22,27 +22,26 @@ */ +#include "igraph_structural.h" +#include "misc/feedback_arc_set.h" + #include "igraph_components.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_structural.h" +#include "igraph_vector_list.h" #include "igraph_visitor.h" #include "internal/glpk_support.h" -#include "misc/feedback_arc_set.h" - -int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, - const igraph_vector_t *weights); +#include "math/safe_intop.h" +#include /** * \ingroup structural * \function igraph_feedback_arc_set - * \brief Calculates a feedback arc set of the graph using different - * algorithms. + * \brief Feedback arc set of a graph using exact or heuristic methods. * - * * 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. @@ -81,7 +80,7 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, * * Time complexity: depends on \p algo, see the time complexities there. */ -int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, +igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_int_t *result, const igraph_vector_t *weights, igraph_fas_algorithm_t algo) { if (weights && igraph_vector_size(weights) < igraph_ecount(graph)) @@ -107,17 +106,19 @@ int igraph_feedback_arc_set(const igraph_t *graph, igraph_vector_t *result, /** * 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_error_t igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + IGRAPH_VECTOR_INT_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_CHECK(igraph_vector_init_copy(&vcopy, weights)); IGRAPH_FINALLY(igraph_vector_destroy, &vcopy); igraph_vector_scale(&vcopy, -1); IGRAPH_CHECK(igraph_minimum_spanning_tree(graph, &edges, &vcopy)); @@ -131,32 +132,30 @@ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t /* 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 */ + igraph_vector_int_sort(&edges); + IGRAPH_CHECK(igraph_vector_int_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 (result) { + igraph_vector_int_clear(result); + for (igraph_integer_t i = 0, j = 0; i < no_of_edges; i++) { if (i == VECTOR(edges)[j]) { j++; continue; } - IGRAPH_CHECK(igraph_vector_push_back(result, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(result, i)); } } - if (layering != 0) { + if (layering) { igraph_vector_t degrees; - igraph_vector_t roots; + igraph_vector_int_t roots; IGRAPH_VECTOR_INIT_FINALLY(°rees, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&roots, no_of_nodes); - + IGRAPH_VECTOR_INT_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_ALL, /* loops */ false, weights)); + IGRAPH_CHECK(igraph_vector_qsort_ind(°rees, &roots, IGRAPH_DESCENDING)); + IGRAPH_CHECK(igraph_bfs(graph, /* root = */ 0, /* roots = */ &roots, @@ -165,7 +164,7 @@ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t /* restricted = */ 0, /* order = */ 0, /* rank = */ 0, - /* father = */ 0, + /* parents = */ 0, /* pred = */ 0, /* succ = */ 0, /* dist = */ layering, @@ -173,11 +172,11 @@ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t /* extra = */ 0)); igraph_vector_destroy(°rees); - igraph_vector_destroy(&roots); + igraph_vector_int_destroy(&roots); IGRAPH_FINALLY_CLEAN(2); } - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -186,29 +185,32 @@ int igraph_i_feedback_arc_set_undirected(const igraph_t *graph, igraph_vector_t /** * 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_error_t igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layers) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, k, v, eid, nodes_left; + igraph_dqueue_int_t sources, sinks; + igraph_vector_int_t neis; + igraph_vector_int_t indegrees, outdegrees; igraph_vector_t instrengths, outstrengths; - long int* ordering; - long int order_next_pos = 0, order_next_neg = -1; + igraph_integer_t *ordering; + igraph_integer_t order_next_pos = 0, order_next_neg = -1; igraph_real_t diff, maxdiff; - ordering = IGRAPH_CALLOC(no_of_nodes, long int); + ordering = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(ordering, "Insufficient memory for finding feedback arc set."); IGRAPH_FINALLY(igraph_free, ordering); - IGRAPH_VECTOR_INIT_FINALLY(&indegrees, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&outdegrees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&indegrees, no_of_nodes); + IGRAPH_VECTOR_INT_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_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); + IGRAPH_CHECK(igraph_dqueue_int_init(&sinks, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_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)); @@ -217,8 +219,12 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu 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)); + IGRAPH_CHECK(igraph_vector_resize(&instrengths, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(&outstrengths, no_of_nodes)); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(instrengths)[i] = VECTOR(indegrees)[i]; + VECTOR(outstrengths)[i] = VECTOR(outdegrees)[i]; + } } /* Find initial sources and sinks */ @@ -232,29 +238,29 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu VECTOR(indegrees)[i] = VECTOR(outdegrees)[i] = -1; } else { /* This is a source */ - igraph_dqueue_push(&sources, i); + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, i)); } } else if (VECTOR(outdegrees)[i] == 0) { /* This is a sink */ - igraph_dqueue_push(&sinks, i); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&sources)) { + i = igraph_dqueue_int_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_CHECK(igraph_incident(graph, &neis, i, IGRAPH_OUT)); - j = igraph_vector_size(&neis); + j = igraph_vector_int_size(&neis); for (i = 0; i < j; i++) { - eid = (long int) VECTOR(neis)[i]; + eid = VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ @@ -263,15 +269,15 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, k)); } } nodes_left--; } /* (2) Remove the sinks one by one */ - while (!igraph_dqueue_empty(&sinks)) { - i = (long)igraph_dqueue_pop(&sinks); + while (!igraph_dqueue_int_empty(&sinks)) { + i = igraph_dqueue_int_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) { @@ -282,11 +288,11 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu /* 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_CHECK(igraph_incident(graph, &neis, i, IGRAPH_IN)); - j = igraph_vector_size(&neis); + j = igraph_vector_int_size(&neis); for (i = 0; i < j; i++) { - eid = (long int) VECTOR(neis)[i]; + eid = VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ @@ -295,7 +301,7 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu VECTOR(outdegrees)[k]--; VECTOR(outstrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(outdegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&sinks, k)); + IGRAPH_CHECK(igraph_dqueue_int_push(&sinks, k)); } } nodes_left--; @@ -318,11 +324,11 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu /* Remove vertex v */ ordering[v] = order_next_pos++; /* Remove outgoing edges */ - IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, + IGRAPH_CHECK(igraph_incident(graph, &neis, v, IGRAPH_OUT)); - j = igraph_vector_size(&neis); + j = igraph_vector_int_size(&neis); for (i = 0; i < j; i++) { - eid = (long int) VECTOR(neis)[i]; + eid = VECTOR(neis)[i]; k = IGRAPH_TO(graph, eid); if (VECTOR(indegrees)[k] <= 0) { /* Already removed, continue */ @@ -331,15 +337,15 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu VECTOR(indegrees)[k]--; VECTOR(instrengths)[k] -= (weights ? VECTOR(*weights)[eid] : 1.0); if (VECTOR(indegrees)[k] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&sources, k)); + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, k)); } } /* Remove incoming edges */ - IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) v, + IGRAPH_CHECK(igraph_incident(graph, &neis, v, IGRAPH_IN)); - j = igraph_vector_size(&neis); + j = igraph_vector_int_size(&neis); for (i = 0; i < j; i++) { - eid = (long int) VECTOR(neis)[i]; + eid = VECTOR(neis)[i]; k = IGRAPH_FROM(graph, eid); if (VECTOR(outdegrees)[k] <= 0) { /* Already removed, continue */ @@ -348,7 +354,7 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&sinks, k)); } } @@ -358,13 +364,13 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu } } - igraph_dqueue_destroy(&sinks); - igraph_dqueue_destroy(&sources); - igraph_vector_destroy(&neis); + igraph_dqueue_int_destroy(&sinks); + igraph_dqueue_int_destroy(&sources); + igraph_vector_int_destroy(&neis); igraph_vector_destroy(&outstrengths); igraph_vector_destroy(&instrengths); - igraph_vector_destroy(&outdegrees); - igraph_vector_destroy(&indegrees); + igraph_vector_int_destroy(&outdegrees); + igraph_vector_int_destroy(&indegrees); IGRAPH_FINALLY_CLEAN(7); /* Tidy up the ordering */ @@ -375,39 +381,37 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu } /* 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 (result) { + igraph_vector_int_clear(result); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_vector_int_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; + if (layers) { + igraph_vector_int_t ranks; + igraph_vector_int_t order_vec; - IGRAPH_CHECK(igraph_vector_resize(layers, no_of_nodes)); - igraph_vector_null(layers); + IGRAPH_CHECK(igraph_vector_int_resize(layers, no_of_nodes)); + igraph_vector_int_null(layers); - igraph_vector_long_view(&order_vec, ordering, no_of_nodes); + igraph_vector_int_view(&order_vec, ordering, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_INIT_FINALLY(&ranks, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ranks, 0); - IGRAPH_CHECK((int) igraph_vector_long_qsort_ind(&order_vec, &ranks, 0)); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(&order_vec, &ranks, IGRAPH_ASCENDING)); 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); + igraph_integer_t from = VECTOR(ranks)[i]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, from, IGRAPH_OUT)); + k = igraph_vector_int_size(&neis); for (j = 0; j < k; j++) { - long int to = (long int) VECTOR(neis)[j]; + igraph_integer_t to = VECTOR(neis)[j]; if (from == to) { continue; } @@ -420,13 +424,13 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu } } - igraph_vector_destroy(&neis); - igraph_vector_destroy(&ranks); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&ranks); IGRAPH_FINALLY_CLEAN(2); } /* Free the ordering vector */ - igraph_free(ordering); + IGRAPH_FREE(ordering); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -435,68 +439,44 @@ int igraph_i_feedback_arc_set_eades(const igraph_t *graph, igraph_vector_t *resu /** * Solves the feedback arc set problem using integer programming. */ -int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, +igraph_error_t igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_int_t *result, const igraph_vector_t *weights) { #ifndef HAVE_GLPK - IGRAPH_ERROR("GLPK is not available", IGRAPH_UNIMPLEMENTED); + 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_vector_int_t membership, *vec; + igraph_vector_int_t ordering, vertex_remapping; + igraph_vector_int_list_t vertices_by_components, edges_by_components; + igraph_integer_t i, j, k, l, m, n, from, to, no_of_rows, n_choose_2; 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_INT_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ordering, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_remapping, no_of_vertices); - igraph_vector_clear(result); + igraph_vector_int_clear(result); /* Decompose the graph into connected components */ - IGRAPH_CHECK(igraph_clusters(graph, &membership, 0, &no_of_components, - IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&vertices_by_components, no_of_components); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edges_by_components, no_of_components); 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)); + j = VECTOR(membership)[i]; + vec = igraph_vector_int_list_get_ptr(&vertices_by_components, j); + IGRAPH_CHECK(igraph_vector_int_push_back(vec, 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)); + j = VECTOR(membership)[IGRAPH_FROM(graph, i)]; + vec = igraph_vector_int_list_get_ptr(&edges_by_components, j); + IGRAPH_CHECK(igraph_vector_int_push_back(vec, i)); } #define VAR2IDX(i, j) (i*(n-1)+j-(i+1)*i/2) @@ -513,8 +493,8 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, /* 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]; + igraph_vector_int_t *vertices_in_comp = igraph_vector_int_list_get_ptr(&vertices_by_components, i); + igraph_vector_int_t *edges_in_comp = igraph_vector_int_list_get_ptr(&edges_by_components, i); /* * Let x_ij denote whether layer(i) < layer(j). @@ -540,31 +520,35 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, * * max sum_{i 0) { - glp_add_cols(ip, (int) k); - for (j = 1; j <= k; j++) { + IGRAPH_SAFE_N_CHOOSE_2(n, &n_choose_2); + if (n_choose_2 > INT_MAX) { + IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + + if (n_choose_2 > 0) { + glp_add_cols(ip, (int) n_choose_2); + for (j = 1; j <= n_choose_2; j++) { glp_set_col_kind(ip, (int) j, GLP_BV); } } /* Set up coefficients in the goal function */ - k = igraph_vector_size(edges_in_comp); + k = igraph_vector_int_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)]; + l = VECTOR(*edges_in_comp)[j]; + from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; + to = VECTOR(vertex_remapping)[IGRAPH_TO(graph, l)]; if (from == to) { continue; } @@ -582,7 +566,27 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, /* Add constraints */ if (n > 1) { - glp_add_rows(ip, (int)(n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3)); + { + /* Overflow-safe block for: + * no_of_rows = n * (n - 1) / 2 + n * (n - 1) * (n - 2) / 3 + */ + + /* res = n * (n - 1) * (n - 2) / 3 */ + igraph_integer_t mod = n % 3; + igraph_integer_t res = n / 3; /* same as (n - mod) / 3 */ + + mod = (mod + 1) % 3; + IGRAPH_SAFE_MULT(res, n - mod, &res); + mod = (mod + 1) % 3; + IGRAPH_SAFE_MULT(res, n - mod, &res); + + /* no_of_rows = n * (n - 1) / 2 + res */ + IGRAPH_SAFE_ADD(n_choose_2, res, &no_of_rows); + } + if (no_of_rows > INT_MAX) { + IGRAPH_ERROR("Feedback arc set problem too large for GLPK.", IGRAPH_EOVERFLOW); + } + glp_add_rows(ip, (int) no_of_rows); m = 1; for (j = 0; j < n; j++) { int ind[4]; @@ -613,11 +617,10 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, 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; + IGRAPH_CHECK(igraph_vector_int_resize(&ordering, n)); + igraph_vector_int_null(&ordering); j = 0; k = 1; - for (l = 1; l <= m; l++) { + for (l = 1; l <= n_choose_2; 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) { @@ -634,13 +637,13 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, } /* Find the feedback edges */ - k = igraph_vector_size(edges_in_comp); + k = igraph_vector_int_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)]; + l = VECTOR(*edges_in_comp)[j]; + from = VECTOR(vertex_remapping)[IGRAPH_FROM(graph, l)]; + to = VECTOR(vertex_remapping)[IGRAPH_TO(graph, l)]; if (from == to || VECTOR(ordering)[from] < VECTOR(ordering)[to]) { - IGRAPH_CHECK(igraph_vector_push_back(result, l)); + IGRAPH_CHECK(igraph_vector_int_push_back(result, l)); } } @@ -649,11 +652,11 @@ int igraph_i_feedback_arc_set_ip(const igraph_t *graph, igraph_vector_t *result, 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_vector_int_list_destroy(&vertices_by_components); + igraph_vector_int_list_destroy(&edges_by_components); + igraph_vector_int_destroy(&vertex_remapping); + igraph_vector_int_destroy(&ordering); + igraph_vector_int_destroy(&membership); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/misc/feedback_arc_set.h b/src/vendor/cigraph/src/misc/feedback_arc_set.h index 7091e1f3ab9..7d34eb0b60c 100644 --- a/src/vendor/cigraph/src/misc/feedback_arc_set.h +++ b/src/vendor/cigraph/src/misc/feedback_arc_set.h @@ -29,10 +29,17 @@ __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); +igraph_error_t igraph_i_feedback_arc_set_eades( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering +); +igraph_error_t igraph_i_feedback_arc_set_ip( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights); +igraph_error_t igraph_i_feedback_arc_set_undirected( + const igraph_t *graph, igraph_vector_int_t *result, + const igraph_vector_t *weights, igraph_vector_int_t *layering +); __END_DECLS diff --git a/src/vendor/cigraph/src/misc/graphicality.c b/src/vendor/cigraph/src/misc/graphicality.c index f468d348008..24d092de2e4 100644 --- a/src/vendor/cigraph/src/misc/graphicality.c +++ b/src/vendor/cigraph/src/misc/graphicality.c @@ -25,18 +25,18 @@ #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 igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_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 igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res); +static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_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); +static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res); +static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res); /** @@ -113,8 +113,8 @@ static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const * 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, +igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, + const igraph_vector_int_t *in_degrees, const igraph_edge_type_sw_t allowed_edge_types, igraph_bool_t *res) { @@ -147,6 +147,10 @@ int igraph_is_graphical(const igraph_vector_t *out_degrees, /* Directed case: */ else { + if (igraph_vector_int_size(in_degrees) != igraph_vector_int_size(out_degrees)) { + IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); + } + 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); } @@ -216,8 +220,8 @@ int igraph_is_graphical(const igraph_vector_t *out_degrees, * 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, +igraph_error_t igraph_is_bigraphical(const igraph_vector_int_t *degrees1, + const igraph_vector_int_t *degrees2, const igraph_edge_type_sw_t allowed_edge_types, igraph_bool_t *res) { @@ -238,16 +242,16 @@ int igraph_is_bigraphical(const igraph_vector_t *degrees1, * * 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; +static igraph_error_t igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_integer_t sum_parity = 0; /* 0 if the degree sum is even, 1 if it is odd */ + igraph_integer_t n = igraph_vector_int_size(degrees); + igraph_integer_t i; - for (i=0; i < n; ++i) { - long int d = VECTOR(*degrees)[i]; + for (i = 0; i < n; ++i) { + igraph_integer_t d = VECTOR(*degrees)[i]; if (d < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } sum_parity = (sum_parity + d) & 1; @@ -264,23 +268,23 @@ static int igraph_i_is_graphical_undirected_multi_loops(const igraph_vector_t *d * - 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; +static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_integer_t i; + igraph_integer_t n = igraph_vector_int_size(degrees); + igraph_integer_t dsum, dmax; /* Zero-length sequences are considered graphical. */ if (n == 0) { - *res = 1; + *res = true; return IGRAPH_SUCCESS; } dsum = 0; dmax = 0; - for (i=0; i < n; ++i) { - long int d = VECTOR(*degrees)[i]; + for (i = 0; i < n; ++i) { + igraph_integer_t d = VECTOR(*degrees)[i]; if (d < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } dsum += d; @@ -300,15 +304,15 @@ static int igraph_i_is_graphical_undirected_loopless_multi(const igraph_vector_t * - 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; +static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_int_t *degrees, igraph_bool_t *res) { + igraph_vector_int_t work; + igraph_integer_t w, b, s, c, n, k; - n = igraph_vector_size(degrees); + n = igraph_vector_int_size(degrees); /* Zero-length sequences are considered graphical. */ if (n == 0) { - *res = 1; + *res = true; return IGRAPH_SUCCESS; } @@ -342,12 +346,12 @@ static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t * * 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_CHECK(igraph_vector_int_init_copy(&work, degrees)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &work); - igraph_vector_reverse_sort(&work); + igraph_vector_int_reverse_sort(&work); - *res = 1; + *res = true; w = n - 1; b = 0; s = 0; c = 0; for (k = 0; k < n; k++) { b += VECTOR(work)[k]; @@ -358,7 +362,7 @@ static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t * w--; } if (b > c + s + 2*(k + 1)) { - *res = 0; + *res = false; break; } if (w == k) { @@ -366,7 +370,7 @@ static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t * } } - igraph_vector_destroy(&work); + igraph_vector_int_destroy(&work); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -378,17 +382,17 @@ static int igraph_i_is_graphical_undirected_loopy_simple(const igraph_vector_t * * - 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) { +static igraph_error_t igraph_i_is_graphical_undirected_simple(const igraph_vector_int_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; + const igraph_integer_t p = igraph_vector_int_size(degrees); + igraph_integer_t dmin, dmax, dsum; + igraph_integer_t n; /* number of non-zero degrees */ + igraph_integer_t k, sum_deg, sum_ni, sum_ini; + igraph_integer_t i, dk; + igraph_integer_t zverovich_bound; if (p == 0) { - *res = 1; + *res = true; return IGRAPH_SUCCESS; } @@ -405,11 +409,11 @@ static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degree 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]; + for (i = 0; i < p; ++i) { + igraph_integer_t d = VECTOR(*degrees)[i]; if (d < 0 || d >= p) { - *res = 0; + *res = false; goto finish; } @@ -423,12 +427,12 @@ static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degree } if (dsum % 2 != 0) { - *res = 0; + *res = false; goto finish; } if (n == 0) { - *res = 1; + *res = true; goto finish; /* all degrees are zero => graphical */ } @@ -456,16 +460,16 @@ static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degree } if (dmin*n >= zverovich_bound) { - *res = 1; + *res = true; goto finish; } k = 0; sum_deg = 0; sum_ni = 0; sum_ini = 0; for (dk = dmax; dk >= dmin; --dk) { - long int run_size, v; + igraph_integer_t run_size, v; if (dk < k+1) { - *res = 1; + *res = true; goto finish; } @@ -481,13 +485,13 @@ static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degree } k += run_size; if (sum_deg > k*(n-1) - k*sum_ni + sum_ini) { - *res = 0; + *res = false; goto finish; } } } - *res = 1; + *res = true; finish: igraph_vector_int_destroy(&num_degs); @@ -503,22 +507,20 @@ static int igraph_i_is_graphical_undirected_simple(const igraph_vector_t *degree * - 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; +static igraph_error_t igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_integer_t sumdiff; /* difference between sum of in- and out-degrees */ + igraph_integer_t n = igraph_vector_int_size(out_degrees); + igraph_integer_t i; - if (igraph_vector_size(in_degrees) != n) { - IGRAPH_ERROR("The length of out- and in-degree sequences must be the same.", IGRAPH_EINVAL); - } + IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); sumdiff = 0; - for (i=0; i < n; ++i) { - long int dout = VECTOR(*out_degrees)[i]; - long int din = VECTOR(*in_degrees)[i]; + for (i = 0; i < n; ++i) { + igraph_integer_t dout = VECTOR(*out_degrees)[i]; + igraph_integer_t din = VECTOR(*in_degrees)[i]; if (dout < 0 || din < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } @@ -537,23 +539,21 @@ static int igraph_i_is_graphical_directed_loopy_multi(const igraph_vector_t *out * - 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); +static igraph_error_t igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_integer_t i, sumin, sumout, dmax; + igraph_integer_t n = igraph_vector_int_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); - } + IGRAPH_ASSERT(igraph_vector_int_size(in_degrees) == n); 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; + for (i = 0; i < n; ++i) { + igraph_integer_t dout = VECTOR(*out_degrees)[i]; + igraph_integer_t din = VECTOR(*in_degrees)[i]; + igraph_integer_t d = dout + din; if (dout < 0 || din < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } @@ -574,13 +574,7 @@ static int igraph_i_is_graphical_directed_loopless_multi(const igraph_vector_t * * - 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); - } - +static igraph_error_t igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { return igraph_i_is_bigraphical_simple(out_degrees, in_degrees, res); } @@ -592,15 +586,15 @@ static int igraph_i_is_graphical_directed_loopy_simple(const igraph_vector_t *ou */ typedef struct { - const igraph_vector_t* first; - const igraph_vector_t* second; + const igraph_vector_int_t* first; + const igraph_vector_int_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); + igraph_integer_t index1 = *((igraph_integer_t*)p1); + igraph_integer_t index2 = *((igraph_integer_t*)p2); if (VECTOR(*sort_data->first)[index1] < VECTOR(*sort_data->first)[index2]) { return 1; } @@ -616,9 +610,9 @@ static int igraph_i_qsort_dual_vector_cmp_desc(void* data, const void *p1, const 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; +static igraph_error_t igraph_i_is_graphical_directed_simple(const igraph_vector_int_t *out_degrees, const igraph_vector_int_t *in_degrees, igraph_bool_t *res) { + igraph_vector_int_t index_array; + igraph_integer_t 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. */ @@ -627,22 +621,22 @@ static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degr return IGRAPH_SUCCESS; } - vcount = igraph_vector_size(out_degrees); + vcount = igraph_vector_int_size(out_degrees); if (vcount == 0) { - *res = 1; + *res = true; 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); + IGRAPH_CHECK(igraph_vector_int_init_range(&index_array, 0, vcount)); + IGRAPH_FINALLY(igraph_vector_int_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_qsort_r(VECTOR(index_array), vcount, sizeof(VECTOR(index_array)[0]), &sort_data, igraph_i_qsort_dual_vector_cmp_desc); /* Be optimistic, then check whether the Fulkerson–Chen–Anstee condition @@ -657,7 +651,7 @@ static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degr #define INDEGREE(x) (VECTOR(*in_degrees)[VECTOR(index_array)[x]]) #define OUTDEGREE(x) (VECTOR(*out_degrees)[VECTOR(index_array)[x]]) - *res = 1; + *res = true; lhs = 0; for (i = 0; i < vcount; i++) { lhs += INDEGREE(i); @@ -678,7 +672,7 @@ static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degr } if (lhs > rhs) { - *res = 0; + *res = false; break; } } @@ -686,7 +680,7 @@ static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degr #undef INDEGREE #undef OUTDEGREE - igraph_vector_long_destroy(&index_array); + igraph_vector_int_destroy(&index_array); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -700,17 +694,17 @@ static int igraph_i_is_graphical_directed_simple(const igraph_vector_t *out_degr * - 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); +static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { + igraph_integer_t i; + igraph_integer_t sum1, sum2; + igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); sum1 = 0; - for (i=0; i < n1; ++i) { - long int d = VECTOR(*degrees1)[i]; + for (i = 0; i < n1; ++i) { + igraph_integer_t d = VECTOR(*degrees1)[i]; if (d < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } @@ -718,11 +712,11 @@ static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const } sum2 = 0; - for (i=0; i < n2; ++i) { - long int d = VECTOR(*degrees2)[i]; + for (i = 0; i < n2; ++i) { + igraph_integer_t d = VECTOR(*degrees2)[i]; if (d < 0) { - *res = 0; + *res = false; return IGRAPH_SUCCESS; } @@ -740,14 +734,14 @@ static int igraph_i_is_bigraphical_multi(const igraph_vector_t *degrees1, const * - 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; +static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t *degrees1, const igraph_vector_int_t *degrees2, igraph_bool_t *res) { + igraph_vector_int_t sorted_deg1, sorted_deg2; + igraph_integer_t n1 = igraph_vector_int_size(degrees1), n2 = igraph_vector_int_size(degrees2); + igraph_integer_t i, k; + igraph_integer_t lhs_sum, partial_rhs_sum; if (n1 == 0 && n2 == 0) { - *res = 1; + *res = true; return IGRAPH_SUCCESS; } @@ -759,8 +753,8 @@ static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const /* Ensure that degrees1 is the shorter vector as a minor optimization: */ if (n2 < n1) { - const igraph_vector_t *tmp; - long int n; + const igraph_vector_int_t *tmp; + igraph_integer_t n; tmp = degrees1; degrees1 = degrees2; @@ -773,13 +767,13 @@ static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const /* 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_int_init_copy(&sorted_deg1, degrees1)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sorted_deg1); + igraph_vector_int_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 */ + IGRAPH_CHECK(igraph_vector_int_init_copy(&sorted_deg2, degrees2)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &sorted_deg2); + igraph_vector_int_sort(&sorted_deg2); /* increasing sort */ /* * We follow the description of the Gale-Ryser theorem in: @@ -803,11 +797,11 @@ static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const * of the inequality's right-hand-side. */ - *res = 1; /* be optimistic */ + *res = true; /* 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) { + 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 @@ -823,137 +817,14 @@ static int igraph_i_is_bigraphical_simple(const igraph_vector_t *degrees1, const /* 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; + *res = false; break; } } - igraph_vector_destroy(&sorted_deg2); - igraph_vector_destroy(&sorted_deg1); + igraph_vector_int_destroy(&sorted_deg2); + igraph_vector_int_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/vendor/cigraph/src/misc/matching.c b/src/vendor/cigraph/src/misc/matching.c index 7e58bd01e80..e0555cbd7ee 100644 --- a/src/vendor/cigraph/src/misc/matching.c +++ b/src/vendor/cigraph/src/misc/matching.c @@ -83,15 +83,15 @@ static void debug(const char* fmt, ...) { * * \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_error_t igraph_is_matching(const igraph_t *graph, + const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, + igraph_bool_t *result) { + igraph_integer_t 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; + if (igraph_vector_int_size(matching) != no_of_nodes) { + *result = false; return IGRAPH_SUCCESS; } for (i = 0; i < no_of_nodes; i++) { @@ -99,7 +99,7 @@ int igraph_is_matching(const igraph_t* graph, /* Checking range of each element in the match vector */ if (j < -1 || j >= no_of_nodes) { - *result = 0; return IGRAPH_SUCCESS; + *result = false; return IGRAPH_SUCCESS; } /* When i is unmatched, we're done */ if (j == -1) { @@ -107,17 +107,17 @@ int igraph_is_matching(const igraph_t* graph, } /* Matches must be mutual */ if (VECTOR(*matching)[j] != i) { - *result = 0; return IGRAPH_SUCCESS; + *result = false; return IGRAPH_SUCCESS; } /* Matched vertices must be connected */ - IGRAPH_CHECK(igraph_are_connected(graph, (igraph_integer_t) i, - (igraph_integer_t) j, &conn)); + IGRAPH_CHECK(igraph_are_connected(graph, i, + 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)); + IGRAPH_CHECK(igraph_are_connected(graph, j, + i, &conn)); if (!conn) { - *result = 0; return IGRAPH_SUCCESS; + *result = false; return IGRAPH_SUCCESS; } } } @@ -130,12 +130,12 @@ int igraph_is_matching(const igraph_t* graph, continue; } if (VECTOR(*types)[i] == VECTOR(*types)[j]) { - *result = 0; return IGRAPH_SUCCESS; + *result = false; return IGRAPH_SUCCESS; } } } - *result = 1; + *result = true; return IGRAPH_SUCCESS; } @@ -165,19 +165,20 @@ int igraph_is_matching(const igraph_t* graph, * * \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_error_t igraph_is_maximal_matching(const igraph_t *graph, + const igraph_vector_bool_t *types, const igraph_vector_int_t *matching, + igraph_bool_t *result) { + + igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; igraph_bool_t valid; IGRAPH_CHECK(igraph_is_matching(graph, types, matching, &valid)); if (!valid) { - *result = 0; return IGRAPH_SUCCESS; + *result = false; return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); valid = 1; for (i = 0; i < no_of_nodes; i++) { @@ -186,35 +187,36 @@ int igraph_is_maximal_matching(const igraph_t* graph, continue; } - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_ALL)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - if (VECTOR(*matching)[(long int)VECTOR(neis)[j]] == -1) { + if (VECTOR(*matching)[VECTOR(neis)[j]] == -1) { if (types == 0 || - VECTOR(*types)[i] != VECTOR(*types)[(long int)VECTOR(neis)[j]]) { + VECTOR(*types)[i] != VECTOR(*types)[VECTOR(neis)[j]]) { valid = 0; break; } } } } - igraph_vector_destroy(&neis); + igraph_vector_int_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); +static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_integer_t *matching_size, + igraph_vector_int_t *matching); + +static igraph_error_t 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_int_t *matching, + const igraph_vector_t *weights, igraph_real_t eps); #define MATCHED(v) (VECTOR(match)[v] != -1) #define UNMATCHED(v) (!MATCHED(v)) @@ -280,10 +282,10 @@ static int igraph_i_maximum_bipartite_matching_weighted( * * \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) { +igraph_error_t 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_int_t *matching, + const igraph_vector_t *weights, igraph_real_t eps) { /* Sanity checks */ if (igraph_vector_bool_size(types) < igraph_vcount(graph)) { @@ -307,10 +309,10 @@ int igraph_maximum_bipartite_matching(const igraph_t* graph, } } -static int igraph_i_maximum_bipartite_matching_unweighted_relabel( +static igraph_error_t 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); + const igraph_vector_bool_t* types, igraph_vector_int_t* labels, + igraph_vector_int_t* matching, igraph_bool_t smaller_set); /** * Finding maximum bipartite matchings on bipartite graphs using the @@ -325,19 +327,19 @@ static int igraph_i_maximum_bipartite_matching_unweighted_relabel( * 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 */ +static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_integer_t *matching_size, + igraph_vector_int_t *matching) { + igraph_integer_t i, j, k, n, no_of_nodes = igraph_vcount(graph); + igraph_integer_t num_matched; /* number of matched vertex pairs */ + igraph_vector_int_t match; /* will store the matching */ + igraph_vector_int_t labels; /* will store the labels */ + igraph_vector_int_t neis; /* used to retrieve the neighbors of a node */ + igraph_dqueue_int_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; + igraph_integer_t label_changed = 0; /* Counter to decide when to run a global relabeling */ + igraph_integer_t relabeling_freq = no_of_nodes / 2; /* We will use: * - FIFO push ordering @@ -346,15 +348,15 @@ static int igraph_i_maximum_bipartite_matching_unweighted( */ /* (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); + IGRAPH_CHECK(igraph_vector_int_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &match); + IGRAPH_VECTOR_INT_INIT_FINALLY(&labels, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); /* (2) Initially, every node is unmatched */ - igraph_vector_long_fill(&match, -1); + igraph_vector_int_fill(&match, -1); /* (3) Find an initial matching in a greedy manner. * At the same time, find which side of the graph is smaller. */ @@ -366,11 +368,11 @@ static int igraph_i_maximum_bipartite_matching_unweighted( if (MATCHED(i)) { continue; } - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_ALL)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - k = (long int) VECTOR(neis)[j]; + k = VECTOR(neis)[j]; if (VECTOR(*types)[k] == VECTOR(*types)[i]) { IGRAPH_ERROR("Graph is not bipartite with supplied types vector", IGRAPH_EINVAL); } @@ -392,16 +394,16 @@ static int igraph_i_maximum_bipartite_matching_unweighted( /* (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)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); /* Line 13 */ + igraph_integer_t u = -1, label_u = 2 * no_of_nodes; + igraph_integer_t w; if (label_changed >= relabeling_freq) { /* Run global relabeling */ @@ -413,13 +415,13 @@ static int igraph_i_maximum_bipartite_matching_unweighted( 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_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_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]; + if (VECTOR(labels)[VECTOR(neis)[i]] < label_u) { + u = VECTOR(neis)[i]; + label_u = VECTOR(labels)[u]; label_changed++; } } @@ -433,7 +435,7 @@ static int igraph_i_maximum_bipartite_matching_unweighted( 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 */ + IGRAPH_CHECK(igraph_dqueue_int_push(&q, w)); /* Line 10 */ debug(" Unmatching & activating vertex %ld\n", w); num_matched--; } @@ -447,73 +449,73 @@ static int igraph_i_maximum_bipartite_matching_unweighted( /* Fill the output parameters */ if (matching != 0) { - IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); + IGRAPH_CHECK(igraph_vector_int_update(matching, &match)); } if (matching_size != 0) { - *matching_size = (igraph_integer_t) num_matched; + *matching_size = num_matched; } /* Release everything */ - igraph_dqueue_long_destroy(&q); - igraph_vector_destroy(&neis); - igraph_vector_destroy(&labels); - igraph_vector_long_destroy(&match); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(&labels); + igraph_vector_int_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; +static igraph_error_t igraph_i_maximum_bipartite_matching_unweighted_relabel( + const igraph_t *graph, + const igraph_vector_bool_t *types, igraph_vector_int_t *labels, + igraph_vector_int_t *match, igraph_bool_t smaller_set) { + igraph_integer_t i, j, n, no_of_nodes = igraph_vcount(graph), matched_to; + igraph_dqueue_int_t q; + igraph_vector_int_t neis; debug("Running global relabeling.\n"); /* Set all the labels to no_of_nodes first */ - igraph_vector_fill(labels, no_of_nodes); + igraph_vector_int_fill(labels, no_of_nodes); /* Allocate vector for neighbors */ - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_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)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t v = igraph_dqueue_int_pop(&q); + igraph_integer_t w; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, IGRAPH_ALL)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - w = (long int) VECTOR(neis)[j]; + w = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, matched_to)); VECTOR(*labels)[matched_to] = VECTOR(*labels)[w] + 1; } } } } - igraph_dqueue_long_destroy(&q); - igraph_vector_destroy(&neis); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -533,25 +535,25 @@ static int igraph_i_maximum_bipartite_matching_unweighted_relabel( * 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; +static igraph_error_t 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_int_t *matching, + const igraph_vector_t *weights, igraph_real_t eps) { + igraph_integer_t 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_int_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_int_t parent; /* parent vertices during a BFS */ + igraph_vector_int_t vec1, vec2; /* general temporary vectors */ igraph_vector_t labels; /* will store the labels */ - igraph_dqueue_long_t q; /* a FIFO for BST */ + igraph_dqueue_int_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_integer_t smaller_set_size; /* size of the smaller set */ + igraph_integer_t 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 */ @@ -574,18 +576,18 @@ static int igraph_i_maximum_bipartite_matching_weighted( } /* (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_int_init(&match, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_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_INT_INIT_FINALLY(&vec1, 0); + IGRAPH_VECTOR_INT_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_dqueue_int_init(&q, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); + IGRAPH_VECTOR_INT_INIT_FINALLY(&parent, no_of_nodes); IGRAPH_CHECK(igraph_adjlist_init_empty(&tight_phantom_edges, - (igraph_integer_t) no_of_nodes)); + 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); @@ -627,7 +629,7 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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]; + k = 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); @@ -641,24 +643,24 @@ static int igraph_i_maximum_bipartite_matching_weighted( dual += max_weight; } - igraph_vector_clear(&vec1); + igraph_vector_int_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]; + u = VECTOR(vec2)[j]; + v = 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_CHECK(igraph_vector_int_push_back(&vec1, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, v)); } } - igraph_vector_clear(&vec2); + igraph_vector_int_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)); + 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); @@ -674,21 +676,21 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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); + igraph_vector_int_clear(&vec1); + igraph_vector_int_clear(&vec2); + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); VECTOR(parent)[i] = i; - IGRAPH_CHECK(igraph_vector_push_back(&vec1, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, i)); } } #ifdef MATCHING_DEBUG debug("Matching:"); - igraph_vector_long_print(&match); + igraph_vector_int_print(&match); debug("Unmatched vertices are marked by non-negative numbers:\n"); igraph_vector_print(&parent); debug("Labeling:"); @@ -699,10 +701,10 @@ static int igraph_i_maximum_bipartite_matching_weighted( /* (8) Run the BFS */ alternating_path_endpoint = -1; - while (!igraph_dqueue_long_empty(&q)) { - v = (int) igraph_dqueue_long_pop(&q); + while (!igraph_dqueue_int_empty(&q)) { + v = igraph_dqueue_int_pop(&q); - debug("Considering vertex %ld\n", (long int)v); + debug("Considering vertex %ld\n", 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 @@ -717,7 +719,7 @@ static int igraph_i_maximum_bipartite_matching_weighted( neis = igraph_inclist_get(&inclist, v); n = igraph_vector_int_size(neis); for (i = 0; i < n; i++) { - j = (long int) VECTOR(*neis)[i]; + j = VECTOR(*neis)[i]; /* We only care about tight edges */ if (!IS_TIGHT(j)) { continue; @@ -727,10 +729,10 @@ static int igraph_i_maximum_bipartite_matching_weighted( if (VECTOR(parent)[u] >= 0) { continue; } - debug(" Reached vertex %ld via edge %ld\n", (long)u, (long)j); + debug(" Reached vertex %" IGRAPH_PRId " via edge %" IGRAPH_PRId "\n", u, j); VECTOR(parent)[u] = v; - IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); - w = (int) VECTOR(match)[u]; + IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); + w = 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 @@ -739,17 +741,17 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, w)); VECTOR(parent)[w] = u; } - IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); + IGRAPH_CHECK(igraph_vector_int_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]; + u = VECTOR(*neis2)[i]; /* Have we seen u already? */ if (VECTOR(parent)[u] >= 0) { continue; @@ -758,13 +760,13 @@ static int igraph_i_maximum_bipartite_matching_weighted( * 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) { + if (fabs(VECTOR(labels)[v] + VECTOR(labels)[u]) > eps) { continue; } - debug(" Reached vertex %ld via tight phantom edge\n", (long)u); + debug(" Reached vertex %" IGRAPH_PRId " via tight phantom edge\n", u); VECTOR(parent)[u] = v; - IGRAPH_CHECK(igraph_vector_push_back(&vec2, u)); - w = (int) VECTOR(match)[u]; + IGRAPH_CHECK(igraph_vector_int_push_back(&vec2, u)); + w = 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 @@ -773,10 +775,10 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, w)); VECTOR(parent)[w] = u; } - IGRAPH_CHECK(igraph_vector_push_back(&vec1, w)); + IGRAPH_CHECK(igraph_vector_int_push_back(&vec1, w)); } } @@ -788,40 +790,40 @@ static int igraph_i_maximum_bipartite_matching_weighted( #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); + u = VECTOR(parent)[v]; + debug("Extending matching with alternating path ending in %ld.\n", v); while (u != v) { - w = (int) VECTOR(match)[v]; + w = VECTOR(match)[v]; if (w != -1) { VECTOR(match)[w] = -1; } VECTOR(match)[v] = u; VECTOR(match)[v] = u; - w = (int) VECTOR(match)[u]; + w = 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]; + v = VECTOR(parent)[u]; + u = 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); + igraph_vector_int_print(&match); + debug("Matching size is now: %" IGRAPH_PRId "\n", msize); #endif continue; } #ifdef MATCHING_DEBUG debug("Vertices reachable from unmatched ones via tight edges:\n"); - igraph_vector_print(&vec1); + igraph_vector_int_print(&vec1); igraph_vector_print(&vec2); #endif @@ -840,17 +842,17 @@ static int igraph_i_maximum_bipartite_matching_weighted( * based on this. */ min_slack = IGRAPH_INFINITY; min_slack_u = min_slack_v = 0; - n = igraph_vector_size(&vec1); + n = igraph_vector_int_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_v = i; } } min_slack_2 = IGRAPH_INFINITY; for (i = 0; i < n; i++) { - u = (igraph_integer_t) VECTOR(vec1)[i]; + u = 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) { @@ -863,31 +865,31 @@ static int igraph_i_maximum_bipartite_matching_weighted( } 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); + min_slack, min_slack_u, min_slack_v); - n = igraph_vector_size(&vec1); + n = igraph_vector_int_size(&vec1); for (i = 0; i < n; i++) { - u = (igraph_integer_t) VECTOR(vec1)[i]; + u = 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); + debug("Trying to expand along vertex %" IGRAPH_PRId "\n", 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]); + debug(" Edge %" IGRAPH_PRId " -- %" IGRAPH_PRId " (ID=%" IGRAPH_PRId ")\n", u, v, 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); + debug(" %" IGRAPH_PRId " was reached, so we are not interested in it\n", v); continue; } /* v is the ID of the edge from now on */ - v = (igraph_integer_t) VECTOR(*neis)[j]; + v = VECTOR(*neis)[j]; if (VECTOR(slack)[v] < min_slack) { min_slack = VECTOR(slack)[v]; min_slack_u = u; @@ -897,39 +899,39 @@ static int igraph_i_maximum_bipartite_matching_weighted( VECTOR(slack)[v], min_slack); } } - debug("Minimum slack: %.4f on edge %d--%d\n", min_slack, (int)min_slack_u, (int)min_slack_v); + debug("Minimum slack: %.4f on edge %" IGRAPH_PRId "--%" IGRAPH_PRId "\n", min_slack, min_slack_u, 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); + n = igraph_vector_int_size(&vec1); for (i = 0; i < n; i++) { - u = (igraph_integer_t) VECTOR(vec1)[i]; + u = 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; + debug(" Decreasing slack of edge %" IGRAPH_PRId " (%" IGRAPH_PRId "--%" IGRAPH_PRId ") by %.4f\n", + VECTOR(*neis)[j], u, + IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[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); + n = igraph_vector_int_size(&vec2); for (i = 0; i < n; i++) { - u = (igraph_integer_t) VECTOR(vec2)[i]; + u = 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; + debug(" Increasing slack of edge %" IGRAPH_PRId " (%" IGRAPH_PRId"--%" IGRAPH_PRId ") by %.4f\n", + VECTOR(*neis)[j], u, + IGRAPH_OTHER(graph, VECTOR(*neis)[j], u), min_slack); + VECTOR(slack)[VECTOR(*neis)[j]] += min_slack; } dual += min_slack; } @@ -945,13 +947,13 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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) { + if (VECTOR(labels)[u] + VECTOR(labels)[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); + debug("New tight phantom edge: %" IGRAPH_PRId " -- %" IGRAPH_PRId "\n", u, v); IGRAPH_CHECK(igraph_vector_int_insert(neis2, k, v)); } } @@ -981,7 +983,7 @@ static int igraph_i_maximum_bipartite_matching_weighted( /* Fill the output parameters */ if (matching != 0) { - IGRAPH_CHECK(igraph_vector_long_update(matching, &match)); + IGRAPH_CHECK(igraph_vector_int_update(matching, &match)); } if (matching_size != 0) { *matching_size = msize; @@ -990,7 +992,7 @@ static int igraph_i_maximum_bipartite_matching_weighted( *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)); + IGRAPH_CHECK(igraph_edge(graph, i, &u, &v)); if (VECTOR(match)[u] == v) { *matching_weight += VECTOR(*weights)[i]; } @@ -1004,21 +1006,21 @@ static int igraph_i_maximum_bipartite_matching_weighted( 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_int_destroy(&parent); + igraph_dqueue_int_destroy(&q); igraph_vector_destroy(&labels); - igraph_vector_destroy(&vec1); - igraph_vector_destroy(&vec2); + igraph_vector_int_destroy(&vec1); + igraph_vector_int_destroy(&vec2); igraph_vector_destroy(&slack); - igraph_vector_long_destroy(&match); + igraph_vector_int_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_error_t igraph_maximum_matching(const igraph_t *graph, igraph_integer_t *matching_size, + igraph_real_t *matching_weight, igraph_vector_int_t *matching, + const igraph_vector_t *weights) { IGRAPH_UNUSED(graph); IGRAPH_UNUSED(matching_size); IGRAPH_UNUSED(matching_weight); diff --git a/src/vendor/cigraph/src/misc/microscopic_update.c b/src/vendor/cigraph/src/misc/microscopic_update.c index 80fc7c2030f..25a0936b072 100644 --- a/src/vendor/cigraph/src/misc/microscopic_update.c +++ b/src/vendor/cigraph/src/misc/microscopic_update.c @@ -94,7 +94,7 @@ * of \p vid. */ -static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, +static igraph_error_t igraph_i_ecumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, @@ -106,7 +106,7 @@ static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, igraph_real_t C; /* cumulative probability */ igraph_real_t P; /* probability */ igraph_real_t S; /* sum of values */ - long int i; + igraph_integer_t 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 */ @@ -136,12 +136,12 @@ static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, 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]; + e = IGRAPH_EIT_GET(A); + S += VECTOR(*U)[e]; IGRAPH_EIT_NEXT(A); } /* avoid division by zero later on */ - if (S == (igraph_real_t)0.0) { + if (S == 0.0) { igraph_eit_destroy(&A); igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); @@ -159,10 +159,10 @@ static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, 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); + e = 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; + P = VECTOR(*U)[e] / S; C += P; VECTOR(*V)[i] = C; i++; @@ -243,7 +243,7 @@ static int igraph_i_ecumulative_proportionate_values(const igraph_t *graph, * perspective of vid. */ -static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, +static igraph_error_t igraph_i_vcumulative_proportionate_values(const igraph_t *graph, const igraph_vector_t *U, igraph_vector_t *V, igraph_bool_t islocal, @@ -255,7 +255,7 @@ static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, igraph_real_t S; /* sum of values */ igraph_vit_t A; /* all vertices in v's perspective */ igraph_vs_t vs; - long int i; + igraph_integer_t 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 */ @@ -289,15 +289,15 @@ static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, 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]; + v = IGRAPH_VIT_GET(A); + S += VECTOR(*U)[v]; IGRAPH_VIT_NEXT(A); } if (islocal) { - S += (igraph_real_t)VECTOR(*U)[vid]; + S += VECTOR(*U)[vid]; } /* avoid division by zero later on */ - if (S == (igraph_real_t)0.0) { + if (S == 0.0) { igraph_vit_destroy(&A); igraph_vs_destroy(&vs); IGRAPH_FINALLY_CLEAN(2); @@ -319,10 +319,10 @@ static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, 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); + v = 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; + P = VECTOR(*U)[v] / S; C += P; VECTOR(*V)[i] = C; i++; @@ -417,16 +417,16 @@ static int igraph_i_vcumulative_proportionate_values(const igraph_t *graph, * \endclist */ -static int igraph_i_microscopic_standard_tests(const igraph_t *graph, +static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, igraph_integer_t vid, const igraph_vector_t *quantities, - const igraph_vector_t *strategies, + const igraph_vector_int_t *strategies, igraph_neimode_t mode, igraph_bool_t *updates, igraph_bool_t islocal) { igraph_integer_t nvert; - igraph_vector_t degv; + igraph_vector_int_t degv; *updates = 1; /* sanity checks */ @@ -446,11 +446,11 @@ static int igraph_i_microscopic_standard_tests(const igraph_t *graph, IGRAPH_ERROR("Graph cannot be the empty graph", IGRAPH_EINVAL); } /* invalid vector length */ - if (nvert != (igraph_integer_t)igraph_vector_size(quantities)) { + if (nvert != 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)) { + if (nvert != igraph_vector_int_size(strategies)) { IGRAPH_ERROR("Size of strategies vector different from number of vertices", IGRAPH_EINVAL); } @@ -479,13 +479,13 @@ static int igraph_i_microscopic_standard_tests(const igraph_t *graph, if (islocal) { /* Moving on ahead with vertex isolation test, since local perspective */ /* is requested. */ - IGRAPH_VECTOR_INIT_FINALLY(°v, 1); + IGRAPH_VECTOR_INT_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_vector_int_destroy(°v); IGRAPH_FINALLY_CLEAN(1); } @@ -498,7 +498,7 @@ static int igraph_i_microscopic_standard_tests(const igraph_t *graph, * \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 + * strategy to that which yields a local optimum. 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. @@ -572,15 +572,15 @@ static int igraph_i_microscopic_standard_tests(const igraph_t *graph, * \example examples/simple/igraph_deterministic_optimal_imitation.c */ -int igraph_deterministic_optimal_imitation(const igraph_t *graph, +igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode) { igraph_integer_t i, k, v; igraph_real_t q; - igraph_vector_t adj; + igraph_vector_int_t adj; igraph_bool_t updates; IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, @@ -599,33 +599,33 @@ int igraph_deterministic_optimal_imitation(const igraph_t *graph, /* 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_VECTOR_INT_INIT_FINALLY(&adj, 0); IGRAPH_CHECK(igraph_neighbors(graph, &adj, vid, mode)); - IGRAPH_CHECK(igraph_vector_shuffle(&adj)); + IGRAPH_CHECK(igraph_vector_int_shuffle(&adj)); /* maximum deterministic imitation */ i = vid; - q = (igraph_real_t)VECTOR(*quantities)[vid]; + q = 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) { + for (k = 0; k < igraph_vector_int_size(&adj); k++) { + v = VECTOR(adj)[k]; + if (VECTOR(*quantities)[v] > q) { i = v; - q = (igraph_real_t)VECTOR(*quantities)[v]; + q = 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) { + for (k = 0; k < igraph_vector_int_size(&adj); k++) { + v = VECTOR(adj)[k]; + if (VECTOR(*quantities)[v] < q) { i = v; - q = (igraph_real_t)VECTOR(*quantities)[v]; + q = 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_vector_int_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -725,23 +725,23 @@ int igraph_deterministic_optimal_imitation(const igraph_t *graph, * \endclist */ -int igraph_moran_process(const igraph_t *graph, +igraph_error_t igraph_moran_process(const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *quantities, - igraph_vector_t *strategies, + igraph_vector_int_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_int_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; + igraph_integer_t i; /* don't test for vertex isolation, hence vid = -1 and islocal = 0 */ IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, /*vid*/ -1, @@ -754,7 +754,7 @@ int igraph_moran_process(const igraph_t *graph, IGRAPH_ERROR("Weights vector is a null pointer", IGRAPH_EINVAL); } nedge = igraph_ecount(graph); - if (nedge != (igraph_integer_t)igraph_vector_size(weights)) { + if (nedge != igraph_vector_size(weights)) { IGRAPH_ERROR("Size of weights vector different from number of edges", IGRAPH_EINVAL); } @@ -784,9 +784,9 @@ int igraph_moran_process(const igraph_t *graph, r = RNG_UNIF01(); RNG_END(); i = 0; - IGRAPH_VECTOR_INIT_FINALLY(°, 1); + IGRAPH_VECTOR_INT_INIT_FINALLY(°, 1); while (!IGRAPH_VIT_END(vA)) { - u = (igraph_integer_t)IGRAPH_VIT_GET(vA); + u = IGRAPH_VIT_GET(vA); IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_1(u), mode, IGRAPH_NO_LOOPS)); if (VECTOR(deg)[0] < 1) { @@ -832,7 +832,7 @@ int igraph_moran_process(const igraph_t *graph, RNG_END(); i = 0; while (!IGRAPH_EIT_END(eA)) { - e = (igraph_integer_t)IGRAPH_EIT_GET(eA); + e = 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. */ @@ -860,7 +860,7 @@ int igraph_moran_process(const igraph_t *graph, igraph_eit_destroy(&eA); igraph_es_destroy(&es); - igraph_vector_destroy(°); + igraph_vector_int_destroy(°); igraph_vit_destroy(&vA); igraph_vs_destroy(&vs); igraph_vector_destroy(&V); @@ -965,11 +965,11 @@ int igraph_moran_process(const igraph_t *graph, * \example examples/simple/igraph_roulette_wheel_imitation.c */ -int igraph_roulette_wheel_imitation(const igraph_t *graph, +igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; @@ -977,7 +977,7 @@ int igraph_roulette_wheel_imitation(const igraph_t *graph, 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_integer_t i; IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, @@ -1023,7 +1023,7 @@ int igraph_roulette_wheel_imitation(const igraph_t *graph, 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); + u = IGRAPH_VIT_GET(A); VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; break; } @@ -1129,16 +1129,16 @@ int igraph_roulette_wheel_imitation(const igraph_t *graph, * \example examples/simple/igraph_stochastic_imitation.c */ -int igraph_stochastic_imitation(const igraph_t *graph, +igraph_error_t 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_vector_int_t *strategies, igraph_neimode_t mode) { igraph_bool_t updates; igraph_integer_t u; - igraph_vector_t adj; - int i; + igraph_vector_int_t adj; + igraph_integer_t i; /* sanity checks */ if (algo != IGRAPH_IMITATE_AUGMENTED && @@ -1155,7 +1155,7 @@ int igraph_stochastic_imitation(const igraph_t *graph, } /* immediate neighbours of v */ - IGRAPH_VECTOR_INIT_FINALLY(&adj, 0); + IGRAPH_VECTOR_INT_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. */ @@ -1165,11 +1165,11 @@ int igraph_stochastic_imitation(const igraph_t *graph, /* 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adj, vid)); RNG_BEGIN(); - i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); RNG_END(); - u = (igraph_integer_t) VECTOR(adj)[i]; + u = VECTOR(adj)[i]; VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } /* Augmented imitation. Let v be the vertex whose strategy we want to */ @@ -1179,9 +1179,9 @@ int igraph_stochastic_imitation(const igraph_t *graph, /* retains its current strategy. */ else if (algo == IGRAPH_IMITATE_AUGMENTED) { RNG_BEGIN(); - i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); RNG_END(); - u = (igraph_integer_t) VECTOR(adj)[i]; + u = VECTOR(adj)[i]; if (VECTOR(*quantities)[u] > VECTOR(*quantities)[vid]) { VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } @@ -1193,16 +1193,16 @@ int igraph_stochastic_imitation(const igraph_t *graph, /* Otherwise v retains its current strategy. */ else if (algo == IGRAPH_IMITATE_CONTRACTED) { RNG_BEGIN(); - i = (int) RNG_INTEGER(0, igraph_vector_size(&adj) - 1); + i = RNG_INTEGER(0, igraph_vector_int_size(&adj) - 1); RNG_END(); - u = (igraph_integer_t) VECTOR(adj)[i]; + u = VECTOR(adj)[i]; if (VECTOR(*quantities)[u] < VECTOR(*quantities)[vid]) { VECTOR(*strategies)[vid] = VECTOR(*strategies)[u]; } } /* clean up */ - igraph_vector_destroy(&adj); + igraph_vector_int_destroy(&adj); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/misc/mixing.c b/src/vendor/cigraph/src/misc/mixing.c index d6da467d50f..d417ba7850b 100644 --- a/src/vendor/cigraph/src/misc/mixing.c +++ b/src/vendor/cigraph/src/misc/mixing.c @@ -24,10 +24,11 @@ #include "igraph_mixing.h" #include "igraph_interface.h" +#include "igraph_structural.h" /** * \function igraph_assortativity_nominal - * Assortativity of a graph based on vertex categories + * \brief 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 @@ -36,43 +37,82 @@ * 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. + * + * The unnormalized version, computed when \p normalized is set to false, + * is identical to the modularity, and is defined as follows for + * directed networks: + * + * 1/m sum_ij (A_ij - k^out_i k^in_j / m) d(i,j), + * + * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, + * k^out and k^in are the out- and in-degrees, + * and d(i,j) is one if vertices \c i and \c j are in the same + * category and zero otherwise. + * + * + * The normalized assortativity coefficient is obtained by dividing the + * previous expression by + * + * 1/m sum_ij (m - k^out_i k^in_j d(i,j) / m). + * + * It can take any value within the interval [-1, 1]. + * + * + * Undirected graphs are effectively treated as directed ones with all-reciprocal + * edges. Thus, self-loops are taken into account twice in undirected graphs. + * + * + * References: + * + * + * M. E. J. Newman: Mixing patterns in networks, + * Phys. Rev. E 67, 026126 (2003) + * https://doi.org/10.1103/PhysRevE.67.026126. + * See section II and equation (2) for the definition of the concept. + * + * + * For an educational overview of assortativity, see + * M. E. J. Newman, + * Networks: An Introduction, Oxford University Press (2010). + * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. * * \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 types Integer vector giving the vertex categories. The types + * are represented by integers starting at 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. + * \param normalized Boolean, whether to compute the usual normalized + * assortativity. The unnormalized version is identical to + * modularity. Supply true here to compute the standard assortativity. * \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. + * \sa \ref igraph_assortativity() for computing the assortativity + * based on continuous vertex values instead of discrete categories. + * \ref igraph_modularity() to compute generalized modularity. * - * \example examples/simple/assortativity.c + * \example examples/simple/igraph_assortativity_nominal.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_error_t igraph_assortativity_nominal(const igraph_t *graph, + const igraph_vector_int_t *types, + igraph_real_t *res, + igraph_bool_t directed, + igraph_bool_t normalized) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t no_of_edges_real = no_of_edges; /* for divisions */ + igraph_integer_t no_of_types; + igraph_vector_int_t ai, bi, eii; 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 (igraph_vector_int_size(types) != no_of_nodes) { + IGRAPH_ERROR("Invalid types vector length.", IGRAPH_EINVAL); } if (no_of_nodes == 0) { @@ -81,22 +121,22 @@ int igraph_assortativity_nominal(const igraph_t *graph, } /* 'types' length > 0 here, safe to call vector_min() */ - if (igraph_vector_min(types) < 0) { - IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL); + if (igraph_vector_int_min(types) < 0) { + IGRAPH_ERROR("Vertex types must not be negative.", 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); + no_of_types = igraph_vector_int_max(types) + 1; + IGRAPH_VECTOR_INT_INIT_FINALLY(&ai, no_of_types); + IGRAPH_VECTOR_INT_INIT_FINALLY(&bi, no_of_types); + IGRAPH_VECTOR_INT_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]; + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_integer_t from_type = VECTOR(*types)[from]; + igraph_integer_t to_type = VECTOR(*types)[to]; VECTOR(ai)[from_type] += 1; VECTOR(bi)[to_type] += 1; @@ -112,9 +152,9 @@ int igraph_assortativity_nominal(const igraph_t *graph, } } - 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); + for (igraph_integer_t i = 0; i < no_of_types; i++) { + sumaibi += (VECTOR(ai)[i] / no_of_edges_real) * (VECTOR(bi)[i] / no_of_edges_real); + sumeii += (VECTOR(eii)[i] / no_of_edges_real); } if (!directed) { @@ -122,46 +162,92 @@ int igraph_assortativity_nominal(const igraph_t *graph, sumeii /= 2.0; } - *res = (sumeii - sumaibi) / (1.0 - sumaibi); + if (normalized) { + *res = (sumeii - sumaibi) / (1.0 - sumaibi); + } else { + *res = (sumeii - sumaibi); + } - igraph_vector_destroy(&eii); - igraph_vector_destroy(&bi); - igraph_vector_destroy(&ai); + igraph_vector_int_destroy(&eii); + igraph_vector_int_destroy(&bi); + igraph_vector_int_destroy(&ai); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_assortativity - * Assortativity based on numeric properties of vertices + * \brief Assortativity based on numeric properties of vertices. + * + * This function calculates the assortativity coefficient of a + * graph based on given values \c x_i for each vertex \c i. This type of + * assortativity coefficient equals the Pearson correlation of the values + * at the two ends of the edges. + * + * + * The unnormalized covariance of values, computed when \p normalized is + * set to false, is defined as follows in a directed graph: + * + * cov(x_out, x_in) = 1/m sum_ij (A_ij - k^out_i k^in_j / m) x_i x_j, + * + * where \c m denotes the number of edges, \c A_ij is the adjacency matrix, and + * k^out and k^in are the out- and in-degrees. + * \c x_out and \c x_in refer to the sets of vertex values at the start and end of + * the directed edges. * - * 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. + * + * The normalized covariance, i.e. Pearson correlation, is obtained by dividing + * the previous expression by + * sqrt(var(x_out)) sqrt(var(x_in)), where + * + * var(x_out) = 1/m sum_i k^out_i x_i^2 - (1/m sum_i k^out_i x_i^2)^2 + * + * var(x_in) = 1/m sum_j k^in_j x_j^2 - (1/m sum_j k^in_j x_j^2)^2 * - * See equation (21) in M. E. J. Newman: Mixing patterns + * + * Undirected graphs are effectively treated as directed graphs where all edges + * are reciprocal. Therefore, self-loops are effectively considered twice in + * undirected graphs. + * + * + * References: + * + * + * 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 + * https://doi.org/10.1103/PhysRevE.67.026126. + * See section III and equation (21) for the definition, and equation (26) for + * performing the calculation in directed graphs with the degrees as values. + * + * * M. E. J. Newman: Assortative mixing in networks, * Phys. Rev. Lett. 89, 208701 (2002) - * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs. + * http://doi.org/10.1103/PhysRevLett.89.208701. + * See equation (4) for performing the calculation in undirected + * graphs with the degrees as values. + * + * + * For an educational overview of the concept of assortativity, see + * M. E. J. Newman, + * Networks: An Introduction, Oxford University Press (2010). + * https://doi.org/10.1093/acprof%3Aoso/9780199206650.001.0001. * * \param graph The input graph, it can be directed or undirected. - * \param types1 The vertex values, these can be arbitrary numeric + * \param values The vertex values, these can be arbitrary numeric * values. - * \param types2 A second value vector to be using for the incoming + * \param values_in A second value vector to be used for the incoming * edges when calculating assortativity for a directed graph. - * Supply a null pointer here if you want to use the same values + * Supply \c NULL 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 + * (with a warning) if it is not a null pointer and the 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. + * \param normalized Boolean, whether to compute the normalized + * covariance, i.e. Pearson correlation. Supply true here to + * compute the standard assortativity. * \return Error code. * * Time complexity: O(|E|), linear in the number of edges of the @@ -170,100 +256,121 @@ int igraph_assortativity_nominal(const igraph_t *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 + * based on vertex degrees. */ -int igraph_assortativity(const igraph_t *graph, - const igraph_vector_t *types1, - const igraph_vector_t *types2, +igraph_error_t igraph_assortativity(const igraph_t *graph, + const igraph_vector_t *values, + const igraph_vector_t *values_in, igraph_real_t *res, - igraph_bool_t directed) { + igraph_bool_t directed, + igraph_bool_t normalized) { - long int no_of_nodes = igraph_vcount(graph); - long int no_of_edges = igraph_ecount(graph); - long int e; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); directed = directed && igraph_is_directed(graph); - if (!directed && types2) { - IGRAPH_WARNING("Only `types1' is used for undirected case"); + if (!directed && values_in) { + IGRAPH_WARNING("Incoming vertex values ignored when calculating undirected assortativity."); } - if (igraph_vector_size(types1) != no_of_nodes) { - IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL); + if (igraph_vector_size(values) != no_of_nodes) { + IGRAPH_ERROR("Invalid vertex values vector length.", IGRAPH_EINVAL); } - if (types2 && igraph_vector_size(types2) != no_of_nodes) { - IGRAPH_ERROR("Invalid `types2' vector length", IGRAPH_EINVAL); + if (values_in && igraph_vector_size(values_in) != no_of_nodes) { + IGRAPH_ERROR("Invalid incoming vertex values 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]; + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t from_value = VECTOR(*values)[from]; + igraph_real_t to_value = VECTOR(*values)[to]; - num1 += from_type * to_type; - num2 += from_type + to_type; - den1 += from_type * from_type + to_type * to_type; + num1 += from_value * to_value; + num2 += from_value + to_value; + if (normalized) { + den1 += from_value * from_value + to_value * to_value; + } } num1 /= no_of_edges; - den1 /= no_of_edges * 2; - num2 /= no_of_edges * 2; + if (normalized) { + den1 /= no_of_edges * 2.0; + } + num2 /= no_of_edges * 2.0; num2 = num2 * num2; - *res = (num1 - num2) / (den1 - num2); + if (normalized) { + *res = (num1 - num2) / (den1 - num2); + } else { + *res = (num1 - 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; + if (!values_in) { + values_in = values; } - 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; + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t from_value = VECTOR(*values)[from]; + igraph_real_t to_value = VECTOR(*values_in)[to]; + + num1 += from_value * to_value; + num2 += from_value; + num3 += to_value; + if (normalized) { + den1 += from_value * from_value; + den2 += to_value * to_value; + } } num = num1 - num2 * num3 / no_of_edges; - den = sqrt(den1 - num2 * num2 / no_of_edges) * - sqrt(den2 - num3 * num3 / no_of_edges); + if (normalized) { + den = sqrt(den1 - num2 * num2 / no_of_edges) * + sqrt(den2 - num3 * num3 / no_of_edges); - *res = num / den; + *res = num / den; + } else { + *res = num / no_of_edges; + } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_assortativity_degree - * Assortativity of a graph based on vertex degree + * \brief 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. + * This function simply calls \ref igraph_assortativity() with + * the degrees as the vertex values and normalization enabled. + * In the directed case, it uses out-degrees as out-values and + * in-degrees as in-values. + * + * + * For regular graphs, i.e. graphs in which all vertices have the + * same degree, computing degree correlations is not meaningful, + * and this function returns NaN. * * \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 + * graphs. Supply 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. @@ -274,34 +381,36 @@ int igraph_assortativity(const igraph_t *graph, * \sa \ref igraph_assortativity() for the general function * calculating assortativity for any kind of numeric vertex values. * - * \example examples/simple/assortativity.c + * \example examples/simple/igraph_assortativity_degree.c */ -int igraph_assortativity_degree(const igraph_t *graph, +igraph_error_t igraph_assortativity_degree(const igraph_t *graph, igraph_real_t *res, igraph_bool_t directed) { directed = directed && igraph_is_directed(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + /* This function uses igraph_strength() instead of igraph_degree() in order to obtain + * a vector of reals instead of a vector of integers. */ 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_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, NULL)); + IGRAPH_CHECK(igraph_strength(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL)); + IGRAPH_CHECK(igraph_assortativity(graph, &outdegree, &indegree, res, /* directed */ true, /* normalized */ true)); igraph_vector_destroy(&indegree); igraph_vector_destroy(&outdegree); + IGRAPH_FINALLY_CLEAN(2); } 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_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL)); + IGRAPH_CHECK(igraph_assortativity(graph, °ree, 0, res, /* directed */ false, /* normalized */ true)); igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/misc/motifs.c b/src/vendor/cigraph/src/misc/motifs.c index 5b96f761abe..2fdd1cc33ab 100644 --- a/src/vendor/cigraph/src/misc/motifs.c +++ b/src/vendor/cigraph/src/misc/motifs.c @@ -32,19 +32,19 @@ #include "core/interruption.h" #include "isomorphism/isoclasses.h" -#include "graph/neighbors.h" +#include "graph/internal.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( +static igraph_error_t igraph_i_motifs_randesu_update_hist( const igraph_t *graph, - igraph_vector_t *vids, int isoclass, void* extra) { + igraph_vector_int_t *vids, igraph_integer_t isoclass, void* extra) { igraph_vector_t *hist = (igraph_vector_t*)extra; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(vids); VECTOR(*hist)[isoclass]++; - return 0; + return IGRAPH_SUCCESS; } /** @@ -66,8 +66,8 @@ static igraph_bool_t igraph_i_motifs_randesu_update_hist( * * * 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 + * it takes a lot of time to find all of them. In this case, 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 @@ -105,16 +105,18 @@ static igraph_bool_t igraph_i_motifs_randesu_update_hist( * 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. + * subgraphs on more than 4 (directed) or 6 (undirected) vertices; + * \ref igraph_graph_count() to find the number of graph on a given + * number of vertices, i.e. the length of the \p hist vector. * * 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_error_t igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, + igraph_integer_t size, const igraph_vector_t *cut_prob) { igraph_bool_t directed = igraph_is_directed(graph); - int histlen; + igraph_integer_t histlen; if (directed) { switch (size) { @@ -149,7 +151,7 @@ int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") must agree with motif size (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(cut_prob), size); } @@ -167,10 +169,9 @@ int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, } } 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); + const int not_connected[] = { 0, 1, 2, 4, 5, 6, 9, 10, 11, 15, 22, 23, 27, + 28, 33, 34, 39, 62, 120 }; + size_t i, n = sizeof(not_connected) / sizeof(not_connected[0]); for (i = 0; i < n; i++) { VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; } @@ -180,17 +181,17 @@ int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, } } 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); + const int not_connected[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 19 }; + size_t 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); + const 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}; + size_t i, n = sizeof(not_connected) / sizeof(int); for (i = 0; i < n; i++) { VECTOR(*hist)[not_connected[i]] = IGRAPH_NAN; } @@ -234,29 +235,29 @@ int igraph_motifs_randesu(const igraph_t *graph, igraph_vector_t *hist, * \example examples/simple/igraph_motifs_randesu.c */ -int igraph_motifs_randesu_callback(const igraph_t *graph, int size, +igraph_error_t igraph_motifs_randesu_callback(const igraph_t *graph, igraph_integer_t size, const igraph_vector_t *cut_prob, igraph_motifs_handler_t *callback, void* extra) { - long int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t father; + igraph_integer_t i, j, s; + igraph_integer_t 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; + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; char *subg; const unsigned int *arr_idx, *arr_code; unsigned int code = 0; unsigned int mul, idx; - igraph_bool_t terminate = 0; + igraph_bool_t terminate = false; if (igraph_is_directed(graph)) { switch (size) { @@ -303,20 +304,16 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") 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); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to find motifs."); IGRAPH_FINALLY(igraph_free, added); subg = IGRAPH_CALLOC(no_of_nodes, char); - if (subg == 0) { - IGRAPH_ERROR("Cannot find motifs", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(subg, "Insufficient memory to find motifs."); IGRAPH_FINALLY(igraph_free, subg); IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); @@ -324,69 +321,69 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); RNG_BEGIN(); for (father = 0; father < no_of_nodes; father++) { - long int level; + igraph_integer_t level; IGRAPH_ALLOW_INTERRUPTION(); - if (VECTOR(*cut_prob)[0] == 1 || - RNG_UNIF01() < VECTOR(*cut_prob)[0]) { + 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)); + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_push_back(&vids, father)); subg[father] = 1; added[father] += 1; level += 1; /* init V_E */ - igraph_vector_clear(&adjverts); + igraph_vector_int_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]; + igraph_integer_t nei = VECTOR(*neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_clear(&stack); + igraph_stack_int_clear(&stack); - while (level > 1 || !igraph_vector_empty(&adjverts)) { + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_size(&adjverts) / 2; + s = igraph_vector_int_size(&adjverts) / 2; for (i = 0; i < s; i++) { - long int k, s2; - long int last; + igraph_integer_t k, s2; + igraph_integer_t last; + igraph_error_t ret; if (cp != 0 && RNG_UNIF01() < cp) { continue; } motifs += 1; - last = (long int) VECTOR(adjverts)[2 * i]; - IGRAPH_CHECK(igraph_vector_push_back(&vids, last)); + last = VECTOR(adjverts)[2 * i]; + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t from = 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]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (subg[nei] && k != subg[nei] - 1) { idx = (unsigned char) (mul * k + (subg[nei] - 1)); code |= arr_idx[idx]; @@ -394,11 +391,17 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, } } - if (callback(graph, &vids, (int) arr_code[code], extra)) { - terminate = 1; + IGRAPH_CHECK_CALLBACK( + callback(graph, &vids, arr_code[code], extra), + &ret + ); + + if (ret == IGRAPH_STOP) { + terminate = true; break; } - igraph_vector_pop_back(&vids); + + igraph_vector_int_pop_back(&vids); subg[last] = 0; } } @@ -410,54 +413,54 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, /* can we step down? */ if (level < size - 1 && - !igraph_vector_empty(&adjverts)) { + !igraph_vector_int_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); + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); if (cp == 0 || RNG_UNIF01() > cp) { /* yes, step down */ - IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_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]; + igraph_integer_t nei2 = VECTOR(*neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); } - nei = (long int) igraph_vector_pop_back(&vids); + nei = igraph_vector_int_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; + added[ 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 (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_pop_back(&adjverts); } } @@ -474,7 +477,7 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, 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; + added[ VECTOR(*neis)[i] ] -= 1; } } /* for father */ @@ -483,11 +486,11 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, IGRAPH_FREE(added); IGRAPH_FREE(subg); - igraph_vector_destroy(&vids); - igraph_vector_destroy(&adjverts); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); igraph_adjlist_destroy(&alloutneis); igraph_adjlist_destroy(&allneis); - igraph_stack_destroy(&stack); + igraph_stack_int_destroy(&stack); IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; @@ -497,32 +500,26 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, * \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 + * This function estimates the total number of (weakly) connected induced + * subgraphs on \p size 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 + * feasible to count all connected subgraphs, as there are too * 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. + * The estimate is made by taking a sample of vertices and counting all + * connected subgraphs 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. * * \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 est Pointer to an integer, the result will be stored here. + * \param size The size of the subgraphs 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 @@ -531,48 +528,45 @@ int igraph_motifs_randesu_callback(const igraph_t *graph, int size, * 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 + * 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_error_t igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, + igraph_integer_t size, const igraph_vector_t *cut_prob, igraph_integer_t sample_size, - const igraph_vector_t *parsample) { + const igraph_vector_int_t *parsample) { - long int no_of_nodes = igraph_vcount(graph); - igraph_vector_t neis; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_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; + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; + igraph_vector_int_t *sample; + igraph_integer_t sam; + igraph_integer_t i; if (size < 3) { IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, (igraph_integer_t) size); + IGRAPH_EINVAL, size); } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") 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 (parsample && !igraph_vector_int_isininterval(parsample, 0, no_of_nodes-1)) { + IGRAPH_ERROR("Sample vertex ID out of range.", IGRAPH_EINVVID); } if (no_of_nodes == 0) { @@ -580,29 +574,25 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, return IGRAPH_SUCCESS; } - added = IGRAPH_CALLOC(no_of_nodes, long int); - if (added == 0) { - IGRAPH_ERROR("Cannot find motifs.", IGRAPH_ENOMEM); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to count motifs."); 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); if (parsample == NULL) { - sample = IGRAPH_CALLOC(1, igraph_vector_t); - if (sample == NULL) { - IGRAPH_ERROR("Cannot estimate motifs.", IGRAPH_ENOMEM); - } + sample = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(sample, "Insufficient memory to count motifs."); IGRAPH_FINALLY(igraph_free, sample); - IGRAPH_VECTOR_INIT_FINALLY(sample, 0); + IGRAPH_VECTOR_INT_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); + sample = (igraph_vector_int_t*) parsample; + sample_size = igraph_vector_int_size(sample); } *est = 0; @@ -610,8 +600,8 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, RNG_BEGIN(); for (sam = 0; sam < sample_size; sam++) { - long int father = (long int) VECTOR(*sample)[sam]; - long int level, s; + igraph_integer_t father = VECTOR(*sample)[sam]; + igraph_integer_t level, s; IGRAPH_ALLOW_INTERRUPTION(); @@ -621,32 +611,31 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, } /* init G */ - igraph_vector_clear(&vids); level = 0; - IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_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); + igraph_vector_int_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - long int nei = (long int) VECTOR(neis)[i]; + igraph_integer_t nei = VECTOR(neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_clear(&stack); + igraph_stack_int_clear(&stack); - while (level > 1 || !igraph_vector_empty(&adjverts)) { + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_size(&adjverts) / 2; + s = igraph_vector_int_size(&adjverts) / 2; for (i = 0; i < s; i++) { if (cp != 0 && RNG_UNIF01() < cp) { continue; @@ -656,56 +645,54 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, } if (level < size - 1 && - !igraph_vector_empty(&adjverts)) { + !igraph_vector_int_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); + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); if (cp == 0 || RNG_UNIF01() > cp) { /* Yes, step down */ - IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_int_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_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, - IGRAPH_ALL)); - s = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - long int nei2 = (long int) VECTOR(neis)[i]; + igraph_integer_t nei2 = VECTOR(neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); } - nei = (long int) igraph_vector_pop_back(&vids); + nei = igraph_vector_int_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - added[ (long int) VECTOR(neis)[i] ] -= 1; + added[ 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 (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_pop_back(&adjverts); } } @@ -713,42 +700,40 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, /* clear the added vector */ added[father] -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, - IGRAPH_ALL)); - s = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - added[ (long int) VECTOR(neis)[i] ] -= 1; + added[ VECTOR(neis)[i] ] -= 1; } } /* for father */ RNG_END(); - (*est) *= ((double)no_of_nodes / sample_size); + (*est) *= ((igraph_real_t) no_of_nodes / sample_size); if (parsample == 0) { - igraph_vector_destroy(sample); + igraph_vector_int_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_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } /** * \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. + * This function counts the total number of (weakly) connected + * induced subgraphs on \p size vertices, without assigning isomorphism + * classes to them. Arbitrarily large motif sizes are supported. * * \param graph The graph object to study. * \param no Pointer to an integer type, the result will be stored @@ -763,46 +748,43 @@ int igraph_motifs_randesu_estimate(const igraph_t *graph, igraph_integer_t *est, * 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_error_t igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, + igraph_integer_t size, const igraph_vector_t *cut_prob) { - 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + igraph_vector_int_t vids; /* this is G */ + igraph_vector_int_t adjverts; /* this is V_E */ + igraph_stack_int_t stack; /* this is S */ + igraph_integer_t *added; + igraph_integer_t father; + igraph_integer_t i; if (size < 3) { IGRAPH_ERRORF("Motif size must be at least 3, received %" IGRAPH_PRId ".", - IGRAPH_EINVAL, (igraph_integer_t) size); + IGRAPH_EINVAL, size); } if (igraph_vector_size(cut_prob) != size) { - IGRAPH_ERRORF("Cut probability vector size (%ld) must agree with motif size (%" IGRAPH_PRId ").", + IGRAPH_ERRORF("Cut probability vector size (%" IGRAPH_PRId ") 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); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Insufficient memory to count motifs."); 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&adjverts, 0); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); *no = 0; RNG_BEGIN(); for (father = 0; father < no_of_nodes; father++) { - long int level, s; + igraph_integer_t level, s; IGRAPH_ALLOW_INTERRUPTION(); @@ -812,32 +794,31 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, } /* init G */ - igraph_vector_clear(&vids); level = 0; - IGRAPH_CHECK(igraph_vector_push_back(&vids, father)); + igraph_vector_int_clear(&vids); level = 0; + IGRAPH_CHECK(igraph_vector_int_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); + igraph_vector_int_clear(&adjverts); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - long int nei = (long int) VECTOR(neis)[i]; + igraph_integer_t nei = VECTOR(neis)[i]; if (!added[nei] && nei > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, father)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, father)); } added[nei] += 1; } /* init S */ - igraph_stack_clear(&stack); + igraph_stack_int_clear(&stack); - while (level > 1 || !igraph_vector_empty(&adjverts)) { + while (level > 1 || !igraph_vector_int_empty(&adjverts)) { igraph_real_t cp = VECTOR(*cut_prob)[level]; if (level == size - 1) { - s = igraph_vector_size(&adjverts) / 2; + s = igraph_vector_int_size(&adjverts) / 2; for (i = 0; i < s; i++) { if (cp != 0 && RNG_UNIF01() < cp) { continue; @@ -847,56 +828,54 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, } if (level < size - 1 && - !igraph_vector_empty(&adjverts)) { + !igraph_vector_int_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); + igraph_integer_t neifather = igraph_vector_int_pop_back(&adjverts); + igraph_integer_t nei = igraph_vector_int_pop_back(&adjverts); if (cp == 0 || RNG_UNIF01() > cp) { /* Yes, step down */ - IGRAPH_CHECK(igraph_vector_push_back(&vids, nei)); + IGRAPH_CHECK(igraph_vector_int_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_stack_int_push(&stack, neifather)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, level)); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) nei, - IGRAPH_ALL)); - s = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - long int nei2 = (long int) VECTOR(neis)[i]; + igraph_integer_t nei2 = VECTOR(neis)[i]; if (!added[nei2] && nei2 > father) { - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei2)); - IGRAPH_CHECK(igraph_vector_push_back(&adjverts, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&adjverts, nei2)); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t nei, neifather; + while (!igraph_stack_int_empty(&stack) && + level == igraph_stack_int_top(&stack) - 1) { + igraph_stack_int_pop(&stack); + nei = igraph_stack_int_pop(&stack); + neifather = igraph_stack_int_pop(&stack); + igraph_vector_int_push_back(&adjverts, nei); + igraph_vector_int_push_back(&adjverts, neifather); } - nei = (long int) igraph_vector_pop_back(&vids); + nei = igraph_vector_int_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, nei, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - added[ (long int) VECTOR(neis)[i] ] -= 1; + added[ 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 (!igraph_vector_int_empty(&adjverts) && + igraph_vector_int_tail(&adjverts) == nei) { + igraph_vector_int_pop_back(&adjverts); + igraph_vector_int_pop_back(&adjverts); } } @@ -904,11 +883,10 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, /* clear the added vector */ added[father] -= 1; - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) father, - IGRAPH_ALL)); - s = igraph_vector_size(&neis); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, father, IGRAPH_ALL)); + s = igraph_vector_int_size(&neis); for (i = 0; i < s; i++) { - added[ (long int) VECTOR(neis)[i] ] -= 1; + added[ VECTOR(neis)[i] ] -= 1; } } /* for father */ @@ -916,19 +894,18 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, RNG_END(); IGRAPH_FREE(added); - igraph_vector_destroy(&vids); - igraph_vector_destroy(&adjverts); - igraph_stack_destroy(&stack); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&vids); + igraph_vector_int_destroy(&adjverts); + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; } /** * \function igraph_dyad_census - * \brief Calculating the dyad census as defined by Holland and Leinhardt. + * \brief 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 @@ -942,13 +919,12 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, * * \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 + * \param mut Pointer to a real, the number of mutual dyads is * stored here. - * \param asym Pointer to an integer, the number of asymmetric dyads + * \param asym Pointer to a real, 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. + * \param null Pointer to a real, the number of null dyads is + * stored here. * \return Error code. * * \sa \ref igraph_reciprocity(), \ref igraph_triad_census(). @@ -956,26 +932,30 @@ int igraph_motifs_randesu_no(const igraph_t *graph, igraph_integer_t *no, * 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_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, + igraph_real_t *asym, igraph_real_t *null) { + + /* This function operates with a floating point type instead of an + * integer type in order to avoid integer overflow, which is likely + * for 'null' in large graphs on 32-bit systems. */ - igraph_integer_t nonrec = 0, rec = 0; - igraph_vector_t inneis, outneis; + igraph_real_t nonrec = 0, rec = 0; + igraph_vector_int_t inneis, outneis; igraph_integer_t vc = igraph_vcount(graph); - long int i; + igraph_integer_t i; - IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); - IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); for (i = 0; i < vc; i++) { - long int ideg, odeg; - long int ip, op; + igraph_integer_t ideg, odeg; + igraph_integer_t 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); + ideg = igraph_vector_int_size(&inneis); + odeg = igraph_vector_int_size(&outneis); ip = op = 0; while (ip < ideg && op < odeg) { @@ -994,43 +974,28 @@ int igraph_dyad_census(const igraph_t *graph, igraph_integer_t *mut, nonrec += (ideg - ip) + (odeg - op); } - igraph_vector_destroy(&inneis); - igraph_vector_destroy(&outneis); + igraph_vector_int_destroy(&inneis); + igraph_vector_int_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); - } + *null = 0.5 * vc * (vc - 1.0) - (*mut + *asym); + if (*null == 0.0) *null = 0.0; /* avoid returning -0.0 */ return IGRAPH_SUCCESS; } -/** - * \function igraph_triad_census_24 - * TODO - */ - -int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, +static igraph_error_t igraph_i_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_integer_t vc = igraph_vcount(graph); + igraph_vector_int_t seen; igraph_vector_int_t *neis, *neis2; - long int i, j, k, s, neilen, neilen2, ign; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&seen, vc); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); *res2 = *res4 = 0; @@ -1044,7 +1009,7 @@ int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, VECTOR(seen)[i] = i + 1; ign = 0; for (j = 0; j < neilen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (VECTOR(seen)[nei] == i + 1 || VECTOR(seen)[nei] == -(i + 1)) { /* multiple edges or loop edge */ VECTOR(seen)[nei] = -(i + 1); @@ -1055,7 +1020,7 @@ int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, } for (j = 0; j < neilen; j++) { - long int nei = (long int) VECTOR(*neis)[j]; + igraph_integer_t nei = VECTOR(*neis)[j]; if (nei <= i || (j > 0 && nei == VECTOR(*neis)[j - 1])) { continue; } @@ -1063,7 +1028,7 @@ int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, neilen2 = igraph_vector_int_size(neis2); s = 0; for (k = 0; k < neilen2; k++) { - long int nei2 = (long int) VECTOR(*neis2)[k]; + igraph_integer_t nei2 = VECTOR(*neis2)[k]; if (k > 0 && nei2 == VECTOR(*neis2)[k - 1]) { continue; } @@ -1080,15 +1045,15 @@ int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, } igraph_adjlist_destroy(&adjlist); - igraph_vector_long_destroy(&seen); + igraph_vector_int_destroy(&seen); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_triad_census - * \brief Triad census, as defined by Davis and Leinhardt + * \brief Triad census, as defined by Davis and Leinhardt. * * * Calculating the triad census means classifying every triple of @@ -1153,7 +1118,7 @@ int igraph_triad_census_24(const igraph_t *graph, igraph_real_t *res2, * Time complexity: TODO. */ -int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { +igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { igraph_vector_t cut_prob; igraph_real_t m2, m4; @@ -1170,7 +1135,7 @@ int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { 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)); + IGRAPH_CHECK(igraph_i_triad_census_24(graph, &m2, &m4)); total = ((igraph_real_t)vc) * (vc - 1); total *= (vc - 2); @@ -1214,5 +1179,5 @@ int igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { igraph_vector_destroy(&tmp); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/misc/order_cycle.cpp b/src/vendor/cigraph/src/misc/order_cycle.cpp new file mode 100644 index 00000000000..e977db593e3 --- /dev/null +++ b/src/vendor/cigraph/src/misc/order_cycle.cpp @@ -0,0 +1,100 @@ +/* + 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, see . +*/ + +#include "misc/order_cycle.h" + +#include "igraph_interface.h" + +#include "core/exceptions.h" + +#include +#include + +// Initialized to {-1, -1} +struct eid_pair_t : public std::pair { + eid_pair_t() : std::pair(-1, -1) { } +}; + +/** + * \function igraph_i_order_cycle + * \brief Reorders edges of a cycle in cycle order + * + * This function takes \p cycle, a vector of arbitrarily ordered edge IDs, + * representing a graph cycle. It produces a vector \p res containing the + * same IDs in cycle order. \p res must be initialized when calling this function. + */ +igraph_error_t igraph_i_order_cycle( + const igraph_t *graph, + const igraph_vector_int_t *cycle, + igraph_vector_int_t *res) { + + IGRAPH_HANDLE_EXCEPTIONS_BEGIN; + + igraph_integer_t n = igraph_vector_int_size(cycle); + IGRAPH_ASSERT(n > 0); + + std::map inclist; + for (igraph_integer_t i=0; i < n; ++i) { + igraph_integer_t eid = VECTOR(*cycle)[i]; + + { + igraph_integer_t from = IGRAPH_FROM(graph, eid); + auto &p = inclist[from]; + if (p.first < 0) { + p.first = eid; + } else { + IGRAPH_ASSERT(p.second < 0); + p.second = eid; + } + } + + { + igraph_integer_t to = IGRAPH_TO(graph, eid); + auto &p = inclist[to]; + if (p.first < 0) { + p.first = eid; + } else { + IGRAPH_ASSERT(p.second < 0); + p.second = eid; + } + } + } + + igraph_vector_int_clear(res); + IGRAPH_CHECK(igraph_vector_int_reserve(res, igraph_vector_int_size(cycle))); + igraph_integer_t current_e = VECTOR(*cycle)[0]; + igraph_integer_t current_v = IGRAPH_FROM(graph, current_e); + for (igraph_integer_t i=0; i < n; ++i) { + const auto &p = inclist.at(current_v); + igraph_vector_int_push_back(res, current_e); /* reserved */ + igraph_integer_t next_e = p.first; + if (next_e == current_e) { + next_e = p.second; + } + current_e = next_e; + igraph_integer_t next_v = IGRAPH_FROM(graph, current_e); + if (next_v == current_v) { + next_v = IGRAPH_TO(graph, current_e); + } + current_v = next_v; + } + + IGRAPH_HANDLE_EXCEPTIONS_END; + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/misc/conversion_internal.h b/src/vendor/cigraph/src/misc/order_cycle.h similarity index 62% rename from src/vendor/cigraph/src/misc/conversion_internal.h rename to src/vendor/cigraph/src/misc/order_cycle.h index e234a9b8b97..2ed87f54ee2 100644 --- a/src/vendor/cigraph/src/misc/conversion_internal.h +++ b/src/vendor/cigraph/src/misc/order_cycle.h @@ -1,6 +1,6 @@ /* IGraph library. - Copyright (C) 2021 The igraph development team + 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 @@ -16,13 +16,20 @@ along with this program. If not, see . */ -#ifndef IGRAPH_MISC_CONVERSION_INTERNAL_H -#define IGRAPH_MISC_CONVERSION_INTERNAL_H +#ifndef IGRAPH_ORDER_CYCLE_H +#define IGRAPH_ORDER_CYCLE_H -#include "igraph_sparsemat.h" -#include "igraph_types.h" +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_vector.h" -int igraph_i_normalize_sparsemat(igraph_sparsemat_t *sparsemat, - igraph_bool_t column_wise); +__BEGIN_DECLS -#endif +igraph_error_t igraph_i_order_cycle( + const igraph_t *graph, + const igraph_vector_int_t *cycle, + igraph_vector_int_t *res); + +__END_DECLS + +#endif /* IGRAPH_ORDER_CYCLE_H */ diff --git a/src/vendor/cigraph/src/misc/other.c b/src/vendor/cigraph/src/misc/other.c index 45dbcbbb9df..d6f5ca6ec99 100644 --- a/src/vendor/cigraph/src/misc/other.c +++ b/src/vendor/cigraph/src/misc/other.c @@ -21,15 +21,11 @@ */ +#include "igraph_interface.h" #include "igraph_nongraph.h" -#include "igraph_random.h" -#include "igraph_types.h" +#include "igraph_paths.h" #include "core/interruption.h" -#include "plfit/plfit_error.h" -#include "plfit/plfit.h" - -#include /** * \ingroup nongraph @@ -52,15 +48,15 @@ * the data vector. */ -int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, +igraph_error_t igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, igraph_integer_t binwidth) { double sum = 0; - long int i; + igraph_integer_t 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); + IGRAPH_ERRORF("Data vector length (%" IGRAPH_PRId ") 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); @@ -68,7 +64,7 @@ int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, /* Memory for result */ - IGRAPH_CHECK(igraph_vector_resize(res, (long int)(igraph_vector_size(data) - binwidth + 1))); + IGRAPH_CHECK(igraph_vector_resize(res, (igraph_vector_size(data) - binwidth + 1))); /* Initial bin */ for (i = 0; i < binwidth; i++) { @@ -80,7 +76,7 @@ int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, 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)]; + sum += VECTOR(*data)[ (i + binwidth - 1)]; VECTOR(*res)[i] = sum / binwidth; } @@ -91,7 +87,7 @@ int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, /** * \ingroup nongraph * \function igraph_convex_hull - * \brief Determines the convex hull of a given set of points in the 2D plane + * \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. @@ -115,34 +111,35 @@ int igraph_running_mean(const igraph_vector_t *data, igraph_vector_t *res, * \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 + * Time complexity: O(n log(n)) where n is the number of vertices. */ -int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, - igraph_matrix_t *rescoords) { +igraph_error_t igraph_convex_hull( + const igraph_matrix_t *data, igraph_vector_int_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_integer_t i, pivot_idx = 0, last_idx, before_last_idx, next_idx, j; + igraph_vector_t angles; + igraph_vector_int_t order, stack; igraph_real_t px, py, cp; - no_of_nodes = (igraph_integer_t) igraph_matrix_nrow(data); + no_of_nodes = igraph_matrix_nrow(data); if (igraph_matrix_ncol(data) != 2) { - IGRAPH_ERROR("matrix must have 2 columns", IGRAPH_EINVAL); + IGRAPH_ERROR("Only two-dimensional point sets are supports, matrix must have two columns.", IGRAPH_EINVAL); } if (no_of_nodes == 0) { - if (resverts != 0) { - IGRAPH_CHECK(igraph_vector_resize(resverts, 0)); + if (resverts) { + igraph_vector_int_clear(resverts); } - if (rescoords != 0) { + if (rescoords) { IGRAPH_CHECK(igraph_matrix_resize(rescoords, 0, 2)); } /**************************** this is an exit here *********/ - return 0; + return IGRAPH_SUCCESS; } IGRAPH_VECTOR_INIT_FINALLY(&angles, no_of_nodes); - IGRAPH_VECTOR_INIT_FINALLY(&stack, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&stack, 0); /* Search for the pivot vertex */ for (i = 1; i < no_of_nodes; i++) { @@ -170,16 +167,16 @@ int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, } /* Sort points by angles */ - IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes); - IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, 0)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_CHECK(igraph_vector_qsort_ind(&angles, &order, IGRAPH_ASCENDING)); /* 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]; + last_idx = VECTOR(order)[0]; + pivot_idx = VECTOR(order)[no_of_nodes - 1]; for (i = 1; i < no_of_nodes; i++) { - next_idx = (long int) VECTOR(order)[i]; + next_idx = 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 */ @@ -203,11 +200,11 @@ int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, 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]; + while (!igraph_vector_int_empty(&order)) { + next_idx = igraph_vector_int_tail(&order); if (next_idx < 0) { /* This vertex should be skipped; was excluded in an earlier step */ - igraph_vector_pop_back(&order); + igraph_vector_int_pop_back(&order); continue; } /* Determine whether we are at a left or right turn */ @@ -221,214 +218,161 @@ int igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_t *resverts, (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; + before_last_idx = (j >= 2) ? VECTOR(stack)[j - 2] : -1; } } /* Create result vector */ if (resverts != 0) { - igraph_vector_clear(resverts); - IGRAPH_CHECK(igraph_vector_append(resverts, &stack)); + igraph_vector_int_clear(resverts); + IGRAPH_CHECK(igraph_vector_int_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_int_destroy(&order); + igraph_vector_int_destroy(&stack); igraph_vector_destroy(&angles); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } +/** + * \function igraph_expand_path_to_pairs + * \brief Helper function to convert a sequence of vertex IDs describing a path into a "pairs" vector. + * + * + * This function is useful when you have a sequence of vertex IDs in a graph and + * you would like to retrieve the IDs of the edges between them. The function + * duplicates all but the first and the last elements in the vector, effectively + * converting the path into a vector of vertex IDs that can be passed to + * \ref igraph_get_eids(). + * + * \param path the input vector. It will be modified in-place and it will be + * resized as needed. When the vector contains less than two vertex IDs, + * it will be cleared. + * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory to expand + * the vector. + */ +igraph_error_t igraph_expand_path_to_pairs(igraph_vector_int_t* path) { + igraph_integer_t no_of_vertices = igraph_vector_int_size(path); + igraph_integer_t i, j, no_of_items = (no_of_vertices - 1) * 2; -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); + if (no_of_vertices <= 1) { + igraph_vector_int_clear(path); + } else { + IGRAPH_CHECK(igraph_vector_int_resize(path, no_of_items)); + + i = no_of_vertices - 1; + j = no_of_items - 1; + VECTOR(*path)[j] = VECTOR(*path)[i]; + while (i > 1) { + i--; j--; + VECTOR(*path)[j] = VECTOR(*path)[i]; + j--; + VECTOR(*path)[j] = VECTOR(*path)[i]; + } + } - igraph_i_plfit_error_message = reason; + return IGRAPH_SUCCESS; } /** - * \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. + * \function igraph_vertex_path_from_edge_path + * \brief Converts a path of edge IDs to the traversed vertex IDs. * * - * 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). + * This function is useful when you have a sequence of edge IDs representing a + * continuous path in a graph and you would like to obtain the vertex IDs that + * the path traverses. The function is used implicitly by several shortest path + * related functions to convert a path of edge IDs to the corresponding + * representation that describes the path in terms of vertex IDs instead. * - * \example examples/simple/igraph_power_law_fit.c + * \param graph the graph that the edge IDs refer to + * \param start the start vertex of the path + * \param edge_path the sequence of edge IDs that describe the path + * \param vertex_path the sequence of vertex IDs traversed will be returned here + * \return Error code: \c IGRAPH_ENOMEM if there is not enough memory, + * \c IGRAPH_EINVAL if the edge path does not start at the given vertex + * or if there is at least one edge whose start vertex does not match + * the end vertex of the previous edge */ -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; - } - } +igraph_error_t igraph_vertex_path_from_edge_path( + const igraph_t *graph, igraph_integer_t start, + const igraph_vector_int_t *edge_path, igraph_vector_int_t *vertex_path, + igraph_neimode_t mode +) { + igraph_integer_t i, no_of_edges; + igraph_bool_t directed = igraph_is_directed(graph); + igraph_bool_t next_edge_ok; + igraph_integer_t next_start; + + igraph_vector_int_clear(vertex_path); + + no_of_edges = igraph_vector_int_size(edge_path); + IGRAPH_CHECK(igraph_vector_int_reserve(vertex_path, no_of_edges + 1)); + + if (!directed) { + mode = IGRAPH_ALL; } - RNG_BEGIN(); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, VECTOR(*edge_path)[i]); + igraph_integer_t to = IGRAPH_TO(graph, VECTOR(*edge_path)[i]); - 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; + igraph_vector_int_push_back(vertex_path, start); /* reserved */ - 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; + switch (mode) { + case IGRAPH_OUT: + next_edge_ok = from == start; + next_start = to; + break; - case PLFIT_EINVAL: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EINVAL); - break; + case IGRAPH_IN: + next_edge_ok = to == start; + next_start = from; + break; - case PLFIT_UNDRFLOW: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EUNDERFLOW); - break; + case IGRAPH_ALL: + if (from == start) { + next_edge_ok = true; + next_start = to; + } else if (to == start) { + next_edge_ok = true; + next_start = from; + } else { + next_edge_ok = false; + } + break; - case PLFIT_OVERFLOW: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); - break; + default: + IGRAPH_ERROR("Invalid neighborhood mode.", IGRAPH_EINVAL); + } - case PLFIT_ENOMEM: - IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); - break; + if (!next_edge_ok) { + IGRAPH_ERROR("Edge IDs do not form a continuous path.", IGRAPH_EINVAL); + } - default: - break; + start = next_start; } - 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; - } + igraph_vector_int_push_back(vertex_path, start); /* reserved */ - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/misc/power_law_fit.c b/src/vendor/cigraph/src/misc/power_law_fit.c new file mode 100644 index 00000000000..e9e036cfd2b --- /dev/null +++ b/src/vendor/cigraph/src/misc/power_law_fit.c @@ -0,0 +1,328 @@ +/* -*- 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 "plfit/plfit_error.h" +#include "plfit/plfit.h" + +#include + +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; +} + +static void igraph_i_plfit_prepare_continuous_options( + plfit_continuous_options_t* options, igraph_bool_t finite_size_correction +) { + plfit_continuous_options_init(options); + options->p_value_method = PLFIT_P_VALUE_SKIP; + options->xmin_method = PLFIT_STRATIFIED_SAMPLING; + options->finite_size_correction = (plfit_bool_t) finite_size_correction; +} + +static void igraph_i_plfit_prepare_discrete_options( + plfit_discrete_options_t* options, igraph_bool_t finite_size_correction +) { + plfit_discrete_options_init(options); + options->p_value_method = PLFIT_P_VALUE_SKIP; + options->finite_size_correction = (plfit_bool_t) finite_size_correction; +} + +/* Decides whether to use finite size correction for the given input data */ +static igraph_bool_t igraph_i_plfit_should_use_finite_size_correction(const igraph_vector_t* data) { + return igraph_vector_size(data) < 50; +} + +static igraph_error_t igraph_i_handle_plfit_error(int code) { + switch (code) { + case PLFIT_SUCCESS: + return IGRAPH_SUCCESS; + + 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); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_OVERFLOW: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_EOVERFLOW); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_ENOMEM: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ + break; + + case PLFIT_EMAXITER: + IGRAPH_ERROR(igraph_i_plfit_error_message, IGRAPH_DIVERGED); /* LCOV_EXCL_LINE */ + break; + + default: + IGRAPH_ERRORF("Unknown error code returned from plfit (%d)", IGRAPH_FAILURE, code); + break; + } + + return IGRAPH_SUCCESS; +} + +/** + * \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. + * https://doi.org/10.1137/070710111 + * + * \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. Note that the p-value of the fit is \em not + * calculated by default as it is time-consuming; you need to call + * \ref igraph_plfit_result_calculate_p_value() to calculate the + * p-value itself + * \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 + */ +igraph_error_t 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 ? false : true; + igraph_bool_t finite_size_correction; + + int retval; + size_t i, n; + + finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(data); + n = (size_t) igraph_vector_size(data); + + if (discrete) { + /* Does the vector contain discrete values only? */ + for (i = 0; i < n; i++) { + if (trunc(VECTOR(*data)[i]) != VECTOR(*data)[i]) { + discrete = false; + break; + } + } + } + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (discrete) { + igraph_i_plfit_prepare_discrete_options(&disc_options, 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 { + igraph_i_plfit_prepare_continuous_options(&cont_options, 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(); + + IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); + + if (result) { + result->data = data; + result->continuous = !discrete; + result->alpha = plfit_result.alpha; + result->xmin = plfit_result.xmin; + result->L = plfit_result.L; + result->D = plfit_result.D; + } + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup nongraph + * \function igraph_plfit_result_calculate_p_value + * \brief Calculates the p-value of a fitted power-law model. + * + * + * The p-value is calculated by resampling the input data many times in a way + * that the part below the fitted \c x_min threshold is resampled from the + * input data itself, while the part above the fitted \c x_min threshold is + * drawn from the fitted power-law function. A Kolmogorov-Smirnov test is then + * performed for each resampled dataset and its test statistic is compared with the + * observed test statistic from the original dataset. The fraction of resampled + * datasets that have a \em higher test statistic is the returned p-value. + * + * + * Note that the precision of the returned p-value depends on the number of + * resampling attempts. The number of resampling trials is determined by + * 0.25 divided by the square of the required precision. For instance, a required + * precision of 0.01 means that 2500 samples will be drawn. + * + * + * If igraph is compiled with OpenMP support, this function will use parallel + * OpenMP threads for the resampling. Each OpenMP thread gets its own instance + * of a random number generator. However, since the scheduling of OpenMP threads + * is outside our control, we cannot guarantee how many resampling instances the + * threads are asked to execute, thus it may happen that the random number + * generators are used differently between runs. If you want to obtain + * reproducible results, seed igraph's master RNG appropriately, and force the + * number of OpenMP threads to 1 early in your program, either by calling + * omp_set_num_threads(1) or by setting the value of the \c OMP_NUM_THREADS + * environment variable to 1. + * + * \param model The fitted power-law model from the \ref igraph_power_law_fit() + * function + * \param result The calculated p-value is returned here + * \param precision The desired precision of the p-value. Higher values correspond + * to longer calculation time. + * @return igraph_error_t + */ +igraph_error_t igraph_plfit_result_calculate_p_value( + const igraph_plfit_result_t* model, igraph_real_t* result, igraph_real_t precision +) { + int retval; + plfit_continuous_options_t cont_options; + plfit_discrete_options_t disc_options; + plfit_result_t plfit_result; + plfit_error_handler_t* plfit_stored_error_handler; + igraph_bool_t finite_size_correction; + + IGRAPH_ASSERT(model != NULL); + + plfit_result.alpha = model->alpha; + plfit_result.xmin = model->xmin; + plfit_result.L = model->L; + plfit_result.D = model->D; + + finite_size_correction = igraph_i_plfit_should_use_finite_size_correction(model->data); + + RNG_BEGIN(); + + plfit_stored_error_handler = plfit_set_error_handler(igraph_i_plfit_error_handler_store); + if (model->continuous) { + igraph_i_plfit_prepare_continuous_options(&cont_options, finite_size_correction); + cont_options.p_value_method = PLFIT_P_VALUE_EXACT; + cont_options.p_value_precision = precision; + retval = plfit_calculate_p_value_continuous( + VECTOR(*model->data), (size_t) igraph_vector_size(model->data), + &cont_options, /* xmin_fixed = */ 0, &plfit_result + ); + } else { + igraph_i_plfit_prepare_discrete_options(&disc_options, finite_size_correction); + disc_options.p_value_method = PLFIT_P_VALUE_EXACT; + disc_options.p_value_precision = precision; + retval = plfit_calculate_p_value_discrete( + VECTOR(*model->data), (size_t) igraph_vector_size(model->data), + &disc_options, /* xmin_fixed = */ 0, &plfit_result + ); + } + plfit_set_error_handler(plfit_stored_error_handler); + + RNG_END(); + + IGRAPH_CHECK(igraph_i_handle_plfit_error(retval)); + + if (result) { + *result = plfit_result.p; + } + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/misc/scan.c b/src/vendor/cigraph/src/misc/scan.c index 3b56f674223..6e96595b2e4 100644 --- a/src/vendor/cigraph/src/misc/scan.c +++ b/src/vendor/cigraph/src/misc/scan.c @@ -24,10 +24,7 @@ #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" @@ -35,7 +32,6 @@ #include "igraph_structural.h" #include "core/interruption.h" -#include "properties/properties_internal.h" /** * \section about_local_scan @@ -66,39 +62,34 @@ * */ -int igraph_local_scan_0(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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; + return igraph_strength(graph, res, igraph_vss_all(), mode, /*loops=*/ 1, + weights); } /* 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, +static igraph_error_t 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_integer_t i; + igraph_integer_t n = il->length; igraph_vector_int_t mark; - igraph_vector_int_init(&mark, n); - IGRAPH_FINALLY(igraph_vector_int_destroy, &mark); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&mark, n); 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]; + igraph_integer_t j, l = igraph_vector_int_size(v); + igraph_integer_t 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); + igraph_integer_t edge = VECTOR(*v)[j]; + igraph_integer_t e = IGRAPH_OTHER(graph, edge, i); if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) { VECTOR(mark)[e] = i + 1; j++; @@ -112,43 +103,42 @@ static int igraph_i_trans4_il_simplify(const igraph_t *graph, igraph_inclist_t * igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /* This one handles both weighted and unweighted cases */ -static int igraph_i_local_scan_1_directed(const igraph_t *graph, +static igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_inclist_t incs; - int i, node; + igraph_integer_t 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_INT_INIT_FINALLY(&neis, no_of_nodes); - igraph_vector_resize(res, no_of_nodes); + 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 *edges1 = igraph_inclist_get(&incs, node); - int edgeslen1 = igraph_vector_int_size(edges1); + igraph_integer_t 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_integer_t e = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e, node); igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; VECTOR(neis)[nei] = node + 1; VECTOR(*res)[node] += w; @@ -156,16 +146,16 @@ static int igraph_i_local_scan_1_directed(const igraph_t *graph, /* Crawl neighbors */ for (i = 0; i < edgeslen1; i++) { - int e2 = VECTOR(*edges1)[i]; - int nei = IGRAPH_OTHER(graph, e2, node); + igraph_integer_t e2 = VECTOR(*edges1)[i]; + igraph_integer_t 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); + igraph_integer_t 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_integer_t e2 = VECTOR(*edges2)[j]; + igraph_integer_t 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; @@ -179,31 +169,30 @@ static int igraph_i_local_scan_1_directed(const igraph_t *graph, igraph_inclist_destroy(&incs); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, +static igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_inclist_t incs; - int i, node; + igraph_integer_t i, node; igraph_vector_int_t neis; IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs); - igraph_vector_int_init(&neis, no_of_nodes); - IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); - igraph_vector_resize(res, no_of_nodes); + 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 *edges1 = igraph_inclist_get(&incs, node); - int edgeslen1 = igraph_vector_int_size(edges1); + igraph_integer_t edgeslen1 = igraph_vector_int_size(edges1); IGRAPH_ALLOW_INTERRUPTION(); @@ -212,8 +201,8 @@ static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, 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_integer_t e = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e, node); igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; VECTOR(neis)[nei] = node + 1; VECTOR(*res)[node] += w; @@ -226,18 +215,18 @@ static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, only crawled 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_integer_t e2 = VECTOR(*edges1)[i]; + igraph_integer_t nei = IGRAPH_OTHER(graph, e2, node); igraph_vector_int_t *edges2; - int j, edgeslen2; + igraph_integer_t 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_integer_t e2 = VECTOR(*edges2)[j]; + igraph_integer_t 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; @@ -252,99 +241,7 @@ static int igraph_i_local_scan_1_directed_all(const igraph_t *graph, 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; + return IGRAPH_SUCCESS; } /** @@ -364,7 +261,7 @@ static int igraph_i_local_scan_1_sumweights(const igraph_t *graph, * */ -int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode) { @@ -375,24 +272,19 @@ int igraph_local_scan_1_ecount(const igraph_t *graph, igraph_vector_t *res, 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 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, +static igraph_error_t 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; + igraph_vector_int_t map2; + igraph_vector_t weights; + igraph_integer_t i, m; if (!weights_them) { IGRAPH_ERROR("Edge weights not given for weighted scan-0", @@ -402,24 +294,26 @@ static int igraph_i_local_scan_0_them_w(const igraph_t *us, const igraph_t *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_VECTOR_INT_INIT_FINALLY(&map2, 0); + IGRAPH_CHECK(igraph_intersection(&is, us, them, /* edge_map1= */ 0, &map2)); IGRAPH_FINALLY(igraph_destroy, &is); /* Rewrite the map as edge weights */ - m = igraph_vector_size(&map2); + m = igraph_vector_int_size(&map2); + IGRAPH_VECTOR_INIT_FINALLY(&weights, m); for (i = 0; i < m; i++) { - VECTOR(map2)[i] = VECTOR(*weights_them)[ (int) VECTOR(map2)[i] ]; + VECTOR(weights)[i] = VECTOR(*weights_them)[ VECTOR(map2)[i] ]; } - igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, - /*weights=*/ &map2); + IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, + /*weights=*/ &weights)); igraph_destroy(&is); - igraph_vector_destroy(&map2); - IGRAPH_FINALLY_CLEAN(2); + igraph_vector_int_destroy(&map2); + igraph_vector_destroy(&weights); + IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -441,7 +335,7 @@ static int igraph_i_local_scan_0_them_w(const igraph_t *us, const igraph_t *them * */ -int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, +igraph_error_t 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) { @@ -459,15 +353,15 @@ int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *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_CHECK(igraph_intersection(&is, us, them, /*edge_map1=*/ 0, /*edge_map2=*/ 0)); IGRAPH_FINALLY(igraph_destroy, &is); - igraph_degree(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS); + IGRAPH_CHECK(igraph_strength(&is, res, igraph_vss_all(), mode, IGRAPH_LOOPS, /* weights = */ 0)); igraph_destroy(&is); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -488,16 +382,16 @@ int igraph_local_scan_0_them(const igraph_t *us, const igraph_t *them, * \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_error_t 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_integer_t no_of_nodes = igraph_vcount(us); igraph_adjlist_t adj_us; igraph_inclist_t incs_them; igraph_vector_int_t neis; - int node; + igraph_integer_t node; if (igraph_vcount(them) != no_of_nodes) { IGRAPH_ERROR("Number of vertices must match in scan-1", IGRAPH_EINVAL); @@ -518,8 +412,7 @@ int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, 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_VECTOR_INT_INIT_FINALLY(&neis, no_of_nodes); IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); igraph_vector_null(res); @@ -527,23 +420,23 @@ int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, 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_integer_t len1_us = igraph_vector_int_size(neis_us); + igraph_integer_t len1_them = igraph_vector_int_size(edges1_them); + igraph_integer_t 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]; + igraph_integer_t 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); + igraph_integer_t e = VECTOR(*edges1_them)[i]; + igraph_integer_t 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; @@ -551,12 +444,12 @@ int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, } /* Then the rest */ for (i = 0; i < len1_us; i++) { - int nei = VECTOR(*neis_us)[i]; + igraph_integer_t 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); + igraph_integer_t 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); + igraph_integer_t e2 = VECTOR(*edges2_them)[j]; + igraph_integer_t 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; @@ -576,7 +469,7 @@ int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, igraph_adjlist_destroy(&adj_us); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -595,13 +488,13 @@ int igraph_local_scan_1_ecount_them(const igraph_t *us, const igraph_t *them, * */ -int igraph_local_scan_k_ecount(const igraph_t *graph, int k, +igraph_error_t igraph_local_scan_k_ecount(const igraph_t *graph, igraph_integer_t k, igraph_vector_t *res, const igraph_vector_t *weights, igraph_neimode_t mode) { - int no_of_nodes = igraph_vcount(graph); - int node; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t node; igraph_dqueue_int_t Q; igraph_vector_int_t marked; igraph_inclist_t incs; @@ -610,8 +503,8 @@ int igraph_local_scan_k_ecount(const igraph_t *graph, int k, 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_ERRORF("The weight vector length (%" IGRAPH_PRId ") in k-scan should equal " + "the number of edges of the graph (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(weights), igraph_ecount(graph)); } @@ -628,8 +521,7 @@ int igraph_local_scan_k_ecount(const igraph_t *graph, int k, 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_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); IGRAPH_CHECK(igraph_inclist_init(graph, &incs, mode, IGRAPH_LOOPS)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs); @@ -637,24 +529,24 @@ int igraph_local_scan_k_ecount(const igraph_t *graph, int k, igraph_vector_null(res); for (node = 0 ; node < no_of_nodes ; node++) { - igraph_dqueue_int_push(&Q, node); - igraph_dqueue_int_push(&Q, 0); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, node)); + IGRAPH_CHECK(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_integer_t act = igraph_dqueue_int_pop(&Q); + igraph_integer_t 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); + igraph_integer_t 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); + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, dist)); VECTOR(marked)[nei] = node + 1; } } @@ -676,7 +568,7 @@ int igraph_local_scan_k_ecount(const igraph_t *graph, int k, /** * \function igraph_local_scan_k_ecount_them - * Local THEM scan-statistics, general function, edge count and sum of weights + * \brief Local THEM scan-statistics, edge count or sum of weights. * * Count the number of edges or the sum the edge weights in the * k-neighborhood of vertices. @@ -694,31 +586,37 @@ int igraph_local_scan_k_ecount(const igraph_t *graph, int k, * \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, +igraph_error_t igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, + igraph_integer_t 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_integer_t no_of_nodes = igraph_vcount(us); + igraph_integer_t 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); + IGRAPH_ERROR("The number of vertices in the two graphs 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); + IGRAPH_ERROR("Directedness in the two graphs must match " + "in scan-k", IGRAPH_EINVAL); } if (k < 0) { - IGRAPH_ERROR("k must be non-negative in k-scan", IGRAPH_EINVAL); + IGRAPH_ERRORF("k must be non-negative in k-scan, got %" IGRAPH_PRId + ".", IGRAPH_EINVAL, k); } if (weights_them && igraph_vector_size(weights_them) != igraph_ecount(them)) { - IGRAPH_ERROR("Invalid weight vector length in k-scan (them)", - IGRAPH_EINVAL); + IGRAPH_ERRORF("The weight vector length (%" IGRAPH_PRId + ") must be equal to the number of edges (%" IGRAPH_PRId + ").", IGRAPH_EINVAL, igraph_vector_size(weights_them), + igraph_ecount(them)); } if (k == 0) { @@ -733,8 +631,7 @@ int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *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_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); 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)); @@ -753,30 +650,30 @@ int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, 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_integer_t act = igraph_dqueue_int_pop(&Q); + igraph_integer_t 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); + igraph_integer_t 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); + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t 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); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, dist)); VECTOR(marked)[nei] = node + 1; - igraph_stack_int_push(&ST, nei); + IGRAPH_CHECK(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_integer_t 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); + igraph_integer_t 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); + igraph_integer_t edge = VECTOR(*edges)[i]; + igraph_integer_t 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; @@ -797,80 +694,73 @@ int igraph_local_scan_k_ecount_them(const igraph_t *us, const igraph_t *them, igraph_dqueue_int_destroy(&Q); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } - /** - * \function igraph_local_scan_neighborhood_ecount - * Local scan-statistics with pre-calculated neighborhoods + * \function igraph_local_scan_subset_ecount + * \brief Local scan-statistics of subgraphs induced by subsets of vertices. * - * Count the number of edges, or sum the edge weigths in - * neighborhoods given as a parameter. + * Count the number of edges, or sum the edge weights in + * induced subgraphs from vertices 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. + * \param subsets List of \type igraph_vector_int_t + * objects, the vertex subsets. * \return Error code. */ -int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, +igraph_error_t igraph_local_scan_subset_ecount(const igraph_t *graph, igraph_vector_t *res, const igraph_vector_t *weights, - const igraph_vector_ptr_t *neighborhoods) { + const igraph_vector_int_list_t *subsets) { - int node, no_of_nodes = igraph_vcount(graph); + igraph_integer_t subset, no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_subsets = igraph_vector_int_list_size(subsets); 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_ERROR("Invalid weight vector length in local scan.", IGRAPH_EINVAL); } - IGRAPH_CHECK(igraph_vector_int_init(&marked, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &marked); + IGRAPH_VECTOR_INT_INIT_FINALLY(&marked, no_of_nodes); IGRAPH_CHECK(igraph_inclist_init(graph, &incs, IGRAPH_OUT, IGRAPH_LOOPS_TWICE)); IGRAPH_FINALLY(igraph_inclist_destroy, &incs); - IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_resize(res, no_of_subsets)); 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 (subset = 0; subset < no_of_subsets; subset++) { + igraph_vector_int_t *nei = igraph_vector_int_list_get_ptr(subsets, subset); + igraph_integer_t i, neilen = igraph_vector_int_size(nei); for (i = 0; i < neilen; i++) { - int vertex = VECTOR(*nei)[i]; + igraph_integer_t vertex = VECTOR(*nei)[i]; if (vertex < 0 || vertex >= no_of_nodes) { - IGRAPH_ERROR("Invalid vertex id in neighborhood list in local scan", + IGRAPH_ERROR("Invalid vertex ID in neighborhood list in local scan.", IGRAPH_EINVAL); } - VECTOR(marked)[vertex] = node + 1; + VECTOR(marked)[vertex] = subset + 1; } for (i = 0; i < neilen; i++) { - int vertex = VECTOR(*nei)[i]; + igraph_integer_t vertex = VECTOR(*nei)[i]; igraph_vector_int_t *edges = igraph_inclist_get(&incs, vertex); - int j, edgeslen = igraph_vector_int_size(edges); + igraph_integer_t 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_integer_t edge = VECTOR(*edges)[j]; + igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge, vertex); + if (VECTOR(marked)[nei2] == subset + 1) { igraph_real_t w = weights ? VECTOR(*weights)[edge] : 1; - VECTOR(*res)[node] += w; + VECTOR(*res)[subset] += w; } } } if (!directed) { - VECTOR(*res)[node] /= 2.0; + VECTOR(*res)[subset] /= 2.0; } } @@ -878,5 +768,39 @@ int igraph_local_scan_neighborhood_ecount(const igraph_t *graph, igraph_vector_int_destroy(&marked); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_local_scan_neighborhood_ecount + * Local scan-statistics with pre-calculated neighborhoods + * + * Count the number of edges, or sum the edge weights in + * neighborhoods given as a parameter. + * + * \deprecated-by igraph_local_scan_subset_ecount 0.10.0 + * + * \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 \type igraph_vector_int_t + * objects, the neighborhoods, one for each vertex in the + * graph. + * \return Error code. + */ + +igraph_error_t igraph_local_scan_neighborhood_ecount(const igraph_t *graph, + igraph_vector_t *res, + const igraph_vector_t *weights, + const igraph_vector_int_list_t *neighborhoods) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + if (igraph_vector_int_list_size(neighborhoods) != no_of_nodes) { + IGRAPH_ERROR("Invalid neighborhood list length in local scan.", + IGRAPH_EINVAL); + } + + return igraph_local_scan_subset_ecount(graph, res, weights, neighborhoods); } diff --git a/src/vendor/cigraph/src/misc/sir.c b/src/vendor/cigraph/src/misc/sir.c index 4adb12a1d3a..0e63307c16b 100644 --- a/src/vendor/cigraph/src/misc/sir.c +++ b/src/vendor/cigraph/src/misc/sir.c @@ -32,7 +32,7 @@ #include "core/interruption.h" -int igraph_sir_init(igraph_sir_t *sir) { +igraph_error_t 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)); @@ -41,7 +41,7 @@ int igraph_sir_init(igraph_sir_t *sir) { 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; + return IGRAPH_SUCCESS; } /** @@ -59,7 +59,7 @@ void igraph_sir_destroy(igraph_sir_t *sir) { } static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { - int i, n = igraph_vector_ptr_size(v); + igraph_integer_t i, n = igraph_vector_ptr_size(v); for (i = 0; i < n; i++) { if ( VECTOR(*v)[i] ) { igraph_sir_destroy( VECTOR(*v)[i]) ; @@ -80,7 +80,7 @@ static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { * 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 + * their number of infected neighbors. Infected people become recovered * with a constant rate. See these parameters below. * * @@ -109,27 +109,24 @@ static void igraph_i_sir_destroy(igraph_vector_ptr_t *v) { * Time complexity: O(no_sim * (|V| + |E| log(|V|))). */ -int igraph_sir(const igraph_t *graph, igraph_real_t beta, +igraph_error_t 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_integer_t infected; igraph_vector_int_t status; igraph_adjlist_t adjlist; - int no_of_nodes = igraph_vcount(graph); - int i, j, ns, ni, nr; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, ns, ni, nr; igraph_vector_int_t *neis; igraph_psumtree_t tree; igraph_real_t psum; - int neilen; + igraph_integer_t 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); } @@ -145,6 +142,16 @@ int igraph_sir(const igraph_t *graph, igraph_real_t beta, if (!simple) { IGRAPH_ERROR("SIR model only works with simple graphs.", IGRAPH_EINVAL); } + if (igraph_is_directed(graph)) { + igraph_bool_t has_mutual; + IGRAPH_WARNING("Edge directions are ignored in SIR model."); + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &has_mutual, false)); + if (has_mutual) { + 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); @@ -159,7 +166,7 @@ int igraph_sir(const igraph_t *graph, igraph_real_t beta, 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_ERROR("Cannot run SIR model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_CHECK(igraph_sir_init(sir)); VECTOR(*result)[i] = sir; @@ -198,14 +205,14 @@ int igraph_sir(const igraph_t *graph, igraph_real_t beta, neis = igraph_adjlist_get(&adjlist, infected); neilen = igraph_vector_int_size(neis); for (i = 0; i < neilen; i++) { - int nei = VECTOR(*neis)[i]; + igraph_integer_t 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_integer_t vchange; IGRAPH_ALLOW_INTERRUPTION(); @@ -222,7 +229,7 @@ int igraph_sir(const igraph_t *graph, igraph_real_t beta, ni--; nr++; IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, 0.0)); for (i = 0; i < neilen; i++) { - int nei = VECTOR(*neis)[i]; + igraph_integer_t 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)); @@ -234,7 +241,7 @@ int igraph_sir(const igraph_t *graph, igraph_real_t beta, ns--; ni++; IGRAPH_CHECK(igraph_psumtree_update(&tree, vchange, gamma)); for (i = 0; i < neilen; i++) { - int nei = VECTOR(*neis)[i]; + igraph_integer_t 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)); diff --git a/src/vendor/cigraph/src/misc/spanning_trees.c b/src/vendor/cigraph/src/misc/spanning_trees.c index 89d5aaf7a6c..fd3c45e52c7 100644 --- a/src/vendor/cigraph/src/misc/spanning_trees.c +++ b/src/vendor/cigraph/src/misc/spanning_trees.c @@ -1,9 +1,6 @@ -/* -*- 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 + Copyright (C) 2011-2023 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 @@ -16,10 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - + along with this program. If not, see . */ #include "igraph_adjlist.h" @@ -28,40 +22,38 @@ #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); +static igraph_error_t igraph_i_minimum_spanning_tree_unweighted( + const igraph_t *graph, igraph_vector_int_t *result); +static igraph_error_t igraph_i_minimum_spanning_tree_prim( + const igraph_t *graph, igraph_vector_int_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. + * Finds a spanning tree of the graph. 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. * * * 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. + * This function is deterministic, i.e. it always returns the same + * spanning tree. See \ref igraph_random_spanning_tree() for the uniform + * random sampling of spanning trees of a graph. * * \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 + * \ref igraph_subgraph_from_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 @@ -80,9 +72,10 @@ static int igraph_i_minimum_spanning_tree_prim(const igraph_t *graph, * * \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_error_t igraph_minimum_spanning_tree( + const igraph_t *graph, igraph_vector_int_t *res, const igraph_vector_t *weights +) { + if (weights == NULL) { IGRAPH_CHECK(igraph_i_minimum_spanning_tree_unweighted(graph, res)); } else { IGRAPH_CHECK(igraph_i_minimum_spanning_tree_prim(graph, res, weights)); @@ -95,7 +88,6 @@ int igraph_minimum_spanning_tree(const igraph_t* graph, * \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. @@ -107,7 +99,8 @@ int igraph_minimum_spanning_tree(const igraph_t* graph, * 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 graph The graph object. Edge directions will be ignored. * \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 @@ -126,19 +119,19 @@ int igraph_minimum_spanning_tree(const igraph_t* graph, * edges that constitute the spanning tree. */ -int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, +igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, igraph_t *mst) { - igraph_vector_t edges = IGRAPH_VECTOR_NULL; + igraph_vector_int_t edges; igraph_integer_t no_of_nodes = igraph_vcount(graph); - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_nodes > 0 ? no_of_nodes - 1 : 0); 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_CHECK(igraph_subgraph_from_edges( + graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -146,26 +139,24 @@ int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, * \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. + * Finds a spanning tree or spanning forest for which the sum of edge + * weights is the smallest. This function uses Prim's method for carrying + * out the computation. * * - * 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. * * - * Directed graphs are considered as undirected for this computation. + * Reference: * * - * 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. + * Prim, R.C.: Shortest connection networks and some + * generalizations, Bell System Technical + * Journal, Vol. 36, + * 1957, 1389--1401. + * https://doi.org/10.1002/j.1538-7305.1957.tb01515.x * - * \param graph The graph object. + * \param graph The graph object. Edge directions will be ignored. * \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 @@ -191,171 +182,166 @@ int igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, * \example examples/simple/igraph_minimum_spanning_tree.c */ -int igraph_minimum_spanning_tree_prim(const igraph_t *graph, igraph_t *mst, +igraph_error_t 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_int_t edges; - IGRAPH_VECTOR_INIT_FINALLY(&edges, igraph_vcount(graph) - 1); + IGRAPH_VECTOR_INT_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_CHECK(igraph_subgraph_from_edges( + graph, mst, igraph_ess_vector(&edges), /* delete_vertices = */ false)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -static int igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_t* res) { +static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* graph, igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + bool *already_added, *added_edges; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; - igraph_vector_t tmp = IGRAPH_VECTOR_NULL; - long int i, j; + igraph_dqueue_int_t q; + igraph_vector_int_t eids; - igraph_vector_clear(res); + igraph_vector_int_clear(res); - added_edges = IGRAPH_CALLOC(no_of_edges, char); - if (added_edges == 0) { - IGRAPH_ERROR("unweighted spanning tree failed", IGRAPH_ENOMEM); - } + added_edges = IGRAPH_CALLOC(no_of_edges, bool); + IGRAPH_CHECK_OOM(added_edges, "Insufficient memory for unweighted spanning tree."); 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); - } + + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for unweighted spanning tree."); 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) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + + /* Perform a BFS */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (already_added[i]) { 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, + already_added[i] = true; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + while (! igraph_dqueue_int_empty(&q)) { + igraph_integer_t eids_size; + igraph_integer_t act_node = igraph_dqueue_int_pop(&q); + IGRAPH_CHECK(igraph_incident(graph, &eids, 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) { + eids_size = igraph_vector_int_size(&eids); + for (igraph_integer_t j = 0; j < eids_size; j++) { + igraph_integer_t edge = VECTOR(eids)[j]; + if (! added_edges[edge]) { 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)); + if (! already_added[to]) { + already_added[to] = true; + added_edges[edge] = true; + IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, to)); } } } } } - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&eids); 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; +static igraph_error_t igraph_i_minimum_spanning_tree_prim( + const igraph_t* graph, igraph_vector_int_t* res, const igraph_vector_t *weights) { - igraph_d_indheap_t heap = IGRAPH_D_INDHEAP_NULL; - igraph_integer_t mode = IGRAPH_ALL; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + bool *already_added, *added_edges; - igraph_vector_t adj; + igraph_d_indheap_t heap; + const igraph_neimode_t mode = IGRAPH_ALL; - long int i, j; + igraph_vector_int_t adj; - igraph_vector_clear(res); + igraph_vector_int_clear(res); - if (weights == 0) { + if (weights == NULL) { return igraph_i_minimum_spanning_tree_unweighted(graph, res); } if (igraph_vector_size(weights) != igraph_ecount(graph)) { - IGRAPH_ERROR("Invalid weights length", IGRAPH_EINVAL); + IGRAPH_ERROR("Weight vector length does not match number of edges.", IGRAPH_EINVAL); } - added_edges = IGRAPH_CALLOC(no_of_edges, char); - if (added_edges == 0) { - IGRAPH_ERROR("prim spanning tree failed", IGRAPH_ENOMEM); + if (igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weigths must not contain NaN values.", IGRAPH_EINVAL); } + + added_edges = IGRAPH_CALLOC(no_of_edges, bool); + IGRAPH_CHECK_OOM(added_edges, "Insufficient memory for minimum spanning tree calculation."); 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); - } + + already_added = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for minimum spanning tree calculation."); 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) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&adj, 0); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t adj_size; + if (already_added[i]) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[i] = 1; + already_added[i] = true; /* 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_CHECK(igraph_incident(graph, &adj, i, mode)); + adj_size = igraph_vector_int_size(&adj); + for (igraph_integer_t j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = 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)); + if (! already_added[neighbor]) { + 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_integer_t 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) { + if (! added_edges[edge]) { 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)); + if (! already_added[to]) { + already_added[to] = true; + added_edges[edge] = true; + IGRAPH_CHECK(igraph_vector_int_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)); + IGRAPH_CHECK(igraph_incident(graph, &adj, to, mode)); + adj_size = igraph_vector_int_size(&adj); + for (igraph_integer_t j = 0; j < adj_size; j++) { + igraph_integer_t edgeno = VECTOR(adj)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, to); + if (! already_added[neighbor]) { + IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], to, edgeno)); } } } /* for */ @@ -365,7 +351,7 @@ static int igraph_i_minimum_spanning_tree_prim( igraph_d_indheap_destroy(&heap); IGRAPH_FREE(already_added); - igraph_vector_destroy(&adj); + igraph_vector_int_destroy(&adj); IGRAPH_FREE(added_edges); IGRAPH_FINALLY_CLEAN(4); @@ -382,19 +368,19 @@ static int igraph_i_minimum_spanning_tree_prim( * 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, +static igraph_error_t igraph_i_lerw(const igraph_t *graph, igraph_vector_int_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(); + IGRAPH_CHECK(igraph_vector_int_reserve(res, igraph_vector_int_size(res) + comp_size - 1)); VECTOR(*visited)[start] = 1; visited_count = 1; + RNG_BEGIN(); + while (visited_count < comp_size) { - long degree, edge; + igraph_integer_t degree, edge; igraph_vector_int_t *edges; edges = igraph_inclist_get(il, start); @@ -408,7 +394,7 @@ static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_int /* 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); VECTOR(*visited)[start] = 1; visited_count++; } @@ -423,7 +409,7 @@ static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_int /** * \function igraph_random_spanning_tree - * \brief Uniformly sample the spanning trees of a graph + * \brief Uniformly samples 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. @@ -438,7 +424,7 @@ static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_int * \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 + * \ref igraph_subgraph_from_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 @@ -451,13 +437,13 @@ static int igraph_i_lerw(const igraph_t *graph, igraph_vector_t *res, igraph_int * \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_error_t igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_int_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_ERROR("Invalid vertex ID given for random spanning tree.", IGRAPH_EINVVID); } IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); @@ -466,41 +452,40 @@ int igraph_random_spanning_tree(const igraph_t *graph, igraph_vector_t *res, igr IGRAPH_CHECK(igraph_vector_bool_init(&visited, vcount)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); - igraph_vector_clear(res); + igraph_vector_int_clear(res); if (vid < 0) { /* generate random spanning forest: consider each component separately */ - igraph_vector_t membership, csize; + igraph_vector_int_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_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); - IGRAPH_CHECK(igraph_clusters(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(graph, &membership, &csize, &comp_count, IGRAPH_WEAK)); /* for each component ... */ - for (i = 0; i < comp_count; ++i) { + for (igraph_integer_t 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_CHECK(igraph_i_lerw(graph, res, j, VECTOR(csize)[i], &visited, &il)); } - igraph_vector_destroy(&membership); - igraph_vector_destroy(&csize); + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&csize); IGRAPH_FINALLY_CLEAN(2); } else { /* consider the component containing vid */ - igraph_vector_t comp_vertices; + igraph_vector_int_t comp_vertices; igraph_integer_t comp_size; /* we measure the size of the component */ - IGRAPH_VECTOR_INIT_FINALLY(&comp_vertices, 0); + IGRAPH_VECTOR_INT_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); + comp_size = igraph_vector_int_size(&comp_vertices); + igraph_vector_int_destroy(&comp_vertices); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_i_lerw(graph, res, vid, comp_size, &visited, &il)); diff --git a/src/vendor/cigraph/src/operators/add_edge.c b/src/vendor/cigraph/src/operators/add_edge.c index 2f5bbcea87d..70a5be54ca8 100644 --- a/src/vendor/cigraph/src/operators/add_edge.c +++ b/src/vendor/cigraph/src/operators/add_edge.c @@ -48,17 +48,16 @@ * 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_error_t igraph_add_edge(igraph_t *graph, igraph_integer_t from, igraph_integer_t to) { + igraph_vector_int_t edges; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2); VECTOR(edges)[0] = from; VECTOR(edges)[1] = to; - IGRAPH_CHECK(ret = igraph_add_edges(graph, &edges, 0)); + IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return ret; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/complementer.c b/src/vendor/cigraph/src/operators/complementer.c index fe711882bc0..569617afe20 100644 --- a/src/vendor/cigraph/src/operators/complementer.c +++ b/src/vendor/cigraph/src/operators/complementer.c @@ -30,9 +30,9 @@ /** * \function igraph_complementer - * \brief Create the complementer of a graph + * \brief Creates the complementer of a graph. * - * The complementer graph means that all edges which are + * 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. @@ -48,17 +48,17 @@ * * \example examples/simple/igraph_complementer.c */ -int igraph_complementer(igraph_t *res, const igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; + igraph_vector_int_t neis; + igraph_integer_t i, j; + igraph_integer_t zero = 0, *limit; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); if (igraph_is_directed(graph)) { limit = &zero; @@ -68,37 +68,35 @@ int igraph_complementer(igraph_t *res, const igraph_t *graph, for (i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, - IGRAPH_OUT)); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, 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)); + if (igraph_vector_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } else { - igraph_vector_pop_back(&neis); + igraph_vector_int_pop_back(&neis); } } } else { for (j = no_of_nodes - 1; j >= *limit; j--) { - if (igraph_vector_empty(&neis) || j > igraph_vector_tail(&neis)) { + if (igraph_vector_int_empty(&neis) || j > igraph_vector_int_tail(&neis)) { if (i != j) { - IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, j)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); } } else { - igraph_vector_pop_back(&neis); + igraph_vector_int_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_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); + igraph_vector_int_destroy(&edges); + igraph_vector_int_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; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/compose.c b/src/vendor/cigraph/src/operators/compose.c index ad7aa710836..625c1021990 100644 --- a/src/vendor/cigraph/src/operators/compose.c +++ b/src/vendor/cigraph/src/operators/compose.c @@ -64,16 +64,16 @@ * * \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) { +igraph_error_t igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, + igraph_vector_int_t *edge_map1, igraph_vector_int_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_integer_t no_of_nodes_left = igraph_vcount(g1); + igraph_integer_t no_of_nodes_right = igraph_vcount(g2); + igraph_integer_t no_of_nodes; igraph_bool_t directed = igraph_is_directed(g1); - igraph_vector_t edges; - igraph_vector_t neis1, neis2; - long int i; + igraph_vector_int_t edges; + igraph_vector_int_t neis1, neis2; + igraph_integer_t i; if (directed != igraph_is_directed(g2)) { IGRAPH_ERROR("Cannot compose directed and undirected graph", @@ -83,53 +83,52 @@ int igraph_compose(igraph_t *res, const igraph_t *g1, const igraph_t *g2, 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis2, 0); if (edge_map1) { - igraph_vector_clear(edge_map1); + igraph_vector_int_clear(edge_map1); } if (edge_map2) { - igraph_vector_clear(edge_map2); + igraph_vector_int_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_CHECK(igraph_incident(g1, &neis1, 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); + while (!igraph_vector_int_empty(&neis1)) { + igraph_integer_t con = igraph_vector_int_pop_back(&neis1); + igraph_integer_t v1 = IGRAPH_OTHER(g1, con, i); if (v1 < no_of_nodes_right) { - IGRAPH_CHECK(igraph_incident(g2, &neis2, (igraph_integer_t) v1, + IGRAPH_CHECK(igraph_incident(g2, &neis2, 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)); + while (!igraph_vector_int_empty(&neis2)) { + igraph_integer_t con2 = igraph_vector_int_pop_back(&neis2); + igraph_integer_t v2 = IGRAPH_OTHER(g2, con2, v1); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v2)); if (edge_map1) { - IGRAPH_CHECK(igraph_vector_push_back(edge_map1, con)); + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map1, con)); } if (edge_map2) { - IGRAPH_CHECK(igraph_vector_push_back(edge_map2, con2)); + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, con2)); } } } } - igraph_vector_destroy(&neis1); - igraph_vector_destroy(&neis2); + igraph_vector_int_destroy(&neis1); + igraph_vector_int_destroy(&neis2); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, - directed)); + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/connect_neighborhood.c b/src/vendor/cigraph/src/operators/connect_neighborhood.c index 1ab6f2a8da7..af382f49088 100644 --- a/src/vendor/cigraph/src/operators/connect_neighborhood.c +++ b/src/vendor/cigraph/src/operators/connect_neighborhood.c @@ -28,7 +28,7 @@ /** * \function igraph_connect_neighborhood - * \brief Connects every vertex to its neighborhood + * \brief Graph power: connect each 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 @@ -53,24 +53,25 @@ * considered as an undirected one. * \return Error code. * - * \sa \ref igraph_lattice() uses this function to connect the + * \sa \ref igraph_square_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_error_t 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; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; + igraph_vector_int_t edges; + igraph_integer_t i, j, in; + igraph_integer_t *added; + igraph_vector_int_t neis; if (order < 0) { - IGRAPH_ERROR("Negative order, cannot connect neighborhood", IGRAPH_EINVAL); + IGRAPH_ERRORF("Order can not be negative, found %" IGRAPH_PRId ".", + IGRAPH_EINVAL, order); } if (order < 2) { @@ -81,65 +82,65 @@ int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, mode = IGRAPH_ALL; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - added = IGRAPH_CALLOC(no_of_nodes, long int); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (added == 0) { - IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot connect neighborhood", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); + in = igraph_vector_int_size(&neis); if (order > 1) { for (j = 0; j < in; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = VECTOR(neis)[j]; added[nei] = i + 1; - igraph_dqueue_push(&q, nei); - igraph_dqueue_push(&q, 1); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_size(&neis); if (actdist < order - 1) { for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); } else { - IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); } } } } } else { for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); } else { - IGRAPH_CHECK(igraph_vector_push_back(&edges, i)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, nei)); } } } @@ -149,15 +150,15 @@ int igraph_connect_neighborhood(igraph_t *graph, igraph_integer_t order, } /* while q not empty */ } /* for i < no_of_nodes */ - igraph_vector_destroy(&neis); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&q); igraph_free(added); IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_add_edges(graph, &edges, 0)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/contract.c b/src/vendor/cigraph/src/operators/contract.c index af2fbe2c273..ea0787dab02 100644 --- a/src/vendor/cigraph/src/operators/contract.c +++ b/src/vendor/cigraph/src/operators/contract.c @@ -24,33 +24,29 @@ #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. + * \brief Replace multiple vertices with a single one. + * + * This function modifies the graph by merging several vertices + * into one. The vertices in the modified graph correspond + * to groups of vertices in the input graph. No edges are removed, + * thus the modified graph will typically have self-loops + * (corresponding to in-group edges) and multi-edges + * (corresponding to multiple connections between two groups). + * Use \ref igraph_simplify() to eliminate self-loops and + * merge multi-edges. * - * 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 graph The input graph. It will be modified in-place. * \param mapping A vector giving the mapping. For each * vertex in the original graph, it should contain - * its id in the new graph. + * its desired ID in the result graph. In order to create + * "orphan vertices" that have no corresponding vertices + * in the original graph, ensure that the IDs are consecutive + * integers starting from zero. * \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 @@ -62,38 +58,39 @@ static void igraph_i_simplify_free(igraph_vector_ptr_t *p) { * or vertices plus edges. */ -int igraph_contract_vertices(igraph_t *graph, - const igraph_vector_t *mapping, +igraph_error_t igraph_contract_vertices(igraph_t *graph, + const igraph_vector_int_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_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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; + igraph_integer_t e, last = -1; + igraph_integer_t no_new_vertices; - if (igraph_vector_size(mapping) != no_of_nodes) { - IGRAPH_ERROR("Invalid mapping vector length", - IGRAPH_EINVAL); + if (igraph_vector_int_size(mapping) != no_of_nodes) { + IGRAPH_ERRORF("Mapping vector length (%" IGRAPH_PRId ") " + "not equal to number of nodes (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, igraph_vector_int_size(mapping), no_of_nodes); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); if (no_of_nodes > 0) { - last = (long int) igraph_vector_max(mapping); + last = igraph_vector_int_max(mapping); } for (e = 0; e < no_of_edges; e++) { - long int from = IGRAPH_FROM(graph, e); - long int to = IGRAPH_TO(graph, e); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); - long int nfrom = (long int) VECTOR(*mapping)[from]; - long int nto = (long int) VECTOR(*mapping)[to]; + igraph_integer_t nfrom = VECTOR(*mapping)[from]; + igraph_integer_t nto = VECTOR(*mapping)[to]; - igraph_vector_push_back(&edges, nfrom); - igraph_vector_push_back(&edges, nto); + igraph_vector_int_push_back(&edges, nfrom); + igraph_vector_int_push_back(&edges, nto); if (nfrom > last) { last = nfrom; @@ -105,10 +102,10 @@ int igraph_contract_vertices(igraph_t *graph, no_new_vertices = last + 1; - IGRAPH_CHECK(igraph_create(&res, &edges, (igraph_integer_t) no_new_vertices, + IGRAPH_CHECK(igraph_create(&res, &edges, no_new_vertices, igraph_is_directed(graph))); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &res); @@ -118,50 +115,32 @@ int igraph_contract_vertices(igraph_t *graph, /*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); + igraph_integer_t i; + igraph_vector_int_list_t merges; + igraph_vector_int_t sizes; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&merges, no_new_vertices); + IGRAPH_VECTOR_INT_INIT_FINALLY(&sizes, no_new_vertices); for (i = 0; i < no_of_nodes; i++) { - long int to = (long int) VECTOR(*mapping)[i]; + igraph_integer_t to = VECTOR(*mapping)[i]; + igraph_vector_int_t *v = igraph_vector_int_list_get_ptr(&merges, to); 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_vector_int_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_vector_int_destroy(&sizes); + igraph_vector_int_list_destroy(&merges); + IGRAPH_FINALLY_CLEAN(2); } IGRAPH_FINALLY_CLEAN(1); igraph_destroy(graph); *graph = res; - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/difference.c b/src/vendor/cigraph/src/operators/difference.c index c229068f441..f9c5786d65a 100644 --- a/src/vendor/cigraph/src/operators/difference.c +++ b/src/vendor/cigraph/src/operators/difference.c @@ -31,9 +31,8 @@ /** * \function igraph_difference - * \brief Calculate the difference of two graphs + * \brief Calculates 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 @@ -53,32 +52,32 @@ * * \example examples/simple/igraph_difference.c */ -int igraph_difference(igraph_t *res, +igraph_error_t 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. */ + 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_integer_t no_of_nodes_orig = igraph_vcount(orig); + igraph_integer_t no_of_nodes_sub = igraph_vcount(sub); + igraph_integer_t no_of_nodes = no_of_nodes_orig; + igraph_integer_t smaller_nodes; igraph_bool_t directed = igraph_is_directed(orig); - igraph_vector_t edges; - igraph_vector_t edge_ids; + igraph_vector_int_t edges; + igraph_vector_int_t edge_ids; igraph_vector_int_t *nei1, *nei2; igraph_inclist_t inc_orig, inc_sub; - long int i; + igraph_integer_t i; igraph_integer_t v1, v2; if (directed != igraph_is_directed(sub)) { - IGRAPH_ERROR("Cannot subtract directed and undirected graphs", + IGRAPH_ERROR("Cannot subtract directed and undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edge_ids, 0); - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_ids, 0); + IGRAPH_VECTOR_INT_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)); @@ -88,15 +87,15 @@ int igraph_difference(igraph_t *res, no_of_nodes_sub : no_of_nodes_orig; for (i = 0; i < smaller_nodes; i++) { - long int n1, n2, e1, e2; + igraph_integer_t 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]; + e1 = VECTOR(*nei1)[n1]; + e2 = VECTOR(*nei2)[n2]; v1 = IGRAPH_OTHER(orig, e1, i); v2 = IGRAPH_OTHER(sub, e2, i); @@ -105,9 +104,9 @@ int igraph_difference(igraph_t *res, } 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); n1--; /* handle loop edges properly in undirected graphs */ if (!directed && i == v1) { @@ -123,12 +122,12 @@ int igraph_difference(igraph_t *res, /* Copy remaining edges */ while (n1 >= 0) { - e1 = (long int) VECTOR(*nei1)[n1]; + e1 = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); /* handle loop edges properly in undirected graphs */ if (!directed && v1 == i) { @@ -141,16 +140,16 @@ int igraph_difference(igraph_t *res, /* copy remaining edges, use the previous value of 'i' */ for (; i < no_of_nodes_orig; i++) { - long int n1, e1; + igraph_integer_t 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]; + e1 = 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edge_ids, e1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v1)); /* handle loop edges properly in undirected graphs */ if (!directed && v1 == i) { @@ -164,20 +163,21 @@ int igraph_difference(igraph_t *res, 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_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + + igraph_vector_int_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_I_ATTRIBUTE_COPY(res, orig, /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); IGRAPH_CHECK(igraph_i_attribute_permute_edges(orig, res, &edge_ids)); } - igraph_vector_destroy(&edge_ids); + igraph_vector_int_destroy(&edge_ids); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/disjoint_union.c b/src/vendor/cigraph/src/operators/disjoint_union.c index 0ea11a945bf..c8cedb8394b 100644 --- a/src/vendor/cigraph/src/operators/disjoint_union.c +++ b/src/vendor/cigraph/src/operators/disjoint_union.c @@ -25,15 +25,12 @@ #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 + * \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 + * 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 @@ -60,52 +57,53 @@ * * \example examples/simple/igraph_disjoint_union.c */ -int igraph_disjoint_union(igraph_t *res, const igraph_t *left, +igraph_error_t 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_integer_t no_of_nodes_left = igraph_vcount(left); + igraph_integer_t no_of_nodes_right = igraph_vcount(right); + igraph_integer_t no_of_edges_left = igraph_ecount(left); + igraph_integer_t no_of_edges_right = igraph_ecount(right); + igraph_vector_int_t edges; igraph_bool_t directed_left = igraph_is_directed(left); igraph_integer_t from, to; - long int i; + igraph_integer_t i; if (directed_left != igraph_is_directed(right)) { - IGRAPH_ERROR("Cannot union directed and undirected graphs", + IGRAPH_ERROR("Cannot create disjoint union of directed and undirected graphs.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_edge(left, i, &from, &to); + igraph_vector_int_push_back(&edges, from); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ } 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_edge(right, i, &from, &to); + igraph_vector_int_push_back(&edges, from + no_of_nodes_left); /* reserved */ + igraph_vector_int_push_back(&edges, to + no_of_nodes_left); /* reserved */ } - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) + IGRAPH_CHECK(igraph_create(res, &edges, (no_of_nodes_left + no_of_nodes_right), directed_left)); - igraph_vector_destroy(&edges); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } /** * \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 + * 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. @@ -130,15 +128,15 @@ int igraph_disjoint_union(igraph_t *res, const igraph_t *left, * 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, +igraph_error_t 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_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_integer_t no_of_edges = 0; + igraph_integer_t shift = 0; igraph_t *graph; - long int i, j; + igraph_integer_t i, j; igraph_integer_t from, to; if (no_of_graphs != 0) { @@ -148,29 +146,31 @@ int igraph_disjoint_union_many(igraph_t *res, 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_ERROR("Cannot create disjoint union of directed and undirected graphs.", IGRAPH_EINVAL); } } } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, 2 * no_of_edges)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2 * no_of_edges)); for (i = 0; i < no_of_graphs; i++) { - long int ec; + igraph_integer_t 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); + igraph_edge(graph, j, &from, &to); + igraph_vector_int_push_back(&edges, from + shift); /* reserved */ + igraph_vector_int_push_back(&edges, to + shift); /* reserved */ } shift += igraph_vcount(graph); } - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) shift, directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(res, &edges, shift, directed)); + + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/intersection.c b/src/vendor/cigraph/src/operators/intersection.c index 610c3cd1a8a..9e5a9aeda68 100644 --- a/src/vendor/cigraph/src/operators/intersection.c +++ b/src/vendor/cigraph/src/operators/intersection.c @@ -25,31 +25,39 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_interface.h" -#include "igraph_memory.h" #include "igraph_qsort.h" +#include "igraph_vector_list.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. * + * + * The directedness of the operand graphs must be the same. + * + * + * Edge multiplicities are handled by taking the \em smaller of the two + * multiplicities in the input graphs. In other words, if the first graph + * has N edges between a vertex pair (u, v) and the second graph has M edges, + * the result graph will have min(N, M) edges between them. + * * \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. + * \param edge_map1 Null pointer, or an initialized vector. * 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. + * the edges of the \p left input graph is stored here. For the edges that + * are not in the intersection, -1 is stored. + * \param edge_map2 Null pointer, or an initialized vector. The same + * as \p edge_map1, but for the \p right input graph. For the edges that + * are not in the intersection, -1 is stored. * \return Error code. * \sa \ref igraph_intersection_many() to calculate the intersection * of many graphs at once, \ref igraph_union(), \ref @@ -61,10 +69,10 @@ * * \example examples/simple/igraph_intersection.c */ -int igraph_intersection(igraph_t *res, +igraph_error_t igraph_intersection(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_t *edge_map1, - igraph_vector_t *edge_map2) { + igraph_vector_int_t *edge_map1, + igraph_vector_int_t *edge_map2) { return igraph_i_merge(res, IGRAPH_MERGE_MODE_INTERSECTION, left, right, edge_map1, edge_map2); } @@ -73,7 +81,6 @@ int igraph_intersection(igraph_t *res, * \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. @@ -82,15 +89,25 @@ int igraph_intersection(igraph_t *res, * The number of vertices in the result graph will be the maximum * number of vertices in the argument graphs. * + * + * The directedness of the argument graphs must be the same. + * If the graph list has length zero, the result will be a \em directed + * graph with no vertices. + * + * + * Edge multiplicities are handled by taking the \em minimum multiplicity of the + * all multiplicities for the same vertex pair (u, v) in the input graphs; this + * will be the multiplicity of (u, v) in the result graph. + * * \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 + * list of integer vectors, 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 + * \type igraph_vector_int_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, @@ -101,19 +118,21 @@ int igraph_intersection(igraph_t *res, * |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) { +igraph_error_t igraph_intersection_many( + igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_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; + igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_integer_t no_of_nodes = 0; + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_vector_int_list_t edge_vects, order_vects; + igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + igraph_vector_int_t no_edges; + igraph_bool_t allne = no_of_graphs > 0; + igraph_bool_t allsame = false; + igraph_integer_t idx = 0; /* Check directedness */ if (no_of_graphs != 0) { @@ -121,24 +140,18 @@ int igraph_intersection_many(igraph_t *res, } 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_ERROR("Cannot create intersection of 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_int_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]); + igraph_integer_t n = igraph_vcount(VECTOR(*graphs)[i]); if (n > no_of_nodes) { no_of_nodes = n; } @@ -147,55 +160,38 @@ int igraph_intersection_many(igraph_t *res, } if (edgemaps) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); 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); + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); + IGRAPH_CHECK(igraph_vector_int_resize(v, VECTOR(no_edges)[i])); + igraph_vector_int_fill(v, -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])); - } + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); /* 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)); + igraph_integer_t k, j, n = VECTOR(no_edges)[i]; + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); 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; + if (VECTOR(*ev)[j] > VECTOR(*ev)[j + 1]) { + igraph_integer_t tmp = VECTOR(*ev)[j]; + VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; + VECTOR(*ev)[j + 1] = tmp; } } } + IGRAPH_CHECK(igraph_vector_int_resize(order, n)); for (k = 0; k < n; k++) { VECTOR(*order)[k] = k; } - igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, igraph_i_order_edgelist_cmp); } @@ -208,11 +204,12 @@ int igraph_intersection_many(igraph_t *res, 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]; + for (j = 0, tailfrom = IGRAPH_INTEGER_MAX, tailto = IGRAPH_INTEGER_MAX; j < no_of_graphs; j++) { + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; if (from < tailfrom || (from == tailfrom && to < tailto)) { tailfrom = from; tailto = to; } @@ -221,16 +218,17 @@ int igraph_intersection_many(igraph_t *res, /* 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; + igraph_integer_t from = -1, to = -1; + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); while (1) { - long int edge = igraph_vector_long_tail(VECTOR(order_vects)[j]); - igraph_vector_t *ev = VECTOR(edge_vects)[j]; + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&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; + igraph_vector_int_pop_back(order); + if (igraph_vector_int_empty(order)) { + allne = false; break; } } else { @@ -245,25 +243,26 @@ int igraph_intersection_many(igraph_t *res, /* 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t 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; + igraph_vector_int_pop_back(order); + if (igraph_vector_int_empty(order)) { + allne = false; } if (edgemaps && allsame) { - igraph_vector_t *map = VECTOR(*edgemaps)[j]; + igraph_vector_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); VECTOR(*map)[edge] = idx; } } @@ -275,22 +274,14 @@ int igraph_intersection_many(igraph_t *res, } /* 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_vector_int_list_destroy(&order_vects); + igraph_vector_int_list_destroy(&edge_vects); + igraph_vector_int_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(3); - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - if (edgemaps) { - IGRAPH_FINALLY_CLEAN(1); - } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/misc_internal.c b/src/vendor/cigraph/src/operators/misc_internal.c index d2a48638aae..f57d88d35d0 100644 --- a/src/vendor/cigraph/src/operators/misc_internal.c +++ b/src/vendor/cigraph/src/operators/misc_internal.c @@ -25,44 +25,21 @@ #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]; + igraph_vector_int_t *edgelist = edges; + igraph_integer_t edge1 = (*(const igraph_integer_t*) e1) * 2; + igraph_integer_t edge2 = (*(const igraph_integer_t*) e2) * 2; + igraph_integer_t from1 = VECTOR(*edgelist)[edge1]; + igraph_integer_t 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]; + igraph_integer_t to1 = VECTOR(*edgelist)[edge1 + 1]; + igraph_integer_t to2 = VECTOR(*edgelist)[edge2 + 1]; if (to1 < to2) { return -1; } else if (to1 > to2) { @@ -73,54 +50,57 @@ int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2) { } } -int igraph_i_merge(igraph_t *res, int mode, +igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t mode, const igraph_t *left, const igraph_t *right, - igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { + igraph_vector_int_t *edge_map1, igraph_vector_int_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_integer_t no_of_nodes_left = igraph_vcount(left); + igraph_integer_t no_of_nodes_right = igraph_vcount(right); + igraph_integer_t no_of_nodes; + igraph_integer_t no_edges_left = igraph_ecount(left); + igraph_integer_t 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_vector_int_t edges; + igraph_vector_int_t edges1, edges2; + igraph_vector_int_t order1, order2; + igraph_integer_t i, j, eptr = 0; + igraph_integer_t 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_ERROR("Cannot create 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges1, no_edges_left * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges2, no_edges_right * 2); + IGRAPH_CHECK(igraph_vector_int_init(&order1, no_edges_left)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order1); + IGRAPH_CHECK(igraph_vector_int_init(&order2, no_edges_right)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &order2); if (edge_map1) { switch (mode) { case IGRAPH_MERGE_MODE_UNION: - IGRAPH_CHECK(igraph_vector_resize(edge_map1, no_edges_left)); + IGRAPH_CHECK(igraph_vector_int_resize(edge_map1, no_edges_left)); break; case IGRAPH_MERGE_MODE_INTERSECTION: - igraph_vector_clear(edge_map1); + igraph_vector_int_clear(edge_map1); break; + default: + IGRAPH_FATAL("Invalid merge mode."); } } if (edge_map2) { switch (mode) { case IGRAPH_MERGE_MODE_UNION: - IGRAPH_CHECK(igraph_vector_resize(edge_map2, no_edges_right)); + IGRAPH_CHECK(igraph_vector_int_resize(edge_map2, no_edges_right)); break; case IGRAPH_MERGE_MODE_INTERSECTION: - igraph_vector_clear(edge_map2); + igraph_vector_int_clear(edge_map2); break; + default: + IGRAPH_FATAL("Invalid merge mode."); } } @@ -129,22 +109,22 @@ int igraph_i_merge(igraph_t *res, int mode, /* 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 + for every edge, the 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)); + IGRAPH_CHECK(igraph_get_edgelist(left, &edges1, /*bycol=*/ false)); + IGRAPH_CHECK(igraph_get_edgelist(right, &edges2, /*bycol=*/ false)); 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]; + igraph_integer_t 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]; + igraph_integer_t tmp = VECTOR(edges2)[j]; VECTOR(edges2)[j] = VECTOR(edges2)[j + 1]; VECTOR(edges2)[j + 1] = tmp; } @@ -196,8 +176,8 @@ int igraph_i_merge(igraph_t *res, int mode, (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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to1)); if (edge_map1) { VECTOR(*edge_map1)[edge1] = eptr; } @@ -209,8 +189,8 @@ int igraph_i_merge(igraph_t *res, int mode, (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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from2)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to2)); if (edge_map2) { VECTOR(*edge_map2)[edge2] = eptr; } @@ -219,8 +199,8 @@ int igraph_i_merge(igraph_t *res, int mode, INC2(); } else { /* Edge from both */ - IGRAPH_CHECK(igraph_vector_push_back(&edges, from1)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, to1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to1)); if (mode == IGRAPH_MERGE_MODE_UNION) { if (edge_map1) { VECTOR(*edge_map1)[edge1] = eptr; @@ -230,10 +210,10 @@ int igraph_i_merge(igraph_t *res, int mode, } } else if (mode == IGRAPH_MERGE_MODE_INTERSECTION) { if (edge_map1) { - IGRAPH_CHECK(igraph_vector_push_back(edge_map1, edge1)); + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map1, edge1)); } if (edge_map2) { - IGRAPH_CHECK(igraph_vector_push_back(edge_map2, edge2)); + IGRAPH_CHECK(igraph_vector_int_push_back(edge_map2, edge2)); } } eptr++; @@ -246,14 +226,15 @@ int igraph_i_merge(igraph_t *res, int mode, #undef INC1 #undef INC2 - igraph_vector_long_destroy(&order2); - igraph_vector_long_destroy(&order1); - igraph_vector_destroy(&edges2); - igraph_vector_destroy(&edges1); + igraph_vector_int_destroy(&order2); + igraph_vector_int_destroy(&order1); + igraph_vector_int_destroy(&edges2); + igraph_vector_int_destroy(&edges1); IGRAPH_FINALLY_CLEAN(4); IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/misc_internal.h b/src/vendor/cigraph/src/operators/misc_internal.h index 2701fcc3483..fd519033b1f 100644 --- a/src/vendor/cigraph/src/operators/misc_internal.h +++ b/src/vendor/cigraph/src/operators/misc_internal.h @@ -26,20 +26,21 @@ #include "igraph_decls.h" #include "igraph_datatype.h" +#include "igraph_error.h" #include "igraph_vector.h" #include "igraph_vector_ptr.h" __BEGIN_DECLS -#define IGRAPH_MERGE_MODE_UNION 1 -#define IGRAPH_MERGE_MODE_INTERSECTION 2 +typedef enum { + IGRAPH_MERGE_MODE_UNION = 1, + IGRAPH_MERGE_MODE_INTERSECTION = 2 +} igraph_i_merge_mode_t; int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2); -int igraph_i_merge(igraph_t *res, int mode, +igraph_error_t igraph_i_merge(igraph_t *res, igraph_i_merge_mode_t 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); + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); __END_DECLS diff --git a/src/vendor/cigraph/src/operators/permute.c b/src/vendor/cigraph/src/operators/permute.c index 8ad0673f391..3fcfb3b8846 100644 --- a/src/vendor/cigraph/src/operators/permute.c +++ b/src/vendor/cigraph/src/operators/permute.c @@ -38,14 +38,14 @@ * \param inverse An initialized vector. The inverse of \p permutation will be stored here. * \return Error code. */ -static int igraph_i_invert_permutation(const igraph_vector_t *permutation, igraph_vector_t *inverse) { - const long int n = igraph_vector_size(permutation); +static igraph_error_t igraph_i_invert_permutation(const igraph_vector_int_t *permutation, igraph_vector_int_t *inverse) { + const igraph_integer_t n = igraph_vector_int_size(permutation); - IGRAPH_CHECK(igraph_vector_resize(inverse, n)); - igraph_vector_fill(inverse, -1); + IGRAPH_CHECK(igraph_vector_int_resize(inverse, n)); + igraph_vector_int_fill(inverse, -1); - for (long int i=0; i < n; i++) { - long int j = VECTOR(*permutation)[i]; + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t j = VECTOR(*permutation)[i]; if (j < 0 || j >= n) { IGRAPH_ERROR("Invalid index in permutation vector.", IGRAPH_EINVAL); } @@ -78,52 +78,51 @@ static int igraph_i_invert_permutation(const igraph_vector_t *permutation, igrap * 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) { +igraph_error_t igraph_permute_vertices(const igraph_t *graph, igraph_t *res, + const igraph_vector_int_t *permutation) { - long int no_of_nodes = igraph_vcount(graph); - long int no_of_edges = igraph_ecount(graph); - igraph_vector_t edges; - igraph_vector_t index; - long int p; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t edges; + igraph_vector_int_t index; + igraph_integer_t p; - if (igraph_vector_size(permutation) != no_of_nodes) { + if (igraph_vector_int_size(permutation) != no_of_nodes) { IGRAPH_ERROR("Permute vertices: invalid permutation vector size.", IGRAPH_EINVAL); } - IGRAPH_VECTOR_INIT_FINALLY(&index, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, no_of_nodes); /* Also checks that 'permutation' is valid: */ IGRAPH_CHECK(igraph_i_invert_permutation(permutation, &index)); - IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_of_edges * 2); p = 0; - for (long int 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) ]; + for (igraph_integer_t i = 0; i < no_of_edges; i++) { + VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_FROM(graph, i) ]; + VECTOR(edges)[p++] = VECTOR(*permutation)[ IGRAPH_TO(graph, i) ]; } - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, - igraph_is_directed(graph))); + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, igraph_is_directed(graph))); IGRAPH_FINALLY(igraph_destroy, res); /* Attributes */ if (graph->attr) { - igraph_vector_t vtypes; + igraph_vector_int_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_VECTOR_INT_INIT_FINALLY(&vtypes, 0); IGRAPH_CHECK(igraph_i_attribute_get_info(graph, 0, 0, 0, &vtypes, 0, 0)); - if (igraph_vector_size(&vtypes) != 0) { + if (igraph_vector_int_size(&vtypes) != 0) { IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, &index)); } - igraph_vector_destroy(&vtypes); + igraph_vector_int_destroy(&vtypes); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_destroy(&index); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&index); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); /* +1 for res */ return IGRAPH_SUCCESS; diff --git a/src/vendor/cigraph/src/operators/reverse.c b/src/vendor/cigraph/src/operators/reverse.c index 5d298a52cc1..6b6348498dc 100644 --- a/src/vendor/cigraph/src/operators/reverse.c +++ b/src/vendor/cigraph/src/operators/reverse.c @@ -29,6 +29,7 @@ #include "igraph_vector.h" #include "graph/attributes.h" +#include "graph/internal.h" /** * \function igraph_reverse_edges @@ -47,12 +48,13 @@ * Pass igraph_ess_all(IGRAPH_EDGEORDER_ID) to reverse all edges. * \return Error code. * - * Time complexity: O(|E|) where |E| is the number of edges in the graph. + * Time complexity: O(1) if all edges are reversed, otherwise + * O(|E|) where |E| is the number of edges in the graph. */ -int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { - long int no_of_edges = igraph_ecount(graph); - long int no_of_nodes = igraph_vcount(graph); - igraph_vector_t edges; +igraph_error_t igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t edges; igraph_eit_t eit; igraph_t new_graph; @@ -61,8 +63,13 @@ int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { return IGRAPH_SUCCESS; } + /* Use fast method when all edges are to be reversed. */ + if (igraph_es_is_all(&eids)) { + return igraph_i_reverse(graph); + } + /* Convert graph to edge list. */ - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2*no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*no_of_edges); IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, /* bycol= */ 0)); /* Reverse the edges. */ @@ -71,8 +78,8 @@ int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { IGRAPH_FINALLY(igraph_eit_destroy, &eit); for (; !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) { - long int eid = IGRAPH_EIT_GET(eit); - long int tmp = VECTOR(edges)[2*eid]; + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + igraph_integer_t tmp = VECTOR(edges)[2*eid]; VECTOR(edges)[2*eid] = VECTOR(edges)[2*eid + 1]; VECTOR(edges)[2*eid + 1] = tmp; } @@ -85,7 +92,7 @@ int igraph_reverse_edges(igraph_t *graph, const igraph_es_t eids) { IGRAPH_I_ATTRIBUTE_COPY(&new_graph, graph, 1, 1, 1); /* does IGRAPH_CHECK */ igraph_eit_destroy(&eit); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); igraph_destroy(graph); IGRAPH_FINALLY_CLEAN(3); diff --git a/src/vendor/cigraph/src/operators/rewire.c b/src/vendor/cigraph/src/operators/rewire.c index 3cb784f8dcf..6820c63027f 100644 --- a/src/vendor/cigraph/src/operators/rewire.c +++ b/src/vendor/cigraph/src/operators/rewire.c @@ -39,12 +39,13 @@ #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); +igraph_error_t igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, igraph_bool_t use_adjlist) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_vector_int_t eids; + igraph_vector_int_t edgevec, alledges; igraph_bool_t directed, loops, ok; igraph_es_t es; igraph_adjlist_t al; @@ -58,7 +59,7 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, RNG_BEGIN(); - IGRAPH_VECTOR_INIT_FINALLY(&eids, 2); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 2); if (use_adjlist) { /* As well as the sorted adjacency list, we maintain an unordered @@ -66,10 +67,10 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, */ 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_VECTOR_INT_INIT_FINALLY(&alledges, no_of_edges * 2); igraph_get_edgelist(graph, &alledges, /*bycol=*/ 0); } else { - IGRAPH_VECTOR_INIT_FINALLY(&edgevec, 4); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edgevec, 4); es = igraph_ess_vector(&eids); } @@ -104,15 +105,13 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, /* 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]; + a = VECTOR(alledges)[VECTOR(eids)[0] * 2]; + b = VECTOR(alledges)[VECTOR(eids)[0] * 2 + 1]; + c = VECTOR(alledges)[VECTOR(eids)[1] * 2]; + d = VECTOR(alledges)[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)); + IGRAPH_CHECK(igraph_edge(graph, VECTOR(eids)[0], &a, &b)); + IGRAPH_CHECK(igraph_edge(graph, VECTOR(eids)[1], &c, &d)); } /* For an undirected graph, we have two "variants" of each edge, i.e. @@ -123,8 +122,8 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, 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; + VECTOR(alledges)[VECTOR(eids)[1] * 2] = c; + VECTOR(alledges)[VECTOR(eids)[1] * 2 + 1] = d; } } @@ -173,22 +172,22 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, /* 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); */ + /* printf("Deleting: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", + a, b, c, 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; + VECTOR(alledges)[VECTOR(eids)[0] * 2 + 1] = d; + VECTOR(alledges)[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); + /* printf("Adding: %" IGRAPH_PRId " -> %" IGRAPH_PRId ", %" IGRAPH_PRId " -> %" IGRAPH_PRId "\n", + a, d, c, b); */ + IGRAPH_CHECK(igraph_add_edges(graph, &edgevec, 0)); } num_successful_swaps++; } @@ -209,18 +208,18 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, IGRAPH_PROGRESS("Random rewiring: ", 100.0, 0); if (use_adjlist) { - igraph_vector_destroy(&alledges); + igraph_vector_int_destroy(&alledges); igraph_adjlist_destroy(&al); } else { - igraph_vector_destroy(&edgevec); + igraph_vector_int_destroy(&edgevec); } - igraph_vector_destroy(&eids); + igraph_vector_int_destroy(&eids); IGRAPH_FINALLY_CLEAN(use_adjlist ? 3 : 2); RNG_END(); - return 0; + return IGRAPH_SUCCESS; } /** @@ -262,7 +261,7 @@ int igraph_i_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, * * Time complexity: TODO. */ -int igraph_rewire(igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode) { +igraph_error_t 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/vendor/cigraph/src/operators/rewire_edges.c b/src/vendor/cigraph/src/operators/rewire_edges.c index 48211e2d59c..6c5b856bfe2 100644 --- a/src/vendor/cigraph/src/operators/rewire_edges.c +++ b/src/vendor/cigraph/src/operators/rewire_edges.c @@ -30,21 +30,21 @@ #include "graph/attributes.h" -static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, +static igraph_error_t igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, - igraph_vector_t *edges) { + igraph_vector_int_t *edges) { - int no_verts = igraph_vcount(graph); - int no_edges = igraph_ecount(graph); - igraph_vector_t eorder, tmp; + igraph_integer_t no_verts = igraph_vcount(graph); + igraph_integer_t no_edges = igraph_ecount(graph); + igraph_vector_int_t eorder, tmp; igraph_vector_int_t first, next, prev, marked; - int i, to_rewire, last_other = -1; + igraph_integer_t 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(prev)[VECTOR(first)[(vertex)]-1]=(stub)+1; \ } \ VECTOR(next)[(stub)]=VECTOR(first)[(vertex)]; \ VECTOR(prev)[(stub)]=0; \ @@ -63,9 +63,9 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob } while (0) # define MARK_NEIGHBORS(vertex) do { \ - int xxx_ =VECTOR(first)[(vertex)]; \ + igraph_integer_t xxx_ =VECTOR(first)[(vertex)]; \ while (xxx_) { \ - int o= (int) VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2]; \ + igraph_integer_t o= VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2]; \ VECTOR(marked)[o]=other+1; \ xxx_=VECTOR(next)[xxx_-1]; \ } \ @@ -78,17 +78,18 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eorder, no_edges); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t idx1 = 2 * i, idx2 = idx1 + 1; + igraph_integer_t from = VECTOR(*edges)[idx1]; + igraph_integer_t to = 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_CHECK(igraph_vector_int_order1(&tmp, &eorder, no_verts)); + igraph_vector_int_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_vector_int_init(&marked, no_verts)); @@ -96,22 +97,22 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob /* Rewire the stubs, part I */ - to_rewire = (int) RNG_GEOM(prob); + to_rewire = (igraph_integer_t) 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; + igraph_integer_t stub = 2 * VECTOR(eorder)[to_rewire] + 1; + igraph_integer_t v = VECTOR(*edges)[stub]; + igraph_integer_t ostub = stub - 1; + igraph_integer_t other = VECTOR(*edges)[ostub]; + igraph_integer_t pot; if (last_other != other) { MARK_NEIGHBORS(other); } /* Do the rewiring */ do { if (loops) { - pot = (int) RNG_INTEGER(0, no_verts - 1); + pot = RNG_INTEGER(0, no_verts - 1); } else { - pot = (int) RNG_INTEGER(0, no_verts - 2); + pot = RNG_INTEGER(0, no_verts - 2); pot = pot != other ? pot : no_verts - 1; } } while (VECTOR(marked)[pot] == other + 1 && pot != v); @@ -130,12 +131,12 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob /* Create the new index, from the potentially rewired stubs */ - IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges); + IGRAPH_VECTOR_INT_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_CHECK(igraph_vector_int_order1(&tmp, &eorder, no_verts)); + igraph_vector_int_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); /* Rewire the stubs, part II */ @@ -143,22 +144,22 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob igraph_vector_int_null(&marked); last_other = -1; - to_rewire = (int) RNG_GEOM(prob); + to_rewire = (igraph_integer_t) 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; + igraph_integer_t stub = (2 * VECTOR(eorder)[to_rewire]); + igraph_integer_t v = VECTOR(*edges)[stub]; + igraph_integer_t ostub = stub + 1; + igraph_integer_t other = VECTOR(*edges)[ostub]; + igraph_integer_t pot; if (last_other != other) { MARK_NEIGHBORS(other); } /* Do the rewiring */ do { if (loops) { - pot = (int) RNG_INTEGER(0, no_verts - 1); + pot = RNG_INTEGER(0, no_verts - 1); } else { - pot = (int) RNG_INTEGER(0, no_verts - 2); + pot = RNG_INTEGER(0, no_verts - 2); pot = pot != other ? pot : no_verts - 1; } } while (VECTOR(marked)[pot] == other + 1 && pot != v); @@ -178,10 +179,10 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob igraph_vector_int_destroy(&prev); igraph_vector_int_destroy(&next); igraph_vector_int_destroy(&first); - igraph_vector_destroy(&eorder); + igraph_vector_int_destroy(&eorder); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } #undef ADD_STUB @@ -215,15 +216,15 @@ static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob * * Time complexity: O(|V|+|E|). */ -int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, +igraph_error_t 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; + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t endpoints = no_of_edges * 2; + igraph_integer_t to_rewire; + igraph_vector_int_t edges; if (prob < 0 || prob > 1) { IGRAPH_ERROR("Rewiring probability should be between zero and one", @@ -235,7 +236,7 @@ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, endpoints); RNG_BEGIN(); @@ -246,14 +247,14 @@ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, 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); + to_rewire = 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); + igraph_integer_t opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1; + igraph_integer_t nei = VECTOR(edges)[opos]; + igraph_integer_t 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; @@ -267,9 +268,9 @@ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, RNG_END(); - IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &newgraph); @@ -279,7 +280,7 @@ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, igraph_destroy(graph); *graph = newgraph; - return 0; + return IGRAPH_SUCCESS; } /** @@ -318,7 +319,7 @@ int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob, * * Time complexity: O(|E|). */ -int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, +igraph_error_t igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, igraph_bool_t loops, igraph_neimode_t mode) { if (prob < 0 || prob > 1) { @@ -337,13 +338,13 @@ int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, 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_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t to_rewire; + igraph_integer_t offset = 0; + igraph_vector_int_t edges; - IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2 * no_of_edges); switch (mode) { case IGRAPH_IN: @@ -365,8 +366,8 @@ int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, 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); + igraph_integer_t nei = VECTOR(edges)[2 * to_rewire + (1 - offset)]; + igraph_integer_t 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; @@ -374,9 +375,9 @@ int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, RNG_END(); - IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes, + IGRAPH_CHECK(igraph_create(&newgraph, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); IGRAPH_FINALLY(igraph_destroy, &newgraph); @@ -390,5 +391,5 @@ int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob, IGRAPH_CHECK(igraph_rewire_edges(graph, prob, loops, /* multiple = */ 1)); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/rewire_internal.h b/src/vendor/cigraph/src/operators/rewire_internal.h index c9e5d7e5fa4..b82b10eec95 100644 --- a/src/vendor/cigraph/src/operators/rewire_internal.h +++ b/src/vendor/cigraph/src/operators/rewire_internal.h @@ -1,8 +1,15 @@ #ifndef IGRAPH_OPERATORS_REWIRE_INTERNAL_H #define IGRAPH_OPERATORS_REWIRE_INTERNAL_H +#include "igraph_decls.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); +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_rewire( + igraph_t *graph, igraph_integer_t n, igraph_rewiring_t mode, + igraph_bool_t use_adjlist); + +__END_DECLS #endif diff --git a/src/vendor/cigraph/src/operators/simplify.c b/src/vendor/cigraph/src/operators/simplify.c index 6924f18c988..d100dd0b104 100644 --- a/src/vendor/cigraph/src/operators/simplify.c +++ b/src/vendor/cigraph/src/operators/simplify.c @@ -33,12 +33,16 @@ * \function igraph_simplify * \brief Removes loop and/or multiple edges from the graph. * + * This function merges parallel edges and removes self-loops, according + * to the \p multiple and \p loops parameters. Note that this function + * may change the edge order, even if the input was already a simple 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 + * that were unaffected. See the igraph manual section about attributes * for details. * \return Error code: * \c IGRAPH_ENOMEM if we are out of memory. @@ -48,21 +52,33 @@ * \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_error_t 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_vector_int_t edges; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t edge; igraph_bool_t attr = edge_comb && igraph_has_attribute_table(); - long int from, to, pfrom = -1, pto = -2; + igraph_integer_t from, to, pfrom = -1, pto = -2; igraph_t res; igraph_es_t es; igraph_eit_t eit; - igraph_vector_t mergeinto; - long int actedge; + igraph_vector_int_t mergeinto; + igraph_integer_t actedge; + + /* if we already know there are no multi-edges, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI)) { + multiple = false; + } + + /* if we already know there are no loops, they don't need to be removed */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + loops = false; + } if (!multiple && !loops) /* nothing to do */ @@ -71,9 +87,11 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, } if (!multiple) { + igraph_vector_int_t edges_to_delete; + /* 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_VECTOR_INT_INIT_FINALLY(&edges_to_delete, 0); IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_ID)); IGRAPH_FINALLY(igraph_es_destroy, &es); IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -84,7 +102,7 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, from = IGRAPH_FROM(graph, edge); to = IGRAPH_TO(graph, edge); if (from == to) { - IGRAPH_CHECK(igraph_vector_push_back(&edges, edge)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges_to_delete, edge)); } IGRAPH_EIT_NEXT(eit); } @@ -93,21 +111,23 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_es_destroy(&es); IGRAPH_FINALLY_CLEAN(2); - if (igraph_vector_size(&edges) > 0) { - IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges))); + if (igraph_vector_int_size(&edges_to_delete) > 0) { + IGRAPH_CHECK(igraph_delete_edges(graph, igraph_ess_vector(&edges_to_delete))); } - igraph_vector_destroy(&edges); + igraph_vector_int_destroy(&edges_to_delete); IGRAPH_FINALLY_CLEAN(1); + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, false); + return IGRAPH_SUCCESS; } if (attr) { - IGRAPH_VECTOR_INIT_FINALLY(&mergeinto, no_of_edges); + IGRAPH_VECTOR_INT_INIT_FINALLY(&mergeinto, no_of_edges); } - IGRAPH_VECTOR_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_reserve(&edges, no_of_edges * 2)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); IGRAPH_CHECK(igraph_es_all(&es, IGRAPH_EDGEORDER_FROM)); IGRAPH_FINALLY(igraph_es_destroy, &es); @@ -131,8 +151,8 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, } } else { /* Edge to be kept */ - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + igraph_vector_int_push_back(&edges, from); /* reserved */ + igraph_vector_int_push_back(&edges, to); /* reserved */ if (attr) { actedge++; VECTOR(mergeinto)[edge] = actedge; @@ -145,29 +165,25 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, 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_CHECK(igraph_create(&res, &edges, no_of_nodes, igraph_is_directed(graph))); - igraph_vector_destroy(&edges); + igraph_vector_int_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); + IGRAPH_I_ATTRIBUTE_COPY(&res, graph, /*graph=*/ true, /*vertex=*/ true, /*edge=*/ false); if (attr) { igraph_fixed_vectorlist_t vl; - IGRAPH_CHECK(igraph_fixed_vectorlist_convert(&vl, &mergeinto, - actedge + 1)); + 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_CHECK(igraph_i_attribute_combine_edges(graph, &res, &vl.vecs, edge_comb)); igraph_fixed_vectorlist_destroy(&vl); - igraph_vector_destroy(&mergeinto); + igraph_vector_int_destroy(&mergeinto); IGRAPH_FINALLY_CLEAN(2); } @@ -175,5 +191,20 @@ int igraph_simplify(igraph_t *graph, igraph_bool_t multiple, igraph_destroy(graph); *graph = res; - return 0; + /* The cache must be set as the very last step, only after all functions that can + * potentially return with an error have finished. */ + + if (loops) { + /* Loop edges were removed so we know for sure that there aren't any + * loop edges now */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, false); + } + + if (multiple) { + /* Multi-edges were removed so we know for sure that there aren't any + * multi-edges now */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, false); + } + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/subgraph.c b/src/vendor/cigraph/src/operators/subgraph.c index f798f5df7f0..287b5e6dd9b 100644 --- a/src/vendor/cigraph/src/operators/subgraph.c +++ b/src/vendor/cigraph/src/operators/subgraph.c @@ -27,44 +27,45 @@ #include "igraph_memory.h" #include "core/interruption.h" +#include "core/set.h" #include "graph/attributes.h" +#include "graph/internal.h" #include "operators/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; +static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_int_t *map, igraph_vector_int_t *invmap) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t delete; + bool *remain; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&delete, 0); + + remain = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(remain, "Insufficient memory for taking subgraph."); IGRAPH_FINALLY(igraph_free, remain); - IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_nodes - IGRAPH_VIT_SIZE(vit))); + + IGRAPH_CHECK(igraph_vector_int_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; + remain[ IGRAPH_VIT_GET(vit) ] = true; } for (i = 0; i < no_of_nodes; i++) { - IGRAPH_ALLOW_INTERRUPTION(); - if (remain[i] == 0) { - IGRAPH_CHECK(igraph_vector_push_back(&delete, i)); + if (! remain[i]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } @@ -78,10 +79,11 @@ static int igraph_i_induced_subgraph_copy_and_delete( IGRAPH_CHECK(igraph_delete_vertices_idx(res, igraph_vss_vector(&delete), map, invmap)); - igraph_vector_destroy(&delete); + igraph_vector_int_destroy(&delete); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); - return 0; + + return IGRAPH_SUCCESS; } /** @@ -94,45 +96,47 @@ static int igraph_i_induced_subgraph_copy_and_delete( * 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 -) { +static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( + const igraph_t *graph, igraph_t *res, const igraph_vs_t vids, + igraph_vector_int_t *map, igraph_vector_int_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 no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_new_nodes = 0; + igraph_integer_t i, j, n; + igraph_integer_t 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_vector_int_t vids_old2new, vids_new2old; + igraph_vector_int_t eids_new2old; + igraph_vector_int_t vids_vec; + igraph_vector_int_t nei_edges; + igraph_vector_int_t new_edges; igraph_vit_t vit; - igraph_vector_t *my_vids_old2new = &vids_old2new, - *my_vids_new2old = &vids_new2old; + igraph_vector_int_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids_new2old, 0); if (invmap) { my_vids_new2old = invmap; - igraph_vector_clear(my_vids_new2old); + igraph_vector_int_clear(my_vids_new2old); } else { - IGRAPH_VECTOR_INIT_FINALLY(&vids_new2old, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_new2old, 0); } - IGRAPH_VECTOR_INIT_FINALLY(&new_edges, 0); - IGRAPH_VECTOR_INIT_FINALLY(&nei_edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_vector_int_resize(map, no_of_nodes)); + igraph_vector_int_null(map); } } else { - IGRAPH_VECTOR_INIT_FINALLY(&vids_old2new, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); } + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_vec, 0); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -143,45 +147,47 @@ static int igraph_i_induced_subgraph_create_from_scratch( * 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. + * a vector first and then sort it. */ - IGRAPH_CHECK(igraph_vit_as_vector(&vit, &nei_edges)); + IGRAPH_CHECK(igraph_vit_as_vector(&vit, &vids_vec)); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - igraph_vector_sort(&nei_edges); - n = igraph_vector_size(&nei_edges); + igraph_vector_int_sort(&vids_vec); + n = igraph_vector_int_size(&vids_vec); for (i = 0; i < n; i++) { - long int vid = (long int) VECTOR(nei_edges)[i]; + igraph_integer_t vid = VECTOR(vids_vec)[i]; if (VECTOR(*my_vids_old2new)[vid] == 0) { - IGRAPH_CHECK(igraph_vector_push_back(my_vids_new2old, vid)); + IGRAPH_CHECK(igraph_vector_int_push_back(my_vids_new2old, vid)); no_of_new_nodes++; VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes; } } + igraph_vector_int_destroy(&vids_vec); + IGRAPH_FINALLY_CLEAN(1); /* 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_integer_t old_vid = VECTOR(*my_vids_new2old)[i]; + igraph_integer_t 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); + n = igraph_vector_int_size(&nei_edges); if (directed) { /* directed graph; this is easier */ for (j = 0; j < n; j++) { - eid = (igraph_integer_t) VECTOR(nei_edges)[j]; + eid = VECTOR(nei_edges)[j]; - to = (long int) VECTOR(*my_vids_old2new)[ (long int)IGRAPH_TO(graph, eid) ]; + to = VECTOR(*my_vids_old2new)[ 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to - 1)); + IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid)); } } else { /* undirected graph. We need to be careful with loop edges as each @@ -189,14 +195,14 @@ static int igraph_i_induced_subgraph_create_from_scratch( * second loop edge */ skip_loop_edge = 0; for (j = 0; j < n; j++) { - eid = (igraph_integer_t) VECTOR(nei_edges)[j]; + eid = 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) ]; + to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ]; if (!to) { continue; } @@ -210,28 +216,27 @@ static int igraph_i_induced_subgraph_create_from_scratch( } } - 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to)); + IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid)); } } } /* Get rid of some vectors that are not needed anymore */ if (!map) { - igraph_vector_destroy(&vids_old2new); + igraph_vector_int_destroy(&vids_old2new); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_destroy(&nei_edges); + igraph_vector_int_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_CHECK(igraph_create(res, &new_edges, 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_vector_int_destroy(&new_edges); IGRAPH_FINALLY_CLEAN(1); /* Make sure that the newly created graph is destroyed if something happens from @@ -243,20 +248,20 @@ static int igraph_i_induced_subgraph_create_from_scratch( /* ga = */ 1, /* va = */ 0, /* ea = */ 0)); /* Copy the vertex attributes */ - IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, - my_vids_new2old)); + 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)); + /* Get rid of the remaining stuff */ if (!invmap) { - igraph_vector_destroy(my_vids_new2old); + igraph_vector_int_destroy(my_vids_new2old); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_destroy(&eids_new2old); + igraph_vector_int_destroy(&eids_new2old); IGRAPH_FINALLY_CLEAN(2); /* 1 + 1 since we don't need to destroy res */ - return 0; + return IGRAPH_SUCCESS; } /** @@ -267,8 +272,8 @@ static int igraph_i_induced_subgraph_create_from_scratch( * * 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. + * 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 @@ -292,7 +297,7 @@ static int igraph_i_induced_subgraph_create_from_scratch( * \return Error code: * \c IGRAPH_ENOMEM, not enough memory for * temporary data. - * \c IGRAPH_EINVVID, invalid vertex id in + * \c IGRAPH_EINVVID, invalid vertex ID in * \p vids. * * Time complexity: O(|V|+|E|), @@ -303,13 +308,13 @@ static int igraph_i_induced_subgraph_create_from_scratch( * \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, +igraph_error_t 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( +static igraph_error_t igraph_i_induced_subgraph_suggest_implementation( const igraph_t *graph, const igraph_vs_t vids, igraph_subgraph_implementation_t *result) { double ratio; @@ -329,14 +334,14 @@ static int igraph_i_induced_subgraph_suggest_implementation( *result = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH; } - return 0; + return IGRAPH_SUCCESS; } -int igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +igraph_error_t 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_vector_int_t *map, + igraph_vector_int_t *invmap, igraph_bool_t map_is_prepared) { if (impl == IGRAPH_SUBGRAPH_AUTO) { @@ -356,24 +361,155 @@ int igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res, } } -int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, +/** + * \ingroup structural + * \function igraph_induced_subgraph_map + * \brief Creates an induced subraph and returns the mapping from the original. + * + * 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 be + * used 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. + * \param map Returns a map of the vertices in \p graph to the vertices + * in \p res. A 0 indicates a vertex is not mapped. An \c i + 1 at + * position \c j indicates the vertex \c j in \p graph is mapped + * to vertex i in \p res. + * \param invmap Returns a map of the vertices in \p res to the vertices + * in \p graph. An i at position \c j indicates the vertex \c i + * in \p graph is mapped to vertex j in \p res. + * + * \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. + */ +igraph_error_t 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); + igraph_vector_int_t *map, + igraph_vector_int_t *invmap) { + return igraph_i_induced_subgraph_map(graph, res,vids, impl, map, invmap, /* map_is_prepared = */ false); +} + +/** + * \function igraph_induced_subgraph_edges + * \brief The edges contained within an induced subgraph. + * + * This function finds the IDs of those edges which connect vertices from + * a given list, passed in the \p vids parameter. + * + * \param graph The graph. + * \param vids A vertex selector specifying the vertices that make up the subgraph. + * \param edges Integer vector. The IDs of edges within the subgraph induces by + * \p vids will be stored here. + * \return Error code. + * + * Time complexity: O(mv log(nv)) where nv is the number of vertices in \p vids + * and mv is the sum of degrees of vertices in \p vids. + */ +igraph_error_t igraph_induced_subgraph_edges(const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges) { + /* TODO: When the size of \p vids is large, is it faster to use a boolean vector instead of a set + * to test membership within \p vids? Benchmark to find out at what size it is worth switching + * to the alternative implementation. + */ + igraph_vit_t vit; + igraph_set_t vids_set; + igraph_vector_int_t incedges; + + if (igraph_vs_is_all(&vids)) { + IGRAPH_CHECK(igraph_vector_int_range(edges, 0, igraph_ecount(graph))); + return IGRAPH_SUCCESS; + } + + igraph_vector_int_clear(edges); + + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + IGRAPH_FINALLY(igraph_vit_destroy, &vit); + + IGRAPH_SET_INIT_FINALLY(&vids_set, IGRAPH_VIT_SIZE(vit)); + for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + IGRAPH_CHECK(igraph_set_add(&vids_set, IGRAPH_VIT_GET(vit))); + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&incedges, 0); + + for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { + igraph_integer_t v = IGRAPH_VIT_GET(vit); + IGRAPH_CHECK(igraph_i_incident(graph, &incedges, v, IGRAPH_ALL, IGRAPH_LOOPS_ONCE)); + + igraph_integer_t d = igraph_vector_int_size(&incedges); + for (igraph_integer_t i=0; i < d; i++) { + igraph_integer_t e = VECTOR(incedges)[i]; + igraph_integer_t u = IGRAPH_OTHER(graph, e, v); + /* The v <= u check avoids adding non-loop edges twice. + * Loop edges only appear once due to the use of + * IGRAPH_LOOPS_ONCE in igraph_i_incident() */ + if (v <= u && igraph_set_contains(&vids_set, u)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edges, e)); + } + } + } + + IGRAPH_FINALLY_CLEAN(3); + igraph_vector_int_destroy(&incedges); + igraph_set_destroy(&vids_set); + igraph_vit_destroy(&vit); + + return IGRAPH_SUCCESS; } /** * \ingroup structural * \function igraph_subgraph_edges + * \brief Creates a subgraph with the specified edges and their endpoints (deprecated alias). + * + * \deprecated-by igraph_subgraph_from_edges 0.10.3 + */ +igraph_error_t igraph_subgraph_edges( + const igraph_t *graph, igraph_t *res, const igraph_es_t eids, + igraph_bool_t delete_vertices +) { + return igraph_subgraph_from_edges(graph, res, eids, delete_vertices); +} + +/** + * \ingroup structural + * \function igraph_subgraph_from_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. + * graph. As the vertex IDs in a graph always start with zero, this function + * very likely needs to reassign IDs to the vertices. Attributes are preserved. + * * \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 @@ -381,13 +517,13 @@ int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, * 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 + * 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 + * \c IGRAPH_EINVEID, invalid edge ID in * \p eids. * * Time complexity: O(|V|+|E|), @@ -399,45 +535,44 @@ int igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res, * 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) { +igraph_error_t igraph_subgraph_from_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_vector_int_t delete = IGRAPH_VECTOR_NULL; + bool *vremain, *eremain; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&delete, 0); + vremain = IGRAPH_CALLOC(no_of_nodes, bool); + IGRAPH_CHECK_OOM(vremain, "Insufficient memory for taking subgraph based on edges."); IGRAPH_FINALLY(igraph_free, vremain); - eremain = IGRAPH_CALLOC(no_of_edges, char); - if (eremain == 0) { - IGRAPH_ERROR("subgraph_edges failed", IGRAPH_ENOMEM); - } + + eremain = IGRAPH_CALLOC(no_of_edges, bool); + IGRAPH_CHECK_OOM(eremain, "Insufficient memory for taking subgraph based on edges."); IGRAPH_FINALLY(igraph_free, eremain); - IGRAPH_CHECK(igraph_vector_reserve(&delete, no_of_edges - IGRAPH_EIT_SIZE(eit))); + + IGRAPH_CHECK(igraph_vector_int_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; + igraph_integer_t eid = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid); + eremain[eid] = vremain[from] = vremain[to] = true; } /* 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)); + if (! eremain[i]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } @@ -453,11 +588,11 @@ int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, if (delete_vertices) { /* Collect the vertex IDs to be deleted */ - igraph_vector_clear(&delete); + igraph_vector_int_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)); + if (! vremain[i]) { + IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } } @@ -470,8 +605,8 @@ int igraph_subgraph_edges(const igraph_t *graph, igraph_t *res, IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete))); } - igraph_vector_destroy(&delete); + igraph_vector_int_destroy(&delete); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/operators/subgraph.h b/src/vendor/cigraph/src/operators/subgraph.h index a27aa8efad4..d293f7b2f9f 100644 --- a/src/vendor/cigraph/src/operators/subgraph.h +++ b/src/vendor/cigraph/src/operators/subgraph.h @@ -24,12 +24,19 @@ #ifndef IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H #define IGRAPH_OPERATORS_SUBGRAPH_INTERNAL_H -#include "igraph_interface.h" +#include "igraph_decls.h" +#include "igraph_datatype.h" +#include "igraph_error.h" +#include "igraph_iterators.h" -IGRAPH_PRIVATE_EXPORT int igraph_i_induced_subgraph_map( +__BEGIN_DECLS + +IGRAPH_PRIVATE_EXPORT igraph_error_t 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 + igraph_subgraph_implementation_t impl, igraph_vector_int_t *map, + igraph_vector_int_t *invmap, igraph_bool_t map_is_prepared ); +__END_DECLS + #endif diff --git a/src/vendor/cigraph/src/operators/union.c b/src/vendor/cigraph/src/operators/union.c index d55390d0f26..d1fd87a6800 100644 --- a/src/vendor/cigraph/src/operators/union.c +++ b/src/vendor/cigraph/src/operators/union.c @@ -27,8 +27,8 @@ #include "igraph_constructors.h" #include "igraph_conversion.h" #include "igraph_interface.h" -#include "igraph_memory.h" #include "igraph_qsort.h" +#include "igraph_vector_list.h" #include "operators/misc_internal.h" @@ -36,11 +36,19 @@ * \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. * + * + * The directedness of the operand graphs must be the same. + * + * + * Edge multiplicities are handled by taking the \em larger of the two + * multiplicities in the input graphs. In other words, if the first graph + * has N edges between a vertex pair (u, v) and the second graph has M edges, + * the result graph will have max(N, M) edges between them. + * * \param res Pointer to an uninitialized graph object, the result * will be stored here. * \param left The first graph. @@ -61,9 +69,9 @@ * * \example examples/simple/igraph_union.c */ -int igraph_union(igraph_t *res, +igraph_error_t igraph_union(igraph_t *res, const igraph_t *left, const igraph_t *right, - igraph_vector_t *edge_map1, igraph_vector_t *edge_map2) { + igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2) { return igraph_i_merge(res, IGRAPH_MERGE_MODE_UNION, left, right, edge_map1, edge_map2); } @@ -72,25 +80,33 @@ int igraph_union(igraph_t *res, * \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. + * The number of vertices in the result graph will be the maximum + * number of vertices in the argument graphs. + * + * + * The directedness of the argument graphs must be the same. * If the graph list has length zero, the result will be a \em directed * graph with no vertices. * + * + * Edge multiplicities are handled by taking the \em maximum multiplicity of the + * all multiplicities for the same vertex pair (u, v) in the input graphs; this + * will be the multiplicity of (u, v) in the result graph. + * * \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 + * list of integer vectors, 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. + * \type igraph_vector_int_t object. * \return Error code. * \sa \ref igraph_union() for the union of two graphs, \ref * igraph_intersection_many(), \ref igraph_intersection() and \ref @@ -102,17 +118,19 @@ int igraph_union(igraph_t *res, * * \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; +igraph_error_t igraph_union_many( + igraph_t *res, const igraph_vector_ptr_t *graphs, + igraph_vector_int_list_t *edgemaps +) { + + igraph_integer_t no_of_graphs = igraph_vector_ptr_size(graphs); + igraph_integer_t no_of_nodes = 0; + igraph_bool_t directed = true; + igraph_vector_int_t edges; + igraph_vector_int_list_t edge_vects, order_vects; + igraph_vector_int_t no_edges; + igraph_integer_t i, j, tailfrom = no_of_graphs > 0 ? 0 : -1, tailto = -1; + igraph_integer_t idx = 0; /* Check directedness */ if (no_of_graphs != 0) { @@ -121,24 +139,18 @@ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, } 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_ERROR("Cannot create union of 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_init(&no_edges, no_of_graphs)); + IGRAPH_FINALLY(igraph_vector_int_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]); + igraph_integer_t n = igraph_vcount(VECTOR(*graphs)[i]); if (n > no_of_nodes) { no_of_nodes = n; } @@ -146,54 +158,37 @@ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, } if (edgemaps) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edgemaps, no_of_graphs)); 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])); + igraph_vector_int_t* v = igraph_vector_int_list_get_ptr(edgemaps, i); + IGRAPH_CHECK(igraph_vector_int_resize(v, 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])); - } + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&edge_vects, no_of_graphs); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&order_vects, no_of_graphs); /* 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)); + igraph_integer_t k, j, n = VECTOR(no_edges)[i]; + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, i); + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, i); + IGRAPH_CHECK(igraph_get_edgelist(VECTOR(*graphs)[i], ev, /*bycol=*/ false)); 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; + if (VECTOR(*ev)[j] > VECTOR(*ev)[j + 1]) { + igraph_integer_t tmp = VECTOR(*ev)[j]; + VECTOR(*ev)[j] = VECTOR(*ev)[j + 1]; + VECTOR(*ev)[j + 1] = tmp; } } } + IGRAPH_CHECK(igraph_vector_int_resize(order, n)); for (k = 0; k < n; k++) { VECTOR(*order)[k] = k; } - igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), edges, + igraph_qsort_r(VECTOR(*order), n, sizeof(VECTOR(*order)[0]), ev, igraph_i_order_edgelist_cmp); } @@ -202,11 +197,12 @@ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, /* 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]; + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + if (!igraph_vector_int_empty(order)) { + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; if (from > tailfrom || (from == tailfrom && to > tailto)) { tailfrom = from; tailto = to; } @@ -217,20 +213,21 @@ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, } /* add the edge */ - IGRAPH_CHECK(igraph_vector_push_back(&edges, tailfrom)); - IGRAPH_CHECK(igraph_vector_push_back(&edges, tailto)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, tailfrom)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_vector_int_t *order = igraph_vector_int_list_get_ptr(&order_vects, j); + if (!igraph_vector_int_empty(order)) { + igraph_vector_int_t *ev = igraph_vector_int_list_get_ptr(&edge_vects, j); + igraph_integer_t edge = igraph_vector_int_tail(order); + igraph_integer_t from = VECTOR(*ev)[2 * edge]; + igraph_integer_t to = VECTOR(*ev)[2 * edge + 1]; if (from == tailfrom && to == tailto) { - igraph_vector_long_pop_back(VECTOR(order_vects)[j]); + igraph_vector_int_pop_back(order); if (edgemaps) { - igraph_vector_t *map = VECTOR(*edgemaps)[j]; + igraph_vector_int_t *map = igraph_vector_int_list_get_ptr(edgemaps, j); VECTOR(*map)[edge] = idx; } } @@ -240,22 +237,14 @@ int igraph_union_many(igraph_t *res, const igraph_vector_ptr_t *graphs, } - 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_vector_int_list_destroy(&order_vects); + igraph_vector_int_list_destroy(&edge_vects); + igraph_vector_int_destroy(&no_edges); + IGRAPH_FINALLY_CLEAN(3); - IGRAPH_CHECK(igraph_create(res, &edges, (igraph_integer_t) no_of_nodes, - directed)); - igraph_vector_destroy(&edges); + IGRAPH_CHECK(igraph_create(res, &edges, no_of_nodes, directed)); + igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); - if (edgemaps) { - IGRAPH_FINALLY_CLEAN(1); - } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/all_shortest_paths.c b/src/vendor/cigraph/src/paths/all_shortest_paths.c index d16ed57fd08..536586f80cd 100644 --- a/src/vendor/cigraph/src/paths/all_shortest_paths.c +++ b/src/vendor/cigraph/src/paths/all_shortest_paths.c @@ -23,7 +23,6 @@ #include "igraph_paths.h" -#include "igraph_adjlist.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" @@ -32,17 +31,6 @@ #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. @@ -51,13 +39,23 @@ static void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { * 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 + * \param vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. 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. The list will be resized as needed. 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. Each vector object contains the edges + * 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. The list will be resized as needed. Supply a null pointer here + * if you don't need these vectors. + * \param nrgeo Pointer to an initialized \ref igraph_vector_int_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 @@ -65,7 +63,7 @@ static void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { * 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 + * \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 @@ -84,7 +82,7 @@ static void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { * \cli IGRAPH_ENOMEM * not enough memory for temporary data. * \cli IGRAPH_EINVVID - * \p from is invalid vertex id. + * \p from is invalid vertex ID. * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -94,22 +92,26 @@ static void igraph_i_gasp_paths_destroy(igraph_vector_ptr_t *v) { * 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_error_t igraph_get_all_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *geodist; + igraph_vector_int_list_t paths; + igraph_vector_int_list_t path_edge; + igraph_dqueue_int_t q; + igraph_vector_int_t *vptr; + igraph_vector_int_t *vptr_e; + igraph_vector_int_t neis; + igraph_vector_int_t ptrlist; + igraph_vector_int_t ptrhead; + igraph_integer_t n; + igraph_integer_t to_reach, reached = 0, maxdist = 0; igraph_vit_t vit; @@ -118,79 +120,77 @@ int igraph_get_all_shortest_paths(const igraph_t *graph, } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + 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); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&paths, 0); + /* path_edge will store the shortest paths during the search */ + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&path_edge, 0); /* neis is a temporary vector holding the neighbors of the * node being examined */ - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_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); + IGRAPH_VECTOR_INT_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); + IGRAPH_VECTOR_INT_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); - } + geodist = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(geodist, "Insufficient memory for calculating shortest paths."); 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); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); if (nrgeo) { - IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); - igraph_vector_null(nrgeo); + IGRAPH_CHECK(igraph_vector_int_resize(nrgeo, no_of_nodes)); + igraph_vector_int_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; + if (geodist[ IGRAPH_VIT_GET(vit) ] == 0) { + geodist[ IGRAPH_VIT_GET(vit) ] = -1; } else { to_reach--; /* this node was given multiple times */ } } - if (geodist[ (long int) from ] < 0) { + if (geodist[ 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)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); + IGRAPH_CHECK(igraph_vector_int_push_back(vptr, from)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); + + geodist[from] = 1; + VECTOR(ptrhead)[from] = 1; + IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, 0)); if (nrgeo) { - VECTOR(*nrgeo)[(long int)from] = 1; + VECTOR(*nrgeo)[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_CHECK(igraph_dqueue_int_push(&q, from)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); IGRAPH_ALLOW_INTERRUPTION(); @@ -201,20 +201,34 @@ int igraph_get_all_shortest_paths(const igraph_t *graph, * 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); - } + IGRAPH_ASSERT(maxdist >= 0); 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 we need the edge-paths, we need to use igraph_incident() followed by an + * IGRAPH_OTHER() macro in the main loop. This is going to be slower than + * using igraph_neighbors() due to branch mispredictions in IGRAPH_OTHER(), so we + * use igraph_incident() only if the user needs the edge-paths */ + if (edges) { + IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); + } else { + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + } + + n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; j++) { + igraph_integer_t neighbor; + igraph_integer_t parentptr; + + if (edges) { + /* user needs the edge-paths, so 'neis' contains edge IDs, we need to resolve + * the next edge ID into a vertex ID */ + neighbor = IGRAPH_OTHER(graph, VECTOR(neis)[j], actnode); + } else { + /* user does not need the edge-paths, so 'neis' contains vertex IDs */ + neighbor = VECTOR(neis)[j]; + } if (geodist[neighbor] > 0 && geodist[neighbor] - 1 < actdist + 1) { @@ -231,8 +245,8 @@ int igraph_get_all_shortest_paths(const igraph_t *graph, } 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); if (geodist[neighbor] < 0) { reached++; } @@ -243,76 +257,80 @@ int igraph_get_all_shortest_paths(const igraph_t *graph, 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]; + parentptr = VECTOR(ptrhead)[actnode]; + while (parentptr != 0) { + /* allocate a new igraph_vector_int_t at the end of paths */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&paths, &vptr)); + IGRAPH_CHECK(igraph_vector_int_update(vptr, igraph_vector_int_list_get_ptr(&paths, parentptr - 1))); + IGRAPH_CHECK(igraph_vector_int_push_back(vptr, neighbor)); + + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(&path_edge, &vptr_e)); + if (actnode != from) { + /* If the previous vertex was the source then there is no edge to add*/ + IGRAPH_CHECK(igraph_vector_int_update(vptr_e, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1))); + } + IGRAPH_CHECK(igraph_vector_int_push_back(vptr_e, VECTOR(neis)[j])); + + IGRAPH_CHECK(igraph_vector_int_push_back(&ptrlist, VECTOR(ptrhead)[neighbor])); + VECTOR(ptrhead)[neighbor] = igraph_vector_int_size(&ptrlist); + + parentptr = VECTOR(ptrlist)[parentptr - 1]; } } } - igraph_dqueue_destroy(&q); + igraph_dqueue_int_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); + memset(geodist, 0, sizeof(geodist[0]) * (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; + geodist[ 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]; - } - } + if (vertices) { + igraph_vector_int_list_clear(vertices); + } + if (edges) { + igraph_vector_int_list_clear(edges); } - 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]; + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_integer_t parentptr = 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]; + /* yes, transfer them to the result vector */ + while (parentptr != 0) { + /* Given two vector lists, list1 and list2, an efficient way to transfer + * a vector from list1 to the end of list2 is to extend list2 with an + * empty vector, then swap that empty vector with the given element of + * list1. This approach avoids creating a full copy of the vector. */ + if (vertices) { + igraph_vector_int_t *p; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &p)); + igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&paths, parentptr - 1)); + } + if (edges) { + igraph_vector_int_t *p; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &p)); + igraph_vector_int_swap(p, igraph_vector_int_list_get_ptr(&path_edge, parentptr - 1)); + } + parentptr = VECTOR(ptrlist)[parentptr - 1]; } } } IGRAPH_FREE(geodist); - igraph_vector_destroy(&ptrlist); - igraph_vector_destroy(&ptrhead); - igraph_vector_destroy(&neis); - igraph_vector_ptr_destroy(&paths); + igraph_vector_int_destroy(&ptrlist); + igraph_vector_int_destroy(&ptrhead); + igraph_vector_int_destroy(&neis); + igraph_vector_int_list_destroy(&paths); + igraph_vector_int_list_destroy(&path_edge); igraph_vit_destroy(&vit); - IGRAPH_FINALLY_CLEAN(6); + IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/astar.c b/src/vendor/cigraph/src/paths/astar.c new file mode 100644 index 00000000000..c3d5f55c822 --- /dev/null +++ b/src/vendor/cigraph/src/paths/astar.c @@ -0,0 +1,275 @@ +/* + 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 + +*/ + +#include "igraph_paths.h" +#include "igraph_interface.h" +#include "igraph_adjlist.h" +#include "igraph_memory.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static igraph_error_t null_heuristic( + igraph_real_t *result, igraph_integer_t from, igraph_integer_t to, + void *extra +) { + IGRAPH_UNUSED(from); + IGRAPH_UNUSED(to); + IGRAPH_UNUSED(extra); + + *result = 0; + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_shortest_path_astar + * \brief A* gives the shortest path from one vertex to another, with heuristic. + * + * \experimental + * + * Calculates a shortest path from a single source vertex to a single + * target, using the A* algorithm. A* tries to find a shortest path by + * starting at \p from and moving to vertices that lie on a path with + * the lowest estimated length. This length estimate is the sum of two + * numbers: the distance from the source (\p from) to the intermediate vertex, + * and the value returned by the heuristic function. The heuristic function + * provides an estimate the distance between intermediate candidate + * vertices and the target vertex \p to. The A* algorithm is guaranteed + * to give the correct shortest path (if one exists) only if the heuristic + * does not overestimate distances, i.e. if the heuristic function is + * \em admissible. + * + * \param graph The input graph, it can be directed or undirected. + * \param vertices Pointer to an initialized vector or the \c NULL + * pointer. If not \c NULL, then the vertex IDs along + * the path are stored here, including the source and target + * vertices. + * \param edges Pointer to an initialized vector or the \c NULL + * pointer. If not \c NULL, 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 Optional edge weights. Supply \c NULL for unweighted graphs. + * All edge weights must be non-negative. Additionally, no + * edge weight may be NaN. If either case does not hold, an error + * is returned. Edges with positive infinite weights are ignored. + * \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. + * \param heuristic A function that provides distance estimates to the + * target vertex. See \ref igraph_astar_heuristic_func_t for + * more information. + * \param extra This is passed on to the heuristic function. + * \return Error code. + * + * Time complexity: In the worst case, O(|E|log|V|+|V|), where + * |V| is the number of vertices and + * |E| is the number of edges in the graph. + * The running time depends on the accuracy of the distance estimates + * returned by the heuristic function. Assuming that the heuristic + * is admissible, the better the estimates, the shortert the running + * time. + */ + +igraph_error_t igraph_get_shortest_path_astar(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_astar_heuristic_func_t *heuristic, + void *extra) +{ + igraph_real_t heur_res; + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t dists; + igraph_integer_t *parent_eids; + + if (from < 0 || from >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex out of range.", IGRAPH_EINVVID); + } + + if (to < 0 || to >= no_of_nodes) { + IGRAPH_ERROR("End vertex out of range.", IGRAPH_EINVVID); + } + + if (!heuristic) { + heuristic = null_heuristic; + } + + if (weights) { /* If there are no weights, they are treated as 1. */ + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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, found weight of %g.", IGRAPH_EINVAL, min); + } + else if (isnan(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_lazy_inclist_init(graph, &inclist, mode, IGRAPH_LOOPS_TWICE)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + /* dists[v] is the length of the shortest path found so far between 'from' and 'v'. */ + IGRAPH_VECTOR_INIT_FINALLY(&dists, no_of_nodes); + igraph_vector_fill(&dists, IGRAPH_INFINITY); + + /* parent_eids[v] is the 1 + the ID of v's inbound edge in the shortest path tree. + * A value of 0 indicates unreachable vertices. */ + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with A* algorithm."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + VECTOR(dists)[from] = 0.0; + IGRAPH_CHECK(heuristic(&heur_res, from, to, extra)); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, from, -heur_res)); + + igraph_bool_t found = false; + while (!igraph_2wheap_empty(&Q)) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* The from -> u -> to distance estimate is the sum of the + * from -> u distance and the u -> to distance estimate + * obtained from the heuristic. + * + * We use an indexed heap to process 'u' vertices in order + * of the smallest from -> u -> to distance estimate. Since + * we only have a maximum heap available, we store negated values + * in order to obtain smallest values first. The value taken off + * the heap is ignored, we just want the index of 'u'. */ + + igraph_integer_t u; + igraph_2wheap_delete_max_index(&Q, &u); + + /* Reached the target vertex, the search can be stopped. */ + if (u == to) { + found = true; + break; + } + + /* Now we check all neighbors 'u' for a path with a shorter actual (not estimated) + * length than what was found so far. */ + + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, u); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + igraph_integer_t nlen = igraph_vector_int_size(neis); + for (igraph_integer_t i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); + igraph_real_t altdist; /* candidate from -> v distance */ + if (weights) { + igraph_real_t weight = VECTOR(*weights)[edge]; + if (weight == IGRAPH_INFINITY) { + continue; + } + altdist = VECTOR(dists)[u] + weight; + } else { + altdist = VECTOR(dists)[u] + 1; + } + igraph_real_t curdist = VECTOR(dists)[v]; + if (curdist == IGRAPH_INFINITY) { + /* This is the first finite from -> v distance we found. + * Here we rely on infinite weight edges having been skipped, see TODO above. */ + VECTOR(dists)[v] = altdist; + parent_eids[v] = edge + 1; + IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, v, -(altdist + heur_res))); + } else if (altdist < curdist) { + /* This is a shorter from -> v path than what was found before. */ + VECTOR(dists)[v] = altdist; + parent_eids[v] = edge + 1; + IGRAPH_CHECK(heuristic(&heur_res, v, to, extra)); + igraph_2wheap_modify(&Q, v, -(altdist + heur_res)); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + if (!found) { + IGRAPH_WARNING("Couldn't reach the target vertex."); + } + + /* Reconstruct the shortest paths based on vertex and/or edge IDs */ + if (vertices || edges) { + igraph_integer_t size, act, edge; + + if (vertices) { + igraph_vector_int_clear(vertices); + } + if (edges) { + igraph_vector_int_clear(edges); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = to; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vertices && (size > 0 || to == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vertices, size + 1)); + VECTOR(*vertices)[size] = to; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_resize(edges, size)); + } + act = to; + while (parent_eids[act]) { + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + size--; + if (vertices) { + VECTOR(*vertices)[size] = act; + } + if (edges) { + VECTOR(*edges)[size] = edge; + } + } + } + + + IGRAPH_FREE(parent_eids); + igraph_vector_destroy(&dists); + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + IGRAPH_FINALLY_CLEAN(4); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/paths/bellman_ford.c b/src/vendor/cigraph/src/paths/bellman_ford.c index 06aea098241..e5023e9eafe 100644 --- a/src/vendor/cigraph/src/paths/bellman_ford.c +++ b/src/vendor/cigraph/src/paths/bellman_ford.c @@ -24,38 +24,33 @@ #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 + * \function igraph_distances_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() . + * weights, you are better off with \ref igraph_distances_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 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 + * \param to The target vertices. + * \param weights The edge weights. There must not 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. + * \ref igraph_distances() 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 @@ -65,30 +60,30 @@ * 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 + * \sa \ref igraph_distances() for a faster unweighted version + * or \ref igraph_distances_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_error_t igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t i; + igraph_integer_t no_of_from, no_of_to; + igraph_dqueue_int_t Q; + igraph_vector_bool_t clean_vertices; + igraph_vector_int_t num_queued; igraph_vit_t fromvit, tovit; - igraph_real_t my_infinity = IGRAPH_INFINITY; igraph_bool_t all_to; igraph_vector_t dist; + int counter = 0; /* - speedup: a vertex is marked clean if its distance from the source @@ -99,23 +94,25 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, n times. */ if (!weights) { - return igraph_shortest_paths(graph, res, from, to, mode); + return igraph_distances(graph, res, from, to, mode); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); } if (no_of_edges > 0 && igraph_vector_is_any_nan(weights)) { - IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + 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_DQUEUE_INT_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INT_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); @@ -126,6 +123,11 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); IGRAPH_FINALLY(igraph_vit_destroy, &tovit); no_of_to = IGRAPH_VIT_SIZE(tovit); + + /* No need to check here whether the vertices in 'to' are unique because + * the loop below uses a temporary distance vector that is then copied + * into the result matrix at the end of the outer loop iteration, and + * this is safe even if 'to' contains the same vertex multiple times */ } IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); @@ -134,47 +136,52 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, for (IGRAPH_VIT_RESET(fromvit), i = 0; !IGRAPH_VIT_END(fromvit); IGRAPH_VIT_NEXT(fromvit), i++) { - long int source = IGRAPH_VIT_GET(fromvit); + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); - igraph_vector_fill(&dist, my_infinity); + igraph_vector_fill(&dist, IGRAPH_INFINITY); VECTOR(dist)[source] = 0; - igraph_vector_null(&clean_vertices); - igraph_vector_null(&num_queued); + igraph_vector_bool_null(&clean_vertices); + igraph_vector_int_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)); + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, j)); } - while (!igraph_dqueue_empty(&Q)) { - igraph_vector_int_t *neis; - long int nlen; + while (!igraph_dqueue_int_empty(&Q)) { + if (++counter >= 10000) { + counter = 0; + IGRAPH_ALLOW_INTERRUPTION(); + } - j = (long int) igraph_dqueue_pop(&Q); - VECTOR(clean_vertices)[j] = 1; + igraph_integer_t j = igraph_dqueue_int_pop(&Q); + VECTOR(clean_vertices)[j] = true; VECTOR(num_queued)[j] += 1; if (VECTOR(num_queued)[j] > no_of_nodes) { - IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP); + IGRAPH_ERROR("Negative loop in graph while calculating distances with 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])) { + if (VECTOR(dist)[j] == IGRAPH_INFINITY) { continue; } - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); - nlen = igraph_vector_int_size(neis); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); - 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]) { + igraph_integer_t nlen = igraph_vector_int_size(neis); + for (igraph_integer_t k = 0; k < nlen; k++) { + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); + igraph_real_t altdist = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + if (VECTOR(dist)[target] > altdist) { /* relax the edge */ - VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + VECTOR(dist)[target] = altdist; if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = 0; - IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); + VECTOR(clean_vertices)[target] = false; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); } } } @@ -184,9 +191,10 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, if (all_to) { igraph_matrix_set_row(res, &dist, i); } else { + igraph_integer_t j; for (IGRAPH_VIT_RESET(tovit), j = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit), j++) { - long int v = IGRAPH_VIT_GET(tovit); + igraph_integer_t v = IGRAPH_VIT_GET(tovit); MATRIX(*res, i, j) = VECTOR(dist)[v]; } } @@ -201,15 +209,29 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, } igraph_vit_destroy(&fromvit); - igraph_dqueue_destroy(&Q); - igraph_vector_destroy(&clean_vertices); - igraph_vector_destroy(&num_queued); + igraph_dqueue_int_destroy(&Q); + igraph_vector_bool_destroy(&clean_vertices); + igraph_vector_int_destroy(&num_queued); igraph_lazy_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } +/** + * \function igraph_shortest_paths_bellman_ford + * \brief Weighted shortest path lengths between vertices, allowing negative weights (deprecated). + * + * \deprecated-by igraph_distances_bellman_ford 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_bellman_ford(graph, res, from, to, weights, mode); +} /** * \ingroup structural @@ -224,44 +246,35 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, * \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 vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 + * \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 + * \param weights The edge weights. There must not 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. + * unweighted version, \ref igraph_get_shortest_paths() is called. + * Edges with positive infinite weights are ignored. * \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 + * \param parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent 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 + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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. @@ -280,8 +293,7 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, * \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. + * \p from is invalid vertex ID * \cli IGRAPH_ENEGLOOP * Bellman-ford algorithm encounted a negative loop. * \endclist @@ -289,35 +301,35 @@ int igraph_shortest_paths_bellman_ford(const igraph_t *graph, * 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 + * \sa \ref igraph_get_shortest_paths() for a faster unweighted version + * or \ref igraph_get_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_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_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_vector_int_t *parents, + igraph_vector_int_t *inbound_edges) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t *parent_eids; 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_integer_t i, j, k; + igraph_dqueue_int_t Q; + igraph_vector_bool_t clean_vertices; + igraph_vector_int_t num_queued; igraph_vit_t tovit; - igraph_real_t my_infinity = IGRAPH_INFINITY; igraph_vector_t dist; + int counter = 0; if (!weights) { - return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, - predecessors, inbound_edges); + return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, + parents, inbound_edges); } if (from < 0 || from >= no_of_nodes) { @@ -328,103 +340,109 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, 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_DQUEUE_INT_INIT_FINALLY(&Q, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_VECTOR_INT_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 (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(tovit))); } - if (edges && IGRAPH_VIT_SIZE(tovit) != igraph_vector_ptr_size(edges)) { - IGRAPH_ERROR("Size of `edges' and `to' should match.", IGRAPH_EINVAL); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(tovit))); } - 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); + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with Bellman-Ford."); + IGRAPH_FINALLY(igraph_free, parent_eids); + IGRAPH_VECTOR_INIT_FINALLY(&dist, no_of_nodes); - igraph_vector_fill(&dist, my_infinity); + igraph_vector_fill(&dist, IGRAPH_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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, j)); } - while (!igraph_dqueue_empty(&Q)) { - igraph_vector_int_t *neis; - long int nlen; + while (!igraph_dqueue_int_empty(&Q)) { + if (++counter >= 10000) { + counter = 0; + IGRAPH_ALLOW_INTERRUPTION(); + } - j = (long int) igraph_dqueue_pop(&Q); - VECTOR(clean_vertices)[j] = 1; + j = igraph_dqueue_int_pop(&Q); + VECTOR(clean_vertices)[j] = true; VECTOR(num_queued)[j] += 1; if (VECTOR(num_queued)[j] > no_of_nodes) { - IGRAPH_ERROR("cannot run Bellman-Ford algorithm", IGRAPH_ENEGLOOP); + IGRAPH_ERROR("Negative loop in graph while calculating distances with 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])) { + /* If we cannot get to j in finite time yet, there is no need to relax its edges */ + if (VECTOR(dist)[j] == IGRAPH_INFINITY) { continue; } - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) j); - nlen = igraph_vector_int_size(neis); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, j); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + igraph_integer_t 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]) { + igraph_integer_t nei = VECTOR(*neis)[k]; + igraph_integer_t target = IGRAPH_OTHER(graph, nei, j); + igraph_real_t altdist = VECTOR(dist)[j] + VECTOR(*weights)[nei]; + + /* infinite weights are handled correctly here; if an edge has + * infinite weight, altdist will also be infinite so the condition + * will never be true as if the edge was ignored */ + + if (VECTOR(dist)[target] > altdist) { /* relax the edge */ - VECTOR(dist)[target] = VECTOR(dist)[j] + VECTOR(*weights)[nei]; - parents[target] = nei + 1; + VECTOR(dist)[target] = altdist; + parent_eids[target] = nei + 1; if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = 0; - IGRAPH_CHECK(igraph_dqueue_push(&Q, target)); + VECTOR(clean_vertices)[target] = false; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); } } } } - /* Create `predecessors' if needed */ - if (predecessors) { - IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, 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) { + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { /* i was not reached */ - VECTOR(*predecessors)[i] = -1; + VECTOR(*parents)[i] = -2; } else { - /* i was reached via the edge with ID = parents[i] - 1 */ - VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i] - 1, i); + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); } } } /* Create `inbound_edges' if needed */ if (inbound_edges) { - IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (parents[i] <= 0) { + if (parent_eids[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; + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; } } } @@ -432,37 +450,37 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, /* 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; + igraph_integer_t node = IGRAPH_VIT_GET(tovit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; if (vertices) { - vvec = VECTOR(*vertices)[i]; - igraph_vector_clear(vvec); + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); } if (edges) { - evec = VECTOR(*edges)[i]; - igraph_vector_clear(evec); + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); size = 0; act = node; - while (parents[act]) { + while (parent_eids[act]) { size++; - edge = parents[act] - 1; + edge = parent_eids[act] - 1; act = IGRAPH_OTHER(graph, edge, act); } if (vvec && (size > 0 || node == from)) { - IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); } act = node; - while (parents[act]) { - edge = parents[act] - 1; + while (parent_eids[act]) { + edge = parent_eids[act] - 1; act = IGRAPH_OTHER(graph, edge, act); size--; if (vvec) { @@ -481,10 +499,10 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, 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_FREE(parent_eids); + igraph_dqueue_int_destroy(&Q); + igraph_vector_bool_destroy(&clean_vertices); + igraph_vector_int_destroy(&num_queued); igraph_lazy_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(5); @@ -493,10 +511,10 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, /** * \function igraph_get_shortest_path_bellman_ford - * \brief Weighted shortest path from one vertex to another one. + * \brief Weighted shortest path from one vertex to another one (Bellman-Ford). * - * Calculates a single (positively) weighted shortest path from - * a single vertex to another one, using Bellman-Ford algorithm. + * Finds a weighted shortest path from a single source vertex to + * a single target using the Bellman-Ford algorithm. * * * This function is a special case (and a wrapper) to @@ -504,15 +522,15 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, * * \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 + * 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 + * \param edges Pointer to an initialized 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 + * \param from The ID of the source vertex. + * \param to The ID of the target vertex. + * \param weights The edge weights. There must not 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 @@ -531,28 +549,26 @@ int igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, * more target vertices. */ -int igraph_get_shortest_path_bellman_ford(const igraph_t *graph, - igraph_vector_t *vertices, - igraph_vector_t *edges, +igraph_error_t igraph_get_shortest_path_bellman_ford(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_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; + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_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; + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); } else { vp = NULL; } if (edges) { - IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); - VECTOR(edges2)[0] = edges; + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); } else { ep = NULL; } @@ -561,12 +577,16 @@ int igraph_get_shortest_path_bellman_ford(const igraph_t *graph, from, igraph_vss_1(to), weights, mode, NULL, NULL)); + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ if (edges) { - igraph_vector_ptr_destroy(&edges2); + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - igraph_vector_ptr_destroy(&vertices2); + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } diff --git a/src/vendor/cigraph/src/paths/dijkstra.c b/src/vendor/cigraph/src/paths/dijkstra.c index 2c1e32e56aa..aa0d7b57ebb 100644 --- a/src/vendor/cigraph/src/paths/dijkstra.c +++ b/src/vendor/cigraph/src/paths/dijkstra.c @@ -24,33 +24,33 @@ #include "igraph_paths.h" #include "igraph_adjlist.h" -#include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" +#include "igraph_nongraph.h" #include "igraph_stack.h" +#include "igraph_vector_ptr.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. + * \function igraph_distances_dijkstra_cutoff + * \brief Weighted shortest path lengths between vertices, with cutoff. * - * 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. + * \experimental + * + * This function is similar to \ref igraph_distances_dijkstra(), but + * paths longer than \p cutoff will not be considered. * * \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. + * vertices given in the \p to argument. + * Vertices that are not reachable within distance \p cutoff will + * be assigned 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. @@ -58,29 +58,35 @@ * 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. + * version, \ref igraph_distances() is called. Edges with positive infinite + * weights are ignored. * \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 cutoff The maximal length of paths that will be considered. + * When the distance of two vertices is greater than this value, + * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are + * treated as infinity. * \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. + * Time complexity: at most O(s |E| log|V| + |V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. The + * \p cutoff parameter will limit the number of edges traversed from each + * source vertex, which reduces the computation time. * - * \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). + * \sa \ref igraph_distances_cutoff() for a (slightly) faster unweighted + * version. * - * \example examples/simple/dijkstra.c + * \example examples/simple/distances.c */ -int igraph_shortest_paths_dijkstra(const igraph_t *graph, +igraph_error_t igraph_distances_dijkstra_cutoff(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) { + igraph_neimode_t mode, + igraph_real_t cutoff) { /* Implementation details. This is the basic Dijkstra algorithm, with a binary heap. The heap is indexed, i.e. it stores not only @@ -89,39 +95,37 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, 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 + 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_vit_t fromvit, tovit; - long int no_of_from, no_of_to; + igraph_integer_t no_of_from, no_of_to; igraph_lazy_inclist_t inclist; - long int i, j; - igraph_real_t my_infinity = IGRAPH_INFINITY; + igraph_integer_t i, j; igraph_bool_t all_to; - igraph_vector_t indexv; + igraph_vector_int_t indexv; if (!weights) { - return igraph_shortest_paths(graph, res, from, to, mode); + return igraph_distances_cutoff(graph, res, from, to, mode, cutoff); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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_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_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); + } else if (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); } } @@ -138,14 +142,21 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, if (all_to) { no_of_to = no_of_nodes; } else { - IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes); + IGRAPH_VECTOR_INT_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); + + /* We need to check whether the vertices in 'tovit' are unique; this is + * because the inner while loop of the main algorithm updates the + * distance matrix whenever a shorter path is encountered from the + * source vertex 'i' to a target vertex, and we need to be able to + * map a target vertex to its column in the distance matrix. The mapping + * is constructed by the loop below */ for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { - long int v = IGRAPH_VIT_GET(tovit); + igraph_integer_t v = IGRAPH_VIT_GET(tovit); if (VECTOR(indexv)[v]) { - IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", + IGRAPH_ERROR("Target vertex list must not have any duplicates.", IGRAPH_EINVAL); } VECTOR(indexv)[v] = ++i; @@ -153,28 +164,38 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); - igraph_matrix_fill(res, my_infinity); + igraph_matrix_fill(res, IGRAPH_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_integer_t reached = 0; + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + igraph_2wheap_clear(&Q); - igraph_2wheap_push_with_index(&Q, source, -1.0); + + /* Many systems distinguish between +0.0 and -0.0. + * Since we store negative distances in the heap, + * we must insert -0.0 in order to get +0.0 as the + * final distance result. */ + igraph_2wheap_push_with_index(&Q, source, -0.0); while (!igraph_2wheap_empty(&Q)) { - long int minnei = igraph_2wheap_max_index(&Q); + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - long int nlen; + igraph_integer_t nlen; + + if (cutoff >= 0 && mindist > cutoff) { + continue; + } if (all_to) { - MATRIX(*res, i, minnei) = mindist - 1.0; + MATRIX(*res, i, minnei) = mindist; } else { if (VECTOR(indexv)[minnei]) { - MATRIX(*res, i, (long int)(VECTOR(indexv)[minnei] - 1)) = mindist - 1.0; + MATRIX(*res, i, VECTOR(indexv)[minnei] - 1) = mindist; reached++; if (reached == no_of_to) { igraph_2wheap_clear(&Q); @@ -184,21 +205,30 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); 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) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_real_t weight = VECTOR(*weights)[edge]; + + /* Optimization: do not follow infinite-weight edges. */ + if (weight == IGRAPH_INFINITY) { + continue; + } + + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); + igraph_real_t altdist = mindist + weight; + + if (! igraph_2wheap_has_elem(&Q, tto)) { /* 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)); + } else if (igraph_2wheap_has_active(&Q, tto)) { + igraph_real_t curdist = -igraph_2wheap_get(&Q, tto); + if (altdist < curdist) { + /* This is a shorter path */ + igraph_2wheap_modify(&Q, tto, -altdist); + } } } @@ -208,7 +238,7 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, if (!all_to) { igraph_vit_destroy(&tovit); - igraph_vector_destroy(&indexv); + igraph_vector_int_destroy(&indexv); IGRAPH_FINALLY_CLEAN(2); } @@ -217,7 +247,72 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, igraph_vit_destroy(&fromvit); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_distances_dijkstra + * \brief Weighted shortest path lengths between vertices. + * + * This function implements Dijkstra's algorithm, which can find + * the weighted shortest path lengths from a source vertex to all + * other vertices. This function allows specifying a set of source + * and target vertices. The algorithm is run independently for each + * source and the results are retained only for the specified targets. + * This implementation uses a binary heap for efficiency. + * + * \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 \p to argument. + * Unreachable vertices have 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_distances() 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|V|+|V|), where |V| is the number of + * vertices, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_distances() for a (slightly) faster unweighted + * version or \ref igraph_distances_bellman_ford() for a weighted + * variant that works in the presence of negative edge weights (but no + * negative loops) + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances_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) { + return igraph_distances_dijkstra_cutoff(graph, res, from, to, weights, mode, -1); +} + +/** + * \function igraph_shortest_paths_dijkstra + * \brief Weighted shortest path lengths between vertices (deprecated). + * + * \deprecated-by igraph_distances_dijkstra 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_dijkstra(graph, res, from, to, weights, mode); } /** @@ -229,27 +324,17 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, * 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 vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 + * \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 @@ -268,12 +353,12 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, * 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 + * \param parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent 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 + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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. @@ -290,30 +375,29 @@ int igraph_shortest_paths_dijkstra(const igraph_t *graph, * \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. + * \p from is invalid vertex ID * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * - * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * Time complexity: O(|E|log|V|+|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 + * \sa \ref igraph_distances_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_error_t igraph_get_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_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) { + igraph_vector_int_t *parents, + igraph_vector_int_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 @@ -325,27 +409,27 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, - 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 + computation, as isfinite() 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 + - `parent_eids' 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t *parent_eids; igraph_bool_t *is_target; - long int i, to_reach; + igraph_integer_t i, to_reach; if (!weights) { return igraph_get_shortest_paths(graph, vertices, edges, from, to, mode, - predecessors, inbound_edges); + parents, inbound_edges); } if (from < 0 || from >= no_of_nodes) { @@ -353,26 +437,26 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + IGRAPH_ERROR("Weight vector length does not match 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); + IGRAPH_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); } - else if (igraph_is_nan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + else if (isnan(min)) { + IGRAPH_ERROR("Weights 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 (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); } - if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { - IGRAPH_ERROR("Size of `edges' and `to' should match", IGRAPH_EINVAL); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); } IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); @@ -383,98 +467,96 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, 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); + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_eids); + is_target = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); - if (is_target == 0) { - IGRAPH_ERROR("Can't calculate shortest paths", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(is_target, "Insufficient memory for shortest paths with Dijkstra's algorithm."); 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; + if (!is_target[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = true; } else { to_reach--; /* this node was given multiple times */ } } - VECTOR(dists)[(long int)from] = 0.0; /* zero distance */ - parents[(long int)from] = 0; + VECTOR(dists)[from] = 0.0; /* zero distance */ + parent_eids[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_integer_t 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; + is_target[minnei] = false; to_reach--; } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); 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_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t 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; + parent_eids[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)); + parent_eids[tto] = edge + 1; + igraph_2wheap_modify(&Q, tto, -altdist); } } } /* !igraph_2wheap_empty(&Q) */ if (to_reach > 0) { - IGRAPH_WARNING("Couldn't reach some vertices"); + IGRAPH_WARNING("Couldn't reach some vertices."); } - /* Create `predecessors' if needed */ - if (predecessors) { - IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, 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) { + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { /* i was not reached */ - VECTOR(*predecessors)[i] = -1; + VECTOR(*parents)[i] = -2; } else { - /* i was reached via the edge with ID = parents[i] - 1 */ - VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, parents[i] - 1, i); + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); } } } /* Create `inbound_edges' if needed */ if (inbound_edges) { - IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (parents[i] <= 0) { + if (parent_eids[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; + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; } } } @@ -482,37 +564,37 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, /* 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; + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; if (vertices) { - vvec = VECTOR(*vertices)[i]; - igraph_vector_clear(vvec); + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); } if (edges) { - evec = VECTOR(*edges)[i]; - igraph_vector_clear(evec); + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); size = 0; act = node; - while (parents[act]) { + while (parent_eids[act]) { size++; - edge = parents[act] - 1; + edge = parent_eids[act] - 1; act = IGRAPH_OTHER(graph, edge, act); } if (vvec && (size > 0 || node == from)) { - IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); } act = node; - while (parents[act]) { - edge = parents[act] - 1; + while (parent_eids[act]) { + edge = parent_eids[act] - 1; act = IGRAPH_OTHER(graph, edge, act); size--; if (vvec) { @@ -529,33 +611,35 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, igraph_2wheap_destroy(&Q); igraph_vector_destroy(&dists); IGRAPH_FREE(is_target); - IGRAPH_FREE(parents); + IGRAPH_FREE(parent_eids); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(6); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_get_shortest_path_dijkstra - * \brief Weighted shortest path from one vertex to another one. + * \brief Weighted shortest path from one vertex to another one (Dijkstra). * - * Calculates a single (positively) weighted shortest path from - * a single vertex to another one, using Dijkstra's algorithm. + * Finds a weighted shortest path from a single source vertex to + * a single target, using Dijkstra's algorithm. If more than one + * shortest path exists, an arbitrary one is returned. * - * This function is a special case (and a wrapper) to + * + * 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 + * 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 + * \param edges Pointer to an initialized 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 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 @@ -568,63 +652,55 @@ int igraph_get_shortest_paths_dijkstra(const igraph_t *graph, * ignored for undirected graphs. * \return Error code. * - * Time complexity: O(|E|log|E|+|V|), |V| is the number of vertices, + * Time complexity: O(|E|log|V|+|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_error_t igraph_get_shortest_path_dijkstra(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_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; + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_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; + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); } else { - vp = 0; + vp = NULL; } if (edges) { - IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); - VECTOR(edges2)[0] = edges; + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); } else { - ep = 0; + ep = NULL; } IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph, vp, ep, from, igraph_vss_1(to), - weights, mode, 0, 0)); + weights, mode, NULL, NULL)); + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ if (edges) { - igraph_vector_ptr_destroy(&edges2); + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - igraph_vector_ptr_destroy(&vertices2); + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_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)); + return IGRAPH_SUCCESS; } /** @@ -633,13 +709,19 @@ static int igraph_i_vector_tail_cmp(const void* path1, const void* path2) { * \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 + * \param vertices Pointer to an initialized integer vector list or NULL. + * If not NULL, then 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 edges Pointer to an initialized integer vector list or NULL. If + * not NULL, then each vector object contains the edges 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_int_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 @@ -647,7 +729,7 @@ static int igraph_i_vector_tail_cmp(const void* path1, const void* path2) { * 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 + * \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 @@ -671,24 +753,24 @@ static int igraph_i_vector_tail_cmp(const void* path1, const void* path2) { * \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. + * \p from is an invalid vertex ID * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist * - * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * Time complexity: O(|E|log|V|+|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 + * \sa \ref igraph_distances_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_error_t igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_vector_int_t *nrgeo, igraph_integer_t from, igraph_vs_t to, const igraph_vector_t *weights, igraph_neimode_t mode) { @@ -696,57 +778,77 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, it's basically the same. */ - long int no_of_nodes = igraph_vcount(graph); - long int no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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; + igraph_vector_t dists; + igraph_vector_int_t index; + igraph_vector_int_t order; + igraph_vector_ptr_t parents, parents_edge; + + unsigned char *is_target; /* uses more than two discrete values, can't be 'bool' */ + igraph_integer_t i, n, to_reach; + igraph_bool_t free_vertices = false; int cmp_result; const double eps = IGRAPH_SHORTEST_PATH_EPSILON; if (!weights) { - return igraph_get_all_shortest_paths(graph, res, nrgeo, from, to, mode); + return igraph_get_all_shortest_paths(graph, vertices, edges, nrgeo, from, to, mode); } if (from < 0 || from >= no_of_nodes) { IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); } - if (res == 0 && nrgeo == 0) { + if (vertices == NULL && nrgeo == NULL && edges == NULL) { return IGRAPH_SUCCESS; } - if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + IGRAPH_ERROR("Weight vector length does not match 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); + IGRAPH_ERRORF("Edge weights must not be negative, got %g.", IGRAPH_EINVAL, min); } - else if (igraph_is_nan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + else if (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); } } /* parents stores a vector for each vertex, listing the parent vertices - * of each vertex in the traversal */ + * of each vertex in the traversal. Right now we do not use an + * igraph_vector_int_list_t because that would pre-initialize vectors + * for all the nodes even if the traversal would involve only a small part + * of the graph */ 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); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&parents, igraph_vector_destroy); + + /* parents_edge stores a vector for each vertex, listing the parent edges + * of each vertex in the traversal */ + IGRAPH_CHECK(igraph_vector_ptr_init(&parents_edge, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &parents_edge); + IGRAPH_VECTOR_PTR_SET_ITEM_DESTRUCTOR(&parents_edge, 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)); + igraph_vector_int_t *parent_vec, *parent_edge_vec; + + parent_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(parent_vec, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_vec); + IGRAPH_CHECK(igraph_vector_int_init(parent_vec, 0)); VECTOR(parents)[i] = parent_vec; + IGRAPH_FINALLY_CLEAN(1); + + parent_edge_vec = IGRAPH_CALLOC(1, igraph_vector_int_t); + IGRAPH_CHECK_OOM(parent_edge_vec, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, parent_edge_vec); + IGRAPH_CHECK(igraph_vector_int_init(parent_edge_vec, 0)); + VECTOR(parents_edge)[i] = parent_edge_vec; + IGRAPH_FINALLY_CLEAN(1); } /* distance of each vertex from the root */ @@ -755,13 +857,11 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* order lists the order of vertices in which they were found during * the traversal */ - IGRAPH_VECTOR_INIT_FINALLY(&order, 0); + IGRAPH_VECTOR_INT_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_CHECK_OOM(is_target, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); IGRAPH_FINALLY(igraph_free, is_target); /* two-way heap storing vertices and distances */ @@ -777,76 +877,83 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, 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; + if (!is_target[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = 1; } else { - to_reach--; /* this node was given multiple times */ + 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); + VECTOR(dists)[from] = 0.0; /* zero distance */ + igraph_2wheap_push_with_index(&Q, from, 0.0); while (!igraph_2wheap_empty(&Q) && to_reach > 0) { - long int nlen, minnei = igraph_2wheap_max_index(&Q); + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_vector_int_push_back(&order, minnei)); /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); 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_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t 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; + igraph_vector_int_t *parent_vec, *parent_edge_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)); + + parent_vec = (igraph_vector_int_t*)VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + 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)); + * 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_int_t*) VECTOR(parents)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[tto]; + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); } 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)); + + parent_vec = (igraph_vector_int_t*)VECTOR(parents)[tto]; + igraph_vector_int_clear(parent_vec); + IGRAPH_CHECK(igraph_vector_int_push_back(parent_vec, minnei)); + parent_edge_vec = (igraph_vector_int_t*)VECTOR(parents_edge)[tto]; + igraph_vector_int_clear(parent_edge_vec); + IGRAPH_CHECK(igraph_vector_int_push_back(parent_edge_vec, edge)); + + igraph_2wheap_modify(&Q, tto, -altdist); } } } /* !igraph_2wheap_empty(&Q) */ if (to_reach > 0) { - IGRAPH_WARNING("Couldn't reach some vertices"); + IGRAPH_WARNING("Couldn't reach some of the requested target vertices."); } /* we don't need these anymore */ @@ -856,20 +963,20 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* printf("Order:\n"); - igraph_vector_print(&order); + igraph_vector_int_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 (igraph_vector_int_size(VECTOR(parents)[i]) > 0) { + printf("[%ld]: ", i); + igraph_vector_int_print(VECTOR(parents)[i]); } } */ if (nrgeo) { - IGRAPH_CHECK(igraph_vector_resize(nrgeo, no_of_nodes)); - igraph_vector_null(nrgeo); + IGRAPH_CHECK(igraph_vector_int_resize(nrgeo, no_of_nodes)); + igraph_vector_int_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 @@ -877,26 +984,27 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, * 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); + VECTOR(*nrgeo)[from] = 1; + n = igraph_vector_int_size(&order); for (i = 1; i < n; i++) { - long int node, j, k; - igraph_vector_t *parent_vec; + igraph_integer_t node, j, k; + igraph_vector_int_t *parent_vec; - node = (long int)VECTOR(order)[i]; + node = VECTOR(order)[i]; /* now, take the parent vertices */ - parent_vec = (igraph_vector_t*)VECTOR(parents)[node]; - k = igraph_vector_size(parent_vec); + parent_vec = (igraph_vector_int_t*)VECTOR(parents)[node]; + k = igraph_vector_int_size(parent_vec); for (j = 0; j < k; j++) { - VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[(long int)VECTOR(*parent_vec)[j]]; + VECTOR(*nrgeo)[node] += VECTOR(*nrgeo)[VECTOR(*parent_vec)[j]]; } } } - if (res) { - igraph_vector_t *path, *paths_index, *parent_vec; - igraph_stack_t stack; - long int j, node; + if (vertices || edges) { + igraph_vector_int_t *path, *parent_vec, *parent_edge_vec; + igraph_vector_t *paths_index; + igraph_stack_int_t stack; + igraph_integer_t j, node; /* a shortest path from the starting vertex to vertex i can be * obtained by calculating the shortest paths from the "parents" @@ -914,77 +1022,85 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, } 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); + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_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); + i = IGRAPH_VIT_GET(vit); if (!is_target[i]) { is_target[i] = 1; - IGRAPH_CHECK(igraph_stack_push(&stack, i)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, i)); } } igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - while (!igraph_stack_empty(&stack)) { + while (!igraph_stack_int_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); + igraph_integer_t el = igraph_stack_int_pop(&stack); + parent_vec = (igraph_vector_int_t*) VECTOR(parents)[el]; + i = igraph_vector_int_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]; + n = VECTOR(*parent_vec)[j]; if (!is_target[n]) { is_target[n] = 2; - IGRAPH_CHECK(igraph_stack_push(&stack, n)); + IGRAPH_CHECK(igraph_stack_int_push(&stack, n)); } } } - igraph_stack_destroy(&stack); + igraph_stack_int_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 + * index in vertices 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); + n = igraph_vector_int_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); + if (edges) { + igraph_vector_int_list_clear(edges); + } + + if (vertices) { + igraph_vector_int_list_clear(vertices); + } else { + /* If the 'vertices' vector doesn't exist, then create one, in order + * for the algorithm to work. */ + vertices = IGRAPH_CALLOC(1, igraph_vector_int_list_t); + IGRAPH_CHECK_OOM(vertices, "Insufficient memory for all shortest paths with Dijkstra's algorithm."); + IGRAPH_FINALLY(igraph_free, vertices); + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(vertices, 0); + free_vertices = true; + } /* 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; + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); + IGRAPH_CHECK(igraph_vector_int_push_back(path, from)); + + if (edges) { + /* the shortest path from the source to itself is empty */ + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); + } + VECTOR(*paths_index)[from] = 1; for (i = 1; i < n; i++) { - long int m, path_count; - igraph_vector_t *parent_path; + igraph_integer_t m, path_count; + igraph_vector_int_t *parent_path, *parent_path_edge; - node = (long int) VECTOR(order)[i]; + node = 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 @@ -997,13 +1113,13 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, /* we are calculating the shortest paths of node now. */ /* first, we update the paths_index */ - path_count = igraph_vector_ptr_size(res); + path_count = igraph_vector_int_list_size(vertices); 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); + parent_vec = (igraph_vector_int_t*) VECTOR(parents)[node]; + parent_edge_vec = (igraph_vector_int_t*) VECTOR(parents_edge)[node]; + m = igraph_vector_int_size(parent_vec); /* printf("Calculating shortest paths to vertex %ld\n", node); @@ -1014,63 +1130,84 @@ int igraph_get_all_shortest_paths_dijkstra(const igraph_t *graph, 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; + igraph_integer_t parent_node = VECTOR(*parent_vec)[j]; + igraph_integer_t parent_edge = VECTOR(*parent_edge_vec)[j]; + igraph_integer_t parent_path_idx = 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); + printf(" Paths to parent start at index %ld in vertices\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) { + parent_path = igraph_vector_int_list_get_ptr(vertices, parent_path_idx); + if (igraph_vector_int_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)); + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(vertices, &path)); + + /* We need to re-read parent_path because the previous push_back_new() + * call might have reallocated the entire vector list */ + parent_path = igraph_vector_int_list_get_ptr(vertices, parent_path_idx); + IGRAPH_CHECK(igraph_vector_int_update(path, parent_path)); + IGRAPH_CHECK(igraph_vector_int_push_back(path, node)); + + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_new(edges, &path)); + if (parent_node != from) { + parent_path_edge = igraph_vector_int_list_get_ptr(edges, parent_path_idx); + IGRAPH_CHECK(igraph_vector_int_update(path, parent_path_edge)); + } + IGRAPH_CHECK(igraph_vector_int_push_back(path, parent_edge)); + } } } } - /* 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) { + /* free those paths from the result vector that we won't need */ + n = igraph_vector_int_list_size(vertices); + i = 0; + while (i < n) { + igraph_integer_t tmp; + path = igraph_vector_int_list_get_ptr(vertices, i); + tmp = igraph_vector_int_tail(path); + if (is_target[tmp] == 1) { /* we need this path, keep it */ - VECTOR(*res)[j] = path; - j++; + i++; } else { /* we don't need this path, free it */ - igraph_vector_destroy(path); free(path); + igraph_vector_int_list_discard_fast(vertices, i); + if (edges) { + igraph_vector_int_list_discard_fast(edges, i); + } + n--; } } - 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); + /* sort the remaining paths by the target vertices */ + IGRAPH_VECTOR_INT_INIT_FINALLY(&index, 0); + igraph_vector_int_list_sort_ind(vertices, &index, igraph_vector_int_colex_cmp); + IGRAPH_CHECK(igraph_vector_int_list_permute(vertices, &index)); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_permute(edges, &index)); + } + igraph_vector_int_destroy(&index); + IGRAPH_FINALLY_CLEAN(1); } /* free the allocated memory */ - igraph_vector_destroy(&order); + if (free_vertices) { + igraph_vector_int_list_destroy(vertices); + IGRAPH_FREE(vertices); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_vector_int_destroy(&order); IGRAPH_FREE(is_target); igraph_vector_destroy(&dists); igraph_vector_ptr_destroy_all(&parents); - IGRAPH_FINALLY_CLEAN(4); + igraph_vector_ptr_destroy_all(&parents_edge); + IGRAPH_FINALLY_CLEAN(5); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/distances.c b/src/vendor/cigraph/src/paths/distances.c index 1e5a91ef78a..e14a13c5a07 100644 --- a/src/vendor/cigraph/src/paths/distances.c +++ b/src/vendor/cigraph/src/paths/distances.c @@ -2,8 +2,7 @@ /* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2011-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + 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 @@ -16,10 +15,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - + along with this program. If not, see . */ #include "igraph_paths.h" @@ -30,35 +26,35 @@ #include "igraph_vector.h" #include "igraph_interface.h" #include "igraph_adjlist.h" +#include "igraph_random.h" #include "core/interruption.h" +#include "core/indheap.h" -static int igraph_i_eccentricity(const igraph_t *graph, +/* When vid_ecc is not NULL, only one vertex ID should be passed in vids. + * vid_ecc will then return the id of the vertex farthest from the one in + * vids. If unconn == false and not all other vertices were reachable from + * the single given vertex, -1 is returned in vid_ecc. */ +static igraph_error_t igraph_i_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, - igraph_neimode_t mode, - const igraph_adjlist_t *adjlist) { + igraph_lazy_adjlist_t *adjlist, + igraph_integer_t *vid_ecc, + igraph_bool_t unconn) { - int no_of_nodes = igraph_vcount(graph); - igraph_dqueue_long_t q; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_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_integer_t i, mark = 1; + igraph_integer_t min_degree = 0; - IGRAPH_CHECK(igraph_dqueue_long_init(&q, 100)); - IGRAPH_FINALLY(igraph_dqueue_long_destroy, &q); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); 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_VECTOR_INT_INIT_FINALLY(&counted, no_of_nodes); IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_VIT_SIZE(vit))); igraph_vector_fill(res, -1); @@ -67,61 +63,166 @@ static int igraph_i_eccentricity(const igraph_t *graph, !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), mark++, i++) { - long int source; + igraph_integer_t source; + igraph_integer_t nodes_reached = 1; source = IGRAPH_VIT_GET(vit); - IGRAPH_CHECK(igraph_dqueue_long_push(&q, source)); - IGRAPH_CHECK(igraph_dqueue_long_push(&q, 0)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + igraph_integer_t dist = igraph_dqueue_int_pop(&q); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(adjlist, act); + igraph_integer_t j, n; - if (dist > VECTOR(*res)[i]) { - VECTOR(*res)[i] = dist; - } + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); - 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)); - } + n = igraph_vector_int_size(neis); + for (j = 0; j < n; j++) { + igraph_integer_t nei = VECTOR(*neis)[j]; + if (VECTOR(counted)[nei] != mark) { + VECTOR(counted)[nei] = mark; + nodes_reached++; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_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)); - } + } + if (vid_ecc) { + /* Return the vertex ID of the vertex which has the lowest + * degree of the vertices most distant from the starting + * vertex. Assumes there is only 1 vid in vids. Used for + * pseudo_diameter calculations. */ + if (dist > VECTOR(*res)[i] || (dist == VECTOR(*res)[i] && n < min_degree)) { + VECTOR(*res)[i] = dist; + *vid_ecc = act; + min_degree = n; } + } else if (dist > VECTOR(*res)[i]) { + VECTOR(*res)[i] = dist; } - } /* while !igraph_dqueue_long_empty(dqueue) */ + } /* while !igraph_dqueue_int_empty(dqueue) */ + if (nodes_reached != no_of_nodes && !unconn && vid_ecc) { + *vid_ecc = -1; + break; + } } /* 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_dqueue_int_destroy(&q); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * This function finds the weighted eccentricity and returns it via \p ecc. + * It's used for igraph_pseudo_diameter_dijkstra() and igraph_eccentricity_dijkstra(). + * \p vid_ecc returns the vertex id of the ecc with the greatest + * distance from \p vid_start. If two vertices have the same greatest distance, + * the one with the lowest degree is chosen. + * When the graph is not (strongly) connected and \p unconn is false, then \p ecc + * wil be set to infinity, and \p vid_ecc to -1; + */ +static igraph_error_t igraph_i_eccentricity_dijkstra( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *ecc, + igraph_integer_t vid_start, + igraph_integer_t *vid_ecc, + igraph_bool_t unconn, + igraph_lazy_inclist_t *inclist) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_2wheap_t Q; + igraph_vector_t vec_dist; + igraph_integer_t i; + igraph_real_t degree_ecc, dist; + igraph_integer_t degree_i; + igraph_vector_int_t *neis; + + IGRAPH_VECTOR_INIT_FINALLY(&vec_dist, no_of_nodes); + igraph_vector_fill(&vec_dist, IGRAPH_INFINITY); + IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); + + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, vid_start, -1.0); + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); + igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); + igraph_integer_t nlen; + + VECTOR(vec_dist)[minnei] = mindist - 1.0; + + /* Now check all neighbors of 'minnei' for a shorter path */ + neis = igraph_lazy_inclist_get(inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t 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 (altdist == IGRAPH_INFINITY) { + /* Ignore edges with positive infinite weights */ + } else 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_2wheap_modify(&Q, tto, -altdist); + } + } + } + + *ecc = 0; + *vid_ecc = vid_start; + degree_ecc = 0; + + for (i = 0; i < no_of_nodes; i++) { + if (i == vid_start) { + continue; + } + dist = VECTOR(vec_dist)[i]; + + /* inclist is used to ignore multiple edges when finding the degree */ + neis = igraph_lazy_inclist_get(inclist, i); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + degree_i = igraph_vector_int_size(neis); + + if (dist > *ecc) { + if (!isfinite(dist)) { + if (!unconn) { + *ecc = IGRAPH_INFINITY; + *vid_ecc = -1; + break; + } + } else { + *ecc = dist; + *vid_ecc = i; + degree_ecc = degree_i; + } + } else if (dist == *ecc) { + if (degree_i < degree_ecc) { + degree_ecc = degree_i; + *vid_ecc = i; + } + } + } + igraph_2wheap_destroy(&Q); + igraph_vector_destroy(&vec_dist); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; } /** @@ -156,12 +257,100 @@ static int igraph_i_eccentricity(const igraph_t *graph, * \example examples/simple/igraph_eccentricity.c */ -int igraph_eccentricity(const igraph_t *graph, +igraph_error_t igraph_eccentricity(const igraph_t *graph, igraph_vector_t *res, igraph_vs_t vids, igraph_neimode_t mode) { + igraph_lazy_adjlist_t adjlist; - return igraph_i_eccentricity(graph, res, vids, mode, /*adjlist=*/ 0); + 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_i_eccentricity(graph, res, vids, &adjlist, + /*vid_ecc*/ NULL, /*unconn*/ 1)); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_eccentricity_dijkstra + * \brief Eccentricity of some vertices, using weighted edges. + * + * 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 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_eccentricity() is called. Edges with positive + * infinite weights are ignored. + * \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. + * + */ + +igraph_error_t igraph_eccentricity_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_t *res, + igraph_vs_t vids, + igraph_neimode_t mode) { + igraph_lazy_inclist_t inclist; + igraph_vit_t vit; + igraph_integer_t dump; + igraph_real_t ecc; + igraph_integer_t no_of_edges = igraph_ecount(graph); + + if (weights == NULL) { + return igraph_eccentricity(graph, res, vids, mode); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, mode, + IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + IGRAPH_CHECK(igraph_vector_resize(res, 0)); + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); + + for (IGRAPH_VIT_RESET(vit); + !IGRAPH_VIT_END(vit); + IGRAPH_VIT_NEXT(vit)) { + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc, IGRAPH_VIT_GET(vit), /*vid_ecc*/ &dump, /*unconn*/ 1, &inclist)); + IGRAPH_CHECK(igraph_vector_push_back(res, ecc)); + } + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } /** @@ -189,26 +378,539 @@ int igraph_eccentricity(const igraph_t *graph, * \example examples/simple/igraph_radius.c */ -int igraph_radius(const igraph_t *graph, igraph_real_t *radius, +igraph_error_t igraph_radius(const igraph_t *graph, igraph_real_t *radius, igraph_neimode_t mode) { - int no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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)); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), + mode)); *radius = igraph_vector_min(&ecc); igraph_vector_destroy(&ecc); - igraph_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_pseudo_diameter + * \brief Approximation and lower bound of diameter. + * + * This algorithm finds a pseudo-peripheral vertex and returns its + * eccentricity. This value can be used as an approximation + * and lower bound of the diameter of a graph. + * + * + * A pseudo-peripheral vertex is a vertex v, such that for every + * vertex u which is as far away from v as possible, v is also as + * far away from u as possible. The process of finding one depends + * on where the search starts, and for a disconnected graph the + * maximum diameter found will be that of the component \p vid_start + * is in. + * + * \param graph The input graph, if it is directed, its edge directions + * are ignored. + * \param diameter Pointer to a real variable, the result is stored + * here. + * \param vid_start Id of the starting vertex. If this is negative, a + * random starting vertex is chosen. + * \param from Pointer to an integer, if not \c NULL it will be set to the + * source vertex of the diameter path. If \p unconn is \c false, and + * a disconnected graph is detected, this is set to -1. + * \param to Pointer to an integer, if not \c NULL it will be set to the + * target vertex of the diameter path. If \p unconn is \c false, and + * a disconnected graph is detected, this is set to -1. + * \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. + * + * Time complexity: O(|V||E|)), where |V| is the number of + * vertices and |E| is the number of edges. + * + * \sa \ref igraph_eccentricity(), \ref igraph_diameter(). + * + */ +igraph_error_t igraph_pseudo_diameter(const igraph_t *graph, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_real_t ecc_v; + igraph_real_t ecc_u; + igraph_integer_t vid_ecc; + igraph_integer_t ito, ifrom; + igraph_bool_t inf = false; + + if (vid_start >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); + } + + /* We will reach here when vid_start < 0 and the graph has no vertices. */ + if (no_of_nodes == 0) { + if (diameter) { + *diameter = IGRAPH_NAN; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + return IGRAPH_SUCCESS; + } + + if (vid_start < 0) { + RNG_BEGIN(); + vid_start = RNG_INTEGER(0, no_of_nodes - 1); + RNG_END(); + } + + if (!igraph_is_directed(graph) || !directed) { + igraph_lazy_adjlist_t adjlist; + igraph_vector_t ecc_vec; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); + ifrom = vid_start; + IGRAPH_VECTOR_INIT_FINALLY(&ecc_vec, no_of_nodes); + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_start), + &adjlist, &vid_ecc, unconn)); + ecc_u = VECTOR(ecc_vec)[0]; + + if (!unconn && vid_ecc == -1) { + inf = true; + } else { + while (true) { + IGRAPH_ALLOW_INTERRUPTION(); + + ito = vid_ecc; + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_vec, igraph_vss_1(vid_ecc), + &adjlist, &vid_ecc, 1)); + + ecc_v = VECTOR(ecc_vec)[0]; + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + ifrom = ito; + } else { + break; + } + } + } + igraph_vector_destroy(&ecc_vec); + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(2); + } else { + igraph_vector_t ecc_out; + igraph_vector_t ecc_in; + igraph_integer_t vid_ecc_in; + igraph_integer_t vid_ecc_out; + igraph_integer_t vid_end; + igraph_bool_t direction; + igraph_lazy_adjlist_t adjlist_in; + igraph_lazy_adjlist_t adjlist_out; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_in, IGRAPH_IN, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_in); + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist_out, IGRAPH_OUT, + IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist_out); + + IGRAPH_VECTOR_INIT_FINALLY(&ecc_in, igraph_vcount(graph)); + IGRAPH_VECTOR_INIT_FINALLY(&ecc_out, igraph_vcount(graph)); + + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_start), + &adjlist_out, &vid_ecc_out, unconn)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_start), + &adjlist_in, &vid_ecc_in, unconn)); + + /* A directed graph is strongly connected iff all vertices are reachable + * from vid_start both when moving along or moving opposite the edge directions. */ + if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { + inf = true; + } else { + if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { + vid_ecc = vid_ecc_out; + ecc_u = VECTOR(ecc_out)[0]; + } else { + vid_ecc = vid_ecc_in; + ecc_u = VECTOR(ecc_in)[0]; + } + + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + vid_end = vid_ecc; + + /* TODO: In the undirected case, we break ties between vertices at the + * same distance based on their degree. In te directed case, should we + * use in-, out- or total degree? */ + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_out, igraph_vss_1(vid_ecc), + &adjlist_out, &vid_ecc_out, 1)); + IGRAPH_CHECK(igraph_i_eccentricity(graph, &ecc_in, igraph_vss_1(vid_ecc), + &adjlist_in, &vid_ecc_in, 1)); + + if (VECTOR(ecc_out)[0] > VECTOR(ecc_in)[0]) { + vid_ecc = vid_ecc_out; + ecc_v = VECTOR(ecc_out)[0]; + direction = 1; + } else { + vid_ecc = vid_ecc_in; + ecc_v = VECTOR(ecc_in)[0]; + direction = 0; + } + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + vid_start = vid_end; + } else { + break; + } + } + + if (direction) { + ifrom = vid_end; + ito = vid_start; + } else { + ifrom = vid_start; + ito = vid_end; + } + + } + igraph_vector_destroy(&ecc_out); + igraph_vector_destroy(&ecc_in); + igraph_lazy_adjlist_destroy(&adjlist_in); + igraph_lazy_adjlist_destroy(&adjlist_out); + IGRAPH_FINALLY_CLEAN(4); + } + + if (inf) { + if (diameter) { + *diameter = IGRAPH_INFINITY; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + } else { + if (diameter) { + *diameter = ecc_u; + } + if (from) { + *from = ifrom; + } + if (to) { + *to = ito; + } + } + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_pseudo_diameter_dijkstra + * \brief Approximation and lower bound of the diameter of a weighted graph. + * + * This algorithm finds a pseudo-peripheral vertex and returns its + * weighted eccentricity. This value can be used as an approximation + * and lower bound of the diameter of a graph. + * + * + * A pseudo-peripheral vertex is a vertex v, such that for every + * vertex u which is as far away from v as possible, v is also as + * far away from u as possible. The process of finding one depends + * on where the search starts, and for a disconnected graph the + * maximum diameter found will be that of the component \p vid_start + * is in. + * + * + * If the graph has no vertices, \c IGRAPH_NAN is returned. + * + * \param graph The input graph, can be directed or undirected. + * \param weights The edge weights of the graph. Can be \c NULL for an + * unweighted graph. All weights should be non-negative. Edges with + * positive infinite weights are ignored. + * \param diameter This will contain the weighted pseudo-diameter. + * \param vid_start Id of the starting vertex. If this is negative, a + * random starting vertex is chosen. + * \param from If not \c NULL this 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 to If not \c NULL this 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 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. + * + * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices, + * |E| is the number of edges. + * + * \sa \ref igraph_diameter_dijkstra() + */ +igraph_error_t igraph_pseudo_diameter_dijkstra(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_real_t *diameter, + igraph_integer_t vid_start, + igraph_integer_t *from, + igraph_integer_t *to, + igraph_bool_t directed, + igraph_bool_t unconn) { + + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t ecc_v; + igraph_real_t ecc_u; + igraph_integer_t vid_ecc; + igraph_integer_t ito, ifrom; + igraph_bool_t inf = false; + + if (vid_start >= no_of_nodes) { + IGRAPH_ERROR("Starting vertex ID for pseudo-diameter out of range.", IGRAPH_EINVAL); + } + + if (!weights) { + return igraph_pseudo_diameter(graph, diameter, vid_start, from, to, directed, unconn); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + /* We will reach here when vid_start < 0 and the graph has no vertices. */ + if (no_of_nodes == 0) { + if (diameter) { + *diameter = IGRAPH_NAN; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + return IGRAPH_SUCCESS; + } + + if (vid_start < 0) { + RNG_BEGIN(); + vid_start = RNG_INTEGER(0, no_of_nodes - 1); + RNG_END(); + } + + if (!igraph_is_directed(graph) || !directed) { + igraph_lazy_inclist_t inclist; + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); + + ifrom = vid_start; + + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_u, vid_start, &vid_ecc, unconn, &inclist)); + + inf = !isfinite(ecc_u); + + if (!inf) { + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + ito = vid_ecc; + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_v, vid_ecc, &vid_ecc, unconn, &inclist)); + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + ifrom = ito; + } else { + break; + } + } + } + igraph_lazy_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(1); + } else { + igraph_real_t ecc_out; + igraph_real_t ecc_in; + igraph_integer_t vid_ecc_in; + igraph_integer_t vid_ecc_out; + igraph_integer_t vid_end; + igraph_bool_t direction; + igraph_lazy_inclist_t inclist_out; + igraph_lazy_inclist_t inclist_in; + + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_out, IGRAPH_OUT, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_out); + IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &inclist_in, IGRAPH_IN, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist_in); + + + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_start, &vid_ecc_out, unconn, &inclist_out)); + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_start, &vid_ecc_in, unconn, &inclist_in)); + + /* A directed graph is strongly connected iff all vertices are reachable + * from vid_start both when moving along or moving opposite the edge directions. */ + if (!unconn && (vid_ecc_out == -1 || vid_ecc_in == -1)) { + inf = true; + } else { + if (ecc_out > ecc_in) { + vid_ecc = vid_ecc_out; + ecc_u = ecc_out; + } else { + vid_ecc = vid_ecc_in; + ecc_u = ecc_in; + } + + while (1) { + IGRAPH_ALLOW_INTERRUPTION(); + + vid_end = vid_ecc; + + /* TODO: In the undirected case, we break ties between vertices at the + * same distance based on their degree. In te directed case, should we + * use in-, out- or total degree? */ + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_out, vid_ecc, &vid_ecc_out, unconn, &inclist_out)); + IGRAPH_CHECK(igraph_i_eccentricity_dijkstra(graph, weights, &ecc_in, vid_ecc, &vid_ecc_in, unconn, &inclist_in)); + + if (ecc_out > ecc_in) { + vid_ecc = vid_ecc_out; + ecc_v = ecc_out; + direction = 1; + } else { + vid_ecc = vid_ecc_in; + ecc_v = ecc_in; + direction = 0; + } + + if (ecc_u < ecc_v) { + ecc_u = ecc_v; + vid_start = vid_end; + } else { + break; + } + } + + if (direction) { + ifrom = vid_end; + ito = vid_start; + } else { + ifrom = vid_start; + ito = vid_end; + } + } + igraph_lazy_inclist_destroy(&inclist_out); + igraph_lazy_inclist_destroy(&inclist_in); IGRAPH_FINALLY_CLEAN(2); } - return 0; + if (inf) { + if (diameter) { + *diameter = IGRAPH_INFINITY; + } + if (from) { + *from = -1; + } + if (to) { + *to = -1; + } + } else { + if (diameter) { + *diameter = ecc_u; + } + if (from) { + *from = ifrom; + } + if (to) { + *to = ito; + } + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_graph_center + * \brief Central vertices of a graph. + * + * The central vertices of a graph are calculated by finding the vertices + * with the minimum eccentricity. This concept is typically applied to + * connected graphs. In undirected disconnected graphs, the calculation + * is effectively done per connected component. + * + * \param graph The input graph, it can be directed or undirected. + * \param res Pointer to an initialized vector, 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(). + * + */ +igraph_error_t igraph_graph_center( + const igraph_t *graph, igraph_vector_int_t *res, igraph_neimode_t mode +) { + + igraph_vector_t ecc; + + igraph_vector_int_clear(res); + if (igraph_vcount(graph) == 0) { + return IGRAPH_SUCCESS; + } + + IGRAPH_VECTOR_INIT_FINALLY(&ecc, 0); + IGRAPH_CHECK(igraph_eccentricity(graph, &ecc, igraph_vss_all(), mode)); + + /* igraph_eccentricity() does not return infinity or NaN, and the null graph + * case was handled above, therefore calling vector_min() is safe. */ + igraph_real_t min_eccentricity = igraph_vector_min(&ecc); + igraph_real_t n = igraph_vector_size(&ecc); + for (igraph_integer_t i = 0; i < n; i++) { + if (VECTOR(ecc)[i] == min_eccentricity) { + IGRAPH_CHECK(igraph_vector_int_push_back(res, i)); + } + } + + igraph_vector_destroy(&ecc); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/eulerian.c b/src/vendor/cigraph/src/paths/eulerian.c index f1d15e0f015..54979962cbf 100644 --- a/src/vendor/cigraph/src/paths/eulerian.c +++ b/src/vendor/cigraph/src/paths/eulerian.c @@ -40,42 +40,44 @@ 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) { +static igraph_error_t 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; + igraph_vector_int_t degree; + igraph_vector_int_t csize; /* boolean vector to mark singletons: */ - igraph_vector_t nonsingleton; - long int i, n, vsize; - long int cluster_count; + igraph_vector_int_t nonsingleton; + igraph_integer_t i, n, vsize; + igraph_integer_t cluster_count; /* number of self-looping singletons: */ - long int es; + igraph_integer_t es; /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ - long int ens; + igraph_integer_t 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; + *has_path = true; + *has_cycle = true; 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)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&csize, 0); + IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); cluster_count = 0; - vsize = igraph_vector_size(&csize); + vsize = igraph_vector_int_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); + *has_path = false; + *has_cycle = false; + igraph_vector_int_destroy(&csize); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -83,12 +85,12 @@ static int igraph_i_is_eulerian_undirected(const igraph_t *graph, igraph_bool_t } } - igraph_vector_destroy(&csize); + igraph_vector_int_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_VECTOR_INT_INIT_FINALLY(&nonsingleton, 0); IGRAPH_CHECK(igraph_degree(graph, &nonsingleton, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS)); /* check the degrees for odd/even: @@ -96,13 +98,13 @@ static int igraph_i_is_eulerian_undirected(const igraph_t *graph, igraph_bool_t * - > 2 odd means no path * plus there are a few corner cases with singletons */ - IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_VECTOR_INT_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]; + igraph_integer_t deg = VECTOR(degree)[i]; /* Eulerian is about edges, so skip free vertices */ if (deg == 0) continue; @@ -119,66 +121,68 @@ static int igraph_i_is_eulerian_undirected(const igraph_t *graph, igraph_bool_t 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); + *has_path = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } } - igraph_vector_destroy(&nonsingleton); + igraph_vector_int_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; + *has_path = false; + *has_cycle = false; } else if (odd == 2) { - *has_path = 1; - *has_cycle = 0; + *has_path = true; + *has_cycle = false; } else { - *has_path = 1; - *has_cycle = 1; + *has_path = true; + *has_cycle = true; } /* 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)) { + if ((*has_cycle && VECTOR(degree)[i] > 0) || (!*has_cycle && VECTOR(degree)[i] %2 == 1)) { *start_of_path = i; break; } } - igraph_vector_destroy(°ree); + igraph_vector_int_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) { +static igraph_error_t 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; + igraph_integer_t i, vsize; + igraph_integer_t cluster_count; + igraph_vector_int_t out_degree, in_degree; + igraph_vector_int_t csize; /* boolean vector to mark singletons: */ - igraph_vector_t nonsingleton; + igraph_vector_int_t nonsingleton; /* number of self-looping singletons: */ - long int es; + igraph_integer_t es; /* will be set to 1 if there are non-isolated vertices, otherwise 0: */ - long int ens; + igraph_integer_t 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; + *has_path = true; + *has_cycle = true; return IGRAPH_SUCCESS; } @@ -188,19 +192,19 @@ static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *h /* 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_VECTOR_INT_INIT_FINALLY(&csize, 0); - IGRAPH_CHECK(igraph_clusters(graph, NULL, &csize, NULL, IGRAPH_WEAK)); + IGRAPH_CHECK(igraph_connected_components(graph, NULL, &csize, NULL, IGRAPH_WEAK)); cluster_count = 0; - vsize = igraph_vector_size(&csize); + vsize = igraph_vector_int_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); + *has_path = false; + *has_cycle = false; + igraph_vector_int_destroy(&csize); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -208,28 +212,28 @@ static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *h } } - igraph_vector_destroy(&csize); + igraph_vector_int_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_VECTOR_INT_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_VECTOR_INT_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_VECTOR_INT_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]; + igraph_integer_t degin = VECTOR(in_degree)[i]; + igraph_integer_t degout = VECTOR(out_degree)[i]; /* Eulerian is about edges, so skip free vertices */ if (degin + degout == 0) continue; @@ -247,11 +251,11 @@ static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *h 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); + *has_path = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_destroy(&out_degree); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -282,25 +286,25 @@ static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *h * 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); + *has_path = false; + *has_cycle = false; + igraph_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_destroy(&out_degree); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } } - *has_path = 1; + *has_path = true; /* 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_vector_int_destroy(&nonsingleton); + igraph_vector_int_destroy(&in_degree); + igraph_vector_int_destroy(&out_degree); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -310,23 +314,24 @@ static int igraph_i_is_eulerian_directed(const igraph_t *graph, igraph_bool_t *h /** * \ingroup Eulerian * \function igraph_is_eulerian - * \brief Checks whether an Eulerian path or cycle exists + * \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. + * Must not be \c NULL. * \param has_cycle Pointer to a Boolean, will be set to true if an Eulerian cycle exists. + * Must not be \c NULL. * \return Error code: - * \c IGRAPH_ENOMEM, not enough memory for - * temporary data. + * \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_error_t 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)) { @@ -338,67 +343,62 @@ int igraph_is_eulerian(const igraph_t *graph, igraph_bool_t *has_path, igraph_bo } -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; +static igraph_error_t igraph_i_eulerian_path_undirected( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, + igraph_integer_t start_of_path) { + + igraph_integer_t curr; igraph_integer_t n, m; igraph_inclist_t il; - igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_stack_int_t path, tracker, edge_tracker, edge_path; igraph_vector_bool_t visited_list; - igraph_vector_t degree; + igraph_vector_int_t degree; n = igraph_vcount(graph); m = igraph_ecount(graph); if (edge_res) { - igraph_vector_clear(edge_res); + igraph_vector_int_clear(edge_res); } if (vertex_res) { - igraph_vector_clear(vertex_res); + igraph_vector_int_clear(vertex_res); } if (m == 0 || n == 0) { return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(°ree, 0); + IGRAPH_VECTOR_INT_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_STACK_INT_INIT_FINALLY(&path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); - IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + IGRAPH_CHECK(igraph_stack_int_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)) { + while (!igraph_stack_int_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)); + igraph_integer_t nc, edge = -1; + igraph_integer_t j, next; + IGRAPH_CHECK(igraph_stack_int_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]; + edge = VECTOR(*incedges)[j]; if (!VECTOR(visited_list)[edge]) { break; } @@ -406,7 +406,7 @@ static int igraph_i_eulerian_path_undirected(const igraph_t *graph, igraph_vecto next = IGRAPH_OTHER(graph, edge, curr); - IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); /* remove edge here */ VECTOR(degree)[curr]--; @@ -416,102 +416,97 @@ static int igraph_i_eulerian_path_undirected(const igraph_t *graph, igraph_vecto 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)); + IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); + curr = igraph_stack_int_pop(&tracker); + if (!igraph_stack_int_empty(&edge_tracker)) { + curr_e = igraph_stack_int_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_int_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))); + IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); + while (!igraph_stack_int_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_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_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); + while (!igraph_stack_int_empty(&path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); } } - igraph_stack_destroy(&path); - igraph_stack_destroy(&tracker); - igraph_stack_destroy(&edge_path); - igraph_stack_destroy(&edge_tracker); + igraph_stack_int_destroy(&path); + igraph_stack_int_destroy(&tracker); + igraph_stack_int_destroy(&edge_path); + igraph_stack_int_destroy(&edge_tracker); igraph_vector_bool_destroy(&visited_list); igraph_inclist_destroy(&il); - igraph_vector_destroy(°ree); + igraph_vector_int_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; +static igraph_error_t igraph_i_eulerian_path_directed( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res, + igraph_integer_t start_of_path) { + + igraph_integer_t curr; igraph_integer_t n, m; igraph_inclist_t il; - igraph_stack_t path, tracker, edge_tracker, edge_path; + igraph_stack_int_t path, tracker, edge_tracker, edge_path; igraph_vector_bool_t visited_list; - igraph_vector_t remaining_out_edges; + igraph_vector_int_t remaining_out_edges; n = igraph_vcount(graph); m = igraph_ecount(graph); if (edge_res) { - igraph_vector_clear(edge_res); + igraph_vector_int_clear(edge_res); } if (vertex_res) { - igraph_vector_clear(vertex_res); + igraph_vector_int_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_STACK_INT_INIT_FINALLY(&path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&tracker, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_path, n); + IGRAPH_STACK_INT_INIT_FINALLY(&edge_tracker, n); IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited_list, m); - IGRAPH_CHECK(igraph_stack_push(&tracker, start_of_path)); + IGRAPH_CHECK(igraph_stack_int_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_VECTOR_INT_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)) { + while (!igraph_stack_int_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)); + igraph_integer_t nc, edge = -1; + igraph_integer_t j, next; + IGRAPH_CHECK(igraph_stack_int_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]; + edge = VECTOR(*incedges)[j]; if (!VECTOR(visited_list)[edge]) { break; } @@ -519,7 +514,7 @@ static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_ next = IGRAPH_TO(graph, edge); - IGRAPH_CHECK(igraph_stack_push(&edge_tracker, edge)); + IGRAPH_CHECK(igraph_stack_int_push(&edge_tracker, edge)); /* remove edge here */ VECTOR(remaining_out_edges)[curr]--; @@ -528,35 +523,35 @@ static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_ 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)); + IGRAPH_CHECK(igraph_stack_int_push(&path, curr)); + curr = igraph_stack_int_pop(&tracker); + if (!igraph_stack_int_empty(&edge_tracker)) { + curr_e = igraph_stack_int_pop(&edge_tracker); + IGRAPH_CHECK(igraph_stack_int_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))); + IGRAPH_CHECK(igraph_vector_int_reserve(edge_res, m)); + while (!igraph_stack_int_empty(&edge_path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(edge_res, igraph_stack_int_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_CHECK(igraph_vector_int_reserve(vertex_res, m+1)); + while (!igraph_stack_int_empty(&path)) { + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_res, igraph_stack_int_pop(&path))); } } - igraph_stack_destroy(&path); - igraph_stack_destroy(&tracker); - igraph_stack_destroy(&edge_path); - igraph_stack_destroy(&edge_tracker); + igraph_stack_int_destroy(&path); + igraph_stack_int_destroy(&tracker); + igraph_stack_int_destroy(&edge_path); + igraph_stack_int_destroy(&edge_tracker); igraph_vector_bool_destroy(&visited_list); igraph_inclist_destroy(&il); - igraph_vector_destroy(&remaining_out_edges); + igraph_vector_int_destroy(&remaining_out_edges); IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; @@ -566,12 +561,15 @@ static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_ /** * \ingroup Eulerian * \function igraph_eulerian_cycle - * \brief Finds an 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. * * + * If the graph has no edges, a zero-length cycle is returned. + * + * * This function uses Hierholzer's algorithm. * * \param graph The graph object. @@ -585,7 +583,7 @@ static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_ * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. - * \cli IGRAPH_EINVVID + * \cli IGRAPH_ENOSOL * graph does not have an Eulerian cycle. * \endclist * @@ -593,7 +591,9 @@ static int igraph_i_eulerian_path_directed(const igraph_t *graph, igraph_vector_ * */ -int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { +igraph_error_t igraph_eulerian_cycle( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { + igraph_bool_t has_cycle; igraph_bool_t has_path; igraph_integer_t start_of_path = 0; @@ -602,7 +602,7 @@ int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igra 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_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_ENOSOL); } IGRAPH_CHECK(igraph_i_eulerian_path_directed(graph, edge_res, vertex_res, start_of_path)); @@ -610,7 +610,7 @@ int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igra 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_ERROR("The graph does not have an Eulerian cycle.", IGRAPH_ENOSOL); } IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); @@ -623,12 +623,15 @@ int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igra /** * \ingroup Eulerian * \function igraph_eulerian_path - * \brief Finds an Eulerian path + * \brief Finds an Eulerian path. * * Finds an Eulerian path, if it exists. An Eulerian path traverses * each edge precisely once. * * + * If the graph has no edges, a zero-length path is returned. + * + * * This function uses Hierholzer's algorithm. * * \param graph The graph object. @@ -642,7 +645,7 @@ int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igra * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary data. - * \cli IGRAPH_EINVVID + * \cli IGRAPH_ENOSOL * graph does not have an Eulerian path. * \endclist * @@ -650,7 +653,9 @@ int igraph_eulerian_cycle(const igraph_t *graph, igraph_vector_t *edge_res, igra * */ -int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igraph_vector_t *vertex_res) { +igraph_error_t igraph_eulerian_path( + const igraph_t *graph, igraph_vector_int_t *edge_res, igraph_vector_int_t *vertex_res) { + igraph_bool_t has_cycle; igraph_bool_t has_path; igraph_integer_t start_of_path = 0; @@ -659,14 +664,14 @@ int igraph_eulerian_path(const igraph_t *graph, igraph_vector_t *edge_res, igrap 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_ERROR("The graph does not have an Eulerian path.", IGRAPH_ENOSOL); } 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_ERROR("The graph does not have an Eulerian path.", IGRAPH_ENOSOL); } IGRAPH_CHECK(igraph_i_eulerian_path_undirected(graph, edge_res, vertex_res, start_of_path)); diff --git a/src/vendor/cigraph/src/paths/floyd_warshall.c b/src/vendor/cigraph/src/paths/floyd_warshall.c new file mode 100644 index 00000000000..14be5979a5b --- /dev/null +++ b/src/vendor/cigraph/src/paths/floyd_warshall.c @@ -0,0 +1,367 @@ +/* + IGraph library. + Copyright (C) 2022-2023 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_interface.h" +#include "igraph_stack.h" + +#include "core/interruption.h" +#include "internal/utils.h" + +static igraph_error_t igraph_distances_floyd_warshall_original( + const igraph_t *graph, igraph_matrix_t *res, + const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Iteration order matters for performance! + * First j, then i, because matrices are stored as column-major. */ + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + igraph_real_t dkj = MATRIX(*res, k, j); + if (dkj == IGRAPH_INFINITY) { + continue; + } + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t di = MATRIX(*res, i, k) + dkj; + igraph_real_t dd = MATRIX(*res, i, j); + if (di < dd) { + MATRIX(*res, i, j) = di; + } + if (i == j && MATRIX(*res, i, i) < 0) { + IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP); + } + } + } + } + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_distances_floyd_warshall_tree( + const igraph_t *graph, igraph_matrix_t *res, + const igraph_vector_t *weights) { + + /* This is the "Tree" algorithm of Brodnik et al. + * A difference from the paper is that instead of using the OUT_k tree of shortest + * paths _starting_ in k, we use the IN_k tree of shortest paths _ending_ in k. + * This makes it easier to iterate through matrices in column-major order, + * i.e. storage order, thus increasing performance. */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + + /* successors[v][u] is the second vertex on the shortest path from v to u, + i.e. the parent of v in the IN_u tree. */ + igraph_matrix_int_t successors; + IGRAPH_MATRIX_INT_INIT_FINALLY(&successors, no_of_nodes, no_of_nodes); + + /* children[children_start[u] + i] is the i-th child of u in a tree of shortest paths + rooted at k, and ending in k, in the main loop below (IN_k). There are no_of_nodes-1 + child vertices in total, as the root vertex is excluded. This is essentially a contiguously + stored adjacency list representation of IN_k. */ + igraph_vector_int_t children; + IGRAPH_VECTOR_INT_INIT_FINALLY(&children, no_of_nodes-1); + + /* children_start[u] indicates where the children of u are stored in children[]. + These are effectively the cumulative sums of no_of_children[], with the first + element being 0. The last element, children_start[no_of_nodes], is equal to the + total number of children in the tree, i.e. no_of_nodes-1. */ + igraph_vector_int_t children_start; + IGRAPH_VECTOR_INT_INIT_FINALLY(&children_start, no_of_nodes+1); + + /* no_of_children[u] is the number of children that u has in IN_k in the main loop below. */ + igraph_vector_int_t no_of_children; + IGRAPH_VECTOR_INT_INIT_FINALLY(&no_of_children, no_of_nodes); + + /* dfs_traversal and dfs_skip arrays for running time optimization, + see "Practical improvement" in Section 3.1 of the paper */ + igraph_vector_int_t dfs_traversal; + IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_traversal, no_of_nodes); + igraph_vector_int_t dfs_skip; + IGRAPH_VECTOR_INT_INIT_FINALLY(&dfs_skip, no_of_nodes); + + igraph_stack_int_t stack; + IGRAPH_STACK_INT_INIT_FINALLY(&stack, no_of_nodes); + + for (igraph_integer_t u = 0; u < no_of_nodes; u++) { + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + MATRIX(successors, v, u) = u; + } + } + + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + IGRAPH_ALLOW_INTERRUPTION(); + + /* Count the children of each node in the shortest path tree, assuming that at + this point all elements of no_of_children[] are zeros. */ + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + if (v == k) continue; + igraph_integer_t parent = MATRIX(successors, v, k); + VECTOR(no_of_children)[parent]++; + } + + /* Note: we do not use igraph_vector_int_cumsum() here as that function produces + an output vector of the same length as the input vector. Here we need an output + one longer, with a 0 being prepended to what vector_cumsum() would produce. */ + igraph_integer_t cumsum = 0; + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + VECTOR(children_start)[v] = cumsum; + cumsum += VECTOR(no_of_children)[v]; + } + VECTOR(children_start)[no_of_nodes] = cumsum; + + /* Constructing the tree IN_k (as in the paper) and representing it + as a contiguously stored adjacency list. The entries of the no_of_children + vector as re-used as an index of where to insert child node indices. + At the end of the calculation, all elements of no_of_children[] will be zeros, + making this vector ready for the next iteration of the outer loop. */ + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + if (v == k) continue; + igraph_integer_t parent = MATRIX(successors, v, k); + VECTOR(no_of_children)[parent]--; + VECTOR(children)[ VECTOR(children_start)[parent] + VECTOR(no_of_children)[parent] ] = v; + } + + /* constructing dfs-traversal and dfs-skip arrays for the IN_k tree */ + IGRAPH_CHECK(igraph_stack_int_push(&stack, k)); + igraph_integer_t counter = 0; + while (!igraph_stack_int_empty(&stack)) { + igraph_integer_t parent = igraph_stack_int_pop(&stack); + if (parent >= 0) { + VECTOR(dfs_traversal)[counter] = parent; + counter++; + /* a negative marker -parent - 1 that is popped right after + all the descendants of the parent were processed */ + IGRAPH_CHECK(igraph_stack_int_push(&stack, -parent - 1)); + for (igraph_integer_t l = VECTOR(children_start)[parent]; l < VECTOR(children_start)[parent + 1]; l++) { + IGRAPH_CHECK(igraph_stack_int_push(&stack, VECTOR(children)[l])); + } + } else { + VECTOR(dfs_skip)[-(parent + 1)] = counter; + } + } + + /* main inner loop */ + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + igraph_real_t dki = MATRIX(*res, k, i); + if (dki == IGRAPH_INFINITY || i == k) { + continue; + } + igraph_integer_t counter = 1; + while (counter < no_of_nodes) { + igraph_integer_t j = VECTOR(dfs_traversal)[counter]; + igraph_real_t di = MATRIX(*res, j, k) + dki; + igraph_real_t dd = MATRIX(*res, j, i); + if (di < dd) { + MATRIX(*res, j, i) = di; + MATRIX(successors, j, i) = MATRIX(successors, j, k); + counter++; + } else { + counter = VECTOR(dfs_skip)[j]; + } + if (i == j && MATRIX(*res, i, i) < 0) { + IGRAPH_ERROR("Negative cycle found while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP); + } + } + } + } + + igraph_stack_int_destroy(&stack); + igraph_vector_int_destroy(&dfs_traversal); + igraph_vector_int_destroy(&dfs_skip); + igraph_vector_int_destroy(&no_of_children); + igraph_vector_int_destroy(&children_start); + igraph_vector_int_destroy(&children); + igraph_matrix_int_destroy(&successors); + IGRAPH_FINALLY_CLEAN(7); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_distances_floyd_warshall + * \brief Weighted all-pairs shortest path lengths with the Floyd-Warshall algorithm. + * + * \experimental + * + * The Floyd-Warshall algorithm computes weighted shortest path lengths between + * all pairs of vertices at the same time. It is useful with very dense weighted graphs, + * as its running time is primarily determined by the vertex count, and is not sensitive + * to the graph density. In sparse graphs, other methods such as the Dijkstra or + * Bellman-Ford algorithms will perform significantly better. + * + * + * In addition to the original Floyd-Warshall algorithm, igraph contains implementations + * of variants that offer better asymptotic complexity as well as better practical + * running times for most instances. See the reference below for more information. + * + * + * Note that internally this function always computes the distance matrix + * for all pairs of vertices. The \p from and \p to parameters only serve + * to subset this matrix, but do not affect the time or memory taken by the + * calculation. + * + * + * Reference: + * + * + * Brodnik, A., Grgurovič, M., Požar, R.: + * Modifications of the Floyd-Warshall algorithm with nearly quadratic expected-time, + * Ars Mathematica Contemporanea, vol. 22, issue 1, p. #P1.01 (2021). + * https://doi.org/10.26493/1855-3974.2467.497 + * + * \param graph The graph object. + * \param res An intialized matrix, the distances will be stored here. + * \param from The source vertices. + * \param to The target vertices. + * \param weights The edge weights. If \c NULL, all weights are assumed to be 1. + * Negative weights are allowed, but the graph must not contain negative cycles. + * Edges with positive infinite weights are ignored. + * \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 method The type of the algorithm used. + * \clist + * \cli IGRAPH_FLOYD_WARSHALL_AUTOMATIC + * tried to select the best performing variant for the current graph; + * presently this option always uses the "Tree" method. + * \cli IGRAPH_FLOYD_WARSHALL_ORIGINAL + * the basic Floyd-Warshall algorithm. + * \cli IGRAPH_FLOYD_WARSHALL_TREE + * the "Tree" speedup of Brodnik et al., faster than the original algorithm + * in most cases. + * \endclist + * \return Error code. \c IGRAPH_ENEGLOOP is returned if a negative-weight + * cycle is found. + * + * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(), + * \ref igraph_distances_bellman_ford(), \ref igraph_distances_johnson() + * + * Time complexity: + * The original variant has complexity O(|V|^3 + |E|). + * The "Tree" variant has expected-case complexity of O(|V|^2 log^2 |V|) + * according to Brodnik et al., while its worst-time complexity remains O(|V|^3). + * Here |V| denotes the number of vertices and |E| is the number of edges. + */ +igraph_error_t igraph_distances_floyd_warshall( + const igraph_t *graph, igraph_matrix_t *res, + igraph_vs_t from, igraph_vs_t to, + const igraph_vector_t *weights, igraph_neimode_t mode, + const igraph_floyd_warshall_algorithm_t method) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t in = false, out = false; + + if (weights && igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + switch (mode) { + case IGRAPH_ALL: + in = out = true; + break; + case IGRAPH_OUT: + out = true; + break; + case IGRAPH_IN: + in = true; + break; + default: + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); + } + + if (weights && igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_fill(res, IGRAPH_INFINITY); + + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + MATRIX(*res, v, v) = 0; + } + + for (igraph_integer_t e = 0; e < no_of_edges; e++) { + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_real_t w = weights ? VECTOR(*weights)[e] : 1; + + if (w < 0) { + if (mode == IGRAPH_ALL) { + IGRAPH_ERRORF("Negative edge weight (%g) found in undirected graph " + "while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP, w); + } else if (to == from) { + IGRAPH_ERRORF("Self-loop with negative weight (%g) found " + "while calculating distances with Floyd-Warshall.", + IGRAPH_ENEGLOOP, w); + } + } else if (w == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + continue; + } + + if (out && MATRIX(*res, from, to) > w) { + MATRIX(*res, from, to) = w; + } + if (in && MATRIX(*res, to, from) > w) { + MATRIX(*res, to, from) = w; + } + } + + /* If there are zero or one vertices, nothing needs to be done. + * This is special-cased so that at later stages we can rely on no_of_nodes - 1 >= 0. */ + if (no_of_nodes <= 1) { + return IGRAPH_SUCCESS; + } + + switch (method) { + case IGRAPH_FLOYD_WARSHALL_ORIGINAL: + IGRAPH_CHECK(igraph_distances_floyd_warshall_original(graph, res, weights)); + break; + case IGRAPH_FLOYD_WARSHALL_AUTOMATIC: + case IGRAPH_FLOYD_WARSHALL_TREE: + IGRAPH_CHECK(igraph_distances_floyd_warshall_tree(graph, res, weights)); + break; + default: + IGRAPH_ERROR("Invalid method.", IGRAPH_EINVAL); + } + + IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/paths/histogram.c b/src/vendor/cigraph/src/paths/histogram.c index a84858d7fc5..1f5d9e9467e 100644 --- a/src/vendor/cigraph/src/paths/histogram.c +++ b/src/vendor/cigraph/src/paths/histogram.c @@ -53,23 +53,23 @@ * 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() + * \sa \ref igraph_average_path_length() and \ref igraph_distances() */ -int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, n; + igraph_vector_int_t already_added; + igraph_integer_t nodes_reached; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_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; + igraph_integer_t ressize; if (directed) { dirmode = IGRAPH_OUT; @@ -77,33 +77,33 @@ int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, 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_VECTOR_INT_INIT_FINALLY(&already_added, no_of_nodes); + IGRAPH_DQUEUE_INT_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)); + igraph_vector_clear(res); 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_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]; + igraph_integer_t neighbor = VECTOR(*neis)[j]; if (VECTOR(already_added)[neighbor] == i + 1) { continue; } @@ -117,10 +117,10 @@ int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, } VECTOR(*res)[actdist] += 1; - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_empty */ + } /* while !igraph_dqueue_int_empty */ unconn += (no_of_nodes - nodes_reached); @@ -136,8 +136,8 @@ int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, unconn /= 2; } - igraph_vector_long_destroy(&already_added); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&already_added); + igraph_dqueue_int_destroy(&q); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(3); @@ -145,5 +145,5 @@ int igraph_path_length_hist(const igraph_t *graph, igraph_vector_t *res, *unconnected = unconn; } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/johnson.c b/src/vendor/cigraph/src/paths/johnson.c index 90eebdcf910..fdd09ffc8a0 100644 --- a/src/vendor/cigraph/src/paths/johnson.c +++ b/src/vendor/cigraph/src/paths/johnson.c @@ -26,23 +26,35 @@ #include "igraph_conversion.h" #include "igraph_interface.h" +#include "math/safe_intop.h" /** - * \function igraph_shortest_paths_johnson + * \function igraph_distances_johnson * \brief Weighted shortest path lengths between vertices, using Johnson's algorithm. * - * See Wikipedia at http://en.wikipedia.org/wiki/Johnson's_algorithm - * for Johnson's algorithm. This algorithm works even if the graph - * contains negative edge weights, and it is worth using it if we - * calculate the shortest paths from many sources. + * This algorithm supports directed graphs with negative edge weights, and performs + * better than the Bellman-Ford method when distances are calculated from many different + * sources, the typical use case being all-pairs distance calculations. It works by using + * a single-source Bellman-Ford run to transform all edge weights to non-negative ones, + * then invoking Dijkstra's algorithm with the new weights. See the Wikipedia page + * for more details: http://en.wikipedia.org/wiki/Johnson's_algorithm. + * + * + * If no edge weights are supplied, then the unweighted version, \ref igraph_distances() + * is called. If none of the supplied edge weights are negative, then Dijkstra's algorithm + * is used by calling \ref igraph_distances_dijkstra(). + * + * + * Note that Johnson's algorithm applies only to directed graphs. This function rejects + * undirected graphs with \em any negative edge weights, even when the \p from and \p to + * vertices are all in connected components that are free of negative weights. * * - * If no edge weights are supplied, then the unweighted - * version, \ref igraph_shortest_paths() is called. + * References: * * - * If all the supplied edge weights are non-negative, - * then Dijkstra's algorithm is used by calling - * \ref igraph_shortest_paths_dijkstra(). + * Donald B. Johnson: Efficient Algorithms for Shortest Paths in Sparse Networks. + * J. ACM 24, 1 (1977), 1–13. + * https://doi.org/10.1145/321992.321993 * * \param graph The input graph. If negative weights are present, it * should be directed. @@ -53,61 +65,64 @@ * \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. + * the unweighted breadth-first search based \ref igraph_distances() will + * be called. Edges with positive infinite weights are ignored. * \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 + * \sa \ref igraph_distances() for a faster unweighted version, + * \ref igraph_distances_dijkstra() if you do not have negative + * edge weights, \ref igraph_distances_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_error_t igraph_distances_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_t newgraph; - igraph_vector_t edges, newweights; + igraph_vector_int_t edges; + igraph_vector_t newweights; igraph_matrix_t bfres; - long int i, ptr; - long int nr, nc; + igraph_integer_t i, ptr; + igraph_integer_t nr, nc; igraph_vit_t fromvit; + igraph_integer_t no_edges_reserved; /* If no weights, then we can just run the unweighted version */ if (!weights) { - return igraph_shortest_paths(graph, res, from, to, IGRAPH_OUT); + return igraph_distances(graph, res, from, to, IGRAPH_OUT); } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Weight vector length does not match", IGRAPH_EINVAL); + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); } /* 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); + return igraph_distances(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 (isnan(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); + return igraph_distances_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_ERROR("Johnson's shortest path: undirected graph and negative weight.", IGRAPH_EINVAL); } @@ -117,34 +132,36 @@ int igraph_shortest_paths_johnson(const igraph_t *graph, 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_CHECK(igraph_empty(&newgraph, no_of_nodes + 1, igraph_is_directed(graph))); IGRAPH_FINALLY(igraph_destroy, &newgraph); + IGRAPH_SAFE_MULT(no_of_nodes, 2, &no_edges_reserved); + IGRAPH_SAFE_ADD(no_edges_reserved, no_of_edges * 2, &no_edges_reserved); + /* 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges_reserved); + igraph_get_edgelist(graph, &edges, /*bycol=*/ 0); /* reserved */ + igraph_vector_int_resize(&edges, no_edges_reserved); /* reserved */ 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_vector_int_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); + igraph_vector_update(&newweights, weights); /* reserved */ + igraph_vector_resize(&newweights, no_of_edges + no_of_nodes); /* reserved */ 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 + /* Run Bellman-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_CHECK(igraph_distances_bellman_ford(&newgraph, &bfres, + igraph_vss_1(no_of_nodes), igraph_vss_all(), &newweights, IGRAPH_OUT)); igraph_destroy(&newgraph); @@ -154,10 +171,10 @@ int igraph_shortest_paths_johnson(const igraph_t *graph, 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); + igraph_vector_resize(&newweights, no_of_edges); /* reserved */ for (i = 0; i < no_of_edges; i++) { - long int ffrom = IGRAPH_FROM(graph, i); - long int tto = IGRAPH_TO(graph, i); + igraph_integer_t ffrom = IGRAPH_FROM(graph, i); + igraph_integer_t tto = IGRAPH_TO(graph, i); VECTOR(newweights)[i] += MATRIX(bfres, 0, ffrom) - MATRIX(bfres, 0, tto); /* If a weight becomes slightly negative due to roundoff errors, @@ -166,7 +183,7 @@ int igraph_shortest_paths_johnson(const igraph_t *graph, } /* Run Dijkstra's algorithm on the new weights */ - IGRAPH_CHECK(igraph_shortest_paths_dijkstra(graph, res, from, + IGRAPH_CHECK(igraph_distances_dijkstra(graph, res, from, to, &newweights, IGRAPH_OUT)); @@ -181,20 +198,20 @@ int igraph_shortest_paths_johnson(const igraph_t *graph, IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); for (i = 0; i < nr; i++, IGRAPH_VIT_NEXT(fromvit)) { - long int v1 = IGRAPH_VIT_GET(fromvit); + igraph_integer_t v1 = IGRAPH_VIT_GET(fromvit); if (igraph_vs_is_all(&to)) { - long int v2; + igraph_integer_t 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_integer_t 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_integer_t v2 = IGRAPH_VIT_GET(tovit); igraph_real_t sub = MATRIX(bfres, 0, v1) - MATRIX(bfres, 0, v2); MATRIX(*res, i, j) -= sub; } @@ -209,3 +226,17 @@ int igraph_shortest_paths_johnson(const igraph_t *graph, return IGRAPH_SUCCESS; } + +/** + * \function igraph_shortest_paths_johnson + * \brief Weighted shortest path lengths between vertices, using Johnson's algorithm (deprecated). + * + * \deprecated-by igraph_distances_johnson 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances_johnson(graph, res, from, to, weights); +} diff --git a/src/vendor/cigraph/src/paths/random_walk.c b/src/vendor/cigraph/src/paths/random_walk.c index a629d015920..60556b4e170 100644 --- a/src/vendor/cigraph/src/paths/random_walk.c +++ b/src/vendor/cigraph/src/paths/random_walk.c @@ -27,84 +27,60 @@ #include "igraph_interface.h" #include "igraph_random.h" #include "igraph_memory.h" +#include "igraph_vector_ptr.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. + * This function performs a random walk with a given length on a graph, + * from the given start vertex. + * It's used for igraph_random_walk when the given graph is unweighted, + * and only vertex IDs of the vertices on the walk are needed (edge IDs are not needed). + * \param vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the starting vertex id as well. */ - - -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); +static igraph_error_t igraph_i_random_walk_adjlist(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_integer_t start, + igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { igraph_integer_t i; + igraph_lazy_adjlist_t adj; - if (start < 0 || start >= vc) { - IGRAPH_ERROR("Invalid start vertex.", IGRAPH_EINVAL); - } - if (steps < 0) { - IGRAPH_ERROR("Invalid number of steps.", IGRAPH_EINVAL); + if (vertices == NULL) { + /* Nothing to do */ + return IGRAPH_SUCCESS; } 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)); + IGRAPH_CHECK(igraph_vector_int_resize(vertices, steps + 1)); RNG_BEGIN(); - VECTOR(*walk)[0] = start; - for (i = 1; i < steps; i++) { + VECTOR(*vertices)[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); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + nn = igraph_vector_int_size(neis); if (IGRAPH_UNLIKELY(nn == 0)) { - igraph_vector_resize(walk, i); + igraph_vector_int_resize(vertices, i); /* shrinks */ 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) ]; + start = VECTOR(*vertices)[i] = VECTOR(*neis)[RNG_INTEGER(0, nn - 1)]; + + IGRAPH_ALLOW_INTERRUPTION(); } RNG_END(); @@ -116,7 +92,7 @@ int igraph_random_walk(const igraph_t *graph, igraph_vector_t *walk, } -/* Used as item destructor for 'cdfs' in igraph_random_edge_walk(). */ +/* Used as item destructor for 'cdfs' in igraph_i_random_walk_inclist(). */ static void vec_destr(igraph_vector_t *vec) { if (vec != NULL) { igraph_vector_destroy(vec); @@ -125,91 +101,53 @@ static void vec_destr(igraph_vector_t *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. + * This function performs a random walk with a given length on a graph, + * from the given start vertex. + * It's used for igraph_random_walk: + * - when weights are used or when edge IDs of the traversed edges + * and/or vertex IDs of the visited vertices are requested. * \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 + * \param vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the starting vertex id as well. + * \param edges 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) { +static igraph_error_t igraph_i_random_walk_inclist( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + 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_integer_t i, next; igraph_vector_t weight_temp; + igraph_lazy_inclist_t il; 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 (vertices) { + IGRAPH_CHECK(igraph_vector_int_resize(vertices, steps + 1)); /* size: steps + 1 because vertices includes start vertex */ } - - if (start < 0 || start >= vc) { - IGRAPH_ERROR("Invalid start vertex.", IGRAPH_EINVAL); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_resize(edges, steps)); } - 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_CHECK(igraph_lazy_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &il); IGRAPH_VECTOR_INIT_FINALLY(&weight_temp, 0); - /* cdf vectors will be computed lazily */ + /* cdf vectors will be computed lazily; that's why we are still using + * igraph_vector_ptr_t as it does not require us to pre-initialize all + * the vectors in the vector list */ 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); @@ -219,15 +157,25 @@ int igraph_random_edge_walk(const igraph_t *graph, RNG_BEGIN(); + if (vertices) { + VECTOR(*vertices)[0] = start; + } for (i = 0; i < steps; ++i) { - long degree, edge, idx; - igraph_vector_int_t *edges = igraph_inclist_get(&il, start); + igraph_integer_t degree, edge, idx; + igraph_vector_int_t *inc_edges = igraph_lazy_inclist_get(&il, start); - degree = igraph_vector_int_size(edges); + IGRAPH_CHECK_OOM(inc_edges, "Failed to query incident edges."); + degree = igraph_vector_int_size(inc_edges); /* are we stuck? */ if (IGRAPH_UNLIKELY(degree == 0)) { - igraph_vector_resize(edgewalk, i); /* can't fail since size is reduced, skip IGRAPH_CHECK */ + /* can't fail since size is reduced, skip IGRAPH_CHECK */ + if (vertices) { + igraph_vector_int_resize(vertices, i + 1); /* size: i + 1 because vertices includes start vertex */ + } + if (edges) { + igraph_vector_int_resize(edges, i); + } if (stuck == IGRAPH_RANDOM_WALK_STUCK_RETURN) { break; } else { @@ -237,49 +185,55 @@ int igraph_random_edge_walk(const igraph_t *graph, 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]); + 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; + igraph_integer_t j; *cd = IGRAPH_CALLOC(1, igraph_vector_t); - if (*cd == NULL) { - IGRAPH_ERROR("Random edge walk failed.", IGRAPH_ENOMEM); - } + IGRAPH_CHECK_OOM(*cd, "Insufficient memory for random walk."); 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] ]; + VECTOR(weight_temp)[j] = VECTOR(*weights)[VECTOR(*inc_edges)[j]]; } IGRAPH_CHECK(igraph_vector_cumsum(*cd, &weight_temp)); } - r = RNG_UNIF(0, VECTOR( **cd )[degree - 1]); + r = RNG_UNIF(0, VECTOR(**cd)[degree - 1]); igraph_vector_binsearch(*cd, r, &idx); - } else { /* unweighted: choose an out-edge at random */ + } + else { idx = RNG_INTEGER(0, degree - 1); } - edge = VECTOR(*edges)[idx]; - VECTOR(*edgewalk)[i] = edge; + edge = VECTOR(*inc_edges)[idx]; + if (edges) { + VECTOR(*edges)[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); + next = IGRAPH_TO(graph, edge); break; case IGRAPH_IN: - start = IGRAPH_FROM(graph, edge); + next = IGRAPH_FROM(graph, edge); break; case IGRAPH_ALL: - start = IGRAPH_OTHER(graph, edge, start); + next = IGRAPH_OTHER(graph, edge, start); break; } + if (vertices) { + VECTOR(*vertices)[i + 1] = next; /* index i + 1 because vertices includes start vertex at position 0 */ + } + start = next; + IGRAPH_ALLOW_INTERRUPTION(); } @@ -287,8 +241,156 @@ int igraph_random_edge_walk(const igraph_t *graph, igraph_vector_ptr_destroy_all(&cdfs); igraph_vector_destroy(&weight_temp); - igraph_inclist_destroy(&il); + igraph_lazy_inclist_destroy(&il); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; } + + +/** + * \function igraph_random_walk + * \brief Performs 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 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 \c NULL, all edges are considered to have equal weight. + * \param vertices An allocated vector, the result is stored here as + * a list of vertex IDs. It will be resized as needed. + * It includes the vertex IDs of starting and ending vertices. + * Length of the vertices vector: \p steps + 1 + * \param edges An initialized vector, the indices of traversed + * edges are stored here. It will be resized as needed. + * Length of the edges vector: \p steps + * \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. + * \p steps is the number of edges to traverse during the walk. + * \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 \c IGRAPH_ERWSTUCK error is reported. + * In both cases, \p vertices and \p edges are truncated to contain + * the actual interrupted walk. + * \return Error code: \c IGRAPH_ERWSTUCK if the walk got stuck. + * + * Time complexity: + * O(l + d) for unweighted graphs and + * O(l * log(k) + d) for weighted graphs, + * where \c l is the length of the walk, \c d is the total degree of the visited nodes + * and \c k is the average degree of vertices of the given graph. + */ + + +igraph_error_t igraph_random_walk(const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + 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); + + if (!(mode == IGRAPH_ALL || mode == IGRAPH_IN || mode == IGRAPH_OUT)) { + IGRAPH_ERROR("Invalid mode parameter.", IGRAPH_EINVMODE); + } + + if (start < 0 || start >= vc) { + IGRAPH_ERRORF("Starting vertex must be between 0 and the " + "number of vertices in the graph (%" IGRAPH_PRId + "), got %" IGRAPH_PRId ".", IGRAPH_EINVAL, + vc, start); + } + if (steps < 0) { + IGRAPH_ERRORF("Number of steps should be non-negative, got %" + IGRAPH_PRId ".", IGRAPH_EINVAL, steps); + } + + 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 (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); + } + } + } + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (edges || weights) { + return igraph_i_random_walk_inclist(graph, weights, vertices, edges, + start, mode, steps, stuck); + } else { + return igraph_i_random_walk_adjlist(graph, vertices, + start, mode, steps, stuck); + } +} + + +/** + * \function igraph_random_edge_walk + * \brief Performs a random walk on a graph and returns 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 \c IGRAPH_ERWSTUCK error is reported. In both cases, + * \p edgewalk is truncated to contain the actual interrupted walk. + * + * \return Error code. + * + * \deprecated-by igraph_random_walk 0.10.0 + */ +igraph_error_t igraph_random_edge_walk( + const igraph_t *graph, + const igraph_vector_t *weights, + igraph_vector_int_t *edgewalk, + igraph_integer_t start, igraph_neimode_t mode, + igraph_integer_t steps, + igraph_random_walk_stuck_t stuck) { + + return igraph_random_walk(graph, weights, NULL, edgewalk, + start, mode, steps, stuck); +} diff --git a/src/vendor/cigraph/src/paths/shortest_paths.c b/src/vendor/cigraph/src/paths/shortest_paths.c index 0c967e619b5..29ad1dbee02 100644 --- a/src/vendor/cigraph/src/paths/shortest_paths.c +++ b/src/vendor/cigraph/src/paths/shortest_paths.c @@ -37,7 +37,7 @@ /* 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( +static igraph_error_t 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 */ @@ -45,23 +45,22 @@ static int igraph_i_average_path_length_unweighted( 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t source, j, n; + igraph_integer_t *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_dqueue_int_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); - } + already_added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for average path length."); IGRAPH_FINALLY(igraph_free, already_added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); IGRAPH_CHECK(igraph_adjlist_init( graph, &allneis, @@ -71,20 +70,20 @@ static int igraph_i_average_path_length_unweighted( 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_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]; + igraph_integer_t neighbor = VECTOR(*neis)[j]; if (already_added[neighbor] == source + 1) { continue; } @@ -95,10 +94,10 @@ static int igraph_i_average_path_length_unweighted( *res += actdist + 1.0; } no_of_conn_pairs += 1; - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor)); - IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_empty */ + } /* while !igraph_dqueue_int_empty */ } /* for source < no_of_nodes */ @@ -129,7 +128,7 @@ static int igraph_i_average_path_length_unweighted( /* clean */ IGRAPH_FREE(already_added); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); igraph_adjlist_destroy(&allneis); IGRAPH_FINALLY_CLEAN(3); @@ -141,7 +140,7 @@ static int igraph_i_average_path_length_unweighted( * 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( +static igraph_error_t igraph_i_average_path_length_dijkstra( const igraph_t *graph, igraph_real_t *res, igraph_real_t *unconnected_pairs, @@ -162,15 +161,15 @@ static int igraph_i_average_path_length_dijkstra( - 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 + computation, as isfinite() 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_2wheap_t Q; igraph_lazy_inclist_t inclist; - long int source, j; + igraph_integer_t 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 */ @@ -179,7 +178,7 @@ static int igraph_i_average_path_length_dijkstra( } if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERRORF("Weight vector length (%ld) does not match the number of edges (%ld).", + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match the number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); } if (no_of_edges > 0) { @@ -187,7 +186,7 @@ static int igraph_i_average_path_length_dijkstra( if (min < 0) { IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); } - else if (igraph_is_nan(min)) { + else if (isnan(min)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -216,10 +215,10 @@ static int igraph_i_average_path_length_dijkstra( igraph_2wheap_push_with_index(&Q, source, -1.0); while (!igraph_2wheap_empty(&Q)) { - long int minnei = igraph_2wheap_max_index(&Q); + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - long int nlen; + igraph_integer_t nlen; if (minnei != source) { if (invert) { @@ -231,21 +230,24 @@ static int igraph_i_average_path_length_dijkstra( } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei); + neis = igraph_lazy_inclist_get(&inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); 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_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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) { + if (altdist == IGRAPH_INFINITY) { + /* Ignore edges with positive infinite weight */ + } else 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_modify(&Q, tto, -altdist); } } } /* !igraph_2wheap_empty(&Q) */ @@ -288,7 +290,7 @@ static int igraph_i_average_path_length_dijkstra( * * * 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, + * 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. @@ -299,8 +301,8 @@ static int igraph_i_average_path_length_dijkstra( * \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 + * \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 @@ -312,7 +314,7 @@ static int igraph_i_average_path_length_dijkstra( * \example examples/simple/igraph_average_path_length.c */ -int igraph_average_path_length(const igraph_t *graph, +igraph_error_t 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) { @@ -327,7 +329,7 @@ int igraph_average_path_length(const igraph_t *graph, * * * 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, + * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c true, * NaN is returned. * * @@ -342,11 +344,12 @@ int igraph_average_path_length(const igraph_t *graph, * 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. + * version, \ref igraph_average_path_length() is called. Edges with positive + * infinite weight are ignored. * \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 + * \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 @@ -364,7 +367,7 @@ int igraph_average_path_length(const igraph_t *graph, * \example examples/simple/igraph_grg_game.c */ -int igraph_average_path_length_dijkstra(const igraph_t *graph, +igraph_error_t 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) @@ -399,7 +402,8 @@ int igraph_average_path_length_dijkstra(const igraph_t *graph, * 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. + * the global efficiency. Edges with positive infinite weights are + * ignored. * \param directed Boolean, whether to consider directed paths. * Ignored for undirected graphs. * \return Error code: @@ -416,7 +420,7 @@ int igraph_average_path_length_dijkstra(const igraph_t *graph, * */ -int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, +igraph_error_t igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, const igraph_vector_t *weights, igraph_bool_t directed) { @@ -428,37 +432,37 @@ int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res, /***** Local efficiency *****/ /****************************/ -static int igraph_i_local_efficiency_unweighted( +static igraph_error_t 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_dqueue_int_t *q, + igraph_integer_t *already_counted, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t vertex_neis_size; + igraph_integer_t neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ + igraph_integer_t i, j; - igraph_dqueue_clear(q); + igraph_dqueue_int_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)); + memset(already_counted, 0, no_of_nodes * sizeof(already_counted[0])); IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); - vertex_neis_size = igraph_vector_size(vertex_neis); + vertex_neis_size = igraph_vector_int_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]; + igraph_integer_t v = VECTOR(*vertex_neis)[i]; if (v != vertex && ! VECTOR(*nei_mask)[v]) { VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ neighbor_count++; @@ -473,8 +477,8 @@ static int igraph_i_local_efficiency_unweighted( } for (i=0; i < vertex_neis_size; ++i) { - long int source = VECTOR(*vertex_neis)[i]; - long int reached = 0; + igraph_integer_t source = VECTOR(*vertex_neis)[i]; + igraph_integer_t reached = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -486,21 +490,21 @@ static int igraph_i_local_efficiency_unweighted( VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */ - IGRAPH_CHECK(igraph_dqueue_push(q, source)); - IGRAPH_CHECK(igraph_dqueue_push(q, 0)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, source)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, 0)); already_counted[source] = i + 1; - while (!igraph_dqueue_empty(q)) { + while (!igraph_dqueue_int_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); + igraph_integer_t act_neis_size; + igraph_integer_t act = igraph_dqueue_int_pop(q); + igraph_integer_t actdist = igraph_dqueue_int_pop(q); if (act != source && VECTOR(*nei_mask)[act]) { *res += 1.0 / actdist; reached++; if (reached == neighbor_count) { - igraph_dqueue_clear(q); + igraph_dqueue_int_clear(q); break; } } @@ -508,14 +512,14 @@ static int igraph_i_local_efficiency_unweighted( 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]; + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(q, actdist + 1)); } } } @@ -525,11 +529,11 @@ static int igraph_i_local_efficiency_unweighted( return IGRAPH_SUCCESS; } -static int igraph_i_local_efficiency_dijkstra( +static igraph_error_t 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_int_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, @@ -548,21 +552,21 @@ static int igraph_i_local_efficiency_dijkstra( - 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 + computation, as isfinite() 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_integer_t i, j; + igraph_integer_t vertex_neis_size; + igraph_integer_t 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); + vertex_neis_size = igraph_vector_int_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]; + igraph_integer_t v = VECTOR(*vertex_neis)[i]; if (v != vertex && ! VECTOR(*nei_mask)[v]) { VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ neighbor_count++; @@ -577,8 +581,8 @@ static int igraph_i_local_efficiency_dijkstra( } for (i=0; i < vertex_neis_size; ++i) { - long int source = VECTOR(*vertex_neis)[i]; - long int reached = 0; + igraph_integer_t source = VECTOR(*vertex_neis)[i]; + igraph_integer_t reached = 0; IGRAPH_ALLOW_INTERRUPTION(); @@ -594,10 +598,10 @@ static int igraph_i_local_efficiency_dijkstra( igraph_2wheap_push_with_index(Q, source, -1.0); while (!igraph_2wheap_empty(Q)) { - long int minnei = igraph_2wheap_max_index(Q); + igraph_integer_t minnei = igraph_2wheap_max_index(Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q); igraph_vector_int_t *neis; - long int nlen; + igraph_integer_t nlen; if (minnei != source && VECTOR(*nei_mask)[minnei]) { *res += 1.0/(mindist - 1.0); @@ -609,13 +613,14 @@ static int igraph_i_local_efficiency_dijkstra( } /* Now check all neighbors of 'minnei' for a shorter path */ - neis = igraph_lazy_inclist_get(inclist, (igraph_integer_t) minnei); + neis = igraph_lazy_inclist_get(inclist, minnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); 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); + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, minnei); if (tto == vertex) continue; @@ -629,7 +634,7 @@ static int igraph_i_local_efficiency_dijkstra( 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_modify(Q, tto, -altdist); } } @@ -673,7 +678,8 @@ static int igraph_i_local_efficiency_dijkstra( * 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. + * \ref igraph_average_path_length() is called. Edges with positive + * infinite weights are ignored. * \param directed Boolean, whether to consider directed paths. * Ignored for undirected graphs. * \param mode How to determine the local neighborhood of each vertex @@ -702,18 +708,18 @@ static int igraph_i_local_efficiency_dijkstra( * */ -int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t nodes_to_calc; /* no. of vertices includes in computation */ igraph_vit_t vit; - igraph_vector_t vertex_neis; + igraph_vector_int_t vertex_neis; igraph_vector_char_t nei_mask; - long int i; + igraph_integer_t i; /* 'nei_mask' is a vector indexed by vertices. The meaning of its values is as follows: * 0: not a neighbour of 'vertex' @@ -725,7 +731,7 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, */ 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_VECTOR_INT_INIT_FINALLY(&vertex_neis, 0); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -736,14 +742,12 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, if (! weights) /* unweighted case */ { - long int *already_counted; + igraph_integer_t *already_counted; igraph_adjlist_t adjlist; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_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); - } + already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for local efficiency calculation."); IGRAPH_FINALLY(igraph_free, already_counted); IGRAPH_CHECK(igraph_adjlist_init( @@ -753,7 +757,7 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, )); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); for (IGRAPH_VIT_RESET(vit), i=0; ! IGRAPH_VIT_END(vit); @@ -765,7 +769,7 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode)); } - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); igraph_adjlist_destroy(&adjlist); IGRAPH_FREE(already_counted); IGRAPH_FINALLY_CLEAN(3); @@ -776,15 +780,15 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, 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); + 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); + IGRAPH_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); } - else if (igraph_is_nan(min)) { - IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + else if (isnan(min)) { + IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); } } @@ -811,7 +815,7 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, } igraph_vit_destroy(&vit); - igraph_vector_destroy(&vertex_neis); + igraph_vector_int_destroy(&vertex_neis); igraph_vector_char_destroy(&nei_mask); IGRAPH_FINALLY_CLEAN(3); @@ -829,7 +833,8 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, * \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. + * If a null pointer is given, all weights are assumed to be 1. Edges + * with positive infinite weight are ignored. * \param directed Boolean, whether to consider directed paths. * Ignored for undirected graphs. * \param mode How to determine the local neighborhood of each vertex @@ -858,11 +863,11 @@ int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res, * */ -int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, +igraph_error_t 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_integer_t 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 @@ -902,21 +907,24 @@ int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, * 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 + * \param res 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 + * \param from 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 + * \param to 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 + * \param vertex_path Pointer to an initialized vector. If not \c NULL the actual + * longest geodesic path in terms of vertices will be stored here. The vector will be + * resized as needed. + * \param edge_path Pointer to an initialized vector. If not \c NULL the actual + * longest geodesic path in terms of edges 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 + * \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 @@ -930,19 +938,21 @@ int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res, * \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_error_t igraph_diameter(const igraph_t *graph, igraph_real_t *res, + igraph_integer_t *from, igraph_integer_t *to, + igraph_vector_int_t *vertex_path, igraph_vector_int_t *edge_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t i, j, n; + igraph_integer_t *already_added; + igraph_integer_t nodes_reached; + /* from/to are initialized to 0 because in a singleton graph, or in an edgeless graph + * with unconn = true, the diameter path will be considered to consist of vertex 0 only. */ + igraph_integer_t ifrom = 0, ito = 0; + igraph_real_t ires = 0; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; igraph_vector_int_t *neis; igraph_neimode_t dirmode; igraph_adjlist_t allneis; @@ -950,17 +960,20 @@ int igraph_diameter(const igraph_t *graph, igraph_real_t *pres, /* 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 (res) { + *res = IGRAPH_NAN; + } + if (vertex_path) { + igraph_vector_int_clear(vertex_path); } - if (path) { - igraph_vector_clear(path); + if (edge_path) { + igraph_vector_int_clear(edge_path); } - if (pfrom) { - *pfrom = -1; + if (from) { + *from = -1; } - if (pto) { - *pto = -1; + if (to) { + *to = -1; } return IGRAPH_SUCCESS; } @@ -970,54 +983,53 @@ int igraph_diameter(const igraph_t *graph, igraph_real_t *pres, } else { dirmode = IGRAPH_ALL; } - already_added = IGRAPH_CALLOC(no_of_nodes, long int); - if (already_added == 0) { - IGRAPH_ERROR("diameter failed", IGRAPH_ENOMEM); - } + already_added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_added, "Insufficient memory for diameter calculation."); IGRAPH_FINALLY(igraph_free, already_added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + if (actdist > ires) { + ires = actdist; + ifrom = i; + ito = 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]; + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } - } /* while !igraph_dqueue_empty */ + } /* while !igraph_dqueue_int_empty */ /* not connected, return IGRAPH_INFINITY */ if (nodes_reached != no_of_nodes && !unconn) { - res = IGRAPH_INFINITY; - from = -1; - to = -1; + ires = IGRAPH_INFINITY; + ifrom = -1; + ito = -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); + 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); + else if (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } @@ -1188,13 +1209,13 @@ int igraph_diameter_dijkstra(const igraph_t *graph, nodes_reached = 0.0; while (!igraph_2wheap_empty(&Q)) { - long int minnei = igraph_2wheap_max_index(&Q); + igraph_integer_t minnei = igraph_2wheap_max_index(&Q); igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); igraph_vector_int_t *neis; - long int nlen; + igraph_integer_t nlen; - if (mindist > res) { - res = mindist; from = source; to = minnei; + if (mindist > ires) { + ires = mindist; ifrom = source; ito = minnei; } nodes_reached++; @@ -1202,8 +1223,8 @@ int igraph_diameter_dijkstra(const igraph_t *graph, 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_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t 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); @@ -1214,7 +1235,7 @@ int igraph_diameter_dijkstra(const igraph_t *graph, 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_modify(&Q, tto, -altdist); } } @@ -1222,15 +1243,15 @@ int igraph_diameter_dijkstra(const igraph_t *graph, /* not connected, return infinity */ if (nodes_reached != no_of_nodes && !unconn) { - res = IGRAPH_INFINITY; - from = to = -1; + ires = IGRAPH_INFINITY; + ifrom = ito = -1; break; } } /* source < no_of_nodes */ /* Compensate for the +1 that we have added to distances */ - res -= 1; + ires -= 1; igraph_inclist_destroy(&inclist); igraph_2wheap_destroy(&Q); @@ -1238,33 +1259,416 @@ int igraph_diameter_dijkstra(const igraph_t *graph, IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL); - if (pres) { - *pres = res; + if (res) { + *res = ires; } - if (pfrom) { - *pfrom = (igraph_integer_t) from; + if (from) { + *from = ifrom; } - if (pto) { - *pto = (igraph_integer_t) to; + if (to) { + *to = ito; } - if (path) { - if (!igraph_finite(res)) { - igraph_vector_clear(path); + if ((vertex_path) || (edge_path)) { + if (!isfinite(ires)) { + if (vertex_path){ + igraph_vector_int_clear(vertex_path); + } + if (edge_path) { + igraph_vector_int_clear(edge_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); + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + /*vertices=*/ vertex_path, /*edges=*/ edge_path, + ifrom, ito, + weights, dirmode)); } } + return IGRAPH_SUCCESS; +} + +/** + * Temporarily removes all edges incident on the vertex with the given ID from + * the graph by setting the weights of these edges to infinity. + * + * \param graph the graph + * \param weights the weights of the edges of the graph + * \param vid the ID of the vertex to remove + * \param edges_removed vector that records the IDs of the edges that were + * "removed" (i.e. their weights were set to infinity) + * \param eids temporary vector that is used to retrieve the IDs of the + * incident edges, to make this function free of memory allocations + */ +static igraph_error_t igraph_i_semidelete_vertex( + const igraph_t *graph, igraph_vector_t *weights, + igraph_integer_t vid, igraph_vector_int_t *edges_removed, + igraph_vector_int_t *eids +) { + igraph_integer_t j, n; + + IGRAPH_CHECK(igraph_incident(graph, eids, vid, IGRAPH_ALL)); + + n = igraph_vector_int_size(eids); + for (j = 0; j < n; j++) { + igraph_integer_t eid = VECTOR(*eids)[j]; + IGRAPH_CHECK(igraph_vector_int_push_back(edges_removed, eid)); + VECTOR(*weights)[eid] = IGRAPH_INFINITY; + } + + return IGRAPH_SUCCESS; +} + +static igraph_bool_t igraph_i_has_edge_with_infinite_weight( + const igraph_vector_int_t* path, const igraph_vector_t* weights +) { + igraph_integer_t i, n; + + n = weights ? igraph_vector_int_size(path) : 0; + for (i = 0; i < n; i++) { + igraph_integer_t edge = VECTOR(*path)[i]; + if (!isfinite(VECTOR(*weights)[edge])) { + return true; + } + } + + return false; +} + +static igraph_real_t igraph_i_get_total_weight_of_path( + igraph_vector_int_t* path, const igraph_vector_t* weights +) { + igraph_integer_t i, n = igraph_vector_int_size(path); + igraph_real_t result; + + if (weights) { + result = 0; + for (i = 0; i < n; i++) { + igraph_integer_t edge = VECTOR(*path)[i]; + result += VECTOR(*weights)[edge]; + } + } else { + result = n; + } + + return result; +} + +/** + * \function igraph_get_k_shortest_paths + * \brief k shortest paths between two vertices. + * + * This function returns the \p k shortest paths between two vertices, in order of + * increasing lengths. + * + * + * Reference: + * + * + * Yen, Jin Y.: + * An algorithm for finding shortest routes from all source nodes to a given + * destination in general networks. + * Quarterly of Applied Mathematics. 27 (4): 526–530. (1970) + * https://doi.org/10.1090/qam/253822 + * + * \param graph The graph object. + * \param weights The edge weights of the graph. Can be \c NULL for an + * unweighted graph. Infinite weights will be treated as missing + * edges. + * \param vertex_paths Pointer to an initialized list of integer vectors, the result + * will be stored here in \ref igraph_vector_int_t objects. Each vector + * object contains the vertex IDs along the kth shortest path + * between \p from and \p to, where \c k is the vector list index. May + * be \c NULL if the vertex paths are not needed. + * \param edge_paths Pointer to an initialized list of integer vectors, the result + * will be stored here in \ref igraph_vector_int_t objects. Each vector + * object contains the edge IDs along the kth shortest path + * between \p from and \p to, where \c k is the vector list index. May be + * \c NULL if the edge paths are not needed. + * \param k The number of paths. + * \param from The ID of the vertex from which the paths are calculated. + * \param to The ID of the vertex to which the paths are calculated. + * \param mode The type of paths to be used for the + * calculation in directed graphs. Possible values: + * \clist + * \cli IGRAPH_OUT + * The outgoing paths of \p from are calculated. + * \cli IGRAPH_IN + * The incoming paths of \p from 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 or \p to is an invalid vertex id. + * \cli IGRAPH_EINVMODE + * Invalid mode argument. + * \cli IGRAPH_EINVAL + * Invalid argument. + * \endclist + * + * \sa \ref igraph_get_all_simple_paths(), \ref igraph_get_shortest_paths(), + * \ref igraph_get_shortest_paths_dijkstra() + * + * Time complexity: k |V| (|V| log|V| + |E|), where |V| is the number of vertices, + * and |E| is the number of edges. + */ +igraph_error_t igraph_get_k_shortest_paths( + const igraph_t *graph, const igraph_vector_t *weights, + igraph_vector_int_list_t *vertex_paths, + igraph_vector_int_list_t *edge_paths, + igraph_integer_t k, igraph_integer_t from, igraph_integer_t to, + igraph_neimode_t mode +) { + igraph_vector_int_list_t paths_pot; /* potential shortest paths */ + igraph_integer_t vertex_spur; + igraph_vector_int_t path_spur, path_root, path_total, path_shortest; + igraph_integer_t nr_edges_root, i_path_current, i_path, edge_path_root, vertex_root_del; + igraph_integer_t i, n; + igraph_vector_t current_weights; + igraph_vector_int_t edges_removed; + igraph_integer_t nr_edges = igraph_ecount(graph); + igraph_bool_t infinite_path, already_in_potential_paths; + igraph_vector_int_t *path_0; + igraph_vector_int_t eids; + igraph_real_t path_weight, shortest_path_weight; + igraph_integer_t edge_paths_owned = 0; + + if (!igraph_is_directed(graph) && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) { + mode = IGRAPH_ALL; + } - return 0; + if (vertex_paths) { + igraph_vector_int_list_clear(vertex_paths); + } + + if (!edge_paths) { + /* We will need our own instance */ + edge_paths = IGRAPH_CALLOC(1, igraph_vector_int_list_t); + IGRAPH_CHECK_OOM(edge_paths, "Cannot allocate vector for storing edge paths."); + IGRAPH_FINALLY(igraph_free, edge_paths); + edge_paths_owned = 1; + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(edge_paths, 0); + edge_paths_owned = 2; + } + + igraph_vector_int_list_clear(edge_paths); + + if (k == 0) { + goto cleanup; + } + + IGRAPH_CHECK(igraph_vector_int_list_resize(edge_paths, 1)); + path_0 = igraph_vector_int_list_get_ptr(edge_paths, 0); + + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + NULL, + path_0, + from, + to, + weights, + mode)); + + /* Check if there's a path. */ + infinite_path = igraph_i_has_edge_with_infinite_weight(path_0, weights); + if (infinite_path || (from != to && igraph_vector_int_size(path_0) == 0)) { + /* No path found. */ + igraph_vector_int_list_clear(edge_paths); + goto cleanup; + } + + IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&paths_pot, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_spur, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_root, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path_total, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_removed, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); + IGRAPH_VECTOR_INIT_FINALLY(¤t_weights, nr_edges); + + /* If weights are NULL we use a uniform weight vector where each edge has + * a weight of 1. Later on, we replace the weights of removed edges with + * infinities. Note that we work on a copy of the weight vector so the + * original vector remains intact. + */ + if (weights) { + igraph_vector_update(¤t_weights, weights); + } else { + igraph_vector_fill(¤t_weights, 1); + } + + for (i_path_current = 1; i_path_current < k; i_path_current++) { + igraph_vector_int_t *path_previous = igraph_vector_int_list_tail_ptr(edge_paths); + igraph_integer_t path_previous_length = igraph_vector_int_size(path_previous); + for (nr_edges_root = 0; nr_edges_root < path_previous_length; nr_edges_root++) { + /* Determine spur node. */ + if (mode == IGRAPH_OUT) { + vertex_spur = IGRAPH_FROM(graph, VECTOR(*path_previous)[nr_edges_root]); + } else if (mode == IGRAPH_IN) { + vertex_spur = IGRAPH_TO(graph, VECTOR(*path_previous)[nr_edges_root]); + } else { + igraph_integer_t eid = VECTOR(*path_previous)[nr_edges_root]; + igraph_integer_t vertex_spur_1 = IGRAPH_FROM(graph, eid); + igraph_integer_t vertex_spur_2 = IGRAPH_TO(graph, eid); + igraph_integer_t vertex_spur_3; + igraph_integer_t vertex_spur_4; + if (nr_edges_root < path_previous_length-1) { + igraph_integer_t eid_next = VECTOR(*path_previous)[nr_edges_root + 1]; + vertex_spur_3 = IGRAPH_FROM(graph, eid_next); + vertex_spur_4 = IGRAPH_TO(graph, eid_next); + } else { + vertex_spur_3 = vertex_spur_4 = to; + } + if (vertex_spur_1 == vertex_spur_3 || vertex_spur_1 == vertex_spur_4) { + vertex_spur = vertex_spur_2; + } else { + vertex_spur = vertex_spur_1; + } + } + + /* Determine root path. */ + IGRAPH_CHECK(igraph_vector_int_resize(&path_root, nr_edges_root)); + for (i = 0; i < nr_edges_root; i++) { + VECTOR(path_root)[i] = VECTOR(*path_previous)[i]; + } + + /* Remove edges that are part of the previous shortest paths which share the same root path. */ + for (i_path = 0; i_path < i_path_current; i_path++) { + igraph_vector_int_t *path_check = igraph_vector_int_list_get_ptr(edge_paths, i_path); + igraph_bool_t equal = true; + for (i = 0; i < nr_edges_root; i++) { + if (VECTOR(path_root)[i] != VECTOR(*path_check)[i]) { + equal = false; + break; + } + } + if (equal) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges_removed, VECTOR(*path_check)[nr_edges_root])); + VECTOR(current_weights)[VECTOR(*path_check)[nr_edges_root]] = IGRAPH_INFINITY; + } + } + + /* pseudocode: for each node rootPathNode in rootPath except spurNode: + * remove rootPathNode from Graph; + */ + for (edge_path_root = 0; edge_path_root < nr_edges_root; edge_path_root++) { + if (mode == IGRAPH_OUT) { + vertex_root_del = IGRAPH_FROM(graph, VECTOR(path_root)[edge_path_root]); + } else if (mode == IGRAPH_IN) { + vertex_root_del = IGRAPH_TO(graph, VECTOR(path_root)[edge_path_root]); + } else { + igraph_integer_t eid = VECTOR(*path_previous)[edge_path_root]; + igraph_integer_t eid_next = VECTOR(*path_previous)[edge_path_root + 1]; + igraph_integer_t vertex_root_del_1 = IGRAPH_FROM(graph, eid); + igraph_integer_t vertex_root_del_2 = IGRAPH_TO(graph, eid); + igraph_integer_t vertex_root_del_3 = IGRAPH_FROM(graph, eid_next); + igraph_integer_t vertex_root_del_4 = IGRAPH_TO(graph, eid_next); + if (vertex_root_del_1 == vertex_root_del_3 || vertex_root_del_1 == vertex_root_del_4) { + vertex_root_del = vertex_root_del_2; + } else { + vertex_root_del = vertex_root_del_1; + } + } + /* Remove vertex by setting incident edges to infinity */ + IGRAPH_CHECK(igraph_i_semidelete_vertex( + graph, ¤t_weights, vertex_root_del, &edges_removed, + &eids + )); + } + + /* Determine spur path */ + IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, + NULL, + &path_spur, + vertex_spur, + to, + ¤t_weights, + mode)); + infinite_path = igraph_i_has_edge_with_infinite_weight(&path_spur, ¤t_weights); + + /* Add total (root + spur) path to potential paths if it's not in there yet. */ + if (!infinite_path) { + IGRAPH_CHECK(igraph_vector_int_update(&path_total, &path_root)); + IGRAPH_CHECK(igraph_vector_int_append(&path_total, &path_spur)); + + already_in_potential_paths = 0; + n = igraph_vector_int_list_size(&paths_pot); + for (i = 0; i < n; i++) { + if (igraph_vector_int_all_e(&path_total, igraph_vector_int_list_get_ptr(&paths_pot, i))) { + already_in_potential_paths = 1; + break; + } + } + + if (!already_in_potential_paths) { + IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(&paths_pot, &path_total)); + } + } + + /* Cleanup */ + n = igraph_vector_int_size(&edges_removed); + for (i = 0; i < n; i++) { + VECTOR(current_weights)[VECTOR(edges_removed)[i]] = + weights ? VECTOR(*weights)[VECTOR(edges_removed)[i]] : 1; + } + igraph_vector_int_clear(&edges_removed); + } + + /* Add shortest potential path to shortest paths */ + n = igraph_vector_int_list_size(&paths_pot); + if (n == 0) { + break; + } + + shortest_path_weight = igraph_i_get_total_weight_of_path( + igraph_vector_int_list_get_ptr(&paths_pot, 0), weights + ); + i_path = 0; + for (i = 1; i < n; i++) { + path_weight = igraph_i_get_total_weight_of_path( + igraph_vector_int_list_get_ptr(&paths_pot, i), weights + ); + if (path_weight < shortest_path_weight) { + i_path = i; + shortest_path_weight = path_weight; + } + } + + IGRAPH_CHECK(igraph_vector_int_list_remove_fast(&paths_pot, i_path, &path_shortest)); + IGRAPH_CHECK(igraph_vector_int_list_push_back(edge_paths, &path_shortest)); + } + + igraph_vector_destroy(¤t_weights); + igraph_vector_int_destroy(&eids); + igraph_vector_int_destroy(&edges_removed); + igraph_vector_int_destroy(&path_total); + igraph_vector_int_destroy(&path_root); + igraph_vector_int_destroy(&path_spur); + igraph_vector_int_list_destroy(&paths_pot); + IGRAPH_FINALLY_CLEAN(7); + + if (vertex_paths) { + igraph_integer_t no_of_edge_paths = igraph_vector_int_list_size(edge_paths); + + IGRAPH_CHECK(igraph_vector_int_list_resize(vertex_paths, no_of_edge_paths)); + for (i = 0; i < no_of_edge_paths; i++) { + igraph_vector_int_t* edge_path = igraph_vector_int_list_get_ptr(edge_paths, i); + igraph_vector_int_t* vertex_path = igraph_vector_int_list_get_ptr(vertex_paths, i); + IGRAPH_CHECK(igraph_vertex_path_from_edge_path(graph, from, edge_path, vertex_path, mode)); + } + } + +cleanup: + if (edge_paths_owned >= 2) { + igraph_vector_int_list_destroy(edge_paths); + IGRAPH_FINALLY_CLEAN(1); + } + if (edge_paths_owned >= 1) { + igraph_free(edge_paths); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/simple_paths.c b/src/vendor/cigraph/src/paths/simple_paths.c index 7ca7bad8439..b8a7998357c 100644 --- a/src/vendor/cigraph/src/paths/simple_paths.c +++ b/src/vendor/cigraph/src/paths/simple_paths.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2014 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2014-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 @@ -15,19 +13,15 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - + along with this program. If not, see . */ + #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" @@ -41,15 +35,15 @@ * * 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. + * run out of memory when using this function when the + * graph has many cycles. Consider using the \p cutoff + * parameter when you do not need long paths. * - * - * 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 res Initialized integer vector. The paths are + * returned here in terms of their vertices, 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 @@ -58,11 +52,13 @@ * for undirected graphs. * \return Error code. * + * \sa \ref igraph_get_k_shortest_paths() + * * 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_error_t igraph_get_all_simple_paths(const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t from, const igraph_vs_t to, @@ -72,10 +68,10 @@ int igraph_get_all_simple_paths(const igraph_t *graph, 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 stack, dist; /* used as a stack, but represented as a vector, + in order to be appendable to other vectors */ + igraph_vector_bool_t markto, added; igraph_vector_int_t nptr; int iteration = 0; @@ -84,29 +80,24 @@ int igraph_get_all_simple_paths(const igraph_t *graph, } if (!toall) { - igraph_vector_char_init(&markto, no_nodes); - IGRAPH_FINALLY(igraph_vector_char_destroy, &markto); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&markto, no_nodes); 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; + VECTOR(markto)[ IGRAPH_VIT_GET(vit) ] = true; } 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_VECTOR_BOOL_INIT_FINALLY(&added, no_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&stack, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&dist, 100); 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_INIT_FINALLY(&nptr, no_nodes); igraph_vector_int_clear(res); @@ -114,16 +105,20 @@ int igraph_get_all_simple_paths(const igraph_t *graph, igraph_vector_int_clear(&dist); igraph_vector_int_push_back(&stack, from); igraph_vector_int_push_back(&dist, 0); - VECTOR(added)[from] = 1; + VECTOR(added)[from] = true; while (!igraph_vector_int_empty(&stack)) { - int act = igraph_vector_int_tail(&stack); - int curdist = igraph_vector_int_tail(&dist); + igraph_integer_t act = igraph_vector_int_tail(&stack); + igraph_integer_t 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_integer_t n; + igraph_integer_t *ptr = igraph_vector_int_get_ptr(&nptr, act); igraph_bool_t any; igraph_bool_t within_dist; - int nei; + igraph_integer_t nei; + + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + + n = igraph_vector_int_size(neis); if (iteration == 0) { IGRAPH_ALLOW_INTERRUPTION(); @@ -132,9 +127,9 @@ int igraph_get_all_simple_paths(const igraph_t *graph, within_dist = (curdist < cutoff || cutoff < 0); if (within_dist) { /* Search for a neighbor that was not yet visited */ - any = 0; + any = false; while (!any && (*ptr) < n) { - nei = (int) VECTOR(*neis)[(*ptr)]; + nei = VECTOR(*neis)[(*ptr)]; any = !VECTOR(added)[nei]; (*ptr) ++; } @@ -143,7 +138,7 @@ int igraph_get_all_simple_paths(const igraph_t *graph, /* 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; + VECTOR(added)[nei] = true; /* Add to results */ if (toall || VECTOR(markto)[nei]) { IGRAPH_CHECK(igraph_vector_int_append(res, &stack)); @@ -151,9 +146,9 @@ int igraph_get_all_simple_paths(const igraph_t *graph, } } else { /* There is no such neighbor, finished with the subtree */ - int up = igraph_vector_int_pop_back(&stack); + igraph_integer_t up = igraph_vector_int_pop_back(&stack); igraph_vector_int_pop_back(&dist); - VECTOR(added)[up] = 0; + VECTOR(added)[up] = false; VECTOR(nptr)[up] = 0; } @@ -167,13 +162,13 @@ int igraph_get_all_simple_paths(const igraph_t *graph, igraph_lazy_adjlist_destroy(&adjlist); igraph_vector_int_destroy(&dist); igraph_vector_int_destroy(&stack); - igraph_vector_char_destroy(&added); + igraph_vector_bool_destroy(&added); IGRAPH_FINALLY_CLEAN(5); if (!toall) { - igraph_vector_char_destroy(&markto); + igraph_vector_bool_destroy(&markto); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/sparsifier.c b/src/vendor/cigraph/src/paths/sparsifier.c new file mode 100644 index 00000000000..5c7d103e0cf --- /dev/null +++ b/src/vendor/cigraph/src/paths/sparsifier.c @@ -0,0 +1,460 @@ +/* + 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 . +*/ + + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_random.h" + +#include "core/interruption.h" + +/* + * This internal function gets the adjacency and incidence list representation + * of the current residual graph, the weight vector, the current assignment of + * the vertices to clusters, whether the i-th cluster is sampled, and the + * index of a single node v. The function updates the given lightest_eid vector + * such that the i-th element contains the ID of the lightest edge that leads + * from node v to cluster i. Similarly, the lightest_weight vector is updated + * to contain the weights of these edges. + * + * When the is_cluster_sampled vector is provided, the + * nearest_neighboring_sampled_cluster pointer is also updated to the index of + * the cluster that has the smallest weight among the _sampled_ ones. + * + * As a pre-condition, this function requires the lightest_eid vector to be + * filled with -1 and the lightest_weight vector to be filled with infinity. + * This is _not_ checked within the function. + * + * Use the igraph_i_clean_lightest_edges_to_clusters() function to clear these vectors + * after you are done with them. Avoid using igraph_vector_fill() because that + * one is O(|V|), while igraph_i_clean_lightest_edge_vector() is O(d) where d + * is the degree of the vertex. + */ +static igraph_error_t igraph_i_collect_lightest_edges_to_clusters( + const igraph_adjlist_t *adjlist, + const igraph_inclist_t *inclist, + const igraph_vector_t *weights, + const igraph_vector_int_t *clustering, + const igraph_vector_bool_t *is_cluster_sampled, + igraph_integer_t v, + igraph_vector_int_t *lightest_eid, + igraph_vector_t *lightest_weight, + igraph_vector_int_t *dirty_vids, + igraph_integer_t *nearest_neighboring_sampled_cluster +) { + // This internal function gets the residual graph, the clustering, the sampled clustering and + // the vector and return the lightest edge to each neighboring cluster and the index of the lightest + // sampled cluster (if any) + + igraph_real_t lightest_weight_to_sampled = INFINITY; + igraph_vector_int_t* adjacent_nodes = igraph_adjlist_get(adjlist, v); + igraph_vector_int_t* incident_edges = igraph_inclist_get(inclist, v); + igraph_integer_t i, nlen = igraph_vector_int_size(incident_edges); + + for (i = 0; i < nlen; i++) { + igraph_integer_t neighbor_node = VECTOR(*adjacent_nodes)[i]; + igraph_integer_t edge = VECTOR(*incident_edges)[i]; + igraph_integer_t neighbor_cluster = VECTOR(*clustering)[neighbor_node]; + igraph_real_t weight = weights ? VECTOR(*weights)[edge] : 1; + + // If the weight of the edge being considered is smaller than the weight + // of the lightest edge found so far that connects v to the same + // cluster, remember the new minimum. + if (VECTOR(*lightest_weight)[neighbor_cluster] > weight) { + VECTOR(*lightest_weight)[neighbor_cluster] = weight; + VECTOR(*lightest_eid)[neighbor_cluster] = edge; + + IGRAPH_CHECK(igraph_vector_int_push_back(dirty_vids, neighbor_cluster)); + + // Also, if this cluster happens to be a sampled cluster, also update + // the variables that store which is the lightest edge that connects + // v to any of the sampled clusters. + if (is_cluster_sampled) { + if ((VECTOR(*is_cluster_sampled)[neighbor_cluster]) && (lightest_weight_to_sampled > weight)) { + lightest_weight_to_sampled = weight; + *nearest_neighboring_sampled_cluster = neighbor_cluster; + } + } + } + } + + return IGRAPH_SUCCESS; +} + +static void igraph_i_clear_lightest_edges_to_clusters( + igraph_vector_int_t *dirty_vids, + igraph_vector_int_t *lightest_eid, + igraph_vector_t *lightest_weight +) { + igraph_integer_t i, n = igraph_vector_int_size(dirty_vids); + for (i = 0; i < n; i++) { + igraph_integer_t vid = VECTOR(*dirty_vids)[i]; + VECTOR(*lightest_weight)[vid] = INFINITY; + VECTOR(*lightest_eid)[vid] = -1; + } + + igraph_vector_int_clear(dirty_vids); +} + +/** + * \ingroup structural + * \function igraph_spanner + * \brief Calculates a spanner of a graph with a given stretch factor. + * + * A spanner of a graph G = (V,E) with a stretch t is a subgraph + * H = (V,Es) such that Es is a subset of E and the distance + * between any pair of nodes in H is at most t times the distance + * in G. The returned graph is always a spanner of the + * given graph with the specified stretch. For weighted graphs the + * number of edges in the spanner is O(k * n^(1 + 1 / k)), where k is + * k = (stretch + 1) / 2, m is the number of edges and n is the number + * of nodes in G. For unweighted graphs the number of edges is + * O(n^(1 + 1 / k) + kn). + * + * + * This function is based on the algorithm of Baswana and Sen: "A Simple and + * Linear Time Randomized Algorithm for Computing Sparse Spanners in + * Weighted Graphs". https://doi.org/10.1002/rsa.20130 + * + * \param graph An undirected connected graph object. If the graph + * is directed, the directions of the edges will be ignored. + * \param spanner An initialized vector, the IDs of the edges that constitute + * the calculated spanner will be returned here. Use + * \ref igraph_subgraph_from_edges() to extract the spanner as a separate + * graph object. + * \param stretch The stretch factor of the spanner. + * \param weights The edge weights or NULL. + * + * \return Error code: + * \clist + * \cli IGRAPH_ENOMEM + * not enough memory for temporary data. + * \endclist + * + * Time complexity: The algorithm is a randomized Las Vegas algorithm. The expected + * running time is O(km) where k is the value mentioned above. + */ +igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanner, + igraph_real_t stretch, const igraph_vector_t *weights) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, v, nlen, neighbor, cluster; + igraph_real_t sample_prob, k = (stretch + 1) / 2, weight, lightest_sampled_weight; + igraph_vector_int_t clustering, lightest_eid; + igraph_vector_t lightest_weight; + igraph_vector_bool_t is_cluster_sampled; + igraph_vector_bool_t is_edge_in_spanner; + igraph_vector_int_t new_clustering; + igraph_vector_int_t dirty_vids; + igraph_vector_int_t *adjacent_vertices; + igraph_vector_int_t *incident_edges; + igraph_adjlist_t adjlist; + igraph_inclist_t inclist; + igraph_integer_t edge; + igraph_integer_t index; + + if (spanner == NULL) { + return IGRAPH_SUCCESS; + } + + /* Test validity of stretch factor */ + if (stretch < 1) { + IGRAPH_ERROR("Stretch factor must be at least 1", IGRAPH_EINVAL); + } + + /* Test validity of weights vector */ + if (weights) { + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL); + } + } + } + + // Clear the vector that will contain the IDs of the edges in the spanner + igraph_vector_int_clear(spanner); + + // Create an incidence list representation of the graph and also create the + // corresponding adjacency list. The residual graph will not be constructed + // explicitly; it will only exist in terms of the incidence and the adjacency + // lists, maintained in parallel as the edges are removed from the residual + // graph. + IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_OUT, IGRAPH_NO_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); + IGRAPH_CHECK(igraph_adjlist_init_from_inclist(graph, &adjlist, &inclist)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + // Phase 1: forming the clusters + // Create a vector which maps the nodes to the centers of the corresponding + // clusters. At the beginning each node is its own cluster center. + IGRAPH_CHECK(igraph_vector_int_init_range(&clustering, 0, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &clustering); + + // A mapping vector which indicates the neighboring edge with the smallest + // weight for each cluster central, for a single vertex of interest. + // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() + // are enforced here. + IGRAPH_VECTOR_INT_INIT_FINALLY(&lightest_eid, no_of_nodes); + igraph_vector_int_fill(&lightest_eid, -1); + + // A mapping vector which indicated the minimum weight to each neighboring + // cluster, for a single vertex of interest. + // Preconditions needed by igraph_i_collect_lightest_edges_to_clusters() + // are enforced here. + IGRAPH_VECTOR_INIT_FINALLY(&lightest_weight, no_of_nodes); + igraph_vector_fill(&lightest_weight, IGRAPH_INFINITY); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&new_clustering, no_of_nodes); + + // A boolean vector whose i-th element is 1 if the i-th vertex is a cluster + // center that is sampled in the current iteration, 0 otherwise + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&is_cluster_sampled, no_of_nodes); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&is_edge_in_spanner, no_of_edges); + + // Temporary vector used by igraph_i_collect_lightest_edges_to_clusters() + // to keep track of the nodes that it has written to + IGRAPH_VECTOR_INT_INIT_FINALLY(&dirty_vids, 0); + + sample_prob = pow(no_of_nodes, -1 / k); + +#define ADD_EDGE_TO_SPANNER \ + if (!VECTOR(is_edge_in_spanner)[edge]) { \ + VECTOR(is_edge_in_spanner)[edge] = true; \ + IGRAPH_CHECK(igraph_vector_int_push_back(spanner, edge)); \ + } + + igraph_vector_fill(&lightest_weight, INFINITY); + + for (i = 0; i < k - 1; i++) { + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_vector_int_fill(&new_clustering, -1); + igraph_vector_bool_fill(&is_cluster_sampled, false); + + // Step 1: sample cluster centers + RNG_BEGIN(); + for (j = 0; j < no_of_nodes; j++) { + if (VECTOR(clustering)[j] == j && RNG_UNIF01() < sample_prob) { + VECTOR(is_cluster_sampled)[j] = true; + } + } + RNG_END(); + + // Step 2 and 3 + for (v = 0; v < no_of_nodes; v++) { + // If v is inside a cluster and the cluster of v is sampled, then continue + cluster = VECTOR(clustering)[v]; + if (cluster != -1 && VECTOR(is_cluster_sampled)[cluster]) { + VECTOR(new_clustering)[v] = cluster; + continue; + } + + // Step 2: find the lightest edge that connects vertex v to its + // neighboring sampled clusters + igraph_integer_t nearest_neighboring_sampled_cluster = -1; + IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( + &adjlist, + &inclist, + weights, + &clustering, + &is_cluster_sampled, + v, + &lightest_eid, + &lightest_weight, + &dirty_vids, + &nearest_neighboring_sampled_cluster + )); + + // Step 3: add edges to spanner + if (nearest_neighboring_sampled_cluster == -1) { + // Case 3(a) from the paper: v is not adjacent to any of the + // sampled clusters. + + // Add lightest edge which connects vertex v to each neighboring + // cluster (none of which are sampled) + for (j = 0; j < no_of_nodes; j++) { + edge = VECTOR(lightest_eid)[j]; + if (edge != -1) { + ADD_EDGE_TO_SPANNER; + } + } + + // Remove all edges incident on v from the graph. Note that each + // edge being removed occurs twice in the adjacency / incidence + // lists + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + if (neighbor == v) { + /* should not happen as we did not ask for loop edges in + * the adjacency / incidence lists, but let's be defensive */ + continue; + } + + if (igraph_vector_int_search( + igraph_inclist_get(&inclist, neighbor), + 0, + VECTOR(*incident_edges)[j], + &index + )) { + igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); + igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); + } + } + igraph_vector_int_clear(adjacent_vertices); + igraph_vector_int_clear(incident_edges); + } else { + // Case 3(b) from the paper: v is adjacent to at least one of + // the sampled clusters + + // add the edge connecting to the lightest sampled cluster + edge = VECTOR(lightest_eid)[nearest_neighboring_sampled_cluster]; + ADD_EDGE_TO_SPANNER; + + // 'lightest_sampled_weight' is the weight of the lightest edge connecting v to + // one of the sampled clusters. This is where v will belong in + // the new clustering. + lightest_sampled_weight = VECTOR(lightest_weight)[nearest_neighboring_sampled_cluster]; + VECTOR(new_clustering)[v] = nearest_neighboring_sampled_cluster; + + // Add to the spanner light edges with weight less than 'lightest_sampled_weight' + for (j = 0; j < no_of_nodes; j++) { + if (VECTOR(lightest_weight)[j] < lightest_sampled_weight) { + edge = VECTOR(lightest_eid)[j]; + ADD_EDGE_TO_SPANNER; + } + } + + // Remove edges to centers with edge weight less than 'lightest_sampled_weight' + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + if (neighbor == v) { + /* should not happen as we did not ask for loop edges in + * the adjacency / incidence lists, but let's be defensive */ + continue; + } + + cluster = VECTOR(clustering)[neighbor]; + weight = VECTOR(lightest_weight)[cluster]; + if ((cluster == nearest_neighboring_sampled_cluster) || (weight < lightest_sampled_weight)) { + edge = VECTOR(*incident_edges)[j]; + + if (igraph_vector_int_search( + igraph_inclist_get(&inclist, neighbor), 0, edge, &index + )) { + igraph_vector_int_remove_fast(igraph_adjlist_get(&adjlist, neighbor), index); + igraph_vector_int_remove_fast(igraph_inclist_get(&inclist, neighbor), index); + } + + igraph_vector_int_remove_fast(adjacent_vertices, j); + igraph_vector_int_remove_fast(incident_edges, j); + + j--; + nlen--; + } + } + } + + // We don't need lightest_eids and lightest_weights any more so + // clear them in O(d) time + igraph_i_clear_lightest_edges_to_clusters( + &dirty_vids, &lightest_eid, &lightest_weight + ); + } + + // Commit the new clustering + igraph_vector_int_update(&clustering, &new_clustering); + + // Remove intra-cluster edges + for (v = 0; v < no_of_nodes; v++) { + adjacent_vertices = igraph_adjlist_get(&adjlist, v); + incident_edges = igraph_inclist_get(&inclist, v); + nlen = igraph_vector_int_size(incident_edges); + for (j = 0; j < nlen; j++) { + neighbor = VECTOR(*adjacent_vertices)[j]; + edge = VECTOR(*incident_edges)[j]; + + if (VECTOR(clustering)[neighbor] == VECTOR(clustering)[v]) { + /* We don't need to bother with removing the other copy + * of the edge from the incidence lists (and the corresponding + * vertices from the adjacency lists) because we will find + * them anyway as we are iterating over all nodes */ + igraph_vector_int_remove_fast(adjacent_vertices, j); + igraph_vector_int_remove_fast(incident_edges, j); + j--; + nlen--; + } + } + } + } + + // Phase 2: vertex_clustering joining + for (v = 0; v < no_of_nodes; v++) { + if (VECTOR(clustering)[v] != -1) { + IGRAPH_CHECK(igraph_i_collect_lightest_edges_to_clusters( + &adjlist, + &inclist, + weights, + &clustering, + /* is_cluster_sampled = */ NULL, + v, + &lightest_eid, + &lightest_weight, + &dirty_vids, + NULL + )); + for (j = 0; j < no_of_nodes; j++) { + edge = VECTOR(lightest_eid)[j]; + if (edge != -1) { + ADD_EDGE_TO_SPANNER; + } + } + igraph_i_clear_lightest_edges_to_clusters(&dirty_vids, &lightest_eid, &lightest_weight); + } + } + + // Free memory + igraph_vector_int_destroy(&dirty_vids); + igraph_vector_bool_destroy(&is_edge_in_spanner); + igraph_vector_bool_destroy(&is_cluster_sampled); + igraph_vector_int_destroy(&new_clustering); + igraph_vector_destroy(&lightest_weight); + igraph_vector_int_destroy(&lightest_eid); + igraph_vector_int_destroy(&clustering); + igraph_adjlist_destroy(&adjlist); + igraph_inclist_destroy(&inclist); + IGRAPH_FINALLY_CLEAN(9); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/paths/unweighted.c b/src/vendor/cigraph/src/paths/unweighted.c index 60503b543f2..976af966a4b 100644 --- a/src/vendor/cigraph/src/paths/unweighted.c +++ b/src/vendor/cigraph/src/paths/unweighted.c @@ -29,19 +29,24 @@ /** * \ingroup structural - * \function igraph_shortest_paths - * \brief The length of the shortest paths between vertices. + * \function igraph_distances_cutoff + * \brief Length of the shortest paths between vertices, with cutoff. + * + * \experimental + * + * This function is similar to \ref igraph_distances(), but + * paths longer than \p cutoff will not be considered. * * \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 + * number of rows as the length of the \p 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. + * vertices in the \p to argument. One row of the matrix shows the + * distances from/to a given vertex to the ones in \p to. + * For the unreachable vertices \c IGRAPH_INFINITY is returned. + * \param from The source vertices._d * \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 @@ -55,46 +60,48 @@ * the directed graph is considered as an undirected one for * the computation. * \endclist + * \param cutoff The maximal length of paths that will be considered. + * When the distance of two vertices is greater than this value, + * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs are + * treated as infinity. * \return Error code: * \clist * \cli IGRAPH_ENOMEM * not enough memory for temporary * data. * \cli IGRAPH_EINVVID - * invalid vertex id passed. + * 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. + * Time complexity: O(s |E| + |V|), where s is the number of source vertices to use, + * and |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. + * \sa \ref igraph_distances_dijkstra_cutoff() for the weighted version with non-negative + * weights. + * + * \example examples/simple/distances.c */ -int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, +igraph_error_t igraph_distances_cutoff(const igraph_t *graph, igraph_matrix_t *res, const igraph_vs_t from, const igraph_vs_t to, - igraph_neimode_t mode) { + igraph_neimode_t mode, igraph_real_t cutoff) { - long int no_of_nodes = igraph_vcount(graph); - long int no_of_from, no_of_to; - long int *already_counted; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_from, no_of_to; + igraph_integer_t *already_counted; igraph_adjlist_t adjlist; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; igraph_vector_int_t *neis; igraph_bool_t all_to; - long int i, j; + igraph_integer_t i, j; igraph_vit_t fromvit, tovit; - igraph_real_t my_infinity = IGRAPH_INFINITY; - igraph_vector_t indexv; + igraph_vector_int_t indexv; if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); } IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); @@ -104,25 +111,24 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, 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); - } + already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for graph distance calculation."); IGRAPH_FINALLY(igraph_free, already_counted); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_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_VECTOR_INT_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); + igraph_integer_t v = IGRAPH_VIT_GET(tovit); if (VECTOR(indexv)[v]) { - IGRAPH_ERROR("Duplicate vertices in `to', this is not allowed", + IGRAPH_ERROR("Target vertex list must not have any duplicates.", IGRAPH_EINVAL); } VECTOR(indexv)[v] = ++i; @@ -130,45 +136,49 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, } IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); - igraph_matrix_fill(res, my_infinity); + igraph_matrix_fill(res, IGRAPH_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_integer_t reached = 0; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(fromvit))); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + already_counted[ 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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t act = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + + if (cutoff >= 0 && actdist > cutoff) { + continue; + } if (all_to) { MATRIX(*res, i, act) = actdist; } else { if (VECTOR(indexv)[act]) { - MATRIX(*res, i, (long int)(VECTOR(indexv)[act] - 1)) = actdist; + MATRIX(*res, i, VECTOR(indexv)[act] - 1) = actdist; reached++; if (reached == no_of_to) { - igraph_dqueue_clear(&q); + igraph_dqueue_int_clear(&q); break; } } } neis = igraph_adjlist_get(&adjlist, act); - long int nei_count = igraph_vector_int_size(neis); + igraph_integer_t nei_count = igraph_vector_int_size(neis); for (j = 0; j < nei_count; j++) { - long int neighbor = (long int) VECTOR(*neis)[j]; + igraph_integer_t neighbor = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); } } } @@ -176,17 +186,87 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, /* Clean */ if (!all_to) { igraph_vit_destroy(&tovit); - igraph_vector_destroy(&indexv); + igraph_vector_int_destroy(&indexv); IGRAPH_FINALLY_CLEAN(2); } IGRAPH_FREE(already_counted); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); igraph_vit_destroy(&fromvit); igraph_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_distances + * \brief 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 \p from + * argument, and its number of columns is the number of + * vertices in the \p to argument. One row of the matrix shows the + * distances from/to a given vertex to the ones in \p to. + * For the unreachable vertices \c 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_distances_dijkstra() for the weighted version with non-negative + * weights, \ref igraph_distances_bellman_ford() if you also have negative + * weights. + * + * \example examples/simple/distances.c + */ +igraph_error_t igraph_distances(const igraph_t *graph, igraph_matrix_t *res, + const igraph_vs_t from, const igraph_vs_t to, + igraph_neimode_t mode) { + return igraph_distances_cutoff(graph, res, from, to, mode, -1); +} + +/** + * \function igraph_shortest_paths + * \brief Length of the shortest paths between vertices. + * + * \deprecated-by igraph_distances 0.10.0 + */ +igraph_error_t 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) { + return igraph_distances(graph, res, from, to, mode); } /** @@ -198,23 +278,17 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, * 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 vertices The result, the IDs of the vertices along the paths. + * This is a list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 + * \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 @@ -228,12 +302,12 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, * 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 + * \param parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent 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 + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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. @@ -251,8 +325,7 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, * \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. + * \p from is invalid vertex ID * \cli IGRAPH_EINVMODE * invalid mode argument. * \endclist @@ -262,105 +335,104 @@ int igraph_shortest_paths(const igraph_t *graph, igraph_matrix_t *res, * |E| the number of edges in the * graph. * - * \sa \ref igraph_shortest_paths() if you only need the path length but + * \sa \ref igraph_distances() if you only need the path lengths 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_error_t igraph_get_shortest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_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) { + igraph_vector_int_t *parents, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t *parent_eids; - igraph_dqueue_t q = IGRAPH_DQUEUE_NULL; + igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - long int i, j, vsize; - igraph_vector_t tmp = IGRAPH_VECTOR_NULL; + igraph_integer_t i, j, vsize; + igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; igraph_vit_t vit; - long int to_reach; - long int reached = 0; + igraph_integer_t to_reach; + igraph_integer_t reached = 0; if (from < 0 || from >= no_of_nodes) { IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); } if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE); + 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 (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); } - if (edges && IGRAPH_VIT_SIZE(vit) != igraph_vector_ptr_size(edges)) { - IGRAPH_ERROR("Size of the `edges' and the `to' should match", IGRAPH_EINVAL); + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); } - 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); + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest path calculation."); + IGRAPH_FINALLY(igraph_free, parent_eids); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_DQUEUE_INT_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; + if (parent_eids[ IGRAPH_VIT_GET(vit) ] == 0) { + parent_eids[ IGRAPH_VIT_GET(vit) ] = -1; } else { to_reach--; /* this node was given multiple times */ } } - /* Meaning of father[i]: + /* Meaning of parent_eids[i]: * - * - If father[i] < 0, it means that vertex i has to be reached and has not + * - If parent_eids[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 + * - If parent_eids[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. + * - If parent_eids[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 + * - Otherwise, parent_eids[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) { + IGRAPH_CHECK(igraph_dqueue_int_push(&q, from + 1)); + if (parent_eids[ from ] < 0) { reached++; } - father[ (long int)from ] = 1; + parent_eids[ from ] = 1; - while (!igraph_dqueue_empty(&q) && reached < to_reach) { - long int act = (long int) igraph_dqueue_pop(&q) - 1; + while (!igraph_dqueue_int_empty(&q) && reached < to_reach) { + igraph_integer_t act = igraph_dqueue_int_pop(&q) - 1; - IGRAPH_CHECK(igraph_incident(graph, &tmp, (igraph_integer_t) act, mode)); - vsize = igraph_vector_size(&tmp); + IGRAPH_CHECK(igraph_incident(graph, &tmp, act, mode)); + vsize = igraph_vector_int_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) { + igraph_integer_t edge = VECTOR(tmp)[j]; + igraph_integer_t neighbor = IGRAPH_OTHER(graph, edge, act); + if (parent_eids[neighbor] > 0) { continue; - } else if (father[neighbor] < 0) { + } else if (parent_eids[neighbor] < 0) { reached++; } - father[neighbor] = edge + 2; - IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor + 1)); + parent_eids[neighbor] = edge + 2; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor + 1)); } } @@ -368,35 +440,35 @@ int igraph_get_shortest_paths(const igraph_t *graph, IGRAPH_WARNING("Couldn't reach some vertices"); } - /* Create `predecessors' if needed */ - if (predecessors) { - IGRAPH_CHECK(igraph_vector_long_resize(predecessors, no_of_nodes)); + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (father[i] <= 0) { + if (parent_eids[i] <= 0) { /* i was not reached */ - VECTOR(*predecessors)[i] = -1; - } else if (father[i] == 1) { + VECTOR(*parents)[i] = -2; + } else if (parent_eids[i] == 1) { /* i is the start vertex */ - VECTOR(*predecessors)[i] = i; + VECTOR(*parents)[i] = -1; } else { - /* i was reached via the edge with ID = father[i] - 2 */ - VECTOR(*predecessors)[i] = IGRAPH_OTHER(graph, father[i] - 2, i); + /* i was reached via the edge with ID = parent_eids[i] - 2 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 2, i); } } } /* Create `inbound_edges' if needed */ if (inbound_edges) { - IGRAPH_CHECK(igraph_vector_long_resize(inbound_edges, no_of_nodes)); + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); for (i = 0; i < no_of_nodes; i++) { - if (father[i] <= 1) { + if (parent_eids[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; + /* i was reached via the edge with ID = parent_eids[i] - 2 */ + VECTOR(*inbound_edges)[i] = parent_eids[i] - 2; } } } @@ -406,39 +478,39 @@ int igraph_get_shortest_paths(const igraph_t *graph, 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; + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_vector_int_t *vvec = 0, *evec = 0; if (vertices) { - vvec = VECTOR(*vertices)[j]; - igraph_vector_clear(vvec); + vvec = igraph_vector_int_list_get_ptr(vertices, j); + igraph_vector_int_clear(vvec); } if (edges) { - evec = VECTOR(*edges)[j]; - igraph_vector_clear(evec); + evec = igraph_vector_int_list_get_ptr(edges, j); + igraph_vector_int_clear(evec); } IGRAPH_ALLOW_INTERRUPTION(); - if (father[node] > 0) { - long int act = node; - long int size = 0; - long int edge; - while (father[act] > 1) { + if (parent_eids[node] > 0) { + igraph_integer_t act = node; + igraph_integer_t size = 0; + igraph_integer_t edge; + while (parent_eids[act] > 1) { size++; - edge = father[act] - 2; + edge = parent_eids[act] - 2; act = IGRAPH_OTHER(graph, edge, act); } if (vvec) { - IGRAPH_CHECK(igraph_vector_resize(vvec, size + 1)); + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); VECTOR(*vvec)[size] = node; } if (evec) { - IGRAPH_CHECK(igraph_vector_resize(evec, size)); + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); } act = node; - while (father[act] > 1) { + while (parent_eids[act] > 1) { size--; - edge = father[act] - 2; + edge = parent_eids[act] - 2; act = IGRAPH_OTHER(graph, edge, act); if (vvec) { VECTOR(*vvec)[size] = act; @@ -452,13 +524,13 @@ int igraph_get_shortest_paths(const igraph_t *graph, } /* Clean */ - IGRAPH_FREE(father); - igraph_dqueue_destroy(&q); - igraph_vector_destroy(&tmp); + IGRAPH_FREE(parent_eids); + igraph_dqueue_int_destroy(&q); + igraph_vector_int_destroy(&tmp); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(4); - return 0; + return IGRAPH_SUCCESS; } /** @@ -466,24 +538,25 @@ int igraph_get_shortest_paths(const igraph_t *graph, * \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. + * given vertex to another one. If there is more than one shortest + * path 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. * - * 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 + * 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 + * \param edges Pointer to an initialized 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 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; @@ -499,42 +572,44 @@ int igraph_get_shortest_paths(const igraph_t *graph, * vertices. */ -int igraph_get_shortest_path(const igraph_t *graph, - igraph_vector_t *vertices, - igraph_vector_t *edges, +igraph_error_t igraph_get_shortest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_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; + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_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; + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); } else { - vp = 0; + vp = NULL; } if (edges) { - IGRAPH_CHECK(igraph_vector_ptr_init(&edges2, 1)); - IGRAPH_FINALLY(igraph_vector_ptr_destroy, &edges2); - VECTOR(edges2)[0] = edges; + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); } else { - ep = 0; + ep = NULL; } IGRAPH_CHECK(igraph_get_shortest_paths(graph, vp, ep, from, - igraph_vss_1(to), mode, 0, 0)); + igraph_vss_1(to), mode, NULL, NULL)); + /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the + result to the output parameter. */ if (edges) { - igraph_vector_ptr_destroy(&edges2); + IGRAPH_CHECK(igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); IGRAPH_FINALLY_CLEAN(1); } if (vertices) { - igraph_vector_ptr_destroy(&vertices2); + IGRAPH_CHECK(igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/paths/voronoi.c b/src/vendor/cigraph/src/paths/voronoi.c new file mode 100644 index 00000000000..1af802c1b56 --- /dev/null +++ b/src/vendor/cigraph/src/paths/voronoi.c @@ -0,0 +1,425 @@ +/* + 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, see . +*/ + +#include "igraph_paths.h" + +#include "igraph_adjlist.h" +#include "igraph_dqueue.h" +#include "igraph_interface.h" +#include "igraph_nongraph.h" +#include "igraph_random.h" + +#include "core/indheap.h" +#include "core/interruption.h" + +static igraph_error_t igraph_i_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *mindist, + const igraph_vector_int_t *generators, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_generators = igraph_vector_int_size(generators); + igraph_adjlist_t al; + igraph_dqueue_int_t q; + + igraph_vector_int_t already_counted; + + /* tie_count[vid] is the number of generators that vid is an equal distance from. + * This value is needed to pick one of these generators uniformly at random + * without needing to store all of them. */ + igraph_vector_int_t tie_count; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&already_counted, no_of_nodes); + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); + RNG_BEGIN(); + } + + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, -1); + + IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); + igraph_vector_fill(mindist, IGRAPH_INFINITY); + + /* Loop through all generator points and compute shortest paths to all other vertices. + * As we go, we keep track of the shortest distance from any generator to each vertex + * in 'mindist'. If we find that the distance from the current generator to a vertex + * is shorter than what was recorded so far in 'mindist', we update 'mindist' and + * assign that vertex to the current generator. + */ + for (igraph_integer_t i=0; i < no_of_generators; i++) { + igraph_integer_t g = VECTOR(*generators)[i]; + + IGRAPH_ALLOW_INTERRUPTION(); + + /* BFS-based unweighted shortest path implementation */ + + igraph_dqueue_int_clear(&q); + + VECTOR(already_counted)[g] = i+1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, g)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t vid = igraph_dqueue_int_pop(&q); + igraph_integer_t dist = igraph_dqueue_int_pop(&q); + + /* Attention! This must be igraph_real_t, not igraph_integer_t + * because later it will be compared with another igraph_real_t + * whose value may be infinite. */ + igraph_real_t md = VECTOR(*mindist)[vid]; + + if (dist > md) { + /* This vertex is reachable at a shorter distance from + * another generator. Thus all its descendants in the shortest + * path tree are also reachable at a shorter distance from the + * other generator than from the current one. Therefore + * we do not need to search further from here. */ + continue; + } else if (dist < md) { + /* This vertex is closest to the current generator so far. + * Assign it to the current partition. */ + VECTOR(*mindist)[vid] = dist; + VECTOR(*membership)[vid] = i; + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + VECTOR(tie_count)[vid] = 1; + } + } else { /* md == dist, we have a tie */ + switch (tiebreaker) { + case IGRAPH_VORONOI_FIRST: + /* Never replace existing generator assignment. */ + break; + case IGRAPH_VORONOI_LAST: + /* Always replace existing generator assignment. */ + VECTOR(*membership)[vid] = i; + break; + case IGRAPH_VORONOI_RANDOM: + /* We replace the membership assignment with probability 1/k upon + * encountering the kth same-distance generator. This ensures + * that one of these generators is selected uniformly at random. */ + VECTOR(tie_count)[vid]++; + if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { + VECTOR(*membership)[vid] = i; + } + break; + } + } + + igraph_vector_int_t *neis = igraph_adjlist_get(&al, vid); + igraph_integer_t nei_count = igraph_vector_int_size(neis); + for (igraph_integer_t j = 0; j < nei_count; j++) { + igraph_integer_t neighbor = VECTOR(*neis)[j]; + if (VECTOR(already_counted)[neighbor] == i + 1) { + continue; + } + VECTOR(already_counted)[neighbor] = i + 1; + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, dist + 1)); + } + } + } + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + RNG_END(); + igraph_vector_int_destroy(&tie_count); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_vector_int_destroy(&already_counted); + igraph_dqueue_int_destroy(&q); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +static igraph_error_t igraph_i_voronoi_dijkstra( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *mindist, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_generators = igraph_vector_int_size(generators); + igraph_inclist_t il; + igraph_2wheap_t q; + + /* tie_count[vid] is the number of generators that vid is an equal distance from. + * We use this value to be able to randomly select one of the generators. */ + igraph_vector_int_t tie_count; + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + 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 (isnan(min)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + } + + IGRAPH_CHECK(igraph_inclist_init(graph, &il, mode, IGRAPH_LOOPS)); + IGRAPH_FINALLY(igraph_inclist_destroy, &il); + + IGRAPH_CHECK(igraph_2wheap_init(&q, no_of_nodes)); + IGRAPH_FINALLY(igraph_2wheap_destroy, &q); + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + IGRAPH_VECTOR_INT_INIT_FINALLY(&tie_count, no_of_nodes); + RNG_BEGIN(); + } + + IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); + igraph_vector_int_fill(membership, -1); + + IGRAPH_CHECK(igraph_vector_resize(mindist, no_of_nodes)); + igraph_vector_fill(mindist, IGRAPH_INFINITY); + + /* Loop through all generator points and compute shortest paths to all other vertices. + * As we go, we keep track of the shortest distance from any generator to each vertex + * in 'mindist'. If we find that the distance from the current generator to a vertex + * is shorter than what was recorded so far in 'mindist', we update 'mindist' and + * assign that vertex to the current generator. + */ + for (igraph_integer_t i=0; i < no_of_generators; i++) { + igraph_integer_t g = VECTOR(*generators)[i]; + + /* Weighted shortest path implementation using Dijkstra's algorithm */ + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_2wheap_clear(&q); + + /* We store negative distances in the maximum heap. Since some systems + * distinguish between -0.0 and +0.0, we need -0.0 to ensure +0.0 as + * the final result. */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, g, -0.0)); + + while (!igraph_2wheap_empty(&q)) { + igraph_integer_t vid = igraph_2wheap_max_index(&q); + igraph_real_t dist = -igraph_2wheap_deactivate_max(&q); + + igraph_real_t md = VECTOR(*mindist)[vid]; + + int cmp_result = igraph_cmp_epsilon(dist, md, IGRAPH_SHORTEST_PATH_EPSILON); + if (cmp_result > 0) { /* dist > md */ + /* This vertex is reachable at a shorter distance from + * another generator. Thus all its descendants in the shortest + * path tree are also reachable at a shorter distance from the + * other generator than from the current one. Therefore + * we do not need to search further from here. */ + continue; + } else if (cmp_result < 0) { /* dist < md */ + /* This vertex is closest to the current generator so far. + * Assign it to the current partition. */ + VECTOR(*mindist)[vid] = dist; + VECTOR(*membership)[vid] = i; + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + VECTOR(tie_count)[vid] = 1; + } + } else { /* md == dist, we have a tie */ + switch (tiebreaker) { + case IGRAPH_VORONOI_FIRST: + /* Never replace existing generator assignment. */ + break; + case IGRAPH_VORONOI_LAST: + /* Always replace existing generator assignment. */ + VECTOR(*membership)[vid] = i; + break; + case IGRAPH_VORONOI_RANDOM: + /* We replace the membership assignment with probability 1/k upon + * encountering the kth same-distance generator. This ensures + * that one of these generators is selected uniformly at random. */ + VECTOR(tie_count)[vid]++; + if (RNG_UNIF01() < 1.0 / VECTOR(tie_count)[vid]) { + VECTOR(*membership)[vid] = i; + } + break; + } + } + + igraph_vector_int_t *inc_edges = igraph_inclist_get(&il, vid); + igraph_integer_t inc_count = igraph_vector_int_size(inc_edges); + for (igraph_integer_t j=0; j < inc_count; j++) { + igraph_integer_t edge = VECTOR(*inc_edges)[j]; + igraph_real_t weight = VECTOR(*weights)[edge]; + + /* Optimization: do not follow infinite-weight edges. */ + if (weight == IGRAPH_INFINITY) { + continue; + } + + igraph_integer_t to = IGRAPH_OTHER(graph, edge, vid); + igraph_real_t altdist = dist + weight; + + if (! igraph_2wheap_has_elem(&q, to)) { + /* This is the first non-infinite distance */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&q, to, -altdist)); + } else if (igraph_2wheap_has_active(&q, to)) { + igraph_real_t curdist = -igraph_2wheap_get(&q, to); + if (altdist < curdist) { + /* This is a shorter path */ + igraph_2wheap_modify(&q, to, -altdist); + } + } + } + } /* !igraph_2wheap_empty(&q) */ + } + + if (tiebreaker == IGRAPH_VORONOI_RANDOM) { + RNG_END(); + igraph_vector_int_destroy(&tie_count); + IGRAPH_FINALLY_CLEAN(1); + } + + igraph_2wheap_destroy(&q); + igraph_inclist_destroy(&il); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_voronoi + * \brief Voronoi partitioning of a graph. + * + * \experimental + * + * To obtain a Voronoi partitioning of a graph, we start with a set of generator + * vertices, which will define the partitions. Each vertex is assigned to the generator + * vertex from (or to) which it is closest. + * + * + * This function uses a BFS search for unweighted graphs and Dijkstra's algorithm + * for weights ones. + * + * \param graph The graph to partition. + * \param membership If not \c NULL, the Voronoi partition of each vertex + * will be stored here. membership[v] will be set to the index + * in \p generators of the generator vertex that \c v belongs to. For vertices + * that are not reachable from any generator, -1 is returned. + * \param distances If not \c NULL, the distance of each vertex to its respective + * generator will be stored here. For vertices which are not reachable from + * any generator, \c IGRAPH_INFINITY is returned. + * \param generators Vertex IDs of the generator vertices. + * \param weights The edge weights, interpreted as lengths in the shortest + * path calculation. All weights must be non-negative. + * \param mode In directed graphs, whether to compute distances \em from + * generator vertices to other vertices (\c IGRAPH_OUT), \em to generator + * vertices from other vertices (\c IGRAPH_IN), or ignore edge directions + * entirely (\c IGRAPH_ALL). + * \param tiebreaker Controls which generator vertex to assign a vertex to + * when it is at equal distance from/to multiple generator vertices. + * \clist + * \cli IGRAPH_VORONOI_FIRST assign the vertex to the first generator vertex. + * \cli IGRAPH_VORONOI_LAST assign the vertex to the last generator vertex. + * \cli IGRAPH_VORONOI_RANDOM assign the vertex to a random generator vertex. + * \endclist + * Note that \c IGRAPH_VORONOI_RANDOM does not guarantee that all partitions + * will be contiguous. For example, if 1 and 2 are chosen as generators for the + * graph 1-3, 2-3, 3-4, then 3 and 4 are at equal distance from + * both generators. If 3 is assigned to 2 but 4 is assigned to 1, then the + * partition {1, 4} will not induce a connected subgraph. + * \return Error code. + * + * Time complexity: In weighted graphs, O((log s) |E| log |V| + |V|), and in + * unweighted graphs O((log s) |E| + |V|), where s is the number of generator + * vertices and |V| and |E| are the number of vertices and edges in the graph. + * + * \sa \ref igraph_distances(), \ref igraph_distances_dijkstra(). + */ +igraph_error_t igraph_voronoi( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_t *distances, + const igraph_vector_int_t *generators, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_voronoi_tiebreaker_t tiebreaker) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t *pmembership; + igraph_vector_int_t imembership; + igraph_vector_t *pdistances; + igraph_vector_t idistances; + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (tiebreaker != IGRAPH_VORONOI_FIRST && + tiebreaker != IGRAPH_VORONOI_LAST && + tiebreaker != IGRAPH_VORONOI_RANDOM) { + IGRAPH_ERROR("Invalid tiebreaker specification during Voronoi partitioning.", IGRAPH_EINVAL); + } + + if (! igraph_vector_int_isininterval(generators, 0, igraph_vcount(graph)-1)) { + IGRAPH_ERROR("Invalid vertex ID given as Voronoi generator.", IGRAPH_EINVVID); + } + + if (membership) { + pmembership = membership; + } else { + IGRAPH_VECTOR_INT_INIT_FINALLY(&imembership, no_of_nodes); + pmembership = &imembership; + } + + if (distances) { + pdistances = distances; + } else { + IGRAPH_VECTOR_INIT_FINALLY(&idistances, no_of_nodes); + pdistances = &idistances; + } + + if (weights) { + IGRAPH_CHECK(igraph_i_voronoi_dijkstra(graph, pmembership, pdistances, generators, weights, mode, tiebreaker)); + } else { + IGRAPH_CHECK(igraph_i_voronoi(graph, pmembership, pdistances, generators, mode, tiebreaker)); + } + + if (! distances) { + igraph_vector_destroy(&idistances); + IGRAPH_FINALLY_CLEAN(1); + } + + if (! membership) { + igraph_vector_int_destroy(&imembership); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/paths/widest_paths.c b/src/vendor/cigraph/src/paths/widest_paths.c new file mode 100644 index 00000000000..b2c946d0102 --- /dev/null +++ b/src/vendor/cigraph/src/paths/widest_paths.c @@ -0,0 +1,736 @@ +/* -*- 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_interface.h" +#include "igraph_memory.h" + +#include "core/indheap.h" +#include "core/interruption.h" +#include "internal/utils.h" + +/** + * \function igraph_get_widest_paths + * \brief Widest paths from a single vertex. + * + * Calculates the widest paths from a single node to all other specified nodes, + * using a modified Dijkstra's algorithm. If there is more than one path with + * the largest width 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * 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 list of integer vectors where each element is an + * \ref igraph_vector_int_t object. The list will be resized as needed. + * Supply a null pointer here if you don't need these vectors. + * \param from The id of the vertex from/to which the widest paths are + * calculated. + * \param to Vertex sequence with the IDs of the vertices to/from which the + * widest paths will be calculated. A vertex might be given multiple + * times. + * \param weights The edge weights. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \param mode The type of widest 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 parents A pointer to an initialized igraph vector or null. + * If not null, a vector containing the parent of each vertex in + * the single source widest path tree is returned here. The + * parent of vertex i in the tree is the vertex from which vertex i + * was reached. The parent of the start vertex (in the \c from + * argument) is -1. If the parent is -2, 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 widest 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 + * \cli IGRAPH_EINVMODE + * invalid mode argument. + * \endclist + * + * Time complexity: O(|E|log|E|+|V|), where |V| is the number of + * vertices in the graph and |E| is the number of edges + * + * \sa \ref igraph_widest_path_widths_dijkstra() or + * \ref igraph_widest_path_widths_floyd_warshall() if you only need the + * widths of the paths but not the paths themselves. + */ +igraph_error_t igraph_get_widest_paths(const igraph_t *graph, + igraph_vector_int_list_t *vertices, + igraph_vector_int_list_t *edges, + igraph_integer_t from, + igraph_vs_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode, + igraph_vector_int_t *parents, + igraph_vector_int_t *inbound_edges) { + + /* Implementation details: This is a Dijkstra algorithm with a + binary heap, modified to support widest paths. The heap is indexed, + so it stores both the widest path to a node, as well as it's index. We + use a 2 way heap so that we can query indexes directly in the heap. + + To adapt a Dijkstra to handle widest path, instead of prioritising candidate + nodes with the minimum distance, we prioritise those with the maximum + width instead. When adding a node into our set of 'completed' nodes, we + update all neighbouring nodes with a width that is equal to the min of the + width to the current node and the width of the edge. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_real_t my_posinfinity = IGRAPH_POSINFINITY; + igraph_real_t my_neginfinity = IGRAPH_NEGINFINITY; + igraph_vit_t vit; + igraph_2wheap_t Q; + igraph_lazy_inclist_t inclist; + igraph_vector_t widths; + igraph_integer_t *parent_eids; + igraph_bool_t *is_target; + igraph_integer_t i, to_reach; + + if (!weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (igraph_vector_is_any_nan(weights)) { + 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_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_resize(edges, 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(&widths, no_of_nodes); + igraph_vector_fill(&widths, my_neginfinity); + + parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + if (parent_eids == 0) { + IGRAPH_ERROR("Can't calculate widest paths.", IGRAPH_ENOMEM); + } + IGRAPH_FINALLY(igraph_free, parent_eids); + is_target = IGRAPH_CALLOC(no_of_nodes, igraph_bool_t); + if (is_target == 0) { + IGRAPH_ERROR("Can't calculate widest 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[ IGRAPH_VIT_GET(vit) ]) { + is_target[ IGRAPH_VIT_GET(vit) ] = 1; + } else { + to_reach--; /* this node was given multiple times */ + } + } + + VECTOR(widths)[from] = my_posinfinity; + parent_eids[from] = 0; + igraph_2wheap_push_with_index(&Q, from, my_posinfinity); + + while (!igraph_2wheap_empty(&Q) && to_reach > 0) { + igraph_integer_t nlen, maxnei = igraph_2wheap_max_index(&Q); + igraph_real_t maxwidth = igraph_2wheap_delete_max(&Q); + igraph_vector_int_t *neis; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (is_target[maxnei]) { + is_target[maxnei] = 0; + to_reach--; + } + + /* Now check all neighbors of 'maxnei' for a wider path */ + neis = igraph_lazy_inclist_get(&inclist, maxnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (i = 0; i < nlen; i++) { + igraph_integer_t edge = VECTOR(*neis)[i]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); + igraph_real_t edgewidth = VECTOR(*weights)[edge]; + igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; + igraph_real_t curwidth = VECTOR(widths)[tto]; + if (edgewidth == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + } else if (curwidth < 0) { + /* This is the first assigning a width to this vertex */ + VECTOR(widths)[tto] = altwidth; + parent_eids[tto] = edge + 1; + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); + } else if (altwidth > curwidth) { + /* This is a wider path */ + VECTOR(widths)[tto] = altwidth; + parent_eids[tto] = edge + 1; + igraph_2wheap_modify(&Q, tto, altwidth); + } + } + } /* !igraph_2wheap_empty(&Q) */ + + + if (to_reach > 0) { + IGRAPH_WARNING("Couldn't reach some vertices."); + } + + /* Create `parents' if needed */ + if (parents) { + IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (i == from) { + /* i is the start vertex */ + VECTOR(*parents)[i] = -1; + } else if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*parents)[i] = -2; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 1, i); + } + } + } + + /* Create `inbound_edges' if needed */ + if (inbound_edges) { + IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); + + for (i = 0; i < no_of_nodes; i++) { + if (parent_eids[i] <= 0) { + /* i was not reached */ + VECTOR(*inbound_edges)[i] = -1; + } else { + /* i was reached via the edge with ID = parent_eids[i] - 1 */ + VECTOR(*inbound_edges)[i] = parent_eids[i] - 1; + } + } + } + /* Reconstruct the widest 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++) { + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size, act, edge; + igraph_vector_int_t *vvec = 0, *evec = 0; + + if (vertices) { + vvec = igraph_vector_int_list_get_ptr(vertices, i); + igraph_vector_int_clear(vvec); + } + if (edges) { + evec = igraph_vector_int_list_get_ptr(edges, i); + igraph_vector_int_clear(evec); + } + + IGRAPH_ALLOW_INTERRUPTION(); + + size = 0; + act = node; + while (parent_eids[act]) { + size++; + edge = parent_eids[act] - 1; + act = IGRAPH_OTHER(graph, edge, act); + } + if (vvec && (size > 0 || node == from)) { + IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); + VECTOR(*vvec)[size] = node; + } + if (evec) { + IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); + } + act = node; + while (parent_eids[act]) { + edge = parent_eids[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(&widths); + IGRAPH_FREE(is_target); + IGRAPH_FREE(parent_eids); + igraph_vit_destroy(&vit); + IGRAPH_FINALLY_CLEAN(6); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_get_widest_path + * \brief Widest path from one vertex to another one. + * + * Calculates a single widest 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_widest_paths(). + * + * \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 initialized 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. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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_widest_paths() for the version with + * more target vertices. + */ +igraph_error_t igraph_get_widest_path(const igraph_t *graph, + igraph_vector_int_t *vertices, + igraph_vector_int_t *edges, + igraph_integer_t from, + igraph_integer_t to, + const igraph_vector_t *weights, + igraph_neimode_t mode) { + + igraph_vector_int_list_t vertices2, *vp = &vertices2; + igraph_vector_int_list_t edges2, *ep = &edges2; + + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); + } else { + vp = NULL; + } + if (edges) { + IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); + IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); + } else { + ep = NULL; + } + + IGRAPH_CHECK(igraph_get_widest_paths(graph, vp, ep, + from, igraph_vss_1(to), + weights, mode, 0, 0)); + + if (edges) { + IGRAPH_CHECK(igraph_vector_int_update(edges, igraph_vector_int_list_get_ptr(&edges2, 0))); + igraph_vector_int_list_destroy(&edges2); + IGRAPH_FINALLY_CLEAN(1); + } + if (vertices) { + IGRAPH_CHECK(igraph_vector_int_update(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0))); + igraph_vector_int_list_destroy(&vertices2); + IGRAPH_FINALLY_CLEAN(1); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_widest_path_widths_floyd_warshall + * \brief Widths of widest paths between vertices. + * + * This function implements a modified Floyd-Warshall algorithm, + * to find the widest path widths between a set of source and target + * vertices. It is primarily useful for all-pairs path widths in very dense + * graphs, as its running time is manily determined by the vertex count, + * and is not sensitive to the graph density. In sparse graphs, other methods + * such as the Dijkstra algorithm, will perform better. + * + * + * Note that internally this function always computes the path width matrix + * for all pairs of vertices. The \p from and \p to parameters only serve + * to subset this matrix, but do not affect the time taken by the + * calculation. + * + * \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 widths from a single source, to the + * vertices given in the \c to argument. + * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices + * have a width of \c IGRAPH_POSINFINITY to themselves. + * \param from The source vertices. + * \param to The target vertices. + * \param weights The edge weights. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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(|V|^3), where |V| is the number of vertices in the graph. + * + * \sa \ref igraph_widest_path_widths_dijkstra() for a variant that runs faster + * on sparse graphs. + */ +igraph_error_t igraph_widest_path_widths_floyd_warshall(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 a modified Floyd Warshall algorithm + which computes the widest path between every pair of nodes. The key + difference between this and the regular Floyd Warshall is that instead + of updating the distance between two nodes to be the minimum of itself + and the distance through an intermediate node, we instead set the width + to be the maximum of itself and the width through the intermediate node. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t in = false, out = false; + + if (! weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (igraph_vector_is_any_nan(weights)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + switch (mode) { + case IGRAPH_ALL: + in = out = true; + break; + case IGRAPH_OUT: + out = true; + break; + case IGRAPH_IN: + in = true; + break; + default: + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); + } + + /* Fill out adjacency matrix */ + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_fill(res, IGRAPH_NEGINFINITY); + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + MATRIX(*res, i, i) = IGRAPH_POSINFINITY; + } + + for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_real_t w = VECTOR(*weights)[edge]; + + if (w == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + continue; + } + + if (out && MATRIX(*res, from, to) < w) MATRIX(*res, from, to) = w; + if (in && MATRIX(*res, to, from) < w) MATRIX(*res, to, from) = w; + } + + /* Run modified Floyd Warshall */ + for (igraph_integer_t k = 0; k < no_of_nodes; k++) { + /* Iterate in column-major order for better performance */ + for (igraph_integer_t j = 0; j < no_of_nodes; j++) { + igraph_real_t width_kj = MATRIX(*res, k, j); + if (j == k || width_kj == IGRAPH_NEGINFINITY) continue; + + IGRAPH_ALLOW_INTERRUPTION(); + + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + if (i == j || i == k) continue; + + /* alternative_width := min(A(i,k), A(k,j)) + A(i,j) := max(A(i,j), alternative_width) */ + + igraph_real_t altwidth = MATRIX(*res, i, k); + if (width_kj < altwidth) { + altwidth = width_kj; + } + if (altwidth > MATRIX(*res, i, j)) { + MATRIX(*res, i, j) = altwidth; + } + } + } + } + + IGRAPH_CHECK(igraph_i_matrix_subset_vertices(res, graph, from, to)); + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_widest_path_widths_dijkstra + * \brief Widths of widest paths between vertices. + * + * This function implements a modified Dijkstra's algorithm, which + * can find the widest path widths from a source vertex to all + * other vertices. This function allows specifying a set of source + * and target vertices. The algorithm is run independently for each + * source and the results are retained only for the specified targets. + * This implementation uses a binary heap for efficiency. + * + * \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 widths from a single source, to the + * vertices given in the \c to argument. + * Unreachable vertices have width \c IGRAPH_NEGINFINITY, and vertices + * have a width of \c IGRAPH_POSINFINITY to themselves. + * \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. Edge weights can be negative. If this + * is a null pointer or if any edge weight is NaN, then an error + * is returned. Edges with positive infinite weight are ignored. + * \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 in the graph, |E| the number of edges and s the number of sources. + * + * \sa \ref igraph_widest_path_widths_floyd_warshall() for a variant that runs faster + * on dense graphs. + */ +igraph_error_t igraph_widest_path_widths_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 a Dijkstra algorithm with a + binary heap, modified to support widest paths. The heap is indexed, + so it stores both the widest path to a node, as well as it's index. We + use a 2 way heap so that we can query indexes directly in the heap. + + To adapt a Dijkstra to handle widest path, instead of prioritising candidate + nodes with the minimum distance, we prioritise those with the maximum + width instead. When adding a node into our set of 'completed' nodes, we + update all neighbouring nodes with a width that is equal to the min of the + width to the current node and the width of the edge. + + We denote the widest path from a node to itself as infinity, and the widest + path from a node to a node it cannot reach as negative infinity. + */ + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_2wheap_t Q; + igraph_vit_t fromvit, tovit; + igraph_integer_t no_of_from, no_of_to; + igraph_lazy_inclist_t inclist; + igraph_integer_t i, j; + igraph_bool_t all_to; + igraph_vector_int_t indexv; + + if (!weights) { + IGRAPH_ERROR("Weight vector is required.", IGRAPH_EINVAL); + } + + if (igraph_vector_size(weights) != no_of_edges) { + IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").", + IGRAPH_EINVAL, + igraph_vector_size(weights), no_of_edges); + } + + if (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_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_INT_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)) { + igraph_integer_t 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, IGRAPH_NEGINFINITY); + + for (IGRAPH_VIT_RESET(fromvit), i = 0; + !IGRAPH_VIT_END(fromvit); + IGRAPH_VIT_NEXT(fromvit), i++) { + + igraph_integer_t reached = 0; + igraph_integer_t source = IGRAPH_VIT_GET(fromvit); + igraph_2wheap_clear(&Q); + igraph_2wheap_push_with_index(&Q, source, IGRAPH_POSINFINITY); + + while (!igraph_2wheap_empty(&Q)) { + igraph_integer_t maxnei = igraph_2wheap_max_index(&Q); + igraph_real_t maxwidth = igraph_2wheap_deactivate_max(&Q); + igraph_vector_int_t *neis; + igraph_integer_t nlen; + + IGRAPH_ALLOW_INTERRUPTION(); + + if (all_to) { + MATRIX(*res, i, maxnei) = maxwidth; + } else { + if (VECTOR(indexv)[maxnei]) { + MATRIX(*res, i, VECTOR(indexv)[maxnei] - 1) = maxwidth; + reached++; + if (reached == no_of_to) { + igraph_2wheap_clear(&Q); + break; + } + } + } + + /* Now check all neighbors of 'maxnei' for a wider path*/ + neis = igraph_lazy_inclist_get(&inclist, maxnei); + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + nlen = igraph_vector_int_size(neis); + for (j = 0; j < nlen; j++) { + igraph_integer_t edge = VECTOR(*neis)[j]; + igraph_integer_t tto = IGRAPH_OTHER(graph, edge, maxnei); + igraph_real_t edgewidth = VECTOR(*weights)[edge]; + igraph_real_t altwidth = maxwidth < edgewidth ? maxwidth : edgewidth; + igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); + igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); + igraph_real_t curwidth = active ? igraph_2wheap_get(&Q, tto) : IGRAPH_POSINFINITY; + if (edgewidth == IGRAPH_INFINITY) { + /* Ignore edges with infinite weight */ + } else if (!has) { + /* This is the first time assigning a width to this vertex */ + IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, altwidth)); + } else if (altwidth > curwidth) { + /* This is a wider path */ + igraph_2wheap_modify(&Q, tto, altwidth); + } + } + + } /* !igraph_2wheap_empty(&Q) */ + + } /* !IGRAPH_VIT_END(fromvit) */ + + if (!all_to) { + igraph_vit_destroy(&tovit); + igraph_vector_int_destroy(&indexv); + IGRAPH_FINALLY_CLEAN(2); + } + + igraph_lazy_inclist_destroy(&inclist); + igraph_2wheap_destroy(&Q); + igraph_vit_destroy(&fromvit); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/properties/basic_properties.c b/src/vendor/cigraph/src/properties/basic_properties.c index 58bed348b2e..3de324b751d 100644 --- a/src/vendor/cigraph/src/properties/basic_properties.c +++ b/src/vendor/cigraph/src/properties/basic_properties.c @@ -52,25 +52,25 @@ * \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 + * calculation. If this constant is \c 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 + * this is \c 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_error_t 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_real_t no_of_nodes = (igraph_real_t) igraph_vcount(graph); + igraph_real_t no_of_edges = (igraph_real_t) igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); if (no_of_nodes == 0) { *res = IGRAPH_NAN; - return 0; + return IGRAPH_SUCCESS; } if (!loops) { @@ -89,12 +89,12 @@ int igraph_density(const igraph_t *graph, igraph_real_t *res, } } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_diversity - * Structural diversity index of the vertices + * \brief 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, @@ -117,7 +117,7 @@ int igraph_density(const igraph_t *graph, igraph_real_t *res, * 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 + * \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 @@ -127,12 +127,12 @@ int igraph_density(const igraph_t *graph, igraph_real_t *res, * Time complexity: O(|V|+|E|), linear. * */ -int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, +igraph_error_t 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_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t k, i; + igraph_vector_int_t incident; igraph_bool_t has_multiple; igraph_vit_t vit; @@ -157,12 +157,12 @@ int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, 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)) { + } else if (isnan(minweight)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } } - IGRAPH_VECTOR_INIT_FINALLY(&incident, 10); + IGRAPH_VECTOR_INT_INIT_FINALLY(&incident, 10); IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -172,10 +172,10 @@ int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, 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_integer_t v = IGRAPH_VIT_GET(vit); IGRAPH_CHECK(igraph_incident(graph, &incident, v, /*mode=*/ IGRAPH_ALL)); - k = igraph_vector_size(&incident); /* degree */ + k = igraph_vector_int_size(&incident); /* degree */ /* * Non-normalized diversity is defined as @@ -196,7 +196,7 @@ int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, } 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]]; + igraph_real_t w = VECTOR(*weights)[VECTOR(incident)[i]]; if (w == 0) continue; s += w; ent += (w * log(w)); @@ -208,7 +208,7 @@ int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, } igraph_vit_destroy(&vit); - igraph_vector_destroy(&incident); + igraph_vector_int_destroy(&incident); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; @@ -257,37 +257,37 @@ int igraph_diversity(const igraph_t *graph, const igraph_vector_t *weights, * * \example examples/simple/igraph_reciprocity.c */ -int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, +igraph_error_t 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); + igraph_vector_int_t inneis, outneis; + igraph_integer_t i; + igraph_integer_t no_of_nodes = igraph_vcount(graph); if (mode != IGRAPH_RECIPROCITY_DEFAULT && mode != IGRAPH_RECIPROCITY_RATIO) { - IGRAPH_ERROR("Invalid reciprocity type", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid reciprocity type.", IGRAPH_EINVAL); } /* THIS IS AN EXIT HERE !!!!!!!!!!!!!! */ if (!igraph_is_directed(graph)) { *res = 1.0; - return 0; + return IGRAPH_SUCCESS; } - IGRAPH_VECTOR_INIT_FINALLY(&inneis, 0); - IGRAPH_VECTOR_INIT_FINALLY(&outneis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&inneis, 0); + IGRAPH_VECTOR_INT_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); + igraph_integer_t ip, op; + IGRAPH_CHECK(igraph_neighbors(graph, &inneis, i, IGRAPH_IN)); + IGRAPH_CHECK(igraph_neighbors(graph, &outneis, i, IGRAPH_OUT)); ip = op = 0; - while (ip < igraph_vector_size(&inneis) && - op < igraph_vector_size(&outneis)) { + while (ip < igraph_vector_int_size(&inneis) && + op < igraph_vector_int_size(&outneis)) { if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) { nonrec += 1; ip++; @@ -310,8 +310,8 @@ int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, op++; } } - nonrec += (igraph_vector_size(&inneis) - ip) + - (igraph_vector_size(&outneis) - op); + nonrec += (igraph_vector_int_size(&inneis) - ip) + + (igraph_vector_int_size(&outneis) - op); } if (mode == IGRAPH_RECIPROCITY_DEFAULT) { @@ -324,8 +324,8 @@ int igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, *res = (igraph_real_t) rec / (rec + nonrec); } - igraph_vector_destroy(&inneis); - igraph_vector_destroy(&outneis); + igraph_vector_int_destroy(&inneis); + igraph_vector_int_destroy(&outneis); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/constraint.c b/src/vendor/cigraph/src/properties/constraint.c index 15e5e95d17f..878eb25c21c 100644 --- a/src/vendor/cigraph/src/properties/constraint.c +++ b/src/vendor/cigraph/src/properties/constraint.c @@ -24,6 +24,7 @@ #include "igraph_centrality.h" #include "igraph_interface.h" +#include "igraph_structural.h" /** * \function igraph_constraint @@ -74,19 +75,19 @@ * 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_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t 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_integer_t nodes_to_calc; + igraph_integer_t a, b, c, i, j, q, vsize, vsize2; + igraph_integer_t edge, edge2; igraph_vector_t contrib; igraph_vector_t degree; - igraph_vector_t ineis_in, ineis_out, jneis_in, jneis_out; + igraph_vector_int_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); @@ -94,27 +95,16 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, 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_VECTOR_INT_INIT_FINALLY(&ineis_in, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ineis_out, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&jneis_in, 0); + IGRAPH_VECTOR_INT_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_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_NO_LOOPS, weights)); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); igraph_vector_null(res); @@ -123,54 +113,54 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, i = IGRAPH_VIT_GET(vit); /* get neighbors of i */ - IGRAPH_CHECK(igraph_incident(graph, &ineis_in, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_incident(graph, &ineis_in, i, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &ineis_out, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_incident(graph, &ineis_out, i, IGRAPH_OUT)); /* NaN for isolates */ - if (igraph_vector_size(&ineis_in) == 0 && - igraph_vector_size(&ineis_out) == 0) { + if (igraph_vector_int_size(&ineis_in) == 0 && + igraph_vector_int_size(&ineis_out) == 0) { VECTOR(*res)[a] = IGRAPH_NAN; } /* zero their contribution */ - vsize = igraph_vector_size(&ineis_in); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); VECTOR(contrib)[j] = 0.0; } - vsize = igraph_vector_size(&ineis_out); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_out)[b]; + j = 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); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i != j) { /* excluding loops */ if (weights) { VECTOR(contrib)[j] += - VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i]; + VECTOR(*weights)[edge] / VECTOR(degree)[i]; } else { VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; } } } if (igraph_is_directed(graph)) { - vsize = igraph_vector_size(&ineis_out); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i != j) { if (weights) { VECTOR(contrib)[j] += - VECTOR(*weights)[(long int)edge] / VECTOR(degree)[i]; + VECTOR(*weights)[edge] / VECTOR(degree)[i]; } else { VECTOR(contrib)[j] += 1.0 / VECTOR(degree)[i]; } @@ -179,26 +169,26 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } /* add the indirect contributions, in-in, in-out, out-in, out-out */ - vsize = igraph_vector_size(&ineis_in); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } - IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, j, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, IGRAPH_OUT)); - vsize2 = igraph_vector_size(&jneis_in); + vsize2 = igraph_vector_int_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); + edge2 = VECTOR(jneis_in)[c]; + q = IGRAPH_OTHER(graph, edge2, j); if (j != q) { if (weights) { VECTOR(contrib)[q] += - VECTOR(*weights)[(long int)edge] * - VECTOR(*weights)[(long int)edge2] / + VECTOR(*weights)[edge] * + VECTOR(*weights)[edge2] / VECTOR(degree)[i] / VECTOR(degree)[j]; } else { VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; @@ -206,15 +196,15 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } } if (igraph_is_directed(graph)) { - vsize2 = igraph_vector_size(&jneis_out); + vsize2 = igraph_vector_int_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); + edge2 = VECTOR(jneis_out)[c]; + q = IGRAPH_OTHER(graph, edge2, j); if (j != q) { if (weights) { VECTOR(contrib)[q] += - VECTOR(*weights)[(long int)edge] * - VECTOR(*weights)[(long int)edge2] / + VECTOR(*weights)[edge] * + VECTOR(*weights)[edge2] / VECTOR(degree)[i] / VECTOR(degree)[j]; } else { VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; @@ -224,41 +214,41 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } } if (igraph_is_directed(graph)) { - vsize = igraph_vector_size(&ineis_out); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } - IGRAPH_CHECK(igraph_incident(graph, &jneis_in, (igraph_integer_t) j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_in, j, IGRAPH_IN)); - IGRAPH_CHECK(igraph_incident(graph, &jneis_out, (igraph_integer_t) j, + IGRAPH_CHECK(igraph_incident(graph, &jneis_out, j, IGRAPH_OUT)); - vsize2 = igraph_vector_size(&jneis_in); + vsize2 = igraph_vector_int_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); + edge2 = VECTOR(jneis_in)[c]; + q = IGRAPH_OTHER(graph, edge2, j); if (j != q) { if (weights) { VECTOR(contrib)[q] += - VECTOR(*weights)[(long int)edge] * - VECTOR(*weights)[(long int)edge2] / + VECTOR(*weights)[edge] * + VECTOR(*weights)[edge2] / VECTOR(degree)[i] / VECTOR(degree)[j]; } else { VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; } } } - vsize2 = igraph_vector_size(&jneis_out); + vsize2 = igraph_vector_int_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); + edge2 = VECTOR(jneis_out)[c]; + q = IGRAPH_OTHER(graph, edge2, j); if (j != q) { if (weights) { VECTOR(contrib)[q] += - VECTOR(*weights)[(long int)edge] * - VECTOR(*weights)[(long int)edge2] / + VECTOR(*weights)[edge] * + VECTOR(*weights)[edge2] / VECTOR(degree)[i] / VECTOR(degree)[j]; } else { VECTOR(contrib)[q] += 1 / VECTOR(degree)[i] / VECTOR(degree)[j]; @@ -269,10 +259,10 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } /* squared sum of the contributions */ - vsize = igraph_vector_size(&ineis_in); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_in)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } @@ -280,10 +270,10 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, VECTOR(contrib)[j] = 0.0; } if (igraph_is_directed(graph)) { - vsize = igraph_vector_size(&ineis_out); + vsize = igraph_vector_int_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); + edge = VECTOR(ineis_out)[b]; + j = IGRAPH_OTHER(graph, edge, i); if (i == j) { continue; } @@ -294,13 +284,13 @@ int igraph_constraint(const igraph_t *graph, igraph_vector_t *res, } 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_int_destroy(&jneis_out); + igraph_vector_int_destroy(&jneis_in); + igraph_vector_int_destroy(&ineis_out); + igraph_vector_int_destroy(&ineis_in); igraph_vector_destroy(°ree); igraph_vector_destroy(&contrib); IGRAPH_FINALLY_CLEAN(7); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/convergence_degree.c b/src/vendor/cigraph/src/properties/convergence_degree.c index 8230b5e650f..661818f6aac 100644 --- a/src/vendor/cigraph/src/properties/convergence_degree.c +++ b/src/vendor/cigraph/src/properties/convergence_degree.c @@ -70,23 +70,23 @@ * * 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_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t i, j, k, n; + igraph_integer_t *geodist; igraph_vector_int_t *eids; igraph_vector_t *ins_p, *outs_p, ins_v, outs_v; - igraph_dqueue_t q; + igraph_dqueue_int_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); + IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); if (ins == 0) { ins_p = &ins_v; @@ -106,9 +106,9 @@ int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, igraph_vector_null(outs_p); } - geodist = IGRAPH_CALLOC(no_of_nodes, long int); + geodist = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (geodist == 0) { - IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); + IGRAPH_ERROR("Cannot calculate convergence degrees", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, geodist); @@ -121,19 +121,19 @@ int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, 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); + igraph_dqueue_int_clear(&q); + memset(geodist, 0, sizeof(geodist[0]) * (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_CHECK(igraph_dqueue_int_push(&q, i)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_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); + igraph_integer_t 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) { @@ -141,30 +141,30 @@ int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, * increase either the size of the infield or the outfield */ if (!directed) { if (actnode < neighbor) { - VECTOR(*ins_p)[(long int)VECTOR(*eids)[j]] += 1; + VECTOR(*ins_p)[VECTOR(*eids)[j]] += 1; } else { - VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; + VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; } } else { - vec[(long int)VECTOR(*eids)[j]] += 1; + vec[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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); + IGRAPH_CHECK(igraph_dqueue_int_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; + VECTOR(*ins_p)[VECTOR(*eids)[j]] += 1; } else { - VECTOR(*outs_p)[(long int)VECTOR(*eids)[j]] += 1; + VECTOR(*outs_p)[VECTOR(*eids)[j]] += 1; } } else { - vec[(long int)VECTOR(*eids)[j]] += 1; + vec[VECTOR(*eids)[j]] += 1; } geodist[neighbor] = actdist + 2; } @@ -201,8 +201,8 @@ int igraph_convergence_degree(const igraph_t *graph, igraph_vector_t *result, } IGRAPH_FREE(geodist); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/dag.c b/src/vendor/cigraph/src/properties/dag.c index 69632095f1b..186ce551476 100644 --- a/src/vendor/cigraph/src/properties/dag.c +++ b/src/vendor/cigraph/src/properties/dag.c @@ -59,13 +59,14 @@ * * \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_error_t igraph_topological_sorting( + const igraph_t* graph, igraph_vector_int_t *res, igraph_neimode_t mode) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + igraph_dqueue_int_t sources; igraph_neimode_t deg_mode; - long int node, i, j; + igraph_integer_t node, i, j; if (mode == IGRAPH_ALL || !igraph_is_directed(graph)) { IGRAPH_ERROR("Topological sorting does not make sense for undirected graphs", IGRAPH_EINVAL); @@ -77,49 +78,49 @@ int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res, 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_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_dqueue_int_init(&sources, 0)); + IGRAPH_FINALLY(igraph_dqueue_int_destroy, &sources); IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), deg_mode, 0)); - igraph_vector_clear(res); + igraph_vector_int_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)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&sources)) { + node = igraph_dqueue_int_pop(&sources); /* Add the node to the result vector */ - igraph_vector_push_back(res, node); + IGRAPH_CHECK(igraph_vector_int_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, node, mode)); + j = igraph_vector_int_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])); + VECTOR(degrees)[ VECTOR(neis)[i] ]--; + if (VECTOR(degrees)[ VECTOR(neis)[i] ] == 0) { + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, VECTOR(neis)[i])); } } } - if (igraph_vector_size(res) < no_of_nodes) { + if (igraph_vector_int_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_vector_int_destroy(°rees); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&sources); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } /** @@ -129,6 +130,14 @@ int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res, * * A directed acyclic graph (DAG) is a directed graph with no cycles. * + * + * This function returns false for undirected graphs. + * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * * \param graph The input graph. * \param res Pointer to a boolean constant, the result * is stored here. @@ -140,64 +149,68 @@ int igraph_topological_sorting(const igraph_t* graph, igraph_vector_t *res, * \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; +igraph_error_t igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + igraph_dqueue_int_t sources; if (!igraph_is_directed(graph)) { - *res = 0; + *res = false; 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)); + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_IS_DAG, res); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&sources, 0); - vertices_left = no_of_nodes; + IGRAPH_CHECK(igraph_degree(graph, °rees, igraph_vss_all(), IGRAPH_IN, /* loops */ true)); + + igraph_integer_t vertices_left = no_of_nodes; /* Do we have nodes with no incoming edges? */ - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { if (VECTOR(degrees)[i] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&sources, i)); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&sources)) { + igraph_integer_t node = igraph_dqueue_int_pop(&sources); /* 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]; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, node, IGRAPH_OUT)); + igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t nei = VECTOR(neis)[i]; if (nei == node) { - continue; + /* Found a self-loop, graph is not a DAG */ + *res = false; + goto finalize; } VECTOR(degrees)[nei]--; if (VECTOR(degrees)[nei] == 0) { - IGRAPH_CHECK(igraph_dqueue_push(&sources, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&sources, nei)); } } } + IGRAPH_ASSERT(vertices_left >= 0); *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); +finalize: + igraph_vector_int_destroy(°rees); + igraph_vector_int_destroy(&neis); + igraph_dqueue_int_destroy(&sources); IGRAPH_FINALLY_CLEAN(3); + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_DAG, *res); + return IGRAPH_SUCCESS; } @@ -205,16 +218,15 @@ int igraph_is_dag(const igraph_t* graph, igraph_bool_t *res) { 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_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t deg; + igraph_vector_int_t new_edges; + igraph_vector_int_t ancestors; + igraph_integer_t root; + igraph_vector_int_t neighbors; + igraph_stack_int_t path; igraph_vector_bool_t done; if (!igraph_is_directed(graph)) { @@ -222,12 +234,12 @@ int igraph_transitive_closure_dag(const igraph_t *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_VECTOR_INT_INIT_FINALLY(&new_edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&ancestors, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); + IGRAPH_CHECK(igraph_stack_int_init(&path, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &path); IGRAPH_CHECK(igraph_vector_bool_init(&done, no_of_nodes)); IGRAPH_FINALLY(igraph_vector_bool_destroy, &done); @@ -240,38 +252,38 @@ int igraph_transitive_closure_dag(const igraph_t *graph, if (VECTOR(deg)[root] != 0) { continue; } - IGRAPH_CHECK(igraph_stack_push(&path, root)); + IGRAPH_CHECK(igraph_stack_int_push(&path, root)); - while (!igraph_stack_empty(&path)) { - long int node = (long int) igraph_stack_top(&path); + while (!igraph_stack_int_empty(&path)) { + igraph_integer_t node = igraph_stack_int_top(&path); if (node == STAR) { /* Leaving a node */ - long int j, n; - igraph_stack_pop(&path); - node = (long int) igraph_stack_pop(&path); + igraph_integer_t j, n; + igraph_stack_int_pop(&path); + node = igraph_stack_int_pop(&path); if (!VECTOR(done)[node]) { - igraph_vector_pop_back(&ancestors); - VECTOR(done)[node] = 1; + igraph_vector_int_pop_back(&ancestors); + VECTOR(done)[node] = true; } - n = igraph_vector_size(&ancestors); + n = igraph_vector_int_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, + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, node)); + IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, VECTOR(ancestors)[j])); } } else { /* Getting into a node */ - long int n, j; + igraph_integer_t n, j; if (!VECTOR(done)[node]) { - IGRAPH_CHECK(igraph_vector_push_back(&ancestors, node)); + IGRAPH_CHECK(igraph_vector_int_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)); + node, IGRAPH_IN)); + n = igraph_vector_int_size(&neighbors); + IGRAPH_CHECK(igraph_stack_int_push(&path, STAR)); for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neighbors)[j]; - IGRAPH_CHECK(igraph_stack_push(&path, nei)); + igraph_integer_t nei = VECTOR(neighbors)[j]; + IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); } } } @@ -280,17 +292,17 @@ int igraph_transitive_closure_dag(const igraph_t *graph, #undef STAR igraph_vector_bool_destroy(&done); - igraph_stack_destroy(&path); - igraph_vector_destroy(&neighbors); - igraph_vector_destroy(&ancestors); - igraph_vector_destroy(°); + igraph_stack_int_destroy(&path); + igraph_vector_int_destroy(&neighbors); + igraph_vector_int_destroy(&ancestors); + igraph_vector_int_destroy(°); IGRAPH_FINALLY_CLEAN(5); - IGRAPH_CHECK(igraph_create(closure, &new_edges, (igraph_integer_t)no_of_nodes, + IGRAPH_CHECK(igraph_create(closure, &new_edges, no_of_nodes, IGRAPH_DIRECTED)); - igraph_vector_destroy(&new_edges); + igraph_vector_int_destroy(&new_edges); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/degrees.c b/src/vendor/cigraph/src/properties/degrees.c index 41f00b67498..814e595df90 100644 --- a/src/vendor/cigraph/src/properties/degrees.c +++ b/src/vendor/cigraph/src/properties/degrees.c @@ -29,7 +29,6 @@ * \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. @@ -48,35 +47,35 @@ * \param loops Boolean, gives whether the self-loops should be * counted. * \return Error code: - * \c IGRAPH_EINVVID: invalid vertex id. + * \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 + * Time complexity: O(v) if \p loops is \c 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_error_t 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_int_t tmp; - IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops)); - if (igraph_vector_size(&tmp) == 0) { + if (igraph_vector_int_size(&tmp) == 0) { *res = 0; } else { - *res = (igraph_integer_t) igraph_vector_max(&tmp); + *res = igraph_vector_int_max(&tmp); } - igraph_vector_destroy(&tmp); + igraph_vector_int_destroy(&tmp); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } -static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, +static igraph_error_t 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, @@ -84,14 +83,15 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis, edge_neis; + igraph_integer_t i, j, no_vids; igraph_vit_t vit; igraph_vector_t my_knn_v, *my_knn = knn; - igraph_vector_t strength, deg; + igraph_vector_t strength; + igraph_vector_int_t deg; igraph_integer_t maxdeg; - igraph_vector_t deghist; + igraph_vector_int_t deghist; igraph_real_t mynan = IGRAPH_NAN; if (igraph_vector_size(weights) != igraph_ecount(graph)) { @@ -110,7 +110,7 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, } /* Get degree of neighbours */ - IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_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); @@ -122,30 +122,30 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, /* 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_neis, maxdeg); + igraph_vector_int_clear(&neis); + igraph_vector_int_clear(&edge_neis); if (knnk) { - IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); + IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); igraph_vector_null(knnk); - IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg); + IGRAPH_VECTOR_INT_INIT_FINALLY(°hist, 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_integer_t v = IGRAPH_VIT_GET(vit); + igraph_integer_t 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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode)); + IGRAPH_CHECK(igraph_incident(graph, &edge_neis, v, mode)); + nv = igraph_vector_int_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]; + igraph_integer_t nei = VECTOR(neis)[j]; + igraph_integer_t e = VECTOR(edge_neis)[j]; + igraph_real_t w = VECTOR(*weights)[e]; sum += w * VECTOR(deg)[nei]; } if (str != 0.0) { @@ -159,13 +159,13 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, } } - igraph_vector_destroy(&edge_neis); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&edge_neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); if (knnk) { for (i = 0; i < maxdeg; i++) { - igraph_real_t dh = VECTOR(deghist)[i]; + igraph_integer_t dh = VECTOR(deghist)[i]; if (dh != 0) { VECTOR(*knnk)[i] /= dh; } else { @@ -173,12 +173,12 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, } } - igraph_vector_destroy(°hist); + igraph_vector_int_destroy(°hist); IGRAPH_FINALLY_CLEAN(1); } igraph_vector_destroy(&strength); - igraph_vector_destroy(°); + igraph_vector_int_destroy(°); IGRAPH_FINALLY_CLEAN(2); if (!knn) { @@ -189,7 +189,7 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } /** @@ -254,7 +254,7 @@ static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph, * * \example examples/simple/igraph_knn.c */ -int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, +igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph, igraph_vs_t vids, igraph_neimode_t mode, igraph_neimode_t neighbor_degree_mode, @@ -262,14 +262,14 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t neis; + igraph_integer_t i, j, no_vids; igraph_vit_t vit; igraph_vector_t my_knn_v, *my_knn = knn; - igraph_vector_t deg; + igraph_vector_int_t deg; igraph_integer_t maxdeg; - igraph_vector_t deghist; + igraph_vector_int_t deghist; igraph_real_t mynan = IGRAPH_NAN; igraph_bool_t simple; @@ -295,27 +295,27 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, IGRAPH_CHECK(igraph_vector_resize(knn, no_vids)); } - IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes); + IGRAPH_VECTOR_INT_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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg); + igraph_vector_int_clear(&neis); if (knnk) { - IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg)); + IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg)); igraph_vector_null(knnk); - IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg); + IGRAPH_VECTOR_INT_INIT_FINALLY(°hist, 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); + igraph_integer_t v = IGRAPH_VIT_GET(vit); + igraph_integer_t nv; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode)); + nv = igraph_vector_int_size(&neis); for (j = 0; j < nv; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = VECTOR(neis)[j]; sum += VECTOR(deg)[nei]; } if (nv != 0) { @@ -331,19 +331,19 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, if (knnk) { for (i = 0; i < maxdeg; i++) { - long int dh = (long int) VECTOR(deghist)[i]; + igraph_integer_t dh = VECTOR(deghist)[i]; if (dh != 0) { VECTOR(*knnk)[i] /= dh; } else { VECTOR(*knnk)[i] = mynan; } } - igraph_vector_destroy(°hist); + igraph_vector_int_destroy(°hist); IGRAPH_FINALLY_CLEAN(1); } - igraph_vector_destroy(&neis); - igraph_vector_destroy(°); + igraph_vector_int_destroy(&neis); + igraph_vector_int_destroy(°); igraph_vit_destroy(&vit); IGRAPH_FINALLY_CLEAN(3); @@ -352,16 +352,17 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); } - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_strength - * Strength of the vertices, weighted vertex degree in other words. + * \brief Strength of the vertices, also called weighted vertex degree. * * 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. @@ -369,7 +370,7 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, * \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 + * \param weights A vector giving the edge weights. If this is a \c NULL * pointer, then \ref igraph_degree() is called to perform the * calculation. * \return Error code. @@ -379,54 +380,64 @@ int igraph_avg_nearest_neighbor_degree(const igraph_t *graph, * * \sa \ref igraph_degree() for the traditional, non-weighted version. */ -int igraph_strength(const igraph_t *graph, igraph_vector_t *res, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; - long int no_vids; - igraph_vector_t neis; - long int i; + igraph_integer_t no_vids; + igraph_vector_int_t degrees; + igraph_vector_int_t neis; + igraph_integer_t i; if (!weights) { - return igraph_degree(graph, res, vids, mode, loops); + IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops)); + for (i = 0; i < no_of_nodes; i++) { + VECTOR(*res)[i] = VECTOR(degrees)[i]; + } + igraph_vector_int_destroy(°rees); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; } + if (igraph_vector_size(weights) != igraph_ecount(graph)) { - IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL); + 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_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_int_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); + igraph_integer_t vid = IGRAPH_VIT_GET(vit); + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_incident(graph, &neis, vid, mode)); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { - long int edge = (long int) VECTOR(neis)[j]; + igraph_integer_t edge = 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); + igraph_integer_t vid = IGRAPH_VIT_GET(vit); + igraph_integer_t j, n; + IGRAPH_CHECK(igraph_incident(graph, &neis, vid, mode)); + n = igraph_vector_int_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); + igraph_integer_t edge = VECTOR(neis)[j]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); if (from != to) { VECTOR(*res)[i] += VECTOR(*weights)[edge]; } @@ -435,24 +446,24 @@ int igraph_strength(const igraph_t *graph, igraph_vector_t *res, } igraph_vit_destroy(&vit); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_sort_vertex_ids_by_degree - * \brief Calculate a list of vertex ids sorted by degree of the corresponding vertex. + * \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 + * 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 + * 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, @@ -466,40 +477,41 @@ int igraph_strength(const igraph_t *graph, igraph_vector_t *res, * (\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 + * of vertex IDs. This parameter is ignored if \c vids is set + * to all vertices via \ref igraph_vs_all() or \ref 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_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_error_t igraph_sort_vertex_ids_by_degree(const igraph_t *graph, + igraph_vector_int_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_integer_t i, n; + igraph_vector_int_t degrees; + igraph_vector_int_t vs_vec; + IGRAPH_VECTOR_INT_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)); + IGRAPH_CHECK(igraph_vector_int_qsort_ind(°rees, outvids, order)); if (only_indices || igraph_vs_is_all(&vids) ) { - igraph_vector_destroy(°rees); + igraph_vector_int_destroy(°rees); IGRAPH_FINALLY_CLEAN(1); } else { - IGRAPH_VECTOR_INIT_FINALLY(&vs_vec, 0); + IGRAPH_VECTOR_INT_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]]; + n = igraph_vector_int_size(outvids); + for (i = 0; i < n; i++) { + VECTOR(*outvids)[i] = VECTOR(vs_vec)[VECTOR(*outvids)[i]]; } - igraph_vector_destroy(&vs_vec); - igraph_vector_destroy(°rees); + igraph_vector_int_destroy(&vs_vec); + igraph_vector_int_destroy(°rees); IGRAPH_FINALLY_CLEAN(2); } - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/ecc.c b/src/vendor/cigraph/src/properties/ecc.c new file mode 100644 index 00000000000..a81717d4f7b --- /dev/null +++ b/src/vendor/cigraph/src/properties/ecc.c @@ -0,0 +1,411 @@ +/* + 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, see . +*/ + +#include "igraph_transitivity.h" + +#include "igraph_interface.h" +#include "igraph_iterators.h" +#include "igraph_adjlist.h" + +#include "core/interruption.h" + +/* Computes the size of the intersection of two sorted vectors, treated as sets. + * It is assumed that the vectors contain no duplicates. + * + * We rely on (lazy_)adjlist_get() producing sorted neighbor lists and + * (lazy_)adjlist_init() being called with IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE + * to prevent duplicate entries. + */ +static igraph_integer_t vector_int_intersection_size_sorted( + const igraph_vector_int_t *v1, const igraph_vector_int_t *v2) { + igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); + igraph_integer_t i1 = 0, i2 = 0; + igraph_integer_t count = 0; + + while (i1 < n1 && i2 < n2) { + igraph_integer_t e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; + if (e1 < e2) { + i1++; + } else if (e1 == e2) { + count++; + i1++; i2++; + } else { /* e2 > e1 */ + i2++; + } + } + + return count; +} + + +/* Optimized for the case when computing ECC for all edges. */ +static igraph_error_t igraph_i_ecc3_1( + const igraph_t *graph, igraph_vector_t *res, const igraph_es_t eids, + igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degree; + igraph_adjlist_t al; + igraph_eit_t eit; + const igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of triangles the edge participates in */ + igraph_real_t s; /* max number of triangles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + /* A self-loop isn't, and cannot be part of any triangles. */ + z = 0.0; + s = 0.0; + } else { + const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1), *a2 = igraph_adjlist_get(&al, v2); + igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; + + z = vector_int_intersection_size_sorted(a1, a2); + s = (d1 < d2 ? d1 : d2) - 1.0; + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(°ree); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for computing ECC for a small subset of edges. */ +static igraph_error_t igraph_i_ecc3_2( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_lazy_adjlist_t al; + igraph_eit_t eit; + const igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of triangles the edge participates in */ + igraph_real_t s; /* max number of triangles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + /* A self-loop isn't, and cannot be part of any triangles. */ + z = 0.0; + s = 0.0; + } else { + igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); + igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); + + igraph_integer_t d1, d2; + IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); + + z = vector_int_intersection_size_sorted(a1, a2); + s = (d1 < d2 ? d1 : d2) - 1.0; + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for the case when computing ECC for all edges. */ +static igraph_error_t igraph_i_ecc4_1( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t degree; + igraph_adjlist_t al; + igraph_eit_t eit; + igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &al); + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of 4-cycles the edge participates in */ + igraph_real_t s; /* max number of 4-cycles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + if (v1 == v2) { + z = 0.0; + s = 0.0; + } else { + /* ensure that v1 is the vertex with the smaller degree */ + if (VECTOR(degree)[v1] > VECTOR(degree)[v2]) { + igraph_integer_t tmp = v1; + v1 = v2; + v2 = tmp; + } + + z = 0.0; + const igraph_vector_int_t *a1 = igraph_adjlist_get(&al, v1); + const igraph_integer_t n = igraph_vector_int_size(a1); + for (igraph_integer_t j=0; j < n; j++) { + igraph_integer_t v3 = VECTOR(*a1)[j]; + + /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ + + if (v3 == v2) continue; + + const igraph_vector_int_t *a2 = igraph_adjlist_get(&al, v2), *a3 = igraph_adjlist_get(&al, v3); + + z += vector_int_intersection_size_sorted(a2, a3) - 1.0; + } + + igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; + s = (d1 - 1.0) * (d2 - 1.0); + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_vector_int_destroy(°ree); + igraph_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + + +/* Optimized for computing ECC for a small subset of edges. */ +static igraph_error_t igraph_i_ecc4_2( + const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_bool_t offset, igraph_bool_t normalize) { + + igraph_lazy_adjlist_t al; + igraph_eit_t eit; + igraph_real_t c = offset ? 1.0 : 0.0; + + IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &al); + + IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); + + IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + + for (igraph_integer_t i=0; + ! IGRAPH_EIT_END(eit); + IGRAPH_EIT_NEXT(eit), i++) { + + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t v1 = IGRAPH_FROM(graph, edge), v2 = IGRAPH_TO(graph, edge); + + igraph_real_t z; /* number of 4-cycles the edge participates in */ + igraph_real_t s; /* max number of 4-cycles the edge could be part of */ + + IGRAPH_ALLOW_INTERRUPTION(); + + igraph_integer_t d1, d2; + IGRAPH_CHECK(igraph_degree_1(graph, &d1, v1, IGRAPH_ALL, IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree_1(graph, &d2, v2, IGRAPH_ALL, IGRAPH_LOOPS)); + + if (v1 == v2) { + z = 0.0; + s = 0.0; + } else { + /* ensure that v1 is the vertex with the smaller degree */ + if (d1 > d2) { + igraph_integer_t tmp = v1; + v1 = v2; + v2 = tmp; + + tmp = d1; + d1 = d2; + d2 = tmp; + } + + z = 0.0; + + igraph_vector_int_t *a1 = igraph_lazy_adjlist_get(&al, v1); + + const igraph_integer_t n = igraph_vector_int_size(a1); + for (igraph_integer_t j=0; j < n; j++) { + igraph_integer_t v3 = VECTOR(*a1)[j]; + + /* It is not possible that v3 == v1 because self-loops have been removed from the adjlist. */ + + if (v3 == v2) continue; + + igraph_vector_int_t *a2 = igraph_lazy_adjlist_get(&al, v2); + igraph_vector_int_t *a3 = igraph_lazy_adjlist_get(&al, v3); + + z += vector_int_intersection_size_sorted(a2, a3) - 1.0; + } + + s = (d1 - 1.0) * (d2 - 1.0); + } + + VECTOR(*res)[i] = z + c; + if (normalize) VECTOR(*res)[i] /= s; + } + + igraph_eit_destroy(&eit); + igraph_lazy_adjlist_destroy(&al); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \function igraph_ecc + * \brief Edge clustering coefficient of some edges. + * + * \experimental + * + * The edge clustering coefficient C^(k)_ij of an edge (i, j) + * is defined based on the number of k-cycles the edge participates in, + * z^(k)_ij, and the largest number of such cycles it could + * participate in given the degree of its endpoints, s^(k)_ij. + * The original definition given in the reference below is: + * + * + * C^(k)_ij = (z^(k)_ij + 1) / s^(k)_ij + * + * + * For k=3, s^(k)_ij = min(d_i - 1, d_j - 1), + * where \c d_i and \c d_j are the edge endpoint degrees. + * For k=4, s^(k)_ij = (d_i - 1) (d_j - 1). + * + * + * The \p normalize and \p offset parameters allow for skipping normalization + * by s^(k) and offsetting the cycle count z^(k) + * by one in the numerator of C^(k). Set both to \c true to + * compute the original definition of this metric. + * + * + * This function ignores edge multiplicities when listing k-cycles + * (i.e. z^(k)), but not when computing the maximum number of + * cycles an edge can participate in (s^(k)). + * + * + * Reference: + * + * + * F. Radicchi, C. Castellano, F. Cecconi, V. Loreto, and D. Parisi, + * PNAS 101, 2658 (2004). + * https://doi.org/10.1073/pnas.0400054101 + * + * \param graph The input graph. + * \param res Initialized vector, the result will be stored here. + * \param eids The edges for which the edge clustering coefficient will be computed. + * \param k Size of cycles to use in calculation. Must be at least 3. Currently + * only values of 3 and 4 are supported. + * \param offset Boolean, whether to add one to cycle counts. When \c false, + * z^(k) is used instead of z^(k) + 1. In this case + * the maximum value of the normalized metric is 1. For k=3 this + * is achieved for all edges in a complete graph. + * \param normalize Boolean, whether to normalize cycle counts by the maximum + * possible count s^(k) given the degrees. + * \return Error code. + * + * Time complexity: When \p k is 3, O(|V| d log d + |E| d). + * When \p k is 4, O(|V| d log d + |E| d^2). d denotes the degree of vertices. + */ +igraph_error_t igraph_ecc(const igraph_t *graph, igraph_vector_t *res, + const igraph_es_t eids, igraph_integer_t k, + igraph_bool_t offset, igraph_bool_t normalize) { + + if (k < 3) { + IGRAPH_ERRORF("Cycle size for edge clustering coefficient must be at least 3, got %" IGRAPH_PRId ".", + IGRAPH_EINVAL, k); + } + + switch (k) { + case 3: + if (igraph_es_is_all(&eids)) { + return igraph_i_ecc3_1(graph, res, eids, offset, normalize); + } else { + return igraph_i_ecc3_2(graph, res, eids, offset, normalize); + } + case 4: + if (igraph_es_is_all(&eids)) { + return igraph_i_ecc4_1(graph, res, eids, offset, normalize); + } else { + return igraph_i_ecc4_2(graph, res, eids, offset, normalize); + } + default: + IGRAPH_ERROR("Edge clustering coefficient calculation is only implemented for cycle sizes 3 and 4.", + IGRAPH_UNIMPLEMENTED); + } +} diff --git a/src/vendor/cigraph/src/properties/girth.c b/src/vendor/cigraph/src/properties/girth.c index 4462206895f..20bf0b6dfc2 100644 --- a/src/vendor/cigraph/src/properties/girth.c +++ b/src/vendor/cigraph/src/properties/girth.c @@ -30,8 +30,6 @@ #include "core/interruption.h" -#include - /** * \function igraph_girth * \brief The girth of a graph is the length of the shortest cycle in it. @@ -43,10 +41,7 @@ * * * 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. + * infinity is returned. * * * This implementation is based on Alon Itai and Michael Rodeh: @@ -55,9 +50,9 @@ * 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 + * \param girth Pointer to an \c igraph_real_t, if not \c NULL then the result * will be stored here. - * \param circle Pointer to an initialized vector, the vertex ids in + * \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. @@ -69,26 +64,25 @@ * * \example examples/simple/igraph_girth.c */ -int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, - igraph_vector_t *circle) { +igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t *girth, + igraph_vector_int_t *circle) { - long int no_of_nodes = igraph_vcount(graph); - igraph_dqueue_t q; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; igraph_lazy_adjlist_t adjlist; - long int mincirc = LONG_MAX, minvertex = 0; - long int node; - igraph_bool_t triangle = 0; + igraph_integer_t mincirc = IGRAPH_INTEGER_MAX, minvertex = 0; + igraph_integer_t node; + igraph_bool_t triangle = false; 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_vector_int_t level; + igraph_integer_t stoplevel = no_of_nodes + 1; + igraph_bool_t anycircle = false; + igraph_integer_t 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); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); + IGRAPH_VECTOR_INT_INIT_FINALLY(&level, no_of_nodes); for (node = 0; !triangle && node < no_of_nodes; node++) { @@ -103,27 +97,29 @@ int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, } anycircle = 0; - igraph_dqueue_clear(&q); - igraph_vector_long_null(&level); - IGRAPH_CHECK(igraph_dqueue_push(&q, node)); + igraph_dqueue_int_clear(&q); + igraph_vector_int_null(&level); + IGRAPH_CHECK(igraph_dqueue_int_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; + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actlevel = VECTOR(level)[actnode]; + igraph_integer_t i, n; if (actlevel >= stoplevel) { break; } - neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) actnode); + neis = igraph_lazy_adjlist_get(&adjlist, actnode); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + 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]; + igraph_integer_t nei = VECTOR(*neis)[i]; + igraph_integer_t neilevel = VECTOR(level)[nei]; if (neilevel != 0) { if (neilevel == actlevel - 1) { continue; @@ -146,7 +142,7 @@ int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, } } } else { - igraph_dqueue_push(&q, nei); + igraph_dqueue_int_push(&q, nei); VECTOR(level)[nei] = actlevel + 1; } } @@ -154,33 +150,38 @@ int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, } /* while q !empty */ } /* node */ - if (mincirc == LONG_MAX) { - mincirc = 0; + if (girth) { + if (mincirc == IGRAPH_INTEGER_MAX) { + *girth = IGRAPH_INFINITY; + } else { + *girth = mincirc; + } } - if (girth) { - *girth = (igraph_integer_t) mincirc; + if (mincirc == IGRAPH_INTEGER_MAX) { + mincirc = 0; } /* Store the actual circle, if needed */ if (circle) { - IGRAPH_CHECK(igraph_vector_resize(circle, mincirc)); + IGRAPH_CHECK(igraph_vector_int_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 */ + igraph_integer_t i, n, idx = 0; + igraph_dqueue_int_clear(&q); + igraph_vector_int_null(&level); /* used for father pointers */ #define FATHER(x) (VECTOR(level)[(x)]) - IGRAPH_CHECK(igraph_dqueue_push(&q, minvertex)); + IGRAPH_CHECK(igraph_dqueue_int_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); + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + neis = igraph_lazy_adjlist_get(&adjlist, actnode); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); n = igraph_vector_int_size(neis); for (i = 0; i < n; i++) { - long int nei = (long int) VECTOR(*neis)[i]; + igraph_integer_t nei = VECTOR(*neis)[i]; if (FATHER(nei) == 0) { FATHER(nei) = actnode + 1; - igraph_dqueue_push(&q, nei); + igraph_dqueue_int_push(&q, nei); } } } /* while q !empty */ @@ -199,10 +200,10 @@ int igraph_girth(const igraph_t *graph, igraph_integer_t *girth, } /* circle */ #undef FATHER - igraph_vector_long_destroy(&level); - igraph_dqueue_destroy(&q); + igraph_vector_int_destroy(&level); + igraph_dqueue_int_destroy(&q); igraph_lazy_adjlist_destroy(&adjlist); IGRAPH_FINALLY_CLEAN(3); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/loops.c b/src/vendor/cigraph/src/properties/loops.c index 7728d5720a3..6cf329c8170 100644 --- a/src/vendor/cigraph/src/properties/loops.c +++ b/src/vendor/cigraph/src/properties/loops.c @@ -31,6 +31,12 @@ * * * A loop edge is an edge from a vertex to itself. + * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * * \param graph The input graph. * \param res Pointer to an initialized boolean vector for storing the result. * @@ -40,19 +46,23 @@ * * \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); +igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t i, m = igraph_ecount(graph); - *res = 0; + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_LOOP, res); + + *res = false; for (i = 0; i < m; i++) { if (IGRAPH_FROM(graph, i) == IGRAPH_TO(graph, i)) { - *res = 1; + *res = true; break; } } - return 0; + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, *res); + + return IGRAPH_SUCCESS; } /** @@ -73,10 +83,10 @@ int igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { * * \example examples/simple/igraph_is_loop.c */ -int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, +igraph_error_t igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es) { igraph_eit_t eit; - long int i; + igraph_integer_t i; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); @@ -84,12 +94,12 @@ int igraph_is_loop(const igraph_t *graph, igraph_vector_bool_t *res, 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_integer_t e = IGRAPH_EIT_GET(eit); + VECTOR(*res)[i] = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)); } igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/multiplicity.c b/src/vendor/cigraph/src/properties/multiplicity.c index db2ea935b0d..587d9f000e0 100644 --- a/src/vendor/cigraph/src/properties/multiplicity.c +++ b/src/vendor/cigraph/src/properties/multiplicity.c @@ -31,7 +31,6 @@ * \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. * @@ -47,35 +46,54 @@ * * 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); +igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t ec = igraph_ecount(graph); + + if ( + igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MULTI) + ) { + /* use the cached result */ + *res = ( + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MULTI) + ); + return IGRAPH_SUCCESS; + } if (vc == 0 || ec == 0) { - *res = 1; + *res = true; } else { - igraph_vector_t neis; - long int i, j, n; - igraph_bool_t found = 0; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + igraph_vector_int_t neis; + igraph_integer_t i, j, n; + igraph_bool_t found = false; + IGRAPH_VECTOR_INT_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); + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); + n = igraph_vector_int_size(&neis); for (j = 0; j < n; j++) { if (VECTOR(neis)[j] == i) { - found = 1; break; + found = true; break; } if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { - found = 1; break; + found = true; break; } } } *res = !found; - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } - return 0; + /* If the graph turned out to be simple, we can cache that it has no loop + * and no multiple edges */ + if (*res) { + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_LOOP, 0); + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, 0); + } + + return IGRAPH_SUCCESS; } @@ -83,10 +101,14 @@ int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { * \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. * + * + * The return value of this function is cached in the graph itself; calling + * the function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time. + * * \param graph The input graph. * \param res Pointer to a boolean variable, the result will be stored here. * \return Error code. @@ -99,58 +121,62 @@ int igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { * * \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_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { + igraph_integer_t vc = igraph_vcount(graph); + igraph_integer_t ec = igraph_ecount(graph); igraph_bool_t directed = igraph_is_directed(graph); + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_HAS_MULTI, res); + if (vc == 0 || ec == 0) { - *res = 0; + *res = false; } else { - igraph_vector_t neis; - long int i, j, n; - igraph_bool_t found = 0; - IGRAPH_VECTOR_INIT_FINALLY(&neis, 0); + igraph_vector_int_t neis; + igraph_integer_t i, j, n; + igraph_bool_t found = false; + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); for (i = 0; i < vc && !found; i++) { - IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) i, + IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, IGRAPH_OUT)); - n = igraph_vector_size(&neis); + n = igraph_vector_int_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; + found = true; break; } else if (VECTOR(neis)[j - 1] != i) { /* Undirected, but not a loop edge */ - found = 1; break; + found = true; break; } else if (j < n - 1 && VECTOR(neis)[j] == VECTOR(neis)[j + 1]) { /* Undirected, loop edge, multiple times */ - found = 1; break; + found = true; break; } } } } *res = found; - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(1); } - return 0; + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MULTI, *res); + + return IGRAPH_SUCCESS; } /** * \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. @@ -166,10 +192,10 @@ int igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { * * \example examples/simple/igraph_is_multiple.c */ -int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, +igraph_error_t 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_integer_t i, j, n; igraph_lazy_inclist_t inclist; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -181,25 +207,21 @@ int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, 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); - } + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = igraph_lazy_inclist_get(&inclist, from); - VECTOR(*res)[i] = 0; + IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); + + VECTOR(*res)[i] = false; 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); + igraph_integer_t e2 = VECTOR(*neis)[j]; + igraph_integer_t to2 = IGRAPH_OTHER(graph, e2, from); if (to2 == to && e2 < e) { - VECTOR(*res)[i] = 1; + VECTOR(*res)[i] = true; } } } @@ -207,21 +229,17 @@ int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, igraph_lazy_inclist_destroy(&inclist); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(2); - return 0; + return IGRAPH_SUCCESS; } - /** * \function igraph_count_multiple - * \brief Count the number of appearances of the edges in a graph. + * \brief The multiplicity of some 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.) + * An edge is called a multiple edge when there is one or more other + * edge between the same two vertices. The multiplicity of an edge + * is the number of edges between its endpoints. * - * * \param graph The input graph. * \param res Pointer to a vector, the result will be stored * here. It will be resized as needed. @@ -229,60 +247,101 @@ int igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *res, * to check all edges. * \return Error code. * - * \sa \ref igraph_is_multiple() and \ref igraph_simplify(). + * \sa \ref igraph_count_multiple_1() if you only need the multiplicity of a + * single edge; \ref igraph_is_multiple() if you are only interested in whether + * the graph has at least one edge with multiplicity greater than one; + * \ref igraph_simplify() to ensure that the graph has no multiple edges. * * 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_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, igraph_es_t es) { igraph_eit_t eit; - long int i, j, n; - igraph_lazy_inclist_t inclist; + igraph_integer_t i, j, n; + igraph_lazy_adjlist_t adjlist; 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_lazy_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); - IGRAPH_CHECK(igraph_vector_resize(res, IGRAPH_EIT_SIZE(eit))); + IGRAPH_CHECK(igraph_vector_int_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); - } + igraph_integer_t e = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, e); + igraph_integer_t to = IGRAPH_TO(graph, e); + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, from); + + IGRAPH_CHECK_OOM(neis, "Failed to query adjacent vertices."); 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; + if (VECTOR(*neis)[j] == to) { + VECTOR(*res)[i]++; } } } - igraph_lazy_inclist_destroy(&inclist); + igraph_lazy_adjlist_destroy(&adjlist); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; } +/** + * \function igraph_count_multiple_1 + * \brief The multiplicity of a single edge in a graph. + * + * \param graph The input graph. + * \param res Pointer to an iteger, the result will be stored here. + * \param eid The ID of the edge to check. + * \return Error code. + * + * \sa \ref igraph_count_multiple() if you need the multiplicity of multiple + * edges; \ref igraph_is_multiple() if you are only interested in whether the + * graph has at least one edge with multiplicity greater than one; + * \ref igraph_simplify() to ensure that the graph has no multiple edges. + * + * Time complexity: O(d), where d is the out-degree of the tail of the edge. + */ +igraph_error_t igraph_count_multiple_1(const igraph_t *graph, igraph_integer_t *res, igraph_integer_t eid) +{ + igraph_integer_t i, n, count; + igraph_integer_t from = IGRAPH_FROM(graph, eid); + igraph_integer_t to = IGRAPH_TO(graph, eid); + igraph_vector_int_t vids; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_CHECK(igraph_neighbors(graph, &vids, from, IGRAPH_OUT)); + + count = 0; + n = igraph_vector_int_size(&vids); + for (i = 0; i < n; i++) { + if (VECTOR(vids)[i] == to) { + count++; + } + } + + igraph_vector_int_destroy(&vids); + IGRAPH_FINALLY_CLEAN(1); + + *res = count; + + return IGRAPH_SUCCESS; +} + /** * \function igraph_is_mutual * \brief Check whether some edges of a directed graph are mutual. * - * An (A,B) edge is mutual if the graph contains the (B,A) edge too. + * An (A,B) non-loop directed edge is mutual if the graph contains + * the (B,A) edge too. Whether directed self-loops are considered mutual + * is controlled by the \p loops parameter. * * * An undirected graph only has mutual edges, by definition. @@ -292,14 +351,13 @@ int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es * (A,B) edges and one (B,A) edge, then all three are considered to be * mutual. * - * - * Self-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 * \ref igraph_ess_all() to check all edges. + * \param loops Boolean, whether to consider directed self-loops + * to be mutual. * \return Error code. * * Time complexity: O(n log(d)), n is the number of edges supplied, d @@ -307,11 +365,11 @@ int igraph_count_multiple(const igraph_t *graph, igraph_vector_t *res, igraph_es * 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_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es_t es, igraph_bool_t loops) { igraph_eit_t eit; igraph_lazy_adjlist_t adjlist; - long int i; + igraph_integer_t i; /* How many edges do we have? */ IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); @@ -321,7 +379,7 @@ int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es /* 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_vector_bool_fill(res, true); igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -331,16 +389,19 @@ int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es 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); + igraph_integer_t edge = IGRAPH_EIT_GET(eit); + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + + if (from == to) { + VECTOR(*res)[i] = loops; + continue; /* no need to do binsearch for self-loops */ + } /* Check whether there is a to->from edge, search for from in the out-list of to */ - igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) to); - if (neis == NULL) { - IGRAPH_ERROR("Failed to query neighbors.", IGRAPH_ENOMEM); - } + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); VECTOR(*res)[i] = igraph_vector_int_binsearch2(neis, from); } @@ -350,3 +411,93 @@ int igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res, igraph_es return IGRAPH_SUCCESS; } + + +/** + * \function igraph_has_mutual + * \brief Check whether a directed graph has any mutual edges. + * + * An (A,B) non-loop directed edge is mutual if the graph contains + * the (B,A) edge too. Whether directed self-loops are considered mutual + * is controlled by the \p loops parameter. + * + * + * In undirected graphs, all edges are considered mutual by definition. + * Thus for undirected graph, this function returns false only when there + * are no edges. + * + * + * To check whether a graph is an oriented graph, use this function in + * conjunction with \ref igraph_is_directed(). + * + * \param graph The input graph. + * \param res Pointer to a boolean, the result will be stored here. + * \param loops Boolean, whether to consider directed self-loops + * to be mutual. + * \return Error code. + * + * Time complexity: O(|E| log(d)) where d is the maximum in-degree. + */ +igraph_error_t igraph_has_mutual(const igraph_t *graph, igraph_bool_t *res, igraph_bool_t loops) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_lazy_adjlist_t adjlist; + + if (! igraph_is_directed(graph)) { + /* In undirected graphs, all edges are considered mutual, so we just check + * if there are any edges. */ + *res = no_of_edges > 0; + return IGRAPH_SUCCESS; + } + + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_MUTUAL)) { + if (igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_MUTUAL)) { + /* we know that the graph has at least one mutual non-loop edge + * (because the cache only stores non-loop edges) */ + *res = true; + return IGRAPH_SUCCESS; + } else if (loops) { + /* no non-loop mutual edges, but maybe we have loops? */ + return igraph_has_loop(graph, res); + } else { + /* no non-loop mutual edges, and loops are not to be treated as mutual */ + *res = false; + 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); + + *res = false; /* assume no mutual edges */ + for (igraph_integer_t edge=0; edge < no_of_edges; edge++) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + + if (from == to) { + if (loops) { + *res = true; + break; + } + continue; /* no need to do binsearch for self-loops */ + } + + /* Check whether there is a to->from edge, search for from in the + out-list of to */ + igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&adjlist, to); + IGRAPH_CHECK_OOM(neis, "Failed to query neighbors."); + if (igraph_vector_int_binsearch2(neis, from)) { + *res = true; + break; + } + } + + igraph_lazy_adjlist_destroy(&adjlist); + IGRAPH_FINALLY_CLEAN(1); + + /* cache the result if loops are not treated as mutual */ + if (!loops) { + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_HAS_MUTUAL, *res); + } + + return IGRAPH_SUCCESS; +} diff --git a/src/vendor/cigraph/src/properties/neighborhood.c b/src/vendor/cigraph/src/properties/neighborhood.c index 5a654154474..98fbbe1ed57 100644 --- a/src/vendor/cigraph/src/properties/neighborhood.c +++ b/src/vendor/cigraph/src/properties/neighborhood.c @@ -67,17 +67,17 @@ * 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_error_t igraph_neighborhood_size(const igraph_t *graph, igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; igraph_vit_t vit; - long int i, j; - long int *added; - igraph_vector_t neis; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t neis; if (order < 0) { IGRAPH_ERRORF("Negative order in neighborhood size: %" IGRAPH_PRId ".", @@ -89,42 +89,41 @@ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, IGRAPH_EINVAL, order, mindist); } - added = IGRAPH_CALLOC(no_of_nodes, long int); - if (added == 0) { - IGRAPH_ERROR("Cannot calculate neighborhood size.", IGRAPH_ENOMEM); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); IGRAPH_FINALLY(igraph_free, added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_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))); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_CHECK(igraph_vector_int_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; + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t size = mindist == 0 ? 1 : 0; added[node] = i + 1; - igraph_dqueue_clear(&q); + igraph_dqueue_int_clear(&q); if (order > 0) { - igraph_dqueue_push(&q, node); - igraph_dqueue_push(&q, 0); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_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]; + igraph_integer_t nei = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { size++; } @@ -133,7 +132,7 @@ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, } else { /* we just count them, but don't add them */ for (j = 0; j < n; j++) { - long int nei = (long int) VECTOR(neis)[j]; + igraph_integer_t nei = VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; if (actdist + 1 >= mindist) { @@ -148,9 +147,9 @@ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, VECTOR(*res)[i] = size; } /* for VIT, i */ - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(4); @@ -172,10 +171,8 @@ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, * 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 res An initialized list of integer vectors. The result of the + * calculation will be stored here. The list 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 @@ -199,18 +196,17 @@ int igraph_neighborhood_size(const igraph_t *graph, igraph_vector_t *res, * 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_error_t igraph_neighborhood(const igraph_t *graph, igraph_vector_int_list_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_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; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t neis; + igraph_vector_int_t tmp; if (order < 0) { IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); @@ -221,58 +217,58 @@ int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, IGRAPH_EINVAL); } - added = IGRAPH_CALLOC(no_of_nodes, long int); - if (added == 0) { - IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size."); IGRAPH_FINALLY(igraph_free, added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_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))); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + IGRAPH_CHECK(igraph_vector_int_list_reserve(res, IGRAPH_VIT_SIZE(vit))); + igraph_vector_int_list_clear(res); for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int node = IGRAPH_VIT_GET(vit); + igraph_integer_t node = IGRAPH_VIT_GET(vit); added[node] = i + 1; - igraph_vector_clear(&tmp); + igraph_vector_int_clear(&tmp); if (mindist == 0) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); } if (order > 0) { - igraph_dqueue_push(&q, node); - igraph_dqueue_push(&q, 0); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_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]; + igraph_integer_t nei = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t nei = VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); } } } @@ -280,20 +276,13 @@ int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, } /* 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_CHECK(igraph_vector_int_list_push_back_copy(res, &tmp)); } - igraph_vector_destroy(&tmp); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&tmp); + igraph_vector_int_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(5); @@ -319,10 +308,9 @@ int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, * 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 res Pointer to a list of graphs, the result will be stored + * here. Each item in the list is an \c igraph_t object. The list 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 @@ -346,18 +334,18 @@ int igraph_neighborhood(const igraph_t *graph, igraph_vector_ptr_t *res, * 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_error_t igraph_neighborhood_graphs(const igraph_t *graph, igraph_graph_list_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_dqueue_int_t q; igraph_vit_t vit; - long int i, j; - long int *added; - igraph_vector_t neis; - igraph_vector_t tmp; - igraph_t *newg; + igraph_integer_t i, j; + igraph_integer_t *added; + igraph_vector_int_t neis; + igraph_vector_int_t tmp; + igraph_t newg; if (order < 0) { IGRAPH_ERROR("Negative order in neighborhood size", IGRAPH_EINVAL); @@ -368,58 +356,58 @@ int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, IGRAPH_EINVAL); } - added = IGRAPH_CALLOC(no_of_nodes, long int); - if (added == 0) { - IGRAPH_ERROR("Cannot calculate neighborhood size", IGRAPH_ENOMEM); - } + added = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); + IGRAPH_CHECK_OOM(added, "Cannot calculate neighborhood size"); IGRAPH_FINALLY(igraph_free, added); - IGRAPH_DQUEUE_INIT_FINALLY(&q, 100); + + IGRAPH_DQUEUE_INT_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))); + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); + + igraph_graph_list_clear(res); for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { - long int node = IGRAPH_VIT_GET(vit); + igraph_integer_t node = IGRAPH_VIT_GET(vit); added[node] = i + 1; - igraph_vector_clear(&tmp); + igraph_vector_int_clear(&tmp); if (mindist == 0) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, node)); + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, node)); } if (order > 0) { - igraph_dqueue_push(&q, node); - igraph_dqueue_push(&q, 0); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, node)); + IGRAPH_CHECK(igraph_dqueue_int_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); + while (!igraph_dqueue_int_empty(&q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + igraph_integer_t actdist = igraph_dqueue_int_pop(&q); + igraph_integer_t n; + IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, mode)); + n = igraph_vector_int_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]; + igraph_integer_t nei = 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)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei)); + IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_int_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]; + igraph_integer_t nei = VECTOR(neis)[j]; if (added[nei] != i + 1) { added[nei] = i + 1; if (actdist + 1 >= mindist) { - IGRAPH_CHECK(igraph_vector_push_back(&tmp, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(&tmp, nei)); } } } @@ -427,26 +415,23 @@ int igraph_neighborhood_graphs(const igraph_t *graph, igraph_vector_ptr_t *res, } /* 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, + if (igraph_vector_int_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)); + IGRAPH_CHECK(igraph_copy(&newg, graph)); } - VECTOR(*res)[i] = newg; - IGRAPH_FINALLY_CLEAN(1); + + IGRAPH_FINALLY(igraph_destroy, &newg); + IGRAPH_CHECK(igraph_graph_list_push_back(res, &newg)); + IGRAPH_FINALLY_CLEAN(1); /* ownership of `newg' taken by `res' */ } - igraph_vector_destroy(&tmp); - igraph_vector_destroy(&neis); + igraph_vector_int_destroy(&tmp); + igraph_vector_int_destroy(&neis); igraph_vit_destroy(&vit); - igraph_dqueue_destroy(&q); + igraph_dqueue_int_destroy(&q); IGRAPH_FREE(added); IGRAPH_FINALLY_CLEAN(5); diff --git a/src/vendor/cigraph/src/properties/perfect.c b/src/vendor/cigraph/src/properties/perfect.c new file mode 100644 index 00000000000..de1b42623c8 --- /dev/null +++ b/src/vendor/cigraph/src/properties/perfect.c @@ -0,0 +1,191 @@ +/* -*- 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, see . +*/ + +#include "igraph_structural.h" + +#include "igraph_bipartite.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_operators.h" +#include "igraph_topology.h" + +#include "core/interruption.h" + +/** + * \function igraph_is_perfect + * \brief Checks if the graph is perfect. + * + * A perfect graph is an undirected graph in which the chromatic number of every induced + * subgraph equals the order of the largest clique of that subgraph. + * The chromatic number of a graph G is the smallest number of colors needed to + * color the vertices of G so that no two adjacent vertices share the same color. + * + * + * Warning: This function may create the complement of the graph internally, + * which consumes a lot of memory. For moderately sized graphs, consider + * decomposing them into biconnected components and running the check separately + * on each component. + * + * + * This implementation is based on the strong perfect graph theorem which was + * conjectured by Claude Berge and proved by Maria Chudnovsky, Neil Robertson, + * Paul Seymour, and Robin Thomas. + * + * \param graph The input graph. It is expected to be undirected and simple. + * \param perfect Pointer to an integer, the result will be stored here. + * \return Error code. + * + * Time complexity: worst case exponenital, often faster in practice. + */ +igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_bool_t *perfect) { + + igraph_bool_t is_bipartite, is_chordal, iso, is_simple; + igraph_real_t girth, comp_girth; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t start; + igraph_integer_t cycle_len; + igraph_t comp_graph, cycle; + + // If the graph is directed return error. + if (igraph_is_directed(graph)) { + IGRAPH_ERROR("The concept of perfect graphs is only defined for undirected graphs.", IGRAPH_EINVAL); + } + + // If the graph isn't simple then return an error. + IGRAPH_CHECK(igraph_is_simple(graph, &is_simple)); + if (!is_simple) { + IGRAPH_ERROR("Perfect graph testing is implemented for simple graphs only. Simplify the graph.", IGRAPH_EINVAL); + } + + // All graphs with less than 5 vertices are perfect. + if (no_of_nodes < 5) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // Graphs with less than 5 edges or a complement with less than 5 edges + // are also perfect. The following check handles most 5-vertex graphs, + // but its usefulness quickly diminishes, with only 0.3% of unlabelled + // 8-vertex graphs handled. + // In order to avoid bad results due to integer overflow with large graphs, + // we limit this check for small graphs only. + if ( no_of_nodes < 10000 && + (no_of_edges < 5 || no_of_edges > (no_of_nodes - 1) * no_of_nodes / 2 - 5)) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // Chordal and bipartite graph types are perfect. + // Possibly more optimizations found here: http://www.or.uni-bonn.de/~hougardy/paper/ClassesOfPerfectGraphs.pdf + IGRAPH_CHECK(igraph_is_bipartite(graph, &is_bipartite, NULL)); + if (is_bipartite) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_is_chordal(graph, NULL, NULL, &is_chordal, NULL, NULL)); + if (is_chordal) { + *perfect = true; + return IGRAPH_SUCCESS; + } + + // The weak perfect graph theorem: + // A graph is perfect iff its complement is perfect. + IGRAPH_CHECK(igraph_complementer(&comp_graph, graph, 0)); + IGRAPH_FINALLY(igraph_destroy, &comp_graph); + + IGRAPH_CHECK(igraph_is_bipartite(&comp_graph, &is_bipartite, NULL)); + if (is_bipartite) { + *perfect = true; + goto clean1; + } + + IGRAPH_CHECK(igraph_is_chordal(&comp_graph, NULL, NULL, &is_chordal, NULL, NULL)); + if (is_chordal) { + *perfect = true; + goto clean1; + } + + // Since igraph_is_bipartite also catches trees, at this point the girth + // of the graph and its complementer (to be stored in girth and comp_girth) + // are both guaranteed to be finite. + + // If the girth (or the smallest circle in the graph) is bigger than 3 and have odd number of vertices then + // the graph isn't perfect. + IGRAPH_CHECK(igraph_girth(graph, &girth, NULL)); + if ((girth > 3) && (((igraph_integer_t)girth) % 2 == 1)) { + *perfect = false; + goto clean1; + } + + IGRAPH_CHECK(igraph_girth(&comp_graph, &comp_girth, NULL)); + if ((comp_girth > 3) && (((igraph_integer_t)comp_girth) % 2 == 1)) { + *perfect = false; + goto clean1; + } + + // At this point girth and comp_girth are both at least 3. + + // Strong perfect graph theorem: + // A graph is perfect iff neither it or its complement contains an induced odd cycle of length >= 5 + // (i.e. an odd hole). TODO: Find a more efficient way to check for odd holes. + start = (igraph_integer_t) (girth < comp_girth ? girth : comp_girth); + start = start % 2 == 0 ? start + 1 : start + 2; + for (cycle_len = start; cycle_len <= no_of_nodes ; cycle_len += 2) { + + IGRAPH_ALLOW_INTERRUPTION(); + + IGRAPH_CHECK(igraph_ring(&cycle, cycle_len, IGRAPH_UNDIRECTED, /* mutual */ 0, /* circular */ 1)); + IGRAPH_FINALLY(igraph_destroy, &cycle); + + if (cycle_len > girth) { + IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); + if (iso) { + *perfect = false; + goto clean2; + } + } + + if (cycle_len > comp_girth) { + IGRAPH_CHECK(igraph_subisomorphic_lad(&cycle, &comp_graph, NULL, &iso, NULL, NULL, /* induced */ 1, 0)); + if (iso) { + *perfect = false; + goto clean2; + } + } + + igraph_destroy(&cycle); + IGRAPH_FINALLY_CLEAN(1); + } + + *perfect = true; + +clean1: + /* normal exit route */ + igraph_destroy(&comp_graph); + IGRAPH_FINALLY_CLEAN(1); + return IGRAPH_SUCCESS; + +clean2: + /* exit route if we also have a cycle to destroy */ + igraph_destroy(&cycle); + IGRAPH_FINALLY_CLEAN(1); + goto clean1; +} diff --git a/src/vendor/cigraph/src/properties/properties_internal.h b/src/vendor/cigraph/src/properties/properties_internal.h index f3daab84c08..b12b1b43341 100644 --- a/src/vendor/cigraph/src/properties/properties_internal.h +++ b/src/vendor/cigraph/src/properties/properties_internal.h @@ -21,11 +21,14 @@ #define IGRAPH_PROPERTIES_INTERNAL_H #include "igraph_adjlist.h" -#include "igraph_constants.h" +#include "igraph_decls.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); +__BEGIN_DECLS + +igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, + const igraph_vector_int_t *rank); + +__END_DECLS #endif diff --git a/src/vendor/cigraph/src/properties/spectral.c b/src/vendor/cigraph/src/properties/spectral.c index 8bd78b32656..e362cef7454 100644 --- a/src/vendor/cigraph/src/properties/spectral.c +++ b/src/vendor/cigraph/src/properties/spectral.c @@ -25,412 +25,407 @@ #include "igraph_structural.h" #include "igraph_interface.h" +#include "math/safe_intop.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) { +static igraph_error_t igraph_i_laplacian_validate_weights( + const igraph_t* graph, const igraph_vector_t* weights +) { + igraph_integer_t no_of_edges; - 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 (weights == NULL) { + return IGRAPH_SUCCESS; + } + + no_of_edges = igraph_ecount(graph); if (igraph_vector_size(weights) != no_of_edges) { - IGRAPH_ERROR("Invalid edge weight vector length", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid 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); + 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 (isnan(minweight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } } - IGRAPH_CHECK(igraph_eit_create(graph, igraph_ess_all(0), &edgeit)); - IGRAPH_FINALLY(igraph_eit_destroy, &edgeit); + return IGRAPH_SUCCESS; +} - IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); +/** + * \function igraph_get_laplacian + * \brief Returns the Laplacian matrix of a graph. + * + * The Laplacian matrix \c L of a graph is defined as + * L_ij = - A_ij when i != j and + * L_ii = d_i - A_ii. Here \c A denotes the (possibly weighted) + * adjacency matrix and d_i is the degree (or strength, if weighted) + * of vertex \c i. In directed graphs, the \p mode parameter controls whether to use + * out- or in-degrees. Correspondingly, the rows or columns will sum to zero. + * In undirected graphs, A_ii is taken to be \em twice the number + * (or total weight) of self-loops, ensuring that d_i = \sum_j A_ij. + * Thus, the Laplacian of an undirected graph is the same as the Laplacian + * of a directed one obtained by replacing each undirected edge with two reciprocal + * directed ones. + * + * + * More compactly, L = D - A where the \c D is a diagonal matrix + * containing the degrees. The Laplacian matrix can also be normalized, with several + * conventional normalization methods. See \ref igraph_laplacian_normalization_t for + * the methods available in igraph. + * + * + * 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. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalization The normalization method to use when calculating the + * Laplacian matrix. See \ref igraph_laplacian_normalization_t for + * possible values. + * \param weights An optional vector containing non-negative 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|^2), |V| is the number of vertices in the graph. + * + * \example examples/simple/igraph_get_laplacian.c + */ - if (directed) { +igraph_error_t igraph_get_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_t degree; + igraph_integer_t i; - 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); - } + IGRAPH_ASSERT(res != NULL); - /* 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])); - } - } + IGRAPH_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); - } 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); - } + IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, no_of_nodes)); + igraph_matrix_null(res); + + IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); + + /* Value of 'mode' is validated in igraph_strength() call above. */ + if (! directed) { + mode = IGRAPH_ALL; + } else if (mode == IGRAPH_ALL) { + directed = 0; + } + + for (i = 0; i < no_of_nodes; i++) { + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + MATRIX(*res, i, i) = VECTOR(degree)[i]; + break; - 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)); - } + case IGRAPH_LAPLACIAN_SYMMETRIC: + if (VECTOR(degree)[i] > 0) { + MATRIX(*res, i, i) = 1; + VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); } + break; - 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); + case IGRAPH_LAPLACIAN_LEFT: + case IGRAPH_LAPLACIAN_RIGHT: + if (VECTOR(degree)[i] > 0) { + MATRIX(*res, i, i) = 1; + VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; } + break; + default: + IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); } + } - } 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); + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; + igraph_real_t norm; + + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + MATRIX(*res, from, to) -= weight; + if (!directed) { + MATRIX(*res, to, from) -= weight; } - - /* 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])); - } + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + norm = VECTOR(degree)[from] * VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero %s-%s, " + "cannot perform symmetric normalization of Laplacian with '%s' mode.", + IGRAPH_EINVAL, + mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); } - - } 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); + weight *= norm; + MATRIX(*res, from, to) -= weight; + if (!directed) { + MATRIX(*res, to, from) -= weight; } - - 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]); + break; + + case IGRAPH_LAPLACIAN_LEFT: + norm = VECTOR(degree)[from]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero in-%s, " + "cannot perform left stochastic normalization of Laplacian with 'in' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); } - - 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); + MATRIX(*res, from, to) -= weight * norm; + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + MATRIX(*res, to, from) -= weight * VECTOR(degree)[to]; } - + break; + + case IGRAPH_LAPLACIAN_RIGHT: + norm = VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero out-%s, " + "cannot perform right stochastic normalization of Laplacian with 'out' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + MATRIX(*res, from, to) -= weight * norm; + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + MATRIX(*res, to, from) -= weight * VECTOR(degree)[from]; + } + break; } - } igraph_vector_destroy(°ree); - igraph_eit_destroy(&edgeit); - IGRAPH_FINALLY_CLEAN(2); + IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } + /** - * \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. + * \function igraph_get_laplacian_sparse + * \brief Returns the Laplacian of a graph in a sparse matrix format. * - * - * 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. + * See \ref igraph_get_laplacian() for the definition of the Laplacian matrix. * * * 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 + * result is stored here. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalization The normalization method to use when calculating the + * Laplacian matrix. See \ref igraph_laplacian_normalization_t for + * possible values. + * \param weights An optional vector containing non-negative 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. + * Time complexity: O(|E|), |E| is the number of edges in the graph. * - * \example examples/simple/igraph_laplacian.c + * \example examples/simple/igraph_get_laplacian_sparse.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_error_t igraph_get_laplacian_sparse( + const igraph_t *graph, igraph_sparsemat_t *sparseres, igraph_neimode_t mode, + igraph_laplacian_normalization_t normalization, + const igraph_vector_t *weights +) { + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = 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; + igraph_integer_t i; + igraph_integer_t nz; - if (!res && !sparseres) { - IGRAPH_ERROR("Laplacian: give at least one of `res' or `sparseres'", - IGRAPH_EINVAL); + if (directed) { + IGRAPH_SAFE_ADD(no_of_edges, no_of_nodes, &nz); + } else { + IGRAPH_SAFE_ADD(no_of_edges * 2, no_of_nodes, &nz); } - if (weights) { - return igraph_i_weighted_laplacian(graph, res, sparseres, normalized, - weights); - } + IGRAPH_ASSERT(sparseres != NULL); - 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_CHECK(igraph_i_laplacian_validate_weights(graph, weights)); + + IGRAPH_CHECK(igraph_sparsemat_resize(sparseres, no_of_nodes, no_of_nodes, nz)); IGRAPH_VECTOR_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), mode, IGRAPH_LOOPS, weights)); + + for (i = 0; i < no_of_nodes; i++) { + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, VECTOR(degree)[i])); + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + if (VECTOR(degree)[i] > 0) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); + VECTOR(degree)[i] = 1.0 / sqrt(VECTOR(degree)[i]); + } + break; - IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), - IGRAPH_OUT, IGRAPH_NO_LOOPS)); + case IGRAPH_LAPLACIAN_LEFT: + case IGRAPH_LAPLACIAN_RIGHT: + if (VECTOR(degree)[i] > 0) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, i, i, 1)); + VECTOR(degree)[i] = 1.0 / VECTOR(degree)[i]; + } + break; - 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])); - } + default: + IGRAPH_ERROR("Invalid Laplacian normalization method.", IGRAPH_EINVAL); + } + } + + for (i = 0; i < no_of_edges; i++) { + igraph_integer_t from = IGRAPH_FROM(graph, i); + igraph_integer_t to = IGRAPH_TO(graph, i); + igraph_real_t weight = weights ? VECTOR(*weights)[i] : 1.0; + igraph_real_t norm; + + switch (normalization) { + case IGRAPH_LAPLACIAN_UNNORMALIZED: + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); + if (!directed) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); } - 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); + break; + + case IGRAPH_LAPLACIAN_SYMMETRIC: + norm = VECTOR(degree)[from] * VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero %s-%s, " + "cannot perform symmetric normalization of Laplacian with '%s' mode.", + IGRAPH_EINVAL, + mode == IGRAPH_OUT ? "out" : "in", weights ? "strength" : "degree", mode == IGRAPH_OUT ? "out" : "in"); } - } 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]; - } + weight *= norm; + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight)); + if (!directed) { + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight)); } - - 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); + break; + + case IGRAPH_LAPLACIAN_LEFT: + norm = VECTOR(degree)[from]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero in-%s, " + "cannot perform left stochastic normalization of Laplacian with 'in' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[to])); } + break; + + case IGRAPH_LAPLACIAN_RIGHT: + norm = VECTOR(degree)[to]; + if (norm == 0 && weight != 0) { + IGRAPH_ERRORF( + "Found non-isolated vertex with zero out-%s, " + "cannot perform right stochastic normalization of Laplacian with 'out' mode.", + IGRAPH_EINVAL, + weights ? "strength" : "degree"); + } + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, from, to, -weight * norm)); + if (!directed) { + /* no failure possible in undirected case, as zero degrees occur only for isolated vertices */ + IGRAPH_CHECK(igraph_sparsemat_entry(sparseres, to, from, -weight * VECTOR(degree)[from])); + } + break; } + } - } else { + igraph_vector_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); - 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])); - } - } + return IGRAPH_SUCCESS; +} - 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]); - } +/** + * \function igraph_laplacian + * \brief Returns the Laplacian matrix of a graph (deprecated). + * + * This function produces the Laplacian matrix of a graph in either dense or + * sparse format. When \p normalized is set to true, the type of normalization + * used depends on the directnedness of the graph: symmetric normalization + * is used for undirected graphs and left stochastic normalization for + * directed graphs. + * + * \param graph Pointer to the graph to convert. + * \param res Pointer to an initialized matrix object or \c NULL. The dense matrix + * result will be stored here. + * \param sparseres Pointer to an initialized sparse matrix object or \c NULL. + * The sparse matrix result will be stored here. + * \param mode Controls whether to use out- or in-degrees in directed graphs. + * If set to \c IGRAPH_ALL, edge directions will be ignored. + * \param normalized Boolean, whether to normalize the result. + * \param weights An optional vector containing non-negative edge weights, + * to calculate the weighted Laplacian matrix. Set it to a null pointer to + * calculate the unweighted Laplacian. + * \return Error code. + * + * \deprecated-by igraph_get_laplacian 0.10.0 + */ - 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_error_t igraph_laplacian( + const igraph_t *graph, igraph_matrix_t *res, igraph_sparsemat_t *sparseres, + igraph_bool_t normalized, const igraph_vector_t *weights +) { + igraph_laplacian_normalization_t norm_method = IGRAPH_LAPLACIAN_UNNORMALIZED; + + if (!res && !sparseres) { + IGRAPH_ERROR("Laplacian: specify at least one of 'res' or 'sparseres'", + IGRAPH_EINVAL); + } + + if (normalized) { + if (igraph_is_directed(graph)) { + norm_method = IGRAPH_LAPLACIAN_LEFT; + } else { + norm_method = IGRAPH_LAPLACIAN_SYMMETRIC; } + } + if (res) { + IGRAPH_CHECK(igraph_get_laplacian(graph, res, IGRAPH_OUT, norm_method, weights)); } - igraph_vector_destroy(°ree); - igraph_eit_destroy(&edgeit); - IGRAPH_FINALLY_CLEAN(2); - return 0; + if (sparseres) { + IGRAPH_CHECK(igraph_get_laplacian_sparse(graph, sparseres, IGRAPH_OUT, norm_method, weights)); + } + + return IGRAPH_SUCCESS; } diff --git a/src/vendor/cigraph/src/properties/trees.c b/src/vendor/cigraph/src/properties/trees.c index e2bc8ea61ee..0f08d1424cd 100644 --- a/src/vendor/cigraph/src/properties/trees.c +++ b/src/vendor/cigraph/src/properties/trees.c @@ -22,8 +22,8 @@ */ #include "igraph_structural.h" +#include "igraph_topology.h" -#include "igraph_adjlist.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "igraph_interface.h" @@ -31,11 +31,12 @@ /** * \function igraph_unfold_tree - * Unfolding a graph into a tree, by possibly multiplicating its vertices. + * \brief 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 @@ -54,116 +55,116 @@ * 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) { +igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, + igraph_neimode_t mode, const igraph_vector_int_t *roots, + igraph_vector_int_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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t no_of_roots = igraph_vector_int_size(roots); + igraph_integer_t tree_vertex_count = no_of_nodes; - igraph_vector_t edges; + igraph_vector_int_t edges; igraph_vector_bool_t seen_vertices; igraph_vector_bool_t seen_edges; - igraph_dqueue_t Q; - igraph_vector_t neis; + igraph_dqueue_int_t Q; + igraph_vector_int_t neis; - long int i, n, r, v_ptr = no_of_nodes; + igraph_integer_t v_ptr = no_of_nodes; - /* TODO: handle not-connected graphs, multiple root vertices */ + if (! igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { + IGRAPH_ERROR("All roots should be vertices of the graph.", IGRAPH_EINVVID); + } - 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_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); + IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); + IGRAPH_VECTOR_INT_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; - } + IGRAPH_CHECK(igraph_vector_int_range(vertex_index, 0, no_of_nodes)); } - for (r = 0; r < no_of_roots; r++) { + for (igraph_integer_t 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); + igraph_integer_t root = VECTOR(*roots)[r]; + VECTOR(seen_vertices)[root] = true; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, root)); - while (!igraph_dqueue_empty(&Q)) { - long int actnode = (long int) igraph_dqueue_pop(&Q); + while (!igraph_dqueue_int_empty(&Q)) { + igraph_integer_t actnode = igraph_dqueue_int_pop(&Q); - IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) actnode, mode)); - n = igraph_vector_size(&neis); - for (i = 0; i < n; i++) { + IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode)); - 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); + igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t i = 0; i < n; i++) { + + igraph_integer_t edge = VECTOR(neis)[i]; + igraph_integer_t from = IGRAPH_FROM(graph, edge); + igraph_integer_t to = IGRAPH_TO(graph, edge); + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, actnode); if (! VECTOR(seen_edges)[edge]) { - VECTOR(seen_edges)[edge] = 1; + VECTOR(seen_edges)[edge] = true; if (! VECTOR(seen_vertices)[nei]) { - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - VECTOR(seen_vertices)[nei] = 1; - IGRAPH_CHECK(igraph_dqueue_push(&Q, nei)); + VECTOR(seen_vertices)[nei] = true; + IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); } else { tree_vertex_count++; if (vertex_index) { - IGRAPH_CHECK(igraph_vector_push_back(vertex_index, nei)); + IGRAPH_CHECK(igraph_vector_int_push_back(vertex_index, nei)); } if (from == nei) { - igraph_vector_push_back(&edges, v_ptr++); - igraph_vector_push_back(&edges, to); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); } else { - igraph_vector_push_back(&edges, from); - igraph_vector_push_back(&edges, v_ptr++); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); + IGRAPH_CHECK(igraph_vector_int_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. @@ -241,11 +242,13 @@ static int igraph_i_is_tree_visitor(const igraph_t *graph, igraph_integer_t root * Time complexity: At most O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * - * \sa igraph_is_weakly_connected() + * \sa \ref igraph_is_connected() * - * \example examples/simple/igraph_tree.c + * \example examples/simple/igraph_kary_tree.c */ -int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) { +igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) { + igraph_bool_t is_tree = false; + igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; igraph_integer_t iroot = 0; igraph_integer_t visited_count; igraph_integer_t vcount, ecount; @@ -253,20 +256,34 @@ int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t * vcount = igraph_vcount(graph); ecount = igraph_ecount(graph); + /* For undirected graphs and for directed graphs with mode == IGRAPH_ALL, + * we can return early if we know from the cache that the graph is weakly + * connected and is a forest. We can do this even if the user wants the + * root vertex because we always return zero as the root vertex for + * undirected graphs */ + if (treat_as_undirected && + igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && + igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST) && + igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) + ) { + is_tree = true; + iroot = 0; + goto success; + } + /* 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; + is_tree = false; + goto 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; + is_tree = true; + iroot = 0; + goto success; } /* For higher vertex counts we cannot short-circuit due to the possibility @@ -287,7 +304,7 @@ int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t * * we choose 0. */ - *res = 1; /* assume success */ + is_tree = true; /* assume success */ switch (mode) { case IGRAPH_ALL: @@ -296,11 +313,11 @@ int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t * case IGRAPH_IN: case IGRAPH_OUT: { - igraph_vector_t degree; + igraph_vector_int_t degree; igraph_integer_t i; - IGRAPH_CHECK(igraph_vector_init(°ree, 0)); - IGRAPH_FINALLY(igraph_vector_destroy, °ree); + IGRAPH_CHECK(igraph_vector_int_init(°ree, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, °ree); IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN, /* loops = */ 1)); @@ -316,36 +333,369 @@ int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t * * improve performance when the graph is indeed a tree, persumably * the most common case. Thus we only check until finding the root. */ - *res = 0; + is_tree = false; break; } } /* If no suitable root is found, the graph is not a tree. */ - if (*res && i == vcount) { - *res = 0; + if (is_tree && i == vcount) { + is_tree = false; } else { iroot = i; } - igraph_vector_destroy(°ree); + igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(1); } break; default: - IGRAPH_ERROR("Invalid mode,", IGRAPH_EINVMODE); + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); } /* if no suitable root was found, skip visiting vertices */ - if (*res) { + if (is_tree) { IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); - *res = visited_count == vcount; + is_tree = visited_count == vcount; + } + +success: + if (res) { + *res = is_tree; } if (root) { *root = iroot; } + if (is_tree && treat_as_undirected) { + /* For undirected graphs (or directed graphs that are treated as + * undirected in this calculation), a tree is weakly connected and is + * a forest, so we can cache this */ + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, 1); + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, 1); + } + + return IGRAPH_SUCCESS; +} + +/* igraph_is_forest() -- check if a graph is a forest */ + +/* Verify that the graph has no cycles and count the number of reachable vertices. + * This function performs a DFS starting from 'root'. + * If it finds a cycle, it sets *res to false, otherwise it does not change it. + * *visited_count will be incremented by the number of vertices reachable from 'root', + * including 'root' itself. + */ +static igraph_error_t igraph_i_is_forest_visitor( + const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, + igraph_vector_bool_t *visited, igraph_stack_int_t *stack, igraph_vector_int_t *neis, + igraph_integer_t *visited_count, igraph_bool_t *res) +{ + igraph_integer_t i; + + igraph_stack_int_clear(stack); + + /* push the root onto the stack */ + IGRAPH_CHECK(igraph_stack_int_push(stack, root)); + + while (! igraph_stack_int_empty(stack)) { + igraph_integer_t u; + igraph_integer_t ncount; + + /* Take a vertex from stack and check if it is already visited. + * If yes, then we found a cycle: the graph is not a forest. + * Otherwise mark it as visited and continue. + */ + u = igraph_stack_int_pop(stack); + if (IGRAPH_LIKELY(! VECTOR(*visited)[u])) { + VECTOR(*visited)[u] = true; + *visited_count += 1; + } + else { + *res = 0; + break; + } + + /* Vertex discovery: Register all its neighbours for future processing */ + IGRAPH_CHECK(igraph_neighbors(graph, neis, u, mode)); + ncount = igraph_vector_int_size(neis); + + for (i = 0; i < ncount; ++i) { + igraph_integer_t v = VECTOR(*neis)[i]; + + if (mode == IGRAPH_ALL) { + /* In the undirected case, we avoid returning to the predecessor + * vertex of 'v' in the DFS tree by skipping visited vertices. + * + * Note that in order to succcessfully detect a cycle, a vertex + * within that cycle must end up on the stack more than once. + * Does skipping visited vertices preclude this sometimes? + * No, because any visited vertex can only be accessed through + * an already discovered vertex (i.e. one that has already been + * pushed onto the stack). + */ + if (IGRAPH_LIKELY(! VECTOR(*visited)[v])) { + IGRAPH_CHECK(igraph_stack_int_push(stack, v)); + } + /* To check for a self-loop in undirected graph */ + else if (v == u) { + *res = 0; + break; + } + } + else { + IGRAPH_CHECK(igraph_stack_int_push(stack, v)); + } + } + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_i_is_forest( + const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode +); + +/** + * \ingroup structural + * \function igraph_is_forest + * \brief Decides whether the graph is a forest. + * + * An undirected graph is a forest if it has no cycles. + * + * + * In the directed case, a possible additional requirement is that edges in each + * tree are oriented away from the root (out-trees or arborescences) or all edges + * are oriented towards the root (in-trees or anti-arborescences). + * This test can be controlled using the \p mode parameter. + * + * + * By convention, the null graph (i.e. the graph with no vertices) is considered to be a forest. + * + * + * The \p res return value of this function is cached in the graph itself if + * \p mode is set to \c IGRAPH_ALL or if the graph is undirected. Calling the + * function multiple times with no modifications to the graph in between + * will return a cached value in O(1) time if the roots are not asked for. + * + * \param graph The graph object to analyze. + * \param res Pointer to a logical variable. If not \c NULL, then the result will be stored + * here. + * \param roots If not \c NULL, the root nodes will be stored here. When \p mode + * is \c IGRAPH_ALL or the graph is undirected, any one vertex from each + * component can be the root. When \p mode is \c IGRAPH_OUT + * or \c IGRAPH_IN, all the vertices with zero in- or out-degree, + * respectively are considered as root nodes. + * \param mode For a directed graph this specifies whether to test for an + * out-forest, an in-forest 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_EINVMODE: invalid mode argument. + * + * Time complexity: At most O(|V|+|E|), the + * number of vertices plus the number of edges in the graph. + */ +igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode) { + /* Caching is enabled only if the graph is undirected or mode == IGRAPH_ALL. + * Also, we can't return early if we need to calculate the roots */ + igraph_bool_t use_cache = ( + !igraph_is_directed(graph) || mode == IGRAPH_ALL + ); + + if (!roots && !res) { + return IGRAPH_SUCCESS; + } + + if (use_cache && !roots && res) { + IGRAPH_RETURN_IF_CACHED_BOOL(graph, IGRAPH_PROP_IS_FOREST, res); + } + + IGRAPH_CHECK(igraph_i_is_forest(graph, res, roots, mode)); + + /* At this point we know whether the graph is a forest if we have at least + * one of 'res' or 'roots' */ + if (res) { + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, *res); + } else if (roots) { + igraph_i_property_cache_set_bool(graph, IGRAPH_PROP_IS_FOREST, !igraph_vector_int_empty(roots)); + } + return IGRAPH_SUCCESS; } + +static igraph_error_t igraph_i_is_forest( + const igraph_t *graph, igraph_bool_t *res, + igraph_vector_int_t *roots, igraph_neimode_t mode +) { + igraph_vector_bool_t visited; + igraph_vector_int_t neis; + igraph_stack_int_t stack; + igraph_integer_t visited_count = 0; + igraph_integer_t vcount, ecount; + igraph_integer_t v; + igraph_bool_t result; + + vcount = igraph_vcount(graph); + ecount = igraph_ecount(graph); + + if (roots) { + igraph_vector_int_clear(roots); + } + + /* Any graph with 0 edges is a forest. */ + if (ecount == 0) { + if (res) { + *res = true; + } + if (roots) { + for (v = 0; v < vcount; v++) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + } + return IGRAPH_SUCCESS; + } + + /* A forest can have at most vcount-1 edges. */ + if (ecount > vcount - 1) { + if (res) { + *res = false; + } + return IGRAPH_SUCCESS; + } + + /* Ignore mode for undirected graphs. */ + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + result = true; /* assume success */ + + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, vcount); + + IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); + IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); + + /* The main algorithm: + * + * Undirected Graph:- We add each unvisited vertex to the roots vector, and + * mark all other vertices that are reachable from it as visited. + * + * Directed Graph:- For each tree, the root is the node with no + * incoming/outgoing connections, depending on 'mode'. We add each vertex + * with zero degree to the roots vector and mark all other vertices that are + * reachable from it as visited. + * + * If all the vertices are visited exactly once, then the graph is a forest. + */ + + switch (mode) { + case IGRAPH_ALL: + { + for (v = 0; v < vcount; ++v) { + if (!result) { + break; + } + if (! VECTOR(visited)[v]) { + if (roots) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + IGRAPH_CHECK(igraph_i_is_forest_visitor( + graph, v, mode, + &visited, &stack, &neis, + &visited_count, &result)); + } + } + break; + } + + case IGRAPH_IN: + case IGRAPH_OUT: + { + igraph_vector_int_t degree; + + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), + IGRAPH_REVERSE_MODE(mode), /* loops = */ 1)); + + for (v = 0; v < vcount; ++v) { + /* In an out-tree, roots have in-degree 0, + * and all other vertices have in-degree 1. */ + if (VECTOR(degree)[v] > 1 || !result) { + result = false; + break; + } + if (VECTOR(degree)[v] == 0) { + if (roots) { + IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); + } + IGRAPH_CHECK(igraph_i_is_forest_visitor( + graph, v, mode, + &visited, &stack, &neis, + &visited_count, &result)); + } + } + + igraph_vector_int_destroy(°ree); + IGRAPH_FINALLY_CLEAN(1); + break; + } + + default: + IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); + } + + if (result) { + /* In a forest, all vertices are reachable from the roots. */ + result = (visited_count == vcount); + } + + if (res) { + *res = result; + } + + /* If the graph is not a forest then the root vector will be empty. */ + if (!result && roots) { + igraph_vector_int_clear(roots); + } + + igraph_vector_int_destroy(&neis); + igraph_stack_int_destroy(&stack); + igraph_vector_bool_destroy(&visited); + IGRAPH_FINALLY_CLEAN(3); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_is_acyclic + * \brief Checks whether a graph is acyclic or not. + * + * This function checks whether a graph is acyclic or not. + * + * \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. + */ +igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res) { + if (igraph_is_directed(graph)) { + /* igraph_is_dag is cached */ + return igraph_is_dag(graph, res); + } else { + /* igraph_is_forest is cached if mode == IGRAPH_ALL and we don't need + * the roots */ + return igraph_is_forest(graph, res, NULL, IGRAPH_ALL); + } +} diff --git a/src/vendor/cigraph/src/properties/triangles.c b/src/vendor/cigraph/src/properties/triangles.c index 4e787f34602..c3069116690 100644 --- a/src/vendor/cigraph/src/properties/triangles.c +++ b/src/vendor/cigraph/src/properties/triangles.c @@ -77,7 +77,7 @@ * graph and d is the average degree. */ -int igraph_transitivity_avglocal_undirected(const igraph_t *graph, +igraph_error_t igraph_transitivity_avglocal_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { @@ -97,7 +97,7 @@ int igraph_transitivity_avglocal_undirected(const igraph_t *graph, 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])) { + if (!isnan(VECTOR(vec)[i])) { sum += VECTOR(vec)[i]; } else { nans++; @@ -113,7 +113,7 @@ int igraph_transitivity_avglocal_undirected(const igraph_t *graph, return IGRAPH_SUCCESS; } -int igraph_transitivity_local_undirected1(const igraph_t *graph, +static igraph_error_t igraph_transitivity_local_undirected1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { @@ -125,19 +125,21 @@ int igraph_transitivity_local_undirected1(const igraph_t *graph, return IGRAPH_SUCCESS; } -int igraph_transitivity_local_undirected2(const igraph_t *graph, +static igraph_error_t 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_integer_t 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_integer_t nodes_to_calc, affected_nodes; + igraph_integer_t maxdegree = 0; + igraph_integer_t i, j, k, nn; igraph_lazy_adjlist_t adjlist; - igraph_vector_t indexv, avids, rank, order, triangles, degree; - long int *neis; + igraph_vector_int_t degree; + igraph_vector_t indexv, avids, rank, triangles; + igraph_vector_int_t order; + igraph_integer_t *neis; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); @@ -151,18 +153,20 @@ int igraph_transitivity_local_undirected2(const igraph_t *graph, 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_integer_t v = IGRAPH_VIT_GET(vit); igraph_vector_int_t *neis2; - long int neilen; + igraph_integer_t 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); + neis2 = igraph_lazy_adjlist_get(&adjlist, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + neilen = igraph_vector_int_size(neis2); for (j = 0; j < neilen; j++) { - long int nei = (long int) VECTOR(*neis2)[j]; + igraph_integer_t nei = VECTOR(*neis2)[j]; if (VECTOR(indexv)[nei] == 0) { VECTOR(indexv)[nei] = k + 1; k++; IGRAPH_CHECK(igraph_vector_push_back(&avids, nei)); @@ -172,65 +176,66 @@ int igraph_transitivity_local_undirected2(const igraph_t *graph, /* Degree, ordering, ranking */ affected_nodes = igraph_vector_size(&avids); - IGRAPH_VECTOR_INIT_FINALLY(&order, 0); - IGRAPH_VECTOR_INIT_FINALLY(°ree, affected_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(&order, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, affected_nodes); for (i = 0; i < affected_nodes; i++) { - long int v = (long int) VECTOR(avids)[i]; + igraph_integer_t v = VECTOR(avids)[i]; igraph_vector_int_t *neis2; - long int deg; - neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v); + igraph_integer_t deg; + neis2 = igraph_lazy_adjlist_get(&adjlist, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); 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_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree + 1)); + igraph_vector_int_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; + VECTOR(rank)[ VECTOR(order)[i] ] = affected_nodes - i - 1; } - neis = IGRAPH_CALLOC(no_of_nodes, long int); + neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (neis == 0) { - IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } 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_integer_t node = VECTOR(avids) [ 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_integer_t neilen1, neilen2; + igraph_integer_t nodeindex = VECTOR(indexv)[node]; + igraph_integer_t noderank = VECTOR(rank) [nodeindex - 1]; IGRAPH_ALLOW_INTERRUPTION(); - neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + neis1 = igraph_lazy_adjlist_get(&adjlist, node); + IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); + neilen1 = igraph_vector_int_size(neis1); for (i = 0; i < neilen1; i++) { - long int nei = (long int) VECTOR(*neis1)[i]; + igraph_integer_t nei = 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]; + igraph_integer_t nei = VECTOR(*neis1)[i]; + igraph_integer_t neiindex = VECTOR(indexv)[nei]; + igraph_integer_t neirank = 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); + neis2 = igraph_lazy_adjlist_get(&adjlist, nei); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); 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]; + igraph_integer_t nei2 = VECTOR(*neis2)[j]; + igraph_integer_t nei2index = VECTOR(indexv)[nei2]; + igraph_integer_t nei2rank = VECTOR(rank)[nei2index - 1]; /* fprintf(stderr, " triple %li %li %li\n", node, nei, nei2); */ if (nei2rank < neirank) { continue; @@ -251,11 +256,14 @@ int igraph_transitivity_local_undirected2(const igraph_t *graph, 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); + igraph_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t idx = VECTOR(indexv)[node] - 1; + igraph_vector_int_t *neis2 = igraph_lazy_adjlist_get(&adjlist, node); + igraph_integer_t deg; + + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); + + deg = igraph_vector_int_size(neis2); if (mode == IGRAPH_TRANSITIVITY_ZERO && deg < 2) { VECTOR(*res)[i] = 0.0; } else { @@ -267,91 +275,35 @@ int igraph_transitivity_local_undirected2(const igraph_t *graph, igraph_vector_destroy(&triangles); igraph_free(neis); igraph_vector_destroy(&rank); - igraph_vector_destroy(&order); + igraph_vector_int_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; + return IGRAPH_SUCCESS; } -/* 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, +igraph_error_t igraph_i_trans4_al_simplify(igraph_adjlist_t *al, const igraph_vector_int_t *rank) { - long int i; - long int n = al->length; + igraph_integer_t i; + igraph_integer_t n = al->length; igraph_vector_int_t mark; - igraph_vector_int_init(&mark, n); + + IGRAPH_CHECK(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]; + igraph_integer_t j, l = igraph_vector_int_size(v); + igraph_integer_t irank = VECTOR(*rank)[i]; VECTOR(mark)[i] = i + 1; for (j = 0; j < l; /* nothing */) { - long int e = (long int) VECTOR(*v)[j]; + igraph_integer_t e = VECTOR(*v)[j]; if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) { VECTOR(mark)[e] = i + 1; j++; @@ -365,11 +317,11 @@ int igraph_i_trans4_al_simplify(igraph_adjlist_t *al, igraph_vector_int_destroy(&mark); IGRAPH_FINALLY_CLEAN(1); - return 0; + return IGRAPH_SUCCESS; } -int igraph_transitivity_local_undirected4(const igraph_t *graph, +static igraph_error_t igraph_transitivity_local_undirected4(const igraph_t *graph, igraph_vector_t *res, igraph_transitivity_mode_t mode) { @@ -377,12 +329,12 @@ int igraph_transitivity_local_undirected4(const igraph_t *graph, #include "properties/triangles_template.h" #undef TRANSIT - return 0; + return IGRAPH_SUCCESS; } /** * \function igraph_transitivity_local_undirected - * \brief Calculates the local transitivity (clustering coefficient) of a graph. + * \brief The local transitivity (clustering coefficient) of some vertices. * * The transitivity measures the probability that two neighbors of a * vertex are connected. In case of the local transitivity, this @@ -420,7 +372,7 @@ int igraph_transitivity_local_undirected4(const igraph_t *graph, * the transitivity is calculated, d is the average vertex degree. */ -int igraph_transitivity_local_undirected(const igraph_t *graph, +igraph_error_t igraph_transitivity_local_undirected(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids, igraph_transitivity_mode_t mode) { @@ -429,7 +381,7 @@ int igraph_transitivity_local_undirected(const igraph_t *graph, return igraph_transitivity_local_undirected4(graph, res, mode); } else { igraph_vit_t vit; - long int size; + igraph_integer_t size; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); size = IGRAPH_VIT_SIZE(vit); @@ -443,17 +395,17 @@ int igraph_transitivity_local_undirected(const igraph_t *graph, } } -static int igraph_adjacent_triangles1(const igraph_t *graph, +static igraph_error_t igraph_adjacent_triangles1(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { # include "properties/triangles_template1.h" - return 0; + return IGRAPH_SUCCESS; } -static int igraph_adjacent_triangles4(const igraph_t *graph, +static igraph_error_t igraph_adjacent_triangles4(const igraph_t *graph, igraph_vector_t *res) { # include "properties/triangles_template.h" - return 0; + return IGRAPH_SUCCESS; } /** @@ -471,7 +423,7 @@ static int igraph_adjacent_triangles4(const igraph_t *graph, * queried vertices, n is their number. */ -int igraph_adjacent_triangles(const igraph_t *graph, +igraph_error_t igraph_adjacent_triangles(const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids) { if (igraph_vs_is_all(&vids)) { @@ -485,10 +437,16 @@ int igraph_adjacent_triangles(const igraph_t *graph, * \function igraph_list_triangles * \brief Find all triangles in a graph. * + * + * The triangles are reported as a long list of vertex ID triplets. Use + * the \c int variant of \ref igraph_matrix_view_from_vector() to create a + * matrix view into the vector where each triangle is stored in a column of the + * matrix (see the example). + * * \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. + * 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. @@ -499,9 +457,11 @@ int igraph_adjacent_triangles(const igraph_t *graph, * * Time complexity: O(d^2 n), d is the average degree, n is the number * of vertices. + * + * \example examples/simple/igraph_list_triangles.c */ -int igraph_list_triangles(const igraph_t *graph, +igraph_error_t igraph_list_triangles(const igraph_t *graph, igraph_vector_int_t *res) { # define TRIANGLES # include "properties/triangles_template.h" @@ -554,74 +514,74 @@ int igraph_list_triangles(const igraph_t *graph, * \example examples/simple/igraph_transitivity.c */ -int igraph_transitivity_undirected(const igraph_t *graph, +igraph_error_t 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_integer_t 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_integer_t node, nn; + igraph_integer_t maxdegree; + igraph_integer_t *neis; + igraph_vector_int_t order; igraph_vector_t rank; - igraph_vector_t degree; + igraph_vector_int_t degree; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; - long int i, j, neilen1, neilen2; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_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)); + maxdegree = igraph_vector_int_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); - igraph_vector_destroy(°ree); + igraph_vector_int_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; + VECTOR(rank)[ 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); + neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (! neis) { - IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); + IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, neis); for (nn = no_of_nodes - 1; nn >= 0; nn--) { - node = (long int) VECTOR(order)[nn]; + node = VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1 = igraph_adjlist_get(&allneis, node); neilen1 = igraph_vector_int_size(neis1); - triples += (double)neilen1 * (neilen1 - 1); + triples += (igraph_real_t)neilen1 * (neilen1 - 1); /* Mark the neighbors of 'node' */ for (i = 0; i < neilen1; i++) { - long int nei = (long int) VECTOR(*neis1)[i]; + igraph_integer_t nei = VECTOR(*neis1)[i]; neis[nei] = node + 1; } for (i = 0; i < neilen1; i++) { - long int nei = (long int) VECTOR(*neis1)[i]; + igraph_integer_t nei = 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]; + igraph_integer_t nei2 = VECTOR(*neis2)[j]; if (neis[nei2] == node + 1) { triangles += 1.0; } @@ -633,7 +593,7 @@ int igraph_transitivity_undirected(const igraph_t *graph, IGRAPH_FREE(neis); igraph_adjlist_destroy(&allneis); igraph_vector_destroy(&rank); - igraph_vector_destroy(&order); + igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(4); if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) { @@ -642,23 +602,24 @@ int igraph_transitivity_undirected(const igraph_t *graph, *res = triangles / triples * 2.0; } - return 0; + return IGRAPH_SUCCESS; } -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) { +static igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; - long int nodes_to_calc; + igraph_integer_t nodes_to_calc; igraph_vector_int_t *adj1, *adj2; - igraph_vector_long_t neis; + igraph_vector_int_t neis; igraph_vector_t actw; igraph_lazy_inclist_t incident; - long int i; + igraph_integer_t i; igraph_vector_t strength; /* Precondition: weight vector is not null, its length equals the number of @@ -670,8 +631,8 @@ static int igraph_i_transitivity_barrat1(const igraph_t *graph, 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_CHECK(igraph_vector_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes); @@ -685,18 +646,19 @@ static int igraph_i_transitivity_barrat1(const igraph_t *graph, 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_integer_t node = IGRAPH_VIT_GET(vit); + igraph_integer_t adjlen1, adjlen2, j, k; igraph_real_t triples, triangles; IGRAPH_ALLOW_INTERRUPTION(); - adj1 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) node); + adj1 = igraph_lazy_inclist_get(&incident, node); + IGRAPH_CHECK_OOM(adj1, "Failed to query incident edges."); 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); + igraph_integer_t edge = VECTOR(*adj1)[j]; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge, node); VECTOR(neis)[nei] = i + 1; VECTOR(actw)[nei] = VECTOR(*weights)[edge]; } @@ -704,14 +666,15 @@ static int igraph_i_transitivity_barrat1(const igraph_t *graph, triangles = 0.0; for (j = 0; j < adjlen1; j++) { - long int edge1 = (long int) VECTOR(*adj1)[j]; + igraph_integer_t edge1 = 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); + igraph_integer_t v = IGRAPH_OTHER(graph, edge1, node); + adj2 = igraph_lazy_inclist_get(&incident, v); + IGRAPH_CHECK_OOM(adj2, "Failed to query incident edges."); 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); + igraph_integer_t edge2 = VECTOR(*adj2)[k]; + igraph_integer_t v2 = IGRAPH_OTHER(graph, edge2, v); if (VECTOR(neis)[v2] == i + 1) { triangles += (VECTOR(actw)[v2] + weight1) / 2.0; } @@ -727,54 +690,58 @@ static int igraph_i_transitivity_barrat1(const igraph_t *graph, igraph_lazy_inclist_destroy(&incident); igraph_vector_destroy(&strength); igraph_vector_destroy(&actw); - igraph_vector_long_destroy(&neis); + igraph_vector_int_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) { +static igraph_error_t igraph_i_transitivity_barrat4( + const igraph_t *graph, + igraph_vector_t *res, + 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_vector_int_t order; + igraph_vector_int_t degree; + igraph_vector_t strength; + igraph_vector_t rank; + igraph_integer_t maxdegree; igraph_inclist_t incident; - igraph_vector_long_t neis; + igraph_vector_int_t neis; igraph_vector_int_t *adj1, *adj2; igraph_vector_t actw; - long int i, nn; + igraph_integer_t 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_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); + IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); + IGRAPH_VECTOR_INIT_FINALLY(&strength, 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)); + maxdegree = igraph_vector_int_max(°ree) + 1; + IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); - IGRAPH_CHECK(igraph_strength(graph, °ree, igraph_vss_all(), IGRAPH_ALL, + IGRAPH_CHECK(igraph_strength(graph, &strength, 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; + VECTOR(rank)[ 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_CHECK(igraph_vector_int_init(&neis, no_of_nodes)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &neis); IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes); @@ -782,35 +749,35 @@ static int igraph_i_transitivity_barrat4(const igraph_t *graph, igraph_vector_null(res); for (nn = no_of_nodes - 1; nn >= 0; nn--) { - long int adjlen1, adjlen2; + igraph_integer_t adjlen1, adjlen2; igraph_real_t triples; - long int node = (long int) VECTOR(order)[nn]; + igraph_integer_t node = 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; + triples = VECTOR(strength)[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); + igraph_integer_t edge = VECTOR(*adj1)[i]; + igraph_integer_t 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_integer_t edge1 = VECTOR(*adj1)[i]; igraph_real_t weight1 = VECTOR(*weights)[edge1]; - long int nei = IGRAPH_OTHER(graph, edge1, node); - long int j; + igraph_integer_t nei = IGRAPH_OTHER(graph, edge1, node); + igraph_integer_t 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_integer_t edge2 = VECTOR(*adj2)[j]; igraph_real_t weight2 = VECTOR(*weights)[edge2]; - long int nei2 = IGRAPH_OTHER(graph, edge2, nei); + igraph_integer_t nei2 = IGRAPH_OTHER(graph, edge2, nei); if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) { continue; } @@ -831,19 +798,20 @@ static int igraph_i_transitivity_barrat4(const igraph_t *graph, } igraph_vector_destroy(&actw); - igraph_vector_long_destroy(&neis); + igraph_vector_int_destroy(&neis); igraph_inclist_destroy(&incident); igraph_vector_destroy(&rank); - igraph_vector_destroy(°ree); - igraph_vector_destroy(&order); - IGRAPH_FINALLY_CLEAN(6); + igraph_vector_int_destroy(°ree); + igraph_vector_destroy(&strength); + igraph_vector_int_destroy(&order); + IGRAPH_FINALLY_CLEAN(7); return IGRAPH_SUCCESS; } /** * \function igraph_transitivity_barrat - * \brief Weighted transitivity, as defined by A. Barrat. + * \brief Weighted local transitivity of some vertices, 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 @@ -879,13 +847,13 @@ static int igraph_i_transitivity_barrat4(const igraph_t *graph, * (non-weighted) transitivity. */ -int igraph_transitivity_barrat(const igraph_t *graph, +igraph_error_t 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_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_bool_t has_multiple; /* Handle fallback to unweighted version and common cases */ @@ -897,8 +865,8 @@ int igraph_transitivity_barrat(const igraph_t *graph, } 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_ERRORF("Edge weight vector length (%" IGRAPH_PRId ") not equal to " + "number of edges (%" IGRAPH_PRId ").", IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); } @@ -908,17 +876,20 @@ int igraph_transitivity_barrat(const igraph_t *graph, } IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple)); + if (! has_multiple && igraph_is_directed(graph)) { + /* When the graph is directed, mutual edges are effectively multi-edges as we + * are ignoring edge directions. */ + IGRAPH_CHECK(igraph_has_mutual(graph, &has_multiple, false)); + } if (has_multiple) { - IGRAPH_ERROR( - "Barrat's weighted transitivity measure works only if the graph " - "has no multiple edges.", IGRAPH_EINVAL - ); + IGRAPH_ERROR("Barrat's weighted transitivity measure works only if the graph has no multi-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); + return igraph_i_transitivity_barrat4(graph, res, weights, mode); } else { return igraph_i_transitivity_barrat1(graph, res, vids, weights, mode); } diff --git a/src/vendor/cigraph/src/properties/triangles_template.h b/src/vendor/cigraph/src/properties/triangles_template.h index 61418fdb197..8b83da7317f 100644 --- a/src/vendor/cigraph/src/properties/triangles_template.h +++ b/src/vendor/cigraph/src/properties/triangles_template.h @@ -26,21 +26,21 @@ #define TRANSIT_TRIEDGES #endif -long int no_of_nodes = igraph_vcount(graph); -long int node, i, j, nn; +igraph_integer_t no_of_nodes = igraph_vcount(graph); +igraph_integer_t node, i, j, nn; igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; -long int neilen1, neilen2; -long int *neis; -long int maxdegree; +igraph_integer_t neilen1, neilen2; +igraph_integer_t *neis; +igraph_integer_t maxdegree; #ifdef TRANSIT_TRIEDGES -long int deg1; +igraph_integer_t deg1; #endif igraph_vector_int_t order; igraph_vector_int_t rank; -igraph_vector_t degree; +igraph_vector_int_t degree; if (no_of_nodes == 0) { #ifndef TRIANGLES @@ -51,9 +51,8 @@ if (no_of_nodes == 0) { 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_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); +IGRAPH_VECTOR_INT_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); @@ -62,19 +61,18 @@ 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); +maxdegree = igraph_vector_int_max(°ree) + 1; +IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); +IGRAPH_VECTOR_INT_INIT_FINALLY(&rank, no_of_nodes); 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); +neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (neis == 0) { - IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, neis); @@ -94,20 +92,20 @@ for (nn = no_of_nodes - 1; nn >= 0; nn--) { neilen1 = igraph_vector_int_size(neis1); #ifdef TRANSIT_TRIEDGES - deg1 = (long int) VECTOR(degree)[node]; + deg1 = VECTOR(degree)[node]; #endif /* Mark the neighbors of the node */ for (i = 0; i < neilen1; i++) { - neis[ (long int) VECTOR(*neis1)[i] ] = node + 1; + neis[ VECTOR(*neis1)[i] ] = node + 1; } for (i = 0; i < neilen1; i++) { - long int nei = (long int) VECTOR(*neis1)[i]; + igraph_integer_t nei = 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]; + igraph_integer_t nei2 = VECTOR(*neis2)[j]; if (neis[nei2] == node + 1) { #ifndef TRIANGLES VECTOR(*res)[nei2] += 1; @@ -134,7 +132,7 @@ for (nn = no_of_nodes - 1; nn >= 0; nn--) { igraph_free(neis); igraph_adjlist_destroy(&allneis); igraph_vector_int_destroy(&rank); -igraph_vector_destroy(°ree); +igraph_vector_int_destroy(°ree); igraph_vector_int_destroy(&order); IGRAPH_FINALLY_CLEAN(5); diff --git a/src/vendor/cigraph/src/properties/triangles_template1.h b/src/vendor/cigraph/src/properties/triangles_template1.h index 3ab191f2647..25329ea032f 100644 --- a/src/vendor/cigraph/src/properties/triangles_template1.h +++ b/src/vendor/cigraph/src/properties/triangles_template1.h @@ -22,14 +22,14 @@ */ -long int no_of_nodes = igraph_vcount(graph); +igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_vit_t vit; -long int nodes_to_calc; +igraph_integer_t 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_integer_t i, j, k; +igraph_integer_t neilen1, neilen2; +igraph_integer_t *neis; igraph_lazy_adjlist_t adjlist; IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); @@ -43,9 +43,9 @@ if (nodes_to_calc == 0) { return IGRAPH_SUCCESS; } -neis = IGRAPH_CALLOC(no_of_nodes, long int); +neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); if (neis == 0) { - IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); + IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ } IGRAPH_FINALLY(igraph_free, neis); @@ -55,23 +55,25 @@ IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOO 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_integer_t node = IGRAPH_VIT_GET(vit); IGRAPH_ALLOW_INTERRUPTION(); - neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node); + neis1 = igraph_lazy_adjlist_get(&adjlist, node); + IGRAPH_CHECK_OOM(neis1, "Failed to query neighbors."); neilen1 = igraph_vector_int_size(neis1); for (j = 0; j < neilen1; j++) { - neis[ (long int)VECTOR(*neis1)[j] ] = i + 1; + neis[ 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); + igraph_integer_t v = VECTOR(*neis1)[j]; + neis2 = igraph_lazy_adjlist_get(&adjlist, v); + IGRAPH_CHECK_OOM(neis2, "Failed to query neighbors."); neilen2 = igraph_vector_int_size(neis2); for (k = 0; k < neilen2; k++) { - long int v2 = (long int) VECTOR(*neis2)[k]; + igraph_integer_t v2 = VECTOR(*neis2)[k]; if (neis[v2] == i + 1) { triangles += 1.0; } diff --git a/src/vendor/cigraph/src/random/random.c b/src/vendor/cigraph/src/random/random.c index 40c423791ca..15ef86e12c6 100644 --- a/src/vendor/cigraph/src/random/random.c +++ b/src/vendor/cigraph/src/random/random.c @@ -21,32 +21,41 @@ */ + #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 "math/safe_intop.h" +#include "random/random_internal.h" + +#include "config.h" /* IGRAPH_THREAD_LOCAL, HAVE___UINT128_T, HAVE__UMUL128 */ + +#if defined(HAVE__UMUL128) || defined(HAVE___UMULH) +#include /* _umul128() or __umulh() are defined in intrin.h */ +#endif -#include "config.h" +#include #include -#include +#include /* DBL_MANT_DIG */ /** * \section about_rngs * *
- * About random numbers in igraph, use cases + * About random numbers in igraph * * - * 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. + * Some algorithms in igraph, such as sampling from random graph models, + * require random number generators (RNGs). igraph includes a flexible + * RNG framework that allows hooking up arbitrary random number generators, + * and comes with several ready-to-use generators. This framework is used + * in igraph's high-level interfaces to integrate with the host language's + * own RNG. * *
* @@ -122,377 +131,6 @@ /* ------------------------------------ */ -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 @@ -507,18 +145,22 @@ igraph_i_rng_mt19937_state_t igraph_i_rng_default_state; * igraph_rng_set_default() function. */ -IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { - addr(igraph_rngtype_mt19937), - addr(igraph_i_rng_default_state), - /* def= */ 1 -}; - -#undef addr +extern IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default; /* defined in rng_pcg32.c */ /** * \function igraph_rng_set_default * \brief Set the default igraph random number generator. * + * This function \em copies the internal structure of the given \type igraph_rng_t + * object to igraph's internal default RNG structure. The structure itself + * contains two pointers only, one to the "methods" of the RNG and one to the + * memory buffer holding the internal state of the RNG. This means that if you + * keep on generating random numbers from the RNG after setting it as the + * default, it will affect the state of the default RNG as well because the two + * share the same state pointer. However, do \em not expect + * \ref igraph_rng_default() to return the same pointer as the one you passed + * in here - the state is shared, but the entire structure is not. + * * \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 @@ -540,7 +182,7 @@ void igraph_rng_set_default(igraph_rng_t *rng) { * * \return A pointer to the default random number generator. * - * \sa igraph_rng_set_default() + * \sa \ref igraph_rng_set_default() */ igraph_rng_t *igraph_rng_default(void) { @@ -549,29 +191,42 @@ igraph_rng_t *igraph_rng_default(void) { /* ------------------------------------ */ -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); +static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits); +static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits); + +static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng); +static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range); + +static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng); +static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range); + +#if IGRAPH_INTEGER_SIZE == 64 +static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng); +static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range); +#endif + +static double igraph_i_norm_rand(igraph_rng_t *rng); +static double igraph_i_exp_rand(igraph_rng_t *rng); +static double igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp); +static double igraph_i_rexp(igraph_rng_t *rng, double rate); +static double igraph_i_rgamma(igraph_rng_t *rng, double shape, double scale); +static double igraph_i_rpois(igraph_rng_t *rng, double rate); /** * \function igraph_rng_init - * \brief Initialize a random number generator. + * \brief Initializes 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. + * \param type The type of the RNG, such as \ref igraph_rngtype_mt19937, + * \ref igraph_rngtype_glibc2, \ref igraph_rngtype_pcg32 or + * \ref igraph_rngtype_pcg64. * \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) { +igraph_error_t 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; @@ -579,7 +234,7 @@ int igraph_rng_init(igraph_rng_t *rng, const igraph_rng_type_t *type) { /** * \function igraph_rng_destroy - * \brief Deallocate memory associated with a random number generator. + * \brief Deallocates 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. @@ -593,7 +248,7 @@ void igraph_rng_destroy(igraph_rng_t *rng) { /** * \function igraph_rng_seed - * \brief Set the seed of a random number generator. + * \brief Seeds a random number generator. * * \param rng The RNG. * \param seed The new seed. @@ -602,67 +257,295 @@ void igraph_rng_destroy(igraph_rng_t *rng) { * 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) { +igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed) { const igraph_rng_type_t *type = rng->type; - rng->def = 0; IGRAPH_CHECK(type->seed(rng->state, seed)); + rng->is_seeded = 1; return IGRAPH_SUCCESS; } /** - * \function igraph_rng_max - * \brief Query the maximum possible integer for a random number generator. + * \function igraph_rng_bits + * \brief The number of random bits that a random number generator can produces in a single round. * * \param rng The RNG. - * \return The largest possible integer that can be generated by - * calling \ref igraph_rng_get_integer() on the RNG. + * \return The number of random bits that can be generated in a single round + * with 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; +IGRAPH_EXPORT igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng) { + return rng->type->bits; } /** - * \function igraph_rng_min - * \brief Query the minimum possible integer for a random number generator. + * \function igraph_rng_max + * \brief The maximum possible integer for a random number generator. * - * This function will be removed in a future version. Assume zero - * as the retun value. + * Note that this number is only for informational purposes; it returns the + * maximum possible integer that can be generated with the RNG with a single + * call to its internals. It is derived directly from the number of random + * \em bits that the RNG can generate in a single round. When this is smaller + * than what would be needed by other RNG functions like \ref igraph_rng_get_integer(), + * igraph will call the RNG multiple times to generate more random bits. * * \param rng The RNG. - * \return The smallest possible integer that can be generated by - * calling \ref igraph_rng_get_integer() on the RNG. + * \return The largest possible integer that can be generated in a single round + * with the RNG. * * Time complexity: O(1). - * - * \deprecated 0.9.3 */ -unsigned long int igraph_rng_min(igraph_rng_t *rng) { +igraph_uint_t igraph_rng_max(const 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; +#if IGRAPH_INTEGER_SIZE == 64 + return (type->bits >= 64) ? 0xFFFFFFFFFFFFFFFFULL : ((1ULL << type->bits) - 1); +#else + return (type->bits >= 32) ? 0xFFFFFFFFUL : ((1ULL << type->bits) - 1); +#endif } /** * \function igraph_rng_name - * \brief Query the type of a random number generator. + * \brief 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. + * change the returned string. * * Time complexity: O(1). */ -const char *igraph_rng_name(igraph_rng_t *rng) { +const char *igraph_rng_name(const igraph_rng_t *rng) { const igraph_rng_type_t *type = rng->type; return type->name; } +/** + * Generates a given number of random bits, possibly invoking the underlying + * RNG multiple times if needed, and returns the result in an \c igraph_uint_t . + * + * \param rng The RNG. + * \param bits The number of random bits needed. Must be smaller than or equal + * to the size of the \c igraph_uint_t data type. Passing a value larger + * than the size of \c igraph_uint_t will throw away random bits except + * the last few that are needed to fill an \c igraph_uint_t . + * \return The random bits, packed into the low bits of an \c igraph_uint_t . + * The upper, unused bits of \c igraph_uint_t will be set to zero. + */ +static igraph_uint_t igraph_i_rng_get_random_bits(igraph_rng_t *rng, uint8_t bits) { + const igraph_rng_type_t *type = rng->type; + igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); + igraph_uint_t result; + + if (rng_bitwidth >= bits) { + /* keep the high bits as RNGs sometimes tend to have lower entropy in + * low bits than in high bits */ + result = type->get(rng->state) >> (rng_bitwidth - bits); + } else { + result = 0; + do { + result = (result << rng_bitwidth) + type->get(rng->state); + bits -= rng_bitwidth; + } while (bits > rng_bitwidth); + + /* and now the last piece */ + result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); + } + + return result; +} + +/** + * Generates a given number of random bits, possibly invoking the underlying + * RNG multiple times if needed, and returns the result in an \c uint64_t . + * + * Prefer \c igraph_i_rng_get_random_bits() if you know that you need at most + * 32 bits due to the type of the return value. This function might perform + * worse on 32-bit platforms because the result is always 64 bits. + * + * \param rng The RNG. + * \param bits The number of random bits needed. Must be smaller than or equal + * to the size of the \c uint64_t data type. Passing a value larger + * than the size of \c uint64_t will throw away random bits except + * the last few that are needed to fill an \c uint64_t . + * \return The random bits, packed into the low bits of an \c uint64_t . + * The upper, unused bits of \c uint64_t will be set to zero. + */ +static uint64_t igraph_i_rng_get_random_bits_uint64(igraph_rng_t *rng, uint8_t bits) { + const igraph_rng_type_t *type = rng->type; + igraph_integer_t rng_bitwidth = igraph_rng_bits(rng); + uint64_t result; + + if (rng_bitwidth >= bits) { + /* keep the high bits as RNGs sometimes tend to have lower entropy in + * low bits than in high bits */ + result = type->get(rng->state) >> (rng_bitwidth - bits); + } else { + result = 0; + do { + result = (result << rng_bitwidth) + type->get(rng->state); + bits -= rng_bitwidth; + } while (bits > rng_bitwidth); + + /* and now the last piece */ + result = (result << bits) + (type->get(rng->state) >> (rng_bitwidth - bits)); + } + + return result; +} + +/** + * Generates a random integer in the full range of the \c igraph_uint_t + * data type. + * + * \param rng The RNG. + * \return The random integer. + */ +static igraph_uint_t igraph_i_rng_get_uint(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, sizeof(igraph_uint_t) * 8); +} + +/** + * Generates a random integer in the full range of the \c uint32_t + * data type. + * + * \param rng The RNG. + * \return The random integer. + */ +static uint32_t igraph_i_rng_get_uint32(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, 32); +} + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive), + * restricted to at most 32 bits. + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static uint32_t igraph_i_rng_get_uint32_bounded(igraph_rng_t *rng, uint32_t range) { + /* Debiased integer multiplication -- Lemire's method + * from https://www.pcg-random.org/posts/bounded-rands.html */ + uint32_t x, l, t = (-range) % range; + uint64_t m; + do { + x = igraph_i_rng_get_uint32(rng); + m = (uint64_t)(x) * (uint64_t)(range); + l = (uint32_t)m; + } while (l < t); + return m >> 32; +} + +#if IGRAPH_INTEGER_SIZE == 64 +/** + * Generates a random integer in the full range of the \c uint64_t + * data type. + * + * \param rng The RNG. + * \param range The upper bound (inclusive). + * \return The random integer. + */ +static uint64_t igraph_i_rng_get_uint64(igraph_rng_t *rng) { + return igraph_i_rng_get_random_bits(rng, 64); +} + +#if !defined(HAVE___UINT128_T) +static uint64_t igraph_i_umul128(uint64_t a, uint64_t b, uint64_t *hi) { +#if defined(HAVE__UMUL128) + /* MSVC has _umul128() on x64 but not on arm64 */ + return _umul128(a, b, hi); +#elif defined(HAVE___UMULH) + /* MSVC has __umulh() on arm64 */ + *hi = __umulh(a, b); + return a*b; +#else + /* Portable but slow fallback implementation of unsigned + * 64-bit multiplication obtaining a 128-bit result. + * Based on https://stackoverflow.com/a/28904636/695132 + */ + + uint64_t a_lo = (uint32_t) a; + uint64_t a_hi = a >> 32; + uint64_t b_lo = (uint32_t) b; + uint64_t b_hi = b >> 32; + + uint64_t a_x_b_hi = a_hi * b_hi; + uint64_t a_x_b_mid = a_hi * b_lo; + uint64_t b_x_a_mid = b_hi * a_lo; + uint64_t a_x_b_lo = a_lo * b_lo; + + uint64_t carry_bit = ((uint64_t) (uint32_t) a_x_b_mid + + (uint64_t) (uint32_t) b_x_a_mid + + (a_x_b_lo >> 32) ) >> 32; + + *hi = a_x_b_hi + + (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + + carry_bit; + + return a*b; +#endif +} +#endif /* !defined(HAVE___UINT128_T) */ + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive), + * restricted to at most 64 bits. + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static uint64_t igraph_i_rng_get_uint64_bounded(igraph_rng_t *rng, uint64_t range) { + /* Debiased integer multiplication -- Lemire's method + * from https://www.pcg-random.org/posts/bounded-rands.html */ + uint64_t x, l, t = (-range) % range; +#if defined(HAVE___UINT128_T) + /* gcc and clang have __uint128_t */ + __uint128_t m; + do { + x = igraph_i_rng_get_uint64(rng); + m = (__uint128_t)(x) * (__uint128_t)(range); + l = (uint64_t)m; + } while (l < t); + return m >> 64; +#else + uint64_t hi; + do { + x = igraph_i_rng_get_uint64(rng); + l = igraph_i_umul128(x, range, &hi); + } while (l < t); + return hi; +#endif +} + +#endif /* IGRAPH_INTEGER_SIZE == 64 */ + +/** + * Generates a random integer in the range [0; range) (upper bound exclusive). + * + * \param rng The RNG. + * \param range The upper bound (exclusive). + * \return The random integer. + */ +static igraph_uint_t igraph_i_rng_get_uint_bounded(igraph_rng_t *rng, igraph_uint_t range) { + /* We must make this function behave the same way for range < 2^32 so igraph + * behaves the same way on 32-bit and 64-bit platforms as long as we stick + * to integers less than 2^32. This is to ensure that the unit tests are + * consistent */ + +#if IGRAPH_INTEGER_SIZE == 32 + return igraph_i_rng_get_uint32_bounded(rng, range); +#else + if (range <= UINT32_MAX) { + return igraph_i_rng_get_uint32_bounded(rng, range); + } else { + return igraph_i_rng_get_uint64_bounded(rng, range); + } +#endif +} + /** * \function igraph_rng_get_integer * \brief Generate an integer random number from an interval. @@ -674,43 +557,57 @@ const char *igraph_rng_name(igraph_rng_t *rng) { * should be at least l. * \return The generated random integer. * - * Time complexity: depends on the generator, but should be usually - * O(1). + * Time complexity: O(log2(h-l) / bits) where bits is the value of + * \ref igraph_rng_bits(rng). */ -long int igraph_rng_get_integer(igraph_rng_t *rng, - long int l, long int h) { +igraph_integer_t igraph_rng_get_integer( + igraph_rng_t *rng, igraph_integer_t l, igraph_integer_t 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_uint_t range; + + assert(h >= l); + + if (h == l) { + return l; } - IGRAPH_FATAL("Internal random generator error"); + + if (type->get_int) { + return type->get_int(rng->state, l, h); + } + + if (IGRAPH_UNLIKELY(l == IGRAPH_INTEGER_MIN && h == IGRAPH_INTEGER_MAX)) { + /* Full uint range is needed, we can just grab a random number from + * the uint range and cast it to a signed integer */ + return (igraph_integer_t) igraph_i_rng_get_uint(rng); + } else if (l >= 0 || h < 0) { + /* this is okay, (h - l) will not overflow an igraph_integer_t */ + range = (igraph_uint_t)(h - l) + 1; + } else { + /* (h - l) could potentially overflow so we need to play it safe. If we + * are here, l < 0 and h >= 0 so we can cast -l into an igraph_uint_t + * safely and do the subtraction that way */ + range = ((igraph_uint_t)(h)) + ((igraph_uint_t)(-l)) + 1; + } + + return l + igraph_i_rng_get_uint_bounded(rng, range); } /** * \function igraph_rng_get_normal - * \brief Normally distributed random numbers. + * \brief Samples from a normal distribution. + * + * Generates random variates from a normal distribution with probability + * density + * + * + * exp( -(x - m)^2 / (2 s^2) ). * * \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. + * \param s The standard deviation. * \return The generated normally distributed random number. * * Time complexity: depends on the type of the RNG. @@ -722,13 +619,16 @@ igraph_real_t igraph_rng_get_normal(igraph_rng_t *rng, if (type->get_norm) { return type->get_norm(rng->state) * s + m; } else { - return igraph_norm_rand(rng) * s + m; + return igraph_i_norm_rand(rng) * s + m; } } /** * \function igraph_rng_get_unif - * \brief Generate real, uniform random numbers from an interval. + * \brief Samples real numbers from a given interval. + * + * Generates uniformly distributed real numbers from the [l, h) + * half-open interval. * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. @@ -742,19 +642,24 @@ igraph_real_t igraph_rng_get_normal(igraph_rng_t *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"); + assert(h >= l); + + if (l == h) return h; + + /* Ensure that 'h' is never produced due to numerical roundoff errors, except when l == h. */ + igraph_real_t r; + do { + r = igraph_rng_get_unif01(rng) * (h - l) + l; + } while (IGRAPH_UNLIKELY(r == h)); + return r; } /** * \function igraph_rng_get_unif01 - * \brief Generate real, uniform random number from the unit interval. + * \brief Samples uniformly from the unit interval. + * + * Generates uniformly distributed real numbers from the [0, 1) + * half-open interval. * * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() * here to use the default igraph RNG. @@ -767,113 +672,370 @@ 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); + } else { + /* We extract 52 random bits from a 64-bit uint and fill that directly + * into the mantissa of a double, bit-by-bit, clear the sign bit and + * set the exponent to 2^0. This way we get a 52-bit random double + * between 1 (inclusive) and 2 (exclusive), uniformly distributed. + * Then we subtract 1 to arrive at the [0; 1) interval. This is fast + * but we lose one bit of precision as there are 2^53 possible doubles + * between 0 and 1. */ + uint64_t r = (igraph_i_rng_get_random_bits_uint64(rng, 52) & 0xFFFFFFFFFFFFFull) | 0x3FF0000000000000ull; + return *(double *)(&r) - 1.0; + } +} + +/** + * \function igraph_rng_get_geom + * \brief Samples from a geometric distribution. + * + * Generates random variates from a geometric distribution. The number \c k is + * generated with probability + * + * + * (1 - p)^k p, k = 0, 1, 2, .... + * + * \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 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 (!isfinite(p) || p <= 0 || p > 1) { + return IGRAPH_NAN; + } + if (type->get_geom) { + return type->get_geom(rng->state, p); + } else { + return igraph_rng_get_pois(rng, igraph_i_exp_rand(rng) * ((1 - p) / p)); + } +} + +/** + * \function igraph_rng_get_binom + * \brief Samples from a binomial distribution. + * + * Generates random variates from a binomial distribution. The number \c k is generated + * with probability + * + * + * (n \choose k) p^k (1-p)^(n-k), k = 0, 1, ..., n. + * + * \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 RNG. + */ + +igraph_real_t igraph_rng_get_binom(igraph_rng_t *rng, igraph_integer_t 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_i_rbinom(rng, n, p); + } +} + +/** + * \function igraph_rng_get_gamma + * \brief Samples from a gamma distribution. + * + * Generates random variates from a gamma distribution with probability + * density proportional to + * + * + * x^(shape-1) exp(-x / scale). + * + * \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 the 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_i_rgamma(rng, shape, scale); + } +} + +/** + * \function igraph_rng_get_exp + * \brief Samples from an exponential distribution. + * + * Generates random variates from an exponential distribution with probability + * density proportional to + * + * + * exp(-rate x). + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param rate Rate parameter. + * \return The generated sample. + * + * Time complexity: depends on the RNG. + */ + +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_i_rexp(rng, rate); + } +} + +/** + * \function igraph_rng_get_pois + * \brief Samples from a Poisson distribution. + * + * Generates random variates from a Poisson distribution. The number \c k is generated + * with probability + * + * + * rate^k * exp(-rate) / k!, k = 0, 1, 2, .... + * + * \param rng Pointer to the RNG to use. Use \ref igraph_rng_default() + * here to use the default igraph RNG. + * \param rate The rate parameter of the Poisson distribution. Must not be negative. + * \return The generated geometrically distributed random number. + * + * Time complexity: depends on the RNG. + */ + +igraph_real_t igraph_rng_get_pois(igraph_rng_t *rng, igraph_real_t rate) { + const igraph_rng_type_t *type = rng->type; + if (isnan(rate) || rate < 0) { + return IGRAPH_NAN; + } else if (rate == 0) { + return 0; + } else if (type->get_pois) { + return type->get_pois(rng->state, rate); + } else { + return igraph_i_rpois(rng, rate); + } +} + + +/** + * \ingroup internal + * + * This function appends the rest of the needed random numbers to the + * result vector. It is Algoirthm A in Vitter's paper. + */ + +static void igraph_i_random_sample_alga(igraph_vector_int_t *res, + igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length) { + /* Vitter: Variables V, quot, Nreal, and top are of type real */ + + igraph_integer_t N = h - l + 1; + igraph_integer_t n = length; + + igraph_real_t top = N - n; + igraph_real_t Nreal = N; + igraph_integer_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_int_push_back(res, l); /* allocated */ + Nreal = -1.0 + Nreal; n = -1 + n; + } + + S = trunc(round(Nreal) * RNG_UNIF01()); + l += S + 1; + igraph_vector_int_push_back(res, l); /* allocated */ +} + +/** + * \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. + * + * + * Reference: + * + * + * J. S. Vitter. An efficient algorithm for sequential random sampling. + * ACM Transactions on Mathematical Software, 13(1):58--67, 1987. + * https://doi.org/10.1145/23002.23003 + * + * \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. + * \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. + * \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). + * + * \example examples/simple/igraph_random_sample.c + */ + +igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igraph_integer_t l, igraph_integer_t h, + igraph_integer_t length) { + igraph_integer_t N; /* := h - l + 1 */ + IGRAPH_SAFE_ADD(h, -l, &N); + IGRAPH_SAFE_ADD(N, 1, &N); + + igraph_integer_t n = length; + + 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_integer_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_integer_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_int_resize(res, 1)); + VECTOR(*res)[0] = l; + return IGRAPH_SUCCESS; + } + if (length == 0) { + igraph_vector_int_clear(res); + return IGRAPH_SUCCESS; + } + if (length == N) { + IGRAPH_CHECK(igraph_vector_int_resize(res, length)); + for (igraph_integer_t i = 0; i < length; i++) { + VECTOR(*res)[i] = l++; + } + return IGRAPH_SUCCESS; } - 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_vector_int_clear(res); + IGRAPH_CHECK(igraph_vector_int_reserve(res, length)); -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); - } -} + RNG_BEGIN(); -/** - * \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. - */ + Vprime = exp(log(RNG_UNIF01()) * ninv); + l = l - 1; -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); - } -} + 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; -/** - * \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. - */ + y1 = exp(log(U * Nreal / qu1real) * nmin1inv); + Vprime = y1 * (-X / Nreal + 1.0) * (qu1real / (negSreal + qu1real)); + if (Vprime <= 1.0) { + break; + } -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); - } -} + 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); + } -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); + l += S + 1; + igraph_vector_int_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; } -} -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); + if (n > 1) { + igraph_i_random_sample_alga(res, l + 1, h, n); } else { - return igraph_rexp(rng, rate); + S = floor(N * Vprime); + l += S + 1; + igraph_vector_int_push_back(res, l); /* allocated */ } -} + RNG_END(); -/* - * \ingroup internal - * - * This function appends the rest of the needed random number to the - * result vector. - */ + return IGRAPH_SUCCESS; +} -static int igraph_i_random_sample_alga(igraph_vector_t *res, - igraph_integer_t l, igraph_integer_t h, - igraph_integer_t length) { +static void igraph_i_random_sample_alga_real(igraph_vector_t *res, + igraph_real_t l, igraph_real_t h, + igraph_real_t length) { igraph_real_t N = h - l + 1; igraph_real_t n = length; @@ -895,35 +1057,25 @@ static int igraph_i_random_sample_alga(igraph_vector_t *res, quot = (quot * top) / Nreal; } l += S; - igraph_vector_push_back(res, l); /* allocated */ + igraph_vector_push_back(res, l); /* allocated */ Nreal = -1.0 + Nreal; n = -1 + n; } - S = floor(round(Nreal) * RNG_UNIF01()); + S = trunc(round(Nreal) * RNG_UNIF01()); l += S + 1; - igraph_vector_push_back(res, l); /* allocated */ - - return IGRAPH_SUCCESS; + igraph_vector_push_back(res, l); /* allocated */ } /** * \ingroup nongraph - * \function igraph_random_sample - * \brief Generates an increasing random sequence of integers. + * \function igraph_random_sample_real + * \brief Generates an increasing random sequence of integers (igraph_real_t version). + * + * This function is the 'real' version of \ref igraph_random_sample(), and was added + * so \ref igraph_erdos_renyi_game_gnm() and related functions can use a random sample + * of doubles instead of integers to prevent overflows on systems with 32-bit + * \type igraph_integer_t. * - * - * 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 @@ -939,26 +1091,16 @@ static int igraph_i_random_sample_alga(igraph_vector_t *res, * \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_error_t igraph_random_sample_real(igraph_vector_t *res, igraph_real_t l, + igraph_real_t h, igraph_integer_t length) { + /* This function is the 'real' version of igraph_random_sample, and was added + * so erdos_renyi_game_gnm can use a random sample of doubles instead of integers + * to prevent overflows on systems with 32-bits igraph_integer_t. + */ 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; @@ -972,11 +1114,16 @@ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, /* getting back some sense of sanity */ if (l > h) { - IGRAPH_ERROR("Lower limit is greater than upper limit", IGRAPH_EINVAL); + 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); + IGRAPH_ERROR("Sample size exceeds size of candidate pool.", IGRAPH_EINVAL); + } + + /* ensure that we work in the range where igraph_real_t can represent integers exactly */ + if (h > IGRAPH_MAX_EXACT_REAL || l < -IGRAPH_MAX_EXACT_REAL || N > IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Sampling interval too large.", IGRAPH_EOVERFLOW); } /* treat rare cases quickly */ @@ -990,9 +1137,8 @@ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, return IGRAPH_SUCCESS; } if (length == N) { - long int i = 0; IGRAPH_CHECK(igraph_vector_resize(res, length)); - for (i = 0; i < length; i++) { + for (igraph_integer_t i = 0; i < length; i++) { VECTOR(*res)[i] = l++; } return IGRAPH_SUCCESS; @@ -1060,11 +1206,8 @@ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, } if (n > 1) { - retval = igraph_i_random_sample_alga(res, (igraph_integer_t) l + 1, - (igraph_integer_t) h, - (igraph_integer_t) n); + igraph_i_random_sample_alga_real(res, l + 1, h, n); } else { - retval = 0; S = floor(N * Vprime); l += S + 1; igraph_vector_push_back(res, l); /* allocated */ @@ -1072,7 +1215,7 @@ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, RNG_END(); - return retval; + return IGRAPH_SUCCESS; } /* @@ -1176,53 +1319,6 @@ int igraph_random_sample(igraph_vector_t *res, igraph_real_t l, igraph_real_t h, #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 */ @@ -1284,12 +1380,12 @@ void bratio(double a, double b, double x, double y, #define R_D_negInonint(x) (x < 0. || R_D_nonint(x)) #define R_D_nonint_check(x) \ - if(R_D_nonint(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) { +static double igraph_i_qnorm5(double p, double mu, double sigma, igraph_bool_t lower_tail, igraph_bool_t log_p) { double p_, q, r, val; #ifdef IEEE_754 @@ -1389,125 +1485,44 @@ double igraph_qnorm5(double p, double mu, double sigma, int lower_tail, int log_ 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) { +static igraph_integer_t imax2(igraph_integer_t x, igraph_integer_t y) { return (x < y) ? y : x; } -static int imin2(int x, int y) { +static igraph_integer_t imin2(igraph_integer_t x, igraph_integer_t y) { return (x < y) ? x : y; } -static double igraph_norm_rand(igraph_rng_t *rng) { +static double igraph_i_norm_rand(igraph_rng_t *rng) { + double r; - double u1; + /* Use the inversion method based on uniform variates from (0, 1). + * We exclude 0.0 as it would lead to generating -infinity. + * It is assumed that unif01() provides sufficient accuracy. + * A resolution of 2^-32 may not be sufficient. igraph's default + * implementaton provides an accuracy of 2^-52. + */ + do { + r = igraph_rng_get_unif01(rng); + } while (r == 0.0); -#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); + return igraph_i_qnorm5(r, 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); + * The following function is igraph code (not R / Mathlib). * - * 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. + * We use simple inverse transform sampling, with the assumption that the + * quality/resolution of uniform variates is high (52 bits in the default + * implementation). The quantile function is -log(1 - r) but given that + * r is sampled uniformly form the unit interval, -log(r) is equivalent. + * r = 0 is disallowed as it would yield infinity. */ -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]; +static double igraph_i_exp_rand(igraph_rng_t *rng) { + igraph_real_t r = igraph_rng_get_unif01(rng); + if (r == 0.0) r = 1.0; /* sample from (0, 1] instead of [0, 1) */ + return -log(r); } /* @@ -1565,14 +1580,15 @@ double igraph_exp_rand(igraph_rng_t *rng) { #define TRUE 1 #define M_1_SQRT_2PI 0.398942280401432677939946059934 /* 1/sqrt(2pi) */ -static double igraph_rpois(igraph_rng_t *rng, double mu) { +static double igraph_i_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 IGRAPH_THREAD_LOCAL int l, m; + static IGRAPH_THREAD_LOCAL int l; + static IGRAPH_THREAD_LOCAL igraph_integer_t m; static IGRAPH_THREAD_LOCAL double b1, b2, c, c0, c1, c2, c3; static IGRAPH_THREAD_LOCAL double pp[36], p0, p, q, s, d, omega; @@ -1584,7 +1600,7 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { double pois = -1.; int k, kflag, big_mu, new_big_mu = FALSE; - if (!igraph_finite(mu)) { + if (!isfinite(mu) || mu < 0) { ML_ERR_return_NAN; } @@ -1617,7 +1633,7 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { /*muprev = 0.;-* such that next time, mu != muprev ..*/ if (mu != muprev) { muprev = mu; - m = imax2(1, (int) mu); + m = imax2(1, (igraph_integer_t) mu); l = 0; /* pp[] is already ok up to pp[l] */ q = p0 = p = exp(-mu); } @@ -1662,7 +1678,7 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { /* Only if mu >= 10 : ----------------------- */ /* Step N. normal sample */ - g = mu + s * igraph_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ + g = mu + s * igraph_i_norm_rand(rng);/* norm_rand() ~ N(0,1), standard normal */ if (g >= 0.) { pois = floor(g); @@ -1710,12 +1726,12 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { repeat { /* Step E. Exponential Sample */ - E = igraph_exp_rand(rng);/* ~ Exp(1) (standard exponential) */ + E = igraph_i_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); + t = 1.8 + copysign(E, u); if (t > -0.6744) { pois = floor(mu + s * t); fk = pois; @@ -1771,41 +1787,24 @@ static double igraph_rpois(igraph_rng_t *rng, double mu) { #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 igraph_i_rbinom(igraph_rng_t *rng, igraph_integer_t n, double pp) { static IGRAPH_THREAD_LOCAL double c, fm, npq, p1, p2, p3, p4, qn; static IGRAPH_THREAD_LOCAL double xl, xll, xlr, xm, xr; static IGRAPH_THREAD_LOCAL double psave = -1.0; - static IGRAPH_THREAD_LOCAL int nsave = -1; - static IGRAPH_THREAD_LOCAL int m; + static IGRAPH_THREAD_LOCAL igraph_integer_t nsave = -1; + static IGRAPH_THREAD_LOCAL igraph_integer_t 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; - } + igraph_integer_t i, ix, k; - if (!igraph_finite(pp) || + if (!isfinite(pp) || /* n=0, p=0, p=1 are not errors */ n < 0 || pp < 0. || pp > 1.) { ML_ERR_return_NAN; @@ -1841,7 +1840,11 @@ static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { m = ffm; fm = m; npq = np * q; - p1 = (int)(2.195 * sqrt(npq) - 4.6 * q) + 0.5; + /* Note (igraph): Original code used a cast to (int) for rounding. However, + * the max npq = n*p*(1-p) value is 0.25*n, thus 2.195 * sqrt(npq) may be + * as large as 1.0975 * sqrt(n). This is not representable on a 32-bit signed + * integer when n is a 64-bit signed integer. Thus we use trunc() instead. */ + p1 = trunc(2.195 * sqrt(npq) - 4.6 * q) + 0.5; xm = fm + 0.5; xl = xm - p1; xr = xm + p1; @@ -1893,7 +1896,7 @@ static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { } } /* determine appropriate way to perform accept/reject test */ - k = abs(ix - m); + k = imaxabs(ix - m); if (k <= 20 || k >= npq / 2 - 1) { /* explicit evaluation */ f = 1.0; @@ -1961,75 +1964,15 @@ static double igraph_rbinom(igraph_rng_t *rng, double nin, double pp) { return (double)ix; } -static igraph_real_t igraph_rexp(igraph_rng_t *rng, double rate) { +static igraph_real_t igraph_i_rexp(igraph_rng_t *rng, double rate) { igraph_real_t scale = 1.0 / rate; - if (!IGRAPH_FINITE(scale) || scale <= 0.0) { + if (!isfinite(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) */ + return scale * igraph_i_exp_rand(rng); } /* This is from nmath/rgamma.c */ @@ -2083,7 +2026,7 @@ double igraph_dnorm(double x, double mu, double sigma, int give_log) { * Output: a variate from the gamma(a)-distribution */ -static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { +static double igraph_i_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 */ @@ -2116,7 +2059,7 @@ static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { 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 (!isfinite(a) || !isfinite(scale) || a < 0.0 || scale <= 0.0) { if (scale == 0.) { return 0.; } @@ -2132,12 +2075,12 @@ static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { 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)) { + if (igraph_i_exp_rand(rng) >= (1.0 - a) * log(x)) { break; } } else { x = exp(log(p) / a); - if (igraph_exp_rand(rng) >= x) { + if (igraph_i_exp_rand(rng) >= x) { break; } } @@ -2158,7 +2101,7 @@ static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { x = (s,1/2) -normal deviate. */ /* immediate acceptance (i) */ - t = igraph_norm_rand(rng); + t = igraph_i_norm_rand(rng); x = s + 0.5 * t; ret_val = x * x; if (t >= 0.0) { @@ -2220,7 +2163,7 @@ static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { /* Step 8: e = standard exponential deviate * u = 0,1 -uniform deviate * t = (b,si)-double exponential (laplace) sample */ - e = igraph_exp_rand(rng); + e = igraph_i_exp_rand(rng); u = igraph_rng_get_unif01(rng); u = u + u - 1.0; if (u < 0.0) { @@ -2255,7 +2198,7 @@ static double igraph_rgamma(igraph_rng_t *rng, double a, double scale) { return scale * x * x; } -int igraph_rng_get_dirichlet(igraph_rng_t *rng, +igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, const igraph_vector_t *alpha, igraph_vector_t *result) { @@ -2264,11 +2207,11 @@ int igraph_rng_get_dirichlet(igraph_rng_t *rng, igraph_real_t sum = 0.0; if (len < 2) { - IGRAPH_ERROR("Dirichlet parameter vector too short, must " - "have at least two entries", IGRAPH_EINVAL); + 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_ERROR("Dirichlet concentration parameters must be positive.", IGRAPH_EINVAL); } diff --git a/src/vendor/cigraph/src/random/random_internal.h b/src/vendor/cigraph/src/random/random_internal.h new file mode 100644 index 00000000000..25110da1a34 --- /dev/null +++ b/src/vendor/cigraph/src/random/random_internal.h @@ -0,0 +1,38 @@ +/* -*- 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_RANDOM_INTERNAL_H +#define IGRAPH_RANDOM_INTERNAL_H + +#include "igraph_decls.h" +#include "igraph_types.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +igraph_error_t igraph_random_sample_real( + igraph_vector_t *res, igraph_real_t l, igraph_real_t h, + igraph_integer_t length); + +__END_DECLS + +#endif diff --git a/src/vendor/cigraph/src/random/rng_glibc2.c b/src/vendor/cigraph/src/random/rng_glibc2.c new file mode 100644 index 00000000000..5a687f9155a --- /dev/null +++ b/src/vendor/cigraph/src/random/rng_glibc2.c @@ -0,0 +1,144 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +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; + + /* The original implementation used x[*i] += x[*j] here. Considering that + * x is signed, this is undefined behaviour according to the C standard. + * Therefore, we temporarily cast to unsigned long int to achieve what the + * original intention was */ + x[*i] = ((unsigned long int)x[*i]) + ((unsigned long int)x[*j]); + k = (x[*i] >> 1) & 0x7FFFFFFF; + + (*i)++; + if (*i == n) { + *i = 0; + } + + (*j)++ ; + if (*j == n) { + *j = 0; + } + + return k; +} + +static igraph_uint_t 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); +} + +/* 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] = s ; + } +} + +static igraph_error_t igraph_rng_glibc2_seed(void *vstate, igraph_uint_t 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, (unsigned long) seed); + + state->i = 3; + state->j = 0; + + for (i = 0; i < 10 * 31; i++) { + igraph_rng_glibc2_get(state); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_glibc2_init(void **state) { + igraph_i_rng_glibc2_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_glibc2_state_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize GNU libc 2 RNG."); + (*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", + /* bits= */ 31, + /* init= */ igraph_rng_glibc2_init, + /* destroy= */ igraph_rng_glibc2_destroy, + /* seed= */ igraph_rng_glibc2_seed, + /* get= */ igraph_rng_glibc2_get, + /* get_int= */ 0, + /* get_real= */ 0, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0, + /* get_pois= */ 0 +}; diff --git a/src/vendor/cigraph/src/random/rng_mt19937.c b/src/vendor/cigraph/src/random/rng_mt19937.c new file mode 100644 index 00000000000..9caf97670ac --- /dev/null +++ b/src/vendor/cigraph/src/random/rng_mt19937.c @@ -0,0 +1,179 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include /* memset() */ +#include + + +#define N 624 /* Period parameters */ +#define M 397 + +/* most significant w-r bits */ +static const uint32_t UPPER_MASK = UINT32_C(0x80000000); + +/* least significant r bits */ +static const uint32_t LOWER_MASK = UINT32_C(0x7fffffff); + +typedef struct { + uint32_t mt[N]; + int mti; +} igraph_i_rng_mt19937_state_t; + +static igraph_uint_t igraph_rng_mt19937_get(void *vstate) { + igraph_i_rng_mt19937_state_t *state = vstate; + + uint32_t k; + uint32_t *const mt = state->mt; + +#define MAGIC(y) (((y) & 0x1) ? UINT32_C(0x9908b0df) : 0) + + if (state->mti >= N) { + /* generate N words at one time */ + int kk; + + for (kk = 0; kk < N - M; kk++) { + uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + M] ^ (y >> 1) ^ MAGIC(y); + } + for (; kk < N - 1; kk++) { + uint32_t y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ MAGIC(y); + } + + { + uint32_t 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) & UINT32_C(0x9d2c5680); + k ^= (k << 15) & UINT32_C(0xefc60000); + k ^= (k >> 18); + + state->mti++; + + return k; +} + +static igraph_error_t igraph_rng_mt19937_seed(void *vstate, igraph_uint_t 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 & UINT32_C(0xffffffff); + + for (i = 1; i < N; i++) { + /* See Knuth's "Art of Computer Programming" Vol. 2, 3rd + Ed. p.106 for multiplier. */ + state->mt[i] = + (UINT32_C(1812433253) * (state->mt[i - 1] ^ (state->mt[i - 1] >> 30)) + + (uint32_t) i); + state->mt[i] &= UINT32_C(0xffffffff); + } + + state->mti = i; + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_mt19937_init(void **state) { + igraph_i_rng_mt19937_state_t *st; + + st = IGRAPH_CALLOC(1, igraph_i_rng_mt19937_state_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize MT19937 RNG."); + (*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 igraph_rng_mt19937_seed() 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", + /* bits= */ 32, + /* init= */ igraph_rng_mt19937_init, + /* destroy= */ igraph_rng_mt19937_destroy, + /* seed= */ igraph_rng_mt19937_seed, + /* get= */ igraph_rng_mt19937_get, + /* get_int= */ 0, + /* get_real= */ 0, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0, + /* get_pois= */ 0 +}; + +#undef N +#undef M diff --git a/src/vendor/cigraph/src/random/rng_pcg32.c b/src/vendor/cigraph/src/random/rng_pcg32.c new file mode 100644 index 00000000000..fb51b1ba2f8 --- /dev/null +++ b/src/vendor/cigraph/src/random/rng_pcg32.c @@ -0,0 +1,124 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include "pcg/pcg_variants.h" + +#include "config.h" /* IGRAPH_THREAD_LOCAL */ + +/* The original implementation of the 32-bit PCG random number generator in this + * file was obtained from https://github.com/imneme/pcg-c + * + * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible + * with igraph's GPLv2 license. License notices for PCG are to be found in the + * pcg_variants.h header + */ + +static const pcg32_random_t pcg32_initializer = PCG32_INITIALIZER; + +static igraph_uint_t igraph_rng_pcg32_get(void *vstate) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + return pcg32_random_r(state); +} + +static igraph_error_t igraph_rng_pcg32_seed(void *vstate, igraph_uint_t seed) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + + /* PCG32 is seeded by a 64-bit state and a 64-bit sequence number (well, only + * 63 bits are used from the sequence number, though). Since the unified + * igraph RNG seeding interface provides a single igraph_uint_t as the seed, + * we use the seed to fill in the sequence number and use the state from + * PCG32_INITIALIZER */ + if (seed == 0) { + /* If you feel the temptation to unify the two branches by running + * seed = pcg32_initializer.inc >> 1, don't. + * seed is an igraph_uint_t, so it can be 32-bit or 64-bit. + * pcg32_initializer.inc is always 64-bit. + */ + pcg32_srandom_r(state, pcg32_initializer.state, pcg32_initializer.inc >> 1); + } else { + pcg32_srandom_r(state, pcg32_initializer.state, seed); + } + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_pcg32_init(void **state) { + pcg32_random_t *st; + + st = IGRAPH_CALLOC(1, pcg32_random_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize PCG32 RNG."); + (*state) = st; + + igraph_rng_pcg32_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_pcg32_destroy(void *vstate) { + pcg32_random_t *state = (pcg32_random_t*) vstate; + IGRAPH_FREE(state); +} + +/** + * \var igraph_rngtype_pcg32 + * \brief The PCG random number generator (32-bit version). + * + * This is an implementation of the PCG random number generator; see + * https://www.pcg-random.org for more details. This implementation returns + * 32 random bits in a single iteration. + * + * + * The generator was ported from the original source code published by the + * authors at https://github.com/imneme/pcg-c. + */ + +const igraph_rng_type_t igraph_rngtype_pcg32 = { + /* name= */ "PCG32", + /* bits= */ 32, + /* init= */ igraph_rng_pcg32_init, + /* destroy= */ igraph_rng_pcg32_destroy, + /* seed= */ igraph_rng_pcg32_seed, + /* get= */ igraph_rng_pcg32_get, + /* get_int= */ 0, + /* get_real= */ 0, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0, + /* get_pois= */ 0 +}; + +/***** Default RNG, used upon igraph startup *****/ + +#define addr(a) (&a) + +static pcg32_random_t igraph_i_rng_default_state = PCG32_INITIALIZER; + +IGRAPH_THREAD_LOCAL igraph_rng_t igraph_i_rng_default = { + addr(igraph_rngtype_pcg32), + addr(igraph_i_rng_default_state), + /* is_seeded = */ true +}; + +#undef addr diff --git a/src/vendor/cigraph/src/random/rng_pcg64.c b/src/vendor/cigraph/src/random/rng_pcg64.c new file mode 100644 index 00000000000..e763ff06b87 --- /dev/null +++ b/src/vendor/cigraph/src/random/rng_pcg64.c @@ -0,0 +1,138 @@ +/* + 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, see . +*/ + +#include "igraph_random.h" + +#include "igraph_memory.h" +#include "igraph_types.h" + +#include "config.h" + +/* The original implementation of the 64-bit PCG random number generator in this + * file was obtained from https://github.com/imneme/pcg-c + * + * PCG is dual-licensed under Apache-2.0 and MIT Licenses. MIT is compatible + * with igraph's GPLv2 license. License notices for PCG are to be found in the + * pcg_variants.h header + */ + +#if IGRAPH_INTEGER_SIZE == 64 && defined(HAVE___UINT128_T) + +#include "pcg/pcg_variants.h" + +static const pcg64_random_t pcg64_initializer = PCG64_INITIALIZER; + +static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + return pcg64_random_r(state); +} + +static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + + if (seed == 0) { + seed = (pcg64_initializer.inc >> 1); + } + + /* PCG64 is seeded by a 128-bit state and a 128-bit sequence number (well, only + * 63 bits are used from the sequence number, though). Since the unified + * igraph RNG seeding interface provides a single igraph_uint_t as the seed, + * we use the seed to fill in the sequence number and use the state from + * PCG64_INITIALIZER */ + pcg64_srandom_r(state, pcg64_initializer.state, seed); + + return IGRAPH_SUCCESS; +} + +static igraph_error_t igraph_rng_pcg64_init(void **state) { + pcg64_random_t *st; + + st = IGRAPH_CALLOC(1, pcg64_random_t); + IGRAPH_CHECK_OOM(st, "Cannot initialize PCG64 RNG."); + (*state) = st; + + igraph_rng_pcg64_seed(st, 0); + + return IGRAPH_SUCCESS; +} + +static void igraph_rng_pcg64_destroy(void *vstate) { + pcg64_random_t *state = (pcg64_random_t*) vstate; + IGRAPH_FREE(state); +} + +#else + +/* Dummy implementation if the compiler does not support __uint128_t */ + +static igraph_uint_t igraph_rng_pcg64_get(void *vstate) { + IGRAPH_UNUSED(vstate); + return 0; +} + +static igraph_error_t igraph_rng_pcg64_seed(void *vstate, igraph_uint_t seed) { + IGRAPH_UNUSED(vstate); IGRAPH_UNUSED(seed); + IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); +} + +static igraph_error_t igraph_rng_pcg64_init(void **state) { + IGRAPH_UNUSED(state); + IGRAPH_ERROR("64-bit PCG generator needs __uint128_t.", IGRAPH_UNIMPLEMENTED); +} + +static void igraph_rng_pcg64_destroy(void *vstate) { + IGRAPH_UNUSED(vstate); +} + +#endif + +/** + * \var igraph_rngtype_pcg64 + * \brief The PCG random number generator (64-bit version). + * + * This is an implementation of the PCG random number generator; see + * https://www.pcg-random.org for more details. This implementation returns + * 64 random bits in a single iteration. It is only available on 64-bit plaforms + * with compilers that provide the __uint128_t type. + * + * + * PCG64 typically provides better performance than PCG32 when sampling floating + * point numbers or very large integers, as it can provide twice as many random + * bits in a single generation round. + * + * + * The generator was ported from the original source code published by the + * authors at https://github.com/imneme/pcg-c. + */ + +const igraph_rng_type_t igraph_rngtype_pcg64 = { + /* name= */ "PCG64", + /* bits= */ 64, + /* init= */ igraph_rng_pcg64_init, + /* destroy= */ igraph_rng_pcg64_destroy, + /* seed= */ igraph_rng_pcg64_seed, + /* get= */ igraph_rng_pcg64_get, + /* get_int= */ 0, + /* get_real= */ 0, + /* get_norm= */ 0, + /* get_geom= */ 0, + /* get_binom= */ 0, + /* get_exp= */ 0, + /* get_gamma= */ 0, + /* get_pois= */ 0 +}; diff --git a/src/vendor/cigraph/src/scg/scg.c b/src/vendor/cigraph/src/scg/scg.c deleted file mode 100644 index ab435d1d269..00000000000 --- a/src/vendor/cigraph/src/scg/scg.c +++ /dev/null @@ -1,2303 +0,0 @@ -/* -*- 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/vendor/cigraph/src/scg/scg_approximate_methods.c b/src/vendor/cigraph/src/scg/scg_approximate_methods.c deleted file mode 100644 index fe8022d17bd..00000000000 --- a/src/vendor/cigraph/src/scg/scg_approximate_methods.c +++ /dev/null @@ -1,173 +0,0 @@ -/* -*- 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/vendor/cigraph/src/scg/scg_exact_scg.c b/src/vendor/cigraph/src/scg/scg_exact_scg.c deleted file mode 100644 index 6495eb7075e..00000000000 --- a/src/vendor/cigraph/src/scg/scg_exact_scg.c +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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/vendor/cigraph/src/scg/scg_headers.h b/src/vendor/cigraph/src/scg/scg_headers.h deleted file mode 100644 index 876ac312b63..00000000000 --- a/src/vendor/cigraph/src/scg/scg_headers.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- 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/vendor/cigraph/src/scg/scg_kmeans.c b/src/vendor/cigraph/src/scg/scg_kmeans.c deleted file mode 100644 index 067af308957..00000000000 --- a/src/vendor/cigraph/src/scg/scg_kmeans.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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/vendor/cigraph/src/scg/scg_optimal_method.c b/src/vendor/cigraph/src/scg/scg_optimal_method.c deleted file mode 100644 index 68eca3f36e7..00000000000 --- a/src/vendor/cigraph/src/scg/scg_optimal_method.c +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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/vendor/cigraph/src/scg/scg_utils.c b/src/vendor/cigraph/src/scg/scg_utils.c deleted file mode 100644 index ed5500d01ca..00000000000 --- a/src/vendor/cigraph/src/scg/scg_utils.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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/vendor/cigraph/src/version.c b/src/vendor/cigraph/src/version.c index 72af7ca93f4..f0bf43917df 100644 --- a/src/vendor/cigraph/src/version.c +++ b/src/vendor/cigraph/src/version.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2008-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2008-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 @@ -15,10 +13,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA - + along with this program. If not, see . */ #include "igraph_version.h" @@ -29,32 +24,29 @@ static const char *igraph_version_string = IGRAPH_VERSION; /** * \function igraph_version - * Return the version of the igraph C library + * \brief 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. + * is set to the igraph version string, e.g. "0.9.11" or "0.10.0". This + * string must 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. + * igraph version. E.g. for version "0.9.11" 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. + * igraph version. E.g. for version "0.9.11" this is 11. * \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). + * subminor igraph version. E.g. for version "0.9.11" this is 11. * * \example examples/simple/igraph_version.c */ -int igraph_version(const char **version_string, - int *major, - int *minor, - int *subminor) { +void 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; + int *p1 = major ? major : &i1; + int *p2 = minor ? minor : &i2; + int *p3 = subminor ? subminor : &i3; if (version_string) { *version_string = igraph_version_string; @@ -62,6 +54,4 @@ int igraph_version(const char **version_string, *p1 = *p2 = *p3 = 0; sscanf(IGRAPH_VERSION, "%i.%i.%i", p1, p2, p3); - - return 0; } diff --git a/src/vendor/cigraph/vendor/CMakeLists.txt b/src/vendor/cigraph/vendor/CMakeLists.txt index de793750494..9befa6fa1ae 100644 --- a/src/vendor/cigraph/vendor/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(f2c) add_subdirectory(glpk) add_subdirectory(lapack) add_subdirectory(mini-gmp) +add_subdirectory(pcg) add_subdirectory(plfit) diff --git a/src/vendor/cigraph/vendor/cs/CMakeLists.txt b/src/vendor/cigraph/vendor/cs/CMakeLists.txt index e4bccefb6a3..bef000d88c6 100644 --- a/src/vendor/cigraph/vendor/cs/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/cs/CMakeLists.txt @@ -62,8 +62,6 @@ target_include_directories( PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_BINARY_DIR}/include - PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR} ) if (BUILD_SHARED_LIBS) @@ -89,7 +87,7 @@ if (MSVC) else() target_compile_options( cxsparse_vendored PRIVATE - $<$:-Wno-unused-variable> + $<$:-Wno-unused-variable> ) endif() diff --git a/src/vendor/cigraph/vendor/cs/SuiteSparse_config.h b/src/vendor/cigraph/vendor/cs/SuiteSparse_config.h deleted file mode 100644 index bd0ccedbe8b..00000000000 --- a/src/vendor/cigraph/vendor/cs/SuiteSparse_config.h +++ /dev/null @@ -1,221 +0,0 @@ -/* ========================================================================== */ -/* === 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/vendor/cigraph/vendor/cs/cs.h b/src/vendor/cigraph/vendor/cs/cs.h index 0e58521e14f..a505ca55aa6 100644 --- a/src/vendor/cigraph/vendor/cs/cs.h +++ b/src/vendor/cigraph/vendor/cs/cs.h @@ -1,3 +1,14 @@ +/* This is a MODIFIED version of the original CXSparse/Include/cs.h file from + * SuiteSparse 5.12.0 (CXSparse version 3.2.0). The modifications are outlined + * here: + * + * - Dependency on SuiteSparse_long was removed + * - CXSparse is configured to use igraph_integer_t as cs_long_t + * - CXSparse function prefix is set to cs_igraph instead of cs_igraph + * - Unneeded CXSparse function variants are removed + * + * The remaining comments below are from the original cs.h header */ + /* ========================================================================== */ /* CXSparse/Include/cs.h file */ /* ========================================================================== */ @@ -28,6 +39,7 @@ #include "mex.h" #endif +#include "igraph_types.h" #ifdef __cplusplus #ifndef NCOMPLEX @@ -49,144 +61,9 @@ extern "C" { #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) ; - +#define cs_long_t igraph_integer_t +#define cs_long_t_id "%" IGRAPH_PRId +#define cs_long_t_max IGRAPH_INTEGER_MAX /* -------------------------------------------------------------------------- */ /* double/cs_long_t version of CXSparse */ @@ -194,7 +71,7 @@ cs_did *cs_di_ddone (cs_did *D, cs_di *C, void *w, int ok) ; /* --- primary CSparse routines and data structures ------------------------- */ -typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ +typedef struct cs_igraph_sparse /* matrix in compressed-column or triplet form */ { cs_long_t nzmax ; /* maximum number of entries */ cs_long_t m ; /* number of rows */ @@ -203,35 +80,35 @@ typedef struct cs_dl_sparse /* matrix in compressed-column or triplet form */ 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) ; +} cs_igraph ; + +cs_igraph *cs_igraph_add (const cs_igraph *A, const cs_igraph *B, double alpha, double beta) ; +cs_long_t cs_igraph_cholsol (cs_long_t order, const cs_igraph *A, double *b) ; +cs_long_t cs_igraph_dupl (cs_igraph *A) ; +cs_long_t cs_igraph_entry (cs_igraph *T, cs_long_t i, cs_long_t j, double x) ; +cs_long_t cs_igraph_lusol (cs_long_t order, const cs_igraph *A, double *b, double tol) ; +cs_long_t cs_igraph_gaxpy (const cs_igraph *A, const double *x, double *y) ; +cs_igraph *cs_igraph_multiply (const cs_igraph *A, const cs_igraph *B) ; +cs_long_t cs_igraph_qrsol (cs_long_t order, const cs_igraph *A, double *b) ; +cs_igraph *cs_igraph_transpose (const cs_igraph *A, cs_long_t values) ; +cs_igraph *cs_igraph_compress (const cs_igraph *T) ; +double cs_igraph_norm (const cs_igraph *A) ; +/*cs_long_t cs_igraph_print (const cs_igraph *A, cs_long_t brief) ;*/ +cs_igraph *cs_igraph_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, +void *cs_igraph_calloc (cs_long_t n, size_t size) ; +void *cs_igraph_free (void *p) ; +void *cs_igraph_realloc (void *p, cs_long_t n, size_t size, cs_long_t *ok) ; +cs_igraph *cs_igraph_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) ; +cs_igraph *cs_igraph_spfree (cs_igraph *A) ; +cs_long_t cs_igraph_sprealloc (cs_igraph *A, cs_long_t nzmax) ; +void *cs_igraph_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 */ +typedef struct cs_igraph_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 */ @@ -241,17 +118,17 @@ typedef struct cs_dl_symbolic /* symbolic Cholesky, LU, or QR analysis */ 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 ; +} cs_igraphs ; -typedef struct cs_dl_numeric /* numeric Cholesky, LU, or QR factorization */ +typedef struct cs_igraph_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_igraph *L ; /* L for LU and Cholesky, V for QR */ + cs_igraph *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 ; +} cs_igraphn ; -typedef struct cs_dl_dmperm_results /* cs_dl_dmperm or cs_dl_scc output */ +typedef struct cs_igraph_dmperm_results /* cs_igraph_dmperm or cs_igraph_scc output */ { cs_long_t *p ; /* size m, row permutation */ cs_long_t *q ; /* size n, column permutation */ @@ -260,395 +137,86 @@ typedef struct cs_dl_dmperm_results /* cs_dl_dmperm or cs_dl_scc output */ 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_igraphd ; + +cs_long_t *cs_igraph_amd (cs_long_t order, const cs_igraph *A) ; +cs_igraphn *cs_igraph_chol (const cs_igraph *A, const cs_igraphs *S) ; +cs_igraphd *cs_igraph_dmperm (const cs_igraph *A, cs_long_t seed) ; +cs_long_t cs_igraph_droptol (cs_igraph *A, double tol) ; +cs_long_t cs_igraph_dropzeros (cs_igraph *A) ; +cs_long_t cs_igraph_happly (const cs_igraph *V, cs_long_t i, double beta, double *x) ; +cs_long_t cs_igraph_ipvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_long_t cs_igraph_lsolve (const cs_igraph *L, double *x) ; +cs_long_t cs_igraph_ltsolve (const cs_igraph *L, double *x) ; +cs_igraphn *cs_igraph_lu (const cs_igraph *A, const cs_igraphs *S, double tol) ; +cs_igraph *cs_igraph_permute (const cs_igraph *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, +cs_long_t *cs_igraph_pinv (const cs_long_t *p, cs_long_t n) ; +cs_long_t cs_igraph_pvec (const cs_long_t *p, const double *b, double *x, cs_long_t n) ; +cs_igraphn *cs_igraph_qr (const cs_igraph *A, const cs_igraphs *S) ; +cs_igraphs *cs_igraph_schol (cs_long_t order, const cs_igraph *A) ; +cs_igraphs *cs_igraph_sqr (cs_long_t order, const cs_igraph *A, cs_long_t qr) ; +cs_igraph *cs_igraph_symperm (const cs_igraph *A, const cs_long_t *pinv, cs_long_t values) ; +cs_long_t cs_igraph_usolve (const cs_igraph *U, double *x) ; +cs_long_t cs_igraph_utsolve (const cs_igraph *U, double *x) ; +cs_long_t cs_igraph_updown (cs_igraph *L, cs_long_t sigma, const cs_igraph *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) ; +cs_igraphs *cs_igraph_sfree (cs_igraphs *S) ; +cs_igraphn *cs_igraph_nfree (cs_igraphn *N) ; +cs_igraphd *cs_igraph_dfree (cs_igraphd *D) ; /* --- tertiary CSparse routines -------------------------------------------- */ -cs_long_t *cs_dl_counts (const cs_dl *A, const cs_long_t *parent, +cs_long_t *cs_igraph_counts (const cs_igraph *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, +double cs_igraph_cumsum (cs_long_t *p, cs_long_t *c, cs_long_t n) ; +cs_long_t cs_igraph_dfs (cs_long_t j, cs_igraph *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 *cs_igraph_etree (const cs_igraph *A, cs_long_t ata) ; +cs_long_t cs_igraph_fkeep (cs_igraph *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, +double cs_igraph_house (double *x, double *beta, cs_long_t n) ; +cs_long_t *cs_igraph_maxtrans (const cs_igraph *A, cs_long_t seed) ; +cs_long_t *cs_igraph_post (const cs_long_t *parent, cs_long_t n) ; +cs_igraphd *cs_igraph_scc (cs_igraph *A) ; +cs_long_t cs_igraph_scatter (const cs_igraph *A, cs_long_t j, double beta, cs_long_t *w, + double *x, cs_long_t mark,cs_igraph *C, cs_long_t nz) ; +cs_long_t cs_igraph_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 cs_igraph_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, +cs_long_t cs_igraph_reach (cs_igraph *G, const cs_igraph *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, +cs_long_t cs_igraph_spsolve (cs_igraph *L, const cs_igraph *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 cs_igraph_ereach (const cs_igraph *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) ; +cs_long_t *cs_igraph_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 +cs_igraphd *cs_igraph_dalloc (cs_long_t m, cs_long_t n) ; +cs_igraph *cs_igraph_done (cs_igraph *C, void *w, void *x, cs_long_t ok) ; +cs_long_t *cs_igraph_idone (cs_long_t *p, cs_igraph *C, void *w, cs_long_t ok) ; +cs_igraphn *cs_igraph_ndone (cs_igraphn *N, cs_igraph *C, void *w, void *x, cs_long_t ok) ; +cs_igraphd *cs_igraph_ddone (cs_igraphd *D, cs_igraph *C, void *w, cs_long_t ok) ; /* -------------------------------------------------------------------------- */ /* 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 +#define CS_NAME(nm) cs_igraph ## nm +#define cs cs_igraph -#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)) @@ -741,17 +309,6 @@ cs_cld *cs_cl_ddone (cs_cld *D, cs_cl *C, void *w, cs_long_t ok) ; #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 diff --git a/src/vendor/cigraph/vendor/f2c/CMakeLists.txt b/src/vendor/cigraph/vendor/f2c/CMakeLists.txt index 304ef005038..63db5d68cad 100644 --- a/src/vendor/cigraph/vendor/f2c/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/f2c/CMakeLists.txt @@ -15,13 +15,15 @@ set( if(F2C_EXTERNAL_ARITH_HEADER) configure_file(${F2C_EXTERNAL_ARITH_HEADER} arith.h COPYONLY) else() - if (CMAKE_CROSSCOMPILING) + if (CMAKE_CROSSCOMPILING AND NOT CMAKE_CROSSCOMPILING_EMULATOR) # Warn only, as in some circumstances, such as macOS with Rosetta, - # arithchk can be run through emulation and the build with not fail. + # arithchk can still be run through emulation and the build with not fail. message(WARNING - "Cross-compiling with internal ARPACK, BLAS or LAPACK, " - "but F2C_EXTERNAL_ARITH_HEADER was not set. The build is likely to fail. " - "See igraph's installation instructions for more information.") + "Cross-compiling with internal ARPACK, BLAS or LAPACK, but " + "F2C_EXTERNAL_ARITH_HEADER was not set and no cross-compiling " + "emulator was provided in CMAKE_CROSSCOMPILING_EMULATOR either. " + "The build is likely to fail. See igraph's installation instructions " + "for more information.") endif() add_custom_command( OUTPUT arith.h @@ -103,6 +105,10 @@ target_include_directories( ${CMAKE_CURRENT_BINARY_DIR} ) +# Since these are included as object files, they should call the +# function as is (without visibility specification) +target_compile_definitions(f2c_vendored PRIVATE IGRAPH_STATIC) + if (WIN32) target_compile_definitions(f2c_vendored PRIVATE MSDOS) endif() @@ -111,7 +117,7 @@ if (MSVC) target_include_directories( f2c_vendored PUBLIC - ${PROJECT_SOURCE_DIR}/msvc/include + ${PROJECT_SOURCE_DIR}/msvc/include ) endif() @@ -128,11 +134,11 @@ if(MSVC) ) else() target_compile_options(arithchk PRIVATE - $<$:-Wno-format-zero-length> + $<$:-Wno-format-zero-length> ) target_compile_options( f2c_vendored PRIVATE - $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration -Wno-format-zero-length> + $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration -Wno-format-zero-length> $<$:-Wno-parentheses -Wno-pointer-to-int-cast -Wno-implicit-function-declaration> ) endif() diff --git a/src/vendor/cigraph/vendor/f2c/exit_.c b/src/vendor/cigraph/vendor/f2c/exit_.c index 08e9d07067c..dd5793a1a3e 100644 --- a/src/vendor/cigraph/vendor/f2c/exit_.c +++ b/src/vendor/cigraph/vendor/f2c/exit_.c @@ -33,7 +33,7 @@ exit_(integer *rc) #ifdef NO_ONEXIT f_exit(); #endif - exit(*rc); + IGRAPH_FATAL("exit_() called from f2c code"); } #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/main.c b/src/vendor/cigraph/vendor/f2c/main.c deleted file mode 100644 index d95fdc92cbc..00000000000 --- a/src/vendor/cigraph/vendor/f2c/main.c +++ /dev/null @@ -1,148 +0,0 @@ -/* STARTUP PROCEDURE FOR UNIX FORTRAN PROGRAMS */ - -#include "stdio.h" -#include "signal1.h" - -#ifndef SIGIOT -#ifdef SIGABRT -#define SIGIOT SIGABRT -#endif -#endif - -#ifndef KR_headers -#undef VOID -#include "stdlib.h" -#ifdef __cplusplus -extern "C" { -#endif -#endif - -#ifndef VOID -#define VOID void -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef NO__STDC -#define ONEXIT onexit -extern VOID f_exit(); -#else -#ifndef KR_headers -extern void f_exit(void); -#ifndef NO_ONEXIT -#define ONEXIT atexit -extern int atexit(void (*)(void)); -#endif -#else -#ifndef NO_ONEXIT -#define ONEXIT onexit -extern VOID f_exit(); -#endif -#endif -#endif - -#ifdef KR_headers -extern VOID f_init(), sig_die(); -extern int MAIN__(); -#define Int /* int */ -#else -extern void f_init(void), sig_die(const char*, int); -extern int MAIN__(void); -#define Int int -#endif - -static VOID sigfdie(Sigarg) -{ -Use_Sigarg; -sig_die("Floating Exception", 1); -} - - -static VOID sigidie(Sigarg) -{ -Use_Sigarg; -sig_die("IOT Trap", 1); -} - -#ifdef SIGQUIT -static VOID sigqdie(Sigarg) -{ -Use_Sigarg; -sig_die("Quit signal", 1); -} -#endif - - -static VOID sigindie(Sigarg) -{ -Use_Sigarg; -sig_die("Interrupt", 0); -} - -static VOID sigtdie(Sigarg) -{ -Use_Sigarg; -sig_die("Killed", 0); -} - -#ifdef SIGTRAP -static VOID sigtrdie(Sigarg) -{ -Use_Sigarg; -sig_die("Trace trap", 1); -} -#endif - - -int xargc; -char **xargv; - -#ifdef __cplusplus - } -#endif - - int -#ifdef KR_headers -main(argc, argv) int argc; char **argv; -#else -main(int argc, char **argv) -#endif -{ -xargc = argc; -xargv = argv; -signal1(SIGFPE, sigfdie); /* ignore underflow, enable overflow */ -#ifdef SIGIOT -signal1(SIGIOT, sigidie); -#endif -#ifdef SIGTRAP -signal1(SIGTRAP, sigtrdie); -#endif -#ifdef SIGQUIT -if(signal1(SIGQUIT,sigqdie) == SIG_IGN) - signal1(SIGQUIT, SIG_IGN); -#endif -if(signal1(SIGINT, sigindie) == SIG_IGN) - signal1(SIGINT, SIG_IGN); -signal1(SIGTERM,sigtdie); - -#ifdef pdp11 - ldfps(01200); /* detect overflow as an exception */ -#endif - -f_init(); -#ifndef NO_ONEXIT -ONEXIT(f_exit); -#endif -MAIN__(); -#ifdef NO_ONEXIT -f_exit(); -#endif -exit(0); /* exit(0) rather than return(0) to bypass Cray bug */ -return 0; /* For compilers that complain of missing return values; */ - /* others will complain that this is unreachable code. */ -} -#ifdef __cplusplus -} -#endif diff --git a/src/vendor/cigraph/vendor/f2c/s_paus.c b/src/vendor/cigraph/vendor/f2c/s_paus.c index 51d80eb0878..44ea4b62b96 100644 --- a/src/vendor/cigraph/vendor/f2c/s_paus.c +++ b/src/vendor/cigraph/vendor/f2c/s_paus.c @@ -39,6 +39,8 @@ s_1paus(fin) FILE *fin; s_1paus(FILE *fin) #endif { + IGRAPH_FATAL("s_1paus() called from f2c code"); + /* fprintf(stderr, "To resume execution, type go. Other input will terminate the job.\n"); fflush(stderr); @@ -49,6 +51,7 @@ s_1paus(FILE *fin) #endif exit(0); } + */ } int @@ -58,6 +61,8 @@ s_paus(s, n) char *s; ftnlen n; s_paus(char *s, ftnlen n) #endif { + IGRAPH_FATAL("s_paus() called from f2c code"); + /* fprintf(stderr, "PAUSE "); if(n > 0) fprintf(stderr, " %.*s", (int)n, s); @@ -86,6 +91,7 @@ s_paus(char *s, ftnlen n) } fprintf(stderr, "Execution resumes after PAUSE.\n"); fflush(stderr); + */ return 0; /* NOT REACHED */ #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/s_stop.c b/src/vendor/cigraph/vendor/f2c/s_stop.c index 68233aea7e3..aa4cf4642fb 100644 --- a/src/vendor/cigraph/vendor/f2c/s_stop.c +++ b/src/vendor/cigraph/vendor/f2c/s_stop.c @@ -20,6 +20,8 @@ void f_exit(void); int s_stop(char *s, ftnlen n) #endif { + IGRAPH_FATAL("STOP statement executed from f2c code"); + /* int i; if(n > 0) @@ -33,6 +35,7 @@ if(n > 0) f_exit(); #endif exit(0); +*/ /* We cannot avoid (useless) compiler diagnostics here: */ /* some compilers complain if there is no return statement, */ diff --git a/src/vendor/cigraph/vendor/f2c/sig_die.c b/src/vendor/cigraph/vendor/f2c/sig_die.c index 63a73d91183..19b72da99de 100644 --- a/src/vendor/cigraph/vendor/f2c/sig_die.c +++ b/src/vendor/cigraph/vendor/f2c/sig_die.c @@ -1,6 +1,8 @@ #include "stdio.h" #include "signal.h" +#include "igraph_error.h" + #ifndef SIGIOT #ifdef SIGABRT #define SIGIOT SIGABRT @@ -34,14 +36,13 @@ void sig_die(const char *s, int kill) #ifdef SIGIOT signal(SIGIOT, SIG_DFL); #endif - abort(); } else { #ifdef NO_ONEXIT f_exit(); #endif - exit(1); } + IGRAPH_FATAL("sig_die() called from f2c code"); } #ifdef __cplusplus } diff --git a/src/vendor/cigraph/vendor/f2c/uninit.c b/src/vendor/cigraph/vendor/f2c/uninit.c index 82b62429fee..ba495a92053 100644 --- a/src/vendor/cigraph/vendor/f2c/uninit.c +++ b/src/vendor/cigraph/vendor/f2c/uninit.c @@ -8,6 +8,8 @@ #include #include "arith.h" +#include "igraph_error.h" + #define TYSHORT 2 #define TYLONG 3 #define TYREAL 4 @@ -65,9 +67,8 @@ double _0 = 0.; void unsupported_error() { - fprintf(stderr,"Runtime Error: Your Architecture is not supported by the" - " -trapuv option of f2c\n"); - exit(-1); + IGRAPH_FATAL("Runtime Error: Your Architecture is not supported by the" + " -trapuv option of f2c"); } @@ -217,7 +218,7 @@ ieeeuserhand(unsigned exception[5], int val[2]) else if(exception[0]==_INVALID) fprintf(stderr,"invalid operation\n"); else fprintf(stderr,"\tunknown reason\n"); fflush(stderr); - abort(); + IGRAPH_FATAL("ieee0() aborting"); } static void @@ -228,7 +229,7 @@ ieeeuserhand2(unsigned int **j) #endif { fprintf(stderr,"ieee0() aborting because of confusion\n"); - abort(); + IGRAPH_FATAL("ieee0() aborting"); } static void diff --git a/src/vendor/cigraph/vendor/glpk/CMakeLists.txt b/src/vendor/cigraph/vendor/glpk/CMakeLists.txt index 212068fa471..9e27d977980 100644 --- a/src/vendor/cigraph/vendor/glpk/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/glpk/CMakeLists.txt @@ -106,6 +106,6 @@ if (MSVC) else() target_compile_options(glpk_vendored PRIVATE $<$:-wd161 -Wno-return-type> - $<$:-Wno-return-type -Wno-unused-value -Wno-dangling-else -Wno-logical-op-parentheses> + $<$:-Wno-return-type -Wno-unused-value -Wno-dangling-else -Wno-logical-op-parentheses> ) endif() diff --git a/src/vendor/cigraph/vendor/glpk/minisat/minisat.h b/src/vendor/cigraph/vendor/glpk/minisat/minisat.h index 2733e8d63cd..5d6bb955ca1 100644 --- a/src/vendor/cigraph/vendor/glpk/minisat/minisat.h +++ b/src/vendor/cigraph/vendor/glpk/minisat/minisat.h @@ -34,10 +34,15 @@ /*====================================================================*/ /* Simple types: */ +/* Changed in igraph: do not try to redefine the standard 'bool', + * needed for C23 compatibility. */ +/* typedef int bool; #define true 1 #define false 0 +*/ +#include typedef int lit; #if 0 /* by mao */ diff --git a/src/vendor/cigraph/vendor/lapack/CMakeLists.txt b/src/vendor/cigraph/vendor/lapack/CMakeLists.txt index d2602f18457..a0ff3c1b594 100644 --- a/src/vendor/cigraph/vendor/lapack/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/lapack/CMakeLists.txt @@ -72,9 +72,9 @@ endif() # mess around with the source of lapack too much to fix these if(NOT MSVC) target_compile_options(blas_vendored PRIVATE - $<$:-Wno-logical-op-parentheses> + $<$:-Wno-logical-op-parentheses> ) target_compile_options(lapack_vendored PRIVATE - $<$:-Wno-logical-op-parentheses -Wno-shift-op-parentheses> + $<$:-Wno-logical-op-parentheses -Wno-shift-op-parentheses> ) endif() diff --git a/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt b/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt index ee8d8a95e2b..8de182ecf7a 100644 --- a/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt +++ b/src/vendor/cigraph/vendor/mini-gmp/CMakeLists.txt @@ -31,7 +31,7 @@ if(MSVC) else() target_compile_options( gmp_vendored PRIVATE - $<$:-Wno-unused-variable> + $<$:-Wno-unused-variable> ) endif() diff --git a/src/vendor/cigraph/vendor/pcg/CMakeLists.txt b/src/vendor/cigraph/vendor/pcg/CMakeLists.txt new file mode 100644 index 00000000000..c106f5e1c1b --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/CMakeLists.txt @@ -0,0 +1,21 @@ +# Declare the files needed to compile our vendored copy of the PCG random +# number generator +add_library( + pcg + OBJECT + EXCLUDE_FROM_ALL + pcg-advance-64.c + pcg-advance-128.c + pcg-output-32.c + pcg-output-64.c + pcg-output-128.c + pcg-rngs-64.c + pcg-rngs-128.c +) + +if (BUILD_SHARED_LIBS) + set_property(TARGET pcg PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +use_all_warnings(pcg) + diff --git a/src/vendor/cigraph/vendor/pcg/LICENSE.txt b/src/vendor/cigraph/vendor/pcg/LICENSE.txt new file mode 100644 index 00000000000..23159603635 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c b/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c new file mode 100644 index 00000000000..eceb90e431f --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-advance-128.c @@ -0,0 +1,61 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Repetative C code is derived using C preprocessor metaprogramming + * techniques. + */ + +#include "pcg_variants.h" + +/* Multi-step advance functions (jump-ahead, jump-back) + * + * The method used here is based on Brown, "Random Number Generation + * with Arbitrary Stride,", Transactions of the American Nuclear + * Society (Nov. 1994). The algorithm is very similar to fast + * exponentiation. + * + * Even though delta is an unsigned integer, we can pass a + * signed integer to go backwards, it just goes "the long way round". + */ + +#if PCG_HAS_128BIT_OPS +pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, + pcg128_t cur_plus) +{ + pcg128_t acc_mult = 1u; + pcg128_t acc_plus = 0u; + while (delta > 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta /= 2; + } + return acc_mult * state + acc_plus; +} +#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c b/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c new file mode 100644 index 00000000000..ecd05deac94 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-advance-64.c @@ -0,0 +1,59 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Repetative C code is derived using C preprocessor metaprogramming + * techniques. + */ + +#include "pcg_variants.h" + +/* Multi-step advance functions (jump-ahead, jump-back) + * + * The method used here is based on Brown, "Random Number Generation + * with Arbitrary Stride,", Transactions of the American Nuclear + * Society (Nov. 1994). The algorithm is very similar to fast + * exponentiation. + * + * Even though delta is an unsigned integer, we can pass a + * signed integer to go backwards, it just goes "the long way round". + */ + +uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, uint64_t cur_mult, + uint64_t cur_plus) +{ + uint64_t acc_mult = 1u; + uint64_t acc_plus = 0u; + while (delta > 0) { + if (delta & 1) { + acc_mult *= cur_mult; + acc_plus = acc_plus * cur_mult + cur_plus; + } + cur_plus = (cur_mult + 1) * cur_plus; + cur_mult *= cur_mult; + delta /= 2; + } + return acc_mult * state + acc_plus; +} diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-128.c b/src/vendor/cigraph/vendor/pcg/pcg-output-128.c new file mode 100644 index 00000000000..39554991987 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-output-128.c @@ -0,0 +1,63 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot); +#endif + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +/* XSH RR */ + +/* RXS M XS */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state); +#endif + +/* RXS M */ + +/* XSL RR (only defined for >= 64 bits) */ + +/* XSL RR RR (only defined for >= 64 bits) */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state); +#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-32.c b/src/vendor/cigraph/vendor/pcg/pcg-output-32.c new file mode 100644 index 00000000000..dfe0cc5dae7 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-output-32.c @@ -0,0 +1,63 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +extern inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot); + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +extern inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state); + +/* XSH RR */ + +extern inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state); + +/* RXS M XS */ + +extern inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state); + +/* RXS M */ + +extern inline uint32_t pcg_output_rxs_m_64_32(uint64_t state); + +/* XSL RR (only defined for >= 64 bits) */ + +extern inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state); + +/* XSL RR RR (only defined for >= 64 bits) */ diff --git a/src/vendor/cigraph/vendor/pcg/pcg-output-64.c b/src/vendor/cigraph/vendor/pcg/pcg-output-64.c new file mode 100644 index 00000000000..ba3598d5433 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-output-64.c @@ -0,0 +1,73 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* + * Rotate helper functions. + */ + +extern inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot); + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state); +#endif + +/* XSH RR */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state); +#endif + +/* RXS M XS */ + +extern inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state); + +/* RXS M */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state); +#endif + +/* XSL RR (only defined for >= 64 bits) */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state); +#endif + +/* XSL RR RR (only defined for >= 64 bits) */ + +extern inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state); diff --git a/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c b/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c new file mode 100644 index 00000000000..6f8797f06bc --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-rngs-128.c @@ -0,0 +1,378 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_step_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, + pcg128_t delta); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, + pcg128_t delta); +#endif + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, + pcg128_t initstate, + pcg128_t initseq); +#endif + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empirical tests show that division is preferable to modulus for + * reducing the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSH RR */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound); +#endif + +/* Generation functions for RXS M */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSL RR (only defined for "large" types) */ + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline uint64_t +pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, uint64_t bound); +#endif + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng); +#endif + +#if PCG_HAS_128BIT_OPS +extern inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound); +#endif diff --git a/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c b/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c new file mode 100644 index 00000000000..a22df3116dd --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg-rngs-64.c @@ -0,0 +1,255 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * The contents of this file were mechanically derived from pcg_variants.h + * (every inline function defined there gets a generated extern declaration). + */ + +#include "pcg_variants.h" + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +extern inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_mcg_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_unique_64_step_r(struct pcg_state_64* rng); + +extern inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, + uint64_t delta); + +extern inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng); + +extern inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, + uint64_t delta); + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +extern inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate); + +extern inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, + uint64_t initstate, + uint64_t initseq); + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empirical tests show that division is preferable to modulus for + * reducing the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +extern inline uint32_t +pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSH RR */ + +extern inline uint32_t +pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +extern inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint64_t +pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound); + +/* Generation functions for RXS M */ + +extern inline uint32_t +pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSL RR (only defined for "large" types) */ + +extern inline uint32_t +pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +extern inline uint32_t +pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint32_t +pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound); + +extern inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng); + +extern inline uint32_t +pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, uint32_t bound); + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +extern inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng); + +extern inline uint64_t +pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound); + +extern inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng); + +extern inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound); diff --git a/src/vendor/cigraph/vendor/pcg/pcg_variants.h b/src/vendor/cigraph/vendor/pcg/pcg_variants.h new file mode 100644 index 00000000000..25ca2e71366 --- /dev/null +++ b/src/vendor/cigraph/vendor/pcg/pcg_variants.h @@ -0,0 +1,2557 @@ +/* + * PCG Random Number Generation for C. + * + * Copyright 2014-2019 Melissa O'Neill , + * and the PCG Project contributors. + * + * SPDX-License-Identifier: (Apache-2.0 OR MIT) + * + * Licensed under the Apache License, Version 2.0 (provided in + * LICENSE-APACHE.txt and at http://www.apache.org/licenses/LICENSE-2.0) + * or under the MIT license (provided in LICENSE-MIT.txt and at + * http://opensource.org/licenses/MIT), at your option. This file may not + * be copied, modified, or distributed except according to those terms. + * + * Distributed on an "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND, either + * express or implied. See your chosen license for details. + * + * For additional information about the PCG random number generation scheme, + * visit http://www.pcg-random.org/. + */ + +/* + * This code is derived from the canonical C++ PCG implementation, which + * has many additional features and is preferable if you can use C++ in + * your project. + * + * Much of the derivation was performed mechanically. In particular, the + * output functions were generated by compiling the C++ output functions + * into LLVM bitcode and then transforming that using the LLVM C backend + * (from https://github.com/draperlaboratory/llvm-cbe), and then + * postprocessing and hand editing the output. + * + * Much of the remaining code was generated by C-preprocessor metaprogramming. + */ + +#ifndef PCG_VARIANTS_H_INCLUDED +#define PCG_VARIANTS_H_INCLUDED 1 + +#include + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4146) /* "unary minus operator applied to unsigned type, result still unsigned" */ +#endif + +#if __SIZEOF_INT128__ + typedef __uint128_t pcg128_t; + #define PCG_128BIT_CONSTANT(high,low) \ + ((((pcg128_t)high) << 64) + low) + #define PCG_HAS_128BIT_OPS 1 +#endif + +/* Checking for !__GNUC_STDC_INLINE__ is a hack to work around a bug in the + * Intel compiler where it defined both __GNUC_GNU_INLINE__ and __GNUC_STDC_INLINE__ + * to 1 when using -std=gnu99. igraph is always compiled with -std=gnu99. + * + * Tested with icc (ICC) 2021.3.0 20210609 on Linux */ +#if __GNUC_GNU_INLINE__ && !__GNUC_STDC_INLINE__ && !defined(__cplusplus) + #error Nonstandard GNU inlining semantics. Compile with -std=c99 or better. + /* We could instead use macros PCG_INLINE and PCG_EXTERN_INLINE + but better to just reject ancient C code. */ +#endif + +#if __cplusplus +extern "C" { +#endif + +/* + * Rotate helper functions. + */ + +inline uint8_t pcg_rotr_8(uint8_t value, unsigned int rot) +{ +/* Unfortunately, clang is kinda pathetic when it comes to properly + * recognizing idiomatic rotate code, so for clang we actually provide + * assembler directives (enabled with PCG_USE_INLINE_ASM). Boo, hiss. + */ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorb %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 7)); +#endif +} + +inline uint16_t pcg_rotr_16(uint16_t value, unsigned int rot) +{ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorw %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 15)); +#endif +} + +inline uint32_t pcg_rotr_32(uint32_t value, unsigned int rot) +{ +#if PCG_USE_INLINE_ASM && __clang__ && (__x86_64__ || __i386__) + asm ("rorl %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 31)); +#endif +} + +inline uint64_t pcg_rotr_64(uint64_t value, unsigned int rot) +{ +#if 0 && PCG_USE_INLINE_ASM && __clang__ && __x86_64__ + /* For whatever reason, clang actually *does* generate rotq by + itself, so we don't need this code. */ + asm ("rorq %%cl, %0" : "=r" (value) : "0" (value), "c" (rot)); + return value; +#else + return (value >> rot) | (value << ((- rot) & 63)); +#endif +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_rotr_128(pcg128_t value, unsigned int rot) +{ + return (value >> rot) | (value << ((- rot) & 127)); +} +#endif + +/* + * Output functions. These are the core of the PCG generation scheme. + */ + +/* XSH RS */ + +inline uint8_t pcg_output_xsh_rs_16_8(uint16_t state) +{ + return (uint8_t)(((state >> 7u) ^ state) >> ((state >> 14u) + 3u)); +} + +inline uint16_t pcg_output_xsh_rs_32_16(uint32_t state) +{ + return (uint16_t)(((state >> 11u) ^ state) >> ((state >> 30u) + 11u)); +} + +inline uint32_t pcg_output_xsh_rs_64_32(uint64_t state) +{ + + return (uint32_t)(((state >> 22u) ^ state) >> ((state >> 61u) + 22u)); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rs_128_64(pcg128_t state) +{ + return (uint64_t)(((state >> 43u) ^ state) >> ((state >> 124u) + 45u)); +} +#endif + +/* XSH RR */ + +inline uint8_t pcg_output_xsh_rr_16_8(uint16_t state) +{ + return pcg_rotr_8(((state >> 5u) ^ state) >> 5u, state >> 13u); +} + +inline uint16_t pcg_output_xsh_rr_32_16(uint32_t state) +{ + return pcg_rotr_16(((state >> 10u) ^ state) >> 12u, state >> 28u); +} + +inline uint32_t pcg_output_xsh_rr_64_32(uint64_t state) +{ + return pcg_rotr_32(((state >> 18u) ^ state) >> 27u, state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsh_rr_128_64(pcg128_t state) +{ + return pcg_rotr_64(((state >> 35u) ^ state) >> 58u, state >> 122u); +} +#endif + +/* RXS M XS */ + +inline uint8_t pcg_output_rxs_m_xs_8_8(uint8_t state) +{ + uint8_t word = ((state >> ((state >> 6u) + 2u)) ^ state) * 217u; + return (word >> 6u) ^ word; +} + +inline uint16_t pcg_output_rxs_m_xs_16_16(uint16_t state) +{ + uint16_t word = ((state >> ((state >> 13u) + 3u)) ^ state) * 62169u; + return (word >> 11u) ^ word; +} + +inline uint32_t pcg_output_rxs_m_xs_32_32(uint32_t state) +{ + uint32_t word = ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; + return (word >> 22u) ^ word; +} + +inline uint64_t pcg_output_rxs_m_xs_64_64(uint64_t state) +{ + uint64_t word = ((state >> ((state >> 59u) + 5u)) ^ state) + * 12605985483714917081ull; + return (word >> 43u) ^ word; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_rxs_m_xs_128_128(pcg128_t state) +{ + pcg128_t word = ((state >> ((state >> 122u) + 6u)) ^ state) + * (PCG_128BIT_CONSTANT(17766728186571221404ULL, + 12605985483714917081ULL)); + /* 327738287884841127335028083622016905945 */ + return (word >> 86u) ^ word; +} +#endif + +/* RXS M */ + +inline uint8_t pcg_output_rxs_m_16_8(uint16_t state) +{ + return (((state >> ((state >> 13u) + 3u)) ^ state) * 62169u) >> 8u; +} + +inline uint16_t pcg_output_rxs_m_32_16(uint32_t state) +{ + return (((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u) >> 16u; +} + +inline uint32_t pcg_output_rxs_m_64_32(uint64_t state) +{ + return (((state >> ((state >> 59u) + 5u)) ^ state) + * 12605985483714917081ull) >> 32u; +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_rxs_m_128_64(pcg128_t state) +{ + return (((state >> ((state >> 122u) + 6u)) ^ state) + * (PCG_128BIT_CONSTANT(17766728186571221404ULL, + 12605985483714917081ULL))) >> 64u; + /* 327738287884841127335028083622016905945 */ +} +#endif + +/* XSL RR (only defined for >= 64 bits) */ + +inline uint32_t pcg_output_xsl_rr_64_32(uint64_t state) +{ + return pcg_rotr_32(((uint32_t)(state >> 32u)) ^ (uint32_t)state, + state >> 59u); +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_output_xsl_rr_128_64(pcg128_t state) +{ + return pcg_rotr_64(((uint64_t)(state >> 64u)) ^ (uint64_t)state, + state >> 122u); +} +#endif + +/* XSL RR RR (only defined for >= 64 bits) */ + +inline uint64_t pcg_output_xsl_rr_rr_64_64(uint64_t state) +{ + uint32_t rot1 = (uint32_t)(state >> 59u); + uint32_t high = (uint32_t)(state >> 32u); + uint32_t low = (uint32_t)state; + uint32_t xored = high ^ low; + uint32_t newlow = pcg_rotr_32(xored, rot1); + uint32_t newhigh = pcg_rotr_32(high, newlow & 31u); + return (((uint64_t)newhigh) << 32u) | newlow; +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_output_xsl_rr_rr_128_128(pcg128_t state) +{ + uint32_t rot1 = (uint32_t)(state >> 122u); + uint64_t high = (uint64_t)(state >> 64u); + uint64_t low = (uint64_t)state; + uint64_t xored = high ^ low; + uint64_t newlow = pcg_rotr_64(xored, rot1); + uint64_t newhigh = pcg_rotr_64(high, newlow & 63u); + return (((pcg128_t)newhigh) << 64u) | newlow; +} +#endif + +#define PCG_DEFAULT_MULTIPLIER_8 141U +#define PCG_DEFAULT_MULTIPLIER_16 12829U +#define PCG_DEFAULT_MULTIPLIER_32 747796405U +#define PCG_DEFAULT_MULTIPLIER_64 6364136223846793005ULL + +#define PCG_DEFAULT_INCREMENT_8 77U +#define PCG_DEFAULT_INCREMENT_16 47989U +#define PCG_DEFAULT_INCREMENT_32 2891336453U +#define PCG_DEFAULT_INCREMENT_64 1442695040888963407ULL + +#if PCG_HAS_128BIT_OPS +#define PCG_DEFAULT_MULTIPLIER_128 \ + PCG_128BIT_CONSTANT(2549297995355413924ULL,4865540595714422341ULL) +#define PCG_DEFAULT_INCREMENT_128 \ + PCG_128BIT_CONSTANT(6364136223846793005ULL,1442695040888963407ULL) +#endif + +/* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG_STATE_ONESEQ_8_INITIALIZER { 0xd7U } +#define PCG_STATE_ONESEQ_16_INITIALIZER { 0x20dfU } +#define PCG_STATE_ONESEQ_32_INITIALIZER { 0x46b56677U } +#define PCG_STATE_ONESEQ_64_INITIALIZER { 0x4d595df4d0f33173ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_ONESEQ_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0xb8dc10e158a92392ULL, 0x98046df007ec0a53ULL) } +#endif + +#define PCG_STATE_UNIQUE_8_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG_STATE_UNIQUE_16_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG_STATE_UNIQUE_32_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG_STATE_UNIQUE_64_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_UNIQUE_128_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG_STATE_MCG_8_INITIALIZER { 0xe5U } +#define PCG_STATE_MCG_16_INITIALIZER { 0xa5e5U } +#define PCG_STATE_MCG_32_INITIALIZER { 0xd15ea5e5U } +#define PCG_STATE_MCG_64_INITIALIZER { 0xcafef00dd15ea5e5ULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_MCG_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0x0000000000000000ULL, 0xcafef00dd15ea5e5ULL) } +#endif + +#define PCG_STATE_SETSEQ_8_INITIALIZER { 0x9bU, 0xdbU } +#define PCG_STATE_SETSEQ_16_INITIALIZER { 0xe39bU, 0x5bdbU } +#define PCG_STATE_SETSEQ_32_INITIALIZER { 0xec02d89bU, 0x94b95bdbU } +#define PCG_STATE_SETSEQ_64_INITIALIZER \ + { 0x853c49e6748fea9bULL, 0xda3e39cb94b95bdbULL } +#if PCG_HAS_128BIT_OPS +#define PCG_STATE_SETSEQ_128_INITIALIZER \ + { PCG_128BIT_CONSTANT(0x979c9a98d8462005ULL, 0x7d3e9cb6cfe0549bULL), \ + PCG_128BIT_CONSTANT(0x0000000000000001ULL, 0xda3e39cb94b95bdbULL) } +#endif + +/* Representations for the oneseq, mcg, and unique variants */ + +struct pcg_state_8 { + uint8_t state; +}; + +struct pcg_state_16 { + uint16_t state; +}; + +struct pcg_state_32 { + uint32_t state; +}; + +struct pcg_state_64 { + uint64_t state; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_128 { + pcg128_t state; +}; +#endif + +/* Representations setseq variants */ + +struct pcg_state_setseq_8 { + uint8_t state; + uint8_t inc; +}; + +struct pcg_state_setseq_16 { + uint16_t state; + uint16_t inc; +}; + +struct pcg_state_setseq_32 { + uint32_t state; + uint32_t inc; +}; + +struct pcg_state_setseq_64 { + uint64_t state; + uint64_t inc; +}; + +#if PCG_HAS_128BIT_OPS +struct pcg_state_setseq_128 { + pcg128_t state; + pcg128_t inc; +}; +#endif + +/* Multi-step advance functions (jump-ahead, jump-back) */ + +extern uint8_t pcg_advance_lcg_8(uint8_t state, uint8_t delta, uint8_t cur_mult, + uint8_t cur_plus); +extern uint16_t pcg_advance_lcg_16(uint16_t state, uint16_t delta, + uint16_t cur_mult, uint16_t cur_plus); +extern uint32_t pcg_advance_lcg_32(uint32_t state, uint32_t delta, + uint32_t cur_mult, uint32_t cur_plus); +extern uint64_t pcg_advance_lcg_64(uint64_t state, uint64_t delta, + uint64_t cur_mult, uint64_t cur_plus); + +#if PCG_HAS_128BIT_OPS +extern pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, + pcg128_t cur_mult, pcg128_t cur_plus); +#endif + +/* Functions to advance the underlying LCG, one version for each size and + * each style. These functions are considered semi-private. There is rarely + * a good reason to call them directly. + */ + +inline void pcg_oneseq_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + + PCG_DEFAULT_INCREMENT_8; +} + +inline void pcg_oneseq_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + PCG_DEFAULT_INCREMENT_8); +} + +inline void pcg_mcg_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8; +} + +inline void pcg_mcg_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state + = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, 0u); +} + +inline void pcg_unique_8_step_r(struct pcg_state_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + + (uint8_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_8_advance_r(struct pcg_state_8* rng, uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + (uint8_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_8_step_r(struct pcg_state_setseq_8* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_8 + rng->inc; +} + +inline void pcg_setseq_8_advance_r(struct pcg_state_setseq_8* rng, + uint8_t delta) +{ + rng->state = pcg_advance_lcg_8(rng->state, delta, PCG_DEFAULT_MULTIPLIER_8, + rng->inc); +} + +inline void pcg_oneseq_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + + PCG_DEFAULT_INCREMENT_16; +} + +inline void pcg_oneseq_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state = pcg_advance_lcg_16( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, PCG_DEFAULT_INCREMENT_16); +} + +inline void pcg_mcg_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16; +} + +inline void pcg_mcg_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state + = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, 0u); +} + +inline void pcg_unique_16_step_r(struct pcg_state_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + + (uint16_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_16_advance_r(struct pcg_state_16* rng, uint16_t delta) +{ + rng->state + = pcg_advance_lcg_16(rng->state, delta, PCG_DEFAULT_MULTIPLIER_16, + (uint16_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_16_step_r(struct pcg_state_setseq_16* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_16 + rng->inc; +} + +inline void pcg_setseq_16_advance_r(struct pcg_state_setseq_16* rng, + uint16_t delta) +{ + rng->state = pcg_advance_lcg_16(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_16, rng->inc); +} + +inline void pcg_oneseq_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + + PCG_DEFAULT_INCREMENT_32; +} + +inline void pcg_oneseq_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state = pcg_advance_lcg_32( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, PCG_DEFAULT_INCREMENT_32); +} + +inline void pcg_mcg_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32; +} + +inline void pcg_mcg_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state + = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, 0u); +} + +inline void pcg_unique_32_step_r(struct pcg_state_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + + (uint32_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_32_advance_r(struct pcg_state_32* rng, uint32_t delta) +{ + rng->state + = pcg_advance_lcg_32(rng->state, delta, PCG_DEFAULT_MULTIPLIER_32, + (uint32_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_32_step_r(struct pcg_state_setseq_32* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_32 + rng->inc; +} + +inline void pcg_setseq_32_advance_r(struct pcg_state_setseq_32* rng, + uint32_t delta) +{ + rng->state = pcg_advance_lcg_32(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_32, rng->inc); +} + +inline void pcg_oneseq_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + + PCG_DEFAULT_INCREMENT_64; +} + +inline void pcg_oneseq_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state = pcg_advance_lcg_64( + rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, PCG_DEFAULT_INCREMENT_64); +} + +inline void pcg_mcg_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64; +} + +inline void pcg_mcg_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state + = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, 0u); +} + +inline void pcg_unique_64_step_r(struct pcg_state_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + + (uint64_t)(((intptr_t)rng) | 1u); +} + +inline void pcg_unique_64_advance_r(struct pcg_state_64* rng, uint64_t delta) +{ + rng->state + = pcg_advance_lcg_64(rng->state, delta, PCG_DEFAULT_MULTIPLIER_64, + (uint64_t)(((intptr_t)rng) | 1u)); +} + +inline void pcg_setseq_64_step_r(struct pcg_state_setseq_64* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_64 + rng->inc; +} + +inline void pcg_setseq_64_advance_r(struct pcg_state_setseq_64* rng, + uint64_t delta) +{ + rng->state = pcg_advance_lcg_64(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_64, rng->inc); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + + PCG_DEFAULT_INCREMENT_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state + = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, + PCG_DEFAULT_INCREMENT_128); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, 0u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_step_r(struct pcg_state_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + + (pcg128_t)(((intptr_t)rng) | 1u); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_advance_r(struct pcg_state_128* rng, pcg128_t delta) +{ + rng->state + = pcg_advance_lcg_128(rng->state, delta, PCG_DEFAULT_MULTIPLIER_128, + (pcg128_t)(((intptr_t)rng) | 1u)); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_step_r(struct pcg_state_setseq_128* rng) +{ + rng->state = rng->state * PCG_DEFAULT_MULTIPLIER_128 + rng->inc; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_advance_r(struct pcg_state_setseq_128* rng, + pcg128_t delta) +{ + rng->state = pcg_advance_lcg_128(rng->state, delta, + PCG_DEFAULT_MULTIPLIER_128, rng->inc); +} +#endif + +/* Functions to seed the RNG state, one version for each size and each + * style. Unlike the step functions, regular users can and should call + * these functions. + */ + +inline void pcg_oneseq_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = 0U; + pcg_oneseq_8_step_r(rng); + rng->state += initstate; + pcg_oneseq_8_step_r(rng); +} + +inline void pcg_mcg_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_8_srandom_r(struct pcg_state_8* rng, uint8_t initstate) +{ + rng->state = 0U; + pcg_unique_8_step_r(rng); + rng->state += initstate; + pcg_unique_8_step_r(rng); +} + +inline void pcg_setseq_8_srandom_r(struct pcg_state_setseq_8* rng, + uint8_t initstate, uint8_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_8_step_r(rng); + rng->state += initstate; + pcg_setseq_8_step_r(rng); +} + +inline void pcg_oneseq_16_srandom_r(struct pcg_state_16* rng, + uint16_t initstate) +{ + rng->state = 0U; + pcg_oneseq_16_step_r(rng); + rng->state += initstate; + pcg_oneseq_16_step_r(rng); +} + +inline void pcg_mcg_16_srandom_r(struct pcg_state_16* rng, uint16_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_16_srandom_r(struct pcg_state_16* rng, + uint16_t initstate) +{ + rng->state = 0U; + pcg_unique_16_step_r(rng); + rng->state += initstate; + pcg_unique_16_step_r(rng); +} + +inline void pcg_setseq_16_srandom_r(struct pcg_state_setseq_16* rng, + uint16_t initstate, uint16_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_16_step_r(rng); + rng->state += initstate; + pcg_setseq_16_step_r(rng); +} + +inline void pcg_oneseq_32_srandom_r(struct pcg_state_32* rng, + uint32_t initstate) +{ + rng->state = 0U; + pcg_oneseq_32_step_r(rng); + rng->state += initstate; + pcg_oneseq_32_step_r(rng); +} + +inline void pcg_mcg_32_srandom_r(struct pcg_state_32* rng, uint32_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_32_srandom_r(struct pcg_state_32* rng, + uint32_t initstate) +{ + rng->state = 0U; + pcg_unique_32_step_r(rng); + rng->state += initstate; + pcg_unique_32_step_r(rng); +} + +inline void pcg_setseq_32_srandom_r(struct pcg_state_setseq_32* rng, + uint32_t initstate, uint32_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_32_step_r(rng); + rng->state += initstate; + pcg_setseq_32_step_r(rng); +} + +inline void pcg_oneseq_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate) +{ + rng->state = 0U; + pcg_oneseq_64_step_r(rng); + rng->state += initstate; + pcg_oneseq_64_step_r(rng); +} + +inline void pcg_mcg_64_srandom_r(struct pcg_state_64* rng, uint64_t initstate) +{ + rng->state = initstate | 1u; +} + +inline void pcg_unique_64_srandom_r(struct pcg_state_64* rng, + uint64_t initstate) +{ + rng->state = 0U; + pcg_unique_64_step_r(rng); + rng->state += initstate; + pcg_unique_64_step_r(rng); +} + +inline void pcg_setseq_64_srandom_r(struct pcg_state_setseq_64* rng, + uint64_t initstate, uint64_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_64_step_r(rng); + rng->state += initstate; + pcg_setseq_64_step_r(rng); +} + +#if PCG_HAS_128BIT_OPS +inline void pcg_oneseq_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate) +{ + rng->state = 0U; + pcg_oneseq_128_step_r(rng); + rng->state += initstate; + pcg_oneseq_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_mcg_128_srandom_r(struct pcg_state_128* rng, pcg128_t initstate) +{ + rng->state = initstate | 1u; +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_unique_128_srandom_r(struct pcg_state_128* rng, + pcg128_t initstate) +{ + rng->state = 0U; + pcg_unique_128_step_r(rng); + rng->state += initstate; + pcg_unique_128_step_r(rng); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline void pcg_setseq_128_srandom_r(struct pcg_state_setseq_128* rng, + pcg128_t initstate, pcg128_t initseq) +{ + rng->state = 0U; + rng->inc = (initseq << 1u) | 1u; + pcg_setseq_128_step_r(rng); + rng->state += initstate; + pcg_setseq_128_step_r(rng); +} +#endif + +/* Now, finally we create each of the individual generators. We provide + * a random_r function that provides a random number of the appropriate + * type (using the full range of the type) and a boundedrand_r version + * that provides + * + * Implementation notes for boundedrand_r: + * + * To avoid bias, we need to make the range of the RNG a multiple of + * bound, which we do by dropping output less than a threshold. + * Let's consider a 32-bit case... A naive scheme to calculate the + * threshold would be to do + * + * uint32_t threshold = 0x100000000ull % bound; + * + * but 64-bit div/mod is slower than 32-bit div/mod (especially on + * 32-bit platforms). In essence, we do + * + * uint32_t threshold = (0x100000000ull-bound) % bound; + * + * because this version will calculate the same modulus, but the LHS + * value is less than 2^32. + * + * (Note that using modulo is only wise for good RNGs, poorer RNGs + * such as raw LCGs do better using a technique based on division.) + * Empricical tests show that division is preferable to modulus for + * reducting the range of an RNG. It's faster, and sometimes it can + * even be statistically prefereable. + */ + +/* Generation functions for XSH RS */ + +inline uint8_t pcg_oneseq_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_xsh_rs_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rs_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rs_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rs_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rs_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rs_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rs_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rs_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rs_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rs_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rs_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rs_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rs_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rs_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSH RR */ + +inline uint8_t pcg_oneseq_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_xsh_rr_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_xsh_rr_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_xsh_rr_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsh_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsh_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_xsh_rr_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_xsh_rr_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_xsh_rr_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_xsh_rr_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_xsh_rr_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_xsh_rr_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_xsh_rr_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsh_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsh_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsh_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsh_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsh_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsh_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for RXS M XS (no MCG versions because they + * don't make sense when you want to use the entire state) + */ + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_random_r(struct pcg_state_8* rng) +{ + uint8_t oldstate = rng->state; + pcg_oneseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t pcg_oneseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_8* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_oneseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_oneseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_oneseq_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_oneseq_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint16_t pcg_unique_16_rxs_m_xs_16_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_unique_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_32_rxs_m_xs_32_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_unique_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t pcg_unique_64_rxs_m_xs_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_unique_128_rxs_m_xs_128_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_8_rxs_m_xs_8_random_r(struct pcg_state_setseq_8* rng) +{ + uint8_t oldstate = rng->state; + pcg_setseq_8_step_r(rng); + return pcg_output_rxs_m_xs_8_8(oldstate); +} + +inline uint8_t +pcg_setseq_8_rxs_m_xs_8_boundedrand_r(struct pcg_state_setseq_8* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_8_rxs_m_xs_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_rxs_m_xs_16_16(oldstate); +} + +inline uint16_t +pcg_setseq_16_rxs_m_xs_16_boundedrand_r(struct pcg_state_setseq_16* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_16_rxs_m_xs_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_rxs_m_xs_32_32(oldstate); +} + +inline uint32_t +pcg_setseq_32_rxs_m_xs_32_boundedrand_r(struct pcg_state_setseq_32* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_32_rxs_m_xs_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_rxs_m_xs_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_rxs_m_xs_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_rxs_m_xs_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_rxs_m_xs_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_rxs_m_xs_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_rxs_m_xs_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for RXS M */ + +inline uint8_t pcg_oneseq_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_oneseq_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_oneseq_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_oneseq_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_oneseq_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_oneseq_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_oneseq_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_oneseq_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_oneseq_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_unique_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_unique_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_unique_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_unique_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_unique_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_unique_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_unique_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_unique_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_unique_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_setseq_16_rxs_m_8_random_r(struct pcg_state_setseq_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_setseq_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t +pcg_setseq_16_rxs_m_8_boundedrand_r(struct pcg_state_setseq_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_setseq_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_setseq_32_rxs_m_16_random_r(struct pcg_state_setseq_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_setseq_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t +pcg_setseq_32_rxs_m_16_boundedrand_r(struct pcg_state_setseq_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_setseq_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_setseq_64_rxs_m_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_rxs_m_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_rxs_m_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_rxs_m_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint8_t pcg_mcg_16_rxs_m_8_random_r(struct pcg_state_16* rng) +{ + uint16_t oldstate = rng->state; + pcg_mcg_16_step_r(rng); + return pcg_output_rxs_m_16_8(oldstate); +} + +inline uint8_t pcg_mcg_16_rxs_m_8_boundedrand_r(struct pcg_state_16* rng, + uint8_t bound) +{ + uint8_t threshold = ((uint8_t)(-bound)) % bound; + for (;;) { + uint8_t r = pcg_mcg_16_rxs_m_8_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint16_t pcg_mcg_32_rxs_m_16_random_r(struct pcg_state_32* rng) +{ + uint32_t oldstate = rng->state; + pcg_mcg_32_step_r(rng); + return pcg_output_rxs_m_32_16(oldstate); +} + +inline uint16_t pcg_mcg_32_rxs_m_16_boundedrand_r(struct pcg_state_32* rng, + uint16_t bound) +{ + uint16_t threshold = ((uint16_t)(-bound)) % bound; + for (;;) { + uint16_t r = pcg_mcg_32_rxs_m_16_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +inline uint32_t pcg_mcg_64_rxs_m_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_rxs_m_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_rxs_m_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_rxs_m_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_rxs_m_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_rxs_m_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_rxs_m_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_rxs_m_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR (only defined for "large" types) */ + +inline uint32_t pcg_oneseq_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_oneseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_oneseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_oneseq_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_oneseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_unique_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_unique_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_unique_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_unique_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_unique_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t +pcg_setseq_64_xsl_rr_32_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t +pcg_setseq_64_xsl_rr_32_boundedrand_r(struct pcg_state_setseq_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_setseq_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t +pcg_setseq_128_xsl_rr_64_boundedrand_r(struct pcg_state_setseq_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint32_t pcg_mcg_64_xsl_rr_32_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_mcg_64_step_r(rng); + return pcg_output_xsl_rr_64_32(oldstate); +} + +inline uint32_t pcg_mcg_64_xsl_rr_32_boundedrand_r(struct pcg_state_64* rng, + uint32_t bound) +{ + uint32_t threshold = -bound % bound; + for (;;) { + uint32_t r = pcg_mcg_64_xsl_rr_32_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_random_r(struct pcg_state_128* rng) +{ + pcg_mcg_128_step_r(rng); + return pcg_output_xsl_rr_128_64(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline uint64_t pcg_mcg_128_xsl_rr_64_boundedrand_r(struct pcg_state_128* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_mcg_128_xsl_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/* Generation functions for XSL RR RR (only defined for "large" types) */ + +inline uint64_t pcg_oneseq_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_oneseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_oneseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_oneseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_oneseq_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) +{ + pcg_oneseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_oneseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_oneseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t pcg_unique_64_xsl_rr_rr_64_random_r(struct pcg_state_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_unique_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_unique_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_unique_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t pcg_unique_128_xsl_rr_rr_128_random_r(struct pcg_state_128* rng) +{ + pcg_unique_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_unique_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_unique_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_random_r(struct pcg_state_setseq_64* rng) +{ + uint64_t oldstate = rng->state; + pcg_setseq_64_step_r(rng); + return pcg_output_xsl_rr_rr_64_64(oldstate); +} + +inline uint64_t +pcg_setseq_64_xsl_rr_rr_64_boundedrand_r(struct pcg_state_setseq_64* rng, + uint64_t bound) +{ + uint64_t threshold = -bound % bound; + for (;;) { + uint64_t r = pcg_setseq_64_xsl_rr_rr_64_random_r(rng); + if (r >= threshold) + return r % bound; + } +} + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_random_r(struct pcg_state_setseq_128* rng) +{ + pcg_setseq_128_step_r(rng); + return pcg_output_xsl_rr_rr_128_128(rng->state); +} +#endif + +#if PCG_HAS_128BIT_OPS +inline pcg128_t +pcg_setseq_128_xsl_rr_rr_128_boundedrand_r(struct pcg_state_setseq_128* rng, + pcg128_t bound) +{ + pcg128_t threshold = -bound % bound; + for (;;) { + pcg128_t r = pcg_setseq_128_xsl_rr_rr_128_random_r(rng); + if (r >= threshold) + return r % bound; + } +} +#endif + +/*** Typedefs */ +typedef struct pcg_state_setseq_64 pcg32_random_t; +typedef struct pcg_state_64 pcg32s_random_t; +typedef struct pcg_state_64 pcg32u_random_t; +typedef struct pcg_state_64 pcg32f_random_t; +/*** random_r */ +#define pcg32_random_r pcg_setseq_64_xsh_rr_32_random_r +#define pcg32s_random_r pcg_oneseq_64_xsh_rr_32_random_r +#define pcg32u_random_r pcg_unique_64_xsh_rr_32_random_r +#define pcg32f_random_r pcg_mcg_64_xsh_rs_32_random_r +/*** boundedrand_r */ +#define pcg32_boundedrand_r pcg_setseq_64_xsh_rr_32_boundedrand_r +#define pcg32s_boundedrand_r pcg_oneseq_64_xsh_rr_32_boundedrand_r +#define pcg32u_boundedrand_r pcg_unique_64_xsh_rr_32_boundedrand_r +#define pcg32f_boundedrand_r pcg_mcg_64_xsh_rs_32_boundedrand_r +/*** srandom_r */ +#define pcg32_srandom_r pcg_setseq_64_srandom_r +#define pcg32s_srandom_r pcg_oneseq_64_srandom_r +#define pcg32u_srandom_r pcg_unique_64_srandom_r +#define pcg32f_srandom_r pcg_mcg_64_srandom_r +/*** advance_r */ +#define pcg32_advance_r pcg_setseq_64_advance_r +#define pcg32s_advance_r pcg_oneseq_64_advance_r +#define pcg32u_advance_r pcg_unique_64_advance_r +#define pcg32f_advance_r pcg_mcg_64_advance_r + +#if PCG_HAS_128BIT_OPS +/*** Typedefs */ +typedef struct pcg_state_setseq_128 pcg64_random_t; +typedef struct pcg_state_128 pcg64s_random_t; +typedef struct pcg_state_128 pcg64u_random_t; +typedef struct pcg_state_128 pcg64f_random_t; +/*** random_r */ +#define pcg64_random_r pcg_setseq_128_xsl_rr_64_random_r +#define pcg64s_random_r pcg_oneseq_128_xsl_rr_64_random_r +#define pcg64u_random_r pcg_unique_128_xsl_rr_64_random_r +#define pcg64f_random_r pcg_mcg_128_xsl_rr_64_random_r +/*** boundedrand_r */ +#define pcg64_boundedrand_r pcg_setseq_128_xsl_rr_64_boundedrand_r +#define pcg64s_boundedrand_r pcg_oneseq_128_xsl_rr_64_boundedrand_r +#define pcg64u_boundedrand_r pcg_unique_128_xsl_rr_64_boundedrand_r +#define pcg64f_boundedrand_r pcg_mcg_128_xsl_rr_64_boundedrand_r +/*** srandom_r */ +#define pcg64_srandom_r pcg_setseq_128_srandom_r +#define pcg64s_srandom_r pcg_oneseq_128_srandom_r +#define pcg64u_srandom_r pcg_unique_128_srandom_r +#define pcg64f_srandom_r pcg_mcg_128_srandom_r +/*** advance_r */ +#define pcg64_advance_r pcg_setseq_128_advance_r +#define pcg64s_advance_r pcg_oneseq_128_advance_r +#define pcg64u_advance_r pcg_unique_128_advance_r +#define pcg64f_advance_r pcg_mcg_128_advance_r +#endif + +/*** Typedefs */ +typedef struct pcg_state_8 pcg8si_random_t; +typedef struct pcg_state_16 pcg16si_random_t; +typedef struct pcg_state_32 pcg32si_random_t; +typedef struct pcg_state_64 pcg64si_random_t; +/*** random_r */ +#define pcg8si_random_r pcg_oneseq_8_rxs_m_xs_8_random_r +#define pcg16si_random_r pcg_oneseq_16_rxs_m_xs_16_random_r +#define pcg32si_random_r pcg_oneseq_32_rxs_m_xs_32_random_r +#define pcg64si_random_r pcg_oneseq_64_rxs_m_xs_64_random_r +/*** boundedrand_r */ +#define pcg8si_boundedrand_r pcg_oneseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16si_boundedrand_r pcg_oneseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32si_boundedrand_r pcg_oneseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64si_boundedrand_r pcg_oneseq_64_rxs_m_xs_64_boundedrand_r +/*** srandom_r */ +#define pcg8si_srandom_r pcg_oneseq_8_srandom_r +#define pcg16si_srandom_r pcg_oneseq_16_srandom_r +#define pcg32si_srandom_r pcg_oneseq_32_srandom_r +#define pcg64si_srandom_r pcg_oneseq_64_srandom_r +/*** advance_r */ +#define pcg8si_advance_r pcg_oneseq_8_advance_r +#define pcg16si_advance_r pcg_oneseq_16_advance_r +#define pcg32si_advance_r pcg_oneseq_32_advance_r +#define pcg64si_advance_r pcg_oneseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_128 pcg128si_random_t; +#define pcg128si_random_r pcg_oneseq_128_rxs_m_xs_128_random_r +#define pcg128si_boundedrand_r pcg_oneseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128si_srandom_r pcg_oneseq_128_srandom_r +#define pcg128si_advance_r pcg_oneseq_128_advance_r +#endif + +/*** Typedefs */ +typedef struct pcg_state_setseq_8 pcg8i_random_t; +typedef struct pcg_state_setseq_16 pcg16i_random_t; +typedef struct pcg_state_setseq_32 pcg32i_random_t; +typedef struct pcg_state_setseq_64 pcg64i_random_t; +/*** random_r */ +#define pcg8i_random_r pcg_setseq_8_rxs_m_xs_8_random_r +#define pcg16i_random_r pcg_setseq_16_rxs_m_xs_16_random_r +#define pcg32i_random_r pcg_setseq_32_rxs_m_xs_32_random_r +#define pcg64i_random_r pcg_setseq_64_rxs_m_xs_64_random_r +/*** boundedrand_r */ +#define pcg8i_boundedrand_r pcg_setseq_8_rxs_m_xs_8_boundedrand_r +#define pcg16i_boundedrand_r pcg_setseq_16_rxs_m_xs_16_boundedrand_r +#define pcg32i_boundedrand_r pcg_setseq_32_rxs_m_xs_32_boundedrand_r +#define pcg64i_boundedrand_r pcg_setseq_64_rxs_m_xs_64_boundedrand_r +/*** srandom_r */ +#define pcg8i_srandom_r pcg_setseq_8_srandom_r +#define pcg16i_srandom_r pcg_setseq_16_srandom_r +#define pcg32i_srandom_r pcg_setseq_32_srandom_r +#define pcg64i_srandom_r pcg_setseq_64_srandom_r +/*** advance_r */ +#define pcg8i_advance_r pcg_setseq_8_advance_r +#define pcg16i_advance_r pcg_setseq_16_advance_r +#define pcg32i_advance_r pcg_setseq_32_advance_r +#define pcg64i_advance_r pcg_setseq_64_advance_r + +#if PCG_HAS_128BIT_OPS +typedef struct pcg_state_setseq_128 pcg128i_random_t; +#define pcg128i_random_r pcg_setseq_128_rxs_m_xs_128_random_r +#define pcg128i_boundedrand_r pcg_setseq_128_rxs_m_xs_128_boundedrand_r +#define pcg128i_srandom_r pcg_setseq_128_srandom_r +#define pcg128i_advance_r pcg_setseq_128_advance_r +#endif + +extern uint32_t pcg32_random(void); +extern uint32_t pcg32_boundedrand(uint32_t bound); +extern void pcg32_srandom(uint64_t seed, uint64_t seq); +extern void pcg32_advance(uint64_t delta); + +#if PCG_HAS_128BIT_OPS +extern uint64_t pcg64_random(void); +extern uint64_t pcg64_boundedrand(uint64_t bound); +extern void pcg64_srandom(pcg128_t seed, pcg128_t seq); +extern void pcg64_advance(pcg128_t delta); +#endif + +/* + * Static initialization constants (if you can't call srandom for some + * bizarre reason). + */ + +#define PCG32_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#define PCG32U_INITIALIZER PCG_STATE_UNIQUE_64_INITIALIZER +#define PCG32S_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#define PCG32F_INITIALIZER PCG_STATE_MCG_64_INITIALIZER + +#if PCG_HAS_128BIT_OPS +#define PCG64_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#define PCG64U_INITIALIZER PCG_STATE_UNIQUE_128_INITIALIZER +#define PCG64S_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#define PCG64F_INITIALIZER PCG_STATE_MCG_128_INITIALIZER +#endif + +#define PCG8SI_INITIALIZER PCG_STATE_ONESEQ_8_INITIALIZER +#define PCG16SI_INITIALIZER PCG_STATE_ONESEQ_16_INITIALIZER +#define PCG32SI_INITIALIZER PCG_STATE_ONESEQ_32_INITIALIZER +#define PCG64SI_INITIALIZER PCG_STATE_ONESEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128SI_INITIALIZER PCG_STATE_ONESEQ_128_INITIALIZER +#endif + +#define PCG8I_INITIALIZER PCG_STATE_SETSEQ_8_INITIALIZER +#define PCG16I_INITIALIZER PCG_STATE_SETSEQ_16_INITIALIZER +#define PCG32I_INITIALIZER PCG_STATE_SETSEQ_32_INITIALIZER +#define PCG64I_INITIALIZER PCG_STATE_SETSEQ_64_INITIALIZER +#if PCG_HAS_128BIT_OPS +#define PCG128I_INITIALIZER PCG_STATE_SETSEQ_128_INITIALIZER +#endif + +#if __cplusplus +} +#endif + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* PCG_VARIANTS_H_INCLUDED */ diff --git a/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h b/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h index c58c98a75ad..83ed11e6eb5 100644 --- a/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h +++ b/src/vendor/cigraph/vendor/plfit/arithmetic_ansi.h @@ -51,7 +51,7 @@ inline static void vecfree(void *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; } diff --git a/src/vendor/cigraph/vendor/plfit/gss.c b/src/vendor/cigraph/vendor/plfit/gss.c index 9f332c0dc8e..5c5ffbd1a50 100644 --- a/src/vendor/cigraph/vendor/plfit/gss.c +++ b/src/vendor/cigraph/vendor/plfit/gss.c @@ -45,7 +45,7 @@ */ static const gss_parameter_t _defparam = { /* .epsilon = */ DBL_MIN, - /* .on_error = */ GSS_ERROR_STOP + /* .on_error = */ GSS_ERROR_STOP }; /** @@ -58,7 +58,7 @@ void gss_parameter_init(gss_parameter_t *param) { } unsigned short int gss_get_warning_flag(void) { - return gss_i_warning_flag; + return gss_i_warning_flag; } #define TERMINATE { \ @@ -80,7 +80,7 @@ unsigned short int gss_get_warning_flag(void) { retval = proc_progress(instance, x, fx, min, fmin, \ (a < b) ? a : b, (a < b) ? b : a, k); \ if (retval) { \ - TERMINATE; \ + TERMINATE; \ return PLFIT_SUCCESS; \ } \ } \ @@ -97,7 +97,7 @@ int gss(double a, double b, double *_min, double *_fmin, gss_parameter_t param = _param ? (*_param) : _defparam; - gss_i_warning_flag = 0; + gss_i_warning_flag = 0; if (a > b) { c = a; a = b; b = c; @@ -113,12 +113,12 @@ int gss(double a, double b, double *_min, double *_fmin, EVALUATE(c, fc); if (fc >= fa || fc >= fb) { - if (param.on_error == GSS_ERROR_STOP) { - return PLFIT_FAILURE; - } else { - gss_i_warning_flag = 1; - } - } + if (param.on_error == GSS_ERROR_STOP) { + return PLFIT_FAILURE; + } else { + gss_i_warning_flag = 1; + } + } while (fabs(a-b) > param.epsilon) { k++; @@ -127,12 +127,12 @@ int gss(double a, double b, double *_min, double *_fmin, 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 (param.on_error == GSS_ERROR_STOP) { + successful = 0; + break; + } else { + gss_i_warning_flag = 1; + } } if (fc <= fd) { @@ -146,7 +146,7 @@ int gss(double a, double b, double *_min, double *_fmin, c = (a+b) / 2.0; k++; EVALUATE(c, fc); - TERMINATE; + TERMINATE; } return successful ? PLFIT_SUCCESS : PLFIT_FAILURE; diff --git a/src/vendor/cigraph/vendor/plfit/hzeta.c b/src/vendor/cigraph/vendor/plfit/hzeta.c index eefd70c0e5c..861d1182f63 100644 --- a/src/vendor/cigraph/vendor/plfit/hzeta.c +++ b/src/vendor/cigraph/vendor/plfit/hzeta.c @@ -10,7 +10,7 @@ /* `hsl/specfunc/hzeta.c' C source file // HSL - Home Scientific Library -// Copyright (C) 2017-2018 Jerome Benoit +// Copyright (C) 2017-2022 Jerome Benoit // // HSL is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -97,7 +97,7 @@ typedef struct gsl_sf_result_struct gsl_sf_result; // B_{2j}/(2j) static -double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+1]={ +double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={ +1.0, +1.0/12.0, -1.0/720.0, @@ -130,12 +130,12 @@ double hsl_sf_hzeta_eulermaclaurin_series_coeffs[HSL_SF_HZETA_EULERMACLAURIN_SER +1.01530758555695563116307139454e-46, -2.57180415824187174992481940976e-48, +6.51445603523381493155843485864e-50, - -1.65013099068965245550609878048e-51 - }; // hsl_sf_hzeta_eulermaclaurin_series_coeffs + -1.65013099068965245550609878048e-51, + NAN}; // 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]={ +double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER+2]={ -2.0, +1.0/6.0, +1.0/360.0, @@ -168,8 +168,8 @@ double hsl_sf_hzeta_eulermaclaurin_series_majorantratios[HSL_SF_HZETA_EULERMACLA +2.03061517111391126232614278906e-46, +5.14360831648374349984963881946e-48, +1.30289120704676298631168697172e-49, - +3.30026198137930491101219756091e-51 - }; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios + +3.30026198137930491101219756091e-51, + NAN}; // hsl_sf_hzeta_eulermaclaurin_series_majorantratios extern @@ -234,6 +234,7 @@ int hsl_sf_hzeta_e(const double s, const double q, gsl_sf_result * result) { ratio=scp*pcp; if ((fabs(delta/ans)) < (0.5*GSL_DBL_EPSILON)) break; } + if (HSL_SF_HZETA_EULERMACLAURIN_SERIES_ORDER 0, x <- t, y <- y. - - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, + - Case c: if f(t) <= f(x) && f'(t)*f'(x) < 0, x <- t, y <- x. */ if (*fx < *ft) { diff --git a/src/vendor/cigraph/vendor/plfit/lbfgs.h b/src/vendor/cigraph/vendor/plfit/lbfgs.h index f26ae874e49..c36a0b1f832 100644 --- a/src/vendor/cigraph/vendor/plfit/lbfgs.h +++ b/src/vendor/cigraph/vendor/plfit/lbfgs.h @@ -59,7 +59,7 @@ typedef double lbfgsfloatval_t; #endif -/** +/** * \addtogroup liblbfgs_api libLBFGS API * @{ * @@ -68,7 +68,7 @@ typedef double lbfgsfloatval_t; /** * Return values of lbfgs(). - * + * * Roughly speaking, a negative value indicates an error. */ enum { @@ -365,7 +365,7 @@ typedef struct { * 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 @@ -502,14 +502,14 @@ void lbfgs_parameter_init(lbfgs_parameter_t *param); * 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. */ diff --git a/src/vendor/cigraph/vendor/plfit/mt.c b/src/vendor/cigraph/vendor/plfit/mt.c index f9c2b74609b..349a0a4d477 100644 --- a/src/vendor/cigraph/vendor/plfit/mt.c +++ b/src/vendor/cigraph/vendor/plfit/mt.c @@ -18,7 +18,7 @@ #include "plfit_mt.h" static uint16_t get_random_uint16(void) { - return RNG_INT31() & 0xFFFF; + return RNG_INTEGER(0, 0xffff); } void plfit_mt_init(plfit_mt_rng_t* rng) { @@ -32,7 +32,7 @@ void plfit_mt_init_from_rng(plfit_mt_rng_t* rng, plfit_mt_rng_t* seeder) { 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(); + rng->mt_buffer[i] = (((uint32_t) get_random_uint16()) << 16) + get_random_uint16(); } } else { for (i = 0; i < PLFIT_MT_LEN; i++) { @@ -56,7 +56,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { int idx = rng->mt_index; uint32_t s; int i; - + if (idx == PLFIT_MT_LEN * sizeof(uint32_t)) { idx = 0; i = 0; @@ -68,7 +68,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { 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); } @@ -79,7 +79,7 @@ uint32_t plfit_mt_random(plfit_mt_rng_t* rng) { 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; diff --git a/src/vendor/cigraph/vendor/plfit/plfit.c b/src/vendor/cigraph/vendor/plfit/plfit.c index 266a9b02add..05d72624ca3 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit.c +++ b/src/vendor/cigraph/vendor/plfit/plfit.c @@ -116,7 +116,7 @@ static double* extract_smaller(double* begin, double* end, double xmin, size_t counter = count_smaller(begin, end, xmin); double *p, *result; - result = calloc(counter, sizeof(double)); + result = calloc(counter > 0 ? counter : 1, sizeof(double)); if (result == 0) return 0; @@ -350,7 +350,7 @@ static int plfit_i_calculate_p_value_continuous(double* xs, size_t n, #endif /* Allocate memory to sample into */ - ys = calloc(n, sizeof(double)); + ys = calloc(n > 0 ? n : 1, sizeof(double)); if (ys == 0) { retval = PLFIT_ENOMEM; } else { @@ -540,7 +540,7 @@ static int plfit_i_continuous_xmin_opt_linear_scan( 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; + local_best_n = local_opt_data.end - local_opt_data.probes[i]; } } @@ -583,10 +583,16 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt int success; size_t i, best_n, num_uniques = 0; - double x, *px, **uniques; + double x, *px, **uniques, **strata; + int error_code, retval = PLFIT_SUCCESS; DATA_POINTS_CHECK; + /* Set up pointers that we will allocate */ + opt_data.begin = NULL; + uniques = NULL; + strata = NULL; + /* Sane defaults */ best_n = n; if (!options) @@ -599,8 +605,10 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt /* 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) + if (uniques == 0) { + free(opt_data.begin); 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 @@ -634,7 +642,6 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt 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]; @@ -644,8 +651,8 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt 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; + retval = error_code; + goto cleanup; } opt_data.num_probes = 0; @@ -664,14 +671,17 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt } } - free(strata); + free(strata); strata = NULL; + 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 - ) - ); + error_code = plfit_i_continuous_xmin_opt_linear_scan( + &opt_data, &best_result, &best_n + ); + if (error_code) { + retval = error_code; + goto cleanup; + } success = 1; } else { /* This should not happen, but we handle it anyway */ @@ -689,26 +699,47 @@ int plfit_continuous(double* xs, size_t n, const plfit_continuous_options_t* opt /* 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)); + error_code = plfit_i_continuous_xmin_opt_linear_scan(&opt_data, &best_result, &best_n); + if (error_code) { + retval = error_code; + goto cleanup; + } success = 1; } /* Get rid of the uniques array, we don't need it any more */ - free(uniques); + free(uniques); uniques = NULL; /* 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)); + error_code = plfit_log_likelihood_continuous( + opt_data.begin + n - best_n, best_n, result->alpha, result->xmin, + &result->L + ); + if (error_code) { + retval = error_code; + goto cleanup; + } + + error_code = plfit_i_calculate_p_value_continuous(opt_data.begin, n, options, 0, result); + if (error_code) { + retval = error_code; + goto cleanup; + } - /* Get rid of the copied data as well */ +cleanup: + if (strata) { + free(strata); + } + if (uniques) { + free(uniques); + } free(opt_data.begin); - return PLFIT_SUCCESS; + return retval; } /********** Discrete power law distribution fitting **********/ @@ -1040,7 +1071,7 @@ static int plfit_i_calculate_p_value_discrete(double* xs, size_t n, #endif /* Allocate memory to sample into */ - ys = calloc(n, sizeof(double)); + ys = calloc(n > 0 ? n : 1, sizeof(double)); if (ys == 0) { retval = PLFIT_ENOMEM; } else { @@ -1220,7 +1251,7 @@ int plfit_discrete(double* xs, size_t n, const plfit_discrete_options_t* options 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, + 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)); diff --git a/src/vendor/cigraph/vendor/plfit/plfit_error.c b/src/vendor/cigraph/vendor/plfit/plfit_error.c index d93d1c0db32..81bb31b5ba1 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_error.c +++ b/src/vendor/cigraph/vendor/plfit/plfit_error.c @@ -22,13 +22,14 @@ #include "plfit_error.h" #include "platform.h" -static char *plfit_i_error_strings[] = { +static const char *plfit_i_error_strings[] = { "No error", "Failed", "Invalid value", "Underflow", "Overflow", - "Not enough memory" + "Not enough memory", + "Maximum number of iterations exceeded" }; #ifndef USING_R diff --git a/src/vendor/cigraph/vendor/plfit/plfit_error.h b/src/vendor/cigraph/vendor/plfit/plfit_error.h index e5429975a07..b9170b380a8 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_error.h +++ b/src/vendor/cigraph/vendor/plfit/plfit_error.h @@ -38,7 +38,8 @@ enum { PLFIT_EINVAL = 2, PLFIT_UNDRFLOW = 3, PLFIT_OVERFLOW = 4, - PLFIT_ENOMEM = 5 + PLFIT_ENOMEM = 5, + PLFIT_EMAXITER = 6 }; #if (defined(__GNUC__) && GCC_VERSION_MAJOR >= 3) @@ -55,7 +56,7 @@ enum { if (PLFIT_UNLIKELY(plfit_i_ret != PLFIT_SUCCESS)) {\ return plfit_i_ret; \ } \ - } while(0) + } while (0) #define PLFIT_ERROR(reason,plfit_errno) \ do {\ diff --git a/src/vendor/cigraph/vendor/plfit/plfit_version.h b/src/vendor/cigraph/vendor/plfit/plfit_version.h index 1212dd1cd1f..37da90abc28 100644 --- a/src/vendor/cigraph/vendor/plfit/plfit_version.h +++ b/src/vendor/cigraph/vendor/plfit/plfit_version.h @@ -22,7 +22,7 @@ #define PLFIT_VERSION_MAJOR 0 #define PLFIT_VERSION_MINOR 9 -#define PLFIT_VERSION_PATCH 3 -#define PLFIT_VERSION_STRING "0.9.3" +#define PLFIT_VERSION_PATCH 4 +#define PLFIT_VERSION_STRING "0.9.4" #endif diff --git a/src/vendor/cigraph/vendor/plfit/sampling.c b/src/vendor/cigraph/vendor/plfit/sampling.c index 4becf134c20..cf0ff00242d 100644 --- a/src/vendor/cigraph/vendor/plfit/sampling.c +++ b/src/vendor/cigraph/vendor/plfit/sampling.c @@ -187,11 +187,11 @@ int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, ps_end = ps + n; /* Initialize indexes and probs */ - sampler->indexes = (long int*)calloc(n, sizeof(long int)); + sampler->indexes = (long int*)calloc(n > 0 ? n : 1, sizeof(long int)); if (sampler->indexes == 0) { return PLFIT_ENOMEM; } - sampler->probs = (double*)calloc(n, sizeof(double)); + sampler->probs = (double*)calloc(n > 0 ? n : 1, sizeof(double)); if (sampler->probs == 0) { free(sampler->indexes); return PLFIT_ENOMEM; @@ -215,13 +215,13 @@ int plfit_walker_alias_sampler_init(plfit_walker_alias_sampler_t* sampler, } /* Allocate space for short & long stick indexes */ - long_sticks = (long int*)calloc(num_long_sticks, sizeof(long int)); + long_sticks = (long int*)calloc(num_long_sticks > 0 ? num_long_sticks : 1, 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)); + short_sticks = (long int*)calloc(num_short_sticks > 0 ? num_short_sticks : 1, sizeof(long int)); if (short_sticks == 0) { free(sampler->probs); free(sampler->indexes);