diff --git a/.all-contributorsrc b/.all-contributorsrc index 77cfa9dc3f..e031cc7cc4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -585,6 +585,15 @@ "contributions": [ "code" ] + }, + { + "login": "gendelpiekel", + "name": "Michael", + "avatar_url": "https://avatars.githubusercontent.com/u/14215028?v=4", + "profile": "https://github.com/gendelpiekel", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7 diff --git a/.azure/build-win.yml b/.azure/build-win.yml index c4a4496b75..a2eca4b99f 100644 --- a/.azure/build-win.yml +++ b/.azure/build-win.yml @@ -41,9 +41,14 @@ parameters: - name: vcpkg_target_triplet type: string default: 'x64-windows-static-md' - - name: vsver # for choosing a VS toolset, see https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering + - name: vsver + # For choosing a VS toolset, see https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering + # Generally, 14.1 is VS2017, 14.2 is VS2019, while 14.3 and 14.4 are VS2022. Use a single digit after the dot to\ + # be flexible with the patch version the current runner has installed. Note that VS 2022 17.10 and later require + # a version of 14.4 instead of 14.3, see: + # https://devblogs.microsoft.com/cppblog/msvc-toolset-minor-version-number-14-40-in-vs-2022-v17-10/ type: string - default: '14.1' # VS2017 + default: '14.2' # VS2019 steps: - task: Cache@2 @@ -96,8 +101,7 @@ steps: set CXX=cl.exe set CC=cl.exe - cmake .. -DCMAKE_PREFIX_PATH=%CONDA%\Library\lib ^ - -DIGRAPH_USE_INTERNAL_BLAS=${{ parameters.int_blas }} ^ + cmake .. -DIGRAPH_USE_INTERNAL_BLAS=${{ parameters.int_blas }} ^ -DIGRAPH_USE_INTERNAL_LAPACK=${{ parameters.int_lapack }} ^ -DIGRAPH_USE_INTERNAL_ARPACK=${{ parameters.int_arpack }} ^ -DIGRAPH_USE_INTERNAL_GLPK=${{ parameters.int_glpk }} ^ diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..e03dd7787e --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/.github/stale.yml b/.github/stale.yml index 58e48a20fc..d6483a0caa 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,5 +1,5 @@ # Number of days of inactivity before an issue becomes stale -daysUntilStale: 60 +daysUntilStale: 28 # Number of days of inactivity before a stale issue is closed daysUntilClose: 14 diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml index 2bff51c190..fc03189635 100644 --- a/.github/workflows/cifuzz.yml +++ b/.github/workflows/cifuzz.yml @@ -21,14 +21,14 @@ jobs: fuzz-seconds: 720 output-sarif: true - name: Upload Crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts path: ./out/artifacts - name: Upload Sarif if: always() && steps.build.outcome == 'success' - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: # Path to SARIF file relative to the root of the repository sarif_file: cifuzz-sarif/results.sarif diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index bf14b055c3..848e452b47 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -38,3 +38,5 @@ jobs: - name: Codecov.io uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml index 431aa3609b..65ef5a005c 100644 --- a/.github/workflows/coverity.yml +++ b/.github/workflows/coverity.yml @@ -13,6 +13,9 @@ jobs: coverity: runs-on: ubuntu-latest + # Do not run in forks + if: github.repository == 'igraph/igraph' + steps: - name: Install dependencies run: sudo apt-get install bison flex libarpack2-dev libglpk-dev libgmp-dev libxml2-dev @@ -21,6 +24,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + ref: 'develop' - name: Create build environment run: cmake -E make_directory ${{github.workspace}}/build diff --git a/.github/workflows/stimulus.yml b/.github/workflows/stimulus.yml index 34ff6a2c66..64180145b7 100644 --- a/.github/workflows/stimulus.yml +++ b/.github/workflows/stimulus.yml @@ -5,7 +5,7 @@ jobs: ci: runs-on: ubuntu-latest env: - STIMULUS_VERSION: "0.19.0" + STIMULUS_VERSION: "0.21.6" steps: # Use Clang for more informative error messages - name: Install Clang diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 25cc26e04a..5b284fe1aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ exclude: "(^vendor/|\\.patch$)" repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.6.0 hooks: - id: mixed-line-ending args: ["--fix=lf"] diff --git a/CHANGELOG.md b/CHANGELOG.md index b46da136e5..2b97b52ba3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,11 +2,100 @@ ## [master] +### Fixed + + - `igraph_layout_drl()` and `igraph_layout_drl_3d()` would crash with an assertion failure when interrupted. This is now fixed. + - Removed broken interruption support from `igraph_community_spinglass_single()`. + +### Deprecated + + - `igraph_minimum_spanning_tree_prim()` and `igraph_minimum_spanning_tree_unweighted()` are deprecated. Use `igraph_minimum_spanning_tree()` in conjunction with `igraph_subgraph_from_edges()` instead. + +### Other + + - Fixed multiple memory leaks in benchmark programs. + - Documentation improvements. + +## [0.10.13] + +### Added + + - `igraph_bitset_fill()` sets all elements of a bitset to the same value (experimental function). + - `igraph_bitset_null()` clears all elements of a bitset (experimental function). + - `igraph_bitset_is_all_zero()`, `igraph_bitset_is_all_one()`, `igraph_bitset_is_any_zero()`, `igraph_bitset_is_any_one()` check if any/all elements of a bitset are zeros/ones (experimental functions). + - `igraph_chung_lu_game()` implements the classic Chung-Lu model, as well as a number of its variants (experimental function). + - `igraph_mean_degree()` computes the average of vertex degrees (experimental function). + - `igraph_count_loops()` counts self-loops in the graph (experimental function). + - `igraph_is_clique()` checks if all pairs within a set of vertices are connected (experimental function). + - `igraph_is_independent_vertex_set()` checks if no pairs within a set of vertices are connected (experimental function). + - `igraph_hypercube()` creates a hypercube graph (experimental function). + - `igraph_vector_intersection_size_sorted()` counts elements common to two sorted vectors (experimental function). + - `igraph_stack_capacity()` returns the allocated capacity of a stack. + - `igraph_vector_is_all_finite()` checks if all elements in a vector are finite (i.e. neither NaN nor Inf). + +### Fixed + + - Fixed a bug that incorrectly cached that a graph has no multiple edges when `igraph_init_adjlist()` was called with `IGRAPH_NO_LOOPS` and `IGRAPH_NO_MULTIPLE` and all the multi-edges were loop edges. + - `igraph_is_forest()` would fail to set the result variable when testing for a directed forest, and it was already cached that the graph was not an undirected forest. + - `igraph_hub_and_authority_scores()` no longer clips negative results to zeros when negative weights are present. + - Fixed an assertion failure in `igraph_realize_bipartite_degree_sequence()` with some non-graphical degree sequences when requesting simple bipartite graphs. + - `igraph_static_fitness_game()` checks the input more carefully, and avoids an infinite loop in rare edge cases, such as when (almost) all fitness scores are zero. + - `igraph_arpack_rnsolve()` used the incorrect error message text for some errors. This is now corrected. + - Corrected the detection of some MSVC-specific bitset intrinsics during configuration. + - Corrected a bug in the fallback implementation of `igraph_bitset_countl_zero()` when `IGRAPH_INTEGER_SIZE` was set to 32. This fallback implementation was _not_ used with GCC, Clang, or MSVC. + +### Changed + + - `igraph_is_graphical()` and `igraph_is_bigraphical()` are now linear-time in all cases, and generally several times faster than before (thanks to @gendelpiekel, contributed in #2605). + - `igraph_erdos_renyi_game_gnp()` can now generate graphs with more than a hundred million vertices. + - `igraph_hub_and_authority_scores()` now warns when negative edge weights are present. + - `igraph_layout_lgl()` now uses a BFS tree rooted in the vertex specified as `proot` to guide the layout. Previously it used an unspecified (arbitrary) spanning tree. + - Updated the internal heuristics used by igraph's ARPACK interface, `igraph_arpack_rssolve()` and `igraph_arpack_rnsolve()`, to improve the robustness of calculations. + - Updated the initial vector construction in `igraph_hub_and_authority_scores()`, `igraph_eigenvector_centrality()` and `igraph_(personalized_)pagerank()` with `IGRAPH_PAGERANK_ALGO_ARPACK`. This improves the robustness and convergence of calculations. + +### Other + + - Documentation improvements. + - Reduced the memory usage of several functions by using bitsets instead of boolean vectors. + - `igraph_vector_intersect_sorted()` has better performance when the input vector sizes are similar. + +## [0.10.12] - 2024-05-06 + +### Added + + - `igraph_transitive_closure()` computes the transitive closure of a graph (experimental function). + - `igraph_reachability()` determines which vertices are reachable from each other in a graph (experimental function). + - `igraph_count_reachable()` counts how many vertices are reachable from each vertex (experimental function). + - Added a bitset data structure, `igraph_bitset_t`, and a set of corresponding functions (experimental functionality). + +### Fixed + + - `igraph_community_label_propagation()` is now interruptible. + - `igraph_is_bipartite()` would on rare occasions return invalid results when the cache was employed. + - `igraph_weighted_adjacency()` correctly passes through NaN values with `IGRAPH_ADJ_MAX`, and correctly recognizes symmetric adjacency matrices containing NaN values with `IGRAPH_ADJ_UNDIRECTED`. + - `igraph_read_graph_gml()` can now read GML files that use ids larger than what is representable on 32 bits, provided that igraph was configured with a 64-bit `igraph_integer_t` size. + - Fixed a performance issue in `igraph_read_graph_graphml()` with files containing a very large number of entities, such as `>`. + - `igraph_read_graph_pajek()` has improved vertex ID validation that better matches that of Pajek's own behavior. + +### Changed + + - `igraph_eigenvector_centrality()` no longer issues a warning when the input is directed and weighted. When using this function, keep in mind that eigenvector centrality is well-defined only for (strongly) connected graphs, and edges with a zero weights are effectively treated as absent. + +### Deprecated + + - `igraph_transitive_closure_dag()` is deprecated in favour of `igraph_transitive_closure()` + +### Other + + - Documentation improvements. + - `igraph_strength()` and `igraph_degree(loops=false)` are now faster when calculating values for all vertices (contributed by @gendelpiekel in #2602) + ## [0.10.11] - 2024-04-02 ### Added - `igraph_is_complete()` checks whether there is a connection between all pairs of vertices (experimental function, contributed by Aymeric Agon-Rambosson @aagon in #2510). + - `igraph_join()` creates the _join_ of two graphs (experimental function, contributed by Quinn Buratynski @GanzuraTheConsumer in #2508). ### Fixed @@ -1320,7 +1409,9 @@ 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.11..master +[master]: https://github.com/igraph/igraph/compare/0.10.13..master +[0.10.13]: https://github.com/igraph/igraph/compare/0.10.12..0.10.13 +[0.10.12]: https://github.com/igraph/igraph/compare/0.10.11..0.10.12 [0.10.11]: https://github.com/igraph/igraph/compare/0.10.10..0.10.11 [0.10.10]: https://github.com/igraph/igraph/compare/0.10.9..0.10.10 [0.10.9]: https://github.com/igraph/igraph/compare/0.10.8..0.10.9 diff --git a/CITATION.cff b/CITATION.cff index 842c13a25e..9d902cb363 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -27,7 +27,7 @@ authors: family-names: Noom identifiers: - type: doi - value: 10.5281/zenodo.4319996 + value: 10.5281/zenodo.3630268 description: Zenodo repository-code: 'https://github.com/igraph/igraph' url: 'https://igraph.org' diff --git a/CMakeLists.txt b/CMakeLists.txt index 63f46d963b..26256d5a90 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ # * 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.27) +cmake_minimum_required(VERSION 3.18...3.29) # 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) @@ -113,10 +113,11 @@ 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. +# Check for 128-bit integer multiplication support, floating-point endianness, +# support for built-in overflow detection and fast bit operation support. include(ieee754_endianness) include(uint128_support) +include(bit_operations_support) include(safe_math_support) if(NOT HAVE_USELOCALE AND NOT HAVE__CONFIGTHREADLOCALE) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 3402d546ed..0000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,178 +0,0 @@ -# igraph Code of Conduct - -## Introduction - -This code of conduct applies to all spaces managed by the igraph project, -including all public and private mailing lists, issue trackers, wikis, blogs, -Twitter, and any other communication channel used by our community. Any events -related to our community shall also be bound by this code of conduct or a very -similar variant thereof. - -This code of conduct should be honored by everyone who participates in the -igraph community formally or informally, or claims any affiliation with the -project, in any project-related activities, and, especially, when representing -the project, in any capacity. - -This code of conduct is neither exhaustive nor complete. It serves to distill -our common understanding of a collaborative, shared environment and goals. -Please try to follow this code in spirit as much as in letter, to create -a friendly and productive environment that enriches the surrounding community. - -## Specific guidelines - -We strive to: - -1. Be open. We invite anyone to participate in our community. We prefer to use - public methods of communication for project-related messages, unless - discussing something sensitive. This applies to messages for help or - project-related support, too; not only is a public-support request much more - likely to result in an answer to a question, it also ensures that any - inadvertent mistakes in answering are more easily detected and corrected. - -2. Be empathetic, welcoming, friendly, and patient. We work together to resolve - conflict, and assume good intentions. We may all experience some frustration - from time to time, but we do not allow frustration to turn into a personal - attack. A community where people feel uncomfortable or threatened is not a - productive one. - -3. Be collaborative. Our work will be used by other people, and in turn we will - depend on the work of others. When we make something for the benefit of the - project, we are willing to explain to others how it works, so that they can - build on the work to make it even better. Any decision we make will affect - users and colleagues, and we take those consequences seriously when making - decisions. - -4. Be inquisitive. Nobody knows everything! Asking questions early avoids many - problems later, so we encourage questions, although we may direct them to - the appropriate forum. We will try hard to be responsive and helpful. - -5. Be careful in the words that we choose. We are careful and respectful in - our communication and we take responsibility for our own words. Be kind to - others. Do not insult or put down other participants. We do not tolerate - harassment or other exclusionary behavior, such as: - - * Violent threats or language directed against another person. - - * Sexist, racist, or otherwise discriminatory jokes and language. - - * Posting sexually explicit or violent material. - - * Posting (or threatening to post) other people’s personally identifying - information (“doxing”). - - * Sharing private content, such as emails sent privately or non-publicly, - or unlogged forums, such as IRC channel history, without the sender’s consent. - - * Personal insults, especially those using racist or sexist terms. - - * Unwelcome sexual attention. - - * Excessive profanity. Please avoid swearwords; people differ greatly in - their sensitivity to swearing. - - * Repeated harassment of others. In general, if someone asks you to stop, - then stop. - - * Advocating for, or encouraging, any of the above behavior. - -## Diversity statement - -The igraph project welcomes and encourages participation by everyone. We are -committed to being a community that everyone enjoys being part of. Although -we may not always be able to accommodate each individual’s preferences, we try -our best to treat everyone kindly. - -No matter how you identify yourself or how others perceive you: we welcome you. -Though no list can hope to be comprehensive, we explicitly honor diversity in: -age, culture, ethnicity, genotype, gender identity or expression, language, -national origin, neurotype, phenotype, political beliefs, profession, race, -religion, sexual orientation, socioeconomic status, subculture and technical -ability, to the extent that these do not conflict with this code of conduct. - -Though we welcome people fluent in all languages, igraph development is -conducted in English. - -Standards for behavior in the igraph community are detailed in the Code of -Conduct above. Participants in our community should uphold these standards -in all their interactions and help others to do so as well (see next section). - -## Reporting guidelines - -We know that it is painfully common for internet communication to start at or -devolve into obvious and flagrant abuse. We also recognize that sometimes -people may have a bad day, or be unaware of some of the guidelines in this Code -of Conduct. Please keep this in mind when deciding on how to respond to a -breach of this Code. - -For clearly flagrant breaches, report those to the igraph organisation -(see below). For possibly unintentional breaches, you may reply to the person -and point out this Code of Conduct (either in public or in private, whatever is -most appropriate). If you would prefer not to do that, please feel free to -report to the igraph organisation directly, or ask the organisation for -advice, in confidence. - -You can report issues to the igraph organisation, at . -Currently, the following persons will receive your report: - -* Gábor Csárdi - -* Tamás Nepusz - -* Szabolcs Horvát - -* Vincent Traag - -If your report involves any of the above mentioned persons, or if they feel -they have a conflict of interest in handling it, they will recuse themselves -from considering your report. Alternatively, if, for any reason, you feel -uncomfortable making a report to the organisation directly, then you can also -contact any of the above mentioned persons individually. - -## Incident reporting - -We will investigate and respond to all complaints. The igraph organisation will -protect the identity of the reporter, and treat the content of complaints as -confidential (unless the reporter agrees otherwise). - -In case of flagrant breaches, e.g., personal threats or violent, sexist or -racist language, we will immediately disconnect the originator from igraph. In -particular, the organisation will - -1. Immediately disconnect the originator from all igraph communication channels. - -2. Revoke any granted permissions from the originator. - -3. Reply to the reporter that their report has been received and that the - originator has been disconnected. - -4. In every case, the moderator should make a reasonable effort to contact the - originator, and tell them specifically how their language or actions qualify - as a “flagrant breach”. The moderator should also say that, if the - originator believes this is unfair or they want to be reconnected to igraph, - they have the right to ask for a review, as below, by the igraph - organisation. - -5. The igraph organisation will formally review and sign off on all cases - where this mechanism has been applied to make sure it is not being - used to control ordinary heated disagreement. - -In cases not involving flagrant breaches of this code of conduct, the process -for acting on any received code of conduct violation report will be: - -1. acknowledgement that the report has been received - -2. reasonable discussion/feedback - -3. mediation (if feedback didn’t help, and only if both reporter and reportee - agree to this) - -The organisation will respond to any report as soon as possible, and at most -within 5 working days. - -## Endnotes - -This Code of Conduct is inspired by [the SciPy Code of Conduct](https://docs.scipy.org/doc/scipy/reference/dev/conduct/code_of_conduct.html). - -The current organisation of the igraph community is rudimentary. A more -professional organisation may develop in the future, at which point the -procedure of handling incident reports will also be further formalized. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index eb841b8cf5..3d2f470665 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -89,6 +89,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
Kirill Müller

💻 +
Michael

💻 diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index c732e6fb39..92702f357d 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -64,6 +64,7 @@ Quinn Buratynski (@GanzuraTheConsumer) Arnar Bjarni Arnarson (@Tagl) David Seifert (@SoapGentoo) Kirill Müller (@krlmlr) +Michael (@gendelpiekel) This project follows the [all-contributors][1] specification. Contributions of any kind welcome! diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 10df659b63..18a34e08d5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -125,20 +125,20 @@ jobs: - job: windows_static pool: - vmImage: windows-2022 + vmImage: windows-latest steps: - template: .azure/build-win.yml - job: windows_shared pool: - vmImage: windows-2022 + vmImage: windows-latest steps: - template: .azure/build-win.yml parameters: build_shared: true - vsver: '14.29' # use VS 2019 instead of 2017 + vsver: '14.4' # latest VS2022 vcpkg_target_triplet: x64-windows - job: documentation diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1803837fc3..8e1d892cf5 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -20,6 +20,7 @@ set( attributes.xxml basicigraph.xxml bipartite.xxml + bitset.xxml cliques.xxml coloring.xxml community.xxml diff --git a/doc/adjlist.xxml b/doc/adjlist.xxml index d2fb209c55..7e0717dd9a 100644 --- a/doc/adjlist.xxml +++ b/doc/adjlist.xxml @@ -13,6 +13,7 @@ + diff --git a/doc/bitset.xxml b/doc/bitset.xxml new file mode 100644 index 0000000000..5e9bb198fb --- /dev/null +++ b/doc/bitset.xxml @@ -0,0 +1,59 @@ + + +]> + +
+Bitsets + +
+ +
+ +
+ + + + +
+ +
+ + + + + + + +
+ +
Bitset operations + + + + + + + + + + + + + + + +
+ +
Bitset properties + + +
+ +
Resizing operations + + +
+ +
diff --git a/doc/c-docbook.re b/doc/c-docbook.re index 6430a3a1c1..dacf1a4172 100644 --- a/doc/c-docbook.re +++ b/doc/c-docbook.re @@ -68,7 +68,7 @@ REPLACE ----- function object, extract its signature -------------------------- [\s]*(?P[^\n]*?)\n # brief description (?P.*?)\*\/ # tail of the comment \s* -(IGRAPH_EXPORT )? # strip IGRAPH_EXPORT from prototype +(IGRAPH_EXPORT\s+)? # strip IGRAPH_EXPORT from prototype (?P.*?\)) # function head (?=(\s*;)|(\s*\{)) # prototype ends with ; function head with { .*\Z # and the remainder @@ -178,7 +178,9 @@ REPLACE ----- variables ------------------------------------------------------- (?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
 [\s]*(?P[^\n]*?)\n         # brief description
 (?P.*?)\*\/                # tail of the comment
-\s*(?P[^;]*;)                # the definition of the variable
+\s*
+(IGRAPH_EXPORT\s+)?               # strip IGRAPH_EXPORT
+(?P[^;]*;)                   # the definition of the variable
 .*\Z                              # and the remainder
 
 WITH --------------------------------------------------------------------------
@@ -203,7 +205,7 @@ REPLACE ----- \define ---------------------------------------------------------
 (?P.*?)\*\/                # tail of the comment
 \s*                               # whitespace
 (?P\#define\s+[\w0-9,]+\s*   # macro name
-(\([\w0-9, ]+\))?)                # macro args (optional)
+(\([\w0-9,. ]+\))?)               # macro args (optional)
 .*\Z                              # drop the remainder
 
 WITH --------------------------------------------------------------------------
@@ -372,7 +374,7 @@ WITH --------------------------------------------------------------------------
 
 REPLACE IN *.h ----- structure member descriptions ----------------------------
 
-\\member\b\s*                    # \enumval command
+\\member\b\s*                     # \enumval command
 (?P(\w+)|(...))\s+     # name of the parameter
 (?P.*?)                # text of the \enumval command
 (?=(\\member)|()|
@@ -386,14 +388,14 @@ WITH --------------------------------------------------------------------------
 
 REPLACE ----- \typedef function -----------------------------------------------
 
-(?P\A.*?)                   # comment head
+(?P\A.*?)                 # comment head
 \\typedef\s+                      # \typedef command
 (?P(?P
(igraph_)|(IGRAPH_)|())(?P\w+))
 [\s]*(?P[^\n]*?)\n         # brief description
 (?P.*?)                    # comment tail
 \*\/                              # end of comment block
 \s*
-(?Ptypedef\s+[^;]*;)        # the typedef definition
+(?Ptypedef\s+[^;]*;)         # the typedef definition
 .*\Z
 
 WITH --------------------------------------------------------------------------
diff --git a/doc/cliques.xxml b/doc/cliques.xxml
index 76e6c09a26..22e8b74a0e 100644
--- a/doc/cliques.xxml
+++ b/doc/cliques.xxml
@@ -13,6 +13,7 @@ to cliques and independent vertex sets.
 
 
Cliques + @@ -34,6 +35,7 @@ to cliques and independent vertex sets.
Independent vertex sets + diff --git a/doc/community.xxml b/doc/community.xxml index 0299ab06d7..65b86a0266 100644 --- a/doc/community.xxml +++ b/doc/community.xxml @@ -7,6 +7,8 @@ Detecting community structure + + diff --git a/doc/structural.xxml b/doc/structural.xxml index 5e2014b973..afcdf807d8 100644 --- a/doc/structural.xxml +++ b/doc/structural.xxml @@ -99,6 +99,9 @@ + + + @@ -135,8 +138,8 @@
Subset-limited centrality measures - - + +
Centralization @@ -195,6 +198,8 @@
Non-simple graphs: Multiple and loop edges + + @@ -238,9 +243,11 @@
Other operations + + diff --git a/doc/vector.xxml b/doc/vector.xxml index 8764428788..510edbdfd4 100644 --- a/doc/vector.xxml +++ b/doc/vector.xxml @@ -96,6 +96,7 @@ +
Searching for elements @@ -136,6 +137,7 @@
Set operations on sorted vectors +
diff --git a/etc/cmake/bit_operations_support.cmake b/etc/cmake/bit_operations_support.cmake new file mode 100644 index 0000000000..53575197ff --- /dev/null +++ b/etc/cmake/bit_operations_support.cmake @@ -0,0 +1,92 @@ +include(CheckCXXSourceCompiles) +include(CheckTypeSize) + +cmake_push_check_state(RESET) + +# Check whether the compiler supports the __popcnt64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + volatile unsigned long long b; + b = __popcnt64(a); + return 0; + } + " + HAVE__POPCNT64 +) + +# Check whether the compiler supports the __popcnt() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF; + volatile unsigned long long b; + b = __popcnt(a); + return 0; + } + " + HAVE__POPCNT +) + +# Check whether the compiler supports the _BitScanForward64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + unsigned long b; + volatile unsigned long c; + c = _BitScanForward64(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANFORWARD64 +) + +# Check whether the compiler supports the _BitScanForward() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF, b; + volatile unsigned long c; + c = _BitScanForward(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANFORWARD +) + +# Check whether the compiler supports the _BitScanReverse64() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long long a = 0xDEADBEEF; + unsigned long b; + volatile unsigned long c; + c = _BitScanReverse64(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANREVERSE64 +) + +# Check whether the compiler supports the _BitScanReverse() intrinsic +check_cxx_source_compiles(" + #include + + int main(void) { + unsigned long a = 0xDEADBEEF, b; + volatile unsigned long c; + c = _BitScanReverse(&b, a) ? b : 64; + return 0; + } + " + HAVE__BITSCANREVERSE +) + +cmake_pop_check_state() diff --git a/etc/cmake/igraph-config.cmake.in b/etc/cmake/igraph-config.cmake.in index 2669f8e0a3..27ceda2d7a 100644 --- a/etc/cmake/igraph-config.cmake.in +++ b/etc/cmake/igraph-config.cmake.in @@ -1,4 +1,19 @@ +# igraph-config.cmake +# +# igraph CMake package +# +# The following variables are set: +# +# - IGRAPH_VERSION - The igraph version string. +# - IGRAPH_INTEGER_SIZE - The integer size igraph was configured with (32 or 64). +# - IGRAPH_GLPK_SUPPORT - Whether igraph was compiled with GLPK support. +# - IGRAPH_GRAPHML_SUPPORT - Whether igraph was compiled with GraphML support. +# + set(IGRAPH_VERSION "@PACKAGE_VERSION_BASE@") +set(IGRAPH_INTEGER_SIZE @IGRAPH_INTEGER_SIZE@) +set(IGRAPH_GLPK_SUPPORT @IGRAPH_GLPK_SUPPORT@) +set(IGRAPH_GRAPHML_SUPPORT @IGRAPH_GRAPHML_SUPPORT@) @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/igraph-targets.cmake") diff --git a/examples/simple/cattributes3.c b/examples/simple/cattributes3.c index 823234c9d7..ed0f804834 100644 --- a/examples/simple/cattributes3.c +++ b/examples/simple/cattributes3.c @@ -29,7 +29,7 @@ igraph_error_t mf(const igraph_vector_t *input, igraph_real_t *output) { } static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { - igraph_simplify(g, /*multiple=*/ true, /*loops=*/ true, comb); + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); igraph_attribute_combination_destroy(comb); igraph_destroy(g); diff --git a/examples/simple/cattributes4.c b/examples/simple/cattributes4.c index f5f4dc82ba..e6a21f9660 100644 --- a/examples/simple/cattributes4.c +++ b/examples/simple/cattributes4.c @@ -23,7 +23,7 @@ #include static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { - igraph_simplify(g, /*multiple=*/ true, /*loops=*/ true, comb); + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); igraph_attribute_combination_destroy(comb); igraph_destroy(g); diff --git a/examples/simple/igraph_attribute_combination.c b/examples/simple/igraph_attribute_combination.c index e95b704eca..c79e83df1b 100644 --- a/examples/simple/igraph_attribute_combination.c +++ b/examples/simple/igraph_attribute_combination.c @@ -19,7 +19,7 @@ int main(void) { "type", IGRAPH_ATTRIBUTE_COMBINE_FIRST, "", IGRAPH_ATTRIBUTE_COMBINE_IGNORE, IGRAPH_NO_MORE_ATTRIBUTES); - igraph_simplify(&graph, /*multiple=*/ true, /*loops=*/ true, &comb); + igraph_simplify(&graph, /*remove_multiple=*/ true, /*remove_loops=*/ true, &comb); igraph_write_graph_graphml(&graph, stdout, /*prefixattr=*/ true); igraph_destroy(&graph); diff --git a/examples/simple/igraph_biconnected_components.c b/examples/simple/igraph_biconnected_components.c index 3b74f46ea2..cfcabe0019 100644 --- a/examples/simple/igraph_biconnected_components.c +++ b/examples/simple/igraph_biconnected_components.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2006-2012 Gabor Csardi - 334 Harvard street, Cambridge, MA 02139 USA + Copyright (C) 2006-2024 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,64 +13,56 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public 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 -#include -void sort_and_print_vector(igraph_vector_int_t *v) { - igraph_integer_t i, n = igraph_vector_int_size(v); - igraph_vector_int_sort(v); - for (i = 0; i < n; i++) { - printf(" %" IGRAPH_PRId, VECTOR(*v)[i]); +/* Prints a vector of edge IDs as u--v vertex pairs. */ +void print_edge_vector(const igraph_t *graph, const igraph_vector_int_t *edges) { + const igraph_integer_t n = igraph_vector_int_size(edges); + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t edge = VECTOR(*edges)[i]; + printf("%" IGRAPH_PRId "--%" IGRAPH_PRId " ", IGRAPH_FROM(graph, edge), IGRAPH_TO(graph, edge)); } printf("\n"); } int main(void) { - - igraph_t g; - igraph_vector_int_list_t result; + igraph_t graph; + igraph_vector_int_list_t component_vertices, component_edges; igraph_integer_t no; - igraph_integer_t i; - igraph_set_warning_handler(igraph_warning_handler_ignore); + /* Create an example graph. */ + igraph_small(&graph, 7, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,0, + 2,4, 4,5, 5,2, + 0,6, + 0,7, + -1); - igraph_vector_int_list_init(&result, 0); - igraph_small(&g, 7, 0, 0, 1, 1, 2, 2, 3, 3, 0, 2, 4, 4, 5, 2, 5, -1); + /* The data structures that the result will be written to must be initialized first. */ + igraph_vector_int_list_init(&component_vertices, 0); + igraph_vector_int_list_init(&component_edges, 0); - igraph_biconnected_components(&g, &no, 0, 0, &result, 0); - if (no != 2 || no != igraph_vector_int_list_size(&result)) { - return 1; - } - for (i = 0; i < no; i++) { - sort_and_print_vector(igraph_vector_int_list_get_ptr(&result, i)); - } - igraph_vector_int_list_clear(&result); + igraph_biconnected_components(&graph, &no, NULL, &component_edges, &component_vertices, NULL); - igraph_biconnected_components(&g, &no, 0, &result, 0, 0); - if (no != 2 || no != igraph_vector_int_list_size(&result)) { - return 2; + printf("Number of components: %" IGRAPH_PRId "\n", no); + for (igraph_integer_t i=0; i < no; i++) { + printf("\n"); + printf("Component %" IGRAPH_PRId ":\n", i); + printf("Vertices: "); + igraph_vector_int_print(igraph_vector_int_list_get_ptr(&component_vertices, i)); + printf("Edges: "); + print_edge_vector(&graph, igraph_vector_int_list_get_ptr(&component_edges, i)); } - for (i = 0; i < no; i++) { - sort_and_print_vector(igraph_vector_int_list_get_ptr(&result, i)); - } - igraph_vector_int_list_clear(&result); - igraph_biconnected_components(&g, &no, &result, 0, 0, 0); - if (no != 2 || no != igraph_vector_int_list_size(&result)) { - return 3; - } - for (i = 0; i < no; i++) { - sort_and_print_vector(igraph_vector_int_list_get_ptr(&result, i)); - } + /* Destroy data structures after we no longer need them. */ + + igraph_vector_int_list_destroy(&component_edges); + igraph_vector_int_list_destroy(&component_vertices); - igraph_vector_int_list_destroy(&result); - igraph_destroy(&g); + igraph_destroy(&graph); return 0; } diff --git a/examples/simple/igraph_biconnected_components.out b/examples/simple/igraph_biconnected_components.out index 1257c65204..7ebc7bacd8 100644 --- a/examples/simple/igraph_biconnected_components.out +++ b/examples/simple/igraph_biconnected_components.out @@ -1,6 +1,17 @@ - 2 4 5 - 0 1 2 3 - 4 5 6 - 0 1 2 3 - 4 5 - 0 1 2 +Number of components: 4 + +Component 0: +Vertices: 5 4 2 +Edges: 5--2 5--4 4--2 + +Component 1: +Vertices: 3 2 1 0 +Edges: 3--0 3--2 2--1 1--0 + +Component 2: +Vertices: 6 0 +Edges: 6--0 + +Component 3: +Vertices: 7 0 +Edges: 7--0 diff --git a/examples/simple/igraph_contract_vertices.c b/examples/simple/igraph_contract_vertices.c new file mode 100644 index 0000000000..99794e2548 --- /dev/null +++ b/examples/simple/igraph_contract_vertices.c @@ -0,0 +1,81 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +/* Create the condensation of a directed graph. + * See https://en.wikipedia.org/wiki/Strongly_connected_component#Definitions + * This example demonstrates how to write a basic igraph function, complete + * with error handling. */ +igraph_error_t condensation(const igraph_t *graph, igraph_t *cond) { + igraph_vector_int_t membership; + + /* Data structures such as vector must be initialized in igraph before use. */ + IGRAPH_CHECK(igraph_vector_int_init(&membership, 0)); + + /* Adding the initialized vector to the "finally" stack ensures that it will + * be automatically destroyed if an error occurs. */ + IGRAPH_FINALLY(igraph_vector_int_destroy, &membership); + + /* Functions that return an error code can be wrapped in IGRAPH_CHECK to pass that error + * up to the caller. */ + IGRAPH_CHECK(igraph_connected_components(graph, &membership, /* csize */ NULL, /* no */ NULL, IGRAPH_STRONG)); + + /* To compute the condensation, we simply contract strongly connected components. + * Since igraph_contract_vertices() modifies graphs in-place, we make a copy first. */ + IGRAPH_CHECK(igraph_copy(cond, graph)); + + /* Since we are not done creating the condensation yet, we add 'cond' to the + * "finally" stack, so that it will be destroyed if an error occurs. */ + IGRAPH_FINALLY(igraph_destroy, cond); + + /* Contract strongly connected components. */ + IGRAPH_CHECK(igraph_contract_vertices(cond, &membership, NULL)); + + /* igraph_contract_vertices() preserves all edges, some of which become + * parallel edges or self-loops after the contraction. We simplify these. */ + IGRAPH_CHECK(igraph_simplify(cond, /* remove_multiple */ true, /* remove_loops */ true, NULL)); + + /* Data structures that are no longer needed must be explicitly destroyed. + * If they were added to the "finally" stack, they must be removed explicitly, + * in the opposite order to how they were added. IGRAPH_FINALLY_CLEAN removes + * the indicated number of entries from the "finally" stack. We remove + * 'membership' because it was destroyed, and 'cond' because the responsibility + * to destroy it is now with the caller. */ + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; /* return with no error */ +} + +int main(void) { + igraph_t graph, cond; + + /* Create a random directed graph with mean degree 2 and compute its condensation. */ + igraph_erdos_renyi_game_gnm(&graph, 100, 200, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + condensation(&graph, &cond); + + printf("Number of vertices in the condensation: %" IGRAPH_PRId "\n", igraph_vcount(&cond)); + igraph_write_graph_edgelist(&cond, stdout); + + /* Destroy data structures that are no longer needed. */ + igraph_destroy(&graph); + igraph_destroy(&cond); + + return 0; +} diff --git a/examples/simple/igraph_is_loop.c b/examples/simple/igraph_is_loop.c index c761daf60a..96f33504bb 100644 --- a/examples/simple/igraph_is_loop.c +++ b/examples/simple/igraph_is_loop.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2007-2012 Gabor Csardi - 334 Harvard st, Cambridge MA, 02139 USA + Copyright (C) 2007-2024 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,41 +13,43 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public 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 -void print_vector(igraph_vector_bool_t *v, FILE *f) { - igraph_integer_t i; - for (i = 0; i < igraph_vector_bool_size(v); i++) { - fprintf(f, " %i", VECTOR(*v)[i] ? 1 : 0); - } - fprintf(f, "\n"); +void analyze_loops(const igraph_t *graph) { + igraph_vector_bool_t is_loop; + igraph_bool_t has_loop; + igraph_integer_t loop_count; + + igraph_has_loop(graph, &has_loop); + printf("Has loops? %s\n", has_loop ? "Yes" : "No"); + + igraph_count_loops(graph, &loop_count); + printf("How many? %" IGRAPH_PRId "\n", loop_count); + + igraph_vector_bool_init(&is_loop, 0); + igraph_is_loop(graph, &is_loop, igraph_ess_all(IGRAPH_EDGEORDER_ID)); + printf("Loop positions: "); igraph_vector_bool_print(&is_loop); + igraph_vector_bool_destroy(&is_loop); + + printf("\n"); } int main(void) { igraph_t graph; - igraph_vector_bool_t v; - - igraph_vector_bool_init(&v, 0); - igraph_small(&graph, 0, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 1, 0, 1, 1, 0, 3, 4, 11, 10, -1); - igraph_is_loop(&graph, &v, igraph_ess_all(IGRAPH_EDGEORDER_ID)); - print_vector(&v, stdout); + igraph_small(&graph, 0, IGRAPH_DIRECTED, + 0,1, 1,2, 2,1, 0,1, 1,0, 3,4, 11,10, -1); + analyze_loops(&graph); igraph_destroy(&graph); igraph_small(&graph, 0, IGRAPH_UNDIRECTED, - 0, 0, 1, 1, 2, 2, 2, 3, 2, 4, 2, 5, 2, 6, 2, 2, 0, 0, -1); - igraph_is_loop(&graph, &v, igraph_ess_all(IGRAPH_EDGEORDER_ID)); - print_vector(&v, stdout); + 0,0, 1,1, 2,2, 2,3, 2,4, 2,5, 2,6, 2,2, 0,0, -1); + analyze_loops(&graph); igraph_destroy(&graph); - igraph_vector_bool_destroy(&v); - return 0; } diff --git a/examples/simple/igraph_is_loop.out b/examples/simple/igraph_is_loop.out index 4fadd3c0ea..cb383449de 100644 --- a/examples/simple/igraph_is_loop.out +++ b/examples/simple/igraph_is_loop.out @@ -1,2 +1,8 @@ - 0 0 0 0 0 0 0 - 1 1 1 0 0 0 0 1 1 +Has loops? No +How many? 0 +Loop positions: 0 0 0 0 0 0 0 + +Has loops? Yes +How many? 5 +Loop positions: 1 1 1 0 0 0 0 1 1 + diff --git a/examples/simple/igraph_minimum_spanning_tree.c b/examples/simple/igraph_minimum_spanning_tree.c index a0509ce91b..625191e45e 100644 --- a/examples/simple/igraph_minimum_spanning_tree.c +++ b/examples/simple/igraph_minimum_spanning_tree.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2006-2012 Gabor Csardi - 334 Harvard st, Cambridge MA, 02139 USA + Copyright (C) 2006-2024 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,20 +13,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, 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 int main(void) { - - igraph_t graph, tree; + igraph_t graph; igraph_vector_t eb; igraph_vector_int_t edges; + /* Create the vector where the tree edges will be stored. */ + igraph_vector_int_init(&edges, 0); + /* Create the Frucht graph */ igraph_famous(&graph, "Frucht"); @@ -36,17 +33,17 @@ int main(void) { igraph_vector_init(&eb, igraph_ecount(&graph)); igraph_edge_betweenness(&graph, &eb, IGRAPH_UNDIRECTED, /*weights=*/ NULL); - /* Compute and output a minimum weight spanning tree using edge betweenness - * values as weights. */ - igraph_minimum_spanning_tree_prim(&graph, &tree, &eb); - printf("Minimum spanning tree:\n"); - igraph_write_graph_edgelist(&tree, stdout); + /* Use Prim's algorithm to compute the edges that belong to the minimum weight + * spanning tree, using edge betweenness values as edge weights. */ + igraph_minimum_spanning_tree(&graph, &edges, &eb); + printf("Minimum spanning tree edges:\n"); + igraph_vector_int_print(&edges); /* A maximum spanning tree can be computed by first negating the weights. */ igraph_vector_scale(&eb, -1); - /* Compute and output the edges that belong to the maximum weight spanning tree. */ - igraph_vector_int_init(&edges, 0); + /* Compute and output the edges that belong to the maximum weight spanning tree, + * letting igraph automatically select the most suitable algorithm. */ igraph_minimum_spanning_tree(&graph, &edges, &eb); printf("\nMaximum spanning tree edges:\n"); igraph_vector_int_print(&edges); @@ -59,10 +56,9 @@ int main(void) { printf("\nTotal maximum spanning tree weight: %g\n", total_tree_weight); /* Clean up */ - igraph_vector_int_destroy(&edges); - igraph_destroy(&tree); igraph_destroy(&graph); igraph_vector_destroy(&eb); + igraph_vector_int_destroy(&edges); return 0; } diff --git a/examples/simple/igraph_minimum_spanning_tree.out b/examples/simple/igraph_minimum_spanning_tree.out index 488d35fac9..0646e99926 100644 --- a/examples/simple/igraph_minimum_spanning_tree.out +++ b/examples/simple/igraph_minimum_spanning_tree.out @@ -1,15 +1,5 @@ -Minimum spanning tree: -0 1 -0 11 -1 3 -2 10 -3 4 -3 6 -5 10 -6 7 -7 8 -8 9 -10 11 +Minimum spanning tree edges: +2 17 6 12 0 3 8 7 13 14 16 Maximum spanning tree edges: 0 1 4 13 15 11 2 10 9 7 17 diff --git a/examples/simple/igraph_simplify.c b/examples/simple/igraph_simplify.c index 0c341552fb..67cb0a4301 100644 --- a/examples/simple/igraph_simplify.c +++ b/examples/simple/igraph_simplify.c @@ -56,22 +56,22 @@ int main(void) { /* Loop & multiple edges */ igraph_small(&g, 0, IGRAPH_DIRECTED, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, -1); - igraph_simplify(&g, /* multiple */ true, /* loop */ false, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL); igraph_write_graph_edgelist(&g, stdout); igraph_destroy(&g); igraph_small(&g, 0, IGRAPH_UNDIRECTED, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, -1); - igraph_simplify(&g, /* multiple */ true, /* loop */ false, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple */ true, /* remove_loops */ false, /*edge_comb=*/ NULL); igraph_write_graph_edgelist(&g, stdout); igraph_destroy(&g); igraph_small(&g, 0, IGRAPH_DIRECTED, 2, 2, 2, 2, 2, 2, 3, 2, -1); - igraph_simplify(&g, /* multiple */ false, /* loop */ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL); igraph_write_graph_edgelist(&g, stdout); igraph_destroy(&g); igraph_small(&g, 0, IGRAPH_UNDIRECTED, 3, 3, 3, 3, 3, 4, -1); - igraph_simplify(&g, /* multiple */ false, /* loop */ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple */ false, /* remove_loops */ true, /*edge_comb=*/ NULL); igraph_write_graph_edgelist(&g, stdout); igraph_destroy(&g); diff --git a/examples/simple/igraph_vs_seq.c b/examples/simple/igraph_vs_range.c similarity index 100% rename from examples/simple/igraph_vs_seq.c rename to examples/simple/igraph_vs_range.c diff --git a/examples/simple/igraph_vs_seq.out b/examples/simple/igraph_vs_range.out similarity index 100% rename from examples/simple/igraph_vs_seq.out rename to examples/simple/igraph_vs_range.out diff --git a/fuzzing/build.sh b/fuzzing/build.sh index b5234a83c0..2eb06882bc 100755 --- a/fuzzing/build.sh +++ b/fuzzing/build.sh @@ -8,11 +8,11 @@ mkdir $DEPS_PATH # It may be necessary to leave CMAKE_BUILD_TYPE empty and specify LIBXML2_WITH_MODULES=OFF # in order for fuzz introspector builds to succeed (details unverified). cd $SRC -wget https://download.gnome.org/sources/libxml2/2.12/libxml2-2.12.6.tar.xz -tar xf libxml2-2.12.6.tar.xz -cd libxml2-2.12.6 +wget https://download.gnome.org/sources/libxml2/2.13/libxml2-2.13.1.tar.xz +tar xf libxml2-2.13.1.tar.xz +cd libxml2-2.13.1 mkdir build && cd build -cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_PATH -DBUILD_SHARED_LIBS=OFF -DLIBXML2_WITH_ICU=OFF -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_TESTS=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_PROGRAMS=OFF -DLIBXML2_WITH_MODULES=OFF +CFLAGS="$CFLAGS -O2" cmake .. -DCMAKE_INSTALL_PREFIX=$DEPS_PATH -DBUILD_SHARED_LIBS=OFF -DLIBXML2_WITH_ICU=OFF -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_TESTS=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_PROGRAMS=OFF -DLIBXML2_WITH_MODULES=OFF make install -j$(nproc) # Build igraph diff --git a/fuzzing/centrality.cpp b/fuzzing/centrality.cpp index 9d4b658738..91d1e7b006 100644 --- a/fuzzing/centrality.cpp +++ b/fuzzing/centrality.cpp @@ -72,7 +72,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_spanner(&graph, &iv, 2.34, NULL); igraph_to_undirected(&graph, IGRAPH_TO_UNDIRECTED_COLLAPSE, NULL); - igraph_simplify(&graph, /* multiple */ true, /* loops */ false, NULL); + igraph_simplify(&graph, /* remove_multiple */ true, /* remove_loops */ false, NULL); igraph_trussness(&graph, &iv); igraph_vector_int_destroy(&iv); diff --git a/fuzzing/fuzz_utilities.h b/fuzzing/fuzz_utilities.h new file mode 100644 index 0000000000..2b3933933d --- /dev/null +++ b/fuzzing/fuzz_utilities.h @@ -0,0 +1,14 @@ +#ifndef FUZZ_UTILITIES_H +#define FUZZ_UTILITIES_H + +#include + +#define CHECK_ERROR(funcall, expected_err) \ + do { \ + igraph_error_handler_t *handler; \ + handler = igraph_set_error_handler(igraph_error_handler_ignore); \ + IGRAPH_ASSERT(funcall == expected_err); \ + igraph_set_error_handler(handler); \ + } while (0) + +#endif // FUZZ_UTILITIES_H diff --git a/fuzzing/linear_algos_directed.cpp b/fuzzing/linear_algos_directed.cpp index ef9385edd3..0108a2217f 100644 --- a/fuzzing/linear_algos_directed.cpp +++ b/fuzzing/linear_algos_directed.cpp @@ -21,6 +21,8 @@ #include #include +#include "fuzz_utilities.h" + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_t graph; igraph_vector_int_t edges; @@ -44,8 +46,9 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_vector_bool_t bv; igraph_matrix_t m; igraph_integer_t i, i2; - igraph_bool_t b, b2; + igraph_bool_t b, b2, loop, multi, graphical; igraph_real_t r; + igraph_t g; igraph_vector_int_list_init(&ivl1, 0); igraph_vector_int_list_init(&ivl2, 0); @@ -65,6 +68,75 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_is_multiple(&graph, &bv, igraph_ess_all(IGRAPH_EDGEORDER_TO)); igraph_is_mutual(&graph, &bv, igraph_ess_all(IGRAPH_EDGEORDER_TO), false); igraph_maxdegree(&graph, &i, igraph_vss_all(), IGRAPH_IN, true); + igraph_mean_degree(&graph, &r, IGRAPH_NO_LOOPS); + igraph_reciprocity(&graph, &r, true, IGRAPH_RECIPROCITY_DEFAULT); + + /* Graphicality and graph realization based on the degrees of 'graph'. */ + igraph_has_loop(&graph, &loop); + igraph_has_multiple(&graph, &multi); + igraph_degree(&graph, &iv1, igraph_vss_all(), IGRAPH_OUT, true); + igraph_degree(&graph, &iv2, igraph_vss_all(), IGRAPH_IN, true); + igraph_is_graphical(&iv1, &iv2, IGRAPH_SIMPLE_SW, &graphical); + if (!loop && !multi) { + IGRAPH_ASSERT(graphical); + } + if (graphical) { + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); + } else { + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX), + IGRAPH_EINVAL); + } + igraph_is_graphical(&iv1, &iv2, IGRAPH_LOOPS_SW, &graphical); + if (!multi) { + IGRAPH_ASSERT(graphical); + } + if (graphical) { + /* Directed realization with loops but no multi-edges is not yet implemented. */ + /* Realize as bipartite (equivalent). */ + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); + } else { + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX), + IGRAPH_EINVAL); + } + igraph_is_graphical(&iv1, &iv2, IGRAPH_MULTI_SW, &graphical); + if (!loop) { + IGRAPH_ASSERT(graphical); + /* Directed realization with multi-edges but no loops is not yet implemented. */ + } + igraph_is_graphical(&iv1, &iv2, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, &graphical); + IGRAPH_ASSERT(graphical); + /* Directed realization with loops and multi-edges is not yet implemented. */ + /* Realize as bipartite (equivalent). */ + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_bipartite_degree_sequence(&g, &iv1, &iv2, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); // These algorithms require a starting vertex, // so we require the graph to have at least one vertex. @@ -83,7 +155,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_degree_1(&graph, &i, 0, IGRAPH_OUT, true); igraph_degree_1(&graph, &i, 0, IGRAPH_OUT, false); - igraph_t g; igraph_vector_int_resize(&iv1, 1); VECTOR(iv1)[0] = 0; igraph_unfold_tree(&graph, &g, IGRAPH_IN, &iv1, &iv2); diff --git a/fuzzing/linear_algos_undirected.cpp b/fuzzing/linear_algos_undirected.cpp index 32951a5afe..450793ae60 100644 --- a/fuzzing/linear_algos_undirected.cpp +++ b/fuzzing/linear_algos_undirected.cpp @@ -21,6 +21,8 @@ #include #include +#include "fuzz_utilities.h" + extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_t graph; igraph_vector_int_t edges; @@ -44,7 +46,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_vector_bool_t bv; igraph_matrix_t m; igraph_integer_t i, i2; - igraph_bool_t b, b2; + igraph_bool_t b, b2, loop, multi, graphical; igraph_real_t r; igraph_t g; @@ -69,6 +71,68 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_is_multiple(&graph, &bv, igraph_ess_all(IGRAPH_EDGEORDER_ID)); igraph_maxdegree(&graph, &i, igraph_vss_all(), IGRAPH_ALL, true); + /* Graphicality and graph realization based on the degrees of 'graph'. */ + igraph_has_loop(&graph, &loop); + igraph_has_multiple(&graph, &multi); + igraph_degree(&graph, &iv1, igraph_vss_all(), IGRAPH_ALL, true); + igraph_is_graphical(&iv1, NULL, IGRAPH_SIMPLE_SW, &graphical); + if (!loop && !multi) { + IGRAPH_ASSERT(graphical); + } + if (graphical) { + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); + } else { + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX), + IGRAPH_EINVAL); + } + igraph_is_graphical(&iv1, NULL, IGRAPH_LOOPS_SW, &graphical); + if (!multi) { + IGRAPH_ASSERT(graphical); + /* Undirected realization is not yet implemented. */ + } + igraph_is_graphical(&iv1, NULL, IGRAPH_MULTI_SW, &graphical); + if (!loop) { + IGRAPH_ASSERT(graphical); + } + if (graphical) { + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); + } else { + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST), + IGRAPH_EINVAL); + CHECK_ERROR( + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_INDEX), + IGRAPH_EINVAL); + } + igraph_is_graphical(&iv1, NULL, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, &graphical); + IGRAPH_ASSERT(graphical); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_SMALLEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_LARGEST); + igraph_destroy(&g); + igraph_realize_degree_sequence(&g, &iv1, NULL, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + igraph_destroy(&g); + // These algorithms require a starting vertex, // so we require the graph to have at least one vertex. if (igraph_vcount(&graph) >=1) { @@ -103,7 +167,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_vertex_coloring_greedy(&graph, &iv1, IGRAPH_COLORING_GREEDY_DSATUR); igraph_connected_components(&graph, &iv1, &iv2, &i, IGRAPH_WEAK); - igraph_minimum_spanning_tree_unweighted(&graph, &g); + igraph_minimum_spanning_tree(&graph, &iv1, NULL); + igraph_subgraph_from_edges(&graph, &g, igraph_ess_vector(&iv1), false); if (i == 1 && igraph_vcount(&g) >= 2) { // 'g' is a tree (not a forest) when 'graph' had exactly one // connected component. diff --git a/fuzzing/misc_algos.cpp b/fuzzing/misc_algos.cpp index 169f3da747..af8445005c 100644 --- a/fuzzing/misc_algos.cpp +++ b/fuzzing/misc_algos.cpp @@ -58,6 +58,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { igraph_list_triangles(&graph, &iv1); + igraph_ecc(&graph, &v1, igraph_ess_all(IGRAPH_EDGEORDER_ID), 3, false, true); + + igraph_count_reachable(&graph, &iv1, IGRAPH_OUT); + if (igraph_vcount(&graph) >= 2) { igraph_get_all_simple_paths(&graph,&iv1, 0, igraph_vss_1(1), 5, IGRAPH_ALL); } diff --git a/fuzzing/weighted_community.cpp b/fuzzing/weighted_community.cpp index 63efe2b8dd..0ac7c27f6c 100644 --- a/fuzzing/weighted_community.cpp +++ b/fuzzing/weighted_community.cpp @@ -83,7 +83,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { "strength", IGRAPH_ATTRIBUTE_COMBINE_MAX, IGRAPH_NO_MORE_ATTRIBUTES); - igraph_community_label_propagation(&graph, &membership, IGRAPH_OUT, &weights, NULL, NULL); + // Ignore edge directions in label propagation for now, as on some weighted graphs the + // algorithm seems to never complete. See https://github.com/igraph/igraph/issues/2561 + igraph_community_label_propagation(&graph, &membership, IGRAPH_ALL, &weights, NULL, NULL); + igraph_community_walktrap(&graph, &weights, 3, &merges, &mv, &membership); igraph_community_edge_betweenness(&graph, &iv, &v, &merges, &iv2, &mv, &membership2, IGRAPH_DIRECTED, &weights); diff --git a/fuzzing/write_all_gml.cpp b/fuzzing/write_all_gml.cpp index f7030034ee..21c003df22 100644 --- a/fuzzing/write_all_gml.cpp +++ b/fuzzing/write_all_gml.cpp @@ -70,20 +70,20 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { CHECK_ERR(igraph_write_graph_gml(&g, file, IGRAPH_WRITE_GML_DEFAULT_SW, NULL, "no one")); rewind(file); CHECK_ERR(igraph_read_graph_gml(&g2, file)); - fclose(file); igraph_destroy(&g2); + fclose(file); - /* Reading Pajek files back is disabled because of - * https://github.com/igraph/igraph/issues/2560 */ + // Reading Pajek files back is disabled because of + // https://github.com/igraph/igraph/issues/2560 file = tmpfile(); IGRAPH_ASSERT(file != NULL); CHECK_ERR(igraph_write_graph_pajek(&g, file)); /* rewind(file); CHECK_ERR(igraph_read_graph_pajek(&g2, file)); + igraph_destroy(&g2); */ fclose(file); - igraph_destroy(&g2); file = tmpfile(); IGRAPH_ASSERT(file != NULL); diff --git a/fuzzing/write_all_graphml.cpp b/fuzzing/write_all_graphml.cpp index ad2cb6ed71..195b8895fc 100644 --- a/fuzzing/write_all_graphml.cpp +++ b/fuzzing/write_all_graphml.cpp @@ -70,20 +70,20 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { CHECK_ERR(igraph_write_graph_gml(&g, file, IGRAPH_WRITE_GML_DEFAULT_SW, NULL, "no one")); rewind(file); CHECK_ERR(igraph_read_graph_gml(&g2, file)); - fclose(file); igraph_destroy(&g2); + fclose(file); - /* Reading Pajek files back is disabled because of - * https://github.com/igraph/igraph/issues/2560 */ + // Reading Pajek files back is disabled because of + // https://github.com/igraph/igraph/issues/2560 file = tmpfile(); IGRAPH_ASSERT(file != NULL); CHECK_ERR(igraph_write_graph_pajek(&g, file)); /* rewind(file); CHECK_ERR(igraph_read_graph_pajek(&g2, file)); + igraph_destroy(&g2); */ fclose(file); - igraph_destroy(&g2); file = tmpfile(); IGRAPH_ASSERT(file != NULL); diff --git a/include/igraph.h b/include/igraph.h index d05b7f6961..8426ff755f 100644 --- a/include/igraph.h +++ b/include/igraph.h @@ -36,6 +36,7 @@ #include "igraph_vector.h" #include "igraph_matrix.h" #include "igraph_array.h" +#include "igraph_bitset.h" #include "igraph_dqueue.h" #include "igraph_stack.h" #include "igraph_heap.h" @@ -95,5 +96,6 @@ #include "igraph_eulerian.h" #include "igraph_graphicality.h" #include "igraph_cycles.h" +#include "igraph_reachability.h" #endif diff --git a/include/igraph_adjlist.h b/include/igraph_adjlist.h index 11962620e5..49a584cd11 100644 --- a/include/igraph_adjlist.h +++ b/include/igraph_adjlist.h @@ -46,7 +46,7 @@ IGRAPH_EXPORT igraph_error_t igraph_adjlist_init(const igraph_t *graph, igraph_a 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 igraph_integer_t igraph_adjlist_size(const igraph_adjlist_t *al); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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_adjlist_t *al, igraph_neimode_t mode, @@ -59,7 +59,7 @@ 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 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_FUNCATTR_PURE 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); /** @@ -84,7 +84,7 @@ IGRAPH_EXPORT igraph_error_t igraph_inclist_init(const igraph_t *graph, 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 igraph_integer_t igraph_inclist_size(const igraph_inclist_t *al); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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); @@ -121,7 +121,7 @@ IGRAPH_EXPORT igraph_error_t igraph_lazy_adjlist_init(const igraph_t *graph, igraph_multiple_t multiple); 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); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_lazy_adjlist_size(const igraph_lazy_adjlist_t *al); /** * \define igraph_lazy_adjlist_has @@ -177,7 +177,7 @@ IGRAPH_EXPORT igraph_error_t igraph_lazy_inclist_init(const igraph_t *graph, igraph_loops_t loops); 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); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_lazy_inclist_size(const igraph_lazy_inclist_t *il); /** * \define igraph_lazy_inclist_has diff --git a/include/igraph_array_pmt.h b/include/igraph_array_pmt.h index 035cbf6568..9add5e8476 100644 --- a/include/igraph_array_pmt.h +++ b/include/igraph_array_pmt.h @@ -41,13 +41,13 @@ IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_array3, init)( igraph_integer_t n3); IGRAPH_EXPORT void FUNCTION(igraph_array3, destroy)(TYPE(igraph_array3) *a); IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_array3, size)(const TYPE(igraph_array3) *a); -IGRAPH_EXPORT igraph_integer_t FUNCTION(igraph_array3, n)( +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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 IGRAPH_FUNCATTR_PURE 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, diff --git a/include/igraph_bitset.h b/include/igraph_bitset.h new file mode 100644 index 0000000000..fcf0937ad0 --- /dev/null +++ b/include/igraph_bitset.h @@ -0,0 +1,267 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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_BITSET_H +#define IGRAPH_BITSET_H + +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +/* Required for MSVC intrinsics such as __popcnt and __popcnt64 */ +#ifdef _MSC_VER + #include "intrin.h" +#endif + +__BEGIN_DECLS + +/** + * \ingroup bitset + * \section igraph_bitset_accessing_elements Accessing elements + * + * The simplest way to access an element of a bitset is + * to use the \ref IGRAPH_BIT_TEST(), \ref IGRAPH_BIT_SET() and \ref IGRAPH_BIT_CLEAR() macros. + * + * + * There are a few other macros which allow manual manipulation of bitsets. + * Those are \ref VECTOR(), \ref IGRAPH_BIT_SLOT(), \ref IGRAPH_BIT_MASK() and + * \ref IGRAPH_BIT_NSLOTS(). + */ + +/** + * \ingroup bitset + * \define IGRAPH_BIT_MASK + * \brief Computes mask used to access a specific bit of an integer. + * + * \experimental + * + * Used in combination with \ref IGRAPH_BIT_SLOT() to access an element of a bitset. + * + * Usage: + * \verbatim IGRAPH_BIT_MASK(10) \endverbatim + * to obtain an integer where only the 11th least significant bit is set. + * + * Note that passing negative values here results in undefined behaviour. + * + * \param b The only bit index that should have its bit set. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_MASK(i) ((igraph_uint_t)(1) << ((i) % IGRAPH_INTEGER_SIZE)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_SLOT + * \brief Computes index used to access a specific slot of a bitset. + * + * \experimental + * + * Used in combination with \ref IGRAPH_BIT_MASK to access an element of a bitset. + * + * Usage: + * \verbatim IGRAPH_BIT_SLOT(70) \endverbatim + * will return 1 if using 64-bit words or 2 if using 32-bit words. + * + * \param i The bit index whose slot should be determined. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_SLOT(i) ((i) / IGRAPH_INTEGER_SIZE) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_SET + * \brief Sets a specific bit in a bitset to 1 without altering other bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_SET(bitset, 3) \endverbatim + * will set the fourth least significant bit in the bitset to 1. + * + * \param bitset The bitset + * \param i The bit index that should have its bit set to 1 after the operation. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_SET(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] |= IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_CLEAR + * \brief Sets a specific bit in a bitset to 0 without altering other bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_CLEAR(bitset, 4) \endverbatim + * will set the fifth least significant bit in the bitset to 0. + * + * \param bitset The bitset + * \param i The bit index that should have its bit set to 0 after the operation. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_CLEAR(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] &= ~IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_TEST + * \brief Tests whether a bit is set in a bitset. + * + * \experimental + * + * Returns 0 if the bit at the specified bit index is not set, + * otherwise returns a non-zero value. + * + * Usage: + * \verbatim IGRAPH_BIT_TEST(bitset, 7) \endverbatim + * will test the eighth least significant bit in the bitset. + * + * \param bitset The bitset + * \param i The bit index that should have its bit tested. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_TEST(bitset, i) (VECTOR((bitset))[IGRAPH_BIT_SLOT(i)] & IGRAPH_BIT_MASK(i)) + +/** + * \ingroup bitset + * \define IGRAPH_BIT_NSLOTS + * \brief Computes the number of slots required to store a specified number of bits. + * + * \experimental + * + * Usage: + * \verbatim IGRAPH_BIT_NSLOTS(70) \endverbatim + * will return 2 if using 64-bit words and 3 if using 32-bit words. + * \verbatim IGRAPH_BIT_NSLOTS(128) \endverbatim + * will return 2 if using 64-bit words and 4 if using 32-bit words. + * + * \param nbits The specified number of bits. + * + * Time complexity: O(1). + */ +#define IGRAPH_BIT_NSLOTS(nbits) ((nbits + IGRAPH_INTEGER_SIZE - (igraph_integer_t)(1)) / IGRAPH_INTEGER_SIZE) + + +#if defined(__GNUC__) + /* GCC and Clang support these six builtins from very early versions. */ + + #define IGRAPH_I_POPCOUNT32(x) __builtin_popcount(x) + #define IGRAPH_I_POPCOUNT64(x) __builtin_popcountll(x) + + /* The result of the following four builtins is undefined for zero input, + * therefore we handle this specially. + * See https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html */ + #define IGRAPH_I_CTZ32(x) (x ? __builtin_ctz(x) : 32) + #define IGRAPH_I_CTZ64(x) (x ? __builtin_ctzll(x) : 64) + #define IGRAPH_I_CLZ32(x) (x ? __builtin_clz(x) : 32) + #define IGRAPH_I_CLZ64(x) (x ? __builtin_clzll(x) : 64) + +#else + /* Non-GNU compilers, i.e. MSVC and others. Attempt to use MSVC intrinsics + * when available, otherwise use fallback implementation. */ + + #if IGRAPH_INTEGER_SIZE == 32 + #ifdef HAVE__POPCNT + #define IGRAPH_I_POPCOUNT32(x) __popcnt(x) + #else + igraph_integer_t igraph_i_popcnt(igraph_uint_t x); + #define IGRAPH_I_POPCOUNT32(x) igraph_i_popcnt(x) + #endif + #elif IGRAPH_INTEGER_SIZE == 64 + #ifdef HAVE__POPCNT64 + #define IGRAPH_I_POPCOUNT64(x) __popcnt64(x) + #else + igraph_integer_t igraph_i_popcnt(igraph_uint_t x); + #define IGRAPH_I_POPCOUNT64(x) igraph_i_popcnt(x) + #endif + #else + #error "Unexpected IGRAPH_INTEGER_SIZE value." + #endif + + igraph_integer_t igraph_i_ctz32(igraph_uint_t x); + igraph_integer_t igraph_i_ctz64(igraph_uint_t x); + igraph_integer_t igraph_i_clz32(igraph_uint_t x); + igraph_integer_t igraph_i_clz64(igraph_uint_t x); + #define IGRAPH_I_CTZ32(x) igraph_i_ctz32(x) + #define IGRAPH_I_CTZ64(x) igraph_i_ctz64(x) + #define IGRAPH_I_CLZ32(x) igraph_i_clz32(x) + #define IGRAPH_I_CLZ64(x) igraph_i_clz64(x) + +#endif + + +#if IGRAPH_INTEGER_SIZE == 32 + #define IGRAPH_POPCOUNT IGRAPH_I_POPCOUNT32 + #define IGRAPH_CLZ IGRAPH_I_CLZ32 + #define IGRAPH_CTZ IGRAPH_I_CTZ32 +#elif IGRAPH_INTEGER_SIZE == 64 + #define IGRAPH_POPCOUNT IGRAPH_I_POPCOUNT64 + #define IGRAPH_CLZ IGRAPH_I_CLZ64 + #define IGRAPH_CTZ IGRAPH_I_CTZ64 +#else + #error "Unexpected IGRAPH_INTEGER_SIZE value." +#endif + +#define IGRAPH_CLO(x) IGRAPH_CLZ(~(x)) +#define IGRAPH_CTO(x) IGRAPH_CTZ(~(x)) + +typedef struct { + igraph_integer_t size; + igraph_uint_t *stor_begin; + igraph_uint_t *stor_end; +} igraph_bitset_t; + +IGRAPH_EXPORT igraph_error_t igraph_bitset_init(igraph_bitset_t *bitset, igraph_integer_t size); +IGRAPH_EXPORT void igraph_bitset_destroy(igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_init_copy(igraph_bitset_t *dest, const igraph_bitset_t *src); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_capacity(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_size(const igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_reserve(igraph_bitset_t *bitset, igraph_integer_t capacity); +IGRAPH_EXPORT igraph_error_t igraph_bitset_resize(igraph_bitset_t *bitset, igraph_integer_t new_size); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_popcount(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countl_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countl_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countr_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_bitset_countr_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_all_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_all_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_any_zero(const igraph_bitset_t *bitset); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_bitset_is_any_one(const igraph_bitset_t *bitset); +IGRAPH_EXPORT void igraph_bitset_or(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_and(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_xor(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2); +IGRAPH_EXPORT void igraph_bitset_not(igraph_bitset_t *dest, const igraph_bitset_t *src); +IGRAPH_EXPORT void igraph_bitset_fill(igraph_bitset_t *bitset, igraph_bool_t value); +IGRAPH_EXPORT void igraph_bitset_null(igraph_bitset_t *bitset); +IGRAPH_EXPORT igraph_error_t igraph_bitset_fprint(const igraph_bitset_t *bitset, FILE *file); +IGRAPH_EXPORT igraph_error_t igraph_bitset_print(const igraph_bitset_t *bitset); + +#define IGRAPH_BITSET_INIT_FINALLY(bitset, size) \ +do { IGRAPH_CHECK(igraph_bitset_init(bitset, size)); \ + IGRAPH_FINALLY(igraph_bitset_destroy, bitset); } while (0) + +__END_DECLS + +#endif /* IGRAPH_BITSET_H */ diff --git a/include/igraph_bitset_list.h b/include/igraph_bitset_list.h new file mode 100644 index 0000000000..f45bc12f95 --- /dev/null +++ b/include/igraph_bitset_list.h @@ -0,0 +1,51 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2024 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_BITSET_LIST_H +#define IGRAPH_BITSET_LIST_H + +#include "igraph_bitset.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_types.h" + +__BEGIN_DECLS + +/* -------------------------------------------------- */ +/* List of graphs */ +/* -------------------------------------------------- */ + +#define BITSET_LIST +#define BASE_BITSET +#include "igraph_pmt.h" +#include "igraph_typed_list_pmt.h" +#include "igraph_pmt_off.h" +#undef BASE_BITSET +#undef BITSET_LIST + +#define IGRAPH_BITSET_LIST_INIT_FINALLY(v, size) \ + do { IGRAPH_CHECK(igraph_bitset_list_init(v, size)); \ + IGRAPH_FINALLY(igraph_bitset_list_destroy, v); } while (0) + +__END_DECLS + +#endif diff --git a/include/igraph_centrality.h b/include/igraph_centrality.h index 1e515a696d..816815b135 100644 --- a/include/igraph_centrality.h +++ b/include/igraph_centrality.h @@ -134,7 +134,7 @@ IGRAPH_EXPORT igraph_error_t igraph_constraint(const igraph_t *graph, igraph_vec 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_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_centralization(const igraph_vector_t *scores, igraph_real_t theoretical_max, igraph_bool_t normalized); diff --git a/include/igraph_complex.h b/include/igraph_complex.h index d17b854bd7..6288d6576a 100644 --- a/include/igraph_complex.h +++ b/include/igraph_complex.h @@ -37,71 +37,70 @@ typedef struct igraph_complex_t { #define IGRAPH_IMAG(x) ((x).dat[1]) #define IGRAPH_COMPLEX_EQ(x,y) ((x).dat[0]==(y).dat[0] && (x).dat[1]==(y).dat[1]) -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_FUNCATTR_CONST igraph_complex_t igraph_complex(igraph_real_t x, igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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_real_t igraph_complex_mod(igraph_complex_t z); -IGRAPH_EXPORT igraph_real_t igraph_complex_arg(igraph_complex_t z); - -IGRAPH_EXPORT igraph_real_t igraph_complex_abs(igraph_complex_t z); -IGRAPH_EXPORT igraph_real_t igraph_complex_logabs(igraph_complex_t z); - -IGRAPH_EXPORT igraph_complex_t igraph_complex_add(igraph_complex_t z1, - igraph_complex_t z2); -IGRAPH_EXPORT igraph_complex_t igraph_complex_sub(igraph_complex_t z1, - igraph_complex_t z2); -IGRAPH_EXPORT igraph_complex_t igraph_complex_mul(igraph_complex_t z1, - igraph_complex_t z2); -IGRAPH_EXPORT igraph_complex_t igraph_complex_div(igraph_complex_t z1, - igraph_complex_t z2); - -IGRAPH_EXPORT igraph_complex_t igraph_complex_add_real(igraph_complex_t z, - igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, - igraph_real_t y); -IGRAPH_EXPORT igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, - igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, - igraph_real_t y); -IGRAPH_EXPORT igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, - igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, - igraph_real_t y); -IGRAPH_EXPORT igraph_complex_t igraph_complex_div_real(igraph_complex_t z, - igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, - igraph_real_t y); - -IGRAPH_EXPORT igraph_complex_t igraph_complex_conj(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_neg(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_inv(igraph_complex_t z); - -IGRAPH_EXPORT igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_exp(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_pow(igraph_complex_t z1, - igraph_complex_t z2); -IGRAPH_EXPORT igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, - igraph_real_t x); -IGRAPH_EXPORT igraph_complex_t igraph_complex_log(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_log10(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_log_b(igraph_complex_t z, - igraph_complex_t b); - -IGRAPH_EXPORT igraph_complex_t igraph_complex_sin(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_cos(igraph_complex_t z); -IGRAPH_EXPORT igraph_complex_t igraph_complex_tan(igraph_complex_t z); -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 IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_complex_almost_equals(igraph_complex_t z1, + igraph_complex_t z2, + igraph_real_t eps); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_arg(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_abs(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_real_t igraph_complex_logabs(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div(igraph_complex_t z1, + igraph_complex_t z2); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_add_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sub_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_mul_imag(igraph_complex_t z, + igraph_real_t y); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_div_imag(igraph_complex_t z, + igraph_real_t y); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_conj(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_neg(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_inv(igraph_complex_t z); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sqrt(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sqrt_real(igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_exp(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_pow(igraph_complex_t z1, + igraph_complex_t z2); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_pow_real(igraph_complex_t z, + igraph_real_t x); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log10(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_log_b(igraph_complex_t z, + igraph_complex_t b); + +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sin(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_cos(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_tan(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_sec(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_complex_t igraph_complex_csc(igraph_complex_t z); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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); diff --git a/include/igraph_constants.h b/include/igraph_constants.h index 2452c8c5a5..8a7dbcfb12 100644 --- a/include/igraph_constants.h +++ b/include/igraph_constants.h @@ -206,6 +206,11 @@ typedef enum { IGRAPH_VORONOI_FIRST = 0, IGRAPH_VORONOI_RANDOM } igraph_voronoi_tiebreaker_t; +typedef enum { IGRAPH_CHUNG_LU_ORIGINAL = 0, + IGRAPH_CHUNG_LU_MAXENT, + IGRAPH_CHUNG_LU_NR + } igraph_chung_lu_t; + typedef enum { IGRAPH_ROW_MAJOR = 0, IGRAPH_COLUMN_MAJOR = 1 } igraph_matrix_storage_t; diff --git a/include/igraph_constructors.h b/include/igraph_constructors.h index 38e01cb20f..d734ef47b1 100644 --- a/include/igraph_constructors.h +++ b/include/igraph_constructors.h @@ -55,6 +55,8 @@ IGRAPH_EXPORT igraph_error_t igraph_star(igraph_t *graph, igraph_integer_t n, ig 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_error_t igraph_hypercube(igraph_t *graph, + igraph_integer_t n, igraph_bool_t directed); 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 igraph_error_t igraph_square_lattice(igraph_t *graph, const igraph_vector_int_t *dimvector, igraph_integer_t nei, diff --git a/include/igraph_decls.h b/include/igraph_decls.h index 14c4ca1ab8..dd5bd53740 100644 --- a/include/igraph_decls.h +++ b/include/igraph_decls.h @@ -14,11 +14,19 @@ /* The pure function attribute of GCC-compatible compilers indicates * that the function does not have side-effects, i.e. it does not * modify global memory. This enables additional compiler optimizations - * such as common subexpression elimination. */ + * such as common subexpression elimination. + * + * The const attribute is similar but with much more stringent requirements. + * The function must also not read global memory. Generally, const functions + * should not take pointers, and must compute the return value solely based + * on their input. + */ #ifdef __GNUC__ #define IGRAPH_FUNCATTR_PURE __attribute__((__pure__)) +#define IGRAPH_FUNCATTR_CONST __attribute__((__const__)) #else #define IGRAPH_FUNCATTR_PURE +#define IGRAPH_FUNCATTR_CONST #endif /* IGRAPH_ASSUME() provides hints to the compiler about conditions diff --git a/include/igraph_error.h b/include/igraph_error.h index c367d60351..69e2654c45 100644 --- a/include/igraph_error.h +++ b/include/igraph_error.h @@ -232,6 +232,8 @@ __BEGIN_DECLS * enough. These functions should define their own error handlers and * restore the error handler before they return. * + * + * \example examples/simple/igraph_contract_vertices.c */ /** @@ -419,8 +421,8 @@ typedef igraph_error_type_t igraph_error_t; * \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); +typedef void igraph_error_handler_t(const char *reason, const char *file, + int line, igraph_error_t igraph_errno); /** * \var igraph_error_handler_abort @@ -430,7 +432,7 @@ typedef void igraph_error_handler_t (const char *reason, const char *file, * program. */ -IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_abort; +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN igraph_error_handler_t igraph_error_handler_abort; /** * \var igraph_error_handler_ignore @@ -452,20 +454,7 @@ IGRAPH_EXPORT igraph_error_handler_t igraph_error_handler_ignore; 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); +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 @@ -510,27 +499,8 @@ IGRAPH_EXPORT igraph_error_handler_t* igraph_set_error_handler(igraph_error_hand igraph_error (reason, IGRAPH_FILE_BASENAME, __LINE__, igraph_errno) ; \ } while (0) -/** - * \function igraph_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. - * It calls the currently installed error handler function with the - * supplied arguments. - * - * \param reason Textual description of the error. - * \param file The source file in which the error was noticed. - * \param line The number of line in the source file which triggered the - * error. - * \param igraph_errno The \a igraph error code. - * \return the error code (if it returns) - * - * \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 igraph_error_t igraph_error(const char *reason, const char *file, + int line, igraph_error_t igraph_errno); /** * \define IGRAPH_ERRORF @@ -563,40 +533,17 @@ IGRAPH_EXPORT igraph_error_t igraph_error(const char *reason, const char *file, return igraph_errno; \ } while (0) -/** - * \function igraph_errorf - * \brief Reports an error, printf-like version. - * - * \param reason Textual description of the error, interpreted as - * a \c printf format string. - * \param file The source file in which the error was noticed. - * \param line The line in the source file which triggered the error. - * \param igraph_errno The \a igraph error code. - * \param ... Additional parameters, the values to substitute into the - * format string. - * - * \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 igraph_error_t igraph_errorvf(const char *reason, const char *file, int line, - igraph_error_t igraph_errno, va_list ap); +IGRAPH_EXPORT igraph_error_t igraph_errorf(const char *reason, const char *file, + int line, igraph_error_t igraph_errno, + ...); -/** - * \function igraph_strerror - * \brief Textual description of an error. - * - * This is a simple utility function, it gives a short general textual - * description for an \a igraph error code. - * - * \param igraph_errno The \a igraph error code. - * \return pointer to the textual description of the error code. - */ +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 const char* igraph_strerror(const igraph_error_t igraph_errno); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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)) @@ -614,9 +561,9 @@ struct igraph_i_protectedPtr { void (*func)(void*); }; -typedef void igraph_finally_func_t (void*); +typedef void igraph_finally_func_t(void *); -IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(void (*func)(void*), void* ptr); +IGRAPH_EXPORT void IGRAPH_FINALLY_REAL(igraph_finally_func_t *func, void *ptr); /** * \function IGRAPH_FINALLY_CLEAN @@ -794,9 +741,7 @@ IGRAPH_EXPORT int IGRAPH_FINALLY_STACK_SIZE(void); #define IGRAPH_CHECK_CALLBACK(expr, code) \ do { \ igraph_error_t igraph_i_ret = (expr); \ - if (code) { \ - *(code) = igraph_i_ret; \ - } \ + *(code) = igraph_i_ret; \ if (IGRAPH_UNLIKELY(igraph_i_ret != IGRAPH_SUCCESS && igraph_i_ret != IGRAPH_STOP)) { \ IGRAPH_ERROR("", igraph_i_ret); \ } \ @@ -855,42 +800,17 @@ 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 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. - * \return The current warning handler function. - */ -IGRAPH_EXPORT igraph_warning_handler_t* igraph_set_warning_handler(igraph_warning_handler_t* new_handler); +IGRAPH_EXPORT igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t* new_handler); IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_ignore; IGRAPH_EXPORT extern igraph_warning_handler_t igraph_warning_handler_print; -/** - * \function igraph_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 - * warning. - * \param igraph_errno Warnings could have potentially error codes as well, - * but this is currently not used in igraph. - * \return The supplied error code. - */ - -IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line); +IGRAPH_EXPORT void igraph_warning(const char *reason, + const char *file, int line); /** * \define IGRAPH_WARNINGF @@ -913,28 +833,9 @@ IGRAPH_EXPORT void igraph_warning(const char *reason, const char *file, int line } while (0) - -/** - * \function igraph_warningf - * \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. - * \param line The number of line in the source file which triggered the - * warning. - * \param igraph_errno Warnings could have potentially error codes as well, - * but this is currently not used in igraph. - * \param ... The additional arguments to be substituted into the - * template string. - */ - IGRAPH_FUNCATTR_PRINTFLIKE(1,4) -IGRAPH_EXPORT void igraph_warningf(const char *reason, const char *file, int line, ...); +IGRAPH_EXPORT void igraph_warningf(const char *reason, + const char *file, int line, ...); /** * \define IGRAPH_WARNING @@ -990,25 +891,9 @@ IGRAPH_EXPORT void igraph_warningf(const char *reason, const char *file, int lin * \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); - -/** - * \function igraph_set_fatal_handler - * \brief Installs a fatal error handler. - * - * Installs the supplied fatal error handler function. - * - * - * Fatal error handler functions \em must not return. Typically, the fatal - * error handler would either call abort() or longjmp(). - * - * \param new_handler The new fatal error handler function to install. - * Supply a null pointer here to uninstall the current - * fatal error handler, without installing a new one. - * \return The current fatal error handler function. - */ +typedef void igraph_fatal_handler_t(const char *reason, const char *file, int line); -IGRAPH_EXPORT igraph_fatal_handler_t* igraph_set_fatal_handler(igraph_fatal_handler_t* new_handler); +IGRAPH_EXPORT igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler); /** * \var igraph_fatal_handler_abort @@ -1017,38 +902,13 @@ IGRAPH_EXPORT igraph_fatal_handler_t* igraph_set_fatal_handler(igraph_fatal_hand * The default fatal error handler, prints an error message and aborts the program. */ -IGRAPH_EXPORT igraph_fatal_handler_t igraph_fatal_handler_abort; - -/** - * \function igraph_fatal - * \brief Triggers a fatal error. - * - * This function triggers a fatal error. Typically it is called indirectly through - * \ref IGRAPH_FATAL() or \ref IGRAPH_ASSERT(). - * - * \param reason Textual description of the error. - * \param file The source file in which the error was noticed. - * \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); - -/** - * \function igraph_fatalf - * \brief Triggers a fatal error, printf-like syntax. - * - * This function is similar to \ref igraph_fatal(), but - * uses a printf-like syntax. It substitutes the additional arguments - * into the \p reason template string and calls \ref igraph_fatal(). - * - * \param reason Textual description of the error. - * \param file The source file in which the error was noticed. - * \param line The number of line in the source file which triggered the error. - * \param ... The additional arguments to be substituted into the template string. - */ +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN igraph_fatal_handler_t igraph_fatal_handler_abort; +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatal(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, ...); +IGRAPH_EXPORT IGRAPH_FUNCATTR_NORETURN void igraph_fatalf(const char *reason, + const char *file, int line, ...); /** * \define IGRAPH_FATALF diff --git a/include/igraph_games.h b/include/igraph_games.h index e9384fd59c..3c4cbdb5ae 100644 --- a/include/igraph_games.h +++ b/include/igraph_games.h @@ -170,6 +170,12 @@ IGRAPH_EXPORT igraph_error_t igraph_static_power_law_game(igraph_t *graph, igraph_bool_t loops, igraph_bool_t multiple, igraph_bool_t finite_size_correction); +IGRAPH_EXPORT igraph_error_t igraph_chung_lu_game(igraph_t *graph, + const igraph_vector_t *expected_out_deg, + const igraph_vector_t *expected_in_deg, + igraph_bool_t loops, + igraph_chung_lu_t variant); + 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); diff --git a/include/igraph_iterators.h b/include/igraph_iterators.h index d60ee6397a..144b435e8e 100644 --- a/include/igraph_iterators.h +++ b/include/igraph_iterators.h @@ -65,7 +65,7 @@ typedef struct igraph_vs_t { } igraph_vs_t; IGRAPH_EXPORT igraph_error_t igraph_vs_all(igraph_vs_t *vs); -IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_vs_t igraph_vss_all(void); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_all(void); IGRAPH_EXPORT igraph_error_t igraph_vs_adj(igraph_vs_t *vs, igraph_integer_t vid, igraph_neimode_t mode); @@ -74,10 +74,10 @@ IGRAPH_EXPORT igraph_error_t igraph_vs_nonadj(igraph_vs_t *vs, igraph_integer_t igraph_neimode_t mode); IGRAPH_EXPORT igraph_error_t igraph_vs_none(igraph_vs_t *vs); -IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_vs_t igraph_vss_none(void); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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 IGRAPH_FUNCATTR_PURE igraph_vs_t igraph_vss_1(igraph_integer_t vid); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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); @@ -92,7 +92,7 @@ IGRAPH_EXPORT IGRAPH_DEPRECATED igraph_error_t igraph_vs_seq(igraph_vs_t *vs, ig 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_FUNCATTR_PURE igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end); IGRAPH_EXPORT void igraph_vs_destroy(igraph_vs_t *vs); @@ -272,23 +272,23 @@ typedef struct igraph_es_t { IGRAPH_EXPORT igraph_error_t igraph_es_all(igraph_es_t *es, igraph_edgeorder_type_t order); -IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_all(igraph_edgeorder_type_t order); IGRAPH_EXPORT igraph_error_t 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 IGRAPH_FUNCATTR_PURE igraph_es_t igraph_ess_none(void); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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 IGRAPH_FUNCATTR_PURE igraph_es_t igraph_ess_1(igraph_integer_t eid); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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_FUNCATTR_PURE igraph_es_t igraph_ess_vector(const igraph_vector_int_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_FUNCATTR_PURE igraph_es_t igraph_ess_range(igraph_integer_t from, igraph_integer_t to); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST igraph_es_t igraph_ess_range(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); diff --git a/include/igraph_matrix.h b/include/igraph_matrix.h index 6a77444b49..f092d7a078 100644 --- a/include/igraph_matrix.h +++ b/include/igraph_matrix.h @@ -91,9 +91,9 @@ IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_matrix_all_e_tol(const igra 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_FUNCATTR_PURE 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_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); diff --git a/include/igraph_misc.h b/include/igraph_misc.h deleted file mode 100644 index e26f0786ad..0000000000 --- a/include/igraph_misc.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - IGraph library. - Copyright (C) 2003-2024 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 . -*/ - -/* Semi-public header; not included from igraph.h. This is intentional. - * The macros defined in this header are usually not useful to the end user, - * with some exceptions like the source of the R interface. You need to - * include this header explicitly if needed. */ - -#ifndef IGRAPM_MISC_H -#define IGRAPM_MISC_H - -#include "igraph_decls.h" - -__BEGIN_DECLS - -/* 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/include/igraph_nongraph.h b/include/igraph_nongraph.h index 43b6ec3c50..63aa374989 100644 --- a/include/igraph_nongraph.h +++ b/include/igraph_nongraph.h @@ -94,8 +94,8 @@ IGRAPH_EXPORT igraph_error_t igraph_random_sample(igraph_vector_int_t *res, igra igraph_integer_t length); IGRAPH_EXPORT igraph_error_t igraph_convex_hull(const igraph_matrix_t *data, igraph_vector_int_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_FUNCATTR_CONST igraph_bool_t igraph_almost_equals(double a, double b, double eps); +IGRAPH_EXPORT IGRAPH_FUNCATTR_CONST 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, diff --git a/include/igraph_operators.h b/include/igraph_operators.h index 0433d483be..7b06e657ec 100644 --- a/include/igraph_operators.h +++ b/include/igraph_operators.h @@ -74,9 +74,9 @@ IGRAPH_EXPORT igraph_error_t igraph_connect_neighborhood(igraph_t *graph, igraph IGRAPH_EXPORT igraph_error_t igraph_graph_power(const igraph_t *graph, igraph_t *res, igraph_integer_t order, igraph_bool_t directed); 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 igraph_error_t igraph_simplify(igraph_t *graph, + igraph_bool_t remove_multiple, igraph_bool_t remove_loops, + const igraph_attribute_combination_t *edge_comb); 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, diff --git a/include/igraph_pmt.h b/include/igraph_pmt.h index fe8c350d3e..de90c1ee3d 100644 --- a/include/igraph_pmt.h +++ b/include/igraph_pmt.h @@ -116,6 +116,9 @@ #elif defined(BASE_GRAPH) #define BASE igraph_t +#elif defined(BASE_BITSET) + #define BASE igraph_bitset_t + #else #error unknown BASE_ directive #endif @@ -156,6 +159,11 @@ #define FUNCTION(c) CONCAT2x(igraph_graph_list,c) #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_graph_list,c) #define TYPE igraph_graph_list_t +#elif defined(BITSET_LIST) + #define FUNCTION(c) CONCAT2x(igraph_bitset_list,c) + #define INTERNAL_FUNCTION(c) CONCAT2x(igraph_i_bitset_list,c) + #define TYPE igraph_bitset_list_t + #else #if defined(BASE_IGRAPH_REAL) #define FUNCTION(a,c) CONCAT2(a,c) diff --git a/include/igraph_random.h b/include/igraph_random.h index fcd84f4025..2b2a80a609 100644 --- a/include/igraph_random.h +++ b/include/igraph_random.h @@ -117,9 +117,9 @@ IGRAPH_EXPORT igraph_error_t igraph_rng_init(igraph_rng_t *rng, const igraph_rng 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_FUNCATTR_PURE igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_uint_t igraph_rng_max(const igraph_rng_t *rng); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE 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 diff --git a/include/igraph_reachability.h b/include/igraph_reachability.h new file mode 100644 index 0000000000..472655e8b9 --- /dev/null +++ b/include/igraph_reachability.h @@ -0,0 +1,49 @@ +/* + IGraph library. + Copyright (C) 2024 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_REACHABILITY_H +#define IGRAPH_REACHABILITY_H + +#include "igraph_bitset_list.h" +#include "igraph_datatype.h" +#include "igraph_decls.h" +#include "igraph_error.h" +#include "igraph_vector.h" + +__BEGIN_DECLS + +IGRAPH_EXPORT igraph_error_t igraph_reachability( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize, + igraph_integer_t *no_of_components, + igraph_bitset_list_t *reach, + igraph_neimode_t mode); + +IGRAPH_EXPORT igraph_error_t igraph_count_reachable( + const igraph_t *graph, + igraph_vector_int_t *counts, + igraph_neimode_t mode); + + +IGRAPH_EXPORT igraph_error_t igraph_transitive_closure(const igraph_t *graph, + igraph_t* closure); + +__END_DECLS + +#endif // IGRAPH_REACHABILITY_H diff --git a/include/igraph_stack_pmt.h b/include/igraph_stack_pmt.h index 7cbc36915c..d0a5a25e09 100644 --- a/include/igraph_stack_pmt.h +++ b/include/igraph_stack_pmt.h @@ -39,6 +39,7 @@ 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 IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_stack, empty)(TYPE(igraph_stack)* s); IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_stack, size)(const TYPE(igraph_stack)* s); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_stack, capacity)(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 BASE FUNCTION(igraph_stack, pop)(TYPE(igraph_stack)* s); diff --git a/include/igraph_structural.h b/include/igraph_structural.h index 81b54e72b8..b118bae5cc 100644 --- a/include/igraph_structural.h +++ b/include/igraph_structural.h @@ -52,6 +52,7 @@ IGRAPH_EXPORT igraph_error_t igraph_girth(const igraph_t *graph, igraph_real_t * 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_count_loops(const igraph_t *graph, igraph_integer_t *loop_count); IGRAPH_EXPORT igraph_error_t 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, @@ -66,6 +67,8 @@ IGRAPH_EXPORT igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool 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 igraph_error_t igraph_mean_degree(const igraph_t *graph, igraph_real_t *res, + igraph_bool_t loops); IGRAPH_EXPORT igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, igraph_bool_t ignore_loops, igraph_reciprocity_t mode); @@ -86,11 +89,15 @@ IGRAPH_EXPORT igraph_error_t igraph_is_perfect(const igraph_t *graph, igraph_boo /* -------------------------------------------------- */ IGRAPH_EXPORT igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res); +IGRAPH_EXPORT igraph_error_t igraph_is_independent_vertex_set(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_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 igraph_error_t igraph_minimum_spanning_tree_unweighted(const igraph_t *graph, +IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_error_t 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_DEPRECATED IGRAPH_EXPORT igraph_error_t 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_integer_t vid); @@ -142,7 +149,7 @@ IGRAPH_EXPORT igraph_error_t igraph_feedback_arc_set(const igraph_t *graph, igra * 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_SYMMETRIC Symmetrically 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. */ diff --git a/include/igraph_topology.h b/include/igraph_topology.h index 3afd1e4f5c..9ae7734fd6 100644 --- a/include/igraph_topology.h +++ b/include/igraph_topology.h @@ -40,7 +40,7 @@ __BEGIN_DECLS 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_DEPRECATED IGRAPH_EXPORT igraph_error_t igraph_transitive_closure_dag(const igraph_t *graph, igraph_t *closure); /* -------------------------------------------------- */ @@ -296,8 +296,6 @@ IGRAPH_EXPORT igraph_error_t igraph_automorphism_group( ); /* 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 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); diff --git a/include/igraph_vector.h b/include/igraph_vector.h index 567c835b88..24e8652909 100644 --- a/include/igraph_vector.h +++ b/include/igraph_vector.h @@ -136,16 +136,17 @@ IGRAPH_DEPRECATED IGRAPH_EXPORT igraph_bool_t igraph_vector_e_tol(const igraph_v 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_FUNCATTR_PURE 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_vector_bool_t *is_nan); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_vector_is_all_finite(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, diff --git a/include/igraph_vector_pmt.h b/include/igraph_vector_pmt.h index d324fece26..c303a7802a 100644 --- a/include/igraph_vector_pmt.h +++ b/include/igraph_vector_pmt.h @@ -211,7 +211,7 @@ IGRAPH_EXPORT igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)( 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)( +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t FUNCTION(igraph_vector, binsearch2)( const TYPE(igraph_vector) *v, BASE what); #endif @@ -308,6 +308,9 @@ IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, difference_sorted)(const TY const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result); +IGRAPH_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t FUNCTION(igraph_vector, intersection_size_sorted)( + const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2); #endif IGRAPH_EXPORT igraph_error_t FUNCTION(igraph_vector, index)(const TYPE(igraph_vector) *v, TYPE(igraph_vector) *newv, diff --git a/include/igraph_vector_ptr.h b/include/igraph_vector_ptr.h index 4a75b98670..77ba89cb70 100644 --- a/include/igraph_vector_ptr.h +++ b/include/igraph_vector_ptr.h @@ -70,7 +70,7 @@ IGRAPH_EXPORT igraph_error_t igraph_vector_ptr_append(igraph_vector_ptr_t *to, 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 IGRAPH_FUNCATTR_PURE 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); @@ -80,7 +80,7 @@ IGRAPH_EXPORT void igraph_vector_ptr_sort(igraph_vector_ptr_t *v, int(*compar)(c 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_FUNCATTR_PURE 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); diff --git a/interfaces/functions.yaml b/interfaces/functions.yaml index d3e0484700..3b2f09624b 100644 --- a/interfaces/functions.yaml +++ b/interfaces/functions.yaml @@ -126,6 +126,9 @@ igraph_star: igraph_wheel: PARAMS: OUT GRAPH graph, INTEGER n, WHEEL_MODE mode=OUT, INTEGER center=0 +igraph_hypercube: + PARAMS: OUT GRAPH graph, INTEGER n, BOOLEAN directed=False + igraph_square_lattice: PARAMS: |- OUT GRAPH graph, VECTOR_INT dimvector, INTEGER nei=1, @@ -338,6 +341,12 @@ igraph_simple_interconnected_islands_game: OUT GRAPH graph, INTEGER islands_n, INTEGER islands_size, REAL islands_pin, INTEGER n_inter +# Use a default of loops=True, as this is what's in the original Chung-Lu paper +igraph_chung_lu_game: + PARAMS: |- + OUT GRAPH graph, VECTOR out_weights, OPTIONAL VECTOR in_weights, + BOOLEAN loops=True, CHUNG_LU_VARIANT variant=ORIGINAL + igraph_static_fitness_game: PARAMS: |- OUT GRAPH graph, INTEGER no_of_edges, VECTOR fitness_out, @@ -527,8 +536,8 @@ igraph_get_shortest_paths_dijkstra: 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=0, - OPTIONAL OUT VECTOR_INT inbound_edges=0 + OPTIONAL OUT VECTOR_INT parents=NULL, + OPTIONAL OUT VECTOR_INT inbound_edges=NULL DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph @@ -538,8 +547,8 @@ igraph_get_shortest_paths_bellman_ford: 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=0, - OPTIONAL OUT VECTOR_INT inbound_edges=0 + OPTIONAL OUT VECTOR_INT parents=NULL, + OPTIONAL OUT VECTOR_INT inbound_edges=NULL DEPS: |- vertices ON graph, edges ON graph, from ON graph, to ON graph, weights ON graph @@ -569,7 +578,7 @@ 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 + FWALGORITHM method=AUTOMATIC DEPS: from ON graph, to ON graph, weights ON graph igraph_voronoi: @@ -652,7 +661,7 @@ igraph_betweenness_subset: 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 + vids ON graph, weights ON graph, res ON graph vids, sources ON graph, targets ON graph igraph_edge_betweenness: PARAMS: |- @@ -695,7 +704,7 @@ igraph_pagerank: REAL damping=0.85, EDGEWEIGHTS weights=NULL, INOUT PAGERANKOPT options=NULL DEPS: |- - vids ON graph, weights ON graph, vector ON graph, + vids ON graph, weights ON graph, vector ON graph vids, options ON algo igraph_personalized_pagerank: @@ -798,6 +807,9 @@ igraph_maxdegree: igraph_density: PARAMS: GRAPH graph, OUT REAL res, BOOLEAN loops=False +igraph_mean_degree: + PARAMS: GRAPH graph, OUT REAL res, BOOLEAN loops=True + igraph_neighborhood_size: PARAMS: |- GRAPH graph, OUT VECTOR_INT res, VERTEX_SELECTOR vids, INTEGER order, @@ -850,6 +862,9 @@ igraph_has_loop: igraph_has_multiple: PARAMS: GRAPH graph, OUT BOOLEAN res +igraph_count_loops: + PARAMS: GRAPH graph, OUT INTEGER loop_count + igraph_count_multiple: PARAMS: GRAPH graph, OUT VECTOR_INT res, EDGE_SELECTOR es=ALL DEPS: es ON graph @@ -866,7 +881,7 @@ igraph_add_edge: igraph_eigenvector_centrality: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT ALL_VERTEX_QTY vector, OUT REAL value, BOOLEAN directed=False, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=ARPACK_DEFAULTS @@ -874,21 +889,21 @@ igraph_eigenvector_centrality: igraph_hub_score: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT ALL_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 VERTEX_QTY vector, OUT REAL value, + GRAPH graph, OUT ALL_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_hub_and_authority_scores: PARAMS: |- - GRAPH graph, OUT VERTEX_QTY hub_vector, OUT VERTEX_QTY authority_vector, + GRAPH graph, OUT ALL_VERTEX_QTY hub_vector, OUT ALL_VERTEX_QTY authority_vector, OUT REAL value, BOOLEAN scale=True, EDGEWEIGHTS weights=NULL, INOUT ARPACKOPT options=ARPACK_DEFAULTS @@ -1120,6 +1135,9 @@ igraph_average_local_efficiency: igraph_transitive_closure_dag: PARAMS: GRAPH graph, OUT GRAPH closure +igraph_transitive_closure: + PARAMS: GRAPH graph, OUT GRAPH closure + igraph_trussness: PARAMS: GRAPH graph, OUT VECTOR_INT trussness @@ -1279,10 +1297,19 @@ igraph_bridges: igraph_is_biconnected: PARAMS: GRAPH graph, OUT BOOLEAN res +igraph_count_reachable: + PARAMS: GRAPH graph, OUT VECTOR_INT counts, NEIMODE mode + ####################################### # Cliques ####################################### +igraph_is_clique: + PARAMS: |- + GRAPH graph, VERTEX_SELECTOR candidate, BOOLEAN directed=False, + OUT BOOLEAN res + DEPS: candidate ON graph + igraph_cliques: PARAMS: |- GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, @@ -1347,6 +1374,10 @@ igraph_weighted_clique_number: PARAMS: GRAPH graph, VERTEXWEIGHTS vertex_weights=NULL, OUT REAL res DEPS: vertex_weights ON graph +igraph_is_independent_vertex_set: + PARAMS: GRAPH graph, VERTEX_SELECTOR candidate, OUT BOOLEAN res + DEPS: candidate ON graph + igraph_independent_vertex_sets: PARAMS: |- GRAPH graph, OUT VERTEXSET_LIST res, INTEGER min_size=0, @@ -2550,25 +2581,25 @@ igraph_vertex_coloring_greedy: igraph_deterministic_optimal_imitation: PARAMS: |- - GRAPH graph, VERTEX vid, OPTIMALITY optimality=MAXIMUM, VERTEX_QTY quantities, + GRAPH graph, VERTEX vid, OPTIMALITY optimality=MAXIMUM, ALL_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, + GRAPH graph, EDGEWEIGHTS weights=NULL, INOUT ALL_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, + GRAPH graph, VERTEX vid, BOOLEAN is_local, ALL_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, + GRAPH graph, VERTEX vid, IMITATE_ALGORITHM algo, ALL_VERTEX_QTY quantities, INOUT VECTOR_INT strategies, NEIMODE mode=OUT DEPS: vid ON graph, quantities ON graph, strategies ON graph diff --git a/interfaces/types.yaml b/interfaces/types.yaml index 9799d02652..3c15618944 100644 --- a/interfaces/types.yaml +++ b/interfaces/types.yaml @@ -213,7 +213,7 @@ GRAPH_PTR_LIST: CTYPE: igraph_vector_ptr_t FLAGS: BY_REF -VERTEX_QTY: +ALL_VERTEX_QTY: # 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 @@ -222,6 +222,12 @@ VERTEX_QTY: CTYPE: igraph_vector_t FLAGS: BY_REF +VERTEX_QTY: + # Same as ALL_VERTEX_QTY, but only for a subset of vertices, + # typically referred to by a `vids` argument. + CTYPE: igraph_vector_t + FLAGS: BY_REF + SIR_LIST: # A vector containing pointers to igraph_sir_t objects CTYPE: igraph_vector_ptr_t @@ -288,6 +294,11 @@ BLISSSH: CTYPE: igraph_bliss_sh_t FLAGS: ENUM +CHUNG_LU_VARIANT: + # Enum that describes the Chung-Lu model variants supported by igraph + CTYPE: igraph_chung_lu_t + FLAGS: ENUM + COMMCMP: # Enum containing identifiers for community comparison methods CTYPE: igraph_community_comparison_t diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7751a621a7..b380cfe364 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -40,6 +40,8 @@ add_custom_target(parsersources SOURCES ${PARSER_SOURCES}) add_library( igraph core/array.c + core/bitset.c + core/bitset_list.c core/buckets.c core/cutheap.c core/dqueue.c @@ -113,6 +115,7 @@ add_library( games/barabasi.c games/callaway_traits.c + games/chung_lu.c games/citations.c games/correlated.c games/degree_sequence_vl/gengraph_degree_sequence.cpp @@ -177,6 +180,7 @@ add_library( connectivity/cohesive_blocks.c connectivity/components.c connectivity/separators.c + connectivity/reachability.c flow/flow.c flow/flow_conversion.c @@ -328,7 +332,7 @@ add_library( add_dependencies(igraph parsersources) # Set soname for the library -set_target_properties(igraph PROPERTIES VERSION "3.1.6") +set_target_properties(igraph PROPERTIES VERSION "3.1.8") set_target_properties(igraph PROPERTIES SOVERSION 3) # Add extra compiler definitions if needed diff --git a/src/centrality/coreness.c b/src/centrality/coreness.c index 2d3a7089af..e48d18f15b 100644 --- a/src/centrality/coreness.c +++ b/src/centrality/coreness.c @@ -63,7 +63,7 @@ igraph_error_t igraph_coreness(const igraph_t *graph, igraph_neimode_t omode; if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { - IGRAPH_ERROR("Invalid mode in k-cores.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid mode in k-cores.", IGRAPH_EINVMODE); } if (!igraph_is_directed(graph)) { mode = IGRAPH_ALL; diff --git a/src/centrality/eigenvector.c b/src/centrality/eigenvector.c index 5f14065c89..5c9985ddc0 100644 --- a/src/centrality/eigenvector.c +++ b/src/centrality/eigenvector.c @@ -137,7 +137,8 @@ static igraph_error_t igraph_i_eigenvector_centrality_undirected(const igraph_t * longer guaranteed to be non-negative. */ negative_weights = true; IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " - "will be selected, but it may not be the largest in magnitude."); + "will be selected, but it may not be the largest in magnitude. " + "Some eigenvector centralities may be negative."); } } @@ -150,9 +151,10 @@ static igraph_error_t igraph_i_eigenvector_centrality_undirected(const igraph_t 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); + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(degree)[i] + RNG_UNIF(0, 1e-4); } else { - MATRIX(vectors, i, 0) = 1.0; + MATRIX(vectors, i, 0) = 0.01; } } RNG_END(); @@ -300,9 +302,6 @@ static igraph_error_t igraph_i_eigenvector_centrality_directed(const igraph_t *g "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); @@ -333,7 +332,10 @@ static igraph_error_t igraph_i_eigenvector_centrality_directed(const igraph_t *g options->n = (int) no_of_nodes; options->start = 1; options->nev = 1; - options->ncv = 0; /* 0 means "automatic" in igraph_arpack_rnsolve */ + /* Use higher NCV than usual as long directed cycles cause convergence issues. + * According to numerical experiments, a cycle graph C_n tends to need about + * NCV = n/4 at minimum. */ + options->ncv = no_of_nodes > 30 ? 30 : options->n; /* 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'; @@ -347,9 +349,10 @@ static igraph_error_t igraph_i_eigenvector_centrality_directed(const igraph_t *g 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); + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(0, 1e-4); } else { - MATRIX(vectors, i, 0) = 1.0; + MATRIX(vectors, i, 0) = 0.01; } } RNG_END(); diff --git a/src/centrality/hub_authority.c b/src/centrality/hub_authority.c index 028e74b8bd..a034c926a3 100644 --- a/src/centrality/hub_authority.c +++ b/src/centrality/hub_authority.c @@ -21,9 +21,10 @@ #include "igraph_centrality.h" #include "igraph_adjlist.h" +#include "igraph_blas.h" #include "igraph_interface.h" +#include "igraph_random.h" #include "igraph_structural.h" -#include "igraph_blas.h" #include "centrality/centrality_internal.h" @@ -154,8 +155,8 @@ static igraph_error_t igraph_i_kleinberg_weighted(igraph_real_t *to, * * * If vector \c h and \c a contain hub and authority scores, then the two - * scores are related by h = Aa and a = Ah. - * When the principal eigenvalue of A A^T is dengenerate, there + * scores are related by h = Aa and a = A^T h. + * When the principal eigenvalue of A A^T is degenerate, there * is no unique solution to the hub- and authority-score problem. * igraph guarantees that the scores that are returned are matching, i.e. are * related by these formulas, even in this situation. @@ -190,16 +191,14 @@ static igraph_error_t igraph_i_kleinberg_weighted(igraph_real_t *to, * \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. + * it always starts the calculation from a 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(), + * \sa \ref igraph_pagerank(), \ref igraph_personalized_pagerank(); * \ref igraph_eigenvector_centrality() for a similar measure intended * for undirected graphs. */ @@ -208,6 +207,10 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, igraph_real_t *value, igraph_bool_t scale, const igraph_vector_t *weights, igraph_arpack_options_t *options) { + /* The current implementation computes hub scores, i.e the principal + * eigenvector of A A^T, and transforms these to authority scores as + * authority = A^T hub. */ + igraph_adjlist_t inadjlist, outadjlist; igraph_inclist_t ininclist, outinclist; igraph_integer_t no_of_nodes = igraph_vcount(graph); @@ -218,7 +221,7 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, igraph_i_kleinberg_data2_t extra2; igraph_vector_t *my_hub_vector_p; igraph_vector_t my_hub_vector; - + igraph_bool_t negative_weights = false; if (igraph_ecount(graph) == 0) { /* special case: empty graph */ @@ -248,8 +251,19 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, 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.0) { + /* When there are negative weights, the principal eigenvalue and the eigenvector + * are no longer guaranteed to be non-negative. */ + negative_weights = true; + IGRAPH_WARNING("Negative weight in graph. The largest eigenvalue " + "will be selected, but it may not be the largest in magnitude. " + "Some hub and authority scores may be negative."); + } + if (min == 0 && max == 0) { /* special case: all weights are zeros */ if (value) { @@ -275,7 +289,7 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, options = igraph_arpack_options_get_default(); } - options->n = no_of_nodes; + options->n = (int) no_of_nodes; options->start = 1; /* no random start vector */ IGRAPH_VECTOR_INIT_FINALLY(&values, 0); @@ -294,14 +308,17 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, IGRAPH_FINALLY(igraph_inclist_destroy, &outinclist); } - IGRAPH_CHECK(igraph_strength(graph, &tmp, igraph_vss_all(), IGRAPH_ALL, 0, 0)); + IGRAPH_CHECK(igraph_strength(graph, &tmp, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, weights)); + RNG_BEGIN(); for (igraph_integer_t i = 0; i < options->n; i++) { if (VECTOR(tmp)[i] != 0) { - MATRIX(vectors, i, 0) = VECTOR(tmp)[i]; + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(tmp)[i] + RNG_UNIF(0, 1e-4); } else { - MATRIX(vectors, i, 0) = 1.0; + MATRIX(vectors, i, 0) = 0.01; } } + RNG_END(); extra.in = &inadjlist; extra.out = &outadjlist; extra.tmp = &tmp; extra2.in = &ininclist; extra2.out = &outinclist; extra2.tmp = &tmp; @@ -351,9 +368,11 @@ igraph_error_t igraph_hub_and_authority_scores(const igraph_t *graph, } /* 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 (! negative_weights) { + 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; + } } } } diff --git a/src/centrality/pagerank.c b/src/centrality/pagerank.c index 1bd714d606..375170b719 100644 --- a/src/centrality/pagerank.c +++ b/src/centrality/pagerank.c @@ -612,7 +612,8 @@ static igraph_error_t igraph_i_personalized_pagerank_arpack(const igraph_t *grap * 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); + /* Note: Keep random perturbation non-negative. */ + MATRIX(vectors, i, 0) = VECTOR(indegree)[i] + RNG_UNIF(0, 1e-4); } else { MATRIX(vectors, i, 0) = 1; } diff --git a/src/cliques/cliquer_wrapper.c b/src/cliques/cliquer_wrapper.c index 445faa3742..7e2ca16df2 100644 --- a/src/cliques/cliquer_wrapper.c +++ b/src/cliques/cliquer_wrapper.c @@ -39,14 +39,14 @@ static igraph_error_t igraph_to_cliquer(const igraph_t *ig, graph_t **cg) { igraph_integer_t i; if (igraph_is_directed(ig)) { - IGRAPH_WARNING("Edge directions are ignored for clique calculations"); + IGRAPH_WARNING("Edge directions are ignored for clique calculations."); } vcount = igraph_vcount(ig); ecount = igraph_ecount(ig); if (vcount > INT_MAX) { - IGRAPH_ERROR("Graph too large for Cliquer", IGRAPH_EOVERFLOW); + IGRAPH_ERROR("Graph too large for Cliquer.", IGRAPH_EOVERFLOW); } *cg = graph_new((int) vcount); @@ -71,16 +71,16 @@ static igraph_error_t set_weights(const igraph_vector_t *vertex_weights, graph_t IGRAPH_ASSERT(vertex_weights != NULL); if (igraph_vector_size(vertex_weights) != g->n) { - IGRAPH_ERROR("Invalid vertex weight vector length", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid vertex weight vector length.", IGRAPH_EINVAL); } for (i = 0; i < g->n; ++i) { g->weights[i] = VECTOR(*vertex_weights)[i]; if (g->weights[i] != VECTOR(*vertex_weights)[i]) { - IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts"); + IGRAPH_WARNING("Only integer vertex weights are supported; weights will be truncated to their integer parts."); } if (g->weights[i] <= 0) { - IGRAPH_ERROR("Vertex weights must be positive", IGRAPH_EINVAL); + IGRAPH_ERROR("Vertex weights must be positive.", IGRAPH_EINVAL); } } @@ -155,7 +155,8 @@ igraph_error_t igraph_i_cliquer_cliques(const igraph_t *graph, igraph_vector_int } if (max_size > 0 && max_size < min_size) { - IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); + IGRAPH_ERROR("Maximum clique size must not be smaller than the minimum clique size.", + IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); @@ -301,7 +302,8 @@ igraph_error_t igraph_i_cliquer_callback(const igraph_t *graph, } if (max_size > 0 && max_size < min_size) { - IGRAPH_ERROR("max_size must not be smaller than min_size", IGRAPH_EINVAL); + IGRAPH_ERROR("Maximum clique size must not be smaller than the minimum clique size.", + IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_to_cliquer(graph, &g)); @@ -340,12 +342,12 @@ igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, } if (min_weight != (int) min_weight) { - IGRAPH_WARNING("Only integer vertex weights are supported; the minimum weight will be truncated to its integer part"); + IGRAPH_WARNING("Only integer vertex weights are supported; the minimum weight will be truncated to its integer part."); min_weight = (int) min_weight; } if (max_weight != (int) max_weight) { - IGRAPH_WARNING("Only integer vertex weights are supported; the maximum weight will be truncated to its integer part"); + IGRAPH_WARNING("Only integer vertex weights are supported; the maximum weight will be truncated to its integer part."); max_weight = (int) max_weight; } @@ -357,7 +359,8 @@ igraph_error_t igraph_i_weighted_cliques(const igraph_t *graph, } if (max_weight > 0 && max_weight < min_weight) { - IGRAPH_ERROR("max_weight must not be smaller than min_weight", IGRAPH_EINVAL); + IGRAPH_ERROR("Maximum clique weight must not be smaller than minimum clique weight.", + IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_i_cliquer_cliques_user_data_init(&data, res)); diff --git a/src/cliques/cliques.c b/src/cliques/cliques.c index acd97491aa..4ce00bdbca 100644 --- a/src/cliques/cliques.c +++ b/src/cliques/cliques.c @@ -554,8 +554,8 @@ igraph_error_t igraph_largest_independent_vertex_sets(const igraph_t *graph, 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_adjlist_t adj_list; /* Adjacency list of the graph */ + 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; diff --git a/src/community/community_misc.c b/src/community/community_misc.c index 5666944d22..a283991f49 100644 --- a/src/community/community_misc.c +++ b/src/community/community_misc.c @@ -28,6 +28,30 @@ #include #include +/** + * \section about_community + * + * + * Community detection is concerned with clustering the vertices of networks + * into tightly connected subgraphs called "communities". The following + * references provide a good introduction to the topic of community detection: + * + * + * + * S. Fortunato: + * "Community Detection in Graphs". + * Physics Reports 486, no. 3–5 (2010): 75–174. + * https://doi.org/16/j.physrep.2009.11.002. + * + * + * + * S. Fortunato and D. Hric: + * "Community Detection in Networks: A User Guide". + * Physics Reports 659 (2016): 1–44. + * https://doi.org/10.1016/j.physrep.2016.09.002. + * + */ + /** * \function igraph_community_to_membership * \brief Creates a membership vector from a community structure dendrogram. @@ -313,7 +337,7 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 * of cluster \c i. Then the entropy of the clustering is * * - * H(C) = - \sum_i p_i log p_i + * H(C) = - sum_i p_i log p_i * * * Similarly, we can define the joint entropy of two clusterings \c C_1 and \c C_2 @@ -321,7 +345,7 @@ static igraph_error_t igraph_i_split_join_distance(const igraph_vector_int_t *v1 * in the first clustering and cluster \c j in the second one: * * - * H(C_1, C_2) = - \sum_ii p_ij log p_ij + * H(C_1, C_2) = - sum_ii p_ij log p_ij * * * The mutual information of \c C_1 and \c C_2 is then diff --git a/src/community/edge_betweenness.c b/src/community/edge_betweenness.c index 16b64f186f..493508039e 100644 --- a/src/community/edge_betweenness.c +++ b/src/community/edge_betweenness.c @@ -198,7 +198,7 @@ static igraph_error_t igraph_i_community_eb_get_merges2(const igraph_t *graph, * 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. + * 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 @@ -342,10 +342,7 @@ static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t * \brief Community finding based on edge betweenness. * * Community structure detection based on the betweenness of the edges - * in the network. The algorithm was invented by M. Girvan and - * M. Newman, see: M. Girvan and M. E. J. Newman: Community structure in - * social and biological networks, Proc. Nat. Acad. Sci. USA 99, 7821-7826 - * (2002). https://doi.org/10.1073/pnas.122653799 + * in the network, known as the Grivan-Newman algorithm. * * * The idea is that the betweenness of the edges connecting two @@ -363,6 +360,15 @@ static igraph_integer_t igraph_i_vector_which_max_not_null(const igraph_vector_t * of betweenness and modularity are used, however, only splits into * \em weakly connected components are detected. * + * + * Reference: + * + * + * M. Girvan and M. E. J. Newman: + * Community structure in social and biological networks. + * Proc. Nat. Acad. Sci. USA 99, 7821-7826 (2002). + * https://doi.org/10.1073/pnas.122653799 + * * \param graph The input graph. * \param removed_edges Pointer to an initialized vector, the result will be * stored here, the IDs of the removed edges in the order of their diff --git a/src/community/fluid.c b/src/community/fluid.c index d419f7ac12..7f34dc4e43 100644 --- a/src/community/fluid.c +++ b/src/community/fluid.c @@ -78,7 +78,7 @@ igraph_error_t igraph_community_fluid_communities(const igraph_t *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_vector_int_null(membership); } return IGRAPH_SUCCESS; } diff --git a/src/community/infomap/infomap.cc b/src/community/infomap/infomap.cc index dff92bbf05..88bb2ac2ca 100644 --- a/src/community/infomap/infomap.cc +++ b/src/community/infomap/infomap.cc @@ -193,10 +193,10 @@ static igraph_error_t infomap_partition(FlowGraph &fgraph, bool rcall) { * 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). + * (https://dx.doi.org/10.1073/pnas.0706851105, https://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) + * (https://dx.doi.org/10.1140/epjst/e2010-01179-1, https://arxiv.org/abs/0906.1405) * * The original C++ implementation of Martin Rosvall is used, diff --git a/src/community/label_propagation.c b/src/community/label_propagation.c index e96ce784b2..adaf2ba01e 100644 --- a/src/community/label_propagation.c +++ b/src/community/label_propagation.c @@ -26,6 +26,8 @@ #include "igraph_memory.h" #include "igraph_random.h" +#include "core/interruption.h" + /** * \ingroup communities * \function igraph_community_label_propagation @@ -129,6 +131,7 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, igraph_bool_t running, control_iteration; igraph_bool_t unlabelled_left; igraph_neimode_t reversed_mode; + int iter = 0; /* interruption counter */ igraph_vector_t label_counters; /* real type, stores weight sums */ igraph_vector_int_t dominant_labels, nonzero_labels, node_order; @@ -265,6 +268,8 @@ igraph_error_t igraph_community_label_propagation(const igraph_t *graph, igraph_vector_int_t *ineis; igraph_bool_t was_zero; + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); + 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. diff --git a/src/community/leading_eigenvector.c b/src/community/leading_eigenvector.c index 91b88c4fbf..074dd6ccd9 100644 --- a/src/community/leading_eigenvector.c +++ b/src/community/leading_eigenvector.c @@ -45,13 +45,14 @@ * eigenvectors of matrices, Phys Rev E 74:036104 (2006). * * - * The heart of the method is the definition of the modularity matrix, - * B, which is B=A-P, A being the adjacency matrix of the (undirected) - * network, and P contains the probability that certain edges are - * present according to the configuration model In - * other words, a Pij element of P is the probability that there is an - * edge between vertices i and j in a random network in which the - * degrees of all vertices are the same as in the input graph. + * The heart of the method is the definition of the modularity matrix + * B = A - P, \c A being the adjacency matrix of the (undirected) + * network, and \c P contains the probability that certain edges are + * present according to the configuration model. In + * other words, a \c P_ij element of \c P is the probability that there is an + * edge between vertices \c i and \c j in a random network in which the + * degrees of all vertices are the same as in the input graph. See + * \ref igraph_modularity_matrix() for more details. * * * The leading eigenvector method works by calculating the eigenvector @@ -252,11 +253,11 @@ static void igraph_i_error_handler_none(const char *reason, const char *file, * community detection functions in igraph, the integers in this matrix * represent community indices, not vertex indices. If at the end of * the algorithm (after \p steps steps was done) there are p - * communities, then these are numbered from zero to p-1. + * communities, then these are numbered from zero to p-1. * The first line of the matrix contains the first merge * (which is in reality the last split) of two communities into - * community p, the merge in the second line forms - * community p+1, etc. The matrix should be + * community p, the merge in the second line forms + * community p+1, etc. The matrix should be * initialized before calling and will be resized as needed. * This argument is ignored if it is \c NULL. * \param membership The membership of the vertices after all the diff --git a/src/community/leiden.c b/src/community/leiden.c index e9aa9ada24..803c77d6d4 100644 --- a/src/community/leiden.c +++ b/src/community/leiden.c @@ -25,6 +25,7 @@ #include "igraph_community.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "igraph_interface.h" @@ -62,18 +63,19 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( 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; + const igraph_integer_t n = igraph_vcount(graph); + igraph_bitset_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_int_t nb_nodes_per_cluster; igraph_stack_int_t empty_clusters; - igraph_integer_t i, j, c, nb_neigh_clusters; + igraph_integer_t c, nb_neigh_clusters; + int iter = 0; /* 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_BITSET_INIT_FINALLY(&node_is_stable, n); IGRAPH_DQUEUE_INT_INIT_FINALLY(&unstable_nodes, n); @@ -83,14 +85,14 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); /* Add to the queue */ - for (i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { IGRAPH_CHECK(igraph_dqueue_int_push(&unstable_nodes, 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); - for (i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { c = VECTOR(*membership)[i]; VECTOR(cluster_weights)[c] += VECTOR(*node_weights)[i]; VECTOR(nb_nodes_per_cluster)[c] += 1; @@ -107,11 +109,10 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); /* Initialize neighboring cluster */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_BITSET_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_int_empty(&unstable_nodes)) { igraph_integer_t v = igraph_dqueue_int_pop(&unstable_nodes); igraph_integer_t best_cluster, current_cluster = VECTOR(*membership)[v]; @@ -128,19 +129,19 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( /* Find out neighboring clusters */ c = igraph_stack_int_top(&empty_clusters); VECTOR(neighbor_clusters)[0] = c; - VECTOR(neighbor_cluster_added)[c] = true; + IGRAPH_BIT_SET(neighbor_cluster_added, c); 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++) { + for (igraph_integer_t i = 0; i < degree; i++) { 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] = true; + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c); VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -150,7 +151,7 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( /* Calculate maximum diff */ best_cluster = current_cluster; max_diff = VECTOR(edge_weights_per_cluster)[current_cluster] - VECTOR(*node_weights)[v] * VECTOR(cluster_weights)[current_cluster] * resolution_parameter; - for (i = 0; i < nb_neigh_clusters; i++) { + for (igraph_integer_t 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. @@ -161,7 +162,7 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( max_diff = diff; } VECTOR(edge_weights_per_cluster)[c] = 0.0; - VECTOR(neighbor_cluster_added)[c] = false; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c); } /* Move node to best cluster */ @@ -172,41 +173,37 @@ static igraph_error_t igraph_i_community_leiden_fastmovenodes( } /* Mark node as stable */ - VECTOR(node_is_stable)[v] = true; + IGRAPH_BIT_SET(node_is_stable, v); /* 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++) { + for (igraph_integer_t i = 0; i < degree; i++) { 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) { + if (IGRAPH_BIT_TEST(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_BIT_CLEAR(node_is_stable, u); } } } - j++; - if (j > 10000) { - IGRAPH_ALLOW_INTERRUPTION(); - j = 0; - } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); } IGRAPH_CHECK(igraph_reindex_membership(membership, NULL, nb_clusters)); igraph_vector_int_destroy(&neighbor_clusters); - igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_bitset_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weights_per_cluster); igraph_stack_int_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_bool_destroy(&node_is_stable); + igraph_bitset_destroy(&node_is_stable); IGRAPH_FINALLY_CLEAN(9); return IGRAPH_SUCCESS; @@ -225,7 +222,7 @@ 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) { - igraph_integer_t i, n = igraph_vector_int_size(node_subset); + const igraph_integer_t n = igraph_vector_int_size(node_subset); igraph_vector_int_t new_cluster; IGRAPH_VECTOR_INT_INIT_FINALLY(&new_cluster, n); @@ -233,7 +230,7 @@ static igraph_error_t igraph_i_community_leiden_clean_refined_membership( /* 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++) { + for (igraph_integer_t i = 0; i < n; i++) { igraph_integer_t v = VECTOR(*node_subset)[i]; igraph_integer_t c = VECTOR(*refined_membership)[v]; if (VECTOR(new_cluster)[c] == 0) { @@ -243,7 +240,7 @@ static igraph_error_t igraph_i_community_leiden_clean_refined_membership( } /* Assign new cluster */ - for (i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { 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; @@ -299,13 +296,13 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( igraph_integer_t *nb_refined_clusters, igraph_vector_int_t *refined_membership) { igraph_vector_int_t node_order; - igraph_vector_bool_t non_singleton_cluster, neighbor_cluster_added; + igraph_bitset_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); + const 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; - igraph_integer_t i, j, degree, nb_neigh_clusters; + igraph_integer_t degree, nb_neigh_clusters; /* Initialize cluster weights */ IGRAPH_VECTOR_INIT_FINALLY(&cluster_weights, n); @@ -317,7 +314,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( IGRAPH_VECTOR_INIT_FINALLY(&external_edge_weight_per_cluster_in_subset, n); /* Initialize administration for a singleton partition */ - for (i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { igraph_integer_t v = VECTOR(*node_subset)[i]; VECTOR(*refined_membership)[v] = i; VECTOR(cluster_weights)[i] += VECTOR(*node_weights)[v]; @@ -327,7 +324,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( /* Find out neighboring clusters */ edges = igraph_inclist_get(edges_per_node, v); degree = igraph_vector_int_size(edges); - for (j = 0; j < degree; j++) { + for (igraph_integer_t j = 0; j < degree; j++) { igraph_integer_t e = VECTOR(*edges)[j]; igraph_integer_t u = IGRAPH_OTHER(graph, e, v); if (u != v && VECTOR(*membership)[u] == cluster_subset) { @@ -342,13 +339,13 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( IGRAPH_CHECK(igraph_vector_int_shuffle(&node_order)); /* Initialize non singleton clusters */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&non_singleton_cluster, n); + IGRAPH_BITSET_INIT_FINALLY(&non_singleton_cluster, n); /* Initialize vectors to be used in calculating differences */ IGRAPH_VECTOR_INIT_FINALLY(&edge_weights_per_cluster, n); /* Initialize neighboring cluster */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, n); + IGRAPH_BITSET_INIT_FINALLY(&neighbor_cluster_added, n); IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, n); /* Initialize cumulative transformed difference */ @@ -356,11 +353,11 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( RNG_BEGIN(); - for (i = 0; i < n; i++) { + for (igraph_integer_t 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]; - if (!VECTOR(non_singleton_cluster)[current_cluster] && + if (!IGRAPH_BIT_TEST(non_singleton_cluster, current_cluster) && (VECTOR(external_edge_weight_per_cluster_in_subset)[current_cluster] >= VECTOR(cluster_weights)[current_cluster] * (total_node_weight - VECTOR(cluster_weights)[current_cluster]) * resolution_parameter)) { /* Remove node from current cluster, which is then a singleton by @@ -374,15 +371,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; + IGRAPH_BIT_SET(neighbor_cluster_added, current_cluster); nb_neigh_clusters = 1; - for (j = 0; j < degree; j++) { + for (igraph_integer_t j = 0; j < degree; j++) { igraph_integer_t e = VECTOR(*edges)[j]; igraph_integer_t u = IGRAPH_OTHER(graph, e, v); if (u != v && VECTOR(*membership)[u] == cluster_subset) { igraph_integer_t c = VECTOR(*refined_membership)[u]; - if (!VECTOR(neighbor_cluster_added)[c]) { - VECTOR(neighbor_cluster_added)[c] = true; + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c); VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c; } VECTOR(edge_weights_per_cluster)[c] += VECTOR(*edge_weights)[e]; @@ -393,7 +390,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( best_cluster = current_cluster; max_diff = 0.0; total_cum_trans_diff = 0.0; - for (j = 0; j < nb_neigh_clusters; j++) { + for (igraph_integer_t j = 0; j < nb_neigh_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; @@ -412,7 +409,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; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c); } /* Determine the neighboring cluster to which the currently selected node @@ -431,7 +428,7 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( VECTOR(cluster_weights)[chosen_cluster] += VECTOR(*node_weights)[v]; VECTOR(nb_nodes_per_cluster)[chosen_cluster]++; - for (j = 0; j < degree; j++) { + for (igraph_integer_t j = 0; j < degree; j++) { igraph_integer_t e = VECTOR(*edges)[j]; igraph_integer_t u = IGRAPH_OTHER(graph, e, v); if (VECTOR(*membership)[u] == cluster_subset) { @@ -447,7 +444,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; + IGRAPH_BIT_SET(non_singleton_cluster, chosen_cluster); } } /* end if singleton and may be merged */ } @@ -458,9 +455,9 @@ static igraph_error_t igraph_i_community_leiden_mergenodes( igraph_vector_destroy(&cum_trans_diff); igraph_vector_int_destroy(&neighbor_clusters); - igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_bitset_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weights_per_cluster); - igraph_vector_bool_destroy(&non_singleton_cluster); + igraph_bitset_destroy(&non_singleton_cluster); igraph_vector_int_destroy(&node_order); igraph_vector_destroy(&external_edge_weight_per_cluster_in_subset); igraph_vector_int_destroy(&nb_nodes_per_cluster); @@ -515,8 +512,8 @@ static igraph_error_t igraph_i_community_leiden_aggregate( igraph_vector_int_list_t refined_clusters; igraph_vector_int_t *incident_edges; igraph_vector_int_t neighbor_clusters; - igraph_vector_bool_t neighbor_cluster_added; - igraph_integer_t i, j, c, degree, nb_neigh_clusters; + igraph_bitset_t neighbor_cluster_added; + igraph_integer_t c, degree, nb_neigh_clusters; /* Get refined clusters */ IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&refined_clusters, nb_refined_clusters); @@ -534,7 +531,7 @@ static igraph_error_t igraph_i_community_leiden_aggregate( IGRAPH_VECTOR_INIT_FINALLY(&edge_weight_to_cluster, nb_refined_clusters); /* Initialize neighboring cluster */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&neighbor_cluster_added, nb_refined_clusters); + IGRAPH_BITSET_INIT_FINALLY(&neighbor_cluster_added, nb_refined_clusters); IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbor_clusters, nb_refined_clusters); /* Check per cluster */ @@ -546,19 +543,19 @@ static igraph_error_t igraph_i_community_leiden_aggregate( /* 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++) { + for (igraph_integer_t i = 0; i < n_c; 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++) { + for (igraph_integer_t 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]; if (c2 > c) { - if (!VECTOR(neighbor_cluster_added)[c2]) { - VECTOR(neighbor_cluster_added)[c2] = true; + if (!IGRAPH_BIT_TEST(neighbor_cluster_added, c2)) { + IGRAPH_BIT_SET(neighbor_cluster_added, c2); VECTOR(neighbor_clusters)[nb_neigh_clusters++] = c2; } VECTOR(edge_weight_to_cluster)[c2] += VECTOR(*edge_weights)[e]; @@ -569,7 +566,7 @@ 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++) { + for (igraph_integer_t i = 0; i < nb_neigh_clusters; i++) { igraph_integer_t c2 = VECTOR(neighbor_clusters)[i]; /* Add edge */ @@ -580,7 +577,7 @@ static igraph_error_t igraph_i_community_leiden_aggregate( 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; + IGRAPH_BIT_CLEAR(neighbor_cluster_added, c2); } VECTOR(*aggregated_membership)[c] = VECTOR(*membership)[v]; @@ -588,7 +585,7 @@ static igraph_error_t igraph_i_community_leiden_aggregate( } igraph_vector_int_destroy(&neighbor_clusters); - igraph_vector_bool_destroy(&neighbor_cluster_added); + igraph_bitset_destroy(&neighbor_cluster_added); igraph_vector_destroy(&edge_weight_to_cluster); igraph_vector_int_list_destroy(&refined_clusters); IGRAPH_FINALLY_CLEAN(4); @@ -872,15 +869,13 @@ static igraph_error_t igraph_i_community_leiden( * \brief Finding community structure using the Leiden algorithm. * * This function implements the Leiden algorithm for finding community - * structure, see Traag, V. A., Waltman, L., & van Eck, N. J. (2019). From - * Louvain to Leiden: guaranteeing well-connected communities. Scientific - * reports, 9(1), 5233. http://dx.doi.org/10.1038/s41598-019-41695-z + * structure. * * * It is similar to the multilevel algorithm, often called the Louvain * algorithm, but it is faster and yields higher quality solutions. It can * optimize both modularity and the Constant Potts Model, which does not suffer - * from the resolution-limit (see preprint http://arxiv.org/abs/1104.3083). + * from the resolution-limit (see Tragg, Van Dooren & Nesterov). * * * The Leiden algorithm consists of three phases: (1) local moving of nodes, (2) @@ -911,17 +906,33 @@ static igraph_error_t igraph_i_community_leiden( * The objective function being optimized is * * - * 1 / 2m sum_ij (A_ij - gamma n_i n_j)d(s_i, s_j) + * 1 / 2m sum_ij (A_ij - γ n_i n_j) δ(s_i, s_j) * * - * where m is the total edge weight, A_ij is the weight of edge (i, j), gamma is - * the so-called resolution parameter, n_i is the node weight of node i, s_i is - * the cluster of node i and d(x, y) = 1 if and only if x = y and 0 otherwise. - * By setting n_i = k_i, the degree of node i, and dividing gamma by 2m, you - * effectively obtain an expression for modularity. Hence, the standard - * modularity will be optimized when you supply the degrees as \c node_weights - * and by supplying as a resolution parameter 1.0/(2*m), with m the number of - * edges. + * where m is the total edge weight, A_ij is the weight of edge + * (i, j), \c γ is the so-called resolution parameter, n_i + * is the node weight of node \c i, s_i is the cluster of node + * \c i and δ(x, y) = 1 if and only if x = y and 0 + * otherwise. By setting n_i = k_i, the degree of node \c i, and + * dividing \c γ by 2m, we effectively obtain an expression for + * modularity. Hence, the standard modularity will be optimized when you supply + * the degrees as \c node_weights and by supplying as a resolution parameter + * 1/(2m), with \c m the number of edges. + * + * + * References: + * + * + * V. A. Traag, L. Waltman, N. J. van Eck: + * From Louvain to Leiden: guaranteeing well-connected communities. + * Scientific Reports, 9(1), 5233 (2019). + * http://dx.doi.org/10.1038/s41598-019-41695-z + * + * + * V. A. Traag, P. Van Dooren, and Y. Nesterov: + * Narrow scope for resolution-limit-free community detection. + * Phys. Rev. E 84, 016114 (2011). + * https://doi.org/10.1103/PhysRevE.84.016114 * * \param graph The input graph. It must be an undirected graph. * \param edge_weights Numeric vector containing edge weights. If \c NULL, every edge diff --git a/src/community/louvain.c b/src/community/louvain.c index a6a84c56ef..2f4abf1636 100644 --- a/src/community/louvain.c +++ b/src/community/louvain.c @@ -501,7 +501,7 @@ static igraph_error_t igraph_i_community_multilevel_step( /* We reuse the links_weight vector to store the old edge weights */ IGRAPH_CHECK(igraph_vector_update(&links_weight, weights)); - igraph_vector_fill(weights, 0); + igraph_vector_null(weights); for (igraph_integer_t i = 0; i < ecount; i++) { VECTOR(*weights)[VECTOR(edges)[i]] += VECTOR(links_weight)[i]; @@ -520,16 +520,10 @@ static igraph_error_t igraph_i_community_multilevel_step( /** * \ingroup communities * \function igraph_community_multilevel - * \brief Finding community structure by multi-level optimization of modularity. + * \brief Finding community structure by multi-level optimization of modularity (Louvain). * - * This function implements the multi-level modularity optimization - * algorithm for finding community structure, see - * Blondel, V. D., Guillaume, J.-L., Lambiotte, R., & Lefebvre, E. (2008). Fast - * unfolding of communities in large networks. Journal of Statistical Mechanics: - * Theory and Experiment, 10008(10), 6. - * https://doi.org/10.1088/1742-5468/2008/10/P10008 for the details (preprint: - * http://arxiv.org/abs/0803.0476). The algorithm is sometimes known as the - * "Louvain" algorithm. + * This function implements a multi-level modularity optimization algorithm + * for finding community structure, sometimes known as the Louvain algorithm. * * * The algorithm is based on the modularity measure and a hierarchical approach. @@ -542,16 +536,25 @@ static igraph_error_t igraph_i_community_multilevel_step( * the modularity cannot be increased any more in a step. * * - * The resolution parameter \c gamma allows finding communities at different + * The resolution parameter \c γ allows finding communities at different * resolutions. Higher values of the resolution parameter typically result in * more, smaller communities. Lower values typically result in fewer, larger * communities. The original definition of modularity is retrieved when setting - * gamma=1. Note that the returned modularity value is calculated using + * γ=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. * + * + * Reference: + * + * + * Blondel, V. D., Guillaume, J.-L., Lambiotte, R., & Lefebvre, E.: + * Fast unfolding of communities in large networks. + * Journal of Statistical Mechanics: Theory and Experiment, 10008(10), 6 (2008). + * https://doi.org/10.1088/1742-5468/2008/10/P10008 + * * \param graph The input graph. It must be an undirected graph. * \param weights Numeric vector containing edge weights. If \c NULL, every edge * has equal weight. The weights are expected to be non-negative. diff --git a/src/community/optimal_modularity.c b/src/community/optimal_modularity.c index ce3eb2d9be..53d9506a83 100644 --- a/src/community/optimal_modularity.c +++ b/src/community/optimal_modularity.c @@ -126,7 +126,7 @@ igraph_error_t igraph_community_optimal_modularity(const igraph_t *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_vector_int_null(membership); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, 0, 1, igraph_is_directed(graph), modularity)); diff --git a/src/community/spinglass/clustertool.cpp b/src/community/spinglass/clustertool.cpp index 0ad80b2118..118da4fad0 100644 --- a/src/community/spinglass/clustertool.cpp +++ b/src/community/spinglass/clustertool.cpp @@ -108,9 +108,8 @@ static igraph_error_t igraph_i_community_spinglass_negative( * 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 - * M. E. J. Newman and M. Girvan, Phys. Rev. E 69, 026113 (2004), - * if the gamma parameter is one. + * gereralized modularity, taking into account the resolution parameter + * \p gamma. See \ref igraph_modularity() for details. * \param temperature Pointer to a real number, if not \c NULL then * the temperature at the end of the algorithm will be stored * here. @@ -159,7 +158,7 @@ static igraph_error_t igraph_i_community_spinglass_negative( * weights, using the number of spins as the number of colors. * \return Error code. * - * \sa igraph_community_spinglass_single() for calculating the community + * \sa \ref igraph_community_spinglass_single() for calculating the community * of a single vertex. * * Time complexity: TODO. @@ -270,7 +269,7 @@ static igraph_error_t igraph_i_community_spinglass_orig( if (no_of_nodes < 2) { if (membership) { IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + igraph_vector_int_null(membership); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, nullptr, 1, igraph_is_directed(graph), modularity)); @@ -556,7 +555,7 @@ static igraph_error_t igraph_i_community_spinglass_negative( if (no_of_nodes < 2) { if (membership) { IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); - igraph_vector_int_fill(membership, 0); + igraph_vector_int_null(membership); } if (modularity) { IGRAPH_CHECK(igraph_modularity(graph, membership, nullptr, 1, igraph_is_directed(graph), modularity)); diff --git a/src/community/spinglass/pottsmodel_2.cpp b/src/community/spinglass/pottsmodel_2.cpp index f454154e4d..355f19a608 100644 --- a/src/community/spinglass/pottsmodel_2.cpp +++ b/src/community/spinglass/pottsmodel_2.cpp @@ -45,7 +45,7 @@ #include "pottsmodel_2.h" #include "igraph_random.h" -#include "core/interruption.h" +// #include "core/interruption.h" #include #include @@ -838,7 +838,8 @@ double PottsModel::FindCommunityFromStart( //calculate the affinity changes of all nodes for adding every node in the to_do list to the community //############################## - IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ + // TODO + // IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ max_delta_aff = 0.0; max_aff_node = nullptr; @@ -956,7 +957,8 @@ double PottsModel::FindCommunityFromStart( //add the node to to_do again to_do.Push(max_aff_node); } - IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ + // TODO + // IGRAPH_ALLOW_INTERRUPTION(); /* This is not clean.... */ } //################### //write the node in the community to a file diff --git a/src/community/voronoi.c b/src/community/voronoi.c index 0357aafdec..aeaeac8de7 100644 --- a/src/community/voronoi.c +++ b/src/community/voronoi.c @@ -19,6 +19,7 @@ #include "igraph_community.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_interface.h" #include "igraph_iterators.h" #include "igraph_nongraph.h" @@ -161,7 +162,7 @@ static igraph_error_t choose_generators( igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_vector_int_t ord; - igraph_vector_bool_t excluded; + igraph_bitset_t excluded; igraph_integer_t excluded_count; igraph_inclist_t il; igraph_2wheap_t q; @@ -172,7 +173,7 @@ static igraph_error_t choose_generators( IGRAPH_CHECK(igraph_vector_qsort_ind(local_rel_dens, &ord, IGRAPH_DESCENDING)); /* If excluded[v] is true, then v is closer to some already chosen generator than r */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&excluded, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&excluded, no_of_nodes); excluded_count = 0; /* The input graph is expected to be simple, but we still set IGRAPH_LOOPS, @@ -188,7 +189,7 @@ static igraph_error_t choose_generators( for (igraph_integer_t i=0; i < no_of_nodes; i++) { igraph_integer_t g = VECTOR(ord)[i]; - if (VECTOR(excluded)[g]) continue; + if (IGRAPH_BIT_TEST(excluded, g)) continue; IGRAPH_CHECK(igraph_vector_int_push_back(generators, g)); @@ -204,8 +205,8 @@ static igraph_error_t choose_generators( /* Note: We cannot stop the search after hitting an excluded vertex * because it is possible that another non-excluded one is reachable only * through this one. */ - if (! VECTOR(excluded)[vid]) { - VECTOR(excluded)[vid] = true; + if (! IGRAPH_BIT_TEST(excluded, vid)) { + IGRAPH_BIT_SET(excluded, vid); excluded_count++; } @@ -250,7 +251,7 @@ static igraph_error_t choose_generators( igraph_2wheap_destroy(&q); igraph_inclist_destroy(&il); - igraph_vector_bool_destroy(&excluded); + igraph_bitset_destroy(&excluded); igraph_vector_int_destroy(&ord); IGRAPH_FINALLY_CLEAN(4); @@ -478,13 +479,14 @@ static igraph_error_t get_modularity(igraph_real_t r, igraph_real_t *modularity, * References: * * - * Deritei et al, Community detection by graph Voronoi diagrams, + * Deritei et al., Community detection by graph Voronoi diagrams, * New Journal of Physics 16, 063007 (2014) * https://doi.org/10.1088/1367-2630/16/6/063007 * * - * Molnár et al, Community Detection in Directed Weighted Networks using Voronoi Partitioning, - * https://arxiv.org/abs/2304.12389 + * Molnár et al., Community Detection in Directed Weighted Networks using Voronoi Partitioning, + * Scientific Reports 14, 8124 (2024) + * https://doi.org/10.1038/s41598-024-58624-4 * * \param graph The input graph. It must be simple. * \param membership If not \c NULL, the membership of each vertex is returned here. @@ -509,7 +511,8 @@ static igraph_error_t get_modularity(igraph_real_t r, igraph_real_t *modularity, */ igraph_error_t igraph_community_voronoi( const igraph_t *graph, - igraph_vector_int_t *membership, igraph_vector_int_t *generators, igraph_real_t *modularity, + igraph_vector_int_t *membership, igraph_vector_int_t *generators, + igraph_real_t *modularity, const igraph_vector_t *lengths, const igraph_vector_t *weights, igraph_neimode_t mode, igraph_real_t r) { @@ -632,7 +635,7 @@ igraph_error_t igraph_community_voronoi( IGRAPH_CHECK(choose_generators(graph, pgenerators, NULL, &local_rel_dens, &lengths2, mode, r)); IGRAPH_CHECK(igraph_voronoi(graph, membership, NULL, pgenerators, &lengths2, mode, IGRAPH_VORONOI_RANDOM)); if (modularity) { - IGRAPH_CHECK(igraph_modularity(graph, membership, weights,1, + IGRAPH_CHECK(igraph_modularity(graph, membership, weights, 1, mode == IGRAPH_ALL ? IGRAPH_UNDIRECTED : IGRAPH_DIRECTED, modularity)); } } diff --git a/src/community/walktrap/walktrap.cpp b/src/community/walktrap/walktrap.cpp index 1fb9d09ef0..4efadc9d80 100644 --- a/src/community/walktrap/walktrap.cpp +++ b/src/community/walktrap/walktrap.cpp @@ -109,8 +109,9 @@ using namespace igraph::walktrap; * cluster is created from two other clusters and its id will be * one larger than the largest cluster id so far. This means that * before the first merge we have \c n clusters (the number of - * vertices in the graph) numbered from zero to \c n-1. The first - * merge creates cluster \c n, the second cluster \c n+1, etc. + * vertices in the graph) numbered from zero to n - 1. + * The first merge creates cluster \c n, the second cluster + * n + 1, etc. * \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. diff --git a/src/config.h.in b/src/config.h.in index 9c5df6d90d..28cf4e2f43 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -19,6 +19,13 @@ #cmakedefine HAVE___UMULH 1 #cmakedefine HAVE___UINT128_T 1 +#cmakedefine HAVE__POPCNT64 1 +#cmakedefine HAVE__POPCNT 1 +#cmakedefine HAVE__BITSCANFORWARD64 1 +#cmakedefine HAVE__BITSCANFORWARD 1 +#cmakedefine HAVE__BITSCANREVERSE64 1 +#cmakedefine HAVE__BITSCANREVERSE 1 + #cmakedefine HAVE_GLPK 1 #cmakedefine HAVE_LIBXML 1 @@ -27,6 +34,7 @@ #cmakedefine INTERNAL_ARPACK 1 #cmakedefine INTERNAL_GMP 1 + #define IGRAPH_F77_SAVE static @TLS_KEYWORD@ #define IGRAPH_THREAD_LOCAL @TLS_KEYWORD@ diff --git a/src/connectivity/components.c b/src/connectivity/components.c index aeeed44c06..84bedfd5bc 100644 --- a/src/connectivity/components.c +++ b/src/connectivity/components.c @@ -24,6 +24,7 @@ #include "igraph_components.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" @@ -63,30 +64,35 @@ igraph_error_t igraph_clusters(const igraph_t *graph, igraph_vector_int_t *membe * \function igraph_connected_components * \brief Calculates the (weakly or strongly) connected components in a graph. * + * When computing strongly connected components, the components will be + * indexed in topological order. In other words, vertex \c v is reachable + * from vertex \c u precisely when membership[u] <= membership[v]. + * * \param graph The graph object to analyze. - * \param membership First half of the result will be stored here. For - * every vertex the id of its component is given. The vector - * has to be preinitialized and will be resized. Alternatively - * this argument can be \c NULL, in which case it is ignored. - * \param csize The second half of the result. For every component it - * gives its size, the order is defined by the component ids. - * The vector has to be preinitialized and will be resized. - * Alternatively this argument can be \c NULL, in which - * case it is ignored. + * \param membership For every vertex the ID of its component is given. + * The vector has to be preinitialized and will be resized as needed. + * Alternatively this argument can be \c NULL, in which case it is ignored. + * \param csize For every component it gives its size, the order being defined + * by the component IDs. The vector must be preinitialized and will be + * resized as needed. Alternatively this argument can be \c NULL, in which + * case it is ignored. * \param no Pointer to an integer, if not \c NULL then the number of - * components will be stored here. + * components will be stored here. * \param mode For directed graph this specifies whether to calculate - * weakly or strongly connected components. Possible values: - * \c IGRAPH_WEAK, - * \c IGRAPH_STRONG. This argument is - * ignored for undirected graphs. - * \return Error code: - * \c IGRAPH_EINVAL: invalid mode argument. + * weakly or strongly connected components. Possible values: + * \clist + * \cli IGRAPH_WEAK + * Compute weakly connected components, i.e. ignore edge directions. + * \cli IGRAPH_STRONG + * Compute strongly connnected components, i.e. considr edge directions. + * \endclist + * This parameter is ignored for undirected graphs. + * \return Error code. * - * Time complexity: O(|V|+|E|), - * |V| and - * |E| are the number of vertices and - * edges in the graph. + * Time complexity: O(|V|+|E|), where |V| and |E| are the number of vertices + * and edges in the graph. + * + * \example examples/simple/igraph_contract_vertices.c */ igraph_error_t igraph_connected_components( @@ -109,7 +115,7 @@ static igraph_error_t igraph_i_connected_components_weak( igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_components; - bool *already_added; + igraph_bitset_t already_added; igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; @@ -129,8 +135,9 @@ static igraph_error_t igraph_i_connected_components_weak( * the null graph is considered disconnected, therefore any connected * graph has precisely one component. */ if (membership) { - /* All vertices are members of the same component. */ - igraph_vector_int_fill(membership, 0); + /* All vertices are members of the same component, + * component number 0. */ + igraph_vector_int_null(membership); } if (csize) { /* The size of the single component is the same as the vertex count. */ @@ -143,10 +150,7 @@ static igraph_error_t igraph_i_connected_components_weak( return IGRAPH_SUCCESS; } - 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_BITSET_INIT_FINALLY(&already_added, no_of_nodes); IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); @@ -156,12 +160,12 @@ static igraph_error_t igraph_i_connected_components_weak( for (igraph_integer_t first_node = 0; first_node < no_of_nodes; ++first_node) { igraph_integer_t act_component_size; - if (already_added[first_node]) { + if (IGRAPH_BIT_TEST(already_added, first_node)) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[first_node] = true; + IGRAPH_BIT_SET(already_added, first_node); act_component_size = 1; if (membership) { VECTOR(*membership)[first_node] = no_of_components; @@ -174,11 +178,11 @@ static igraph_error_t igraph_i_connected_components_weak( igraph_integer_t nei_count = igraph_vector_int_size(&neis); for (igraph_integer_t i = 0; i < nei_count; i++) { igraph_integer_t neighbor = VECTOR(neis)[i]; - if (already_added[neighbor]) { + if (IGRAPH_BIT_TEST(already_added, neighbor)) { continue; } IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); - already_added[neighbor] = true; + IGRAPH_BIT_SET(already_added, neighbor); act_component_size++; if (membership) { VECTOR(*membership)[neighbor] = no_of_components; @@ -199,7 +203,7 @@ static igraph_error_t igraph_i_connected_components_weak( } /* Clean up */ - IGRAPH_FREE(already_added); + igraph_bitset_destroy(&already_added); igraph_dqueue_int_destroy(&q); igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); @@ -238,8 +242,9 @@ static igraph_error_t igraph_i_connected_components_strong( * the null graph is considered disconnected, therefore any connected * graph has precisely one component. */ if (membership) { - /* All vertices are members of the same component. */ - igraph_vector_int_fill(membership, 0); + /* All vertices are members of the same component, + * component number 0. */ + igraph_vector_int_null(membership); } if (csize) { /* The size of the single component is the same as the vertex count. */ @@ -488,11 +493,12 @@ igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, } static igraph_error_t igraph_i_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); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t 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; + igraph_bitset_t already_added; + igraph_vector_int_t neis; + igraph_dqueue_int_t q; /* By convention, the null graph is not considered connected. * See https://github.com/igraph/igraph/issues/1538 */ @@ -507,56 +513,53 @@ static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_b goto exit; } - 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_BITSET_INIT_FINALLY(&already_added, no_of_nodes); IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); /* Try to find at least two components */ - already_added[0] = true; + IGRAPH_BIT_SET(already_added, 0); IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); added_count = 1; - while ( !igraph_dqueue_int_empty(&q)) { + while (! igraph_dqueue_int_empty(&q)) { IGRAPH_ALLOW_INTERRUPTION(); - igraph_integer_t actnode = igraph_dqueue_int_pop(&q); + const igraph_integer_t actnode = igraph_dqueue_int_pop(&q); IGRAPH_CHECK(igraph_neighbors(graph, &neis, actnode, IGRAPH_ALL)); - igraph_integer_t nei_count = igraph_vector_int_size(&neis); + const igraph_integer_t nei_count = igraph_vector_int_size(&neis); for (igraph_integer_t i = 0; i < nei_count; i++) { - igraph_integer_t neighbor = VECTOR(neis)[i]; - if (already_added[neighbor]) { + const igraph_integer_t neighbor = VECTOR(neis)[i]; + if (IGRAPH_BIT_TEST(already_added, neighbor)) { continue; } IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); added_count++; - already_added[neighbor] = true; + IGRAPH_BIT_SET(already_added, neighbor); 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); - break; + goto done; } } } +done: /* Connected? */ *res = (added_count == no_of_nodes); - IGRAPH_FREE(already_added); + igraph_bitset_destroy(&already_added); igraph_dqueue_int_destroy(&q); igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); exit: igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res); - if (igraph_is_directed(graph) && *res == 0) { + if (igraph_is_directed(graph) && !(*res)) { /* 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_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res); @@ -618,11 +621,11 @@ static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, * \param maxcompno The maximum number of components to return. The * first \p maxcompno components will be returned (which hold at * least \p minelements vertices, see the next parameter), the - * others will be ignored. Supply -1 here if you don't want to limit - * the number of components. + * others will be ignored. Supply -1 here if you don't + * want to limit the number of components. * \param minelements The minimum number of vertices a component * should contain in order to place it in the \p components - * vector. Eg. supply 2 here to ignore isolated vertices. + * vector. For example, supplying 2 here ignored isolated vertices. * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory * to perform the operation. * @@ -653,7 +656,7 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, 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_bitset_t already_added; igraph_dqueue_int_t q; igraph_vector_int_t verts; igraph_vector_int_t neis; @@ -669,10 +672,7 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, 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, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for decomponsing graph into connected components."); - IGRAPH_FINALLY(igraph_free, already_added); - + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); @@ -686,7 +686,7 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, then switch to next node that has not been added already */ for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) { - if (already_added[actstart]) { + if (IGRAPH_BIT_TEST(already_added, actstart)) { continue; } IGRAPH_ALLOW_INTERRUPTION(); @@ -694,7 +694,7 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, igraph_vector_int_clear(&verts); /* add the node itself */ - already_added[actstart] = true; + IGRAPH_BIT_SET(already_added, actstart); IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart)); IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart)); @@ -707,11 +707,11 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, /* iterate over the neighbors */ for (i = 0; i < nei_count; i++) { igraph_integer_t neighbor = VECTOR(neis)[i]; - if (already_added[neighbor]) { + if (IGRAPH_BIT_TEST(already_added, neighbor)) { continue; } /* add neighbor */ - already_added[neighbor] = true; + IGRAPH_BIT_SET(already_added, neighbor); /* recursion: append neighbor to the queues */ IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); @@ -745,7 +745,7 @@ static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, igraph_vector_int_destroy(&neis); igraph_vector_int_destroy(&verts); igraph_dqueue_int_destroy(&q); - IGRAPH_FREE(already_added); + igraph_bitset_destroy(&already_added); IGRAPH_FINALLY_CLEAN(5); return IGRAPH_SUCCESS; @@ -1025,16 +1025,16 @@ igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_i * biconnected. Use \ref igraph_is_biconnected() for this purpose. * * \param graph The input graph. It will be treated as undirected. - * \param no If not a NULL pointer, the number of biconnected components will + * \param no If not a \c NULL pointer, the number of biconnected components will * be stored here. - * \param tree_edges If not a NULL pointer, then the found components + * \param tree_edges If not a \c 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. - * \param component_edges If not a NULL pointer, then the edges of the + * \param component_edges If not a \c NULL pointer, then the edges of the * biconnected components are stored here, in the same form as for * \c tree_edges. - * \param components If not a NULL pointer, then the vertices of the + * \param components If not a \c NULL pointer, then the vertices of the * biconnected components are stored here, in the same format as * for the previous two arguments. * \param articulation_points If not a NULL pointer, then the @@ -1066,7 +1066,7 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, 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_bitset_t found; igraph_vector_int_t *adjedges; igraph_stack_int_t path; igraph_stack_int_t edgestack; @@ -1079,7 +1079,7 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, 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_BITSET_INIT_FINALLY(&found, no_of_nodes); IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100); @@ -1157,10 +1157,10 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, } /* Check for articulation point */ if (VECTOR(low)[act] >= VECTOR(num)[prev]) { - if (articulation_points && !VECTOR(found)[prev] + if (articulation_points && !IGRAPH_BIT_TEST(found, prev) && prev != i /* the root */) { IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev)); - VECTOR(found)[prev] = true; + IGRAPH_BIT_SET(found, prev); } if (no) { *no += 1; @@ -1244,7 +1244,7 @@ igraph_error_t igraph_biconnected_components(const igraph_t *graph, igraph_inclist_destroy(&inclist); igraph_stack_int_destroy(&edgestack); igraph_stack_int_destroy(&path); - igraph_vector_bool_destroy(&found); + igraph_bitset_destroy(&found); igraph_vector_int_destroy(&low); igraph_vector_int_destroy(&num); igraph_vector_int_destroy(&nextptr); @@ -1436,7 +1436,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_inclist_t il; - igraph_vector_bool_t visited; + igraph_bitset_t visited; igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */ igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */ igraph_vector_int_t incoming_edge; @@ -1447,7 +1447,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge IGRAPH_FINALLY(igraph_inclist_destroy, &il); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); @@ -1461,7 +1461,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge time = 0; for (igraph_integer_t start = 0; start < no_of_nodes; ++start) { - if (! VECTOR(visited)[start]) { + if (! IGRAPH_BIT_TEST(visited, start)) { /* Perform a DFS from 'start'. * The top of the su stack is u, the vertex currently being visited. * The top of the si stack is i, the index of u's neighbour that will @@ -1477,7 +1477,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge if (i == 0) { /* We are at the first step of visiting vertex u. */ - VECTOR(visited)[u] = true; + IGRAPH_BIT_SET(visited, u); time += 1; @@ -1494,7 +1494,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge igraph_integer_t edge = VECTOR(*incedges)[i]; igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); - if (! VECTOR(visited)[v]) { + if (! IGRAPH_BIT_TEST(visited, v)) { VECTOR(incoming_edge)[v] = edge; IGRAPH_CHECK(igraph_stack_int_push(&su, v)); @@ -1525,7 +1525,7 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge igraph_vector_int_destroy(&incoming_edge); igraph_vector_int_destroy(&low); igraph_vector_int_destroy(&vis); - igraph_vector_bool_destroy(&visited); + igraph_bitset_destroy(&visited); igraph_inclist_destroy(&il); IGRAPH_FINALLY_CLEAN(7); @@ -1535,11 +1535,15 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge /** * \ingroup structural * \function igraph_subcomponent - * \brief The vertices in the same component as a given vertex. + * \brief The vertices reachable from a given vertex. + * + * This function returns the set of vertices reachable from a specified + * vertex. In undirected graphs, this is simple the set of vertices within + * the same component. * * \param graph The graph object. - * \param res The result, vector with the IDs of the vertices in the - * same component. + * \param res The result, vector with the IDs of the vertices reachable + * from \p vertex. * \param vertex The id of the vertex of which the component is * searched. * \param mode Type of the component for directed graphs, possible @@ -1572,7 +1576,9 @@ igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridge * edges in the graph. * * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only - * a given set of vertices and the edges between them. + * a given set of vertices and the edges between them; + * \ref igraph_reachability() to efficiently compute the reachable set from \em all + * vertices. */ igraph_error_t igraph_subcomponent( const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vertex, @@ -1581,7 +1587,7 @@ igraph_error_t igraph_subcomponent( igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; - bool *already_added; + igraph_bitset_t already_added; igraph_integer_t i, vsize; igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; @@ -1593,18 +1599,15 @@ 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."); - IGRAPH_FINALLY(igraph_free, already_added); - igraph_vector_int_clear(res); + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); IGRAPH_DQUEUE_INT_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_BIT_SET(already_added, vertex); while (!igraph_dqueue_int_empty(&q)) { igraph_integer_t actnode = igraph_dqueue_int_pop(&q); @@ -1616,10 +1619,10 @@ igraph_error_t igraph_subcomponent( for (i = 0; i < vsize; i++) { igraph_integer_t neighbor = VECTOR(tmp)[i]; - if (already_added[neighbor]) { + if (IGRAPH_BIT_TEST(already_added, neighbor)) { continue; } - already_added[neighbor] = true; + IGRAPH_BIT_SET(already_added, neighbor); IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor)); IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); } @@ -1627,7 +1630,7 @@ igraph_error_t igraph_subcomponent( igraph_dqueue_int_destroy(&q); igraph_vector_int_destroy(&tmp); - IGRAPH_FREE(already_added); + igraph_bitset_destroy(&already_added); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; diff --git a/src/connectivity/reachability.c b/src/connectivity/reachability.c new file mode 100644 index 0000000000..1b5560f36a --- /dev/null +++ b/src/connectivity/reachability.c @@ -0,0 +1,263 @@ +/* + IGraph library. + Copyright (C) 2024 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_reachability.h" + +#include "igraph_adjlist.h" +#include "igraph_bitset_list.h" +#include "igraph_components.h" +#include "igraph_constructors.h" +#include "igraph_interface.h" + + +/** + * \ingroup structural + * \function igraph_reachability + * \brief Calculates which vertices are reachable from each vertex in the graph. + * + * \experimental + * + * The resulting list will contain one bitset for each strongly connected component. + * The bitset for component i will have its j-th bit set, if vertex j is reachable + * from some vertex in component i in 0 or more steps. + * In particular, a vertex is always reachable from itself. + * + * \param graph The graph object to analyze. + * \param membership Pointer to an integer vector. For every vertex, + * the ID of its component is given. The vector will be resized as needed. + * This parameter must not be \c NULL. + * \param csize Pointer to an integer vector or \c NULL. For every component, it + * gives its size (vertex count), the order being defined by the component + * IDs. The vector will be resized as needed. + * \param no_of_components Pointer to an integer or \c NULL. The number of + * components will be stored here. + * \param reach A list of bitsets representing the result. It will be resized + * as needed. reach[membership[u]][v] is set to \c true if + * vertex \c v is reachable from vertex \c u. + * \param mode In directed graphs, controls the treatment of edge directions. + * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed + * by traversing edges along their direction. With \c IGRAPH_IN, edges are + * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions + * are ignored and the graph is treated as undirected. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components() to find the connnected components + * of a graph; \ref igraph_count_reachable() to count how many vertices + * are reachable from each vertex; \ref igraph_subcomponent() to find + * which vertices are rechable from a single vertex. + * + * Time complexity: O(|C||V|/w + |V| + |E|), where + * |C| is the number of strongly connected components (at most |V|), + * |V| is the number of vertices, and + * |E| is the number of edges respectively, + * and w is the bit width of \type igraph_integer_t, typically the + * word size of the machine (32 or 64). + */ + +igraph_error_t igraph_reachability( + const igraph_t *graph, + igraph_vector_int_t *membership, + igraph_vector_int_t *csize, + igraph_integer_t *no_of_components, + igraph_bitset_list_t *reach, + igraph_neimode_t mode) { + + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_comps; + igraph_adjlist_t adjlist, dag; + + if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { + IGRAPH_ERROR("Invalid mode for reachability.", IGRAPH_EINVMODE); + } + + if (! igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + IGRAPH_CHECK(igraph_connected_components(graph, + membership, csize, &no_of_comps, + mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG)); + + if (no_of_components) { + *no_of_components = no_of_comps; + } + + IGRAPH_CHECK(igraph_bitset_list_resize(reach, no_of_comps)); + + for (igraph_integer_t comp = 0; comp < no_of_comps; comp++) { + IGRAPH_CHECK(igraph_bitset_resize(igraph_bitset_list_get_ptr(reach, comp), no_of_nodes)); + } + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + IGRAPH_BIT_SET(*igraph_bitset_list_get_ptr(reach, VECTOR(*membership)[v]), v); + } + + if (mode == IGRAPH_ALL) { + return IGRAPH_SUCCESS; + } + + IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); + + IGRAPH_CHECK(igraph_adjlist_init_empty(&dag, no_of_comps)); + IGRAPH_FINALLY(igraph_adjlist_destroy, &dag); + + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { + const igraph_vector_int_t *neighbours = igraph_adjlist_get(&adjlist, v); + igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, VECTOR(*membership)[v]); + const igraph_integer_t n = igraph_vector_int_size(neighbours); + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t w = VECTOR(*neighbours)[i]; + if (VECTOR(*membership)[v] != VECTOR(*membership)[w]) { + IGRAPH_CHECK(igraph_vector_int_push_back(dag_neighbours, VECTOR(*membership)[w])); + } + } + } + + /* Iterate through strongly connected components in reverser topological order, + * exploiting the fact that they are indexed in topological order. */ + for (igraph_integer_t i = 0; i < no_of_comps; i++) { + const igraph_integer_t comp = mode == IGRAPH_IN ? i : no_of_comps - i - 1; + const igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, comp); + igraph_bitset_t *from_bitset = igraph_bitset_list_get_ptr(reach, comp); + const igraph_integer_t n = igraph_vector_int_size(dag_neighbours); + for (igraph_integer_t j = 0; j < n; j++) { + const igraph_bitset_t *to_bitset = igraph_bitset_list_get_ptr(reach, VECTOR(*dag_neighbours)[j]); + igraph_bitset_or(from_bitset, from_bitset, to_bitset); + } + } + + igraph_adjlist_destroy(&adjlist); + igraph_adjlist_destroy(&dag); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_count_reachable + * \brief The number of vertices reachable from each vertex in the graph. + * + * \experimental + * + * \param graph The graph object to analyze. + * \param counts Integer vector. counts[v] will store the number + * of vertices reachable from vertex \c v, including \c v itself. + * \param mode In directed graphs, controls the treatment of edge directions. + * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed + * by traversing edges along their direction. With \c IGRAPH_IN, edges are + * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions + * are ignored and the graph is treated as undirected. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components(), \ref igraph_transitive_closure() + * + * Time complexity: O(|C||V|/w + |V| + |E|), where + * |C| is the number of strongly connected components (at most |V|), + * |V| is the number of vertices, and + * |E| is the number of edges respectively, + * and w is the bit width of \type igraph_integer_t, typically the + * word size of the machine (32 or 64). + */ + +igraph_error_t igraph_count_reachable(const igraph_t *graph, + igraph_vector_int_t *counts, + igraph_neimode_t mode) { + + igraph_vector_int_t membership; + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_bitset_list_t reach; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); + + IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, mode)); + + IGRAPH_CHECK(igraph_vector_int_resize(counts, igraph_vcount(graph))); + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + VECTOR(*counts)[i] = igraph_bitset_popcount(igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[i])); + } + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + return IGRAPH_SUCCESS; +} + + +/** + * \ingroup structural + * \function igraph_transitive_closure + * \brief Computes the transitive closure of a graph. + * + * \experimental + * + * The resulting graph will have an edge from vertex \c i to vertex \c j + * if \c j is reachable from \c i. + * + * \param graph The graph object to analyze. + * \param closure The resulting graph representing the transitive closure. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory + * to perform the operation. + * + * \sa \ref igraph_connected_components(), \ref igraph_count_reachable() + * + * Time complexity: O(|V|^2 + |E|), where + * |V| is the number of vertices, and + * |E| is the number of edges, respectively. + */ +igraph_error_t igraph_transitive_closure(const igraph_t *graph, igraph_t *closure) { + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_bool_t directed = igraph_is_directed(graph); + igraph_vector_int_t membership, edges; + igraph_bitset_list_t reach; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); + IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); + + IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, IGRAPH_OUT)); + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + for (igraph_integer_t u = 0; u < no_of_nodes; u++) { + const igraph_bitset_t *row = igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[u]); + for (igraph_integer_t v = directed ? 0 : u + 1; v < no_of_nodes; v++) { + if (u != v && IGRAPH_BIT_TEST(*row, v)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); + } + } + } + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&membership); + IGRAPH_FINALLY_CLEAN(2); + + IGRAPH_CHECK(igraph_create(closure, &edges, no_of_nodes, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/connectivity/separators.c b/src/connectivity/separators.c index 962d5259ff..ece42ecfc1 100644 --- a/src/connectivity/separators.c +++ b/src/connectivity/separators.c @@ -795,7 +795,7 @@ igraph_error_t igraph_minimum_size_separators( /* 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, /* remove_multiple */ true, /* remove_loops */ true, NULL)); /* ---------------------------------------------------------------- */ /* 2 Find k vertices with the largest degrees (x1;..,xk). Check diff --git a/src/constructors/adjacency.c b/src/constructors/adjacency.c index 459403da24..cecc46cdd7 100644 --- a/src/constructors/adjacency.c +++ b/src/constructors/adjacency.c @@ -322,7 +322,7 @@ igraph_error_t igraph_adjacency( /* Some checks */ if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { - IGRAPH_ERROR("Adjacency matrix is non-square.", IGRAPH_NONSQUARE); + IGRAPH_ERROR("Adjacency matrices must be square.", IGRAPH_NONSQUARE); } if (no_of_nodes != 0 && igraph_matrix_min(adjmatrix) < 0) { @@ -512,7 +512,7 @@ static igraph_error_t igraph_i_weighted_adjacency_max( for (j = i + 1; j < no_of_nodes; j++) { M1 = MATRIX(*adjmatrix, i, j); M2 = MATRIX(*adjmatrix, j, i); - if (M1 < M2) { + if (M1 < M2 || isnan(M2)) { M1 = M2; } if (M1 != 0.0) { @@ -531,11 +531,21 @@ static igraph_error_t igraph_i_weighted_adjacency_undirected( 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 - ); + /* We do not use igraph_matrix_is_symmetric() for this check, as we need to + * allow symmetric matrices with NaN values. igraph_matrix_is_symmetric() + * returns false for these as NaN != NaN. */ + igraph_integer_t n = igraph_matrix_nrow(adjmatrix); + for (igraph_integer_t i=0; i < n; i++) { + for (igraph_integer_t j=0; j < i; j++) { + igraph_real_t a1 = MATRIX(*adjmatrix, i, j); + igraph_real_t a2 = MATRIX(*adjmatrix, j, i); + if (a1 != a2 && ! (isnan(a1) && isnan(a2))) { + IGRAPH_ERROR( + "Adjacency matrix should be symmetric to produce an undirected graph.", + IGRAPH_EINVAL + ); + } + } } return igraph_i_weighted_adjacency_max(adjmatrix, edges, weights, loops); } @@ -639,7 +649,7 @@ static igraph_error_t igraph_i_weighted_adjacency_min( for (j = i + 1; j < no_of_nodes; j++) { M1 = MATRIX(*adjmatrix, i, j); M2 = MATRIX(*adjmatrix, j, i); - if (M1 > M2) { + if (M1 > M2 || isnan(M2)) { M1 = M2; } if (M1 != 0.0) { @@ -737,7 +747,7 @@ igraph_error_t igraph_weighted_adjacency( /* Some checks */ if (igraph_matrix_nrow(adjmatrix) != igraph_matrix_ncol(adjmatrix)) { - IGRAPH_ERROR("Non-square matrix", IGRAPH_NONSQUARE); + IGRAPH_ERROR("Adjacency matrices must be square.", IGRAPH_NONSQUARE); } IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); @@ -814,7 +824,7 @@ igraph_error_t igraph_weighted_adjacency( * in-adjacency list * \param duplicate Logical, for undirected graphs this specified * whether each edge is included twice, in the vectors of - * both adjacent vertices. If this is false (0), then it is + * both adjacent vertices. If this is \c false, then it is * assumed that every edge is included only once. This argument * is ignored for directed graphs. * \return Error code. @@ -827,16 +837,15 @@ igraph_error_t igraph_weighted_adjacency( igraph_error_t 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); + const igraph_integer_t no_of_nodes = igraph_adjlist_size(adjlist); igraph_integer_t no_of_edges = 0; - igraph_integer_t i; igraph_vector_int_t edges; igraph_integer_t edgeptr = 0; duplicate = duplicate && (mode == IGRAPH_ALL); /* only duplicate if undirected */ - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { no_of_edges += igraph_vector_int_size(igraph_adjlist_get(adjlist, i)); } @@ -846,12 +855,12 @@ igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2 * no_of_edges); - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t 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); + const igraph_integer_t n = igraph_vector_int_size(neis); igraph_integer_t loops = 0; - for (j = 0; j < n; j++) { + for (igraph_integer_t j = 0; j < n; j++) { igraph_integer_t nei = VECTOR(*neis)[j]; if (nei == i) { loops++; @@ -859,7 +868,7 @@ igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, if (! duplicate || nei > i) { if (edgeptr + 2 > 2 * no_of_edges) { IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" - " duplicated edges for an undirected graph", IGRAPH_EINVAL); + " duplicated edges for an undirected graph.", IGRAPH_EINVAL); } if (mode == IGRAPH_IN) { VECTOR(edges)[edgeptr++] = nei; @@ -877,18 +886,19 @@ igraph_error_t igraph_adjlist(igraph_t *graph, const igraph_adjlist_t *adjlist, } if (edgeptr + 2 * loops > 2 * no_of_edges) { IGRAPH_ERROR("Invalid adjacency list, most probably not correctly" - " duplicated edges for an undirected graph", IGRAPH_EINVAL); + " duplicated edges for an undirected graph.", IGRAPH_EINVAL); } - for (j = 0; j < loops; j++) { + for (igraph_integer_t j = 0; j < loops; j++) { VECTOR(edges)[edgeptr++] = i; VECTOR(edges)[edgeptr++] = i; } } - if (mode == IGRAPH_ALL) - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 0)); - else - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, 1)); + if (mode == IGRAPH_ALL) { + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_UNDIRECTED)); + } else { + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, IGRAPH_DIRECTED)); + } igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); diff --git a/src/constructors/atlas.c b/src/constructors/atlas.c index 6f2dc2681e..4fe2db8205 100644 --- a/src/constructors/atlas.c +++ b/src/constructors/atlas.c @@ -25,27 +25,32 @@ * \brief Create a small graph from the \quote Graph Atlas \endquote. * * - * The number of the graph is given as a parameter. - * The graphs are listed: \olist - * \oli in increasing order of number of nodes; - * \oli for a fixed number of nodes, in increasing order of the + * The graph atlas contains all simple undirected unlabeled graphs on between + * 0 and 7 vertices. The number of the graph is given as a parameter. + * The graphs are listed: + * \olist + * \oli in increasing order of number of vertices; + * \oli for a fixed number of vertices, in increasing order of the * number of edges; - * \oli for fixed numbers of nodes and edges, in increasing - * order of the degree sequence, for example 111223 < 112222; + * \oli for fixed numbers of vertices and edges, in laxicographically + * increasing order of the degree sequence, for example + * 111223 < 112222; * \oli for fixed degree sequence, in increasing number of * automorphisms. * \endolist * * * The data was converted from the NetworkX software package, - * see http://networkx.github.io . + * see https://networkx.org/. * * * See \emb An Atlas of Graphs \eme by Ronald C. Read and Robin J. Wilson, * Oxford University Press, 1998. * * \param graph Pointer to an uninitialized graph object. - * \param number The number of the graph to generate. + * \param number The number of the graph to generate. Must be between 0 and + * 1252 (inclusive). Graphs on 0-7 vertices start at numbers 0, 1, 2, 4, + * 8, 19, 53, and 209, respectively. * * Added in version 0.2. * diff --git a/src/constructors/full.c b/src/constructors/full.c index 99c54beca6..67b7de4939 100644 --- a/src/constructors/full.c +++ b/src/constructors/full.c @@ -30,19 +30,12 @@ /** * \ingroup generators * \function igraph_full - * \brief Creates a full graph (directed or undirected, with or without loops). + * \brief Creates a full graph (complete graph). * - * - * In a full graph every possible edge is present, every vertex is - * connected to every other vertex. A full graph in \c igraph should be - * distinguished from the concept of complete graphs as used in graph theory. - * If n is a positive integer, then the complete graph K_n on n vertices is - * the undirected simple graph with the following property. For any distinct - * pair (u,v) of vertices in K_n, uv (or equivalently vu) is an edge of K_n. - * In \c igraph, a full graph on n vertices can be K_n, a directed version of - * K_n, or K_n with at least one loop edge. In any case, if F is a full graph - * on n vertices as generated by \c igraph, then K_n is a subgraph of the - * undirected version of F. + * In a full graph every possible edge is present: every vertex is + * connected to every other vertex. \a igraph generalizes the usual + * concept of complete graphs in graph theory to graphs with self-loops + * as well as to directed graphs. * * \param graph Pointer to an uninitialized graph object. * \param n Integer, the number of vertices in the graph. @@ -51,12 +44,8 @@ * \return Error code: * \c IGRAPH_EINVAL: invalid number of vertices. * - * Time complexity: O(|V|+|E|), - * |V| is the number of vertices, - * |E| the number of edges in the - * graph. Of course this is the same as - * O(|E|)=O(|V||V|) - * here. + * Time complexity: O(|V|^2) = O(|E|), + * where |V| is the number of vertices and |E| is the number of edges. * * \sa \ref igraph_square_lattice(), \ref igraph_star(), \ref igraph_kary_tree() * for creating other regular structures. @@ -137,13 +126,13 @@ igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t di /** * \function igraph_full_multipartite - * \brief Create a full multipartite graph. + * \brief Creates 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 + * \param graph Pointer to an uninitialized graph 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. @@ -152,15 +141,16 @@ igraph_error_t igraph_full(igraph_t *graph, igraph_integer_t n, igraph_bool_t di * \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. + * of low-index vertices to high-index vertices; if + * \c IGRAPH_IN, then the opposite direction is realized; + * \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. + * \sa \ref igraph_full_bipartite() for complete bipartite graphs, + * \ref igraph_turan() for Turán graphs. */ igraph_error_t igraph_full_multipartite(igraph_t *graph, igraph_vector_int_t *types, @@ -259,7 +249,7 @@ igraph_error_t igraph_full_multipartite(igraph_t *graph, /** * \function igraph_turan - * \brief Create a Turán graph. + * \brief Creates 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. @@ -337,20 +327,24 @@ igraph_error_t igraph_turan(igraph_t *graph, /** * \function igraph_full_citation - * \brief Creates a full citation graph. + * \brief Creates a full citation graph (a complete directed acyclic graph). * * This is a directed graph, where every i->j edge is * present if and only if j<i. - * If the \c directed argument is zero then an undirected graph is - * created, and it is just a full graph. + * If the \p directed argument is false then an undirected graph is + * created, and it is just a complete graph. + * * \param graph Pointer to an uninitialized graph object, the result * is stored here. * \param n The number of vertices. - * \param directed Whether to created a directed graph. If zero an + * \param directed Whether to created a directed graph. If false an * undirected graph is created. * \return Error code. * - * Time complexity: O(|V|^2), as we have many edges. + * \sa \ref igraph_full() + * + * Time complexity: O(|V|^2) = O(|E|), + * where |V| is the number of vertices and |E| is the number of edges. */ igraph_error_t igraph_full_citation(igraph_t *graph, igraph_integer_t n, igraph_bool_t directed) { diff --git a/src/constructors/lcf.c b/src/constructors/lcf.c index 76534632d6..329255df51 100644 --- a/src/constructors/lcf.c +++ b/src/constructors/lcf.c @@ -104,7 +104,7 @@ igraph_error_t igraph_lcf_vector(igraph_t *graph, igraph_integer_t n, * number of vertices in the graph, a list of shifts giving additional * edges to a cycle backbone, and another integer giving how many times * the shifts should be performed. See - * http://mathworld.wolfram.com/LCFNotation.html for details. + * https://mathworld.wolfram.com/LCFNotation.html for details. * * \param graph Pointer to an uninitialized graph object. * \param n Integer, the number of vertices in the graph. diff --git a/src/constructors/regular.c b/src/constructors/regular.c index b34726ded7..858b6d8bc1 100644 --- a/src/constructors/regular.c +++ b/src/constructors/regular.c @@ -504,7 +504,7 @@ igraph_error_t igraph_square_lattice( * * Time complexity: O(|V|), the number of vertices in the graph. * - * \sa \ref igraph_lattice() for generating more general lattices. + * \sa \ref igraph_square_lattice() for generating more general lattices. * * \example examples/simple/igraph_ring.c */ @@ -602,8 +602,9 @@ igraph_error_t igraph_ring(igraph_t *graph, igraph_integer_t n, igraph_bool_t di * Time complexity: O(|V|+|E|), the * number of vertices plus the number of edges in the graph. * - * \sa \ref igraph_lattice(), \ref igraph_star() for creating other regular - * structures; \ref igraph_from_prufer() for creating arbitrary trees; + * \sa \ref igraph_regular_tree(), \ref igraph_symmetric_tree() and \ref igraph_star() + * for creating other regular structures; \ref igraph_from_prufer() and + * \ref igraph_tree_from_parent_vector() for creating arbitrary trees; * \ref igraph_tree_game() for uniform random sampling of trees. * * \example examples/simple/igraph_kary_tree.c @@ -931,3 +932,72 @@ igraph_error_t igraph_extended_chordal_ring( IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } + +/** + * \function igraph_hypercube + * \brief The n-dimensional hypercube graph. + * + * \experimental + * + * The hypercube graph \c Q_n has 2^n vertices and + * 2^(n-1) n edges. Two vertices are connected when the binary + * representations of their zero-based vertex IDs differs in precisely one bit. + * + * \param graph An uninitialized graph object. + * \param n The dimension of the hypercube graph. + * \param directed Whether the graph should be directed. Edges will point + * from lower index vertices towards higher index ones. + * \return Error code. + * + * \sa \ref igraph_square_lattice() + * + * Time complexity: O(2^n) + */ +igraph_error_t igraph_hypercube(igraph_t *graph, + igraph_integer_t n, igraph_bool_t directed) { + + /* An n-dimensional hypercube graph has 2^n vertices and 2^(n-1)*n edges. + * The maximum possible dimension is calculated with the assumption that + * the largest possible edge count is no more than half IGRAPH_INTEGER_MAX, + * which is in fact the current limit. */ + + const igraph_integer_t maxn = + (IGRAPH_INTEGER_SIZE - 1) - (igraph_integer_t) ceil(log2(IGRAPH_INTEGER_SIZE)); + + if (n > maxn) { + IGRAPH_ERRORF("The requested hypercube graph dimension (%" IGRAPH_PRId + ") is too high. It must be no greater than %" IGRAPH_PRId ".", + IGRAPH_EINVAL, n, maxn); + } + + /* Integer overflow is no longer a concern after the above check. */ + + const igraph_integer_t vcount = (igraph_integer_t) 1 << n; + const igraph_integer_t ecount = ((igraph_integer_t) 1 << (n-1)) * n; + igraph_vector_int_t edges; + igraph_integer_t p; + int iter = 0; + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 2*ecount); + + p = 0; + for (igraph_integer_t v=0; v < vcount; v++) { + igraph_integer_t bit = 1; + for (igraph_integer_t i=0; i < n; i++) { + const igraph_integer_t u = v ^ bit; + if (v < u) { + VECTOR(edges)[p++] = v; + VECTOR(edges)[p++] = u; + } + bit <<= 1; + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 16); + } + + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, directed)); + + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} diff --git a/src/constructors/trees.c b/src/constructors/trees.c index d747321b6d..16b625ccef 100644 --- a/src/constructors/trees.c +++ b/src/constructors/trees.c @@ -49,7 +49,7 @@ * * \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 + * 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: diff --git a/src/core/bitset.c b/src/core/bitset.c new file mode 100644 index 0000000000..b581ab8391 --- /dev/null +++ b/src/core/bitset.c @@ -0,0 +1,834 @@ +/* + IGraph library. + Copyright (C) 2024 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 "string.h" + +#include "igraph_bitset.h" +#include "igraph_memory.h" + +igraph_integer_t igraph_i_ctz32(igraph_uint_t x) { +#ifdef HAVE__BITSCANFORWARD + unsigned long index; + return _BitScanForward(&index, x) ? index : 32; +#else + for (igraph_integer_t i = 0; i < 32; ++i) { + if (IGRAPH_BIT_MASK(i) & x) { + return i; + } + } + return 32; +#endif +} + +igraph_integer_t igraph_i_clz32(igraph_uint_t x) { +#ifdef HAVE_BITSCANREVERSE + unsigned long index; + return _BitScanReverse(&index, x) ? 31 - index : 32; +#else + for (igraph_integer_t i = 31; i >= 0; --i) { + if (IGRAPH_BIT_MASK(i) & x) { + return 31 - i; + } + } + return 32; +#endif +} + +igraph_integer_t igraph_i_popcnt(igraph_uint_t x) { + igraph_integer_t result = 0; + while (x) { + result++; + x = x & (x - 1); + } + return result; +} + +/* Fallbacks for 64-bit word (and igraph_integer_t/igraph_uint_t) size */ +#if IGRAPH_INTEGER_SIZE == 64 +igraph_integer_t igraph_i_ctz64(igraph_uint_t x) { +#ifdef HAVE_BITSCANFORWARD64 + unsigned long index; + return _BitScanForward64(&index, x) ? index : 64; +#else + for (igraph_integer_t i = 0; i < 64; ++i) { + if (IGRAPH_BIT_MASK(i) & x) { + return i; + } + } + return 64; +#endif +} + +igraph_integer_t igraph_i_clz64(igraph_uint_t x) { +#ifdef HAVE_BITSCANREVERSE64 + unsigned long index; + return _BitScanReverse64(&index, x) ? 63 - index : 64; +#else + for (igraph_integer_t i = 63; i >= 0; --i) { + if (IGRAPH_BIT_MASK(i) & x) { + return 63 - i; + } + } + return 64; +#endif +} +#endif /* IGRAPH_INTEGER_SIZE == 64 */ + +/** + * \ingroup bitset + * \section about_igraph_bitset_t_objects About \type igraph_bitset_t objects + * + * The \type igraph_bitset_t data type is a simple and efficient + * interface to arrays containing boolean values. It is similar to + * the \type bitset template in the C++ standard library, although the main + * difference being the C++ version's size is initialized at compile time. + * + * The \type igraph_bitset_t type and use O(n/w) space + * to store n elements, where w is the bit width of \type igraph_integer_t, + * the integer type used throughout the library (either 32 or 64). + * Sometimes they use more, this is because bitsets can + * shrink, but even if they shrink, the current implementation does not free a + * single bit of memory. + * + * The elements in an \type igraph_bitset_t object and its variants are + * indexed from zero, we follow the usual C convention here. Bitsets are indexed + * from right to left, meaning index 0 is the least significant bit and index + * n - 1 is the most significant bit. + * + * The elements of a bitset always occupy a single block of + * memory, the starting address of this memory block can be queried + * with the \ref VECTOR() macro. This way, bitset objects can be used + * with standard mathematical libraries, like the GNU Scientific + * Library. + * + * Note that while the interface of bitset functions is similar to + * igraph's vector functions, there is one major difference: bitset functions + * such as \ref igraph_bitset_and() do not verify that that sizes of input + * parameters are compatible, and do not automatically resize the output + * parameter. Doing so is the responsibility of the user. + */ + +/** + * \ingroup bitset + * \section igraph_bitset_constructors_and_destructors Constructors and + * destructors + * + * \type igraph_bitset_t objects have to be initialized before using + * them, this is analogous to calling a constructor on them. There are two + * \type igraph_bitset_t constructors, for your convenience. + * \ref igraph_bitset_init() is the basic constructor, it + * creates a bitset of the given length, filled with zeros. + * \ref igraph_bitset_init_copy() creates a new identical copy + * of an already existing and initialized bitset. + * + * If a \type igraph_bitset_t object is not needed any more, it + * should be destroyed to free its allocated memory by calling the + * \type igraph_bitset_t destructor, \ref igraph_bitset_destroy(). + */ + +/** + * \ingroup bitset + * \function igraph_bitset_init + * \brief Initializes a bitset object (constructor). + * + * \experimental + * + * + * Every bitset needs to be initialized before it can be used, and + * there are a number of initialization functions or otherwise called + * constructors. This function constructs a bitset of the given size and + * initializes each entry to 0. + * + * + * Every bitset object initialized by this function should be + * destroyed (ie. the memory allocated for it should be freed) when it + * is not needed anymore, the \ref igraph_bitset_destroy() function is + * responsible for this. + * + * \param bitset Pointer to a not yet initialized bitset object. + * \param size The size of the bitset. + * \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/w) elements, + * n is the number of elements. + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_init(igraph_bitset_t *bitset, igraph_integer_t size) { + igraph_integer_t alloc_size = IGRAPH_BIT_NSLOTS(size); + bitset->stor_begin = IGRAPH_CALLOC(alloc_size, igraph_uint_t); + IGRAPH_CHECK_OOM(bitset->stor_begin, "Cannot initialize bitset"); + bitset->size = size; + bitset->stor_end = bitset->stor_begin + alloc_size; + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_destroy + * \brief Destroys a bitset object. + * + * \experimental + * + * All bitsets initialized by \ref igraph_bitset_init() should be properly + * destroyed by this function. A destroyed bitset needs to be + * reinitialized by \ref igraph_bitset_init() or + * another constructor. + * + * \param bitset Pointer to the (previously initialized) bitset object to + * destroy. + * + * Time complexity: operating system dependent. + */ + +void igraph_bitset_destroy(igraph_bitset_t *bitset) { + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_FREE(bitset->stor_begin); + bitset->size = 0; +} + +/** + * \ingroup bitset + * \function igraph_bitset_init_copy + * \brief Initializes a bitset from another bitset object (constructor). + * + * \experimental + * + * The contents of the existing bitset object will be copied to + * the new one. + * + * \param dest Pointer to a not yet initialized bitset object. + * \param src The original bitset object to copy. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, usually + * O(n/w), + * n is the size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_init_copy(igraph_bitset_t *dest, const igraph_bitset_t *src) { + IGRAPH_ASSERT(src != NULL); + IGRAPH_ASSERT(src->stor_begin != NULL); + IGRAPH_CHECK(igraph_bitset_init(dest, src->size)); + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src)[i]; + } + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_capacity + * \brief Returns the allocated capacity of the bitset. + * + * \experimental + * + * Note that this might be different from the size of the bitset (as + * queried by \ref igraph_bitset_size()), and specifies how many elements + * the bitset can hold, without reallocation. + * + * \param bitset Pointer to the (previously initialized) bitset object + * to query. + * \return The allocated capacity. + * + * \sa \ref igraph_bitset_size(). + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_bitset_capacity(const igraph_bitset_t *bitset) { + return IGRAPH_INTEGER_SIZE * (bitset->stor_end - bitset->stor_begin); +} + +/** + * \ingroup bitset + * \function igraph_bitset_size + * \brief Returns the length of the bitset. + * + * \experimental + * + * \param bitset The bitset object + * \return The size of the bitset. + * + * Time complexity: O(1). + */ + +igraph_integer_t igraph_bitset_size(const igraph_bitset_t *bitset) { + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_reserve + * \brief Reserves memory for a bitset. + * + * \experimental + * + * \a igraph bitsets are flexible, they can grow and + * shrink. Growing + * however occasionally needs the data in the bitset to be copied. + * In order to avoid this, you can call this function to reserve space for + * future growth of the bitset. + * + * + * Note that this function does \em not change the size of the + * bitset. Let us see a small example to clarify things: if you + * reserve space for 100 elements and the size of your + * bitset was (and still is) 60, then you can surely add additional 40 + * elements to your bitset before it will be copied. + * + * \param bitset The bitset object. + * \param capacity The new \em allocated size of the bitset. + * \return Error code: + * \c IGRAPH_ENOMEM if there is not enough memory. + * + * Time complexity: operating system dependent, should be around + * O(n/w), + * n is the new allocated size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_reserve(igraph_bitset_t *bitset, igraph_integer_t capacity) { + igraph_integer_t current_capacity; + igraph_uint_t *tmp; + + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_ASSERT(bitset->stor_begin != NULL); + IGRAPH_ASSERT(capacity >= 0); + + current_capacity = igraph_bitset_capacity(bitset); + + if (IGRAPH_BIT_NSLOTS(capacity) <= IGRAPH_BIT_NSLOTS(current_capacity)) { + return IGRAPH_SUCCESS; + } + + tmp = IGRAPH_REALLOC(bitset->stor_begin, IGRAPH_BIT_NSLOTS(capacity), igraph_uint_t); + IGRAPH_CHECK_OOM(tmp, "Cannot reserve space for bitset."); + + bitset->stor_begin = tmp; + bitset->stor_end = bitset->stor_begin + IGRAPH_BIT_NSLOTS(capacity); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_resize + * \brief Resizes the bitset. + * + * \experimental + * + * Note that this function does not free any memory, just sets the + * size of the bitset to the given one. It may, on the other hand, + * allocate more memory if the new size is larger than the previous + * one. In this case the newly appeared elements in the bitset are + * set to zero. + * + * \param bitset The bitset object + * \param new_size The new size of the bitset. + * \return Error code, + * \c IGRAPH_ENOMEM if there is not enough + * memory. Note that this function \em never returns an error + * if the bitset is made smaller. + * \sa \ref igraph_bitset_reserve() for allocating memory for future + * extensions of a bitset. + * + * Time complexity: O(1) if the new + * size is smaller, operating system dependent if it is larger. In the + * latter case it is usually around + * O(n/w), + * n is the new size of the bitset, + * w is the word size of the machine (32 or 64). + */ + +igraph_error_t igraph_bitset_resize(igraph_bitset_t *bitset, igraph_integer_t new_size) { + IGRAPH_ASSERT(bitset != NULL); + IGRAPH_ASSERT(bitset->stor_begin != NULL); + IGRAPH_CHECK(igraph_bitset_reserve(bitset, new_size)); + + if (new_size > bitset->size) { + for (igraph_integer_t i = bitset->size; i % IGRAPH_INTEGER_SIZE != 0; ++i) { + IGRAPH_BIT_CLEAR(*bitset, i); + } + memset(bitset->stor_begin + IGRAPH_BIT_NSLOTS(bitset->size), 0, + sizeof(igraph_uint_t) * (IGRAPH_BIT_NSLOTS(new_size) - IGRAPH_BIT_NSLOTS(bitset->size))); + } + bitset->size = new_size; + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup bitset + * \function igraph_bitset_popcount + * \brief The population count of the bitset. + * + * \experimental + * + * Returns the number of set bits, also called the population count, + * of the bitset. + * + * \param bitset The bitset object + * \return The population count of the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_popcount(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - 1); + igraph_integer_t count = 0; + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + count += IGRAPH_POPCOUNT(VECTOR(*bitset)[i]); + } + if (bitset->size) { + count += IGRAPH_POPCOUNT(mask & VECTOR(*bitset)[slots - 1]); + } + + return count; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countl_zero + * \brief The number of leading zeros in the bitset. + * + * \experimental + * + * Returns the number of leading (starting at the most significant bit) + * zeros in the bitset before the first one is encountered. If the bitset + * is all zeros, then its size is returned. + * + * \param bitset The bitset object + * \return The number of leading zeros in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countl_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t padding = IGRAPH_INTEGER_SIZE - final_block_size; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != 0) { + return IGRAPH_CLZ(mask & VECTOR(*bitset)[slots - 1]) - padding; + } + for (igraph_integer_t i = 1; i < slots; ++i) { + if (VECTOR(*bitset)[slots - i - 1] != 0) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CLZ(VECTOR(*bitset)[slots - i - 1]) - padding; + } + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countl_one + * \brief The number of leading ones in the bitset. + * + * \experimental + * + * Returns the number of leading ones (starting at the most significant bit) + * in the bitset before the first zero is encountered. + * If the bitset is all ones, then its size is returned. + * + * \param bitset The bitset object + * \return The number of leading ones in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countl_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t padding = IGRAPH_INTEGER_SIZE - final_block_size; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return IGRAPH_CLO(mask | VECTOR(*bitset)[slots - 1]) - padding; + } + for (igraph_integer_t i = 1; i < slots; ++i) { + if (VECTOR(*bitset)[slots - i - 1] != ~zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CLO(VECTOR(*bitset)[slots - i - 1]) - padding; + } + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countr_zero + * \brief The number of trailing zeros in the bitset. + * + * \experimental + * + * Returns the number of trailing (starting at the least significant bit) + * zeros in the bitset before the first one is encountered. + * If the bitset is all zeros, then its size is returned. + * + * \param bitset The bitset object + * \return The number of trailing zeros in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countr_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + if (VECTOR(*bitset)[i] != zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CTZ(VECTOR(*bitset)[i]); + } + } + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != zero) { + return IGRAPH_INTEGER_SIZE * (slots - 1) + IGRAPH_CTZ(mask & VECTOR(*bitset)[slots - 1]); + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_countr_one + * \brief The number of trailing ones in the bitset. + * + * \experimental + * + * Returns the number of trailing ones (starting at the least significant bit) + * in the bitset before the first zero is encountered. + * If the bitset is all ones, then its size is returned. + * + * \param bitset The bitset object + * \return The number of trailing ones in the bitset. + * + * Time complexity: O(n/w). + */ + +igraph_integer_t igraph_bitset_countr_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i + 1 < slots; ++i) { + if (VECTOR(*bitset)[i] != ~zero) { + return IGRAPH_INTEGER_SIZE * i + IGRAPH_CTO(VECTOR(*bitset)[i]); + } + } + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return IGRAPH_INTEGER_SIZE * (slots - 1) + IGRAPH_CTO(mask | VECTOR(*bitset)[slots - 1]); + } + + return bitset->size; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_all_zero + * \brief Are all bits zeros? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if none of the bits are set. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_all_zero(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? ~zero : ((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i < slots - 1; i++) { + if (VECTOR(*bitset)[i] != zero) { + return false; + } + } + if (bitset->size && (mask & VECTOR(*bitset)[slots - 1]) != zero) { + return false; + } + return true; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_all_one + * \brief Are all bits ones? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if all of the bits are set. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_all_one(const igraph_bitset_t *bitset) { + const igraph_integer_t final_block_size = bitset->size % IGRAPH_INTEGER_SIZE ? bitset->size % IGRAPH_INTEGER_SIZE : IGRAPH_INTEGER_SIZE; + const igraph_integer_t slots = IGRAPH_BIT_NSLOTS(bitset->size); + const igraph_uint_t one = 1, zero = 0; /* to avoid the need to cast 1 and 0 to igraph_uint_t below */ + const igraph_uint_t mask = final_block_size == IGRAPH_INTEGER_SIZE ? zero : ~((one << final_block_size) - one); + + for (igraph_integer_t i = 0; i < slots - 1; i++) { + if (VECTOR(*bitset)[i] != ~zero) { + return false; + } + } + if (bitset->size && (mask | VECTOR(*bitset)[slots - 1]) != ~zero) { + return false; + } + return true; +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_any_zero + * \brief Are any bits zeros? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if at least one bit is zero. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_any_zero(const igraph_bitset_t *bitset) { + return ! igraph_bitset_is_all_one(bitset); +} + +/** + * \ingroup bitset + * \function igraph_bitset_is_any_one + * \brief Are any bits ones? + * + * \experimental + * + * \param bitset The bitset object to test. + * \return True if at least one bit is one. + * + * Time complexity: O(n/w). + */ + +igraph_bool_t igraph_bitset_is_any_one(const igraph_bitset_t *bitset) { + return ! igraph_bitset_is_all_zero(bitset); +} + +/** + * \ingroup bitset + * \function igraph_bitset_or + * \brief Bitwise OR of two bitsets. + * + * \experimental + * + * Applies a bitwise or to the contents of two bitsets and stores it in an + * already initialized bitset. The destination bitset may be equal to one + * (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset. Must have have same size as \p dest. + * \param src2 A bitset. Must have have same size as \p dest. + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_or(igraph_bitset_t *dest, + const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] | VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_and + * \brief Bitwise AND of two bitsets. + * + * \experimental + * + * Applies a bitwise and to the contents of two bitsets and stores it in an + * already initialized bitset. The destination bitset may be equal to one + * (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset. Must have have same size as \p dest. + * \param src2 A bitset. Must have have same size as \p dest. + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_and(igraph_bitset_t *dest, const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] & VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_xor + * \brief Bitwise XOR of two bitsets. + * + * \experimental + * + * Applies a bitwise xor to the contents of two bitsets and stores it in + * an already initialized bitset. The destination bitset may be equal to + * one (or even both) of the sources. When working with bitsets, it is common + * that those created are of the same size fixed size. Therefore, this + * function does not check the sizes of the bitsets passed to it, the caller + * must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src1 A bitset. Must have have same size as \p dest. + * \param src2 A bitset. Must have have same size as \p dest. + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_xor(igraph_bitset_t *dest, + const igraph_bitset_t *src1, const igraph_bitset_t *src2) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = VECTOR(*src1)[i] ^ VECTOR(*src2)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_not + * \brief Bitwise negation of a bitset. + * + * \experimental + * + * Applies a bitwise not to the contents of a bitset and stores it in an + * already initialized bitset. The destination bitset may be equal to the + * source. When working with bitsets, it is common that those created are + * of the same size fixed size. Therefore, this function does not check the + * sizes of the bitsets passed to it, the caller must do so if necessary. + * + * \param dest The bitset object where the result is stored + * \param src A bitset. Must have have same size as \p dest. + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_not(igraph_bitset_t *dest, const igraph_bitset_t *src) { + for (igraph_integer_t i = 0; i < IGRAPH_BIT_NSLOTS(dest->size); ++i) { + VECTOR(*dest)[i] = ~VECTOR(*src)[i]; + } +} + +/** + * \ingroup bitset + * \function igraph_bitset_fill + * \brief Fills a bitset with a constant value. + * + * \experimental + * + * Sets all bits of a bitset to the same value. + * + * \param bitset The bitset object to modify. + * \param value The value to set for all bits. + * + * \sa \ref igraph_bitset_null() + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_fill(igraph_bitset_t *bitset, igraph_bool_t value) { + memset(bitset->stor_begin, + value ? ~ (unsigned char) 0 : 0, + sizeof(igraph_uint_t) * IGRAPH_BIT_NSLOTS(bitset->size)); +} + +/** + * \ingroup bitset + * \function igraph_bitset_null + * \brief Clears all bits in a bitset. + * + * \experimental + * + * \param bitset The bitset object to clear all bits in. + * + * \sa \ref igraph_bitset_fill() + * + * Time complexity: O(n/w). + */ + +void igraph_bitset_null(igraph_bitset_t *bitset) { + igraph_bitset_fill(bitset, false); +} + +/** + * \ingroup bitset + * \function igraph_bitset_fprint + * \brief Prints the bits of a bitset. + * + * \experimental + * + * Outputs the contents of a bitset to a file. + * The bitset is written from index n-1 to index 0, left to right, + * such that index 0 is the least significant bit and index n-1 is + * the most significant bit, where n is the size of the bitset. + * This is the reverse of how sequential structures are usually written, + * such as vectors, but consistent with the bitset being a binary + * representation of an integer and how they are usually written. + * + * + * No newline is printed at the end. + * + * \param bitset The bitset to be printed. + * \param file The file to be written. + * \return Error code. + * + * Time complexity: O(n). + */ +igraph_error_t igraph_bitset_fprint(const igraph_bitset_t *bitset, FILE *file) { + for (igraph_integer_t i = bitset->size - 1; i >= 0; i--) { + fputc(IGRAPH_BIT_TEST(*bitset, i) ? '1' : '0', file); + } + return IGRAPH_SUCCESS; +} + +#ifndef USING_R +igraph_error_t igraph_bitset_print(const igraph_bitset_t *bitset) { + return igraph_bitset_fprint(bitset, stdout); +} +#endif diff --git a/src/core/bitset_list.c b/src/core/bitset_list.c new file mode 100644 index 0000000000..3fdd36b0f1 --- /dev/null +++ b/src/core/bitset_list.c @@ -0,0 +1,49 @@ +/* + IGraph library. + Copyright (C) 2024 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_bitset_list.h" + +#include "igraph_error.h" +#include "igraph_interface.h" +#include "igraph_types.h" + +#define BITSET_LIST +#define BASE_BITSET +#define CUSTOM_INIT_DESTROY +#include "igraph_pmt.h" +#include "typed_list.pmt" +#include "igraph_pmt_off.h" +#undef CUSTOM_INIT_DESTROY +#undef BASE_BITSET +#undef BITSET_LIST + +static igraph_error_t igraph_i_bitset_list_init_item( + const igraph_bitset_list_t* list, igraph_bitset_t* item +) { + return igraph_bitset_init(item, 0); +} + +static igraph_error_t igraph_i_bitset_list_copy_item( + igraph_bitset_t* dest, const igraph_bitset_t* source +) { + return igraph_bitset_init_copy(dest, source); +} + +static void igraph_i_bitset_list_destroy_item(igraph_bitset_t* item) { + igraph_bitset_destroy(item); +} diff --git a/src/core/buckets.h b/src/core/buckets.h index 48e340b63e..176eddf546 100644 --- a/src/core/buckets.h +++ b/src/core/buckets.h @@ -42,8 +42,8 @@ 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); -igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b); -igraph_bool_t igraph_buckets_empty_bucket(const igraph_buckets_t *b, +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_buckets_empty(const igraph_buckets_t *b); +IGRAPH_FUNCATTR_PURE 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); @@ -59,8 +59,8 @@ 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); -igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b); -igraph_bool_t igraph_dbuckets_empty_bucket(const igraph_dbuckets_t *b, +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_dbuckets_empty(const igraph_dbuckets_t *b); +IGRAPH_FUNCATTR_PURE 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); diff --git a/src/core/cutheap.h b/src/core/cutheap.h index 62947c6b87..fcbb4cdcc3 100644 --- a/src/core/cutheap.h +++ b/src/core/cutheap.h @@ -40,10 +40,10 @@ typedef struct 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 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_FUNCATTR_PURE igraph_bool_t igraph_i_cutheap_empty(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_i_cutheap_active_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_i_cutheap_size(igraph_i_cutheap_t *ch); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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); diff --git a/src/core/error.c b/src/core/error.c index 6e928cc00f..8436c7f56a 100644 --- a/src/core/error.c +++ b/src/core/error.c @@ -156,6 +156,18 @@ static const char *igraph_i_error_strings[] = { /* 62 */ "Input problem has no solution" }; + +/** + * \function igraph_strerror + * \brief Textual description of an error. + * + * This is a simple utility function, it gives a short general textual + * description for an \a igraph error code. + * + * \param igraph_errno The \a igraph error code. + * \return pointer to the textual description of the error code. + */ + 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])) { @@ -164,8 +176,28 @@ const char *igraph_strerror(const igraph_error_t igraph_errno) { return igraph_i_error_strings[igraph_errno]; } + +/** + * \function igraph_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. + * It calls the currently installed error handler function with the + * supplied arguments. + * + * \param reason Textual description of the error. + * \param file The source file in which the error was noticed. + * \param line The number of line in the source file which triggered the + * error. + * \param igraph_errno The \a igraph error code. + * \return the error code (if it returns) + * + * \sa \ref igraph_errorf() + */ + igraph_error_t igraph_error(const char *reason, const char *file, int line, - igraph_error_t igraph_errno) { + igraph_error_t igraph_errno) { if (igraph_i_error_handler) { igraph_i_error_handler(reason, file, line, igraph_errno); @@ -177,8 +209,24 @@ igraph_error_t igraph_error(const char *reason, const char *file, int line, return igraph_errno; } + +/** + * \function igraph_errorf + * \brief Reports an error, printf-like version. + * + * \param reason Textual description of the error, interpreted as + * a \c printf format string. + * \param file The source file in which the error was noticed. + * \param line The line in the source file which triggered the error. + * \param igraph_errno The \a igraph error code. + * \param ... Additional parameters, the values to substitute into the + * format string. + * + * \sa \ref igraph_error() + */ + igraph_error_t igraph_errorf(const char *reason, const char *file, int line, - igraph_error_t igraph_errno, ...) { + igraph_error_t igraph_errno, ...) { va_list ap; va_start(ap, igraph_errno); vsnprintf(igraph_i_errormsg_buffer, @@ -222,6 +270,20 @@ void igraph_error_handler_printignore(const char *reason, const char *file, } #endif + +/** + * \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_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_handler) { igraph_error_handler_t *previous_handler = igraph_i_error_handler; igraph_i_error_handler = new_handler; @@ -377,6 +439,23 @@ void igraph_warning_handler_print(const char *reason, const char *file, int line } #endif + +/** + * \function igraph_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 + * warning. + * \param igraph_errno Warnings could have potentially error codes as well, + * but this is currently not used in igraph. + * \return The supplied error code. + */ + void igraph_warning(const char *reason, const char *file, int line) { if (igraph_i_warning_handler) { @@ -388,8 +467,27 @@ void igraph_warning(const char *reason, const char *file, int line) { } } -void igraph_warningf(const char *reason, const char *file, int line, - ...) { + +/** + * \function igraph_warningf + * \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. + * \param line The number of line in the source file which triggered the + * warning. + * \param igraph_errno Warnings could have potentially error codes as well, + * but this is currently not used in igraph. + * \param ... The additional arguments to be substituted into the + * template string. + */ + +void igraph_warningf(const char *reason, const char *file, int line, ...) { va_list ap; va_start(ap, line); vsnprintf(igraph_i_warningmsg_buffer, @@ -398,6 +496,19 @@ void igraph_warningf(const char *reason, const char *file, int line, igraph_warning(igraph_i_warningmsg_buffer, file, 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. + * \return The current warning handler function. + */ + igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *new_handler) { igraph_warning_handler_t *previous_handler = igraph_i_warning_handler; igraph_i_warning_handler = new_handler; @@ -409,6 +520,22 @@ igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *n static IGRAPH_THREAD_LOCAL igraph_fatal_handler_t *igraph_i_fatal_handler = NULL; +/** + * \function igraph_set_fatal_handler + * \brief Installs a fatal error handler. + * + * Installs the supplied fatal error handler function. + * + * + * Fatal error handler functions \em must not return. Typically, the fatal + * error handler would either call abort() or longjmp(). + * + * \param new_handler The new fatal error handler function to install. + * Supply a null pointer here to uninstall the current + * fatal error handler, without installing a new one. + * \return The current fatal error handler function. + */ + igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler) { igraph_fatal_handler_t *previous_handler = igraph_i_fatal_handler; igraph_i_fatal_handler = new_handler; @@ -422,6 +549,19 @@ void igraph_fatal_handler_abort(const char *reason, const char *file, int line) } #endif + +/** + * \function igraph_fatal + * \brief Triggers a fatal error. + * + * This function triggers a fatal error. Typically it is called indirectly through + * \ref IGRAPH_FATAL() or \ref IGRAPH_ASSERT(). + * + * \param reason Textual description of the error. + * \param file The source file in which the error was noticed. + * \param line The number of line in the source file which triggered the error. + */ + void igraph_fatal(const char *reason, const char *file, int line) { if (igraph_i_fatal_handler) { igraph_i_fatal_handler(reason, file, line); @@ -435,6 +575,21 @@ void igraph_fatal(const char *reason, const char *file, int line) { igraph_abort(); } + +/** + * \function igraph_fatalf + * \brief Triggers a fatal error, printf-like syntax. + * + * This function is similar to \ref igraph_fatal(), but + * uses a printf-like syntax. It substitutes the additional arguments + * into the \p reason template string and calls \ref igraph_fatal(). + * + * \param reason Textual description of the error. + * \param file The source file in which the error was noticed. + * \param line The number of line in the source file which triggered the error. + * \param ... The additional arguments to be substituted into the template string. + */ + void igraph_fatalf(const char *reason, const char *file, int line, ...) { va_list ap; va_start(ap, line); diff --git a/src/core/estack.c b/src/core/estack.c index 34319debbf..7c45a2a82b 100644 --- a/src/core/estack.c +++ b/src/core/estack.c @@ -25,8 +25,8 @@ 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_bitset_init(&s->isin, setsize)); + IGRAPH_FINALLY(igraph_bitset_destroy, &s->isin); IGRAPH_CHECK(igraph_stack_int_init(&s->stack, stacksize)); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -34,26 +34,26 @@ igraph_error_t igraph_estack_init(igraph_estack_t *s, igraph_integer_t setsize, void igraph_estack_destroy(igraph_estack_t *s) { igraph_stack_int_destroy(&s->stack); - igraph_vector_bool_destroy(&s->isin); + igraph_bitset_destroy(&s->isin); } igraph_error_t igraph_estack_push(igraph_estack_t *s, igraph_integer_t elem) { - if ( !VECTOR(s->isin)[elem] ) { + if ( !IGRAPH_BIT_TEST(s->isin, elem)) { IGRAPH_CHECK(igraph_stack_int_push(&s->stack, elem)); - VECTOR(s->isin)[elem] = true; + IGRAPH_BIT_SET(s->isin, elem); } return IGRAPH_SUCCESS; } igraph_integer_t igraph_estack_pop(igraph_estack_t *s) { igraph_integer_t elem = igraph_stack_int_pop(&s->stack); - VECTOR(s->isin)[elem] = false; + IGRAPH_BIT_CLEAR(s->isin, elem); return elem; } igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, igraph_integer_t elem) { - return VECTOR(s->isin)[elem]; + return IGRAPH_BIT_TEST(s->isin, elem); } igraph_integer_t igraph_estack_size(const igraph_estack_t *s) { diff --git a/src/core/estack.h b/src/core/estack.h index ad315b3edb..9a738a6973 100644 --- a/src/core/estack.h +++ b/src/core/estack.h @@ -25,14 +25,14 @@ #define IGRAPH_ESTACK_H #include "igraph_decls.h" +#include "igraph_bitset.h" #include "igraph_stack.h" -#include "igraph_vector.h" __BEGIN_DECLS typedef struct igraph_estack_t { igraph_stack_int_t stack; - igraph_vector_bool_t isin; + igraph_bitset_t isin; } igraph_estack_t; IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_estack_init( @@ -41,9 +41,9 @@ 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 igraph_bool_t igraph_estack_iselement(const igraph_estack_t *s, +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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_FUNCATTR_PURE 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); diff --git a/src/core/genheap.h b/src/core/genheap.h index 381631104b..e711b430fd 100644 --- a/src/core/genheap.h +++ b/src/core/genheap.h @@ -56,17 +56,17 @@ IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_gen2wheap_init( 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 IGRAPH_FUNCATTR_PURE 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_FUNCATTR_PURE 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 IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gen2wheap_max_size(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE const void *igraph_gen2wheap_max(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gen2wheap_max_index(const igraph_gen2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_gen2wheap_has_elem(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_gen2wheap_has_active(const igraph_gen2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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); diff --git a/src/core/grid.c b/src/core/grid.c index 9e761d09c4..1c620349b8 100644 --- a/src/core/grid.c +++ b/src/core/grid.c @@ -74,8 +74,8 @@ igraph_error_t igraph_2dgrid_init(igraph_2dgrid_t *grid, igraph_matrix_t *coords 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); + igraph_vector_int_null(&grid->prev); + igraph_vector_int_null(&grid->next); grid->massx = 0; grid->massy = 0; diff --git a/src/core/grid.h b/src/core/grid.h index 26d7a67e8f..3043359752 100644 --- a/src/core/grid.h +++ b/src/core/grid.h @@ -57,7 +57,9 @@ 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, igraph_integer_t elem); +IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_2dgrid_in(const igraph_2dgrid_t *grid, igraph_integer_t elem); +IGRAPH_FUNCATTR_PURE igraph_real_t igraph_2dgrid_sq_dist(const igraph_2dgrid_t *grid, + igraph_integer_t e1, igraph_integer_t e2); typedef struct igraph_2dgrid_iterator_t { igraph_integer_t vid, x, y; diff --git a/src/core/indheap.h b/src/core/indheap.h index 9f340c3330..93194cbf7c 100644 --- a/src/core/indheap.h +++ b/src/core/indheap.h @@ -52,15 +52,15 @@ 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_FUNCATTR_PURE 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_FUNCATTR_PURE igraph_real_t igraph_indheap_max(const 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_FUNCATTR_PURE 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); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_indheap_max_index(const igraph_indheap_t *h); /* -------------------------------------------------- */ @@ -94,9 +94,9 @@ 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_FUNCATTR_PURE 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 igraph_integer_t igraph_d_indheap_size(const igraph_d_indheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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); @@ -138,15 +138,15 @@ 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 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 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 IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_2wheap_empty(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_2wheap_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_2wheap_max_size(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_real_t igraph_2wheap_max(const igraph_2wheap_t *h); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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, 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_FUNCATTR_PURE igraph_bool_t igraph_2wheap_has_elem(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_bool_t igraph_2wheap_has_active(const igraph_2wheap_t *h, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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, igraph_integer_t *idx); IGRAPH_PRIVATE_EXPORT void igraph_2wheap_modify(igraph_2wheap_t *h, igraph_integer_t idx, igraph_real_t elem); diff --git a/src/core/marked_queue.h b/src/core/marked_queue.h index 564fe9f4e5..70a2fdce5e 100644 --- a/src/core/marked_queue.h +++ b/src/core/marked_queue.h @@ -54,13 +54,13 @@ IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_marked_queue_int_init(igraph_marked_ 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 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_FUNCATTR_PURE igraph_bool_t igraph_marked_queue_int_empty(const igraph_marked_queue_int_t *q); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_marked_queue_int_size(const igraph_marked_queue_int_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_int_iselement(const igraph_marked_queue_int_t *q, +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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); diff --git a/src/core/matrix.pmt b/src/core/matrix.pmt index 6cb5e1d58d..fbdf918d24 100644 --- a/src/core/matrix.pmt +++ b/src/core/matrix.pmt @@ -154,7 +154,7 @@ const TYPE(igraph_matrix)* FUNCTION(igraph_matrix, view)( * Time complexity: O(1). */ -IGRAPH_EXPORT const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( +const TYPE(igraph_matrix) *FUNCTION(igraph_matrix, view_from_vector)( const TYPE(igraph_matrix) *m, const TYPE(igraph_vector) *v, igraph_integer_t nrow ) { diff --git a/src/core/set.h b/src/core/set.h index 7b435e3e56..8f5b3e3225 100644 --- a/src/core/set.h +++ b/src/core/set.h @@ -51,13 +51,13 @@ typedef struct s_set { 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 IGRAPH_FUNCATTR_PURE 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 igraph_bool_t igraph_set_empty(const igraph_set_t* set); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE 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_FUNCATTR_PURE 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_FUNCATTR_PURE 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); diff --git a/src/core/sparsemat.c b/src/core/sparsemat.c index c7a562beea..524c109cb0 100644 --- a/src/core/sparsemat.c +++ b/src/core/sparsemat.c @@ -27,10 +27,11 @@ #include "igraph_constructors.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_misc.h" /* IGRAPH_STATIC_ASSERT */ #include "igraph_types.h" #include "igraph_vector_ptr.h" +#include "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ + #include #include diff --git a/src/core/trie.h b/src/core/trie.h index 9b1e23d7be..3bfcb47ba8 100644 --- a/src/core/trie.h +++ b/src/core/trie.h @@ -62,8 +62,8 @@ IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_trie_get(igraph_trie_t *t, const cha 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); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE const char* igraph_trie_idx(igraph_trie_t *t, igraph_integer_t idx); +IGRAPH_PRIVATE_EXPORT IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_trie_size(igraph_trie_t *t); const igraph_strvector_t* igraph_i_trie_borrow_keys(igraph_trie_t *t); diff --git a/src/core/typed_list.pmt b/src/core/typed_list.pmt index 5c81b1adbc..2ec3a41a0c 100644 --- a/src/core/typed_list.pmt +++ b/src/core/typed_list.pmt @@ -66,6 +66,9 @@ #if defined(BASE_GRAPH) #define ITEM_FUNCTION(f) CONCAT2x(igraph,f) #endif + #if defined(BASE_BITSET) + #define ITEM_FUNCTION(f) CONCAT2x(igraph_bitset,f) + #endif #endif static igraph_error_t INTERNAL_FUNCTION(init_item)(const TYPE* list, ITEM_TYPE* item); diff --git a/src/core/vector.c b/src/core/vector.c index c90eb528ae..678c8440fd 100644 --- a/src/core/vector.c +++ b/src/core/vector.c @@ -662,7 +662,7 @@ igraph_error_t igraph_vector_is_nan(const igraph_vector_t *v, igraph_vector_bool * \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. + * \return True if any element is NaN, false otherwise. * * Time complexity: O(n), the number of elements. */ @@ -680,3 +680,29 @@ igraph_bool_t igraph_vector_is_any_nan(const igraph_vector_t *v) } return false; } + + +/** + * \ingroup vector + * \function igraph_vector_is_all_finite + * \brief Check if all elements are finite. + * + * \param v The \type igraph_vector_t object to check. + * \return True if none of the elements are infinite or NaN. + * + * Time complexity: O(n), the number of elements. + */ +igraph_bool_t igraph_vector_is_all_finite(const igraph_vector_t *v) +{ + igraph_real_t *ptr; + IGRAPH_ASSERT(v != NULL); + IGRAPH_ASSERT(v->stor_begin != NULL); + ptr = v->stor_begin; + while (ptr < v->end) { + if (!isfinite(*ptr)) { + return false; + } + ptr++; + } + return true; +} diff --git a/src/core/vector.pmt b/src/core/vector.pmt index b0bb44a614..1c503bb482 100644 --- a/src/core/vector.pmt +++ b/src/core/vector.pmt @@ -171,9 +171,9 @@ 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 TYPE(igraph_vector)* FUNCTION(igraph_vector, view)(const TYPE(igraph_vector) *v, const BASE *data, igraph_integer_t length) { - const static BASE dummy = ZERO; + static const BASE dummy = ZERO; TYPE(igraph_vector) *v2 = (TYPE(igraph_vector)*)v; /* When the length is zero, we allow 'data' to be NULL. @@ -560,7 +560,7 @@ 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) { +igraph_error_t FUNCTION(igraph_vector, push_back)(TYPE(igraph_vector)* v, BASE e) { IGRAPH_ASSERT(v != NULL); IGRAPH_ASSERT(v->stor_begin != NULL); @@ -1020,10 +1020,10 @@ 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. * - * 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 + * Takes an unsorted array \c v as input and computes an array of indices + * \p inds such that v[ inds[i] ], with \c i increasing from 0, + * is an ordered array (either ascending or descending, depending on + * \p order). The order of indices for identical elements is not * defined. If the vector contains any NaN values, the ordering of * NaN values is undefined. * @@ -2137,7 +2137,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, +static 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); /** @@ -2220,7 +2220,7 @@ igraph_bool_t FUNCTION(igraph_vector, binsearch_slice)(const TYPE(igraph_vector) return FUNCTION(igraph_i_vector, binsearch_slice)(v, what, pos, start, end); } -igraph_bool_t FUNCTION(igraph_i_vector, binsearch_slice)(const TYPE(igraph_vector) *v, +static 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; @@ -2945,37 +2945,87 @@ igraph_bool_t FUNCTION(igraph_vector, isnull)(const TYPE(igraph_vector) *v) { #ifndef NOTORDERED -igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( + +/* + * Sorted vector intersection + */ + +/* Recursive Baeza-Yates intersection algorithm for sorted vector intersection. */ +static 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); + TYPE(igraph_vector) *result) { + igraph_integer_t size1, size2, probe1, probe2; + + if (begin1 == end1 || begin2 == end2) { + return IGRAPH_SUCCESS; + } + + size1 = end1 - begin1; + size2 = end2 - begin2; + + if (size1 < size2) { + probe1 = begin1 + (size1 >> 1); /* pick the median element */ + FUNCTION(igraph_i_vector, binsearch_slice)(v2, VECTOR(*v1)[probe1], &probe2, begin2, end2); + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + )); + if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) { + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + probe2++; + } + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, probe1 + 1, end1, v2, probe2, end2, result + )); + } else { + probe2 = begin2 + (size2 >> 1); /* pick the median element */ + FUNCTION(igraph_i_vector, binsearch_slice)(v1, VECTOR(*v2)[probe2], &probe1, begin1, end1); + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + )); + if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) { + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + probe1++; + } + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, probe1, end1, v2, probe2 + 1, end2, result + )); + } + + return IGRAPH_SUCCESS; +} /** * \function igraph_vector_intersect_sorted - * \brief Calculates the intersection of two sorted vectors. + * \brief Set intersection of two sorted vectors. * * The elements that are contained in both vectors are stored in the result * vector. All three vectors must be initialized. * * - * Instead of the naive intersection which takes O(n), this function uses - * the set intersection method of Ricardo Baeza-Yates, which is more efficient - * when one of the vectors is significantly smaller than the other, and - * gives similar performance on average when the two vectors are equal. + * For similar-size vectors, this function uses a straightforward linear scan. + * When the vector sizes differ substantially, it uses the set intersection + * method of Ricardo Baeza-Yates, which takes logarithmic time in the length + * of the larger vector. * * * The algorithm keeps the multiplicities of the elements: if an element appears - * k1 times in the first vector and k2 times in the second, the result - * will include that element min(k1, k2) times. + * \c k1 times in the first vector and \c k2 times in the second, the result + * will include that element min(k1, k2) times. * * - * Reference: Baeza-Yates R: A fast set intersection algorithm for sorted + * Reference: + * + * + * Baeza-Yates R: A fast set intersection algorithm for sorted * sequences. In: Lecture Notes in Computer Science, vol. 3109/2004, pp. - * 400--408, 2004. Springer Berlin/Heidelberg. ISBN: 978-3-540-22341-2. + * 400--408, 2004. Springer Berlin/Heidelberg. + * https://doi.org/10.1007/978-3-540-27801-6_30 * - * \param v1 the first vector - * \param v2 the second vector - * \param result the result vector, which will also be sorted. + * \param v1 The first vector + * \param v2 The second vector + * \param result The result vector, which will also be sorted. + * \return Error code. * * Time complexity: O(m log(n)) where m is the size of the smaller vector * and n is the size of the larger one. @@ -2983,6 +3033,7 @@ igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vector) *v1, const TYPE(igraph_vector) *v2, TYPE(igraph_vector) *result) { igraph_integer_t size1, size2; + igraph_real_t r; size1 = FUNCTION(igraph_vector, size)(v1); size2 = FUNCTION(igraph_vector, size)(v2); @@ -2993,19 +3044,40 @@ igraph_error_t FUNCTION(igraph_vector, intersect_sorted)(const TYPE(igraph_vecto return IGRAPH_SUCCESS; } - IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( - v1, 0, size1, v2, 0, size2, result)); + r = size1 > size2 ? (igraph_real_t) size1 / size2 : (igraph_real_t) size2 / size1; + + /* When the set size ratio is small, use a simple linear scan. See comments in + * igraph_vector_intersection_size_sorted() for the justification of this r threshold. */ + if (r < 10) { + igraph_integer_t i1 = 0, i2 = 0; + while (i1 < size1 && i2 < size2) { + BASE e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; + if (e1 < e2) { + i1++; + } else if (e1 > e2) { + i2++; + } else { + i1++; i2++; + IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, e1)); + } + } + } else { + IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( + v1, 0, size1, v2, 0, size2, result)); + } return IGRAPH_SUCCESS; } -igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( +/* Recursive Baeza-Yates intersection algorithm for sorted vector intersection size. */ +static void FUNCTION(igraph_i_vector, intersection_size_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) { + igraph_integer_t *result) { + igraph_integer_t size1, size2, probe1, probe2; if (begin1 == end1 || begin2 == end2) { - return IGRAPH_SUCCESS; + return; } size1 = end1 - begin1; @@ -3014,37 +3086,113 @@ igraph_error_t FUNCTION(igraph_i_vector, intersect_sorted)( if (size1 < size2) { probe1 = begin1 + (size1 >> 1); /* pick the median element */ FUNCTION(igraph_i_vector, binsearch_slice)(v2, VECTOR(*v1)[probe1], &probe2, begin2, end2); - IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( - v1, begin1, probe1, v2, begin2, probe2, result - )); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + ); if (!(probe2 == end2 || VECTOR(*v1)[probe1] < VECTOR(*v2)[probe2])) { - IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + (*result)++; probe2++; } - IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( - v1, probe1 + 1, end1, v2, probe2, end2, result - )); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, probe1 + 1, end1, v2, probe2, end2, result + ); } else { probe2 = begin2 + (size2 >> 1); /* pick the median element */ FUNCTION(igraph_i_vector, binsearch_slice)(v1, VECTOR(*v2)[probe2], &probe1, begin1, end1); - IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( - v1, begin1, probe1, v2, begin2, probe2, result - )); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, begin1, probe1, v2, begin2, probe2, result + ); if (!(probe1 == end1 || VECTOR(*v2)[probe2] < VECTOR(*v1)[probe1])) { - IGRAPH_CHECK(FUNCTION(igraph_vector, push_back)(result, VECTOR(*v2)[probe2])); + (*result)++; probe1++; } - IGRAPH_CHECK(FUNCTION(igraph_i_vector, intersect_sorted)( - v1, probe1, end1, v2, probe2 + 1, end2, result - )); + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, probe1, end1, v2, probe2 + 1, end2, result + ); } +} - return IGRAPH_SUCCESS; +/** + * \function igraph_vector_intersection_size_sorted + * \brief Intersection size of two sorted vectors. + * + * \experimental + * + * Counts elements that are present in both vectors. This is particularly + * useful for counting common neighbours of two vertices. + * + * + * For similar-size vectors, this function uses a straightforward linear scan. + * When the vector sizes differ substantially, it uses the set intersection + * method of Ricardo Baeza-Yates, which takes logarithmic time in the length + * of the larger vector. + * + * + * The algorithm keeps the multiplicities of the elements: if an element appears + * \c k1 times in the first vector and \c k2 times in the second, the result + * will include that element min(k1, k2) times. + * + * + * Reference: + * + * + * Baeza-Yates R: A fast set intersection algorithm for sorted + * sequences. In: Lecture Notes in Computer Science, vol. 3109/2004, pp. + * 400--408, 2004. Springer Berlin/Heidelberg. + * https://doi.org/10.1007/978-3-540-27801-6_30 + * + * \param v1 The first vector + * \param v2 The second vector + * \return The number of common elements. + * + * 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_integer_t FUNCTION(igraph_vector, intersection_size_sorted)( + const TYPE(igraph_vector) *v1, + const TYPE(igraph_vector) *v2) { + + igraph_integer_t size1, size2, count; + igraph_real_t r; + + size1 = FUNCTION(igraph_vector, size)(v1); + size2 = FUNCTION(igraph_vector, size)(v2); + + count = 0; + + if (size1 == 0 || size2 == 0) { + return count; + } + + r = size1 > size2 ? (igraph_real_t) size1 / size2 : (igraph_real_t) size2 / size1; + + /* When the set size ratio is small, use a simple linear scan. The ideal ratio + * seems to be affected by processor cache effects. It depends on the machine, + * as well as on the input sizes. The threshold of 10 was determined empirically + * by using the intersection.c (direct) and igraph_ecc.c (indirect) benchmarks. + * See https://github.com/igraph/igraph/pull/2618 */ + if (r < 10) { + /* This is a fast branchless implementation that uses arithmetic + * instead of conditionals. */ + igraph_integer_t i1 = 0, i2 = 0; + while (i1 < size1 && i2 < size2) { + BASE e1 = VECTOR(*v1)[i1], e2 = VECTOR(*v2)[i2]; + igraph_integer_t d1 = (e1 <= e2), d2 = (e1 >= e2); + i1 += d1; i2 += d2; + count += (d1 == d2); + } + } else { + FUNCTION(igraph_i_vector, intersection_size_sorted)( + v1, 0, size1, v2, 0, size2, &count); + } + + return count; } /** * \function igraph_vector_difference_sorted - * \brief Calculates the difference between two sorted vectors (considered as sets). + * \brief Set difference of two sorted vectors. * * 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. diff --git a/src/flow/flow.c b/src/flow/flow.c index e2c17f6d84..af257db8c2 100644 --- a/src/flow/flow.c +++ b/src/flow/flow.c @@ -156,16 +156,18 @@ * undirected edge. */ -static igraph_error_t 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_integer_t source, - igraph_integer_t target, - const igraph_vector_t *capacity, - igraph_maxflow_stats_t *stats) { +static igraph_error_t 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_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; @@ -304,7 +306,6 @@ static void igraph_i_mf_push(igraph_integer_t v, igraph_integer_t e, igraph_inte igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, igraph_integer_t *npushsince) { - IGRAPH_UNUSED(current); IGRAPH_UNUSED(source); @@ -334,6 +335,7 @@ static void igraph_i_mf_discharge(igraph_integer_t v, igraph_vector_int_t *rev, igraph_maxflow_stats_t *stats, igraph_integer_t *npushsince, igraph_integer_t *nrelabelsince) { + do { igraph_integer_t i; igraph_integer_t start = CURRENT(v); @@ -368,13 +370,13 @@ static void igraph_i_mf_discharge(igraph_integer_t v, } 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_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_integer_t source, igraph_integer_t target, + igraph_integer_t 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_integer_t k, l; @@ -485,11 +487,11 @@ static igraph_error_t igraph_i_mf_bfs(igraph_dqueue_int_t *bfsq, */ 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_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_vcount(graph); igraph_integer_t no_of_orig_edges = igraph_ecount(graph); @@ -617,7 +619,7 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, } for (i = 1; i < no_of_edges; i++) { igraph_integer_t n = (VECTOR(from)[i] - - VECTOR(from)[ VECTOR(first)[idx] ]); + VECTOR(from)[ VECTOR(first)[idx] ]); for (j = 0; j < n; j++) { idx++; VECTOR(first)[idx] = i; } @@ -736,7 +738,7 @@ 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, - no_of_nodes - marked)); + no_of_nodes - marked)); for (i = 0; i < no_of_nodes; i++) { if (!VECTOR(added)[i]) { VECTOR(*partition)[x++] = i; @@ -883,9 +885,9 @@ igraph_error_t igraph_maxflow(const igraph_t *graph, igraph_real_t *value, igraph_integer_t pos = VECTOR(rank)[i]; if ((capacity ? VECTOR(*capacity)[j] : 1.0) > RESCAP(pos)) { IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, - IGRAPH_FROM(graph, j))); + IGRAPH_FROM(graph, j))); IGRAPH_CHECK(igraph_vector_int_push_back(&flow_edges, - IGRAPH_TO(graph, j))); + IGRAPH_TO(graph, j))); } } IGRAPH_CHECK(igraph_create(&flow_graph, &flow_edges, no_of_nodes, @@ -1087,9 +1089,9 @@ igraph_error_t igraph_maxflow(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) { + 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, @@ -1126,8 +1128,8 @@ igraph_error_t igraph_maxflow_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) { + igraph_integer_t source, igraph_integer_t target, + const igraph_vector_t *capacity) { if (source == target) { IGRAPH_ERROR("source and target vertices are the same", IGRAPH_EINVAL); @@ -1175,10 +1177,10 @@ igraph_error_t igraph_st_mincut_value(const igraph_t *graph, igraph_real_t *valu */ 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_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) { return igraph_maxflow(graph, value, /*flow=*/ NULL, cut, partition, partition2, @@ -1192,12 +1194,13 @@ igraph_error_t igraph_st_mincut(const igraph_t *graph, igraph_real_t *value, * It can also calculate the cut itself, not just the cut value. */ -static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, - igraph_real_t *res, - igraph_vector_int_t *partition, - igraph_vector_int_t *partition2, - igraph_vector_int_t *cut, - const igraph_vector_t *capacity) { +static igraph_error_t igraph_i_mincut_undirected( + const igraph_t *graph, + igraph_real_t *res, + igraph_vector_int_t *partition, + igraph_vector_int_t *partition2, + igraph_vector_int_t *cut, + const igraph_vector_t *capacity) { igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_edges = igraph_ecount(graph); @@ -1235,7 +1238,7 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, if (partition) { igraph_integer_t j = 0; IGRAPH_CHECK(igraph_vector_int_resize(partition, - VECTOR(csize)[0])); + VECTOR(csize)[0])); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(memb)[i] == 0) { VECTOR(*partition)[j++] = i; @@ -1245,7 +1248,7 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, if (partition2) { igraph_integer_t j = 0; IGRAPH_CHECK(igraph_vector_int_resize(partition2, no_of_nodes - - VECTOR(csize)[0])); + VECTOR(csize)[0])); for (i = 0; i < no_of_nodes; i++) { if (VECTOR(memb)[i] != 0) { VECTOR(*partition2)[j++] = i; @@ -1458,12 +1461,14 @@ static igraph_error_t igraph_i_mincut_undirected(const igraph_t *graph, return IGRAPH_SUCCESS; } -static igraph_error_t 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, - const igraph_vector_t *capacity) { +static igraph_error_t 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, + const igraph_vector_t *capacity) { + igraph_integer_t i; igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t flow; @@ -1622,11 +1627,11 @@ static igraph_error_t igraph_i_mincut_directed(const igraph_t *graph, */ igraph_error_t 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, - const igraph_vector_t *capacity) { + igraph_real_t *value, + 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)) { if (partition || partition2 || cut) { @@ -1645,9 +1650,10 @@ igraph_error_t igraph_mincut(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) { +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); } @@ -1688,7 +1694,7 @@ static igraph_error_t igraph_i_mincut_value_undirected(const igraph_t *graph, */ igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, - const igraph_vector_t *capacity) { + const igraph_vector_t *capacity) { igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t minmaxflow, flow; @@ -1727,13 +1733,15 @@ igraph_error_t igraph_mincut_value(const igraph_t *graph, igraph_real_t *res, return IGRAPH_SUCCESS; } -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_bool_t *done, - igraph_integer_t *no_conn) { +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_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; @@ -1783,11 +1791,12 @@ static igraph_error_t igraph_i_st_vertex_connectivity_check_errors(const igraph_ 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) { +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) { igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_edges; @@ -1843,11 +1852,13 @@ static igraph_error_t igraph_i_st_vertex_connectivity_directed(const igraph_t *g return IGRAPH_SUCCESS; } -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) { +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_t newgraph; igraph_bool_t done; igraph_integer_t no_conn; @@ -1912,11 +1923,13 @@ 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, - igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target, - igraph_vconn_nei_t neighbors) { +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 (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, source, target, @@ -1931,8 +1944,10 @@ igraph_error_t igraph_st_vertex_connectivity(const igraph_t *graph, } static igraph_error_t igraph_i_vertex_connectivity_directed( - const igraph_t *graph, igraph_integer_t *res, igraph_bool_t all_edges_are_mutual -) { + 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; @@ -1965,9 +1980,9 @@ static igraph_error_t igraph_i_vertex_connectivity_directed( /* 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 - )); + 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 @@ -1994,8 +2009,8 @@ static igraph_error_t igraph_i_vertex_connectivity_directed( /* Do the maximum flow */ IGRAPH_CHECK(igraph_maxflow_value( - &split_graph, &real_res, i, j + no_of_nodes, &capacity, 0 - )); + &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)); @@ -2037,15 +2052,17 @@ static igraph_error_t igraph_i_vertex_connectivity_directed( return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_vertex_connectivity_undirected(const igraph_t *graph, - igraph_integer_t *res) { +static igraph_error_t igraph_i_vertex_connectivity_undirected( + const igraph_t *graph, + igraph_integer_t *res) { + igraph_t newgraph; IGRAPH_CHECK(igraph_copy(&newgraph, graph)); IGRAPH_FINALLY(igraph_destroy, &newgraph); IGRAPH_CHECK(igraph_to_directed(&newgraph, IGRAPH_TO_DIRECTED_MUTUAL)); - IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res, /* all_edges_are_mutual = */ 1)); + IGRAPH_CHECK(igraph_i_vertex_connectivity_directed(&newgraph, res, /* all_edges_are_mutual = */ true)); igraph_destroy(&newgraph); IGRAPH_FINALLY_CLEAN(1); @@ -2053,10 +2070,18 @@ static igraph_error_t igraph_i_vertex_connectivity_undirected(const igraph_t *gr return IGRAPH_SUCCESS; } -/* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) */ -static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, - igraph_integer_t *res, - igraph_bool_t *found) { +/* Use that vertex.connectivity(G) <= edge.connectivity(G) <= min(degree(G)) + * + * Check if the graph is connected, and if its minimum degree is 1. + * This is sufficient to determine both the vertex and edge connectivity, + * which are returned in 'res'. 'found' will indicate if the connectivity + * could be determined. + */ +static igraph_error_t igraph_i_connectivity_checks( + const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t *found) { + igraph_bool_t conn; *found = false; @@ -2118,11 +2143,12 @@ static igraph_error_t igraph_i_connectivity_checks(const igraph_t *graph, * * \param graph The input graph. * \param res Pointer to an integer, the result will be stored here. - * \param checks Logical constant. Whether to check that the graph is - * connected and also the degree of the vertices. If the graph is + * \param checks Logical constant. Whether to check if the graph is + * connected or complete and also the degree of the vertices. If the graph is * not (strongly) connected then the connectivity is obviously zero. Otherwise - * if the minimum degree is one then the vertex connectivity is also - * one. It is a good idea to perform these checks, as they can be + * if the minimum degree is 1 then the vertex connectivity is also + * 1. If the graph is complete, the connectivity is the vertex count + * minus one. It is a good idea to perform these checks, as they can be * done quickly compared to the connectivity calculation itself. * They were suggested by Peter McMahan, thanks Peter. * \return Error code. @@ -2143,6 +2169,20 @@ igraph_error_t igraph_vertex_connectivity( IGRAPH_CHECK(igraph_i_connectivity_checks(graph, res, &ret)); } + if (checks && !ret) { + /* The vertex connectivity of a complete graph is vcount-1 by definition. + * This check cannot be performed within igraph_i_connectivity_checks(), + * as that function is used both for vertex and edge connectivities. + * Checking if the graph is complete does not suffice to determine the + * edge connectivity of a complete multigraph. */ + igraph_bool_t complete; + IGRAPH_CHECK(igraph_is_complete(graph, &complete)); + if (complete) { + *res = igraph_vcount(graph) - 1; + ret = true; + } + } + /* Are we done yet? */ if (!ret) { if (igraph_is_directed(graph)) { @@ -2180,9 +2220,11 @@ igraph_error_t igraph_vertex_connectivity( * igraph_vertex_connectivity(). */ -igraph_error_t igraph_st_edge_connectivity(const igraph_t *graph, igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target) { +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; if (source == target) { @@ -2229,8 +2271,10 @@ 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, - igraph_bool_t checks) { +igraph_error_t igraph_edge_connectivity(const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t checks) { + igraph_bool_t ret = false; igraph_integer_t number_of_nodes = igraph_vcount(graph); @@ -2283,9 +2327,10 @@ 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, - igraph_integer_t source, - igraph_integer_t target) { +igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { igraph_real_t flow; @@ -2330,9 +2375,10 @@ igraph_error_t igraph_edge_disjoint_paths(const igraph_t *graph, igraph_integer_ * \ref igraph_st_vertex_connectivity(), \ref igraph_maxflow_value(). */ -igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_integer_t *res, - igraph_integer_t source, - igraph_integer_t target) { +igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, + igraph_integer_t *res, + igraph_integer_t source, + igraph_integer_t target) { igraph_vector_int_t eids; @@ -2346,12 +2392,12 @@ igraph_error_t igraph_vertex_disjoint_paths(const igraph_t *graph, igraph_intege if (igraph_is_directed(graph)) { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_directed(graph, res, - source, target, - IGRAPH_VCONN_NEI_IGNORE)); + source, target, + IGRAPH_VCONN_NEI_IGNORE)); } else { IGRAPH_CHECK(igraph_i_st_vertex_connectivity_undirected(graph, res, - source, target, - IGRAPH_VCONN_NEI_IGNORE)); + source, target, + IGRAPH_VCONN_NEI_IGNORE)); } *res += igraph_vector_int_size(&eids); @@ -2391,8 +2437,9 @@ 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, - igraph_bool_t checks) { +igraph_error_t igraph_adhesion(const igraph_t *graph, + igraph_integer_t *res, + igraph_bool_t checks) { return igraph_edge_connectivity(graph, res, checks); } @@ -2424,8 +2471,9 @@ 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, - igraph_bool_t checks) { +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 IGRAPH_SUCCESS; @@ -2473,8 +2521,10 @@ 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, - igraph_vector_t *flows, const igraph_vector_t *capacity) { +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; diff --git a/src/flow/st-cuts.c b/src/flow/st-cuts.c index b6e649ce1f..6823b0d88e 100644 --- a/src/flow/st-cuts.c +++ b/src/flow/st-cuts.c @@ -23,6 +23,7 @@ #include "igraph_flow.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_constants.h" #include "igraph_constructors.h" #include "igraph_components.h" @@ -612,8 +613,8 @@ igraph_error_t igraph_dominator_tree(const igraph_t *graph, typedef struct igraph_i_all_st_cuts_minimal_dfs_data_t { igraph_stack_int_t *stack; - igraph_vector_bool_t *nomark; - const igraph_vector_bool_t *GammaX; + igraph_bitset_t *nomark; + const igraph_bitset_t *GammaX; igraph_integer_t root; const igraph_vector_int_t *map; } igraph_i_all_st_cuts_minimal_dfs_data_t; @@ -626,17 +627,17 @@ static igraph_error_t igraph_i_all_st_cuts_minimal_dfs_incb( igraph_i_all_st_cuts_minimal_dfs_data_t *data = extra; igraph_stack_int_t *stack = data->stack; - igraph_vector_bool_t *nomark = data->nomark; - const igraph_vector_bool_t *GammaX = data->GammaX; + igraph_bitset_t *nomark = data->nomark; + const igraph_bitset_t *GammaX = data->GammaX; const igraph_vector_int_t *map = data->map; igraph_integer_t realvid = VECTOR(*map)[vid]; IGRAPH_UNUSED(graph); IGRAPH_UNUSED(dist); - if (VECTOR(*GammaX)[realvid]) { + if (IGRAPH_BIT_TEST(*GammaX, realvid)) { if (!igraph_stack_int_empty(stack)) { igraph_integer_t top = igraph_stack_int_top(stack); - VECTOR(*nomark)[top] = true; /* we just found a smaller one */ + IGRAPH_BIT_SET(*nomark, top); /* we just found a smaller one */ } IGRAPH_CHECK(igraph_stack_int_push(stack, realvid)); } @@ -668,20 +669,20 @@ static igraph_error_t 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, - const igraph_vector_bool_t *GammaX, + const igraph_bitset_t *GammaX, const igraph_vector_int_t *invmap, igraph_vector_int_t *minimal) { igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_stack_int_t stack; - igraph_vector_bool_t nomark; + igraph_bitset_t nomark; igraph_i_all_st_cuts_minimal_dfs_data_t data; igraph_integer_t i; IGRAPH_UNUSED(X); IGRAPH_STACK_INT_INIT_FINALLY(&stack, 10); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&nomark, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&nomark, no_of_nodes); data.stack = &stack; data.nomark = &nomark; @@ -692,9 +693,7 @@ static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, /* We mark all GammaX elements as minimal first. TODO: actually, we could just use GammaX to return the minimal elements. */ - for (i = 0; i < no_of_nodes; i++) { - VECTOR(nomark)[i] = (VECTOR(*GammaX)[i] == 0); - } + igraph_bitset_not(&nomark, GammaX); /* 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 @@ -710,12 +709,12 @@ static igraph_error_t igraph_i_all_st_cuts_minimal(const igraph_t *graph, igraph_vector_int_clear(minimal); for (i = 0; i < no_of_nodes; i++) { - if (!VECTOR(nomark)[i]) { + if (!IGRAPH_BIT_TEST(nomark, i)) { IGRAPH_CHECK(igraph_vector_int_push_back(minimal, i)); } } - igraph_vector_bool_destroy(&nomark); + igraph_bitset_destroy(&nomark); igraph_stack_int_destroy(&stack); IGRAPH_FINALLY_CLEAN(2); @@ -738,7 +737,7 @@ igraph_error_t igraph_i_all_st_cuts_pivot( igraph_integer_t i, nomin, n; igraph_integer_t root; igraph_vector_int_t M; - igraph_vector_bool_t GammaS; + igraph_bitset_t GammaS; igraph_vector_int_t Nuv; igraph_vector_int_t Isv_min; igraph_vector_int_t GammaS_vec; @@ -784,10 +783,9 @@ igraph_error_t igraph_i_all_st_cuts_pivot( /* First we create GammaS */ /* TODO: use the adjacency list, instead of neighbors() */ - IGRAPH_CHECK(igraph_vector_bool_init(&GammaS, no_of_nodes)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &GammaS); + IGRAPH_BITSET_INIT_FINALLY(&GammaS, no_of_nodes); if (igraph_marked_queue_int_size(S) == 0) { - VECTOR(GammaS)[VECTOR(Sbar_map)[source] - 1] = true; + IGRAPH_BIT_SET(GammaS, VECTOR(Sbar_map)[source] - 1); } else { for (i = 0; i < no_of_nodes; i++) { if (igraph_marked_queue_int_iselement(S, i)) { @@ -800,7 +798,7 @@ igraph_error_t igraph_i_all_st_cuts_pivot( for (j = 0; j < n; j++) { igraph_integer_t nei = VECTOR(neis)[j]; if (!igraph_marked_queue_int_iselement(S, nei)) { - VECTOR(GammaS)[nei] = true; + IGRAPH_BIT_SET(GammaS, nei); } } igraph_vector_int_destroy(&neis); @@ -816,7 +814,7 @@ igraph_error_t igraph_i_all_st_cuts_pivot( n = igraph_vector_int_size(&leftout); for (i = 0; i < n; i++) { VECTOR(leftout)[i] = VECTOR(Sbar_invmap)[VECTOR(leftout)[i]]; - VECTOR(GammaS)[VECTOR(leftout)[i]] = false; + IGRAPH_BIT_CLEAR(GammaS, VECTOR(leftout)[i]); } IGRAPH_VECTOR_INT_INIT_FINALLY(&M, 0); @@ -830,7 +828,7 @@ igraph_error_t igraph_i_all_st_cuts_pivot( 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]) { + if (IGRAPH_BIT_TEST(GammaS, i)) { IGRAPH_CHECK(igraph_vector_int_push_back(&GammaS_vec, i)); } } @@ -916,7 +914,7 @@ igraph_error_t igraph_i_all_st_cuts_pivot( IGRAPH_FINALLY_CLEAN(3); igraph_vector_int_destroy(&M); - igraph_vector_bool_destroy(&GammaS); + igraph_bitset_destroy(&GammaS); igraph_destroy(&domtree); igraph_vector_int_destroy(&leftout); igraph_destroy(&Sbar); @@ -1150,16 +1148,16 @@ igraph_error_t igraph_all_st_cuts(const igraph_t *graph, static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual, const igraph_marked_queue_int_t *S, - const igraph_vector_bool_t *active, + const igraph_bitset_t *active, igraph_vector_int_t *minimal) { igraph_integer_t no_of_nodes = igraph_vcount(residual); igraph_integer_t i; igraph_vector_int_t neis; - igraph_vector_bool_t connected_to_minimal; + igraph_bitset_t connected_to_minimal; IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&connected_to_minimal, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&connected_to_minimal, no_of_nodes); // Clear minimal nodes, we will push back to vector as required. igraph_vector_int_clear(minimal); @@ -1183,8 +1181,8 @@ static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual, /* If connected to node that is connected to node that is minimal, * this node is also connected to node that is minimal. */ - if (VECTOR(connected_to_minimal)[nei]) { - VECTOR(connected_to_minimal)[i] = true; + if (IGRAPH_BIT_TEST(connected_to_minimal, nei)) { + IGRAPH_BIT_SET(connected_to_minimal, i); break; } } @@ -1192,19 +1190,19 @@ static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual, /* If this node is not connected to node that is minimal, and this node * is minimal and active itself, set it as a minimal node. */ - if (!VECTOR(connected_to_minimal)[i] && VECTOR(*active)[i]) { + if (!IGRAPH_BIT_TEST(connected_to_minimal, i) && IGRAPH_BIT_TEST(*active, i)) { igraph_vector_int_push_back(minimal, i); /* Also mark this node as connected to minimal, to make sure that * any descendants will be marked as being connected to a minimal * node. */ - VECTOR(connected_to_minimal)[i] = true; + IGRAPH_BIT_SET(connected_to_minimal, i); } } } - igraph_vector_bool_destroy(&connected_to_minimal); + igraph_bitset_destroy(&connected_to_minimal); igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(2); @@ -1212,7 +1210,7 @@ static igraph_error_t igraph_i_all_st_mincuts_minimal(const igraph_t *residual, } typedef struct igraph_i_all_st_mincuts_data_t { - const igraph_vector_bool_t *active; + const igraph_bitset_t *active; } igraph_i_all_st_mincuts_data_t; static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, @@ -1225,7 +1223,7 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, void *arg) { igraph_i_all_st_mincuts_data_t *data = arg; - const igraph_vector_bool_t *active = data->active; + const igraph_bitset_t *active = data->active; igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t i, j; @@ -1270,7 +1268,7 @@ static igraph_error_t igraph_i_all_st_mincuts_pivot(const igraph_t *graph, IGRAPH_VECTOR_INT_INIT_FINALLY(&Isv_min, 0); *v = VECTOR(M)[i]; /* TODO: restricted == keep ? */ - IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v,/*roots=*/ NULL, + IGRAPH_CHECK(igraph_bfs(graph, /*root=*/ *v, /*roots=*/ NULL, /*mode=*/ IGRAPH_IN, /*unreachable=*/ false, /*restricted=*/ &keep, /*order=*/ &Isv_min, /*rank=*/ NULL, /*parents=*/ NULL, /*pred=*/ NULL, @@ -1351,7 +1349,7 @@ igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value 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_bitset_t VE1bool; igraph_integer_t i, nocuts; igraph_integer_t proj_nodes; igraph_vector_int_t revmap_ptr, revmap_next; @@ -1407,7 +1405,7 @@ igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value /*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)); + IGRAPH_CHECK(igraph_simplify(&residual, /*remove_multiple=*/ true, /*remove_loops=*/ true, /*edge_comb=*/ NULL)); /* We relabel the residual graph so that it is in topological sort order. */ igraph_integer_t no_of_nodes_residual = igraph_vcount(&residual); @@ -1447,19 +1445,18 @@ igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value /* -------------------------------------------------------------------- */ /* Determine the active vertices in the projection */ - IGRAPH_CHECK(igraph_vector_bool_init(&VE1bool, proj_nodes)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &VE1bool); + IGRAPH_BITSET_INIT_FINALLY(&VE1bool, proj_nodes); 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]; - if (!VECTOR(VE1bool)[pfrom]) { - VECTOR(VE1bool)[pfrom] = true; + if (!IGRAPH_BIT_TEST(VE1bool, pfrom)) { + IGRAPH_BIT_SET(VE1bool, pfrom); } - if (!VECTOR(VE1bool)[pto]) { - VECTOR(VE1bool)[pto] = true; + if (!IGRAPH_BIT_TEST(VE1bool, pto)) { + IGRAPH_BIT_SET(VE1bool, pto); } } } @@ -1563,7 +1560,7 @@ igraph_error_t igraph_all_st_mincuts(const igraph_t *graph, igraph_real_t *value igraph_vector_int_destroy(&cut); igraph_estack_destroy(&T); igraph_marked_queue_int_destroy(&S); - igraph_vector_bool_destroy(&VE1bool); + igraph_bitset_destroy(&VE1bool); igraph_vector_int_destroy(&NtoL); igraph_destroy(&residual); igraph_vector_destroy(&flow); diff --git a/src/games/callaway_traits.c b/src/games/callaway_traits.c index e4ce24b69f..a1edfe9a3e 100644 --- a/src/games/callaway_traits.c +++ b/src/games/callaway_traits.c @@ -57,12 +57,12 @@ * \param types Number of node types. * \param edges_per_step The number of connections tried in each time step. * \param type_dist Vector giving the distribution of the vertex types. - * If \c NULL, the distribution is assumed to be uniform. + * If \c NULL, the distribution is assumed to be uniform. * \param pref_matrix Matrix giving the connection probabilities for - * the vertex types. + * the vertex types. * \param directed Logical, whether to generate a directed graph. * \param node_type_vec An initialized vector or \c NULL. - * If not \c NULL, the type of each node will be stored here. + * If not \c NULL, the type of each node will be stored here. * \return Error code. * * Added in version 0.2. @@ -158,9 +158,7 @@ igraph_error_t igraph_callaway_traits_game(igraph_t *graph, igraph_integer_t nod IGRAPH_CHECK(igraph_vector_int_resize(nodetypes, nodes)); } else { nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); - if (! nodetypes) { - IGRAPH_ERROR("Insufficient memory for callaway_traits_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(nodetypes, "Insufficient memory for Callaway traits game."); IGRAPH_FINALLY(igraph_free, nodetypes); IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); } diff --git a/src/games/chung_lu.c b/src/games/chung_lu.c new file mode 100644 index 0000000000..c3af93dc2e --- /dev/null +++ b/src/games/chung_lu.c @@ -0,0 +1,323 @@ +/* + IGraph library. + Copyright (C) 2024 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_games.h" + +#include "igraph_constructors.h" +#include "igraph_interface.h" +#include "igraph_random.h" +#include "igraph_vector.h" + +#include "core/interruption.h" +#include "math/safe_intop.h" + +#include /* isfinite() */ + +/* This implementation follows the ideas in: + * + * Joel Miller and Aric Hagberg, + * Efficient generation of networks with given expected degrees + * (2011) + * + * It is analogous to the method used in igraph_erdos_renyi_game_gnp() + * and has linear complexity in the number of edges. + */ + +static igraph_error_t check_expected_degrees(const igraph_vector_t *weights) { + igraph_real_t minw, maxw; + + igraph_vector_minmax(weights, &minw, &maxw); + + if (minw < 0) { + IGRAPH_ERRORF("Vertex weights must not be negative in Chung-Lu model, got %g.", IGRAPH_EINVAL, minw); + } + + /* Catches both NaN and +Inf. */ + if (! isfinite(maxw)) { + IGRAPH_ERRORF("Vertex weights must be finite, got %g.", IGRAPH_EINVAL, maxw); + } + + return IGRAPH_SUCCESS; +} + +/** + * \function igraph_chung_lu_game + * \brief Samples graphs from the Chung-Lu model. + * + * \experimental + * + * The Chung-Lu model is useful for generating random graphs with fixed + * expected degrees. This function implements both the original model of Chung + * and Lu, as well as some additional variants with useful properties. + * + * + * In the original Chung-Lu model, each pair of vertices \c i and \c j is + * connected with independent probability p_ij = w_i w_j / S, + * where \c w_i is a weight associated with vertex \c i and + * S = sum_k w_k is the sum of weights. In the directed variant, + * vertices have both out-weights, w^out, and in-weights, + * w^in, with equal sums, + * S = sum_k w^out_k = sum_k w^in_k. + * The connection probability between \c i and \c j is + * p_ij = w^out_i w^in_j / S. + * + * + * This model is commonly used to create random graphs with a fixed \em expected + * degree sequence. The expected degree of vertex \c i is approximately equal + * to the weight \c w_i. Specifically, if the graph is directed and self-loops + * are allowed, then the expected out- and in-degrees are precisely + * w^out and w^in. If self-loops are disallowed, + * then the expected out- and in-degrees are w^out (S - w^in) / S + * and w^in (S - w^out) / S, respectively. If the graph is + * undirected, then the expected degrees with and without self-loops are + * w (S + w) / S and w (S - w) / S, respectively. + * + * + * A limitation of the original Chung-Lu model is that when some of the + * weights are large, the formula for \c p_ij yields values larger than 1. + * Chung and Lu's original paper exludes the use of such weights. When + * p_ij > 1, this function simply issues a warning and creates + * a connection between \c i and \c j. However, in this case the expected degrees + * will no longer relate to the weights in the manner stated above. Thus the + * original Chung-Lu model cannot produce certain (large) expected degrees. + * + * + * The overcome this limitation, this function implements additional variants of + * the model, with modified expressions for the connection probability \c p_ij + * between vertices \c i and \c j. Let q_ij = w_i w_j / S, or + * q_ij = w^out_i w^in_j / S in the directed case. All model + * variants become equivalent in the limit of sparse graphs where \c q_ij + * approaches zero. In the original Chung-Lu model, selectable by setting + * \p variant to \c IGRAPH_CHUNG_LU_ORIGINAL, p_ij = min(q_ij, 1). + * The \c IGRAPH_CHUNG_LU_MAXENT variant, sometiems referred to a the generalized + * random graph, uses p_ij = q_ij / (1 + q_ij), and is equivalent + * to a maximum entropy model (i.e. exponential random graph model) with + * a constraint on expected degrees; see Park and Newman (2004), Section B, + * setting exp(-Theta_ij) = w_i w_j / S. This model is also + * discussed by Britton, Deijfen and Martin-Löf (2006). By virtue of being + * a degree-constrained maximum entropy model, it graphs with the same degree + * sequence are produced with the same probability. + * A third variant can be requested with \c IGRAPH_CHUNG_LU_NR, and uses + * p_ij = 1 - exp(-q_ij). This is the underlying simple graph + * of a multigraph model introduced by Norros and Reittu (2006). + * For a discussion of these three model variants, see Section 16.4 of + * Bollobás, Janson, Riordan (2007), as well as Van Der Hofstad (2013). + * + * + * References: + * + * + * Chung F and Lu L: Connected components in a random graph with given + * degree sequences. Annals of Combinatorics 6, 125-145 (2002). + * https://doi.org/10.1007/PL00012580 + * + * + * Miller JC and Hagberg A: + * Efficient Generation of Networks with Given Expected Degrees (2011). + * https://doi.org/10.1007/978-3-642-21286-4_10 + * + * + * Park J and Newman MEJ: Statistical mechanics of networks. + * Physical Review E 70, 066117 (2004). + * https://doi.org/10.1103/PhysRevE.70.066117 + * + * + * Britton T, Deijfen M, Martin-Löf A: + * Generating Simple Random Graphs with Prescribed Degree Distribution. + * J Stat Phys 124, 1377–1397 (2006). + * https://doi.org/10.1007/s10955-006-9168-x + * + * + * Norros I and Reittu H: On a conditionally Poissonian graph process. + * Advances in Applied Probability 38, 59–75 (2006). + * https://doi.org/10.1239/aap/1143936140 + * + * + * Bollobás B, Janson S, Riordan O: + * The phase transition in inhomogeneous random graphs. + * Random Struct Algorithms 31, 3–122 (2007). + * https://doi.org/10.1002/rsa.20168 + * + * + * Van Der Hofstad R: Critical behavior in inhomogeneous random graphs. + * Random Struct Algorithms 42, 480–508 (2013). + * https://doi.org/10.1002/rsa.20450 + * + * \param graph Pointer to an uninitialized graph object. + * \param out_weights A vector of non-negative vertex weights (or out-weights). + * In sparse graphs these will be approximately equal to the expected + * (out-)degrees. + * \param in_weights A vector of non-negative in-weights, approximately equal + * to the expected in-degrees in sparse graphs. May be set to \c NULL, + * in which case undirected graphs are generated. + * \param loops Whether to allow the creation of self-loops. Since vertex + * pairs are connected independently, setting this to false is equivalent + * to simply discarding self-loops from an existing loopy Chung-Lu graph. + * \param variant The model variant to sample from, with different definitions + * of the connection probability between vertices \c i and \c j. Given + * q_ij = w_i w_j / S, the following formulations are available: + * \clist + * \cli IGRAPH_CHUNG_LU_ORIGINAL + * the original Chung-Lu model, p_ij = min(q_ij, 1). + * \cli IGRAPH_CHUNG_LU_MAXENT + * maximum entropy model with fixed expected degrees, + * p_ij = q_ij / (1 + q_ij). + * \cli IGRAPH_CHUNG_LU_NR + * Norros and Reittu's model, p_ij = 1 - exp(-q_ij). + * \endclist + * \return Error code. + * + * \sa \ref igraph_static_fitness_game() implements a similar model with + * a sharp constraint on the number of edges; + * \ref igraph_degree_sequence_game() samples random graphs with sharply + * specified degrees; \ref igraph_erdos_renyi_game_gnp() creates random + * graphs with a fixed connection probability \c p between all vertex pairs. + * + * Time complexity: O(|E| + |V|), linear in the number of edges. + */ +igraph_error_t igraph_chung_lu_game(igraph_t *graph, + const igraph_vector_t *out_weights, + const igraph_vector_t *in_weights, + igraph_bool_t loops, + igraph_chung_lu_t variant) { + + const igraph_integer_t no_of_nodes = igraph_vector_size(out_weights); + const igraph_bool_t directed = in_weights != NULL; + igraph_vector_int_t edges, idx; + igraph_real_t wsum = igraph_vector_sum(out_weights); + igraph_bool_t warned = false; + int iter = 0; + + /* Necessitated by floating point arithmetic used in the implementation. */ + if (no_of_nodes >= IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Number of vertices is too large.", IGRAPH_EOVERFLOW); + } + + if (! directed) { + in_weights = out_weights; + } else if (igraph_vector_size(in_weights) != no_of_nodes) { + IGRAPH_ERROR("Vertex out- and in-weight vectors must have the same length.", IGRAPH_EINVAL); + } + + if (no_of_nodes == 0) { + return igraph_empty(graph, 0, directed); + } + + IGRAPH_CHECK(check_expected_degrees(out_weights)); + + if (directed) { + IGRAPH_CHECK(check_expected_degrees(in_weights)); + + if (igraph_vector_sum(in_weights) != wsum) { + IGRAPH_ERRORF("Sum of out- and in-weights must be the same, got %g and %g, respectively.", + IGRAPH_EINVAL, wsum, igraph_vector_sum(in_weights)); + } + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_VECTOR_INT_INIT_FINALLY(&idx, 0); + + igraph_vector_qsort_ind(in_weights, &idx, IGRAPH_DESCENDING); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < no_of_nodes; i++) { + igraph_integer_t vi, vj; + igraph_real_t wi, wj; + igraph_real_t p, q; + + igraph_integer_t j = directed ? 0 : i; + + vi = VECTOR(idx)[i]; + wi = VECTOR(*out_weights)[vi]; + + if (wi == 0) { + if (directed) continue; else break; + } + + p = 1; + + while (true) { + igraph_real_t gap = RNG_GEOM(p); + + /* This formulation not only terminates the loop when necessary, + * but also protects against overflow when 'p' is very small + * and 'gap' becomes very large, perhaps larger than representable + * in an igraph_integer_t. */ + if (gap >= no_of_nodes-j) { + break; + } + + j += gap; + + vj = VECTOR(idx)[j]; + wj = VECTOR(*in_weights)[vj]; + + q = wi * wj / wsum; + switch (variant) { + case IGRAPH_CHUNG_LU_ORIGINAL: + if (q > 1) { + q = 1; + if (! warned && (loops || vi != vj)) { + IGRAPH_WARNINGF( + "Expected degrees %g and %g lead to a calculated connection probability " + "larger than 1 in Chung-Lu model. The degrees of the resulting graph will " + "not be consistent with the given input.", wi, wj); + warned = true; + } + } + break; + case IGRAPH_CHUNG_LU_MAXENT: + q = q / (1 + q); + break; + case IGRAPH_CHUNG_LU_NR: + q = 1 - exp(-q); + break; + default: + IGRAPH_ERROR("Invalid Chung-Lu variant.", IGRAPH_EINVAL); + } + + /* A probability of zero must not be passed to RNG_GEOM(), + * so we catch this case here. */ + if (q == 0) { + break; + } + + if (RNG_UNIF01() < q/p && (loops || vi != vj)) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, vi)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, vj)); + } + + p = q; + + j++; + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 16); + } + } + RNG_END(); + + igraph_vector_int_destroy(&idx); + 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/games/citations.c b/src/games/citations.c index 1cac157c69..4c55b237e4 100644 --- a/src/games/citations.c +++ b/src/games/citations.c @@ -69,7 +69,7 @@ static void igraph_i_citing_cited_type_game_free ( * * \param graph Pointer to an uninitialized graph object, the result * will be stored here. - * \param node The number of vertices in the network. + * \param nodes The number of vertices in the network. * \param edges_per_node The number of edges to add in each time * step. * \param agebins The number of age bins to use. @@ -348,14 +348,13 @@ 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; if (!s->sumtrees) { return; } - for (i = 0; i < s->no; i++) { + for (igraph_integer_t i = 0; i < s->no; i++) { igraph_psumtree_destroy(&s->sumtrees[i]); } - igraph_free(s->sumtrees); + IGRAPH_FREE(s->sumtrees); } /** diff --git a/src/games/degree_sequence.c b/src/games/degree_sequence.c index 6970133ac7..fd9735dfd7 100644 --- a/src/games/degree_sequence.c +++ b/src/games/degree_sequence.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ -/* vim:set ts=4 sw=4 sts=4 et: */ /* IGraph library. - Copyright (C) 2003-2021 The igraph development team + Copyright (C) 2003-2024 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_games.h" @@ -42,19 +37,18 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap const igraph_vector_int_t *out_seq, const igraph_vector_int_t *in_seq) { + const igraph_bool_t directed = (in_seq != NULL && igraph_vector_int_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; + igraph_bool_t graphical; igraph_integer_t no_of_nodes, no_of_edges; - igraph_integer_t *bag1 = 0, *bag2 = 0; + igraph_integer_t *bag1, *bag2; igraph_integer_t bagp1 = 0, bagp2 = 0; - igraph_vector_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t i, j; + igraph_vector_int_t edges; - IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, °seq_ok)); - if (!degseq_ok) { - IGRAPH_ERROR(in_seq ? "No directed graph can realize the given degree sequences" : - "No undirected graph can realize the given degree sequence", IGRAPH_EINVAL); + IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_LOOPS_SW | IGRAPH_MULTI_SW, &graphical)); + if (!graphical) { + IGRAPH_ERROR(in_seq ? "No directed graph can realize the given degree sequences." : + "No undirected graph can realize the given degree sequence.", IGRAPH_EINVAL); } IGRAPH_CHECK(igraph_i_safe_vector_int_sum(out_seq, &outsum)); @@ -66,24 +60,20 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap no_of_edges = directed ? outsum : outsum / 2; bag1 = IGRAPH_CALLOC(outsum, igraph_integer_t); - if (bag1 == 0) { - IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(bag1, "Insufficient memory for sampling from configuration model."); IGRAPH_FINALLY(igraph_free, bag1); - for (i = 0; i < no_of_nodes; i++) { - for (j = 0; j < VECTOR(*out_seq)[i]; j++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < VECTOR(*out_seq)[i]; j++) { bag1[bagp1++] = i; } } if (directed) { bag2 = IGRAPH_CALLOC(insum, igraph_integer_t); - if (bag2 == 0) { - IGRAPH_ERROR("Cannot sample with configuration model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(bag2, "Insufficient memory for sampling from configuration model."); IGRAPH_FINALLY(igraph_free, bag2); - for (i = 0; i < no_of_nodes; i++) { - for (j = 0; j < VECTOR(*in_seq)[i]; j++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < VECTOR(*in_seq)[i]; j++) { bag2[bagp2++] = i; } } @@ -95,7 +85,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap RNG_BEGIN(); if (directed) { - for (i = 0; i < no_of_edges; i++) { + for (igraph_integer_t 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 */ @@ -105,7 +95,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap bagp1--; bagp2--; } } else { - for (i = 0; i < no_of_edges; i++) { + for (igraph_integer_t 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 */ @@ -135,7 +125,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration(igraph_t *grap } static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( - igraph_t *graph, const igraph_vector_int_t *seq) { + igraph_t *graph, const igraph_vector_int_t *seq) { igraph_vector_int_t stubs; igraph_vector_int_t *neis; @@ -146,10 +136,11 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( igraph_integer_t from, to, dummy; igraph_integer_t i, j, k; igraph_integer_t no_of_nodes, outsum = 0; - igraph_bool_t degseq_ok; + igraph_bool_t graphical; + int iter = 0; - IGRAPH_CHECK(igraph_is_graphical(seq, 0, IGRAPH_SIMPLE_SW, °seq_ok)); - if (!degseq_ok) { + IGRAPH_CHECK(igraph_is_graphical(seq, 0, IGRAPH_SIMPLE_SW, &graphical)); + if (!graphical) { IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence.", IGRAPH_EINVAL); } @@ -173,7 +164,7 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( * until it finally succeeds. */ finished = false; while (!finished) { - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); /* Be optimistic :) */ failed = false; @@ -278,18 +269,18 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_undirected( 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_int_t in_stubs; igraph_vector_int_t out_stubs; 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_set_t incomplete_in_vertices; - igraph_set_t incomplete_out_vertices; + igraph_vector_int_t residual_in_degrees, residual_out_degrees; + igraph_set_t incomplete_in_vertices, incomplete_out_vertices; igraph_integer_t from, to; igraph_integer_t i, j, k; igraph_integer_t no_of_nodes, outsum; + int iter = 0; IGRAPH_CHECK(igraph_is_graphical(out_seq, in_seq, IGRAPH_SIMPLE_SW, °_seq_ok)); if (!deg_seq_ok) { @@ -321,7 +312,7 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t * until it finally succeeds. */ finished = false; while (!finished) { - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); /* Be optimistic :) */ failed = false; @@ -412,7 +403,7 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t IGRAPH_FINALLY_CLEAN(6); /* Create the graph */ - IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); + IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, true)); /* Clear the adjacency list */ igraph_adjlist_destroy(&al); @@ -433,13 +424,14 @@ static igraph_error_t igraph_i_degree_sequence_game_fast_heur_directed(igraph_t 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_int_t edges; - igraph_bool_t degseq_ok; + igraph_bool_t graphical; igraph_vector_ptr_t adjlist; igraph_integer_t i, j; igraph_integer_t vcount, ecount, stub_count; + int iter = 0; - IGRAPH_CHECK(igraph_is_graphical(degseq, NULL, IGRAPH_SIMPLE_SW, °seq_ok)); - if (!degseq_ok) { + IGRAPH_CHECK(igraph_is_graphical(degseq, NULL, IGRAPH_SIMPLE_SW, &graphical)); + if (!graphical) { IGRAPH_ERROR("No simple undirected graph can realize the given degree sequence.", IGRAPH_EINVAL); } @@ -467,9 +459,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); for (i = 0; i < vcount; ++i) { igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t); - if (! set) { - IGRAPH_ERROR("Cannot sample from configuration model (simple graphs).", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(set, "Insufficient memory for configuation model (simple graphs)."); IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*degseq)[i])); @@ -523,7 +513,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); } - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); } RNG_END(); @@ -532,7 +522,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec igraph_vector_int_destroy(&stubs); IGRAPH_FINALLY_CLEAN(2); - IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 0)); + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, IGRAPH_UNDIRECTED)); igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); @@ -543,15 +533,17 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_undirec 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_int_t edges; - igraph_bool_t degseq_ok; + igraph_bool_t graphical; igraph_vector_ptr_t adjlist; igraph_integer_t i, j; igraph_integer_t vcount, ecount; + int iter = 0; - IGRAPH_CHECK(igraph_is_graphical(out_deg, in_deg, IGRAPH_SIMPLE_SW, °seq_ok)); - if (!degseq_ok) { + IGRAPH_CHECK(igraph_is_graphical(out_deg, in_deg, IGRAPH_SIMPLE_SW, &graphical)); + if (!graphical) { IGRAPH_ERROR("No simple directed graph can realize the given degree sequence", IGRAPH_EINVAL); } @@ -586,9 +578,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &adjlist); for (i = 0; i < vcount; ++i) { igraph_set_t *set = IGRAPH_CALLOC(1, igraph_set_t); - if (! set) { - IGRAPH_ERROR("Out of memory", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(set, "Insufficient memory for directed configuration model (simple graphs)."); IGRAPH_CHECK(igraph_set_init(set, 0)); VECTOR(adjlist)[i] = set; IGRAPH_CHECK(igraph_set_reserve(set, VECTOR(*out_deg)[i])); @@ -640,7 +630,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe igraph_set_clear((igraph_set_t *) VECTOR(adjlist)[j]); } - IGRAPH_ALLOW_INTERRUPTION(); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); } RNG_END(); @@ -650,7 +640,7 @@ static igraph_error_t igraph_i_degree_sequence_game_configuration_simple_directe igraph_vector_int_destroy(&in_stubs); IGRAPH_FINALLY_CLEAN(3); - IGRAPH_CHECK(igraph_create(graph, &edges, vcount, /* directed = */ 1)); + IGRAPH_CHECK(igraph_create(graph, &edges, vcount, IGRAPH_DIRECTED)); igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); @@ -677,6 +667,28 @@ igraph_error_t igraph_i_degree_sequence_game_edge_switching( * \function igraph_degree_sequence_game * \brief Generates a random graph with a given degree sequence. * + * This function generates random graphs with a prescribed degree sequence. + * Several sampling methods are available, which respect different constraints + * (simple graph or multigraphs, connected graphs, etc.), and provide different + * tradeoffs between performance and unbiased sampling. See Section 2.1 of + * Horvát and Modes (2021) for an overview of sampling techniques for graphs + * with fixed degrees. + * + * + * References: + * + * + * Fabien Viger, and Matthieu Latapy: + * Efficient and Simple Generation of Random Simple Connected Graphs with Prescribed Degree Sequence, + * Journal of Complex Networks 4, no. 1, pp. 15–37 (2015). + * https://doi.org/10.1093/comnet/cnv013. + * + * + * Szabolcs Horvát, and Carl D Modes: + * Connectedness Matters: Construction and Exact Random Sampling of Connected Networks, + * Journal of Physics: Complexity 2, no. 1, pp. 015008 (2021). + * https://doi.org/10.1088/2632-072x/abced5. + * * \param graph Pointer to an uninitialized graph object. * \param out_deg The degree sequence for an undirected graph (if * \p in_seq is \c NULL or of length zero), or the out-degree @@ -745,11 +757,16 @@ igraph_error_t igraph_i_degree_sequence_game_edge_switching( * should match for directed graphs. * * Time complexity: O(|V|+|E|), the number of vertices plus the number of edges - * for \c IGRAPH_DEGSEQ_SIMPLE. The time complexity of the - * other modes is not known. + * for \c IGRAPH_DEGSEQ_CONFIGURATION and \c IGRAPH_DEGSEQ_EDGE_SWITCHING_SIMPLE. + * The time complexity of the other modes is not known. * - * \sa \ref igraph_barabasi_game(), \ref igraph_erdos_renyi_game_gnm(), - * \ref igraph_erdos_renyi_game_gnp(), \ref igraph_is_graphical() + * \sa \ref igraph_is_graphical() to determine if there exist graphs with a certain + * degree sequence; \ref igraph_erdos_renyi_game_gnm() to generate graphs with a + * fixed number of edges, without any degree constraints; \ref igraph_chung_lu_game() + * and \ref igraph_static_fitness_game() to sample random graphs with a prescribed + * \em expected degree sequence (but variable actual degrees); + * \ref igraph_realize_degree_sequence() and \ref igraph_realize_bipartite_degree_sequence() + * to generate a single (non-random) graph with given degrees. * * \example examples/simple/igraph_degree_sequence_game.c */ @@ -758,7 +775,7 @@ igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_ const igraph_vector_int_t *in_deg, igraph_degseq_t method) { if (in_deg && igraph_vector_int_empty(in_deg) && !igraph_vector_int_empty(out_deg)) { - in_deg = 0; + in_deg = NULL; } switch (method) { @@ -769,14 +786,14 @@ igraph_error_t igraph_degree_sequence_game(igraph_t *graph, const igraph_vector_ return igraph_degree_sequence_game_vl(graph, out_deg, in_deg); case IGRAPH_DEGSEQ_FAST_HEUR_SIMPLE: - if (in_deg == 0) { + if (! in_deg) { return igraph_i_degree_sequence_game_fast_heur_undirected(graph, out_deg); } else { return igraph_i_degree_sequence_game_fast_heur_directed(graph, out_deg, in_deg); } case IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE: - if (in_deg == 0) { + if (! in_deg) { return igraph_i_degree_sequence_game_configuration_simple_undirected(graph, out_deg); } else { return igraph_i_degree_sequence_game_configuration_simple_directed(graph, out_deg, in_deg); diff --git a/src/games/erdos_renyi.c b/src/games/erdos_renyi.c index b7b2dbd6de..c0e0e346e8 100644 --- a/src/games/erdos_renyi.c +++ b/src/games/erdos_renyi.c @@ -34,17 +34,93 @@ /** * \section about_games * - * Games are randomized graph generators. Randomization means that - * they generate a different graph every time you call them. + * Games are random graph generators, i.e. they generate a different + * graph every time they are called. igraph includes many such generators. + * Some implement stochastic graph construction processes inspired by real-world + * mechanics, such as preferential attachment, while others are designed to + * produce graphs with certain used properties (e.g. fixed number of edges, + * fixed degrees, etc.) */ +/* This implementation is used only with very large vertex counts, above + * sqrt(MAX_EXACT_REAL) ~ 100 million, when the default implementation would + * fail due to overflow. While this version avoids overflow and uses less memory, + * it is also slower than the default implementation. */ +static igraph_error_t gnp_large( + igraph_t *graph, igraph_integer_t n, igraph_real_t p, + igraph_bool_t directed, igraph_bool_t loops, igraph_integer_t ecount_estimate +) { + + igraph_vector_int_t edges; + int iter = 0; + + /* Necessitated by floating point arithmetic used in the implementation. */ + if (n >= IGRAPH_MAX_EXACT_REAL) { + IGRAPH_ERROR("Number of vertices is too large.", IGRAPH_EOVERFLOW); + } + + if (ecount_estimate > IGRAPH_ECOUNT_MAX) { + ecount_estimate = IGRAPH_ECOUNT_MAX; + } + + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2*ecount_estimate)); + + RNG_BEGIN(); + for (igraph_integer_t i=0; i < n; i++) { + igraph_integer_t j = directed ? 0 : i; + + while (true) { + igraph_real_t gap = RNG_GEOM(p); + + /* This formulation not only terminates the loop when necessary, + * but also protects against overflow when 'p' is very small + * and 'gap' becomes very large, perhaps larger than representable + * in an igraph_integer_t. */ + if (gap >= n - j) { + break; + } + + j += gap; + + if (loops || i != j) { + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, i)); + IGRAPH_CHECK(igraph_vector_int_push_back(&edges, j)); + } + + j++; + + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); + } + } + RNG_END(); + + IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); + igraph_vector_int_destroy(&edges); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + /** * \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. + * In the G(n, p) Erdős-Rényi model, also known as the Gilbert model, + * or Bernoulli random graph, a graph with \p n vertices is generated such that + * every possible edge is included in the graph independently with probability + * \p p. This is equivalent to a maximum entropy random graph model model with + * a constraint on the \em expected edge count. Setting p = 1/2 + * generates all graphs on \p n vertices with the same probability. + * + * + * The expected mean degree of the graph is approximately p n; + * set p = k/n when a mean degree of approximately \c k is + * desired. More precisely, the expected mean degree is p(n-1) + * in (undirected or directed) graphs without self-loops, + * p(n+1) in undirected graphs with self-loops, and + * p n in directed graphs with self-loops. * * \param graph Pointer to an uninitialized graph object. * \param n The number of vertices in the graph. @@ -58,8 +134,12 @@ * 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() + * \sa \ref igraph_erdos_renyi_game_gnm() to generate random graphs with + * a sharply fixed edge count; \ref igraph_chung_lu_game() and + * \ref igraph_static_fitness_game() to generate random graphs with a + * fixed expected degree sequence; \ref igraph_bipartite_game_gnm() for the + * bipartite version of this model; \ref igraph_barabasi_game() and + * \ref igraph_growing_random_game() for other commonly used random graph models. * * \example examples/simple/igraph_erdos_renyi_game_gnp.c */ @@ -76,7 +156,6 @@ igraph_error_t igraph_erdos_renyi_game_gnp( 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; - igraph_integer_t vsize; int iter = 0; if (n < 0) { @@ -91,9 +170,8 @@ igraph_error_t igraph_erdos_renyi_game_gnp( } else if (p == 1.0) { IGRAPH_CHECK(igraph_full(graph, n, directed, loops)); } else { - igraph_real_t maxedges = n, last; - igraph_integer_t maxedges_int; + igraph_integer_t ecount_estimate, ecount; if (directed && loops) { maxedges *= n; @@ -105,12 +183,15 @@ igraph_error_t igraph_erdos_renyi_game_gnp( maxedges *= (n - 1) / 2.0; } + IGRAPH_CHECK(igraph_i_safe_floor(maxedges * p * 1.1, &ecount_estimate)); + if (maxedges > IGRAPH_MAX_EXACT_REAL) { - IGRAPH_ERROR("Too many vertices, overflow in maximum number of edges.", IGRAPH_EOVERFLOW); + /* Use a slightly slower, but overflow-free implementation. */ + return gnp_large(graph, n, p, directed, loops, ecount_estimate); } + 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, ecount_estimate)); RNG_BEGIN(); @@ -124,13 +205,17 @@ igraph_error_t igraph_erdos_renyi_game_gnp( RNG_END(); + ecount = igraph_vector_size(&s); + if (ecount > IGRAPH_ECOUNT_MAX) { + IGRAPH_ERROR("Overflow in number of edges.", IGRAPH_EOVERFLOW); + } + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); - IGRAPH_CHECK(igraph_vector_int_reserve(&edges, igraph_vector_size(&s) * 2)); + IGRAPH_CHECK(igraph_vector_int_reserve(&edges, 2*ecount)); iter = 0; - vsize = igraph_vector_size(&s); if (directed && loops) { - for (igraph_integer_t i = 0; i < vsize; i++) { + for (igraph_integer_t i = 0; i < ecount; 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); @@ -138,7 +223,7 @@ igraph_error_t igraph_erdos_renyi_game_gnp( IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); } } else if (directed && !loops) { - for (igraph_integer_t i = 0; i < vsize; i++) { + for (igraph_integer_t i = 0; i < ecount; 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; if (from == to) { @@ -149,7 +234,7 @@ igraph_error_t igraph_erdos_renyi_game_gnp( IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); } } else if (!directed && loops) { - for (igraph_integer_t i = 0; i < vsize; i++) { + for (igraph_integer_t i = 0; i < ecount; 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); @@ -157,7 +242,7 @@ igraph_error_t igraph_erdos_renyi_game_gnp( IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 14); } } else { /* !directed && !loops */ - for (igraph_integer_t i = 0; i < vsize; i++) { + for (igraph_integer_t i = 0; i < ecount; 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); @@ -181,8 +266,8 @@ igraph_error_t igraph_erdos_renyi_game_gnp( * \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. + * In the G(n, m) Erdős-Rényi model, a graph with \p n vertices + * and \p m edges is generated uniformly at random. * * \param graph Pointer to an uninitialized graph object. * \param n The number of vertices in the graph. @@ -196,8 +281,12 @@ igraph_error_t igraph_erdos_renyi_game_gnp( * 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() + * \sa \ref igraph_erdos_renyi_game_gnp() to sample from the related + * G(n, p) model, which constrains the \em expected edge count; + * \ref igraph_degree_sequence_game() to constrain the degree sequence; + * \ref igraph_bipartite_game_gnm() for the bipartite version of this model; + * \ref igraph_barabasi_game() and \ref igraph_growing_random_game() for other + * commonly used random graph models. * * \example examples/simple/igraph_erdos_renyi_game_gnm.c */ diff --git a/src/games/forestfire.c b/src/games/forestfire.c index 9b5d073f30..a5cb2aea6f 100644 --- a/src/games/forestfire.c +++ b/src/games/forestfire.c @@ -40,8 +40,7 @@ typedef struct igraph_i_forest_fire_data_t { static void igraph_i_forest_fire_free(igraph_i_forest_fire_data_t *data) { - igraph_integer_t i; - for (i = 0; i < data->no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < data->no_of_nodes; i++) { igraph_vector_int_destroy(data->inneis + i); igraph_vector_int_destroy(data->outneis + i); } @@ -140,15 +139,13 @@ igraph_error_t igraph_forest_fire_game(igraph_t *graph, igraph_integer_t nodes, IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); inneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); - if (!inneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(inneis, "Insufficient memory for forest fire model."); IGRAPH_FINALLY(igraph_free, inneis); + outneis = IGRAPH_CALLOC(no_of_nodes, igraph_vector_int_t); - if (!outneis) { - IGRAPH_ERROR("Cannot run forest fire model.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(outneis, "Insufficient memory for forest fire model."); IGRAPH_FINALLY(igraph_free, outneis); + data.inneis = inneis; data.outneis = outneis; data.no_of_nodes = no_of_nodes; diff --git a/src/games/growing_random.c b/src/games/growing_random.c index cdbfe7fff6..79e5231f3f 100644 --- a/src/games/growing_random.c +++ b/src/games/growing_random.c @@ -65,8 +65,6 @@ igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, igraph_integer_t resp = 0; - igraph_integer_t i, j; - if (n < 0) { IGRAPH_ERROR("Invalid number of vertices.", IGRAPH_EINVAL); } @@ -88,8 +86,8 @@ igraph_error_t igraph_growing_random_game(igraph_t *graph, igraph_integer_t n, RNG_BEGIN(); - for (i = 1; i < no_of_nodes; i++) { - for (j = 0; j < no_of_neighbors; j++) { + for (igraph_integer_t i = 1; i < no_of_nodes; i++) { + for (igraph_integer_t j = 0; j < no_of_neighbors; j++) { if (citation) { igraph_integer_t to = RNG_INTEGER(0, i - 1); VECTOR(edges)[resp++] = i; diff --git a/src/games/preference.c b/src/games/preference.c index 1af55299c9..1a537efccf 100644 --- a/src/games/preference.c +++ b/src/games/preference.c @@ -154,9 +154,7 @@ igraph_error_t igraph_preference_game(igraph_t *graph, igraph_integer_t nodes, nodetypes = node_type_vec; } else { nodetypes = IGRAPH_CALLOC(1, igraph_vector_int_t); - if (nodetypes == 0) { - IGRAPH_ERROR("Insufficient memory for preference_game.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(nodetypes, "Insufficient memory for preference_game."); IGRAPH_FINALLY(igraph_free, nodetypes); IGRAPH_VECTOR_INT_INIT_FINALLY(nodetypes, nodes); } diff --git a/src/games/static_fitness.c b/src/games/static_fitness.c index 8c4c1f057d..e24e4ecba9 100644 --- a/src/games/static_fitness.c +++ b/src/games/static_fitness.c @@ -39,32 +39,44 @@ * \brief Non-growing random graph with edge probabilities proportional to node fitness scores. * * This game generates a directed or undirected random graph where the - * probability of an edge between vertices i and j depends on the fitness + * probability of an edge between vertices \c i and \c j depends on the fitness * scores of the two vertices involved. For undirected graphs, each vertex * has a single fitness score. For directed graphs, each vertex has an out- - * and an in-fitness, and the probability of an edge from i to j depends on - * the out-fitness of vertex i and the in-fitness of vertex j. + * and an in-fitness, and the probability of an edge from \c i to \c j depends on + * the out-fitness of vertex \c i and the in-fitness of vertex \c j. * * - * The generation process goes as follows. We start from N disconnected nodes - * (where N is given by the length of the fitness vector). Then we randomly - * select two vertices i and j, with probabilities proportional to their - * fitnesses. (When the generated graph is directed, i is selected according to - * the out-fitnesses and j is selected according to the in-fitnesses). If the + * The generation process goes as follows. We start from \c N disconnected nodes + * (where \c N is given by the length of the fitness vector). Then we randomly + * select two vertices \c i and \c j, with probabilities proportional to their + * fitnesses. (When the generated graph is directed, \c i is selected according to + * the out-fitnesses and \c j is selected according to the in-fitnesses). If the * vertices are not connected yet (or if multiple edges are allowed), we * connect them; otherwise we select a new pair. This is repeated until the * desired number of links are created. * * - * It can be shown that the \em expected degree of each vertex will be - * proportional to its fitness, although the actual, observed degree will not - * be. If you need to generate a graph with an exact degree sequence, consider - * \ref igraph_degree_sequence_game instead. + * The \em expected degree (though not the actual degree) of each vertex will be + * proportional to its fitness. This is exactly true when self-loops and multi-edges + * are allowed, and approximately true otherwise. If you need to generate a graph + * with an exact degree sequence, consider \ref igraph_degree_sequence_game() and + * \ref igraph_realize_degree_sequence() instead. + * + * + * To generate random undirected graphs with a given expected degree sequence, set + * \p fitness_out (and in the directed case \p fitness_out) to the desired expected + * degrees, and \p no_of_edges to the corresponding edge count, i.e. half the sum of + * expected degrees in the undirected case, and the sum of out- or in-degrees in the + * directed case. + * + * + * This model is similar to the better-known Chung-Lu model, implemented in igraph + * as \ref igraph_chung_lu_game(), but with a sharply fixed edge count. * * * This model is commonly used to generate static scale-free networks. To * achieve this, you have to draw the fitness scores from the desired power-law - * distribution. Alternatively, you may use \ref igraph_static_power_law_game + * distribution. Alternatively, you may use \ref igraph_static_power_law_game() * which generates the fitnesses for you with a given exponent. * * @@ -91,22 +103,25 @@ * \c IGRAPH_ENOMEM: there is not enough * memory for the operation. * + * \sa \ref igraph_static_power_law_game(), \ref igraph_chung_lu_game(), + * \ref igraph_degree_sequence_game() + * * Time complexity: O(|V| + |E| log |E|). */ 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_int_t edges = IGRAPH_VECTOR_NULL; - igraph_integer_t no_of_nodes; - igraph_integer_t outnodes, innodes, nodes; + + const igraph_integer_t no_of_nodes = igraph_vector_size(fitness_out); + const igraph_bool_t directed = (fitness_in != NULL); + igraph_vector_int_t edges; igraph_vector_t cum_fitness_in, cum_fitness_out; igraph_vector_t *p_cum_fitness_in, *p_cum_fitness_out; igraph_real_t x, max_in, max_out; igraph_real_t max_no_of_edges; - igraph_bool_t is_directed = (fitness_in != 0); igraph_real_t num_steps; - igraph_integer_t step_counter = 0; - igraph_integer_t i, from, to, pos; + igraph_integer_t from, to, pos; + int iter = 0; IGRAPH_ASSERT(fitness_out != NULL); @@ -114,29 +129,30 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o IGRAPH_ERRORF("Number of edges cannot be negative, got %" IGRAPH_PRId ".", IGRAPH_EINVAL, no_of_edges); } - no_of_nodes = igraph_vector_size(fitness_out); + if (no_of_edges > IGRAPH_ECOUNT_MAX && (no_of_nodes == 0 && no_of_edges > 0)) { + IGRAPH_ERROR("Too many edges requested.", IGRAPH_EINVAL); + } + if (no_of_nodes == 0) { - IGRAPH_CHECK(igraph_empty(graph, 0, is_directed)); - return IGRAPH_SUCCESS; + return igraph_empty(graph, no_of_nodes, directed); } - if (is_directed && igraph_vector_size(fitness_in) != no_of_nodes) { - IGRAPH_ERROR("fitness_in must have the same size as fitness_out.", IGRAPH_EINVAL); + if (directed && igraph_vector_size(fitness_in) != no_of_nodes) { + IGRAPH_ERROR("The in-fitness vector must have the same length as the out-fitness vector.", IGRAPH_EINVAL); } /* Sanity checks for the fitnesses */ - if (igraph_vector_min(fitness_out) < 0) { - IGRAPH_ERROR("Fitness scores must be non-negative.", IGRAPH_EINVAL); - } - if (fitness_in != 0 && igraph_vector_min(fitness_in) < 0) { - IGRAPH_ERROR("Fitness scores must be non-negative.", IGRAPH_EINVAL); + if (igraph_vector_min(fitness_out) < 0 && (!directed || igraph_vector_min(fitness_in) < 0)) { + IGRAPH_ERROR("Fitness scores must not be negative.", IGRAPH_EINVAL); } - /* Avoid getting into an infinite loop when too many edges are requested */ - if (!multiple) { - if (is_directed) { + /* Avoid getting into an infinite loop when too many edges are requested. */ + { + igraph_integer_t nodes; + if (directed) { + igraph_integer_t outnodes, innodes; outnodes = innodes = nodes = 0; - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { if (VECTOR(*fitness_out)[i] != 0) { outnodes++; } @@ -150,7 +166,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o max_no_of_edges = ((igraph_real_t) outnodes) * innodes - (loops ? 0 : nodes); } else { nodes = 0; - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { if (VECTOR(*fitness_out)[i] != 0) { nodes++; } @@ -159,7 +175,14 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o ? nodes * ((igraph_real_t)nodes + 1) / 2 : nodes * ((igraph_real_t)nodes - 1) / 2; } - if (no_of_edges > max_no_of_edges) { + if (max_no_of_edges == 0 && no_of_edges > 0) { + /* This check is necessary even when multiple edges are allowed, + * for example when all fitnesses are zero or only one is nonzero + * not self-loops are disallowed. */ + IGRAPH_ERRORF("No edges are possible with the given fitness scores, " + "but %" IGRAPH_PRId " edges were requested.", IGRAPH_EINVAL, no_of_edges); + } + if (!multiple && no_of_edges > max_no_of_edges) { IGRAPH_ERROR("Too many edges requested.", IGRAPH_EINVAL); } } @@ -169,7 +192,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_out, fitness_out)); max_out = igraph_vector_tail(&cum_fitness_out); p_cum_fitness_out = &cum_fitness_out; - if (is_directed) { + if (directed) { IGRAPH_VECTOR_INIT_FINALLY(&cum_fitness_in, no_of_nodes); IGRAPH_CHECK(igraph_vector_cumsum(&cum_fitness_in, fitness_in)); max_in = igraph_vector_tail(&cum_fitness_in); @@ -189,7 +212,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o while (no_of_edges > 0) { /* Report progress after every 10000 edges */ - if ((step_counter++) % 10000 == 0) { + if ((iter++) % 10000 == 0) { IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL); IGRAPH_ALLOW_INTERRUPTION(); } @@ -211,7 +234,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o } /* Create the graph */ - IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, is_directed)); + IGRAPH_CHECK(igraph_create(graph, &edges, no_of_nodes, directed)); /* Clear the edge list */ igraph_vector_int_destroy(&edges); @@ -225,7 +248,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o IGRAPH_FINALLY(igraph_adjlist_destroy, &al); while (no_of_edges > 0) { /* Report progress after every 10000 edges */ - if ((step_counter++) % 10000 == 0) { + if ((iter++) % 10000 == 0) { IGRAPH_PROGRESS("Static fitness game", 100.0 * (1 - no_of_edges / num_steps), NULL); IGRAPH_ALLOW_INTERRUPTION(); } @@ -241,7 +264,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o } /* For undirected graphs, ensure that from < to */ - if (!is_directed && from > to) { + if (!directed && from > to) { pos = from; from = to; to = pos; } @@ -261,7 +284,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o * because we did not add edges in both directions in the adjacency list. * We will use igraph_to_undirected in an extra step. */ IGRAPH_CHECK(igraph_adjlist(graph, &al, IGRAPH_OUT, 1)); - if (!is_directed) { + if (!directed) { IGRAPH_CHECK(igraph_to_undirected(graph, IGRAPH_TO_UNDIRECTED_EACH, 0)); } @@ -274,7 +297,7 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o IGRAPH_PROGRESS("Static fitness game", 100.0, NULL); /* Cleanup before we create the graph */ - if (is_directed) { + if (directed) { igraph_vector_destroy(&cum_fitness_in); IGRAPH_FINALLY_CLEAN(1); } @@ -296,23 +319,23 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o * distributions may be specified separately. * * - * The game simply uses \ref igraph_static_fitness_game with appropriately - * constructed fitness vectors. In particular, the fitness of vertex i - * is i-alpha, where alpha = 1/(gamma-1) - * and gamma is the exponent given in the arguments. + * The game simply uses \ref igraph_static_fitness_game() with appropriately + * constructed fitness vectors. In particular, the fitness of vertex \c i + * is i^(-alpha), where alpha = 1/(gamma-1) + * and \c gamma is the exponent given in the arguments. * * * To remove correlations between in- and out-degrees in case of directed * graphs, the in-fitness vector will be shuffled after it has been set up - * and before \ref igraph_static_fitness_game is called. + * and before \ref igraph_static_fitness_game() is called. * * * Note that significant finite size effects may be observed for exponents * smaller than 3 in the original formulation of the game. This function * provides an argument that lets you remove the finite size effects by - * assuming that the fitness of vertex i is - * (i+i0-1)-alpha, - * where i0 is a constant chosen appropriately to ensure that the maximum + * assuming that the fitness of vertex \c i is + * (i+i0-1)^(-alpha), + * where \c i0 is a constant chosen appropriately to ensure that the maximum * degree is less than the square root of the number of edges times the * average degree; see the paper of Chung and Lu, and Cho et al for more * details. @@ -323,15 +346,18 @@ igraph_error_t igraph_static_fitness_game(igraph_t *graph, igraph_integer_t no_o * * 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 * * * Chung F and Lu L: Connected components in a random graph with given * degree sequences. Annals of Combinatorics 6, 125-145, 2002. + * https://doi.org/10.1007/PL00012580 * * * Cho YS, Kim JS, Park J, Kahng B, Kim D: Percolation transitions in * scale-free networks under the Achlioptas process. Phys Rev Lett * 103:135702, 2009. + * https://doi.org/10.1103/PhysRevLett.103.135702 * * \param graph Pointer to an uninitialized graph object. * \param no_of_nodes The number of nodes in the generated graph. @@ -365,8 +391,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, igraph_bool_t finite_size_correction) { igraph_vector_t fitness_out, fitness_in; - igraph_real_t alpha_out = 0.0, alpha_in = 0.0; - igraph_integer_t i; + igraph_real_t alpha_out, alpha_in; igraph_real_t j; if (no_of_nodes < 0) { @@ -393,7 +418,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, if (j < no_of_nodes) { j = no_of_nodes; } - for (i = 0; i < no_of_nodes; i++, j--) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++, j--) { VECTOR(fitness_out)[i] = pow(j, alpha_out); } @@ -417,7 +442,7 @@ igraph_error_t igraph_static_power_law_game(igraph_t *graph, if (j < no_of_nodes) { j = no_of_nodes; } - for (i = 0; i < no_of_nodes; i++, j--) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++, j--) { VECTOR(fitness_in)[i] = pow(j, alpha_in); } IGRAPH_CHECK(igraph_vector_shuffle(&fitness_in)); diff --git a/src/games/tree.c b/src/games/tree.c index fc70feccf1..1c24619d55 100644 --- a/src/games/tree.c +++ b/src/games/tree.c @@ -23,6 +23,7 @@ #include "igraph_games.h" +#include "igraph_bitset.h" #include "igraph_constructors.h" #include "igraph_interface.h" #include "igraph_random.h" @@ -79,16 +80,16 @@ static igraph_error_t igraph_i_tree_game_prufer(igraph_t *graph, igraph_integer_ 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; - igraph_integer_t i, j, k; + igraph_bitset_t visited; + igraph_integer_t i, j; igraph_integer_t no_edges; IGRAPH_SAFE_MULT(n - 1, 2, &no_edges); IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, no_edges); - IGRAPH_CHECK(igraph_vector_bool_init(&visited, n)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited); + IGRAPH_CHECK(igraph_bitset_init(&visited, n)); + IGRAPH_FINALLY(igraph_bitset_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)); @@ -121,16 +122,16 @@ static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph */ i = RNG_INTEGER(0, n - 1); - VECTOR(visited)[i] = true; + IGRAPH_BIT_SET(visited, i); SWAP_INT_ELEM(vertices, 0, i); - for (k = 1; k < n; ++k) { + for (igraph_integer_t k = 1; k < n; ++k) { j = RNG_INTEGER(0, n - 1); - if (VECTOR(visited)[VECTOR(vertices)[j]]) { + if (IGRAPH_BIT_TEST(visited, VECTOR(vertices)[j])) { i = VECTOR(vertices)[j]; j = RNG_INTEGER(k, n - 1); } - VECTOR(visited)[VECTOR(vertices)[j]] = true; + IGRAPH_BIT_SET(visited, VECTOR(vertices)[j]); SWAP_INT_ELEM(vertices, k, j); VECTOR(edges)[2 * k - 2] = i; i = VECTOR(vertices)[k]; @@ -142,7 +143,7 @@ static igraph_error_t igraph_i_tree_game_loop_erased_random_walk(igraph_t *graph IGRAPH_CHECK(igraph_create(graph, &edges, n, directed)); igraph_vector_int_destroy(&vertices); - igraph_vector_bool_destroy(&visited); + igraph_bitset_destroy(&visited); igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(3); diff --git a/src/graph/adjlist.c b/src/graph/adjlist.c index 3015ad4f04..f0275588fb 100644 --- a/src/graph/adjlist.c +++ b/src/graph/adjlist.c @@ -22,6 +22,7 @@ */ #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_memory.h" #include "igraph_interface.h" @@ -293,7 +294,7 @@ igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, igraph_neimode_t mode, igraph_bool_t loops) { - igraph_vector_bool_t seen; + igraph_bitset_t seen; igraph_vector_int_t neis; if (mode != IGRAPH_IN && mode != IGRAPH_OUT && mode != IGRAPH_ALL) { @@ -309,45 +310,45 @@ igraph_error_t igraph_adjlist_init_complementer(const igraph_t *graph, IGRAPH_CHECK_OOM(al->adjs, "Insufficient memory for creating complementer adjlist view."); IGRAPH_FINALLY(igraph_adjlist_destroy, al); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen, al->length); + IGRAPH_BITSET_INIT_FINALLY(&seen, al->length); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); for (igraph_integer_t i = 0; i < al->length; i++) { - /* For each vertex, we mark neighbors within the 'seen' bool vector. + /* For each vertex, we mark neighbors within the 'seen' bitset. * Then we iterate over 'seen' and record non-marked vertices in * the adjacency list. */ IGRAPH_ALLOW_INTERRUPTION(); /* Reset neighbor counter and 'seen' vector. */ - igraph_vector_bool_null(&seen); + igraph_bitset_null(&seen); igraph_integer_t n = al->length; IGRAPH_CHECK(igraph_neighbors(graph, &neis, i, mode)); if (!loops) { - VECTOR(seen)[i] = true; + IGRAPH_BIT_SET(seen, i); 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] ] ) { + if (! IGRAPH_BIT_TEST(seen, VECTOR(neis)[j]) ) { n--; - VECTOR(seen)[ VECTOR(neis)[j] ] = true; + IGRAPH_BIT_SET(seen, VECTOR(neis)[j]); } } /* 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]) { + if (!IGRAPH_BIT_TEST(seen, j)) { VECTOR(al->adjs[i])[k++] = j; } } } - igraph_vector_bool_destroy(&seen); + igraph_bitset_destroy(&seen); igraph_vector_int_destroy(&neis); IGRAPH_FINALLY_CLEAN(3); /* +1 for the adjlist itself */ @@ -877,7 +878,7 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( igraph_bool_t *has_multiple ) { - igraph_bool_t dummy1, dummy2; + igraph_bool_t dummy1 = true, dummy2 = true; /* set dummies to avoid uninitialized read */ if (has_loops == NULL) { has_loops = &dummy1; } @@ -909,6 +910,23 @@ static igraph_error_t igraph_i_simplify_sorted_int_adjacency_vector_in_place( } else { if (VECTOR(*v)[i] == index) { *has_loops = true; + /* If we haven't found multi-edges yet, we also need to check + * if this is a multi-loop, to set 'has_multiple' correctly. */ + if (! *has_multiple) { + if (mode == IGRAPH_ALL) { + /* Undirected loops appear twice in the neighbour list, + * so we check two following items instead of one. */ + if (i < n - 2 && + VECTOR(*v)[i + 1] == VECTOR(*v)[i] && + VECTOR(*v)[i + 2] == VECTOR(*v)[i]) { + *has_multiple = true; + } + } else { + if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { + *has_multiple = true; + } + } + } } else if (i != n - 1 && VECTOR(*v)[i + 1] == VECTOR(*v)[i]) { *has_multiple = true; } diff --git a/src/graph/caching.c b/src/graph/caching.c index b2741710cb..2f9781c44e 100644 --- a/src/graph/caching.c +++ b/src/graph/caching.c @@ -17,7 +17,6 @@ */ #include "igraph_interface.h" -#include "igraph_misc.h" /* IGRAPH_STATIC_ASSERT */ #include "graph/caching.h" diff --git a/src/graph/iterators.c b/src/graph/iterators.c index c82ea990d7..4e81855188 100644 --- a/src/graph/iterators.c +++ b/src/graph/iterators.c @@ -444,7 +444,7 @@ igraph_error_t igraph_vs_vector_copy(igraph_vs_t *vs, const igraph_vector_int_t * * Time complexity: O(1). * - * \example examples/simple/igraph_vs_seq.c + * \example examples/simple/igraph_vs_range.c */ igraph_error_t igraph_vs_range(igraph_vs_t *vs, igraph_integer_t start, igraph_integer_t end) { @@ -492,7 +492,7 @@ igraph_vs_t igraph_vss_range(igraph_integer_t start, igraph_integer_t end) { * * Time complexity: O(1). * - * \example examples/simple/igraph_vs_seq.c + * \example examples/simple/igraph_vs_range.c */ igraph_error_t igraph_vs_seq(igraph_vs_t *vs, igraph_integer_t from, igraph_integer_t to) { @@ -915,13 +915,16 @@ igraph_error_t igraph_vit_as_vector(const igraph_vit_t *vit, igraph_vector_int_t * \param es Pointer to an uninitialized edge selector object. * \param order Constant giving the order in which the edges will be * included in the selector. Possible values: - * \c IGRAPH_EDGEORDER_ID, edge ID order. - * \c IGRAPH_EDGEORDER_FROM, vertex ID order, the id of the - * \em source vertex counts for directed graphs. The order - * of the incident edges of a given vertex is arbitrary. - * \c IGRAPH_EDGEORDER_TO, vertex ID order, the ID of the \em - * target vertex counts for directed graphs. The order - * of the incident edges of a given vertex is arbitrary. + * \clist + * \cli IGRAPH_EDGEORDER_ID + * Edge ID order; currently performs the fastest. + * \cli 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. + * \cli IGRAPH_EDGEORDER_TO + * Vertex ID order, the ID of the \em target vertex counts for directed + * graphs. The order of the incident edges of a given vertex is arbitrary. + * \endclist * For undirected graph the latter two is the same. * \return Error code. * \sa \ref igraph_ess_all(), \ref igraph_es_destroy() @@ -1426,7 +1429,7 @@ igraph_error_t igraph_es_path_small(igraph_es_t *es, igraph_bool_t directed, int * * Time complexity: O(1). */ -IGRAPH_EXPORT igraph_error_t igraph_es_all_between( +igraph_error_t igraph_es_all_between( igraph_es_t *es, igraph_integer_t from, igraph_integer_t to, igraph_bool_t directed ) { @@ -1948,7 +1951,7 @@ 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(), \ref igraph_es_vector(), \ref igraph_es_range() it is * O(1). For \ref igraph_es_incident() it is O(d) where d is the number of * incident edges of the vertex. */ diff --git a/src/graph/type_indexededgelist.c b/src/graph/type_indexededgelist.c index 6380207380..73fdd69b02 100644 --- a/src/graph/type_indexededgelist.c +++ b/src/graph/type_indexededgelist.c @@ -1258,6 +1258,26 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, VECTOR(*res)[i] += (VECTOR(graph->is)[vid + 1] - VECTOR(graph->is)[vid]); } } + } else if (igraph_vs_is_all(&vids)) { /* no loops, calculating degree for all vertices */ + // When calculating degree for all vertices, iterating over edges is faster + igraph_integer_t no_of_edges = igraph_ecount(graph); + + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + if (from != IGRAPH_TO(graph, edge)) { + VECTOR(*res)[from]++; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t to = IGRAPH_TO(graph, edge); + if (IGRAPH_FROM(graph, edge) != to) { + VECTOR(*res)[to]++; + } + } + } } else { /* no loops */ if (mode & IGRAPH_OUT) { for (IGRAPH_VIT_RESET(vit), i = 0; @@ -1316,7 +1336,7 @@ igraph_error_t igraph_degree(const igraph_t *graph, igraph_vector_int_t *res, in 'eid' if it is found; otherwise 'eid' is left intact. */ -#define BINSEARCH(start,end,value,iindex,edgelist,N,result,result_pos) \ +#define BINSEARCH(start, end, value, iindex, edgelist, N, result, result_pos) \ do { \ while ((start) < (end)) { \ igraph_integer_t mid =(start)+((end)-(start))/2; \ @@ -1531,7 +1551,7 @@ igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, #undef FIND_DIRECTED_EDGE #undef FIND_UNDIRECTED_EDGE -#define FIND_ALL_DIRECTED_EDGES(graph,xfrom,xto,eidvec) \ +#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]; \ @@ -1542,7 +1562,7 @@ igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, 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); \ + 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; } \ @@ -1558,7 +1578,7 @@ igraph_error_t igraph_get_eids(const igraph_t *graph, igraph_vector_int_t *eids, } \ } while (0) -#define FIND_ALL_UNDIRECTED_EDGES(graph,from,to,eidvec) \ +#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; \ diff --git a/src/graph/visitors.c b/src/graph/visitors.c index e8cdd93f77..f635ca28ef 100644 --- a/src/graph/visitors.c +++ b/src/graph/visitors.c @@ -17,8 +17,9 @@ */ #include "igraph_visitor.h" -#include "igraph_memory.h" + #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_interface.h" #include "igraph_dqueue.h" #include "igraph_stack.h" @@ -116,7 +117,7 @@ igraph_error_t igraph_bfs(const igraph_t *graph, igraph_dqueue_int_t Q; igraph_integer_t actroot = 0; - igraph_vector_char_t added; + igraph_bitset_t added; igraph_lazy_adjlist_t adjlist; @@ -147,7 +148,7 @@ igraph_error_t igraph_bfs(const igraph_t *graph, mode = IGRAPH_ALL; } - IGRAPH_VECTOR_CHAR_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_BITSET_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)); @@ -158,10 +159,10 @@ igraph_error_t igraph_bfs(const igraph_t *graph, 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_char_fill(&added, true); + igraph_bitset_fill(&added, true); for (i = 0; i < n; i++) { igraph_integer_t v = VECTOR(*restricted)[i]; - VECTOR(added)[v] = false; + IGRAPH_BIT_CLEAR(added, v); } } @@ -206,12 +207,12 @@ igraph_error_t igraph_bfs(const igraph_t *graph, } /* OK, we have a new root, start BFS */ - if (VECTOR(added)[actroot]) { + if (IGRAPH_BIT_TEST(added, actroot)) { continue; } IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actroot)); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, 0)); - VECTOR(added)[actroot] = true; + IGRAPH_BIT_SET(added, actroot); if (parents) { VECTOR(*parents)[actroot] = -1; } @@ -242,8 +243,8 @@ igraph_error_t igraph_bfs(const igraph_t *graph, for (igraph_integer_t i = 0; i < n; i++) { igraph_integer_t nei = VECTOR(*neis)[i]; - if (! VECTOR(added)[nei]) { - VECTOR(added)[nei] = true; + if (! IGRAPH_BIT_TEST(added, nei)) { + IGRAPH_BIT_SET(added, nei); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, actdist + 1)); if (parents) { @@ -279,7 +280,7 @@ igraph_error_t igraph_bfs(const igraph_t *graph, igraph_lazy_adjlist_destroy(&adjlist); igraph_dqueue_int_destroy(&Q); - igraph_vector_char_destroy(&added); + igraph_bitset_destroy(&added); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -334,7 +335,7 @@ igraph_error_t igraph_bfs_simple( igraph_dqueue_int_t q; igraph_integer_t num_visited = 0; igraph_vector_int_t neis; - bool *added; + igraph_bitset_t added; igraph_integer_t lastlayer = -1; if (!igraph_is_directed(graph)) { @@ -348,10 +349,7 @@ igraph_error_t igraph_bfs_simple( /* temporary storage */ - added = IGRAPH_CALLOC(no_of_nodes, bool); - IGRAPH_CHECK_OOM(added, "Insufficient memory for BFS."); - IGRAPH_FINALLY(igraph_free, added); - + IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); IGRAPH_CHECK(igraph_dqueue_int_init(&q, 100)); IGRAPH_FINALLY(igraph_dqueue_int_destroy, &q); @@ -381,7 +379,7 @@ igraph_error_t igraph_bfs_simple( VECTOR(*parents)[root] = -1; } num_visited++; - added[root] = true; + IGRAPH_BIT_SET(added, root); while (!igraph_dqueue_int_empty(&q)) { igraph_integer_t actvect = igraph_dqueue_int_pop(&q); @@ -391,8 +389,8 @@ igraph_error_t igraph_bfs_simple( igraph_integer_t nei_count = igraph_vector_int_size(&neis); for (igraph_integer_t i = 0; i < nei_count; i++) { const igraph_integer_t neighbor = VECTOR(neis)[i]; - if (! added[neighbor]) { - added[neighbor] = true; + if (! IGRAPH_BIT_TEST(added, neighbor)) { + IGRAPH_BIT_SET(added, neighbor); if (parents) { VECTOR(*parents)[neighbor] = actvect; } @@ -416,7 +414,7 @@ igraph_error_t igraph_bfs_simple( igraph_vector_int_destroy(&neis); igraph_dqueue_int_destroy(&q); - IGRAPH_FREE(added); + igraph_bitset_destroy(&added); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -488,7 +486,7 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, const igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_lazy_adjlist_t adjlist; igraph_stack_int_t stack; - igraph_vector_char_t added; + igraph_bitset_t added; igraph_vector_int_t nptr; igraph_error_t ret; igraph_integer_t act_rank = 0; @@ -508,7 +506,7 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, mode = IGRAPH_ALL; } - IGRAPH_VECTOR_CHAR_INIT_FINALLY(&added, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&added, no_of_nodes); IGRAPH_STACK_INT_INIT_FINALLY(&stack, 100); IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); @@ -520,7 +518,7 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_vector_int_destroy(&nptr); \ igraph_lazy_adjlist_destroy(&adjlist); \ igraph_stack_int_destroy(&stack); \ - igraph_vector_char_destroy(&added); \ + igraph_bitset_destroy(&added); \ IGRAPH_FINALLY_CLEAN(4); } while (0) /* Resize result vectors and fill them with the initial value */ @@ -537,7 +535,7 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, # undef VINIT IGRAPH_CHECK(igraph_stack_int_push(&stack, root)); - VECTOR(added)[root] = true; + IGRAPH_BIT_SET(added, root); if (parents) { VECTOR(*parents)[root] = -1; } @@ -562,12 +560,12 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, if (!unreachable) { break; } - if (VECTOR(added)[actroot]) { + if (IGRAPH_BIT_TEST(added, actroot)) { actroot++; continue; } IGRAPH_CHECK(igraph_stack_int_push(&stack, actroot)); - VECTOR(added)[actroot] = true; + IGRAPH_BIT_SET(added, actroot); if (parents) { VECTOR(*parents)[actroot] = -1; } @@ -603,13 +601,13 @@ igraph_error_t igraph_dfs(const igraph_t *graph, igraph_integer_t root, igraph_integer_t nei = 0; while (!any && (*ptr) < n) { nei = VECTOR(*neis)[(*ptr)]; - any = !VECTOR(added)[nei]; + any = !IGRAPH_BIT_TEST(added, nei); (*ptr) ++; } if (any) { /* There is such a neighbor, add it */ IGRAPH_CHECK(igraph_stack_int_push(&stack, nei)); - VECTOR(added)[nei] = true; + IGRAPH_BIT_SET(added, nei); if (parents) { VECTOR(*parents)[ nei ] = actvect; } diff --git a/src/internal/glpk_support.h b/src/internal/glpk_support.h index 311c00fd86..9ecc99ac9f 100644 --- a/src/internal/glpk_support.h +++ b/src/internal/glpk_support.h @@ -54,7 +54,7 @@ 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); void igraph_i_glpk_interruption_hook(glp_tree *tree, void *info); -void igraph_i_glpk_error_hook(void *info); +IGRAPH_FUNCATTR_NORETURN 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); diff --git a/src/internal/hacks.h b/src/internal/hacks.h index fc8cf07143..b38bfce873 100644 --- a/src/internal/hacks.h +++ b/src/internal/hacks.h @@ -63,6 +63,11 @@ __BEGIN_DECLS #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/internal/qsort.c b/src/internal/qsort.c index 48f3003cd7..191b9f222a 100644 --- a/src/internal/qsort.c +++ b/src/internal/qsort.c @@ -38,6 +38,7 @@ */ #include "igraph_qsort.h" +#include "igraph_error.h" #ifdef _MSC_VER /* MSVC does not have inline when compiling C source files */ @@ -54,10 +55,6 @@ #define __unused __attribute__ ((unused)) #endif -#if defined(LIBC_SCCS) && !defined(lint) -static char sccsid[] = "@(#)qsort.c 8.1 (Berkeley) 6/4/93"; -#endif /* LIBC_SCCS and not lint */ - #include #if defined(I_AM_QSORT_R) @@ -127,6 +124,9 @@ local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) int cmp_result; int swap_cnt; + /* if there are less than 2 elements, then sorting is not needed */ + if (IGRAPH_UNLIKELY(n < 2)) + return; loop: swap_cnt = 0; if (n < 7) { diff --git a/src/io/dimacs.c b/src/io/dimacs.c index 3539db82aa..131c23d6a0 100644 --- a/src/io/dimacs.c +++ b/src/io/dimacs.c @@ -120,7 +120,7 @@ igraph_error_t igraph_read_graph_dimacs(igraph_t *graph, FILE *instream, * Time complexity: O(|V|+|E|+c), the number of vertices plus the * number of edges, plus the size of the file in characters. * - * \sa \ref igraph_write_graph_dimacs() + * \sa \ref igraph_write_graph_dimacs_flow() */ igraph_error_t igraph_read_graph_dimacs_flow( igraph_t *graph, FILE *instream, diff --git a/src/io/dot.c b/src/io/dot.c index 72c28aa03f..4b8a904e57 100644 --- a/src/io/dot.c +++ b/src/io/dot.c @@ -37,7 +37,7 @@ #define CHECK(cmd) do { int ret=cmd; if (ret<0) IGRAPH_ERROR("Writing DOT format failed.", IGRAPH_EFILE); } while (0) -static igraph_error_t dot_escape(const char *orig, char **result) { +static igraph_error_t dot_escape(const char *orig, igraph_vector_char_t* 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; @@ -82,14 +82,14 @@ static igraph_error_t dot_escape(const char *orig, char **result) { } if (is_number || !need_quote) { - *result = strdup(orig); - IGRAPH_CHECK_OOM(*result, "Insufficient memory for writing DOT format."); + IGRAPH_CHECK(igraph_vector_char_resize(result, newlen + 1)); + memcpy(VECTOR(*result), orig, newlen); } else { - *result = IGRAPH_CALLOC(newlen + 3, char); - IGRAPH_CHECK_OOM(*result, "Insufficient memory for writing DOT format."); - (*result)[0] = '"'; - (*result)[newlen + 1] = '"'; - (*result)[newlen + 2] = '\0'; + newlen += 2; + IGRAPH_CHECK(igraph_vector_char_resize(result, newlen + 1)); + VECTOR(*result)[0] = '"'; + VECTOR(*result)[newlen - 1] = '"'; + /* Escape quotes, backslashes and newlines. * Even though the format spec at https://graphviz.org/doc/info/lang.html * claims that only quotes need escaping, escaping backslashes appears to @@ -97,16 +97,17 @@ static igraph_error_t dot_escape(const char *orig, char **result) { * Tested with GraphViz 2.50. */ for (i = 0, j = 1; i < len; i++) { if (orig[i] == '\n') { - (*result)[j++] = '\\'; - (*result)[j++] = 'n'; + VECTOR(*result)[j++] = '\\'; + VECTOR(*result)[j++] = 'n'; continue; } if (orig[i] == '\\' || orig[i] == '"') { - (*result)[j++] = '\\'; + VECTOR(*result)[j++] = '\\'; } - (*result)[j++] = orig[i]; + VECTOR(*result)[j++] = orig[i]; } } + VECTOR(*result)[newlen] = 0; return IGRAPH_SUCCESS; } @@ -114,7 +115,7 @@ static igraph_error_t dot_escape(const char *orig, char **result) { /* Writes exactly representable integral values in standard integer notation, without decimal points or e-notation. * Floating point values that are written with e-notation are quoted, otherwise the Graphviz parser cannot handle them. */ -static igraph_error_t fprint_integral_or_precise(FILE *file, igraph_real_t x) { +static igraph_error_t fprint_integral_or_precise(FILE *file, igraph_real_t x, igraph_vector_char_t *buf) { if (fabs(x) <= IGRAPH_MAX_EXACT_REAL && floor(x) == x) { /* write exactly representable integral values in standard integer notation; * the above conditional skips +-Inf and NaN */ @@ -122,14 +123,9 @@ static igraph_error_t fprint_integral_or_precise(FILE *file, igraph_real_t x) { } else { /* write as precise float and quote if necessary */ char str[50]; /* large enough to hold any precisely printed real */ - char *str2; - CHECK(igraph_real_snprintf_precise(str, sizeof(str) / sizeof(str[0]), x)); - IGRAPH_CHECK(dot_escape(str, &str2)); - IGRAPH_FINALLY(igraph_free, str2); - CHECK(fputs(str2, file)); - IGRAPH_FREE(str2); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(dot_escape(str, buf)); + CHECK(fputs(VECTOR(*buf), file)); } return IGRAPH_SUCCESS; } @@ -162,15 +158,16 @@ static igraph_error_t fprint_integral_or_precise(FILE *file, igraph_real_t x) { * \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); - char edgeop[3]; + const igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_edges = igraph_ecount(graph); + const igraph_bool_t directed = igraph_is_directed(graph); + const char *edgeop = directed ? "->" : "--"; igraph_strvector_t gnames, vnames, enames; igraph_vector_int_t gtypes, vtypes, etypes; igraph_vector_t numv; igraph_strvector_t strv; igraph_vector_bool_t boolv; + igraph_vector_char_t buf, buf2; IGRAPH_STRVECTOR_INIT_FINALLY(&gnames, 0); IGRAPH_STRVECTOR_INIT_FINALLY(&vnames, 0); @@ -183,97 +180,81 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { &vnames, &vtypes, &enames, &etypes)); - IGRAPH_VECTOR_INIT_FINALLY(&numv, 1); - IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 1); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 1); + IGRAPH_VECTOR_INIT_FINALLY(&numv, 0); + IGRAPH_STRVECTOR_INIT_FINALLY(&strv, 0); + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&boolv, 0); + + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&buf, 0); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&buf2, 0); - CHECK(fprintf(outstream, "/* Created by igraph %s */\n", - IGRAPH_VERSION)); + CHECK(fprintf(outstream, "/* Created by igraph %s */\n", IGRAPH_VERSION)); - if (igraph_is_directed(graph)) { + if (directed) { CHECK(fprintf(outstream, "digraph {\n")); - strcpy(edgeop, "->"); } else { CHECK(fprintf(outstream, "graph {\n")); - strcpy(edgeop, "--"); } /* Write the graph attributes */ if (igraph_vector_int_size(>ypes) > 0) { CHECK(fprintf(outstream, " graph [\n")); - for (i = 0; i < igraph_vector_int_size(>ypes); i++) { + for (igraph_integer_t i = 0; i < igraph_vector_int_size(>ypes); i++) { const char *name; - char *newname; name = igraph_strvector_get(&gnames, i); - IGRAPH_CHECK(dot_escape(name, &newname)); - IGRAPH_FINALLY(igraph_free, newname); + IGRAPH_CHECK(dot_escape(name, &buf)); if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_CHECK(igraph_i_attribute_get_numeric_graph_attr(graph, name, &numv)); - CHECK(fprintf(outstream, " %s=", newname)); - IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0])); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); CHECK(fputc('\n', outstream)); } else if (VECTOR(gtypes)[i] == IGRAPH_ATTRIBUTE_STRING) { const char *s; - char *news; IGRAPH_CHECK(igraph_i_attribute_get_string_graph_attr(graph, name, &strv)); s = igraph_strvector_get(&strv, 0); - IGRAPH_CHECK(dot_escape(s, &news)); - IGRAPH_FINALLY(igraph_free, news); - CHECK(fprintf(outstream, " %s=%s\n", newname, news)); - IGRAPH_FREE(news); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); } 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("Boolean graph attribute was converted to numeric"); + CHECK(fprintf(outstream, " %s=%d\n", VECTOR(buf), VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("Boolean graph attribute was converted to numeric."); } else { - IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute was ignored"); + IGRAPH_WARNING("A non-numeric, non-string, non-boolean graph attribute was ignored."); } - IGRAPH_FREE(newname); - IGRAPH_FINALLY_CLEAN(1); } CHECK(fprintf(outstream, " ];\n")); } /* Write the vertices */ if (igraph_vector_int_size(&vtypes) > 0) { - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { CHECK(fprintf(outstream, " %" IGRAPH_PRId " [\n", i)); - for (j = 0; j < igraph_vector_int_size(&vtypes); j++) { + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&vtypes); j++) { const char *name; - char *newname; name = igraph_strvector_get(&vnames, j); - IGRAPH_CHECK(dot_escape(name, &newname)); - IGRAPH_FINALLY(igraph_free, newname); + IGRAPH_CHECK(dot_escape(name, &buf)); if (VECTOR(vtypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_CHECK(igraph_i_attribute_get_numeric_vertex_attr(graph, name, igraph_vss_1(i), &numv)); - CHECK(fprintf(outstream, " %s=", newname)); - IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0])); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); 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); - IGRAPH_CHECK(dot_escape(s, &news)); - IGRAPH_FINALLY(igraph_free, news); - CHECK(fprintf(outstream, " %s=%s\n", newname, news)); - IGRAPH_FREE(news); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); } else if (VECTOR(vtypes)[j] == 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)); - IGRAPH_WARNING("A boolean vertex attribute was converted to numeric"); + CHECK(fprintf(outstream, " %s=%d\n", VECTOR(buf), VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean vertex attribute was converted to numeric."); } else { - IGRAPH_WARNING("A non-numeric, non-string, non-boolean vertex attribute was ignored"); + IGRAPH_WARNING("A non-numeric, non-string, non-boolean vertex attribute was ignored."); } - IGRAPH_FREE(newname); - IGRAPH_FINALLY_CLEAN(1); } CHECK(fprintf(outstream, " ];\n")); } } else { - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { CHECK(fprintf(outstream, " %" IGRAPH_PRId ";\n", i)); } } @@ -281,48 +262,40 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { /* Write the edges */ if (igraph_vector_int_size(&etypes) > 0) { - for (i = 0; i < no_of_edges; i++) { + for (igraph_integer_t 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++) { + for (igraph_integer_t j = 0; j < igraph_vector_int_size(&etypes); j++) { const char *name; - char *newname; name = igraph_strvector_get(&enames, j); - IGRAPH_CHECK(dot_escape(name, &newname)); - IGRAPH_FINALLY(igraph_free, newname); + IGRAPH_CHECK(dot_escape(name, &buf)); if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_NUMERIC) { IGRAPH_CHECK(igraph_i_attribute_get_numeric_edge_attr(graph, name, igraph_ess_1(i), &numv)); - CHECK(fprintf(outstream, " %s=", newname)); - IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0])); + CHECK(fprintf(outstream, " %s=", VECTOR(buf))); + IGRAPH_CHECK(fprint_integral_or_precise(outstream, VECTOR(numv)[0], &buf)); CHECK(fputc('\n', outstream)); } else if (VECTOR(etypes)[j] == IGRAPH_ATTRIBUTE_STRING) { const char *s; - char *news; IGRAPH_CHECK(igraph_i_attribute_get_string_edge_attr(graph, name, igraph_ess_1(i), &strv)); s = igraph_strvector_get(&strv, 0); - IGRAPH_CHECK(dot_escape(s, &news)); - IGRAPH_FINALLY(igraph_free, news); - CHECK(fprintf(outstream, " %s=%s\n", newname, news)); - IGRAPH_FREE(news); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_CHECK(dot_escape(s, &buf2)); + CHECK(fprintf(outstream, " %s=%s\n", VECTOR(buf), VECTOR(buf2))); } else if (VECTOR(etypes)[j] == 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)); - IGRAPH_WARNING("A boolean edge attribute was converted to numeric"); + CHECK(fprintf(outstream, " %s=%d\n", VECTOR(buf), VECTOR(boolv)[0] ? 1 : 0)); + IGRAPH_WARNING("A boolean edge attribute was converted to numeric."); } else { - IGRAPH_WARNING("A non-numeric, non-string graph attribute ignored"); + IGRAPH_WARNING("A non-numeric, non-string graph attribute ignored."); } - IGRAPH_FREE(newname); - IGRAPH_FINALLY_CLEAN(1); } CHECK(fprintf(outstream, " ];\n")); } } else { - for (i = 0; i < no_of_edges; i++) { + for (igraph_integer_t 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)); @@ -330,6 +303,8 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { } CHECK(fprintf(outstream, "}\n")); + igraph_vector_char_destroy(&buf2); + igraph_vector_char_destroy(&buf); igraph_vector_bool_destroy(&boolv); igraph_strvector_destroy(&strv); igraph_vector_destroy(&numv); @@ -339,7 +314,7 @@ igraph_error_t igraph_write_graph_dot(const igraph_t *graph, FILE* outstream) { igraph_strvector_destroy(&enames); igraph_strvector_destroy(&vnames); igraph_strvector_destroy(&gnames); - IGRAPH_FINALLY_CLEAN(9); + IGRAPH_FINALLY_CLEAN(11); return IGRAPH_SUCCESS; } diff --git a/src/io/gml-parser.y b/src/io/gml-parser.y index 4de1fdcdd0..c9ee9454b7 100644 --- a/src/io/gml-parser.y +++ b/src/io/gml-parser.y @@ -51,6 +51,7 @@ #include "io/parsers/gml-lexer.h" #include "io/parse_utils.h" #include "internal/hacks.h" /* strcasecmp & strndup */ +#include "math/safe_intop.h" #include #include @@ -184,11 +185,14 @@ static igraph_error_t igraph_i_gml_make_numeric(const char *name, } IGRAPH_FINALLY(igraph_free, t); - /* The GML spec only requires support for 32-bit signed integers. + /* The GML spec only requires support for 32-bit signed integers, + * but igraph tries to support the same range as igraph_integer_t, + * so that it can read/write all graphs it can represent. * 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_real_t trunc_value = trunc(value); + if (value == trunc_value && igraph_i_is_real_representable_as_integer(trunc_value)) { IGRAPH_CHECK(igraph_gml_tree_init_integer(t, name, line, value)); } else { IGRAPH_CHECK(igraph_gml_tree_init_real(t, name, line, value)); diff --git a/src/io/gml-tree.h b/src/io/gml-tree.h index 71aacfeeb6..26e9308f1f 100644 --- a/src/io/gml-tree.h +++ b/src/io/gml-tree.h @@ -64,22 +64,22 @@ 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); -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, +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_length(const igraph_gml_tree_t *t); +IGRAPH_FUNCATTR_PURE 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, +IGRAPH_FUNCATTR_PURE 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, +IGRAPH_FUNCATTR_PURE igraph_i_gml_tree_type_t igraph_gml_tree_type(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE const char *igraph_gml_tree_name(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_line(const igraph_gml_tree_t *t, igraph_integer_t pos); +IGRAPH_FUNCATTR_PURE igraph_integer_t igraph_gml_tree_get_integer(const igraph_gml_tree_t *t, igraph_integer_t pos); -igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, +IGRAPH_FUNCATTR_PURE igraph_real_t igraph_gml_tree_get_real(const igraph_gml_tree_t *t, igraph_integer_t pos); -const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, +IGRAPH_FUNCATTR_PURE const char *igraph_gml_tree_get_string(const igraph_gml_tree_t *t, igraph_integer_t pos); -igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, +IGRAPH_FUNCATTR_PURE igraph_gml_tree_t *igraph_gml_tree_get_tree(const igraph_gml_tree_t *t, igraph_integer_t pos); __END_DECLS diff --git a/src/io/gml.c b/src/io/gml.c index b0fb9622e7..f5ddc1c102 100644 --- a/src/io/gml.c +++ b/src/io/gml.c @@ -29,6 +29,7 @@ #include "core/trie.h" #include "graph/attributes.h" #include "internal/hacks.h" /* strdup, strncasecmp */ +#include "math/safe_intop.h" #include "io/gml-header.h" #include "io/parsers/gml-parser.h" @@ -1057,7 +1058,8 @@ igraph_error_t igraph_write_graph_gml(const igraph_t *graph, FILE *outstream, } for (i = 0; i < no_of_nodes; ++i) { igraph_real_t val = VECTOR(*myid)[i]; - if (val != (igraph_integer_t) val) { + igraph_real_t trunc_val = trunc(val); + if (! (val == trunc_val && igraph_i_is_real_representable_as_integer(trunc_val))) { IGRAPH_WARNINGF("%g is not a valid integer id for GML files, ignoring all supplied ids.", val); if (myid == &v_myid) { igraph_vector_destroy(&v_myid); @@ -1219,7 +1221,7 @@ 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); + igraph_vector_int_null(&warning_shown); for (i = 0; i < no_of_edges; i++) { igraph_integer_t from = IGRAPH_FROM(graph, i); igraph_integer_t to = IGRAPH_TO(graph, i); diff --git a/src/io/graphml.c b/src/io/graphml.c index 2083154d22..80e1a190ae 100644 --- a/src/io/graphml.c +++ b/src/io/graphml.c @@ -30,7 +30,7 @@ #include "config.h" -#include +#include /* isdigit */ #include /* isnan */ #include #include /* va_start & co */ @@ -129,7 +129,7 @@ struct igraph_i_graphml_parser_state { xmlChar *data_key; igraph_attribute_elemtype_t data_type; char *error_message; - char *data_char; + igraph_vector_char_t data_char; igraph_integer_t act_node; igraph_bool_t ignore_namespaces; }; @@ -285,8 +285,11 @@ static igraph_error_t igraph_i_graphml_parser_state_init(struct igraph_i_graphml IGRAPH_FINALLY(igraph_trie_destroy, &state->e_attr_ids); IGRAPH_CHECK(igraph_trie_init(&state->g_attr_ids, 0)); + IGRAPH_FINALLY(igraph_trie_destroy, &state->g_attr_ids); - IGRAPH_FINALLY_CLEAN(9); + IGRAPH_VECTOR_CHAR_INIT_FINALLY(&state->data_char, 0); + + IGRAPH_FINALLY_CLEAN(11); return IGRAPH_SUCCESS; } @@ -304,13 +307,12 @@ static void igraph_i_graphml_parser_state_destroy(struct igraph_i_graphml_parser igraph_vector_ptr_destroy_all(&state->e_attrs); igraph_vector_ptr_destroy_all(&state->g_attrs); + igraph_vector_char_destroy(&state->data_char); + if (state->data_key) { xmlFree((void *) state->data_key); state->data_key = NULL; } - if (state->data_char) { - IGRAPH_FREE(state->data_char); - } if (state->error_message) { IGRAPH_FREE(state->error_message); @@ -404,7 +406,6 @@ static void igraph_i_graphml_sax_handler_start_document(void *state0) { state->successful = 1; state->edges_directed = 0; state->data_key = NULL; - state->data_char = NULL; state->unknown_depth = 0; state->ignore_namespaces = 0; } @@ -586,14 +587,15 @@ static igraph_error_t igraph_i_graphml_parser_state_finish_parsing(struct igraph return IGRAPH_SUCCESS; } -#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_LENGTH(it) (int)((*(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" */ +/* See https://gnome.pages.gitlab.gnome.org/libxml2/devhelp/libxml2-parser.html#startElementNsSAX2Func */ +#define XML_ATTR_LOCALNAME(it) it[0] +#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_LENGTH(it) (size_t)(it[4] - it[3]) +#define XML_ATTR_VALUE(it) it[3], (int)(it[4] - it[3]) /* for use in strnxxx()-style functions that take a char * and a length */ +#define XML_ATTR_VALUE_PF(it) (int)(it[4] - it[3]), it[3] /* for use in printf-style function with "%.*s" */ static igraph_bool_t xmlAttrValueEqual(xmlChar** attr, const char* expected) { size_t expected_length = strlen(expected); @@ -734,7 +736,7 @@ static igraph_error_t igraph_i_graphml_add_attribute_key( } /* throw an error if the ID is an empty string; this is also a clear violation of the GraphML DTD */ - if (*(rec->id) == 0) { + if (*(rec->id) == '\0') { IGRAPH_ERROR("Found tag with an empty 'id' attribute.", IGRAPH_PARSEERROR); } @@ -869,9 +871,7 @@ static igraph_error_t igraph_i_graphml_attribute_data_setup( if (state->data_key == 0) { return IGRAPH_ENOMEM; /* LCOV_EXCL_LINE */ } - if (state->data_char) { - IGRAPH_FREE(state->data_char); - } + igraph_vector_char_clear(&state->data_char); state->data_type = type; } else { /* ignore */ @@ -884,31 +884,17 @@ static igraph_error_t igraph_i_graphml_attribute_data_setup( 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 IGRAPH_SUCCESS; } - if (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 { - new_data_char = IGRAPH_CALLOC((size_t) len + 1, char); + /* vector_push_back() minimizes reallocations by doubling the size of the buffer, + * while vector_append() would only allocate as much additional memory as needed. */ + IGRAPH_STATIC_ASSERT(sizeof(char) == sizeof(xmlChar)); + for (int i=0; i < len; i++) { + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, data[i])); } - if (new_data_char == NULL) { - /* 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; } @@ -967,6 +953,7 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra igraph_strvector_t *strvec; 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); @@ -976,10 +963,14 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra VECTOR(*boolvec)[i] = graphmlrec->default_value.as_boolean; } } + + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); IGRAPH_CHECK(igraph_i_graphml_parse_boolean( - state->data_char, VECTOR(*boolvec) + id, graphmlrec->default_value.as_boolean + VECTOR(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); @@ -989,11 +980,15 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra VECTOR(*vec)[i] = graphmlrec->default_value.as_numeric; } } + + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); IGRAPH_CHECK(igraph_i_graphml_parse_numeric( - state->data_char, VECTOR(*vec) + id, + VECTOR(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); @@ -1004,24 +999,25 @@ static igraph_error_t igraph_i_graphml_attribute_data_finish(struct igraph_i_gra IGRAPH_CHECK(igraph_strvector_set(strvec, i, strvalue)); } } - if (state->data_char) { - strvalue = state->data_char; + if (igraph_vector_char_size(&state->data_char) > 0) { + /* Ensure that the vector ends with a null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + strvalue = VECTOR(state->data_char); } else { strvalue = graphmlrec->default_value.as_string; } IGRAPH_CHECK(igraph_strvector_set(strvec, id, strvalue)); break; + 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; - } + igraph_vector_char_clear(&state->data_char); return result; } @@ -1033,23 +1029,29 @@ static igraph_error_t igraph_i_graphml_attribute_default_value_finish(struct igr IGRAPH_ASSERT(state->current_attr_record != NULL); - if (state->data_char == 0) { + if (igraph_vector_char_size(&state->data_char) == 0) { return IGRAPH_SUCCESS; } switch (graphmlrec->record.type) { case IGRAPH_ATTRIBUTE_BOOLEAN: + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); IGRAPH_CHECK(igraph_i_graphml_parse_boolean( - state->data_char, &graphmlrec->default_value.as_boolean, 0 + VECTOR(state->data_char), &graphmlrec->default_value.as_boolean, 0 )); break; case IGRAPH_ATTRIBUTE_NUMERIC: + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); IGRAPH_CHECK(igraph_i_graphml_parse_numeric( - state->data_char, &graphmlrec->default_value.as_numeric, IGRAPH_NAN + VECTOR(state->data_char), &graphmlrec->default_value.as_numeric, IGRAPH_NAN )); break; case IGRAPH_ATTRIBUTE_STRING: - str = strdup(state->data_char); + /* Add null terminator */ + IGRAPH_CHECK(igraph_vector_char_push_back(&state->data_char, '\x00')); + str = strdup(VECTOR(state->data_char)); IGRAPH_CHECK_OOM(str, "Cannot allocate memory for string attribute."); if (graphmlrec->default_value.as_string != 0) { @@ -1064,9 +1066,7 @@ static igraph_error_t igraph_i_graphml_attribute_default_value_finish(struct igr IGRAPH_FATAL("Unexpected attribute type."); } - if (state->data_char) { - IGRAPH_FREE(state->data_char); - } + igraph_vector_char_clear(&state->data_char); return result; } diff --git a/src/io/lgl.c b/src/io/lgl.c index 37c6d7e2b1..8c2f477a51 100644 --- a/src/io/lgl.c +++ b/src/io/lgl.c @@ -48,7 +48,7 @@ void igraph_lgl_yylex_destroy_wrapper (void *scanner ) { * * The .lgl format is used by the Large Graph * Layout visualization software - * (http://lgl.sourceforge.net), it can + * (https://lgl.sourceforge.net), it can * describe undirected optionally weighted graphs. From the LGL * manual: * diff --git a/src/io/ncol.c b/src/io/ncol.c index 8a06461b24..084a77c3e4 100644 --- a/src/io/ncol.c +++ b/src/io/ncol.c @@ -51,7 +51,7 @@ void igraph_ncol_yylex_destroy_wrapper (void *scanner ) { * * * This format is used by the Large Graph Layout program - * (http://lgl.sourceforge.net), and it is simply a + * (https://lgl.sourceforge.net), and it is simply a * symbolic weighted edge list. It is a simple text file with one edge * per line. An edge is defined by two symbolic vertex names separated * by whitespace. The vertex names themselves cannot contain diff --git a/src/io/pajek-parser.y b/src/io/pajek-parser.y index 20d9a59ecf..d9d377e98b 100644 --- a/src/io/pajek-parser.y +++ b/src/io/pajek-parser.y @@ -238,9 +238,17 @@ vertexline: vertex NEWLINE | vertex: integer { igraph_integer_t v = $1; + /* Per feedback from Pajek's authors, negative signs should be ignored for vertex IDs. + * See https://nascol.discourse.group/t/pajek-arcslist-edgelist-format/44/2 + * This applies to all of *Edges, *Arcs, *Edgeslist, *Arcslist and *Vertices section. + * IGRAPH_INTEGER_MIN cannot be negated on typical platforms so we keep it as-is. + */ + if (v < 0 && v > IGRAPH_INTEGER_MIN) { + v = -v; + } if (v < 1 || v > context->vcount) { IGRAPH_YY_ERRORF( - "Invalid vertex id (%" IGRAPH_PRId ") in Pajek file. " + "Invalid vertex ID (%" IGRAPH_PRId ") in Pajek file. " "The number of vertices is %" IGRAPH_PRId ".", IGRAPH_EINVAL, v, context->vcount); } @@ -448,11 +456,11 @@ arclistline: arclistfrom arctolist NEWLINE; arctolist: /* empty */ | arctolist arclistto; -arclistfrom: integer { context->actfrom=labs($1)-1; }; +arclistfrom: vertex { context->actfrom=$1-1; }; -arclistto: integer { +arclistto: vertex { 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_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); }; edgeslist: EDGESLISTLINE NEWLINE edgelistlines { context->directed=0; }; @@ -463,11 +471,11 @@ edgelistline: edgelistfrom edgetolist NEWLINE; edgetolist: /* empty */ | edgetolist edgelistto; -edgelistfrom: integer { context->actfrom=labs($1)-1; }; +edgelistfrom: vertex { context->actfrom=$1-1; }; -edgelistto: integer { +edgelistto: vertex { 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_YY_CHECK(igraph_vector_int_push_back(context->vector, $1-1)); }; /* -----------------------------------------------------*/ diff --git a/src/io/pajek.c b/src/io/pajek.c index 02bab22905..d54c293bb8 100644 --- a/src/io/pajek.c +++ b/src/io/pajek.c @@ -26,10 +26,11 @@ #include "igraph_error.h" #include "igraph_interface.h" #include "igraph_memory.h" -#include "igraph_misc.h" /* IGRAPH_STATIC_ASSERT */ #include "graph/attributes.h" +#include "internal/hacks.h" /* IGRAPH_STATIC_ASSERT */ + #include "io/pajek-header.h" #include "io/parsers/pajek-parser.h" diff --git a/src/isomorphism/lad.c b/src/isomorphism/lad.c index 877ef74f37..689c3260c9 100644 --- a/src/isomorphism/lad.c +++ b/src/isomorphism/lad.c @@ -47,13 +47,15 @@ */ #include "igraph_topology.h" -#include "igraph_interface.h" + #include "igraph_adjlist.h" -#include "igraph_vector.h" -#include "igraph_vector_ptr.h" +#include "igraph_bitset.h" +#include "igraph_interface.h" #include "igraph_memory.h" #include "igraph_matrix.h" #include "igraph_qsort.h" +#include "igraph_vector.h" +#include "igraph_vector_ptr.h" #include "core/interruption.h" @@ -65,34 +67,33 @@ /* 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_FINALLY(igraph_free, VAR); \ + VAR = IGRAPH_CALLOC(SIZE, TYPE); \ + IGRAPH_CHECK_OOM(VAR, "Cannot allocate '" #VAR "' array in LAD isomorphism search."); \ + IGRAPH_FINALLY(igraph_free, VAR); \ } /* helper to allocate an array of given size and store its address in a * pointer array */ #define ALLOC_ARRAY_IN_HISTORY(VAR, SIZE, TYPE, HISTORY) { \ - VAR = IGRAPH_CALLOC(SIZE, TYPE); \ - if (VAR == 0) { \ - IGRAPH_ERROR("cannot allocate '" #VAR "' array in LAD isomorphism search", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ \ - } \ - IGRAPH_FINALLY(igraph_free, VAR); \ - IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR)); \ - IGRAPH_FINALLY_CLEAN(1); \ + VAR = IGRAPH_CALLOC(SIZE, TYPE); \ + IGRAPH_CHECK_OOM(VAR, "Cannot allocate '" #VAR "' array in LAD isomorphism search."); \ + IGRAPH_FINALLY(igraph_free, VAR); \ + IGRAPH_CHECK(igraph_vector_ptr_push_back(HISTORY, VAR)); \ + IGRAPH_FINALLY_CLEAN(1); \ } /* ---------------------------------------------------------*/ /* Coming from graph.c */ /* ---------------------------------------------------------*/ +#define ISEDGE(G, i, j) IGRAPH_BIT_TEST(G->isEdge, i * G->nbVertices + j) +#define SETEDGE(G, i, j) IGRAPH_BIT_SET(G->isEdge, i * G->nbVertices + j) + typedef struct { igraph_integer_t nbVertices; /* Number of vertices */ igraph_vector_int_t nbSucc; igraph_adjlist_t succ; - igraph_matrix_char_t isEdge; + igraph_bitset_t isEdge; } Tgraph; static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* graph) { @@ -110,18 +111,17 @@ static igraph_error_t igraph_i_lad_createGraph(const igraph_t *igraph, Tgraph* g VECTOR(graph->nbSucc)[i] = igraph_vector_int_size(igraph_adjlist_get(&graph->succ, i)); } - IGRAPH_CHECK(igraph_matrix_char_init(&graph->isEdge, no_of_nodes, no_of_nodes)); - IGRAPH_FINALLY(igraph_matrix_char_destroy, &graph->isEdge); + IGRAPH_BITSET_INIT_FINALLY(&graph->isEdge, no_of_nodes * no_of_nodes); for (i = 0; i < no_of_nodes; i++) { neis = igraph_adjlist_get(&graph->succ, i); n = igraph_vector_int_size(neis); for (j = 0; j < n; j++) { igraph_integer_t v = VECTOR(*neis)[j]; - if (MATRIX(graph->isEdge, i, v)) { + if (ISEDGE(graph, i, v)) { IGRAPH_ERROR("LAD functions do not support graphs with multi-edges.", IGRAPH_EINVAL); } - MATRIX(graph->isEdge, i, v) = 1; + SETEDGE(graph, i, v); } } @@ -131,7 +131,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_bitset_destroy(&graph->isEdge); igraph_adjlist_destroy(&graph->succ); igraph_vector_int_destroy(&graph->nbSucc); } @@ -165,7 +165,7 @@ typedef struct { domain should be filtered */ igraph_vector_int_t toFilter; /* contain all pattern nodes whose domain should be filtered */ - igraph_vector_char_t markedToFilter; /* markedToFilter[u]=true if u + igraph_bitset_t markedToFilter; /* markedToFilter[u]=true if u is in toFilter; false otherwise */ igraph_vector_int_t globalMatchingP; /* globalMatchingP[u] = node of Gt matched to u in globalAllDiff(Np) */ @@ -181,7 +181,7 @@ static bool igraph_i_lad_toFilterEmpty(Tdomain* D) { static void igraph_i_lad_resetToFilter(Tdomain *D) { /* empty to filter and unmark the vertices that are marked to be filtered */ - igraph_vector_char_null(&D->markedToFilter); + igraph_bitset_null(&D->markedToFilter); D->nextOutToFilter = -1; } @@ -191,7 +191,7 @@ static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t s remove a node from toFilter (FIFO) unmark this node and return it */ igraph_integer_t u = VECTOR(D->toFilter)[D->nextOutToFilter]; - VECTOR(D->markedToFilter)[u] = false; + IGRAPH_BIT_CLEAR(D->markedToFilter, u); if (D->nextOutToFilter == D->lastInToFilter) { /* u was the last node in tofilter */ D->nextOutToFilter = -1; @@ -205,10 +205,10 @@ static igraph_integer_t igraph_i_lad_nextToFilter(Tdomain* D, igraph_integer_t s 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]) { + if (IGRAPH_BIT_TEST(D->markedToFilter, u)) { return; } - VECTOR(D->markedToFilter)[u] = true; + IGRAPH_BIT_SET(D->markedToFilter, u); if (D->nextOutToFilter < 0) { D->lastInToFilter = 0; D->nextOutToFilter = 0; @@ -233,7 +233,7 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D 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; - bool *marked; + igraph_bitset_t marked; igraph_integer_t nextIn = 0; igraph_integer_t nextOut = 0; igraph_integer_t i, v, v2, u2; @@ -243,7 +243,7 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D /* Allocate memory */ ALLOC_ARRAY(fifo, nbV, igraph_integer_t); ALLOC_ARRAY(pred, nbV, igraph_integer_t); - ALLOC_ARRAY(marked, nbV, bool); + IGRAPH_BITSET_INIT_FINALLY(&marked, nbV); for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ @@ -257,7 +257,7 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D /* v is not free => add it to fifo */ pred[v] = u; fifo[nextIn++] = v; - marked[v] = true; + IGRAPH_BIT_SET(marked, v); } while (nextOut < nextIn) { u2 = VECTOR(D->globalMatchingT)[fifo[nextOut++]]; @@ -277,10 +277,10 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D *result = true; goto cleanup; } - if (!marked[v]) { /* v is not free and not marked => add it to fifo */ + if (!IGRAPH_BIT_TEST(marked, v)) { /* v is not free and not marked => add it to fifo */ pred[v] = u2; fifo[nextIn++] = v; - marked[v] = true; + IGRAPH_BIT_SET(marked, v); } } } @@ -288,7 +288,7 @@ static igraph_error_t igraph_i_lad_augmentingPath(igraph_integer_t u, Tdomain* D cleanup: igraph_free(fifo); igraph_free(pred); - igraph_free(marked); + igraph_bitset_destroy(&marked); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -392,11 +392,11 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec return IGRAPH_SUCCESS; } } - if (MATRIX(Gp->isEdge, u, u2)) { + if (ISEDGE(Gp, u, u2)) { /* remove from D[u2] vertices which are not adjacent to v */ j = VECTOR(D->firstVal)[u2]; while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { - if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { + if (ISEDGE(Gt, v, VECTOR(D->val)[j])) { j++; } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); @@ -411,7 +411,7 @@ static igraph_error_t igraph_i_lad_matchVertices(igraph_integer_t nb, igraph_vec if (VECTOR(D->nbVal)[u2] < VECTOR(Gt->nbSucc)[v]) { j = VECTOR(D->firstVal)[u2]; while (j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]) { - if (!MATRIX(Gt->isEdge, v, VECTOR(D->val)[j])) { + if (!ISEDGE(Gt, v, VECTOR(D->val)[j])) { j++; } else { IGRAPH_CHECK(igraph_i_lad_removeValue(u2, VECTOR(D->val)[j], D, Gp, Gt, &result)); @@ -507,36 +507,27 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, compatibilities given in file return false if a domain is empty and true otherwise */ igraph_integer_t *val; - bool *dom; + igraph_bitset_t dom; 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, igraph_integer_t); - ALLOC_ARRAY(dom, Gt->nbVertices, bool); + IGRAPH_BITSET_INIT_FINALLY(&dom, Gt->nbVertices); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingP, Gp->nbVertices); - igraph_vector_int_fill(&D->globalMatchingP, -1L); + igraph_vector_int_fill(&D->globalMatchingP, -1); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->globalMatchingT, Gt->nbVertices); - igraph_vector_int_fill(&D->globalMatchingT, -1L); + igraph_vector_int_fill(&D->globalMatchingT, -1); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->nbVal, Gp->nbVertices); - - IGRAPH_CHECK(igraph_vector_int_init(&D->firstVal, Gp->nbVertices)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &D->firstVal); - - IGRAPH_CHECK(igraph_matrix_int_init(&D->posInVal, - Gp->nbVertices, Gt->nbVertices)); - IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->posInVal); - - IGRAPH_CHECK(igraph_matrix_int_init(&D->firstMatch, - Gp->nbVertices, Gt->nbVertices)); - IGRAPH_FINALLY(igraph_matrix_int_destroy, &D->firstMatch); - - IGRAPH_CHECK(igraph_vector_char_init(&D->markedToFilter, Gp->nbVertices)); - IGRAPH_FINALLY(igraph_vector_char_destroy, &D->markedToFilter); - + IGRAPH_VECTOR_INT_INIT_FINALLY(&D->firstVal, Gp->nbVertices); + IGRAPH_MATRIX_INT_INIT_FINALLY(&D->posInVal, + Gp->nbVertices, Gt->nbVertices); + IGRAPH_MATRIX_INT_INIT_FINALLY(&D->firstMatch, + Gp->nbVertices, Gt->nbVertices); + IGRAPH_BITSET_INIT_FINALLY(&D->markedToFilter, Gp->nbVertices); IGRAPH_VECTOR_INT_INIT_FINALLY(&D->toFilter, Gp->nbVertices); D->valSize = 0; @@ -548,19 +539,19 @@ static igraph_error_t igraph_i_lad_initDomains(bool 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); - memset(dom, false, sizeof(bool) * (size_t)(Gt->nbVertices)); + igraph_bitset_null(&dom); for (j = 0; j < i; j++) { v = VECTOR(*vec)[j]; - dom[v] = true; + IGRAPH_BIT_SET(dom, v); } } - VECTOR(D->markedToFilter)[u] = true; + IGRAPH_BIT_SET(D->markedToFilter, u); VECTOR(D->toFilter)[u] = u; VECTOR(D->nbVal)[u] = 0; VECTOR(D->firstVal)[u] = D->valSize; for (v = 0; v < Gt->nbVertices; v++) { igraph_vector_int_t *Gt_vneis = igraph_adjlist_get(&Gt->succ, v); - if ((initialDomains) && (!dom[v])) { /* v not in D(u) */ + if ((initialDomains) && (!IGRAPH_BIT_TEST(dom, v))) { /* v not in D(u) */ MATRIX(D->posInVal, u, v) = VECTOR(D->firstVal)[u] + Gt->nbVertices; } else { @@ -568,15 +559,13 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, matchingSize += VECTOR(Gp->nbSucc)[u]; if (VECTOR(Gp->nbSucc)[u] <= VECTOR(Gt->nbSucc)[v]) { 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); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(mu, "Insufficient memory for subisomorphism search with LAD."); + IGRAPH_FINALLY(igraph_free, mu); + 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); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(mu, "Insufficient memory for subisomorphism search with LAD."); + IGRAPH_FINALLY(igraph_free, mv); + for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { mu[i] = VECTOR(Gp->nbSucc)[VECTOR(*Gp_uneis)[i]]; } @@ -584,7 +573,7 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, mv[i] = VECTOR(Gt->nbSucc)[VECTOR(*Gt_vneis)[i]]; } if (igraph_i_lad_compare(VECTOR(Gp->nbSucc)[u], mu, - VECTOR(Gt->nbSucc)[v], mv) == 1) { + VECTOR(Gt->nbSucc)[v], mv)) { val[D->valSize] = v; VECTOR(D->nbVal)[u]++; MATRIX(D->posInVal, u, v) = D->valSize++; @@ -592,11 +581,11 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, MATRIX(D->posInVal, u, v) = VECTOR(D->firstVal)[u] + Gt->nbVertices; } - igraph_free(mu); mu = 0; - igraph_free(mv); mv = 0; + IGRAPH_FREE(mu); + IGRAPH_FREE(mv); + IGRAPH_FINALLY_CLEAN(2); } else { /* v not in D(u) */ - MATRIX(D->posInVal, u, v) = - VECTOR(D->firstVal)[u] + Gt->nbVertices; + MATRIX(D->posInVal, u, v) = VECTOR(D->firstVal)[u] + Gt->nbVertices; } } } @@ -604,7 +593,7 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, *empty = true; /* empty domain */ igraph_free(val); - igraph_free(dom); + igraph_bitset_destroy(&dom); /* On this branch, 'val' and 'matching' are unused. * We init them anyway so that we can have a consistent destructor. */ @@ -629,7 +618,7 @@ static igraph_error_t igraph_i_lad_initDomains(bool initialDomains, *empty = false; igraph_free(val); - igraph_free(dom); + igraph_bitset_destroy(&dom); IGRAPH_FINALLY_CLEAN(12); @@ -643,7 +632,7 @@ static void igraph_i_lad_destroyDomains(Tdomain *D) { igraph_vector_int_destroy(&D->firstVal); igraph_matrix_int_destroy(&D->posInVal); igraph_matrix_int_destroy(&D->firstMatch); - igraph_vector_char_destroy(&D->markedToFilter); + igraph_bitset_destroy(&D->markedToFilter); igraph_vector_int_destroy(&D->toFilter); igraph_vector_int_destroy(&D->val); @@ -730,8 +719,7 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra 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); + IGRAPH_VECTOR_INT_INIT_FINALLY(&path, 0); /* initialize matchedWithV and unmatched */ memset(matchedWithV, -1, (size_t)sizeOfV * sizeof(matchedWithV[0])); @@ -919,7 +907,7 @@ static igraph_error_t igraph_i_lad_updateMatching(igraph_integer_t sizeOfU, igra return IGRAPH_SUCCESS; } -static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t u, bool* marked, igraph_integer_t* nbSucc, +static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_integer_t u, igraph_bitset_t *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 @@ -934,10 +922,10 @@ static void igraph_i_lad_DFS(igraph_integer_t nbU, igraph_integer_t nbV, igraph_ 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 */ - marked[u] = true; + IGRAPH_BIT_SET(*marked, u); if (v >= 0) { for (i = 0; i < nbSucc[v]; i++) { - if (!marked[succ[v * nbU + i]]) { + if (!IGRAPH_BIT_TEST(*marked, succ[v * nbU + i])) { igraph_i_lad_DFS(nbU, nbV, succ[v * nbU + i], marked, nbSucc, succ, matchedWithU, order, nb); } @@ -963,20 +951,20 @@ 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; - bool *marked; + igraph_bitset_t marked; igraph_integer_t *fifo; igraph_integer_t u, v, i, j, k, nbSCC, nb; /* Allocate memory */ ALLOC_ARRAY(order, nbU, igraph_integer_t); - ALLOC_ARRAY(marked, nbU, bool); + IGRAPH_BITSET_INIT_FINALLY(&marked, nbU); ALLOC_ARRAY(fifo, nbV, igraph_integer_t); /* Order vertices of Gp wrt DFS */ nb = nbU - 1; for (u = 0; u < nbU; u++) { - if (!marked[u]) { - igraph_i_lad_DFS(nbU, nbV, u, marked, nbSucc, succ, matchedWithU, + if (!IGRAPH_BIT_TEST(marked, u)) { + igraph_i_lad_DFS(nbU, nbV, u, &marked, nbSucc, succ, matchedWithU, order, &nb); } } @@ -1014,7 +1002,7 @@ static igraph_error_t igraph_i_lad_SCC(igraph_integer_t nbU, igraph_integer_t nb /* Free memory */ igraph_free(fifo); - igraph_free(marked); + igraph_bitset_destroy(&marked); igraph_free(order); IGRAPH_FINALLY_CLEAN(3); @@ -1043,7 +1031,7 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg igraph_integer_t u, v, i, w, oldNbVal, nbToMatch; igraph_integer_t *numV, *numU; igraph_vector_int_t toMatch; - bool *used; + igraph_bitset_t used; igraph_integer_t *list; igraph_integer_t nb = 0; bool result; @@ -1055,15 +1043,14 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg 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); + IGRAPH_BITSET_INIT_FINALLY(&used, Gp->nbVertices * Gt->nbVertices); ALLOC_ARRAY(list, Gt->nbVertices, igraph_integer_t); - IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp->nbVertices)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); + IGRAPH_VECTOR_INT_INIT_FINALLY(&toMatch, Gp->nbVertices); for (u = 0; u < Gp->nbVertices; u++) { for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ - used[u * Gt->nbVertices + v] = false; + IGRAPH_BIT_CLEAR(used, u * Gt->nbVertices + v); if (v != VECTOR(D->globalMatchingP)[u]) { pred[u * Gt->nbVertices + (nbPred[u]++)] = v; succ[v * Gp->nbVertices + (nbSucc[v]++)] = u; @@ -1082,11 +1069,11 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg v = list[--nb]; for (i = 0; i < nbSucc[v]; i++) { u = succ[v * Gp->nbVertices + i]; - used[u * Gt->nbVertices + v] = true; + IGRAPH_BIT_SET(used, u * Gt->nbVertices + v); if (numU[u] == false) { numU[u] = true; w = VECTOR(D->globalMatchingP)[u]; - used[u * Gt->nbVertices + w] = true; + IGRAPH_BIT_SET(used, u * Gt->nbVertices + w); if (numV[w] == false) { list[nb++] = w; numV[w] = true; @@ -1108,7 +1095,7 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg oldNbVal = VECTOR(D->nbVal)[u]; for (i = 0; i < VECTOR(D->nbVal)[u]; i++) { v = VECTOR(D->val)[ VECTOR(D->firstVal)[u] + i ]; /* v in D(u) */ - if ((!used[u * Gt->nbVertices + v]) && (numV[v] != numU[u]) && + if ((!IGRAPH_BIT_TEST(used, u * Gt->nbVertices + v)) && (numV[v] != numU[u]) && (VECTOR(D->globalMatchingP)[u] != v)) { IGRAPH_CHECK(igraph_i_lad_removeValue(u, v, D, Gp, Gt, &result)); if (!result) { @@ -1133,7 +1120,7 @@ static igraph_error_t igraph_i_lad_ensureGACallDiff(bool induced, Tgraph* Gp, Tg cleanup: igraph_vector_int_destroy(&toMatch); igraph_free(list); - igraph_free(used); + igraph_bitset_destroy(&used); igraph_free(numU); igraph_free(numV); igraph_free(succ); @@ -1178,7 +1165,7 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t /* look for a support of edge (u, u2) for v */ for (i = VECTOR(D->firstVal)[u2]; i < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; i++) { - if (MATRIX(Gt->isEdge, v, VECTOR(D->val)[i])) { + if (ISEDGE(Gt, v, VECTOR(D->val)[i])) { VECTOR(D->matching)[ MATRIX(D->firstMatch, u, v) ] = VECTOR(D->val)[i]; *result = true; @@ -1213,17 +1200,12 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t 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_FINALLY(igraph_vector_int_destroy, &nbComp); - IGRAPH_CHECK(igraph_vector_int_init(&firstComp, VECTOR(Gp->nbSucc)[u])); - IGRAPH_FINALLY(igraph_vector_int_destroy, &firstComp); + IGRAPH_VECTOR_INT_INIT_FINALLY(&nbComp, VECTOR(Gp->nbSucc)[u]); + IGRAPH_VECTOR_INT_INIT_FINALLY(&firstComp, VECTOR(Gp->nbSucc)[u]); /* 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] * - Gt->nbVertices))); - IGRAPH_FINALLY(igraph_vector_int_destroy, &comp); - IGRAPH_CHECK(igraph_vector_int_init(&matchedWithU, VECTOR(Gp->nbSucc)[u])); - IGRAPH_FINALLY(igraph_vector_int_destroy, &matchedWithU); + IGRAPH_VECTOR_INT_INIT_FINALLY(&comp, (VECTOR(Gp->nbSucc)[u] * Gt->nbVertices)); + IGRAPH_VECTOR_INT_INIT_FINALLY(&matchedWithU, VECTOR(Gp->nbSucc)[u]); memset(num, -1, (size_t) (Gt->nbVertices) * sizeof(num[0])); for (i = 0; i < VECTOR(Gp->nbSucc)[u]; i++) { u2 = VECTOR(*Gp_uneis)[i]; /* u2 is adjacent to u */ @@ -1234,7 +1216,7 @@ static igraph_error_t igraph_i_lad_checkLAD(igraph_integer_t u, igraph_integer_t for (j = VECTOR(D->firstVal)[u2]; j < VECTOR(D->firstVal)[u2] + VECTOR(D->nbVal)[u2]; j++) { v2 = VECTOR(D->val)[j]; /* v2 belongs to D[u2] */ - if (MATRIX(Gt->isEdge, v, v2)) { /* v2 is a successor of v */ + if (ISEDGE(Gt, v, v2)) { /* v2 is a successor of v */ if (num[v2] < 0) { /* v2 has not yet been added to V */ num[v2] = nbNum; numInv[nbNum++] = v2; @@ -1404,7 +1386,7 @@ static igraph_error_t igraph_i_lad_solve(igraph_integer_t timeLimit, bool firstS if (minDom == -1) { /* All vertices are matched => Solution found */ if (iso) { - *iso = 1; + *iso = true; } (*nbSol)++; if (map && igraph_vector_int_size(map) == 0) { @@ -1542,8 +1524,8 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t igraph_vector_int_list_t *maps, igraph_bool_t induced, igraph_integer_t time_limit) { - bool firstSol = maps == 0; - bool initialDomains = domains != 0; + bool firstSol = maps == NULL; + bool initialDomains = domains != NULL; Tgraph Gp, Gt; Tdomain D; igraph_bool_t invalidDomain; @@ -1621,7 +1603,7 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t goto exit; } - IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((char) induced, &Gp, &Gt, &D, + IGRAPH_CHECK(igraph_i_lad_ensureGACallDiff((bool) induced, &Gp, &Gt, &D, &invalidDomain)); if (invalidDomain) { goto exit; @@ -1631,15 +1613,14 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t VECTOR(D.globalMatchingT)[ VECTOR(D.globalMatchingP)[u] ] = u; } - IGRAPH_CHECK(igraph_vector_int_init(&toMatch, Gp.nbVertices)); - IGRAPH_FINALLY(igraph_vector_int_destroy, &toMatch); + IGRAPH_VECTOR_INT_INIT_FINALLY(&toMatch, Gp.nbVertices); for (u = 0; u < Gp.nbVertices; u++) { if (VECTOR(D.nbVal)[u] == 1) { VECTOR(toMatch)[nbToMatch++] = u; } } - IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, (char) induced, + IGRAPH_CHECK(igraph_i_lad_matchVertices(nbToMatch, &toMatch, (bool) induced, &D, &Gp, &Gt, &invalidDomain)); igraph_vector_int_destroy(&toMatch); IGRAPH_FINALLY_CLEAN(1); @@ -1650,7 +1631,7 @@ igraph_error_t igraph_subisomorphic_lad(const igraph_t *pattern, const igraph_t IGRAPH_CHECK(igraph_vector_ptr_init(&alloc_history, 0)); IGRAPH_FINALLY(igraph_vector_ptr_destroy_all, &alloc_history); - IGRAPH_CHECK(igraph_i_lad_solve(time_limit, firstSol, (char) induced, &D, + IGRAPH_CHECK(igraph_i_lad_solve(time_limit, firstSol, (bool) induced, &D, &Gp, &Gt, &invalidDomain, iso, &vec, map, maps, &nbNodes, &nbFail, &nbSol, &begin, &alloc_history)); diff --git a/src/layout/drl/drl_graph.cpp b/src/layout/drl/drl_graph.cpp index 219a1129ce..1707163cdd 100644 --- a/src/layout/drl/drl_graph.cpp +++ b/src/layout/drl/drl_graph.cpp @@ -1254,7 +1254,7 @@ float graph::get_tot_energy ( ) { // } -int graph::draw_graph(igraph_matrix_t *res) { +igraph_error_t graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } @@ -1264,7 +1264,7 @@ int graph::draw_graph(igraph_matrix_t *res) { MATRIX(*res, i, 0) = positions[i].x; MATRIX(*res, i, 1) = positions[i].y; } - return 0; + return IGRAPH_SUCCESS; } } // namespace drl diff --git a/src/layout/drl/drl_graph.h b/src/layout/drl/drl_graph.h index df1424f943..3fc6833795 100644 --- a/src/layout/drl/drl_graph.h +++ b/src/layout/drl/drl_graph.h @@ -65,7 +65,7 @@ class graph { void scan_int ( char *filename ); void read_int ( char *file_name ); void draw_graph ( int int_out, char *coord_file ); - int draw_graph (igraph_matrix_t *res); + igraph_error_t draw_graph(igraph_matrix_t *res); void write_coord ( const char *file_name ); void write_sim ( const char *file_name ); float get_tot_energy ( ); diff --git a/src/layout/drl/drl_graph_3d.cpp b/src/layout/drl/drl_graph_3d.cpp index 98dc567b87..b64fd957c5 100644 --- a/src/layout/drl/drl_graph_3d.cpp +++ b/src/layout/drl/drl_graph_3d.cpp @@ -820,7 +820,7 @@ float graph::get_tot_energy ( ) { } -int graph::draw_graph(igraph_matrix_t *res) { +igraph_error_t graph::draw_graph(igraph_matrix_t *res) { while (ReCompute()) { IGRAPH_ALLOW_INTERRUPTION(); } @@ -831,7 +831,7 @@ int graph::draw_graph(igraph_matrix_t *res) { MATRIX(*res, i, 1) = positions[i].y; MATRIX(*res, i, 2) = positions[i].z; } - return 0; + return IGRAPH_SUCCESS; } } // namespace drl3d diff --git a/src/layout/drl/drl_graph_3d.h b/src/layout/drl/drl_graph_3d.h index b87ad86ce7..ffaad8e483 100644 --- a/src/layout/drl/drl_graph_3d.h +++ b/src/layout/drl/drl_graph_3d.h @@ -60,7 +60,7 @@ class graph { 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 draw_graph (igraph_matrix_t *res); + igraph_error_t draw_graph(igraph_matrix_t *res); float get_tot_energy ( ); // Con/Decon diff --git a/src/layout/drl/drl_layout.cpp b/src/layout/drl/drl_layout.cpp index 6316d44167..96772965c2 100644 --- a/src/layout/drl/drl_layout.cpp +++ b/src/layout/drl/drl_layout.cpp @@ -476,7 +476,7 @@ igraph_error_t igraph_layout_drl(const igraph_t *graph, igraph_matrix_t *res, IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 2)); neighbors.read_real(res); } - neighbors.draw_graph(res); + IGRAPH_CHECK(neighbors.draw_graph(res)); RNG_END(); ); diff --git a/src/layout/drl/drl_layout_3d.cpp b/src/layout/drl/drl_layout_3d.cpp index 557adb9ed7..f3522cdeaf 100644 --- a/src/layout/drl/drl_layout_3d.cpp +++ b/src/layout/drl/drl_layout_3d.cpp @@ -127,7 +127,7 @@ igraph_error_t igraph_layout_drl_3d(const igraph_t *graph, igraph_matrix_t *res, IGRAPH_CHECK(igraph_matrix_resize(res, igraph_vcount(graph), 3)); neighbors.read_real(res); } - neighbors.draw_graph(res); + IGRAPH_CHECK(neighbors.draw_graph(res)); RNG_END(); ); diff --git a/src/layout/large_graph.c b/src/layout/large_graph.c index 311643de7d..2bcbfa5d63 100644 --- a/src/layout/large_graph.c +++ b/src/layout/large_graph.c @@ -26,7 +26,6 @@ #include "igraph_interface.h" #include "igraph_progress.h" #include "igraph_random.h" -#include "igraph_structural.h" #include "igraph_visitor.h" #include "core/grid.h" @@ -97,7 +96,6 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t 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; @@ -138,18 +136,25 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, } if (repulserad <= 0) { - IGRAPH_ERRORF("Repusion cutoff radius must be positive, got %g.", IGRAPH_EINVAL, repulserad); + IGRAPH_ERRORF("Repulsion cutoff radius must be positive, got %g.", IGRAPH_EINVAL, repulserad); } if (cellsize <= 0) { IGRAPH_ERRORF("Cell size must be positive, got %g.", IGRAPH_EINVAL, cellsize); } - IGRAPH_CHECK(igraph_minimum_spanning_tree_unweighted(graph, &mst)); - IGRAPH_FINALLY(igraph_destroy, &mst); - IGRAPH_CHECK(igraph_matrix_resize(res, no_of_nodes, 2)); + /* Note: The LGL paper describes an algorithm that uses weights, and + * determines the layers by traversing the minimum spanning tree (MST) + * of the weighted graph starting from a chosen root. This function + * does not currently use weights, so all spanning trees are of the + * same weight. Therefore, we currently use a BFS traversal of the + * original graph from the root. + * + * TODO: If this function is updated to handle weights, it should + * construct the MST and traverse that instead. */ + RNG_BEGIN(); /* Determine the root vertex, random pick right now */ @@ -164,7 +169,7 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, 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, root, IGRAPH_ALL, &vids, &layers, &parents)); + IGRAPH_CHECK(igraph_bfs_simple(graph, root, IGRAPH_ALL, &vids, &layers, &parents)); } no_of_layers = igraph_vector_int_size(&layers) - 1; @@ -174,10 +179,6 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, IGRAPH_WARNING("LGL layout does not support disconnected graphs yet."); } - /* 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); @@ -374,7 +375,6 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, RNG_END(); 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); @@ -383,7 +383,7 @@ igraph_error_t igraph_layout_lgl(const igraph_t *graph, igraph_matrix_t *res, igraph_vector_int_destroy(&eids); igraph_vector_destroy(&forcex); igraph_vector_destroy(&forcey); - IGRAPH_FINALLY_CLEAN(9); + IGRAPH_FINALLY_CLEAN(8); return IGRAPH_SUCCESS; } diff --git a/src/layout/layout_internal.h b/src/layout/layout_internal.h index f4af93e1cb..a9b193a16a 100644 --- a/src/layout/layout_internal.h +++ b/src/layout/layout_internal.h @@ -45,14 +45,16 @@ IGRAPH_PRIVATE_EXPORT igraph_error_t igraph_i_layout_sphere_3d(igraph_matrix_t * 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 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_FUNCATTR_CONST 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_FUNCATTR_CONST 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_error_t igraph_i_umap_fit_ab(igraph_real_t min_dist, igraph_real_t *a_p, diff --git a/src/layout/mds.c b/src/layout/mds.c index 288e4f71c9..97d0cc31b6 100644 --- a/src/layout/mds.c +++ b/src/layout/mds.c @@ -73,12 +73,12 @@ igraph_error_t igraph_i_layout_mds_single(const igraph_t* graph, igraph_matrix_t /* Handle the trivial cases */ if (no_of_nodes == 1) { IGRAPH_CHECK(igraph_matrix_resize(res, 1, dim)); - igraph_matrix_fill(res, 0); + igraph_matrix_null(res); return IGRAPH_SUCCESS; } if (no_of_nodes == 2) { IGRAPH_CHECK(igraph_matrix_resize(res, 2, dim)); - igraph_matrix_fill(res, 0); + igraph_matrix_null(res); for (j = 0; j < dim; j++) { MATRIX(*res, 1, j) = 1; } diff --git a/src/layout/sugiyama.c b/src/layout/sugiyama.c index e50debd4b0..0107bf29d6 100644 --- a/src/layout/sugiyama.c +++ b/src/layout/sugiyama.c @@ -581,7 +581,7 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_vertically(const igra IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); if (no_of_edges == 0) { - igraph_vector_int_fill(membership, 0); + igraph_vector_int_null(membership); return IGRAPH_SUCCESS; } @@ -909,9 +909,7 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig } */ - IGRAPH_CHECK(igraph_vector_bool_init(&ignored_edges, no_of_edges)); - IGRAPH_FINALLY(igraph_vector_bool_destroy, &ignored_edges); - + IGRAPH_VECTOR_BOOL_INIT_FINALLY(&ignored_edges, no_of_edges); IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_to_the_left, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis1, 0); IGRAPH_VECTOR_INT_INIT_FINALLY(&neis2, 0); @@ -958,9 +956,9 @@ static igraph_error_t igraph_i_layout_sugiyama_place_nodes_horizontally(const ig } if (crossing) { if (j_inner) { - VECTOR(ignored_edges)[k] = 1; + VECTOR(ignored_edges)[k] = true; } else { - VECTOR(ignored_edges)[j] = 1; + VECTOR(ignored_edges)[j] = true; } } } diff --git a/src/layout/umap.c b/src/layout/umap.c index 21173f4e2d..65c969d4ed 100644 --- a/src/layout/umap.c +++ b/src/layout/umap.c @@ -163,7 +163,6 @@ static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, * 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 @@ -171,12 +170,12 @@ static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, * \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: + * clear \em whom you are a neighbor of), the weights are usually undirected. Whenever two + * vertices are doubly connected in the distance graph, the resulting weight \c W is set as: * - * W = W1 + W2 - W1 * W2 + * + * 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 @@ -188,7 +187,6 @@ static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, * 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. * @@ -196,7 +194,9 @@ static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, * References: * * - * Leland McInnes, John Healy, and James Melville. https://arxiv.org/abs/1802.03426 + * Leland McInnes, John Healy, and James Melville: + * UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction (2020) + * 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 @@ -211,8 +211,9 @@ static igraph_error_t igraph_i_umap_find_sigma(const igraph_vector_t *distances, * 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. + * + * \sa \ref igraph_layout_umap(), \ref igraph_layout_umap_3d() */ igraph_error_t igraph_layout_umap_compute_weights( const igraph_t *graph, @@ -1157,7 +1158,6 @@ static igraph_error_t igraph_i_layout_umap( * 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 @@ -1166,28 +1166,29 @@ static igraph_error_t igraph_i_layout_umap( * 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 + * \p 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 - + * Leland McInnes, John Healy, and James Melville: + * UMAP: Uniform Manifold Approximation and Projection for Dimension Reduction (2020) + * 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 use_seed Logical, if \c true the supplied values in the \p res argument are + * used as an initial layout, if \c 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 @@ -1196,10 +1197,12 @@ static igraph_error_t igraph_i_layout_umap( * \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 + * true, the \p distances vector contains precomputed weights. If \c false (the * typical use case), this function will compute weights from distances and * then use them to compute the layout. * \return Error code. + * + * \sa \ref igraph_layout_umap_3d() */ igraph_error_t igraph_layout_umap(const igraph_t *graph, igraph_matrix_t *res, @@ -1219,8 +1222,6 @@ igraph_error_t igraph_layout_umap(const igraph_t *graph, * * \experimental * - * - * * This is the 3D version of the UMAP algorithm * (see \ref igraph_layout_umap() for the 2D version). * @@ -1241,10 +1242,12 @@ igraph_error_t igraph_layout_umap(const igraph_t *graph, * 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 + * then use them to compute the layout. If \c true, the \p distances vector contains * precomputed weights, including possibly some weights equal to zero that are * inconsequential for the layout optimization. * \return Error code. + * + * \sa \ref igraph_layout_umap() */ igraph_error_t igraph_layout_umap_3d(const igraph_t *graph, igraph_matrix_t *res, diff --git a/src/linalg/arpack.c b/src/linalg/arpack.c index d939feb96a..1908ecb4b5 100644 --- a/src/linalg/arpack.c +++ b/src/linalg/arpack.c @@ -126,7 +126,7 @@ 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 = { +static const igraph_arpack_options_t igraph_i_arpack_options_pristine = { /* .bmat = */ { 'I' }, /* .n = */ 0, /* .which = */ { 'X', 'X' }, @@ -866,11 +866,15 @@ static void igraph_i_arpack_auto_ncv(igraph_arpack_options_t* options) { options->ncv = 20; } /* ...but having ncv close to n leads to some problems with small graphs - * (example: PageRank of "A <--> C, D <--> E, B"), so we don't let it - * to be larger than n / 2... + * (example: PageRank of "A <--> C, D <--> E, B"), so we try to keep it + * no more than min(n/2 + 2, n - 1), bounds found empirically using the + * eigen_stress.c test... */ - if (options->ncv > options->n / 2) { - options->ncv = options->n / 2; + if (options->ncv > options->n / 2 + 2) { + options->ncv = options->n / 2 + 2; + } + if (options->ncv > options->n - 1) { + options->ncv = options->n - 1; } /* ...but we need at least min_ncv. */ if (options->ncv < min_ncv) { @@ -1402,7 +1406,7 @@ igraph_error_t igraph_arpack_rnsolve(igraph_arpack_function_t *fun, void *extra, #endif if (options->ierr != 0) { - IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->info)); + IGRAPH_ERROR("ARPACK error", igraph_i_arpack_err_dneupd(options->ierr)); } /* Save the result */ diff --git a/src/linalg/blas.c b/src/linalg/blas.c index 8835e6fb25..4439ac3d73 100644 --- a/src/linalg/blas.c +++ b/src/linalg/blas.c @@ -33,15 +33,17 @@ * * This function is a somewhat more user-friendly interface to * the \c dgemv function in BLAS. \c dgemv performs the operation - * y = alpha*A*x + beta*y, where x and y are vectors and A is an - * appropriately sized matrix (symmetric or non-symmetric). + * y = alpha*A*x + beta*y, where \p x and \p y are vectors + * and \p A is an appropriately sized matrix (symmetric or non-symmetric). * - * \param transpose whether to transpose the matrix \p A - * \param alpha the constant \p alpha - * \param a the matrix \p A - * \param x the vector \p x - * \param beta the constant \p beta - * \param y the vector \p y (which will be modified in-place) + * \param transpose Whether to transpose the matrix \p A. + * \param alpha The constant \p alpha. + * \param a The matrix \p A. + * \param x The vector \p x. + * \param beta The constant \p beta. + * \param y The vector \p y (which will be modified in-place). + * It must always have the correct length, but its + * elements need not be set when beta=0. * * Time complexity: O(nk) if the matrix is of size n x k * diff --git a/src/linalg/eigen.c b/src/linalg/eigen.c index 52ee6c711b..337e1294d2 100644 --- a/src/linalg/eigen.c +++ b/src/linalg/eigen.c @@ -1466,33 +1466,3 @@ igraph_error_t igraph_eigen_adjacency(const igraph_t *graph, return IGRAPH_SUCCESS; } - -/** - * \function igraph_eigen_laplacian - * - */ - -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, - igraph_arpack_storage_t *storage, - igraph_vector_t *values, - igraph_matrix_t *vectors, - igraph_vector_complex_t *cmplxvalues, - igraph_matrix_complex_t *cmplxvectors) { - - IGRAPH_UNUSED(graph); - IGRAPH_UNUSED(algorithm); - IGRAPH_UNUSED(which); - IGRAPH_UNUSED(options); - IGRAPH_UNUSED(storage); - IGRAPH_UNUSED(values); - IGRAPH_UNUSED(vectors); - IGRAPH_UNUSED(cmplxvalues); - IGRAPH_UNUSED(cmplxvectors); - - /* TODO */ - - IGRAPH_ERROR("'igraph_eigen_laplacian'", IGRAPH_UNIMPLEMENTED); -} diff --git a/src/math/safe_intop.c b/src/math/safe_intop.c index d02794c68c..ec6b8be8a4 100644 --- a/src/math/safe_intop.c +++ b/src/math/safe_intop.c @@ -104,11 +104,11 @@ igraph_error_t igraph_i_safe_exp2(igraph_integer_t k, igraph_integer_t *res) { } /** - * 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. + * Checks if an igraph_real_t with no fractional part is representable as an igraph_integer_t. + * Avoids invoking undefined behaviour. + * Must not be called with an input that has a non-zero fractional part. */ -static igraph_error_t igraph_i_safe_real_to_int(igraph_real_t value, igraph_integer_t *result) { +igraph_bool_t igraph_i_is_real_representable_as_integer(igraph_real_t value) { /* 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 changing its value. @@ -122,6 +122,19 @@ static igraph_error_t igraph_i_safe_real_to_int(igraph_real_t value, igraph_inte 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 (IGRAPH_LIKELY(int_min <= value && value < int_max_plus_1)) { + return true; + } else { + return false; + } +} + +/** + * 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) { + if (igraph_i_is_real_representable_as_integer(value)) { *result = (igraph_integer_t) value; return IGRAPH_SUCCESS; } else if (isnan(value)) { @@ -169,5 +182,5 @@ igraph_error_t igraph_i_safe_round(igraph_real_t value, igraph_integer_t* result * This is typically the fastest of this set of functions. */ igraph_error_t igraph_i_safe_trunc(igraph_real_t value, igraph_integer_t* result) { - return igraph_i_safe_real_to_int(round(value), result); + return igraph_i_safe_real_to_int(trunc(value), result); } diff --git a/src/math/safe_intop.h b/src/math/safe_intop.h index 352cdb5b47..e690e7041a 100644 --- a/src/math/safe_intop.h +++ b/src/math/safe_intop.h @@ -120,6 +120,8 @@ __BEGIN_DECLS else IGRAPH_SAFE_MULT(_safe_n, (_safe_n - 1) / 2, res); \ } while (0) +IGRAPH_FUNCATTR_CONST igraph_bool_t igraph_i_is_real_representable_as_integer(igraph_real_t value); + 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); diff --git a/src/misc/bipartite.c b/src/misc/bipartite.c index 3e5da770ae..04c44ead05 100644 --- a/src/misc/bipartite.c +++ b/src/misc/bipartite.c @@ -124,7 +124,7 @@ igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, 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", + IGRAPH_ERROR("Non-bipartite edge found in bipartite projection.", IGRAPH_EINVAL); } neilen2 = igraph_vector_int_size(neis2); @@ -400,7 +400,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, /** * \function igraph_full_bipartite - * \brief Create a full bipartite network. + * \brief Creates a complete bipartite graph. * * A bipartite network contains two kinds of vertices and connections * are only possible between two vertices of different kind. There are @@ -409,10 +409,10 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, * * * igraph does not have direct support for bipartite networks, at - * least not at the C language level. In other words the igraph_t + * least not at the C language level. In other words the \type igraph_t * structure does not contain information about the vertex types. * The C functions for bipartite networks usually have an additional - * input argument to graph, called \c types, a boolean vector giving + * input argument to graph, called \p types, a boolean vector giving * the vertex types. * * @@ -420,7 +420,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, * extra vector, you just need to supply an initialized boolean vector * to them. * - * \param graph Pointer to an igraph_t object, the graph will be + * \param graph Pointer to an uninitialized graph object, the graph will be * created here. * \param types Pointer to a boolean vector. If not a null pointer, * then the vertex types will be stored here. @@ -437,7 +437,8 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. * - * \sa \ref igraph_full() for non-bipartite full graphs. + * \sa \ref igraph_full() for non-bipartite complete graphs, + * \ref igraph_full_multipartite() for complete multipartite graphs. */ igraph_error_t igraph_full_bipartite(igraph_t *graph, @@ -761,27 +762,36 @@ igraph_error_t igraph_get_incidence(const igraph_t *graph, /** * \function igraph_get_biadjacency - * \brief Convert a bipartite graph into a bipartite adjacency matrix. + * \brief Converts a bipartite graph into a bipartite adjacency matrix. + * + * In a bipartite adjacency matrix \c A, element A_ij + * gives the number of edges between the ith vertex of the + * first partition and the jth vertex of the second partition. + * + * + * If the graph contains edges within the same partition, this function + * issues a warning. * * \param graph The input graph, edge directions are ignored. - * \param types Boolean vector containing the vertex types. All vertices - * in one part of the graph should have type 0, the others type 1. + * \param types Boolean vector containing the vertex types. Vertices belonging + * to the first partition have type \c false, the one in the second + * partition type \c true. * \param res Pointer to an initialized matrix, the result is stored * here. An element of the matrix gives the number of edges * (irrespectively of their direction) between the two corresponding - * vertices. The rows will correspond to vertices with type 0, - * the columns correspond to vertices with type 1. - * \param row_ids Pointer to an initialized vector or a null - * pointer. If not a null pointer, then the vertex IDs (in the - * graph) corresponding to the rows of the result matrix are stored - * here. - * \param col_ids Pointer to an initialized vector or a null - * pointer. If not a null pointer, then the vertex IDs corresponding - * to the columns of the result matrix are stored here. + * vertices. The rows will correspond to vertices with type \c false, + * the columns correspond to vertices with type \c true. + * \param row_ids Pointer to an initialized vector or \c NULL. + * If not a null pointer, then the IDs of vertices with type \c false + * are stored here, with the same ordering as the rows of the + * biadjacency matrix. + * \param col_ids Pointer to an initialized vector or \c NULL. + * If not a null pointer, then the IDs of vertices with type \c true + * are stored here, with the same ordering as the columns of the + * biadjacency matrix. * \return Error code. * - * Time complexity: O(n*m), n and m are number of vertices of the two - * different kind. + * Time complexity: O(|E|) where |E| is the number of edges. * * \sa \ref igraph_biadjacency() for the opposite operation. */ @@ -831,7 +841,7 @@ igraph_error_t igraph_get_biadjacency( } } if (ignored_edges) { - IGRAPH_WARNINGF("%" IGRAPH_PRId " edges running within partitions were ignored.", ignored_edges); + IGRAPH_WARNINGF("%" IGRAPH_PRId " edges running within partitions were ignored.", ignored_edges); } if (row_ids) { @@ -913,7 +923,7 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, /* Shortcut: Graphs with self-loops are not bipartite. */ if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { - if (*res) { + if (res) { *res = false; } return IGRAPH_SUCCESS; @@ -924,7 +934,7 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, if (! types && igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST)) { - if (*res) { + if (res) { *res = true; } return IGRAPH_SUCCESS; @@ -953,7 +963,7 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, for (j = 0; j < n; j++) { igraph_integer_t nei = VECTOR(neis)[j]; if (VECTOR(seen)[nei]) { - igraph_integer_t neitype = VECTOR(seen)[nei]; + char neitype = VECTOR(seen)[nei]; if (neitype == acttype) { bi = false; break; @@ -997,8 +1007,9 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, * \function igraph_bipartite_game_gnp * \brief Generates a random bipartite graph with a fixed connection probability. * - * In the G(n1, n2, p) model, every possible edge between the \p n1 bottom vertices - * and \p n2 top vertices is realized with probability \p p. + * In the G(n1, n2, p) model, every possible edge between the \p n1 + * bottom vertices and \p n2 top vertices is realized independently with + * probability \p p. * * \param graph Pointer to an uninitialized igraph graph, the result * is stored here. @@ -1022,7 +1033,7 @@ igraph_error_t igraph_is_bipartite(const igraph_t *graph, * \return Error code. * * \sa \ref igraph_erdos_renyi_game_gnp() for the unipartite version, - * \ref igraph_bipartite_game_gnm() for the G(n1, n2, m) model. + * \ref igraph_bipartite_game_gnm() for the G(n1, n2, m) model. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. @@ -1135,14 +1146,14 @@ igraph_error_t igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t * * \function igraph_bipartite_game_gnm * \brief Generate a random bipartite graph with a fixed number of edges. * - * In the G(n1, n2, m) model we uniformly choose \p m edges to realize - * between the \p n1 bottom vertices and \p n2 top vertices. + * The G(n1, n2, m) model uniformly samples bipartite graphs with + * \p n1 bottom vertices and \p n2 top vertices, and precisely \p m edges. * * \param graph Pointer to an uninitialized igraph graph, the result * is stored here. * \param types Pointer to an initialized boolean vector, or a null * pointer. If not a null pointer, then the vertex types are stored - * here. Bottom vertices come first, n1 of them, then n2 top + * here. Bottom vertices come first, \p n1 of them, then \p n2 top * vertices. * \param n1 The number of bottom vertices. * \param n2 The number of top vertices. @@ -1160,7 +1171,8 @@ igraph_error_t igraph_bipartite_game_gnp(igraph_t *graph, igraph_vector_bool_t * * \return Error code. * * \sa \ref igraph_erdos_renyi_game_gnm() for the unipartite version, - * \ref igraph_bipartite_game_gnp() for the G(n1, n2, p) model. + * \ref igraph_bipartite_game_gnp() for the G(n1, n2, p) + * model. * * Time complexity: O(|V|+|E|), linear in the number of vertices and * edges. diff --git a/src/misc/cocitation.c b/src/misc/cocitation.c index a73253f58f..9657162ec5 100644 --- a/src/misc/cocitation.c +++ b/src/misc/cocitation.c @@ -19,6 +19,7 @@ #include "igraph_cocitation.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_interface.h" #include "core/interruption.h" @@ -172,7 +173,7 @@ igraph_error_t igraph_similarity_inverse_log_weighted(const igraph_t *graph, igraph_integer_t no_of_nodes = igraph_vcount(graph); if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { - IGRAPH_ERROR("Invalid neighbor mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid mode for inverse log weighted similarity.", IGRAPH_EINVMODE); } IGRAPH_VECTOR_INIT_FINALLY(&weights, no_of_nodes); @@ -267,20 +268,9 @@ static igraph_error_t igraph_i_neisets_intersect( igraph_integer_t *len_union, igraph_integer_t *len_intersection ) { /* ASSERT: v1 and v2 are sorted */ - 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; - while (i < i0 && j < jj0) { - if (VECTOR(*v1)[i] == VECTOR(*v2)[j]) { - (*len_intersection)++; (*len_union)--; - i++; j++; - } else if (VECTOR(*v1)[i] < VECTOR(*v2)[j]) { - i++; - } else { - j++; - } - } + igraph_integer_t n1 = igraph_vector_int_size(v1), n2 = igraph_vector_int_size(v2); + *len_intersection = igraph_vector_int_intersection_size_sorted(v1, v2); + *len_union = n1 + n2 - *len_intersection; return IGRAPH_SUCCESS; } @@ -468,15 +458,15 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec if (loops) { /* Add the loop edges */ - igraph_vector_bool_t seen; - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen, no_of_nodes); + igraph_bitset_t seen; + IGRAPH_BITSET_INIT_FINALLY(&seen, no_of_nodes); for (igraph_integer_t i = 0; i < k; i++) { igraph_integer_t j = VECTOR(*pairs)[i]; - if (VECTOR(seen)[j]) { + if (IGRAPH_BIT_TEST(seen, j)) { continue; } - VECTOR(seen)[j] = true; + IGRAPH_BIT_SET(seen, j); v1 = igraph_lazy_adjlist_get(&al, j); IGRAPH_CHECK_OOM(v1, "Failed to query neighbors."); if (!igraph_vector_int_binsearch(v1, j, &u)) { @@ -484,7 +474,7 @@ igraph_error_t igraph_similarity_jaccard_pairs(const igraph_t *graph, igraph_vec } } - igraph_vector_bool_destroy(&seen); + igraph_bitset_destroy(&seen); IGRAPH_FINALLY_CLEAN(1); } diff --git a/src/misc/coloring.c b/src/misc/coloring.c index 906f701fcb..b88da7e31b 100644 --- a/src/misc/coloring.c +++ b/src/misc/coloring.c @@ -36,7 +36,7 @@ static igraph_error_t igraph_i_vertex_coloring_greedy_cn(const igraph_t *graph, igraph_vector_int_t neighbors, nei_colors; IGRAPH_CHECK(igraph_vector_int_resize(colors, vc)); - igraph_vector_int_fill(colors, 0); + igraph_vector_int_null(colors); /* Nothing to do for 0 or 1 vertices. * Remember that colours are integers starting from 0, diff --git a/src/misc/degree_sequence.cpp b/src/misc/degree_sequence.cpp index 7f85482cb2..bc6ae983c9 100644 --- a/src/misc/degree_sequence.cpp +++ b/src/misc/degree_sequence.cpp @@ -193,8 +193,9 @@ static igraph_error_t igraph_i_havel_hakimi_index(const igraph_vector_int_t *deg // move the first element to the correct position fully sort the sequence. template static void bubble_up(It first, It last, Compare comp) { - if (first == last) + if (first == last) { return; + } It it = first; it++; while (it != last) { @@ -218,8 +219,9 @@ static void bubble_up(It first, It last, Compare comp) { 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) + if (vcount == 0) { return IGRAPH_SUCCESS; + } std::vector vertices; vertices.reserve(vcount); @@ -247,9 +249,9 @@ 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) { - VECTOR(*edges)[2*ec] = w.vertex; - VECTOR(*edges)[2*ec+1] = w.vertex; + 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++; } break; @@ -280,7 +282,7 @@ static igraph_error_t igraph_i_realize_undirected_multi(const igraph_vector_int_ // If largest=true, the first two elements may be out of order. // Restore the sorted order using a single step of bubble sort. if (largest) { - bubble_up(vertices.begin()+1, vertices.end(), degree_greater); + bubble_up(vertices.begin() + 1, vertices.end(), degree_greater); } bubble_up(vertices.begin(), vertices.end(), degree_greater); } @@ -292,8 +294,9 @@ 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); - if (vcount == 0) + if (vcount == 0) { return IGRAPH_SUCCESS; + } typedef std::list vlist; vlist vertices; @@ -321,9 +324,9 @@ static igraph_error_t igraph_i_realize_undirected_multi_index(const igraph_vecto 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) { - VECTOR(*edges)[2*ec] = vd.vertex; - VECTOR(*edges)[2*ec+1] = vd.vertex; + 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++; } return IGRAPH_SUCCESS; @@ -509,11 +512,10 @@ static igraph_error_t igraph_i_kleitman_wang_index(const igraph_vector_int_t *ou /**************************/ static igraph_error_t igraph_i_realize_undirected_degree_sequence( - igraph_t *graph, - const igraph_vector_int_t *deg, - igraph_edge_type_sw_t allowed_edge_types, - igraph_realize_degseq_t method) -{ + igraph_t *graph, + const igraph_vector_int_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; @@ -604,12 +606,11 @@ static igraph_error_t igraph_i_realize_undirected_degree_sequence( static igraph_error_t igraph_i_realize_directed_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_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_integer_t node_count = igraph_vector_int_size(outdeg); igraph_integer_t edge_count, edge_count2, indeg_sum; @@ -667,62 +668,71 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * \function igraph_realize_degree_sequence * \brief Generates a graph with the given degree sequence. * - * This function generates an undirected graph that realizes a given degree sequence, - * or a directed graph that realized a given pair of out- and in-degree sequences. + * This function generates an undirected graph that realizes a given degree + * sequence, or a directed graph that realizes a given pair of out- and + * in-degree sequences. * * * Simple undirected graphs are constructed using the Havel-Hakimi algorithm * (undirected case), or the analogous Kleitman-Wang algorithm (directed case). - * These algorithms work by choosing an arbitrary vertex and connecting all its stubs - * to other vertices of highest degree. In the directed case, the "highest" (in, out) degree - * pairs are determined based on lexicographic ordering. This step is repeated until all degrees - * have been connected up. + * These algorithms work by choosing an arbitrary vertex and connecting all its + * stubs to other vertices of highest degree. In the directed case, the + * "highest" (in, out) degree pairs are determined based on lexicographic + * ordering. This step is repeated until all degrees have been connected up. * * - * Loopless multigraphs are generated using an analogous algorithm: an arbitrary vertex is chosen, - * and it is connected with a single connection to a highest remaining degee vertex. If self-loops - * are also allowed, the same algorithm is used, but if a non-zero vertex remains at the end of the - * procedure, the graph is completed by adding self-loops to it. Thus, the result will contain at most - * one vertex with self-loops. + * Loopless multigraphs are generated using an analogous algorithm: an arbitrary + * vertex is chosen, and it is connected with a single connection to a highest + * remaining degee vertex. If self-loops are also allowed, the same algorithm + * is used, but if a non-zero vertex remains at the end of the procedure, the + * graph is completed by adding self-loops to it. Thus, the result will contain + * at most one vertex with self-loops. * * - * The \c method parameter controls the order in which the vertices to be connected are chosen. + * The \c method parameter controls the order in which the vertices to be + * connected are chosen. In the undirected case, \c IGRAPH_REALIZE_DEGSEQ_SMALLEST + * produces a connected graph when one exists. This makes this method suitable + * for constructing trees with a given degree sequence. * * * References: * * - * V. Havel, + * V. Havel: * Poznámka o existenci konečných grafů (A remark on the existence of finite graphs), * Časopis pro pěstování matematiky 80, 477-480 (1955). * http://eudml.org/doc/19050 * * - * S. L. Hakimi, + * 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 * * - * D. J. Kleitman and D. L. Wang, + * D. J. Kleitman and D. L. Wang: * Algorithms for Constructing Graphs and Digraphs with Given Valences and Factors, * Discrete Mathematics 6, 1 (1973). * https://doi.org/10.1016/0012-365X%2873%2990037-X * + * P. L. Erdős, I. Miklós, Z. Toroczkai: + * A simple Havel-Hakimi type algorithm to realize graphical degree sequences of directed graphs, + * The Electronic Journal of Combinatorics 17.1 (2010). + * http://eudml.org/doc/227072 + * * - * Sz. Horvát and C. D. Modes, + * 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 * * \param graph Pointer to an uninitialized graph object. - * \param outdeg The degree sequence of an undirected graph - * (if \p indeg is NULL), or the out-degree sequence of - * a directed graph (if \p indeg is given). - * \param indeg The in-degree sequence of a directed graph. - * Pass \c NULL to generate an undirected graph. - * \param allowed_edge_types The types of edges to allow in the graph. For directed graphs, - * only \c IGRAPH_SIMPLE_SW is implemented at this moment. For undirected - * graphs, the following values are valid: + * \param outdeg The degree sequence of an undirected graph (if \p indeg is NULL), + * or the out-degree sequence of a directed graph (if \p indeg is given). + * \param indeg The in-degree sequence of a directed graph. Pass \c NULL to + * generate an undirected graph. + * \param allowed_edge_types The types of edges to allow in the graph. For + * directed graphs, only \c IGRAPH_SIMPLE_SW is implemented at this moment. + * For undirected graphs, the following values are valid: * \clist * \cli IGRAPH_SIMPLE_SW * simple graphs (i.e. no self-loops or multi-edges allowed). @@ -736,21 +746,24 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * \param method The method to generate the graph. Possible values: * \clist * \cli IGRAPH_REALIZE_DEGSEQ_SMALLEST - * The vertex with smallest remaining degree is selected first. The result is usually - * a graph with high negative degree assortativity. In the undirected case, this method - * is guaranteed to generate a connected graph, regardless of whether multi-edges are allowed, - * provided that a connected realization exists (see Horvát and Modes, 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. + * 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 http://szhorvat.net/pelican/hh-connected-graphs.html). + * In the directed case it tends to generate weakly connected graphs, + * but this is not guaranteed. * \cli IGRAPH_REALIZE_DEGSEQ_LARGEST - * The vertex with the largest remaining degree is selected first. The result - * is usually a graph with high positive degree assortativity, and is often disconnected. + * The vertex with the largest remaining degree is selected first. The + * result is usually a graph with high positive degree assortativity, and + * is often disconnected. * \cli IGRAPH_REALIZE_DEGSEQ_INDEX - * The vertices are selected in order of their index (i.e. their position in the degree vector). - * Note that sorting the degree vector and using the \c INDEX method is not equivalent - * to the \c SMALLEST method above, as \c SMALLEST uses the smallest \em remaining - * degree for selecting vertices, not the smallest \em initial degree. + * The vertices are selected in order of their index (i.e. their position + * in the degree vector). Note that sorting the degree vector and using + * the \c INDEX method is not equivalent to the \c SMALLEST method above, + * as \c SMALLEST uses the smallest \em remaining degree for selecting + * vertices, not the smallest \em initial degree. * \endclist * \return Error code: * \clist @@ -764,10 +777,14 @@ static igraph_error_t igraph_i_realize_directed_degree_sequence( * and sum of \p outdeg and \p indeg should match for directed graphs. * \endclist * - * \sa \ref igraph_is_graphical() to test graphicality without generating a graph; - * \ref igraph_degree_sequence_game() to generate random graphs with a given degree sequence; - * \ref igraph_k_regular_game() to generate random regular graphs; - * \ref igraph_rewire() to randomly rewire the edges of a graph while preserving its degree sequence. + * \sa \ref igraph_is_graphical() to test graphicality without generating a graph; + * \ref igraph_realize_bipartite_degree_sequence() to create bipartite graphs + * from two degree sequence; + * \ref igraph_degree_sequence_game() to generate random graphs with a given + * degree sequence; + * \ref igraph_k_regular_game() to generate random regular graphs; + * \ref igraph_rewire() to randomly rewire the edges of a graph while + * preserving its degree sequence. * * \example examples/simple/igraph_realize_degree_sequence.c */ @@ -818,7 +835,7 @@ static igraph_error_t igraph_i_realize_undirected_bipartite_index( // If both degree sequences are empty, it's bigraphical if (!(n1 == 0 && n2 == 0)) { - if (igraph_vector_int_min(degree1) < 0 || igraph_vector_int_min(degree2) < 0){ + if (igraph_vector_int_min(degree1) < 0 || igraph_vector_int_min(degree2) < 0) { goto fail; } } @@ -829,11 +846,11 @@ static igraph_error_t igraph_i_realize_undirected_bipartite_index( for (igraph_integer_t i = 0; i < n1; i++) { vertices1.push_back(vd_pair(i, VECTOR(*degree1)[i])); } - for (igraph_integer_t i=0; i < n2; i++) { - vertices2.push_back(vd_pair(i+n1, VECTOR(*degree2)[i])); + for (igraph_integer_t i = 0; i < n2; i++) { + vertices2.push_back(vd_pair(i + n1, VECTOR(*degree2)[i])); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum); while (!vertices1.empty() && !vertices2.empty()) { // Go by index, so we start in ds1, so ds2 needs to be sorted. @@ -853,12 +870,16 @@ static igraph_error_t igraph_i_realize_undirected_bipartite_index( goto fail; } - for (igraph_integer_t i=0;i= 0); + for (igraph_integer_t i = 0; i < vd_src.degree; i++) { + if ((*dest_vs)[i].degree == 0) { + // Not enough non-zero remaining degree vertices in opposite partition. + // Not graphical. + goto fail; + } (*dest_vs)[i].degree--; - VECTOR(edges)[2*(ec + i)] = vd_src.vertex; + VECTOR(edges)[2*(ec + i)] = vd_src.vertex; VECTOR(edges)[2*(ec + i) + 1] = (*dest_vs)[i].vertex; } ec += vd_src.degree; @@ -890,10 +911,10 @@ static igraph_error_t igraph_i_realize_undirected_bipartite_index( ec++; } } - IGRAPH_CHECK(igraph_create(graph, &edges, n1+n2, false)); + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, false)); igraph_vector_int_destroy(&edges); - IGRAPH_FINALLY_CLEAN(1); + IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -997,7 +1018,7 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( // If both degree sequences are empty, it's bigraphical if (!(n1 == 0 && n2 == 0)) { - if (igraph_vector_int_min(degrees1) < 0 || igraph_vector_int_min(degrees2) < 0){ + if (igraph_vector_int_min(degrees1) < 0 || igraph_vector_int_min(degrees2) < 0) { goto fail; } } @@ -1008,11 +1029,11 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( for (igraph_integer_t i = 0; i < n1; i++) { vertices1.push_back(vd_pair(i, VECTOR(*degrees1)[i])); } - for (igraph_integer_t i=0; i < n2; i++) { - vertices2.push_back(vd_pair(i+n1, VECTOR(*degrees2)[i])); + for (igraph_integer_t i = 0; i < n2; i++) { + vertices2.push_back(vd_pair(i + n1, VECTOR(*degrees2)[i])); } - IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum+ds2_sum); + IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, ds1_sum + ds2_sum); std::vector *src_vs; @@ -1055,7 +1076,7 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( vd_src = src_vs->front(); } - IGRAPH_ASSERT(vd_src.degree != -1); + IGRAPH_ASSERT(vd_src.degree >= 0); if (!multiedges) { // Remove the smallest element @@ -1072,16 +1093,19 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( if (dest_vs->size() < size_t(vd_src.degree)) { goto fail; } - for (igraph_integer_t i=0;i < vd_src.degree; i++) { - // decrement the degree of the delta largest vertices in the opposite partition + for (igraph_integer_t i = 0; i < vd_src.degree; i++) { + // Decrement the degree of the delta largest vertices in the opposite partition - // We should never decrement below zero, but check just in case. - IGRAPH_ASSERT((*dest_vs)[i].degree - 1 >= 0); + if ((*dest_vs)[i].degree == 0) { + // Not enough non-zero remaining degree vertices in opposite partition. + // Not graphical. + goto fail; + } (*dest_vs)[i].degree--; - VECTOR(edges)[2*(ec + i)] = vd_src.vertex; - VECTOR(edges)[2*(ec + i) + 1] = (*dest_vs)[i].vertex; + VECTOR(edges)[2 * (ec + i)] = vd_src.vertex; + VECTOR(edges)[2 * (ec + i) + 1] = (*dest_vs)[i].vertex; } ec += vd_src.degree; } @@ -1122,7 +1146,7 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( ec++; } } - IGRAPH_CHECK(igraph_create(graph, &edges, n1+n2, IGRAPH_UNDIRECTED)); + IGRAPH_CHECK(igraph_create(graph, &edges, n1 + n2, IGRAPH_UNDIRECTED)); igraph_vector_int_destroy(&edges); IGRAPH_FINALLY_CLEAN(1); @@ -1131,7 +1155,7 @@ igraph_error_t igraph_realize_bipartite_degree_sequence( fail: IGRAPH_ERRORF("The given bidegree sequence cannot be realized as a bipartite %sgraph.", - IGRAPH_EINVAL, multiedges ? "multi" : "simple "); + IGRAPH_EINVAL, multiedges ? "multi" : "simple "); IGRAPH_HANDLE_EXCEPTIONS_END; } diff --git a/src/misc/graphicality.c b/src/misc/graphicality.c index 4ccb7385a9..ba01d016ca 100644 --- a/src/misc/graphicality.c +++ b/src/misc/graphicality.c @@ -108,8 +108,7 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * * \sa \ref igraph_is_bigraphical() to check if a bi-degree-sequence can be realized as a bipartite graph; * \ref igraph_realize_degree_sequence() to construct a graph with a given degree sequence. * - * Time complexity: O(n log n) for directed graphs with at most one self-loop per vertex, - * and O(n) for all other cases, where n is the length of the degree sequence(s). + * Time complexity: O(n), 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, @@ -215,8 +214,7 @@ igraph_error_t igraph_is_graphical(const igraph_vector_int_t *out_degrees, * * \sa \ref igraph_is_graphical() * - * Time complexity: O(n log n) for simple graphs, O(n) for multigraphs, - * where n is the length of the larger degree sequence. + * Time complexity: O(n), 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, @@ -303,8 +301,8 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopless_multi(const igra * - 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; + igraph_vector_int_t num_degs; + igraph_integer_t w, b, s, c, n, k, wd, kd; n = igraph_vector_int_size(degrees); @@ -344,18 +342,40 @@ 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_VECTOR_INT_INIT_FINALLY(&num_degs, n+2); - igraph_vector_int_reverse_sort(&work); + for (igraph_integer_t i = 0; i < n; ++i) { + igraph_integer_t degree = VECTOR(*degrees)[i]; + /* Negative degrees are already checked in igraph_i_is_graphical_undirected_multi_loops() */ + if (degree > n+1) { + *res = false; + goto undirected_loopy_simple_finish; + } + + ++VECTOR(num_degs)[degree]; + } + + /* Convert num_degs to a cumulative sum array. */ + for (igraph_integer_t d = n; d >= 0; --d) { + VECTOR(num_degs)[d] += VECTOR(num_degs)[d+1]; + } + + wd = 0, kd = n+1; *res = true; w = n - 1; b = 0; s = 0; c = 0; for (k = 0; k < n; k++) { - b += VECTOR(work)[k]; + while (k >= VECTOR(num_degs)[kd]) { + --kd; + } + b += kd; c += w; - while (w > k && VECTOR(work)[w] <= k + 1) { - s += VECTOR(work)[w]; + while (w > k) { + while (wd + 1 <= n + 1 && w < VECTOR(num_degs)[wd + 1]) { + wd++; + } + if (wd > k + 1) break; + s += wd; c -= (k + 1); w--; } @@ -368,7 +388,8 @@ static igraph_error_t igraph_i_is_graphical_undirected_loopy_simple(const igraph } } - igraph_vector_int_destroy(&work); +undirected_loopy_simple_finish: + igraph_vector_int_destroy(&num_degs); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; @@ -777,10 +798,10 @@ static igraph_error_t igraph_i_is_bigraphical_multi(const igraph_vector_int_t *d * - 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; + igraph_vector_int_t deg_freq1, deg_freq2; + igraph_integer_t lhs_sum, partial_rhs_sum, partial_rhs_count; + igraph_integer_t a, b, k; if (n1 == 0 && n2 == 0) { *res = true; @@ -807,21 +828,33 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * n2 = n; } - /* 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 */ + /* Counting sort the degrees. */ + /* Since the graph is simple, a vertex's degree can be at most the size of the other partition. */ + IGRAPH_VECTOR_INT_INIT_FINALLY(°_freq1, n2+1); + IGRAPH_VECTOR_INT_INIT_FINALLY(°_freq2, n1+1); - 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 */ + for (igraph_integer_t i = 0; i < n1; ++i) { + igraph_integer_t degree = VECTOR(*degrees1)[i]; + if (degree > n2) { + *res = false; + goto bigraphical_simple_done; + } + ++VECTOR(deg_freq1)[degree]; + } + for (igraph_integer_t i = 0; i < n2; ++i) { + igraph_integer_t degree = VECTOR(*degrees2)[i]; + if (degree > n1) { + *res = false; + goto bigraphical_simple_done; + } + ++VECTOR(deg_freq2)[degree]; + } /* * We follow the description of the Gale-Ryser theorem in: * * A. Berger, A note on the characterization of digraphic sequences, Discrete Math. 314, 38 (2014). - * http://dx.doi.org/10.1016/j.disc.2013.09.010 + * https://doi.org/10.1016/j.disc.2013.09.010 * * Gale-Ryser condition with 0-based indexing: * @@ -831,7 +864,10 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * * * \sum_{i=0}^k a_i <= \sum_{j=0}^{n_2} min(b_i, k+1) * - * for all 0 <= k < n_1 + * for all 0 <= k < n_1. + * + * Additionally, based on Theorem 3 in [Berger 2014], it is sufficient to do the check + * for k such that a_k > a_{k+1} and for k=(n_1-1). */ /* While this formulation does not require sorting degree2, @@ -841,31 +877,33 @@ static igraph_error_t igraph_i_is_bigraphical_simple(const igraph_vector_int_t * *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) { - lhs_sum += VECTOR(sorted_deg1)[k]; + partial_rhs_sum = 0; /* the sum of those elements in the rhs which are <= (k+1) */ + partial_rhs_count = 0; /* number of elements in the rhs which are <= (k+1) */ + b = 0; /* index in deg_freq2 */ + k = -1; + for (a = n2; a >= 0; --a) { + igraph_integer_t acount = VECTOR(deg_freq1)[a]; + lhs_sum += a * acount; + k += acount; - /* Based on Theorem 3 in [Berger 2014], it is sufficient to do the check - * for k such that a_k > a_{k+1} and for k=(n_1-1). - */ - if (k < n1-1 && VECTOR(sorted_deg1)[k] == VECTOR(sorted_deg1)[k+1]) - continue; + while (b <= k + 1) { + igraph_integer_t bcount = VECTOR(deg_freq2)[b]; + partial_rhs_sum += b * bcount; + partial_rhs_count += bcount; - while (i < n2 && VECTOR(sorted_deg2)[i] <= k+1) { - partial_rhs_sum += VECTOR(sorted_deg2)[i]; - i++; + ++b; } - /* rhs_sum for a given k is partial_rhs_sum + (n2 - i) * (k+1) */ - if (lhs_sum > partial_rhs_sum + (n2 - i) * (k+1) ) { + /* rhs_sum for a given k is partial_rhs_sum + (n2 - partial_rhs_count) * (k+1) */ + if (lhs_sum > partial_rhs_sum + (n2 - partial_rhs_count) * (k+1) ) { *res = false; break; } } - igraph_vector_int_destroy(&sorted_deg2); - igraph_vector_int_destroy(&sorted_deg1); +bigraphical_simple_done: + igraph_vector_int_destroy(°_freq1); + igraph_vector_int_destroy(°_freq2); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; diff --git a/src/misc/microscopic_update.c b/src/misc/microscopic_update.c index 25a0936b07..9802d61e8e 100644 --- a/src/misc/microscopic_update.c +++ b/src/misc/microscopic_update.c @@ -427,7 +427,7 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, igraph_integer_t nvert; igraph_vector_int_t degv; - *updates = 1; + *updates = true; /* sanity checks */ if (graph == NULL) { @@ -460,11 +460,11 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, */ /* given graph has < 2 vertices */ if (nvert < 2) { - *updates = 0; + *updates = false; } /* graph has >= 2 vertices, but no edges */ if (igraph_ecount(graph) < 1) { - *updates = 0; + *updates = false; } /* Test for vertex isolation, depending on the perspective given. For @@ -483,7 +483,7 @@ static igraph_error_t igraph_i_microscopic_standard_tests(const igraph_t *graph, IGRAPH_CHECK(igraph_degree(graph, °v, igraph_vss_1(vid), mode, IGRAPH_NO_LOOPS)); if (VECTOR(degv)[0] < 1) { - *updates = 0; + *updates = false; } igraph_vector_int_destroy(°v); IGRAPH_FINALLY_CLEAN(1); @@ -585,7 +585,7 @@ igraph_error_t igraph_deterministic_optimal_imitation(const igraph_t *graph, IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, - /*is local?*/ 1)); + /*is local?*/ true)); if (!updates) { return IGRAPH_SUCCESS; /* Nothing to do */ } @@ -746,7 +746,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, /* don't test for vertex isolation, hence vid = -1 and islocal = 0 */ IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, /*vid*/ -1, quantities, strategies, mode, - &updates, /*is local?*/ 0)); + &updates, /*is local?*/ false)); if (!updates) { return IGRAPH_SUCCESS; /* nothing more to do */ } @@ -764,7 +764,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, /* Cumulative proportionate quantities. We are using the global */ /* perspective, hence islocal = 0, vid = -1 and mode = IGRAPH_ALL. */ IGRAPH_CHECK(igraph_i_vcumulative_proportionate_values(graph, quantities, &V, - /*is local?*/ 0, + /*is local?*/ false, /*vid*/ -1, /*mode*/ IGRAPH_ALL)); @@ -812,7 +812,7 @@ igraph_error_t igraph_moran_process(const igraph_t *graph, /* still might happen that the edge weights of interest would sum to zero. */ /* An error would be raised in that case. */ IGRAPH_CHECK(igraph_i_ecumulative_proportionate_values(graph, weights, &V, - /*is local?*/ 1, + /*is local?*/ true, /*vertex*/ a, mode)); /* Choose a vertex for death from among all vertices in a's perspective. */ @@ -1149,7 +1149,7 @@ igraph_error_t igraph_stochastic_imitation(const igraph_t *graph, } IGRAPH_CHECK(igraph_i_microscopic_standard_tests(graph, vid, quantities, strategies, mode, &updates, - /*is local?*/ 1)); + /*is local?*/ true)); if (!updates) { return IGRAPH_SUCCESS; /* nothing more to do */ } diff --git a/src/misc/mixing.c b/src/misc/mixing.c index ca71c4fb29..ef61a8d5ab 100644 --- a/src/misc/mixing.c +++ b/src/misc/mixing.c @@ -220,7 +220,7 @@ igraph_error_t igraph_assortativity_nominal(const igraph_t *graph, * * M. E. J. Newman: Assortative mixing in networks, * Phys. Rev. Lett. 89, 208701 (2002) - * http://doi.org/10.1103/PhysRevLett.89.208701. + * https://doi.org/10.1103/PhysRevLett.89.208701. * See equation (4) for performing the calculation in undirected * graphs with the degrees as values. * @@ -837,7 +837,7 @@ igraph_error_t igraph_joint_degree_distribution( case IGRAPH_IN: deg_from = °_in; break; case IGRAPH_ALL: deg_from = °_all; break; default: - IGRAPH_ERROR("Invalid 'from' degree mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid 'from' degree mode.", IGRAPH_EINVMODE); } switch (to_mode) { @@ -845,7 +845,7 @@ igraph_error_t igraph_joint_degree_distribution( case IGRAPH_IN: deg_to = °_in; break; case IGRAPH_ALL: deg_to = °_all; break; default: - IGRAPH_ERROR("Invalid 'to' degree mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid 'to' degree mode.", IGRAPH_EINVMODE); } IGRAPH_CHECK(mixing_matrix(graph, diff --git a/src/misc/motifs.c b/src/misc/motifs.c index 07aa0424b6..e1df14ffb2 100644 --- a/src/misc/motifs.c +++ b/src/misc/motifs.c @@ -986,29 +986,31 @@ igraph_error_t igraph_dyad_census(const igraph_t *graph, igraph_real_t *mut, return IGRAPH_SUCCESS; } -static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_real_t *res2, - igraph_real_t *res4) { +static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, + igraph_real_t *res2, + igraph_real_t *res4) { - igraph_integer_t vc = igraph_vcount(graph); + const igraph_integer_t vcount = igraph_vcount(graph); igraph_vector_int_t seen; igraph_vector_int_t *neis, *neis2; - igraph_integer_t i, j, k, s, neilen, neilen2, ign; + igraph_integer_t s, neilen, neilen2, ign; igraph_adjlist_t adjlist; + int iter = 0; - IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, vc); + IGRAPH_VECTOR_INT_INIT_FINALLY(&seen, vcount); IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE, IGRAPH_MULTIPLE)); IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); *res2 = *res4 = 0; - for (i = 0; i < vc; i++) { - IGRAPH_ALLOW_INTERRUPTION(); + for (igraph_integer_t i = 0; i < vcount; i++) { + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 12); neis = igraph_adjlist_get(&adjlist, i); neilen = igraph_vector_int_size(neis); /* mark neighbors of i & i itself */ VECTOR(seen)[i] = i + 1; ign = 0; - for (j = 0; j < neilen; j++) { + for (igraph_integer_t j = 0; j < neilen; j++) { igraph_integer_t nei = VECTOR(*neis)[j]; if (VECTOR(seen)[nei] == i + 1 || VECTOR(seen)[nei] == -(i + 1)) { /* multiple edges or loop edge */ @@ -1019,7 +1021,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea } } - for (j = 0; j < neilen; j++) { + for (igraph_integer_t j = 0; j < neilen; j++) { igraph_integer_t nei = VECTOR(*neis)[j]; if (nei <= i || (j > 0 && nei == VECTOR(*neis)[j - 1])) { continue; @@ -1027,7 +1029,7 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea neis2 = igraph_adjlist_get(&adjlist, nei); neilen2 = igraph_vector_int_size(neis2); s = 0; - for (k = 0; k < neilen2; k++) { + for (igraph_integer_t k = 0; k < neilen2; k++) { igraph_integer_t nei2 = VECTOR(*neis2)[k]; if (k > 0 && nei2 == VECTOR(*neis2)[k - 1]) { continue; @@ -1037,9 +1039,9 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea } } if (VECTOR(seen)[nei] > 0) { - *res2 += vc - s - neilen + ign - 1; + *res2 += vcount - s - neilen + ign - 1; } else { - *res4 += vc - s - neilen + ign - 1; + *res4 += vcount - s - neilen + ign - 1; } } } @@ -1130,10 +1132,9 @@ static igraph_error_t igraph_i_triad_census_24(const igraph_t *graph, igraph_rea igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) { - igraph_vector_t cut_prob; + const igraph_integer_t vcount = igraph_vcount(graph); + igraph_vector_t cut_prob, tmp; igraph_real_t m2, m4; - igraph_vector_t tmp; - igraph_integer_t vc = igraph_vcount(graph); igraph_real_t total; if (!igraph_is_directed(graph)) { @@ -1147,8 +1148,8 @@ igraph_error_t igraph_triad_census(const igraph_t *graph, igraph_vector_t *res) IGRAPH_CHECK(igraph_motifs_randesu(graph, &tmp, 3, &cut_prob)); IGRAPH_CHECK(igraph_i_triad_census_24(graph, &m2, &m4)); - total = ((igraph_real_t)vc) * (vc - 1); - total *= (vc - 2); + total = ((igraph_real_t) vcount) * (vcount - 1); + total *= (vcount - 2); total /= 6; /* Reorder */ diff --git a/src/misc/other.c b/src/misc/other.c index d6f5ca6ec9..8d22697687 100644 --- a/src/misc/other.c +++ b/src/misc/other.c @@ -362,7 +362,7 @@ igraph_error_t igraph_vertex_path_from_edge_path( break; default: - IGRAPH_ERROR("Invalid neighborhood mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid neighborhood mode.", IGRAPH_EINVMODE); } if (!next_edge_ok) { diff --git a/src/misc/spanning_trees.c b/src/misc/spanning_trees.c index 8da2848e3a..77413945a1 100644 --- a/src/misc/spanning_trees.c +++ b/src/misc/spanning_trees.c @@ -17,10 +17,10 @@ */ #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_components.h" #include "igraph_dqueue.h" #include "igraph_interface.h" -#include "igraph_memory.h" #include "igraph_operators.h" #include "igraph_random.h" #include "igraph_structural.h" @@ -88,6 +88,8 @@ igraph_error_t igraph_minimum_spanning_tree( * \function igraph_minimum_spanning_tree_unweighted * \brief Calculates one minimum spanning tree of an unweighted graph. * + * \deprecated-by igraph_minimum_spanning_tree 0.10.14 + * * 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. @@ -139,6 +141,8 @@ 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. * + * \deprecated-by igraph_minimum_spanning_tree 0.10.14 + * * 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. @@ -201,33 +205,27 @@ static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_edges = igraph_ecount(graph); - bool *already_added, *added_edges; + igraph_bitset_t already_added, added_edges; igraph_dqueue_int_t q; igraph_vector_int_t eids; igraph_vector_int_clear(res); - 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, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for unweighted spanning tree."); - IGRAPH_FINALLY(igraph_free, already_added); - + IGRAPH_BITSET_INIT_FINALLY(&added_edges, no_of_edges); + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); 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]) { + if (IGRAPH_BIT_TEST(already_added, i)) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[i] = true; + IGRAPH_BIT_SET(already_added, i); IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); while (! igraph_dqueue_int_empty(&q)) { igraph_integer_t eids_size; @@ -237,11 +235,11 @@ static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* 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]) { + if (! IGRAPH_BIT_TEST(added_edges, edge)) { igraph_integer_t to = IGRAPH_OTHER(graph, edge, act_node); - if (! already_added[to]) { - already_added[to] = true; - added_edges[edge] = true; + if (! IGRAPH_BIT_TEST(already_added, to)) { + IGRAPH_BIT_SET(already_added, to); + IGRAPH_BIT_SET(added_edges, edge); IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); IGRAPH_CHECK(igraph_dqueue_int_push(&q, to)); } @@ -252,8 +250,8 @@ static igraph_error_t igraph_i_minimum_spanning_tree_unweighted(const igraph_t* igraph_dqueue_int_destroy(&q); igraph_vector_int_destroy(&eids); - IGRAPH_FREE(already_added); - IGRAPH_FREE(added_edges); + igraph_bitset_destroy(&already_added); + igraph_bitset_destroy(&added_edges); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; @@ -264,7 +262,7 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_edges = igraph_ecount(graph); - bool *already_added, *added_edges; + igraph_bitset_t already_added, added_edges; igraph_d_indheap_t heap; const igraph_neimode_t mode = IGRAPH_ALL; @@ -285,13 +283,8 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( 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, bool); - IGRAPH_CHECK_OOM(already_added, "Insufficient memory for minimum spanning tree calculation."); - IGRAPH_FINALLY(igraph_free, already_added); + IGRAPH_BITSET_INIT_FINALLY(&added_edges, no_of_edges); + IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); IGRAPH_CHECK(igraph_d_indheap_init(&heap, 0)); IGRAPH_FINALLY(igraph_d_indheap_destroy, &heap); @@ -300,19 +293,19 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( for (igraph_integer_t i = 0; i < no_of_nodes; i++) { igraph_integer_t adj_size; - if (already_added[i]) { + if (IGRAPH_BIT_TEST(already_added, i)) { continue; } IGRAPH_ALLOW_INTERRUPTION(); - already_added[i] = true; + IGRAPH_BIT_SET(already_added, i); /* 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_integer_t neighbor = IGRAPH_OTHER(graph, edgeno, i); - if (! already_added[neighbor]) { + if (! IGRAPH_BIT_TEST(already_added, neighbor)) { IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], i, edgeno)); } } @@ -326,13 +319,13 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( igraph_d_indheap_delete_max(&heap); /* Is this edge already included? */ - if (! added_edges[edge]) { + if (! IGRAPH_BIT_TEST(added_edges, edge)) { 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; + if (! IGRAPH_BIT_TEST(already_added, to)) { + IGRAPH_BIT_SET(already_added, to); + IGRAPH_BIT_SET(added_edges, edge); IGRAPH_CHECK(igraph_vector_int_push_back(res, edge)); /* add all outgoing edges */ IGRAPH_CHECK(igraph_incident(graph, &adj, to, mode)); @@ -340,7 +333,7 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( 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]) { + if (! IGRAPH_BIT_TEST(already_added, neighbor)) { IGRAPH_CHECK(igraph_d_indheap_push(&heap, -VECTOR(*weights)[edgeno], to, edgeno)); } } @@ -349,10 +342,10 @@ static igraph_error_t igraph_i_minimum_spanning_tree_prim( } /* while in the same component */ } /* for all nodes */ - igraph_d_indheap_destroy(&heap); - IGRAPH_FREE(already_added); igraph_vector_int_destroy(&adj); - IGRAPH_FREE(added_edges); + igraph_d_indheap_destroy(&heap); + igraph_bitset_destroy(&already_added); + igraph_bitset_destroy(&added_edges); IGRAPH_FINALLY_CLEAN(4); return IGRAPH_SUCCESS; diff --git a/src/operators/contract.c b/src/operators/contract.c index 4413ac5022..bb965d52a4 100644 --- a/src/operators/contract.c +++ b/src/operators/contract.c @@ -56,6 +56,8 @@ * * Time complexity: O(|V|+|E|), linear in the number * or vertices plus edges. + * + * \example examples/simple/igraph_contract_vertices.c */ igraph_error_t igraph_contract_vertices(igraph_t *graph, diff --git a/src/operators/disjoint_union.c b/src/operators/disjoint_union.c index 76aedf7f2f..f6e31ab4cf 100644 --- a/src/operators/disjoint_union.c +++ b/src/operators/disjoint_union.c @@ -66,8 +66,9 @@ * * \example examples/simple/igraph_disjoint_union.c */ -igraph_error_t igraph_disjoint_union(igraph_t *res, const igraph_t *left, - const igraph_t *right) { +igraph_error_t igraph_disjoint_union(igraph_t *res, + const igraph_t *left, + const igraph_t *right) { const igraph_integer_t no_of_nodes_left = igraph_vcount(left); const igraph_integer_t no_of_nodes_right = igraph_vcount(right); @@ -148,7 +149,7 @@ igraph_error_t igraph_disjoint_union(igraph_t *res, const igraph_t *left, * of edges in the result. */ igraph_error_t igraph_disjoint_union_many(igraph_t *res, - const igraph_vector_ptr_t *graphs) { + 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; diff --git a/src/operators/intersection.c b/src/operators/intersection.c index 3ccd4b0c82..006e390d73 100644 --- a/src/operators/intersection.c +++ b/src/operators/intersection.c @@ -69,10 +69,10 @@ * * \example examples/simple/igraph_intersection.c */ -igraph_error_t 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_error_t 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) { return igraph_i_merge(res, IGRAPH_MERGE_MODE_INTERSECTION, left, right, edge_map1, edge_map2); } diff --git a/src/operators/join.c b/src/operators/join.c index 2dea612401..57bb2d9980 100644 --- a/src/operators/join.c +++ b/src/operators/join.c @@ -18,15 +18,16 @@ */ #include "igraph_operators.h" -#include "math/safe_intop.h" - -#include "igraph_constructors.h" #include "igraph_interface.h" +#include "math/safe_intop.h" + /** * \function igraph_join * \brief Creates the join of two disjoint graphs. * + * \experimental + * * First the vertices of the second graph will be relabeled with new * vertex IDs to have two disjoint sets of vertex IDs, then the union * of the two graphs will be formed. Finally, the vertces from the @@ -62,8 +63,9 @@ * Time complexity: O(|V1|*|V2|+|E1|+|E2|). * */ -igraph_error_t igraph_join(igraph_t *res, const igraph_t *left, - const igraph_t *right) { +igraph_error_t igraph_join(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); diff --git a/src/operators/misc_internal.h b/src/operators/misc_internal.h index fd519033b1..2dc7cc7b5a 100644 --- a/src/operators/misc_internal.h +++ b/src/operators/misc_internal.h @@ -37,7 +37,7 @@ typedef enum { IGRAPH_MERGE_MODE_INTERSECTION = 2 } igraph_i_merge_mode_t; -int igraph_i_order_edgelist_cmp(void *edges, const void *e1, const void *e2); +IGRAPH_FUNCATTR_PURE 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, const igraph_t *left, const igraph_t *right, igraph_vector_int_t *edge_map1, igraph_vector_int_t *edge_map2); diff --git a/src/operators/simplify.c b/src/operators/simplify.c index 2ae746b4ac..4412669a84 100644 --- a/src/operators/simplify.c +++ b/src/operators/simplify.c @@ -38,8 +38,8 @@ * 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 remove_multiple Logical, if true, multiple edges will be removed. + * \param remove_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 @@ -53,7 +53,7 @@ */ igraph_error_t igraph_simplify(igraph_t *graph, - igraph_bool_t multiple, igraph_bool_t loops, + igraph_bool_t remove_multiple, igraph_bool_t remove_loops, const igraph_attribute_combination_t *edge_comb) { igraph_vector_int_t edges; @@ -71,22 +71,22 @@ igraph_error_t igraph_simplify(igraph_t *graph, /* 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; + remove_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; + remove_loops = false; } - if (!multiple && !loops) + if (!remove_multiple && !remove_loops) /* nothing to do */ { return IGRAPH_SUCCESS; } - if (!multiple) { + if (!remove_multiple) { igraph_vector_int_t edges_to_delete; /* removing loop edges only, this is simple. No need to combine anything @@ -139,12 +139,12 @@ igraph_error_t igraph_simplify(igraph_t *graph, from = IGRAPH_FROM(graph, edge); to = IGRAPH_TO(graph, edge); - if (loops && from == to) { + if (remove_loops && from == to) { /* Loop edge to be removed */ if (attr) { VECTOR(mergeinto)[edge] = -1; } - } else if (multiple && from == pfrom && to == pto) { + } else if (remove_multiple && from == pfrom && to == pto) { /* Multiple edge to be contracted */ if (attr) { VECTOR(mergeinto)[edge] = actedge; @@ -194,13 +194,13 @@ igraph_error_t igraph_simplify(igraph_t *graph, /* 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) { + if (remove_loops) { /* Loop edges were removed so we know for sure that there aren't any * loop edges now */ igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); } - if (multiple) { + if (remove_multiple) { /* Multi-edges were removed so we know for sure that there aren't any * multi-edges now */ igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MULTI, false); diff --git a/src/operators/subgraph.c b/src/operators/subgraph.c index d20dfd9eb0..98853512e4 100644 --- a/src/operators/subgraph.c +++ b/src/operators/subgraph.c @@ -1,7 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2006-2021 The igraph development team + Copyright (C) 2006-2024 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,17 +13,14 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public 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_operators.h" +#include "igraph_bitset.h" #include "igraph_constructors.h" #include "igraph_interface.h" -#include "igraph_memory.h" #include "core/interruption.h" #include "core/set.h" @@ -40,21 +36,17 @@ 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); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_integer_t no_of_new_nodes_estimate; igraph_vector_int_t delete; - bool *remain; - igraph_integer_t i; + igraph_bitset_t remain; 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_FINALLY(igraph_free, remain); + IGRAPH_BITSET_INIT_FINALLY(&remain, no_of_nodes); /* Calculate how many nodes there will be in the new graph. The result is * a lower bound only as 'vit' may contain the same vertex more than once. */ @@ -66,18 +58,18 @@ static igraph_error_t igraph_i_induced_subgraph_copy_and_delete( IGRAPH_CHECK(igraph_vector_int_reserve(&delete, no_of_new_nodes_estimate)); for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { - remain[ IGRAPH_VIT_GET(vit) ] = true; + IGRAPH_BIT_SET(remain, IGRAPH_VIT_GET(vit)); } - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); - if (! remain[i]) { + if (! IGRAPH_BIT_TEST(remain, i)) { IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } - IGRAPH_FREE(remain); + igraph_bitset_destroy(&remain); IGRAPH_FINALLY_CLEAN(1); IGRAPH_CHECK(igraph_copy(res, graph)); @@ -107,10 +99,10 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( igraph_vector_int_t *map, igraph_vector_int_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); + const igraph_bool_t directed = igraph_is_directed(graph); + const 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 n; igraph_integer_t to; igraph_integer_t eid; igraph_vector_int_t vids_old2new, vids_new2old; @@ -161,7 +153,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( igraph_vector_int_sort(&vids_vec); n = igraph_vector_int_size(&vids_vec); - for (i = 0; i < n; i++) { + for (igraph_integer_t i = 0; i < n; i++) { igraph_integer_t vid = VECTOR(vids_vec)[i]; /* Cater for duplicate vertex IDs in the input vertex selector; we use @@ -176,7 +168,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( IGRAPH_FINALLY_CLEAN(1); /* Create the new edge list */ - for (i = 0; i < no_of_new_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_new_nodes; i++) { igraph_integer_t old_vid = VECTOR(*my_vids_new2old)[i]; igraph_integer_t new_vid = i; igraph_bool_t skip_loop_edge; @@ -186,7 +178,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( if (directed) { /* directed graph; this is easier */ - for (j = 0; j < n; j++) { + for (igraph_integer_t j = 0; j < n; j++) { eid = VECTOR(nei_edges)[j]; to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ]; @@ -203,7 +195,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * loop edge will appear twice. We use a boolean flag to skip every * second loop edge */ skip_loop_edge = 0; - for (j = 0; j < n; j++) { + for (igraph_integer_t j = 0; j < n; j++) { eid = VECTOR(nei_edges)[j]; if (IGRAPH_FROM(graph, eid) != old_vid) { @@ -254,7 +246,7 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( /* Copy the graph attributes */ IGRAPH_CHECK(igraph_i_attribute_copy(res, graph, - /* ga = */ 1, /* va = */ 0, /* ea = */ 0)); + /* ga = */ true, /* va = */ false, /* ea = */ false)); /* Copy the vertex attributes */ IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, my_vids_new2old)); @@ -278,11 +270,12 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * \function igraph_induced_subgraph * \brief Creates a subgraph induced by the specified vertices. * - * * This function collects the specified vertices and all edges between - * them to a new graph. - * As the vertex IDs in a graph always start with zero, this function - * very likely needs to reassign IDs to the vertices. + * them to a new graph. As vertex IDs are always contiguos integers starting + * at zero, the IDs in the created subgraph will be different from the IDs in + * the original graph. To get the mappings between them, use + * \ref igraph_induced_subgraph_map() + * * \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 @@ -320,13 +313,16 @@ static igraph_error_t igraph_i_induced_subgraph_create_from_scratch( * |E| are the number of vertices and * edges in the original graph. * - * \sa \ref igraph_delete_vertices() to delete the specified set of + * \sa \ref igraph_induced_subgraph_map() to also retrieve the vertex ID + * mapping between the graph and the extracted subgraph; + * \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, - const igraph_vs_t vids, igraph_subgraph_implementation_t impl) { - return igraph_induced_subgraph_map(graph, res, vids, impl, /* map= */ 0, - /* invmap= */ 0); + const igraph_vs_t vids, + igraph_subgraph_implementation_t impl) { + return igraph_induced_subgraph_map(graph, res, vids, impl, + /* map= */ NULL, /* invmap= */ NULL); } static igraph_error_t igraph_i_induced_subgraph_suggest_implementation( @@ -382,9 +378,10 @@ igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *re * \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. + * them to a new graph. As vertex IDs are always contiguos integers starting + * at zero, the IDs in the created subgraph will be different from the IDs in + * the original graph. The mapping between the vertex IDs in the graph and the + * extracted subgraphs are returned in \p map and \p invmap. * * \param graph The graph object. * \param res The subgraph, another graph object will be stored here, @@ -428,11 +425,13 @@ igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *re * 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_int_t *map, - igraph_vector_int_t *invmap) { - return igraph_i_induced_subgraph_map(graph, res,vids, impl, map, invmap, /* map_is_prepared = */ false); + 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); } /** @@ -522,9 +521,10 @@ igraph_error_t igraph_subgraph_edges( * \brief Creates a subgraph with the specified edges and their endpoints. * * This function collects the specified edges and their endpoints to a new - * graph. As the edge IDs in a graph always start with zero, this function - * very likely needs to reassign IDs to the edges. Vertex IDs may also be - * reassigned if \p delete_vertices is set to \c true . Attributes are preserved. + * graph. As the edge IDs in a graph are always contiguous integers starting at + * zero, the edge IDs in the extracted subgraph will be different from those + * in the original graph. Vertex IDs will also be reassigned if + * \p delete_vertices is set to \c true. Attributes are preserved. * * \param graph The graph object. * \param res The subgraph, another graph object will be stored here, @@ -556,21 +556,15 @@ igraph_error_t igraph_subgraph_from_edges( igraph_integer_t no_of_edges = igraph_ecount(graph); igraph_integer_t no_of_edges_to_delete_estimate; igraph_vector_int_t delete = IGRAPH_VECTOR_NULL; - bool *vremain, *eremain; - igraph_integer_t i; + igraph_bitset_t vremain, eremain; 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_FINALLY(igraph_free, vremain); + IGRAPH_BITSET_INIT_FINALLY(&vremain, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&eremain, no_of_edges); - 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_eit_create(graph, eids, &eit)); + IGRAPH_FINALLY(igraph_eit_destroy, &eit); /* Calculate how many edges there will be in the new graph. The result is * a lower bound only as 'eit' may contain the same edge more than once. */ @@ -585,18 +579,23 @@ igraph_error_t igraph_subgraph_from_edges( 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_BIT_SET(eremain, eid); + IGRAPH_BIT_SET(vremain, from); + IGRAPH_BIT_SET(vremain, to); } + igraph_eit_destroy(&eit); + IGRAPH_FINALLY_CLEAN(1); + /* Collect the edge IDs to be deleted */ - for (i = 0; i < no_of_edges; i++) { + for (igraph_integer_t i = 0; i < no_of_edges; i++) { IGRAPH_ALLOW_INTERRUPTION(); - if (! eremain[i]) { + if (! IGRAPH_BIT_TEST(eremain, i)) { IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } - IGRAPH_FREE(eremain); + igraph_bitset_destroy(&eremain); IGRAPH_FINALLY_CLEAN(1); /* Delete the unnecessary edges */ @@ -607,15 +606,15 @@ igraph_error_t igraph_subgraph_from_edges( if (delete_vertices) { /* Collect the vertex IDs to be deleted */ igraph_vector_int_clear(&delete); - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { IGRAPH_ALLOW_INTERRUPTION(); - if (! vremain[i]) { + if (! IGRAPH_BIT_TEST(vremain, i)) { IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i)); } } } - IGRAPH_FREE(vremain); + igraph_bitset_destroy(&vremain); IGRAPH_FINALLY_CLEAN(1); /* Delete the unnecessary vertices */ @@ -624,7 +623,7 @@ igraph_error_t igraph_subgraph_from_edges( } igraph_vector_int_destroy(&delete); - igraph_eit_destroy(&eit); - IGRAPH_FINALLY_CLEAN(3); + IGRAPH_FINALLY_CLEAN(2); + return IGRAPH_SUCCESS; } diff --git a/src/operators/union.c b/src/operators/union.c index aad95a7ed8..71876bdacc 100644 --- a/src/operators/union.c +++ b/src/operators/union.c @@ -69,9 +69,10 @@ * * \example examples/simple/igraph_union.c */ -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_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) { return igraph_i_merge(res, IGRAPH_MERGE_MODE_UNION, left, right, edge_map1, edge_map2); } diff --git a/src/paths/bellman_ford.c b/src/paths/bellman_ford.c index 0b02c0c3ea..692aeb97df 100644 --- a/src/paths/bellman_ford.c +++ b/src/paths/bellman_ford.c @@ -21,6 +21,7 @@ #include "igraph_paths.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_dqueue.h" #include "igraph_interface.h" #include "igraph_memory.h" @@ -78,7 +79,7 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, 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_bitset_t clean_vertices; igraph_vector_int_t num_queued; igraph_vit_t fromvit, tovit; igraph_bool_t all_to; @@ -102,7 +103,7 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); } - if (no_of_edges > 0 && igraph_vector_is_any_nan(weights)) { + if (igraph_vector_is_any_nan(weights)) { IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); } @@ -111,7 +112,7 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, 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_BITSET_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); @@ -140,7 +141,7 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, igraph_vector_fill(&dist, IGRAPH_INFINITY); VECTOR(dist)[source] = 0; - igraph_vector_bool_null(&clean_vertices); + igraph_bitset_null(&clean_vertices); igraph_vector_int_null(&num_queued); /* Fill the queue with vertices to be checked */ @@ -155,7 +156,7 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, } igraph_integer_t j = igraph_dqueue_int_pop(&Q); - VECTOR(clean_vertices)[j] = true; + IGRAPH_BIT_SET(clean_vertices, j); 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.", @@ -179,8 +180,8 @@ igraph_error_t igraph_distances_bellman_ford(const igraph_t *graph, if (VECTOR(dist)[target] > altdist) { /* relax the edge */ VECTOR(dist)[target] = altdist; - if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = false; + if (IGRAPH_BIT_TEST(clean_vertices, target)) { + IGRAPH_BIT_CLEAR(clean_vertices, target); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); } } @@ -210,7 +211,7 @@ 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_bitset_destroy(&clean_vertices); igraph_vector_int_destroy(&num_queued); igraph_lazy_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(5); @@ -321,7 +322,7 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, igraph_lazy_inclist_t inclist; igraph_integer_t i, j, k; igraph_dqueue_int_t Q; - igraph_vector_bool_t clean_vertices; + igraph_bitset_t clean_vertices; igraph_vector_int_t num_queued; igraph_vit_t tovit; igraph_vector_t dist; @@ -341,7 +342,7 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, } IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, no_of_nodes); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&clean_vertices, no_of_nodes); + IGRAPH_BITSET_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); @@ -377,7 +378,7 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, } j = igraph_dqueue_int_pop(&Q); - VECTOR(clean_vertices)[j] = true; + IGRAPH_BIT_SET(clean_vertices, j); 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.", @@ -396,7 +397,12 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, 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]; + igraph_real_t weight = VECTOR(*weights)[nei]; + igraph_real_t altdist = VECTOR(dist)[j] + weight; + + if (isnan(weight)) { + IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); + } /* infinite weights are handled correctly here; if an edge has * infinite weight, altdist will also be infinite so the condition @@ -406,8 +412,8 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, /* relax the edge */ VECTOR(dist)[target] = altdist; parent_eids[target] = nei + 1; - if (VECTOR(clean_vertices)[target]) { - VECTOR(clean_vertices)[target] = false; + if (IGRAPH_BIT_TEST(clean_vertices, target)) { + IGRAPH_BIT_CLEAR(clean_vertices, target); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, target)); } } @@ -501,7 +507,7 @@ igraph_error_t igraph_get_shortest_paths_bellman_ford(const igraph_t *graph, IGRAPH_FREE(parent_eids); igraph_dqueue_int_destroy(&Q); - igraph_vector_bool_destroy(&clean_vertices); + igraph_bitset_destroy(&clean_vertices); igraph_vector_int_destroy(&num_queued); igraph_lazy_inclist_destroy(&inclist); IGRAPH_FINALLY_CLEAN(5); diff --git a/src/paths/eulerian.c b/src/paths/eulerian.c index 54979962cb..11cf63fd76 100644 --- a/src/paths/eulerian.c +++ b/src/paths/eulerian.c @@ -23,6 +23,7 @@ #include "igraph_eulerian.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_interface.h" #include "igraph_components.h" #include "igraph_stack.h" @@ -351,7 +352,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( igraph_integer_t n, m; igraph_inclist_t il; igraph_stack_int_t path, tracker, edge_tracker, edge_path; - igraph_vector_bool_t visited_list; + igraph_bitset_t visited_list; igraph_vector_int_t degree; n = igraph_vcount(graph); @@ -376,7 +377,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( 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_BITSET_INIT_FINALLY(&visited_list, m); IGRAPH_CHECK(igraph_stack_int_push(&tracker, start_of_path)); @@ -399,7 +400,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( for (j = 0; j < nc; j++) { edge = VECTOR(*incedges)[j]; - if (!VECTOR(visited_list)[edge]) { + if (!IGRAPH_BIT_TEST(visited_list, edge)) { break; } } @@ -411,7 +412,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( /* remove edge here */ VECTOR(degree)[curr]--; VECTOR(degree)[next]--; - VECTOR(visited_list)[edge] = 1; + IGRAPH_BIT_SET(visited_list, edge); curr = next; } else { /* back track to find remaining circuit */ @@ -442,7 +443,7 @@ static igraph_error_t igraph_i_eulerian_path_undirected( igraph_stack_int_destroy(&tracker); igraph_stack_int_destroy(&edge_path); igraph_stack_int_destroy(&edge_tracker); - igraph_vector_bool_destroy(&visited_list); + igraph_bitset_destroy(&visited_list); igraph_inclist_destroy(&il); igraph_vector_int_destroy(°ree); IGRAPH_FINALLY_CLEAN(7); @@ -459,7 +460,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( igraph_integer_t n, m; igraph_inclist_t il; igraph_stack_int_t path, tracker, edge_tracker, edge_path; - igraph_vector_bool_t visited_list; + igraph_bitset_t visited_list; igraph_vector_int_t remaining_out_edges; n = igraph_vcount(graph); @@ -481,7 +482,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( 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_BITSET_INIT_FINALLY(&visited_list, m); IGRAPH_CHECK(igraph_stack_int_push(&tracker, start_of_path)); @@ -507,7 +508,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( for (j = 0; j < nc; j++) { edge = VECTOR(*incedges)[j]; - if (!VECTOR(visited_list)[edge]) { + if (!IGRAPH_BIT_TEST(visited_list, edge)) { break; } } @@ -518,7 +519,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( /* remove edge here */ VECTOR(remaining_out_edges)[curr]--; - VECTOR(visited_list)[edge] = 1; + IGRAPH_BIT_SET(visited_list, edge); curr = next; } else { /* back track to find remaining circuit */ @@ -549,7 +550,7 @@ static igraph_error_t igraph_i_eulerian_path_directed( igraph_stack_int_destroy(&tracker); igraph_stack_int_destroy(&edge_path); igraph_stack_int_destroy(&edge_tracker); - igraph_vector_bool_destroy(&visited_list); + igraph_bitset_destroy(&visited_list); igraph_inclist_destroy(&il); igraph_vector_int_destroy(&remaining_out_edges); IGRAPH_FINALLY_CLEAN(7); @@ -577,7 +578,8 @@ static igraph_error_t igraph_i_eulerian_path_directed( * belonging to the cycle will be stored here. May be \c NULL * if it is not needed by the caller. * \param vertex_res Pointer to an initialised vector. The indices of vertices - * belonging to the cycle will be stored here. May be \c NULL + * belonging to the cycle will be stored here. The first and + * last vertex in the vector will be the same. May be \c NULL * if it is not needed by the caller. * \return Error code: * \clist diff --git a/src/paths/floyd_warshall.c b/src/paths/floyd_warshall.c index 89edaa727c..18502e4308 100644 --- a/src/paths/floyd_warshall.c +++ b/src/paths/floyd_warshall.c @@ -248,7 +248,7 @@ static igraph_error_t distances_floyd_warshall_tree(igraph_matrix_t *res) { * \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; + * tries 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. @@ -297,7 +297,7 @@ igraph_error_t igraph_distances_floyd_warshall( in = true; break; default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid mode for Floyd-Warshall shortest path calculation.", IGRAPH_EINVMODE); } if (weights && igraph_vector_is_any_nan(weights)) { diff --git a/src/paths/shortest_paths.c b/src/paths/shortest_paths.c index 381e3019d2..0a870885fa 100644 --- a/src/paths/shortest_paths.c +++ b/src/paths/shortest_paths.c @@ -459,7 +459,7 @@ static igraph_error_t igraph_i_local_efficiency_unweighted( IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); vertex_neis_size = igraph_vector_int_size(vertex_neis); - igraph_vector_char_fill(nei_mask, 0); + igraph_vector_char_null(nei_mask); neighbor_count = 0; for (i=0; i < vertex_neis_size; ++i) { igraph_integer_t v = VECTOR(*vertex_neis)[i]; @@ -563,7 +563,7 @@ static igraph_error_t igraph_i_local_efficiency_dijkstra( IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode)); vertex_neis_size = igraph_vector_int_size(vertex_neis); - igraph_vector_char_fill(nei_mask, 0); + igraph_vector_char_null(nei_mask); neighbor_count = 0; for (i=0; i < vertex_neis_size; ++i) { igraph_integer_t v = VECTOR(*vertex_neis)[i]; diff --git a/src/paths/sparsifier.c b/src/paths/sparsifier.c index a9bf599723..b8b9b98051 100644 --- a/src/paths/sparsifier.c +++ b/src/paths/sparsifier.c @@ -20,6 +20,7 @@ #include "igraph_paths.h" #include "igraph_adjlist.h" +#include "igraph_bitset.h" #include "igraph_error.h" #include "igraph_interface.h" #include "igraph_random.h" @@ -53,7 +54,7 @@ static igraph_error_t igraph_i_collect_lightest_edges_to_clusters( const igraph_inclist_t *inclist, const igraph_vector_t *weights, const igraph_vector_int_t *clustering, - const igraph_vector_bool_t *is_cluster_sampled, + const igraph_bitset_t *is_cluster_sampled, igraph_integer_t v, igraph_vector_int_t *lightest_eid, igraph_vector_t *lightest_weight, @@ -88,7 +89,7 @@ static igraph_error_t igraph_i_collect_lightest_edges_to_clusters( // 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)) { + if ((IGRAPH_BIT_TEST(*is_cluster_sampled, neighbor_cluster)) && (lightest_weight_to_sampled > weight)) { lightest_weight_to_sampled = weight; *nearest_neighboring_sampled_cluster = neighbor_cluster; } @@ -162,8 +163,8 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne 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_bitset_t is_cluster_sampled; + igraph_bitset_t is_edge_in_spanner; igraph_vector_int_t new_clustering; igraph_vector_int_t dirty_vids; igraph_vector_int_t *adjacent_vertices; @@ -235,8 +236,8 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne // 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); + IGRAPH_BITSET_INIT_FINALLY(&is_cluster_sampled, no_of_nodes); + IGRAPH_BITSET_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 @@ -245,8 +246,8 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne 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; \ + if (!IGRAPH_BIT_TEST(is_edge_in_spanner, edge)) { \ + IGRAPH_BIT_SET(is_edge_in_spanner, edge); \ IGRAPH_CHECK(igraph_vector_int_push_back(spanner, edge)); \ } @@ -256,13 +257,13 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne IGRAPH_ALLOW_INTERRUPTION(); igraph_vector_int_fill(&new_clustering, -1); - igraph_vector_bool_fill(&is_cluster_sampled, false); + igraph_bitset_null(&is_cluster_sampled); // 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; + IGRAPH_BIT_SET(is_cluster_sampled, j); } } RNG_END(); @@ -271,7 +272,7 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne 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]) { + if (cluster != -1 && IGRAPH_BIT_TEST(is_cluster_sampled, cluster)) { VECTOR(new_clustering)[v] = cluster; continue; } @@ -447,8 +448,8 @@ igraph_error_t igraph_spanner(const igraph_t *graph, igraph_vector_int_t *spanne // Free memory igraph_vector_int_destroy(&dirty_vids); - igraph_vector_bool_destroy(&is_edge_in_spanner); - igraph_vector_bool_destroy(&is_cluster_sampled); + igraph_bitset_destroy(&is_edge_in_spanner); + igraph_bitset_destroy(&is_cluster_sampled); igraph_vector_int_destroy(&new_clustering); igraph_vector_destroy(&lightest_weight); igraph_vector_int_destroy(&lightest_eid); diff --git a/src/paths/widest_paths.c b/src/paths/widest_paths.c index c655ac671a..faf6d5d381 100644 --- a/src/paths/widest_paths.c +++ b/src/paths/widest_paths.c @@ -492,7 +492,7 @@ igraph_error_t igraph_widest_path_widths_floyd_warshall(const igraph_t *graph, in = true; break; default: - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid mode for Floyd-Warshall shortest path calculation.", IGRAPH_EINVMODE); } /* Fill out adjacency matrix */ diff --git a/src/properties/basic_properties.c b/src/properties/basic_properties.c index 409dac12ea..7b69cba855 100644 --- a/src/properties/basic_properties.c +++ b/src/properties/basic_properties.c @@ -49,9 +49,8 @@ * on such graphs. This function does not check whether the graph has * parallel edges. The result it returns for such graphs is not meaningful. * - * \param graph The input graph object. - * \param res Pointer to a real number, the result will be stored - * here. It must not have parallel edges. + * \param graph The input graph object. It must not have parallel edges. + * \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 * loop edges are thought to be possible in the graph (this does not @@ -93,6 +92,48 @@ igraph_error_t igraph_density(const igraph_t *graph, igraph_real_t *res, return IGRAPH_SUCCESS; } +/** + * \function igraph_mean_degree + * \brief The mean degree of a graph. + * + * \experimental + * + * This is a convenience function that computes the average of all vertex + * degrees. In directed graphs, the average of out-degrees and in-degrees is + * the same; this is the number that is returned. For the null graph, which + * has no vertices, NaN is returned. + * + * \param graph The input graph object. + * \param res Pointer to a real number, the result will be stored here. + * \param loops Whether to consider self-loops during the calculation. + * \return Error code. + * + * Time complexity: O(1) if self-loops are considered, + * O(|E|) where |E| is the number of edges if self-loops are ignored. + */ +igraph_error_t igraph_mean_degree(const igraph_t *graph, igraph_real_t *res, + igraph_bool_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); + + if (no_of_nodes == 0) { + *res = IGRAPH_NAN; + return IGRAPH_SUCCESS; + } + + if (! loops) { + igraph_integer_t loop_count; + IGRAPH_CHECK(igraph_count_loops(graph, &loop_count)); + no_of_edges -= loop_count; + } + + *res = (directed ? 1.0 : 2.0) * (igraph_real_t) no_of_edges / (igraph_real_t) no_of_nodes; + + return IGRAPH_SUCCESS; +} + /** * \function igraph_diversity * \brief Structural diversity index of the vertices. @@ -220,34 +261,33 @@ igraph_error_t igraph_diversity(const igraph_t *graph, const igraph_vector_t *we * \function igraph_reciprocity * \brief Calculates the reciprocity of a directed graph. * - * The measure of reciprocity defines the proportion of mutual - * connections, in a directed graph. It is most commonly defined as - * the probability that the opposite counterpart of a randomly chosen - * directed edge is also included in the graph. In adjacency matrix - * notation: 1 - (sum_ij |A_ij - A_ji|) / (2 sum_ij A_ij). - * In multigraphs, each parallel edges between two vertices must - * have its own separate reciprocal edge, in accordance with the - * above formula. This measure is calculated if the \p mode argument is - * \c IGRAPH_RECIPROCITY_DEFAULT. + * In a directed graph, the measure of reciprocity defines the proportion of + * mutual connections. It is most commonly defined as the probability that the + * opposite counterpart of a randomly chosen directed edge is also included in + * the graph. In adjacency matrix notation: + * 1 - (sum_ij |A_ij - A_ji|) / (2 sum_ij A_ij). + * In multigraphs, each parallel edge between two vertices must have its own + * separate reciprocal edge, in accordance with the above formula. This measure + * is calculated if the \p mode argument is \c IGRAPH_RECIPROCITY_DEFAULT. * * * For directed graphs with no edges, NaN is returned. * For undirected graphs, 1 is returned unconditionally. * * - * Prior to igraph version 0.6, another measure was implemented, - * defined as the probability of mutual connection between a vertex - * pair if we know that there is a (possibly non-mutual) connection - * between them. In other words, (unordered) vertex pairs are - * classified into three groups: (1) disconnected, (2) - * non-reciprocally connected, (3) reciprocally connected. + * Prior to igraph version 0.6, another measure was implemented, defined as the + * probability of having mutual connections between a vertex pair if we know + * that there is a (possibly non-mutual) connection between them. In other + * words, (unordered) vertex pairs are classified into three groups: + * (1) disconnected, (2) non-reciprocally connected, (3) reciprocally connected. * The result is the size of group (3), divided by the sum of group - * sizes (2)+(3). This measure is calculated if \p mode is \c - * IGRAPH_RECIPROCITY_RATIO. + * sizes (2)+(3). This measure is calculated if \p mode is + * \c IGRAPH_RECIPROCITY_RATIO. * * \param graph The graph object. * \param res Pointer to an \c igraph_real_t which will contain the result. * \param ignore_loops Whether to ignore self-loops when counting edges. + * Self-loops are considered as a mutual connection. * \param mode Type of reciprocity to calculate, possible values are * \c IGRAPH_RECIPROCITY_DEFAULT and \c IGRAPH_RECIPROCITY_RATIO, * please see their description above. @@ -274,7 +314,7 @@ igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, IGRAPH_ERROR("Invalid reciprocity type.", IGRAPH_EINVAL); } - /* Undirected graphs has reciprocity 1.0 by definition. */ + /* Undirected graphs have reciprocity 1.0 by definition. */ if (!igraph_is_directed(graph)) { *res = 1.0; return IGRAPH_SUCCESS; @@ -284,13 +324,15 @@ igraph_error_t igraph_reciprocity(const igraph_t *graph, igraph_real_t *res, IGRAPH_VECTOR_INT_INIT_FINALLY(&outneis, 0); for (igraph_integer_t i = 0; i < no_of_nodes; i++) { - igraph_integer_t ip, op; + igraph_integer_t ip, op, indeg, outdeg; IGRAPH_CHECK(igraph_neighbors(graph, &inneis, i, IGRAPH_IN)); IGRAPH_CHECK(igraph_neighbors(graph, &outneis, i, IGRAPH_OUT)); + indeg = igraph_vector_int_size(&inneis); + outdeg = igraph_vector_int_size(&outneis); + ip = op = 0; - while (ip < igraph_vector_int_size(&inneis) && - op < igraph_vector_int_size(&outneis)) { + while (ip < indeg && op < outdeg) { if (VECTOR(inneis)[ip] < VECTOR(outneis)[op]) { nonrec += 1; ip++; @@ -313,8 +355,12 @@ 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 += (indeg - ip) + (outdeg - op); + } + + /* If we found non-loop mutual connections, we can set the cache. */ + if (rec - (ignore_loops ? 0 : loops) > 0) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_MUTUAL, true); } if (mode == IGRAPH_RECIPROCITY_DEFAULT) { diff --git a/src/properties/complete.c b/src/properties/complete.c index 45d5ee80ba..efad9e32ed 100644 --- a/src/properties/complete.c +++ b/src/properties/complete.c @@ -19,6 +19,7 @@ #include "igraph_interface.h" #include "igraph_structural.h" +#include "core/interruption.h" #include "graph/internal.h" /** @@ -42,13 +43,14 @@ * Time complexity: O(|V| + |E|) at worst. */ -igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) -{ +igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) { + const igraph_integer_t vcount = igraph_vcount(graph); const igraph_integer_t ecount = igraph_ecount(graph); igraph_integer_t complete_ecount; igraph_bool_t simple, directed = igraph_is_directed(graph); igraph_vector_int_t neighbours; + int iter = 0; /* If the graph is the null graph or the singleton graph, return early */ if (vcount == 0 || vcount == 1) { @@ -129,8 +131,7 @@ igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbours, vcount); for (igraph_integer_t i = 0; i < vcount; ++i) { - - igraph_vector_int_clear(&neighbours); + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); IGRAPH_CHECK(igraph_i_neighbors(graph, &neighbours, i, IGRAPH_OUT, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); @@ -153,3 +154,126 @@ igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) return IGRAPH_SUCCESS; } + + +/* Test for cliques or independent sets, depending on whether independent_set == true. */ +static igraph_error_t is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res, + igraph_bool_t independent_set) { + igraph_vector_int_t vids; + igraph_integer_t n; /* clique size */ + igraph_bool_t result = true; /* be optimistic */ + int iter = 0; + + /* The following implementation is optimized for testing for small cliques + * in large graphs. */ + + IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); + IGRAPH_CHECK(igraph_vs_as_vector(graph, candidate, &vids)); + + n = igraph_vector_int_size(&vids); + + for (igraph_integer_t i = 0; i < n; i++) { + igraph_integer_t u = VECTOR(vids)[i]; + for (igraph_integer_t j = directed ? 0 : i+1; j < n; j++) { + igraph_integer_t v = VECTOR(vids)[j]; + /* Compare u and v for equality instead of i and j in case + * the vertex list contained duplicates. */ + if (u != v) { + igraph_integer_t eid; + IGRAPH_CHECK(igraph_get_eid(graph, &eid, u, v, directed, false)); + if (independent_set) { + if (eid != -1) { + result = false; + goto done; + } + } else { + if (eid == -1) { + result = false; + goto done; + } + } + } + } + IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); + } + +done: + + *res = result; + + igraph_vector_int_destroy(&vids); + IGRAPH_FINALLY_CLEAN(1); + + return IGRAPH_SUCCESS; +} + +/** + * \ingroup structural + * \function igraph_is_clique + * \brief Does a set of vertices form a clique? + * + * \experimental + * + * Tests if all pairs within a set of vertices are adjacent, i.e. whether they + * form a clique. An empty set and singleton set are considered to be a clique. + * + * \param graph The input graph. + * \param candidate The vertex set to test for being a clique. + * \param directed Whether to take edge directions into account in directed graphs. + * \param res The result will be stored here. + * \return Error code. + * + * \sa \ref igraph_is_complete(), \ref igraph_is_independent_vertex_set() + * + * Time complexity: O(n^2 log(d)) where n is the number of vertices in the + * candidate set and d is the typical vertex degree. + */ +igraph_error_t igraph_is_clique(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t directed, igraph_bool_t *res) { + + if (! igraph_is_directed(graph)) { + directed = false; + } + + if (igraph_is_directed(graph) == directed && igraph_vs_is_all(&candidate)) { + return igraph_is_complete(graph, res); + } + + return is_clique(graph, candidate, directed, res, /* independent_set */ false); +} + +/** + * \ingroup structural + * \function igraph_is_independent_vertex_set + * \brief Does a set of vertices form an independent set? + * + * \experimental + * + * Tests if no pairs within a set of vertices are adjacenct, i.e. whether they + * form a an independent set. An empty set and singleton set are both considered + * to be an independent set. + * + * \param graph The input graph. + * \param candidate The vertex set to test for being an independent set. + * \param res The result will be stored here. + * \return Error code. + * + * \sa \ref igraph_is_clique() + * + * Time complexity: O(n^2 log(d)) where n is the number of vertices in the + * candidate set and d is the typical vertex degree. + */ +igraph_error_t igraph_is_independent_vertex_set(const igraph_t *graph, igraph_vs_t candidate, + igraph_bool_t *res) { + + /* Note: igraph_count_loops() already makes use of the cache. */ + if (igraph_vs_is_all(&candidate)) { + igraph_integer_t loop_count; + igraph_count_loops(graph, &loop_count); + *res = (igraph_ecount(graph) - loop_count) == 0; + return IGRAPH_SUCCESS; + } + + return is_clique(graph, candidate, /* directed */ false, res, /* independent_set */ true); +} diff --git a/src/properties/dag.c b/src/properties/dag.c index 1f49789f35..afad5efbd6 100644 --- a/src/properties/dag.c +++ b/src/properties/dag.c @@ -80,7 +80,7 @@ igraph_error_t igraph_topological_sorting( } else if (mode == IGRAPH_IN) { deg_mode = IGRAPH_OUT; } else { - IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid mode for topological sorting.", IGRAPH_EINVMODE); } IGRAPH_VECTOR_INT_INIT_FINALLY(°rees, no_of_nodes); diff --git a/src/properties/degrees.c b/src/properties/degrees.c index 200134c1c3..5131fd8be3 100644 --- a/src/properties/degrees.c +++ b/src/properties/degrees.c @@ -476,7 +476,7 @@ igraph_error_t igraph_degree_correlation_vector( case IGRAPH_IN: deg_from = °_in; break; case IGRAPH_ALL: deg_from = °_all; break; default: - IGRAPH_ERROR("Invalid 'from' mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid 'from' mode.", IGRAPH_EINVMODE); } switch (to_mode) { @@ -484,7 +484,7 @@ igraph_error_t igraph_degree_correlation_vector( case IGRAPH_IN: deg_to = °_in; break; case IGRAPH_ALL: deg_to = °_all; break; default: - IGRAPH_ERROR("Invalid 'to' mode.", IGRAPH_EINVAL); + IGRAPH_ERROR("Invalid 'to' mode.", IGRAPH_EINVMODE); } maxdeg = no_of_edges > 0 ? igraph_vector_int_max(deg_from) : 0; @@ -536,6 +536,55 @@ igraph_error_t igraph_degree_correlation_vector( return IGRAPH_SUCCESS; } +igraph_error_t igraph_i_strength_all( + const igraph_t *graph, igraph_vector_t *res, + igraph_neimode_t mode, igraph_bool_t loops, + const igraph_vector_t *weights) { + + // When calculating strength for all vertices, iterating over edges is faster + igraph_integer_t no_of_nodes = igraph_vcount(graph); + igraph_integer_t no_of_edges = igraph_ecount(graph); + + IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes)); + igraph_vector_null(res); + + if (!igraph_is_directed(graph)) { + mode = IGRAPH_ALL; + } + + if (loops) { + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + VECTOR(*res)[IGRAPH_FROM(graph, edge)] += VECTOR(*weights)[edge]; + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + VECTOR(*res)[IGRAPH_TO(graph, edge)] += VECTOR(*weights)[edge]; + } + } + } else { + if (mode & IGRAPH_OUT) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t from = IGRAPH_FROM(graph, edge); + if (from != IGRAPH_TO(graph, edge)) { + VECTOR(*res)[from] += VECTOR(*weights)[edge]; + } + } + } + if (mode & IGRAPH_IN) { + for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) { + igraph_integer_t to = IGRAPH_TO(graph, edge); + if (IGRAPH_FROM(graph, edge) != to) { + VECTOR(*res)[to] += VECTOR(*weights)[edge]; + } + } + } + } + + return IGRAPH_SUCCESS; +} + /** * \function igraph_strength * \brief Strength of the vertices, also called weighted vertex degree. @@ -550,6 +599,7 @@ igraph_error_t igraph_degree_correlation_vector( * \param vids The vertices for which the calculation is performed. * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT), * incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL). + * This parameter is ignored for undirected graphs. * \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 * pointer, then \ref igraph_degree() is called to perform the @@ -570,13 +620,12 @@ igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, igraph_integer_t no_vids; igraph_vector_int_t degrees; igraph_vector_int_t neis; - igraph_integer_t i; - if (!weights) { + 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++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { VECTOR(*res)[i] = VECTOR(degrees)[i]; } igraph_vector_int_destroy(°rees); @@ -584,11 +633,18 @@ igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, return IGRAPH_SUCCESS; } - if (igraph_vector_size(weights) != igraph_ecount(graph)) { IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL); } + if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) { + IGRAPH_ERROR("Invalid mode for vertex strength calculation.", IGRAPH_EINVMODE); + } + + if (igraph_vs_is_all(&vids)) { + return igraph_i_strength_all(graph, res, mode, loops, weights); + } + IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); IGRAPH_FINALLY(igraph_vit_destroy, &vit); no_vids = IGRAPH_VIT_SIZE(vit); @@ -599,23 +655,19 @@ igraph_error_t igraph_strength(const igraph_t *graph, igraph_vector_t *res, 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); - for (j = 0; j < n; j++) { + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode)); + const igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t j = 0; j < n; 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++) { - 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++) { + for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) { + IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode)); + const igraph_integer_t n = igraph_vector_int_size(&neis); + for (igraph_integer_t 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); diff --git a/src/properties/ecc.c b/src/properties/ecc.c index 4c95fa81d0..5701596f0c 100644 --- a/src/properties/ecc.c +++ b/src/properties/ecc.c @@ -24,34 +24,10 @@ #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. +/* This implementation of ECC relies on (lazy_)adjlist_get() producing sorted + * neighbor lists and (lazy_)adjlist_init() being called with IGRAPH_NO_LOOPS + * and 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( @@ -95,7 +71,7 @@ static igraph_error_t igraph_i_ecc3_1( 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); + z = igraph_vector_int_intersection_size_sorted(a1, a2); s = (d1 < d2 ? d1 : d2) - 1.0; } @@ -153,7 +129,7 @@ static igraph_error_t igraph_i_ecc3_2( 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); + z = igraph_vector_int_intersection_size_sorted(a1, a2); s = (d1 < d2 ? d1 : d2) - 1.0; } @@ -226,7 +202,7 @@ static igraph_error_t igraph_i_ecc4_1( 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; + z += igraph_vector_int_intersection_size_sorted(a2, a3) - 1.0; } igraph_integer_t d1 = VECTOR(degree)[v1], d2 = VECTOR(degree)[v2]; @@ -309,7 +285,7 @@ static igraph_error_t igraph_i_ecc4_2( 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; + z += igraph_vector_int_intersection_size_sorted(a2, a3) - 1.0; } s = (d1 - 1.0) * (d2 - 1.0); diff --git a/src/properties/loops.c b/src/properties/loops.c index f98876f88c..02eb59033f 100644 --- a/src/properties/loops.c +++ b/src/properties/loops.c @@ -44,7 +44,7 @@ * * Time complexity: O(e), the number of edges to check. * - * \example examples/simple/igraph_has_loop.c + * \example examples/simple/igraph_is_loop.c */ igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { igraph_integer_t i, m = igraph_ecount(graph); @@ -69,8 +69,8 @@ igraph_error_t igraph_has_loop(const igraph_t *graph, igraph_bool_t *res) { * \function igraph_is_loop * \brief Find the loop edges in a graph. * - * - * A loop edge is an edge from a vertex to itself. + * A loop edge, also called a self-loop, is an edge from a vertex to itself. + * * \param graph The input graph. * \param res Pointer to an initialized boolean vector for storing the result, * it will be resized as needed. @@ -84,22 +84,81 @@ 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, - igraph_es_t es) { + igraph_es_t es) { igraph_eit_t eit; - igraph_integer_t i; + igraph_bool_t found_loop = false; IGRAPH_CHECK(igraph_eit_create(graph, es, &eit)); IGRAPH_FINALLY(igraph_eit_destroy, &eit); IGRAPH_CHECK(igraph_vector_bool_resize(res, IGRAPH_EIT_SIZE(eit))); - for (i = 0; !IGRAPH_EIT_END(eit); i++, IGRAPH_EIT_NEXT(eit)) { + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + ! igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + igraph_vector_bool_null(res); + goto done; + } + + for (igraph_integer_t 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)); + igraph_bool_t is_loop = (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)); + VECTOR(*res)[i] = is_loop; + if (is_loop) { + found_loop = true; + } } + if (found_loop) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, true); + } else if (igraph_es_is_all(&es)) { + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, false); + } + +done: igraph_eit_destroy(&eit); IGRAPH_FINALLY_CLEAN(1); return IGRAPH_SUCCESS; } + +/** + * \function igraph_count_loops + * \brief Counts the self-loops in the graph. + * + * \experimental + * + * Counts loop edges, i.e. edges whose two endpoints coincide. + * + * \param graph The input graph. + * \param res Pointer to an integer, the number of self-loops will be stored here. + * \return Error code. + * + * Time complexity: O(|E|), linear in the number of edges. + * + * \example examples/simple/igraph_is_loop.c + */ +igraph_error_t igraph_count_loops(const igraph_t *graph, igraph_integer_t *loop_count) { + igraph_integer_t no_of_edges = igraph_ecount(graph); + igraph_integer_t count; + + /* Nothing to do if we know that there are no loops. */ + if (igraph_i_property_cache_has(graph, IGRAPH_PROP_HAS_LOOP) && + !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_HAS_LOOP)) { + *loop_count = 0; + return IGRAPH_SUCCESS; + } + + count = 0; + for (igraph_integer_t e=0; e < no_of_edges; e++) { + if (IGRAPH_FROM(graph, e) == IGRAPH_TO(graph, e)) { + count++; + } + } + + /* We already checked for loops, so take the opportunity to set the cache. */ + igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_HAS_LOOP, count > 0); + + *loop_count = count; + + return IGRAPH_SUCCESS; +} diff --git a/src/properties/multiplicity.c b/src/properties/multiplicity.c index e8de58adb2..6f24337110 100644 --- a/src/properties/multiplicity.c +++ b/src/properties/multiplicity.c @@ -101,6 +101,10 @@ igraph_error_t igraph_is_simple(const igraph_t *graph, igraph_bool_t *res) { if (VECTOR(neis)[j] == i) { known_loop = true; has_loop = true; break; } + /* Attention: If the graph is undirected, self-loops appears + * twice in the neighbour list. This does not mean that there + * are multi-edges. We do not need to worry about this as loop + * are already caught above. */ if (j > 0 && VECTOR(neis)[j - 1] == VECTOR(neis)[j]) { known_multi = true; has_multi = true; break; } @@ -213,7 +217,8 @@ igraph_error_t igraph_has_multiple(const igraph_t *graph, igraph_bool_t *res) { * to check all edges. * \return Error code. * - * \sa \ref igraph_count_multiple(), \ref igraph_has_multiple() and \ref igraph_simplify(). + * \sa \ref igraph_count_multiple(), \ref igraph_count_multiple_1(), + * \ref igraph_has_multiple() and \ref igraph_simplify(). * * Time complexity: O(e*d), e is the number of edges to check and d is the * average degree (out-degree in directed graphs) of the vertices at the @@ -222,7 +227,7 @@ 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, - igraph_es_t es) { + igraph_es_t es) { igraph_eit_t eit; igraph_integer_t i, j, n; igraph_lazy_inclist_t inclist; @@ -285,7 +290,8 @@ igraph_error_t igraph_is_multiple(const igraph_t *graph, igraph_vector_bool_t *r * 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) { +igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t *res, + igraph_es_t es) { igraph_eit_t eit; igraph_integer_t i, j, n; igraph_lazy_adjlist_t adjlist; @@ -338,7 +344,8 @@ igraph_error_t igraph_count_multiple(const igraph_t *graph, igraph_vector_int_t * * 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_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); @@ -394,7 +401,8 @@ 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) { +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; @@ -467,7 +475,8 @@ igraph_error_t igraph_is_mutual(const igraph_t *graph, igraph_vector_bool_t *res * * 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_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; diff --git a/src/properties/trees.c b/src/properties/trees.c index 84ab392889..b076c9a1d9 100644 --- a/src/properties/trees.c +++ b/src/properties/trees.c @@ -24,6 +24,7 @@ #include "igraph_structural.h" #include "igraph_topology.h" +#include "igraph_bitset.h" #include "igraph_constructors.h" #include "igraph_dqueue.h" #include "igraph_interface.h" @@ -65,8 +66,8 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, igraph_integer_t tree_vertex_count = no_of_nodes; igraph_vector_int_t edges; - igraph_vector_bool_t seen_vertices; - igraph_vector_bool_t seen_edges; + igraph_bitset_t seen_vertices; + igraph_bitset_t seen_edges; igraph_dqueue_int_t Q; igraph_vector_int_t neis; @@ -81,8 +82,8 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, 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); + IGRAPH_BITSET_INIT_FINALLY(&seen_vertices, no_of_nodes); + IGRAPH_BITSET_INIT_FINALLY(&seen_edges, no_of_edges); if (vertex_index) { IGRAPH_CHECK(igraph_vector_int_range(vertex_index, 0, no_of_nodes)); @@ -91,7 +92,7 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, for (igraph_integer_t r = 0; r < no_of_roots; r++) { igraph_integer_t root = VECTOR(*roots)[r]; - VECTOR(seen_vertices)[root] = true; + IGRAPH_BIT_SET(seen_vertices, root); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, root)); while (!igraph_dqueue_int_empty(&Q)) { @@ -107,16 +108,16 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, igraph_integer_t to = IGRAPH_TO(graph, edge); igraph_integer_t nei = IGRAPH_OTHER(graph, edge, actnode); - if (! VECTOR(seen_edges)[edge]) { + if (! IGRAPH_BIT_TEST(seen_edges, edge)) { - VECTOR(seen_edges)[edge] = true; + IGRAPH_BIT_SET(seen_edges, edge); - if (! VECTOR(seen_vertices)[nei]) { + if (! IGRAPH_BIT_TEST(seen_vertices, nei)) { IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); - VECTOR(seen_vertices)[nei] = true; + IGRAPH_BIT_SET(seen_vertices, nei); IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); } else { @@ -142,8 +143,8 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, } /* r < igraph_vector_int_size(roots) */ - igraph_vector_bool_destroy(&seen_edges); - igraph_vector_bool_destroy(&seen_vertices); + igraph_bitset_destroy(&seen_edges); + igraph_bitset_destroy(&seen_vertices); igraph_vector_int_destroy(&neis); igraph_dqueue_int_destroy(&Q); IGRAPH_FINALLY_CLEAN(4); @@ -160,13 +161,13 @@ igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, /* count the number of vertices reachable from the root */ static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_integer_t root, igraph_neimode_t mode, igraph_integer_t *visited_count) { igraph_stack_int_t stack; - igraph_vector_bool_t visited; + igraph_bitset_t visited; igraph_vector_int_t neighbors; igraph_integer_t i; IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, igraph_vcount(graph)); + IGRAPH_BITSET_INIT_FINALLY(&visited, igraph_vcount(graph)); IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); @@ -182,8 +183,8 @@ static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_int /* take a vertex from the stack, mark it as visited */ u = igraph_stack_int_pop(&stack); - if (IGRAPH_LIKELY(! VECTOR(visited)[u])) { - VECTOR(visited)[u] = true; + if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(visited, u))) { + IGRAPH_BIT_SET(visited, u); *visited_count += 1; } @@ -192,7 +193,7 @@ static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_int ncount = igraph_vector_int_size(&neighbors); for (i = 0; i < ncount; ++i) { igraph_integer_t v = VECTOR(neighbors)[i]; - if (! VECTOR(visited)[v]) { + if (! IGRAPH_BIT_TEST(visited, v)) { IGRAPH_CHECK(igraph_stack_int_push(&stack, v)); } } @@ -200,7 +201,7 @@ static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_int igraph_vector_int_destroy(&neighbors); igraph_stack_int_destroy(&stack); - igraph_vector_bool_destroy(&visited); + igraph_bitset_destroy(&visited); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; @@ -400,7 +401,7 @@ igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_ */ 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_bitset_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; @@ -419,8 +420,8 @@ static igraph_error_t igraph_i_is_forest_visitor( * Otherwise mark it as visited and continue. */ u = igraph_stack_int_pop(stack); - if (IGRAPH_LIKELY(! VECTOR(*visited)[u])) { - VECTOR(*visited)[u] = true; + if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, u))) { + IGRAPH_BIT_SET(*visited, u); *visited_count += 1; } else { @@ -446,7 +447,7 @@ static igraph_error_t igraph_i_is_forest_visitor( * an already discovered vertex (i.e. one that has already been * pushed onto the stack). */ - if (IGRAPH_LIKELY(! VECTOR(*visited)[v])) { + if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, v))) { IGRAPH_CHECK(igraph_stack_int_push(stack, v)); } /* To check for a self-loop in undirected graph */ @@ -541,7 +542,7 @@ igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, * - If the graph is not a forest, we don't need to look for roots. */ if (! no_undirected_cycles) { - if (res) { res = false; } + if (res) { *res = false; } if (roots) { igraph_vector_int_clear(roots); } return IGRAPH_SUCCESS; } @@ -577,7 +578,7 @@ 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_bitset_t visited; igraph_vector_int_t neis; igraph_stack_int_t stack; igraph_integer_t visited_count = 0; @@ -620,7 +621,7 @@ static igraph_error_t igraph_i_is_forest( result = true; /* assume success */ - IGRAPH_VECTOR_BOOL_INIT_FINALLY(&visited, vcount); + IGRAPH_BITSET_INIT_FINALLY(&visited, vcount); IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); @@ -647,7 +648,7 @@ static igraph_error_t igraph_i_is_forest( if (!result) { break; } - if (! VECTOR(visited)[v]) { + if (! IGRAPH_BIT_TEST(visited, v)) { if (roots) { IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); } @@ -712,7 +713,7 @@ static igraph_error_t igraph_i_is_forest( igraph_vector_int_destroy(&neis); igraph_stack_int_destroy(&stack); - igraph_vector_bool_destroy(&visited); + igraph_bitset_destroy(&visited); IGRAPH_FINALLY_CLEAN(3); return IGRAPH_SUCCESS; diff --git a/src/random/random.c b/src/random/random.c index b5b44451ae..459af9c0cc 100644 --- a/src/random/random.c +++ b/src/random/random.c @@ -275,7 +275,7 @@ igraph_error_t igraph_rng_seed(igraph_rng_t *rng, igraph_uint_t seed) { * * Time complexity: O(1). */ -IGRAPH_EXPORT igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng) { +igraph_integer_t igraph_rng_bits(const igraph_rng_t* rng) { return rng->type->bits; } @@ -2215,7 +2215,6 @@ igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, igraph_vector_t *result) { igraph_integer_t len = igraph_vector_size(alpha); - igraph_integer_t j; igraph_real_t sum = 0.0; if (len < 2) { @@ -2229,17 +2228,13 @@ igraph_error_t igraph_rng_get_dirichlet(igraph_rng_t *rng, IGRAPH_CHECK(igraph_vector_resize(result, len)); - RNG_BEGIN(); - - for (j = 0; j < len; j++) { - VECTOR(*result)[j] = igraph_rng_get_gamma(rng, VECTOR(*alpha)[j], 1.0); - sum += VECTOR(*result)[j]; + for (igraph_integer_t i = 0; i < len; i++) { + VECTOR(*result)[i] = igraph_rng_get_gamma(rng, VECTOR(*alpha)[i], 1.0); + sum += VECTOR(*result)[i]; } - for (j = 0; j < len; j++) { - VECTOR(*result)[j] /= sum; + for (igraph_integer_t i = 0; i < len; i++) { + VECTOR(*result)[i] /= sum; } - RNG_END(); - return IGRAPH_SUCCESS; } diff --git a/src/random/rng_glibc2.c b/src/random/rng_glibc2.c index 5a687f9155..968f5d46e8 100644 --- a/src/random/rng_glibc2.c +++ b/src/random/rng_glibc2.c @@ -133,12 +133,12 @@ const igraph_rng_type_t igraph_rngtype_glibc2 = { /* 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 + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL }; diff --git a/src/random/rng_mt19937.c b/src/random/rng_mt19937.c index 9caf97670a..adff540a31 100644 --- a/src/random/rng_mt19937.c +++ b/src/random/rng_mt19937.c @@ -165,14 +165,14 @@ const igraph_rng_type_t igraph_rngtype_mt19937 = { /* 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 + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL }; #undef N diff --git a/src/random/rng_pcg32.c b/src/random/rng_pcg32.c index fb51b1ba2f..d821b386d7 100644 --- a/src/random/rng_pcg32.c +++ b/src/random/rng_pcg32.c @@ -99,14 +99,14 @@ const igraph_rng_type_t igraph_rngtype_pcg32 = { /* 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 + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL }; /***** Default RNG, used upon igraph startup *****/ diff --git a/src/random/rng_pcg64.c b/src/random/rng_pcg64.c index e763ff06b8..c460b4ae4d 100644 --- a/src/random/rng_pcg64.c +++ b/src/random/rng_pcg64.c @@ -127,12 +127,12 @@ const igraph_rng_type_t igraph_rngtype_pcg64 = { /* 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 + /* get_int= */ NULL, + /* get_real= */ NULL, + /* get_norm= */ NULL, + /* get_geom= */ NULL, + /* get_binom= */ NULL, + /* get_exp= */ NULL, + /* get_gamma= */ NULL, + /* get_pois= */ NULL }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 715d980fa4..731bec5b07 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -66,6 +66,7 @@ add_examples( add_legacy_tests( FOLDER tests/unit NAMES + bitset heap igraph_array igraph_complex @@ -155,7 +156,7 @@ add_examples( FOLDER examples/simple NAMES igraph_es_pairs igraph_vs_nonadj - igraph_vs_seq + igraph_vs_range igraph_vs_vector ) @@ -203,6 +204,7 @@ add_legacy_tests( igraph_barabasi_game igraph_bipartite_game igraph_callaway_traits_game + igraph_chung_lu_game igraph_circulant igraph_cited_type_game igraph_citing_cited_type_game @@ -326,6 +328,7 @@ add_legacy_tests( igraph_is_bipartite igraph_is_connected igraph_is_chordal + igraph_is_clique igraph_is_complete igraph_is_dag igraph_is_mutual @@ -340,6 +343,7 @@ add_legacy_tests( igraph_local_scan_k_ecount_them igraph_local_scan_subset_ecount igraph_local_transitivity + igraph_mean_degree igraph_neighborhood igraph_neighborhood_graphs igraph_neighborhood_size @@ -350,7 +354,7 @@ add_legacy_tests( igraph_random_walk igraph_rewire # Uses internal igraph_i_rewire igraph_similarity - igraph_transitive_closure_dag + igraph_transitive_closure igraph_transitivity_avglocal_undirected igraph_transitivity_barrat igraph_unfold_tree @@ -359,6 +363,7 @@ add_legacy_tests( igraph_spanner knn random_spanning_tree + reachability single_target_shortest_path topological_sorting ) @@ -384,6 +389,7 @@ add_examples( add_legacy_tests( FOLDER tests/unit NAMES + igraph_biconnected_components igraph_bridges igraph_decompose_strong igraph_is_biconnected @@ -557,6 +563,7 @@ add_examples( FOLDER examples/simple NAMES igraph_complementer igraph_compose + igraph_contract_vertices igraph_difference igraph_disjoint_union igraph_join @@ -568,11 +575,12 @@ add_legacy_tests( FOLDER tests/unit NAMES igraph_contract_vertices igraph_connect_neighborhood + igraph_disjoint_union igraph_graph_power igraph_induced_subgraph igraph_induced_subgraph_map igraph_induced_subgraph_edges - igraph_intersection2 + igraph_intersection igraph_permute_vertices igraph_reverse_edges igraph_rewire_directed_edges @@ -983,6 +991,9 @@ add_legacy_tests( # benchmarks add_benchmarks( NAMES + community + connectivity + erdos_renyi graphicality igraph_average_path_length_unweighted igraph_betweenness @@ -1005,11 +1016,14 @@ add_benchmarks( igraph_power_law_fit igraph_qsort igraph_random_walk + igraph_strength igraph_transitivity igraph_tree_game igraph_vertex_connectivity igraph_voronoi inc_vs_adj + lad + intersection ) # dot product diff --git a/tests/benchmarks/bench.h b/tests/benchmarks/bench.h index a0030d9332..5f135b36ba 100644 --- a/tests/benchmarks/bench.h +++ b/tests/benchmarks/bench.h @@ -1,7 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2013-2021 The igraph development team + Copyright (C) 2013-2024 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 diff --git a/tests/benchmarks/community.c b/tests/benchmarks/community.c new file mode 100644 index 0000000000..8de37d3f2d --- /dev/null +++ b/tests/benchmarks/community.c @@ -0,0 +1,109 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +void run_bench(const igraph_t *graph, const igraph_vector_t *weights, + const char *name, int rep) { + igraph_vector_int_t membership; + igraph_vector_t vertex_weight; + igraph_integer_t vcount = igraph_vcount(graph); + igraph_integer_t ecount = igraph_ecount(graph); + char msg[256], msg2[128]; + + igraph_vector_int_init(&membership, vcount); + igraph_vector_init(&vertex_weight, vcount); + + igraph_strength(graph, &vertex_weight, igraph_vss_all(), IGRAPH_ALL, true, weights); + + snprintf(msg2, sizeof(msg2) / sizeof(msg2[0]), + "%s, vcount=%" IGRAPH_PRId ", ecount=%" IGRAPH_PRId ", %s, %dx", + name, vcount, ecount, weights == NULL ? "unweighted" : "weighted", + rep); + + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), "1 Louvain, %s", msg2); + BENCH(msg, REPEAT(igraph_community_multilevel(graph, weights, 1.0, &membership, NULL, NULL), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), "2 Leiden , %s", msg2); + BENCH(msg, REPEAT(igraph_community_leiden(graph, weights, &vertex_weight, 1.0 / igraph_vector_sum(weights), 0.01, false, 1, &membership, NULL, NULL), rep)); + + printf("\n"); + + igraph_vector_destroy(&vertex_weight); + igraph_vector_int_destroy(&membership); +} + +void rand_weights(const igraph_t *graph, igraph_vector_t *weights) { + igraph_integer_t ecount = igraph_ecount(graph); + igraph_vector_resize(weights, ecount); + for (igraph_integer_t i=0; i < ecount; i++) { + VECTOR(*weights)[i] = RNG_UNIF01(); + } +} + +int main(void) { + igraph_t graph; + igraph_vector_t weights; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + igraph_vector_init(&weights, 0); + + igraph_erdos_renyi_game_gnm(&graph, 100, 500, false, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "G(n,m)", 1000); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 1000, 5000, false, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "G(n,m)", 100); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 1000, 50000, false, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "G(n,m)", 10); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 10000, 50000, false, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "G(n,m)", 10); + igraph_destroy(&graph); + + igraph_erdos_renyi_game_gnm(&graph, 100000, 500000, false, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "G(n,m)", 1); + igraph_destroy(&graph); + + igraph_forest_fire_game(&graph, 1000, 0.2, 1, 2, false); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "forest fire", 100); + igraph_destroy(&graph); + + igraph_barabasi_game(&graph, 1000, 1, 5, NULL, true, 0, false, IGRAPH_BARABASI_PSUMTREE, NULL); + rand_weights(&graph, &weights); + run_bench(&graph, &weights, "PA", 100); + igraph_destroy(&graph); + + igraph_vector_destroy(&weights); + + return 0; +} diff --git a/tests/benchmarks/connectivity.c b/tests/benchmarks/connectivity.c new file mode 100644 index 0000000000..933fcbda67 --- /dev/null +++ b/tests/benchmarks/connectivity.c @@ -0,0 +1,110 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +/* Benchmarks related to biconnected components. */ + +void run_bench(igraph_integer_t vcount, igraph_real_t meandeg, igraph_integer_t rep) { + igraph_t g; + igraph_bool_t b; + igraph_vector_int_t ivec; + char msg[128]; + + igraph_erdos_renyi_game_gnm(&g, vcount, round(meandeg * vcount / 2), IGRAPH_DIRECTED, IGRAPH_LOOPS); + igraph_vector_int_init(&ivec, igraph_vcount(&g)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 1 Weakly connected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, 10*rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_connected(&g, &b, IGRAPH_WEAK), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 2 Weak components. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_connected_components(&g, &ivec, NULL, NULL, IGRAPH_WEAK), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 3 Strongly connected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, 10*rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_connected(&g, &b, IGRAPH_STRONG), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 4 Strong components. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_connected_components(&g, &ivec, NULL, NULL, IGRAPH_STRONG), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 5 Biconnected? vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_is_biconnected(&g, &b), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 6 Bridges. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_bridges(&g, &ivec), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 7 Articulation pts. vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_invalidate_cache(&g); + BENCH(msg, REPEAT(igraph_articulation_points(&g, &ivec), rep)); + + igraph_vector_int_destroy(&ivec); + igraph_destroy(&g); + + printf("\n"); + +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + // Note that whether these random graphs end up being connected + // with high probability depends not only on their mean degree, + // but also their vertex count. + + run_bench(10000, 0.5, 100); // no giant component + run_bench(10000, 3, 100); // not weakly connected + run_bench(10000, 10, 100); // not strongly connected + run_bench(10000, 20, 100); // strongly connnected + + run_bench(100000, 0.5, 100); // no giant component + run_bench(100000, 3, 100); // not weakly connected + run_bench(100000, 10, 100); // not strongly connected + run_bench(100000, 20, 100); // strongly connnected + + return 0; +} diff --git a/tests/benchmarks/erdos_renyi.c b/tests/benchmarks/erdos_renyi.c new file mode 100644 index 0000000000..af06100afa --- /dev/null +++ b/tests/benchmarks/erdos_renyi.c @@ -0,0 +1,136 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +void gnp(igraph_integer_t n, igraph_real_t p, igraph_bool_t directed, igraph_bool_t loops) { + igraph_t g; + igraph_erdos_renyi_game_gnp(&g, n, p, directed, loops); + igraph_destroy(&g); +} + +void gnm(igraph_integer_t n, igraph_real_t meandeg, igraph_bool_t directed, igraph_bool_t loops) { + igraph_t g; + igraph_erdos_renyi_game_gnm(&g, n, round(directed ? n*meandeg : 0.5*n*meandeg), directed, loops); + igraph_destroy(&g); +} + +void chung_lu(const igraph_vector_t *outdeg, const igraph_vector_t *indeg, igraph_bool_t loops) { + igraph_t g; + igraph_chung_lu_game(&g, outdeg, indeg, loops, IGRAPH_CHUNG_LU_ORIGINAL); + igraph_destroy(&g); +} + +void run_bench(igraph_integer_t vcount, igraph_real_t meandeg, igraph_integer_t rep) { + igraph_t g; + igraph_real_t p = meandeg / vcount; + igraph_vector_t outdeg, indeg; + char msg[128], msg2[80]; + + snprintf(msg2, sizeof(msg2) / sizeof(msg2[0]), + "vcount=%" IGRAPH_PRId ", meandeg=%g, %" IGRAPH_PRId "x", + vcount, meandeg, rep); + + igraph_vector_init(&outdeg, 0); + igraph_vector_init(&indeg, 0); + + igraph_erdos_renyi_game_gnp(&g, vcount, p, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); + igraph_strength(&g, &outdeg, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, NULL); + igraph_destroy(&g); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 1 G(n,p) undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 2 G(n,m) undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 3 Chung-Lu undirected, no loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, NULL, IGRAPH_NO_LOOPS), rep)); + + printf("\n"); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 4 G(n,p) undirected, loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_UNDIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 5 G(n,m) undirected, loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_UNDIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 6 Chung-Lu undirected, loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, NULL, IGRAPH_LOOPS), rep)); + + printf("\n"); + + igraph_erdos_renyi_game_gnp(&g, vcount, p, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); + igraph_strength(&g, &outdeg, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS, NULL); + igraph_strength(&g, &indeg, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS, NULL); + igraph_destroy(&g); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 7 G(n,p) directed, no loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 8 G(n,m) directed, no loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + " 9 Chung-Lu directed, no loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, &indeg, IGRAPH_NO_LOOPS), rep)); + + printf("\n"); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "10 G(n,p) directed, loops, %s", msg2); + BENCH(msg, REPEAT(gnp(vcount, p, IGRAPH_DIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "11 G(n,m) directed, loops, %s", msg2); + BENCH(msg, REPEAT(gnm(vcount, meandeg, IGRAPH_DIRECTED, IGRAPH_LOOPS), rep)); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "12 Chung-Lu directed, loops, %s", msg2); + BENCH(msg, REPEAT(chung_lu(&outdeg, &indeg, IGRAPH_LOOPS), rep)); + + printf("\n\n"); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); +} + +int main(void) { + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + + run_bench(100, 3, 10000); + run_bench(10000, 3, 100); + run_bench(1000000, 3, 1); + + run_bench(10000, 1, 1000); + run_bench(10000, 100, 10); + + return 0; +} diff --git a/tests/benchmarks/graphicality.c b/tests/benchmarks/graphicality.c index bd00ed9bb2..98e2917875 100644 --- a/tests/benchmarks/graphicality.c +++ b/tests/benchmarks/graphicality.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_average_path_length_unweighted.c b/tests/benchmarks/igraph_average_path_length_unweighted.c index 2e8c9aee8c..6b3a07b844 100644 --- a/tests/benchmarks/igraph_average_path_length_unweighted.c +++ b/tests/benchmarks/igraph_average_path_length_unweighted.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_betweenness.c b/tests/benchmarks/igraph_betweenness.c index dd92668a36..1bc67390c5 100644 --- a/tests/benchmarks/igraph_betweenness.c +++ b/tests/benchmarks/igraph_betweenness.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_betweenness_weighted.c b/tests/benchmarks/igraph_betweenness_weighted.c index 8f602bdd38..ea3d2df948 100644 --- a/tests/benchmarks/igraph_betweenness_weighted.c +++ b/tests/benchmarks/igraph_betweenness_weighted.c @@ -1,11 +1,29 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { - igraph_integer_t i, n = igraph_ecount(graph); + const igraph_integer_t n = igraph_ecount(graph); igraph_vector_resize(vec, n); - for (i=0; i < n; ++i) { + for (igraph_integer_t i=0; i < n; ++i) { VECTOR(*vec)[i] = RNG_UNIF(1, 10); } } diff --git a/tests/benchmarks/igraph_cliques.c b/tests/benchmarks/igraph_cliques.c index c62933a995..a72fd278bb 100644 --- a/tests/benchmarks/igraph_cliques.c +++ b/tests/benchmarks/igraph_cliques.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_closeness_weighted.c b/tests/benchmarks/igraph_closeness_weighted.c index 14295e65e6..40c6eb9073 100644 --- a/tests/benchmarks/igraph_closeness_weighted.c +++ b/tests/benchmarks/igraph_closeness_weighted.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_coloring.c b/tests/benchmarks/igraph_coloring.c index eea5433599..70229d5d4e 100644 --- a/tests/benchmarks/igraph_coloring.c +++ b/tests/benchmarks/igraph_coloring.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_decompose.c b/tests/benchmarks/igraph_decompose.c index 3fe5b75dea..d21d110b46 100644 --- a/tests/benchmarks/igraph_decompose.c +++ b/tests/benchmarks/igraph_decompose.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_degree.c b/tests/benchmarks/igraph_degree.c index d364c32273..fed53fc00a 100644 --- a/tests/benchmarks/igraph_degree.c +++ b/tests/benchmarks/igraph_degree.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_degree_sequence_game.c b/tests/benchmarks/igraph_degree_sequence_game.c index 536785d3d7..c68e25b172 100644 --- a/tests/benchmarks/igraph_degree_sequence_game.c +++ b/tests/benchmarks/igraph_degree_sequence_game.c @@ -1,8 +1,25 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" - int main(void) { igraph_t g, template; igraph_vector_int_t degrees, outdeg, indeg; @@ -18,7 +35,7 @@ int main(void) { igraph_vector_int_init(°rees, 1000); igraph_vector_int_fill(°rees, 7); - BENCH(" 1 Degree sequence of undirected k-regular graph, N=1000, k=7, CONFIGURATION_SIMPLE", + BENCH(" 1 Degseq of undirected k-regular, N=1000, k=7, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -31,7 +48,7 @@ int main(void) { IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE, /* start_from = */ NULL ); igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, 1); - BENCH(" 2 Degree sequence of undirected BA graph, N=1000, m=1, CONFIGURATION_SIMPLE", + BENCH(" 2 Degseq of undirected BA, N=1000, m=1, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -42,7 +59,7 @@ int main(void) { igraph_vector_int_init(°rees, 0); igraph_erdos_renyi_game_gnm(&template, 200, 600, IGRAPH_UNDIRECTED, IGRAPH_NO_LOOPS); igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, 1); - BENCH(" 3 Degree sequence of undirected Erdos-Renyi graph, N=150, m=450, CONFIGURATION_SIMPLE", + BENCH(" 3 Degseq of undirected G(n,m), N=150, m=450, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -53,7 +70,7 @@ int main(void) { igraph_vector_int_init(°rees, 0); igraph_grg_game(&template, 10000, 0.013, /* torus = */ false, /* x = */ NULL, /* y = */ NULL); igraph_degree(&template, °rees, igraph_vss_all(), IGRAPH_ALL, true); - BENCH(" 4 Degree sequence of GRG graph, N=10000, r=0.013, CONFIGURATION_SIMPLE", + BENCH(" 4 Degseq of GRG, N=10000, r=0.013, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, °rees, /* indeg = */ NULL, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -62,7 +79,7 @@ int main(void) { igraph_vector_int_init(°rees, 1000); igraph_vector_int_fill(°rees, 5); - BENCH(" 5 Degree sequence of directed k-regular graph, N=1000, k=5, CONFIGURATION_SIMPLE", + BENCH(" 5 Degseq of directed k-regular, N=1000, k=5, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, °rees, °rees, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -77,7 +94,7 @@ int main(void) { ); igraph_degree(&template, &outdeg, igraph_vss_all(), IGRAPH_OUT, true); igraph_degree(&template, &indeg, igraph_vss_all(), IGRAPH_IN, true); - BENCH(" 6 Degree sequence of directed BA graph, N=500, m=2, CONFIGURATION_SIMPLE", + BENCH(" 6 Degseq of directed BA, N=500, m=2, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); @@ -91,7 +108,7 @@ int main(void) { igraph_erdos_renyi_game_gnm(&template, 15000, 45000, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS); igraph_degree(&template, &outdeg, igraph_vss_all(), IGRAPH_OUT, true); igraph_degree(&template, &indeg, igraph_vss_all(), IGRAPH_IN, true); - BENCH(" 7 Degree sequence of directed Erdos-Renyi graph, N=15000, m=45000, CONFIGURATION_SIMPLE", + BENCH(" 7 Degseq of directed G(n,m), N=15000, m=45000, CONFIGURATION_SIMPLE", igraph_degree_sequence_game(&g, &outdeg, &indeg, IGRAPH_DEGSEQ_CONFIGURATION_SIMPLE) ); igraph_destroy(&g); diff --git a/tests/benchmarks/igraph_distances.c b/tests/benchmarks/igraph_distances.c index 3237165395..e3ac83d4f9 100644 --- a/tests/benchmarks/igraph_distances.c +++ b/tests/benchmarks/igraph_distances.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 @@ -68,7 +85,7 @@ int main(void) { BENCH(" 9 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall (negative), " TOSTR(REP) "x", REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_ORIGINAL), REP) ); - BENCH(" 10 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup (negative), " TOSTR(REP) "x", + BENCH("10 vcount=" TOSTR(VCOUNT) ", p=" TOSTR(DENS) ", Floyd-Warshall-tree-speedup (negative), " TOSTR(REP) "x", REPEAT(igraph_distances_floyd_warshall(&g, &res, igraph_vss_all(), igraph_vss_all(), &weights, IGRAPH_OUT, IGRAPH_FLOYD_WARSHALL_TREE), REP) ); diff --git a/tests/benchmarks/igraph_ecc.c b/tests/benchmarks/igraph_ecc.c index 61f3318352..5fdefb6109 100644 --- a/tests/benchmarks/igraph_ecc.c +++ b/tests/benchmarks/igraph_ecc.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_induced_subgraph_edges.c b/tests/benchmarks/igraph_induced_subgraph_edges.c index 26b7282687..67ff865c42 100644 --- a/tests/benchmarks/igraph_induced_subgraph_edges.c +++ b/tests/benchmarks/igraph_induced_subgraph_edges.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" @@ -14,6 +32,8 @@ void unit_test_subgraph_edges( igraph_vector_int_t edges; igraph_vector_int_init(&edges, 0); + IGRAPH_UNUSED(vertices); + BENCH(bench_message, REPEAT( igraph_induced_subgraph_edges(graph, igraph_vss_range(0, induced_subgraph_vertice_count), &edges), @@ -66,6 +86,9 @@ void bench_induced_subgraph_edges(void) { #undef INDUCED_SUBGRAPH_VERTEX_COUNT #undef REP + + igraph_destroy(&g); + igraph_vector_int_destroy(&vertices); } int main(void) { diff --git a/tests/benchmarks/igraph_layout_umap.c b/tests/benchmarks/igraph_layout_umap.c index 73d4cecf55..80ede058b0 100644 --- a/tests/benchmarks/igraph_layout_umap.c +++ b/tests/benchmarks/igraph_layout_umap.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_matrix_transpose.c b/tests/benchmarks/igraph_matrix_transpose.c index f54bfcec53..54570c3cf4 100644 --- a/tests/benchmarks/igraph_matrix_transpose.c +++ b/tests/benchmarks/igraph_matrix_transpose.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/igraph_maximal_cliques.c b/tests/benchmarks/igraph_maximal_cliques.c index b1e99cabf2..74de1f27f1 100644 --- a/tests/benchmarks/igraph_maximal_cliques.c +++ b/tests/benchmarks/igraph_maximal_cliques.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2013 Gabor Csardi - 334 Harvard st, Cambridge MA, 02139 USA + Copyright (C) 2013-2024 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 diff --git a/tests/benchmarks/igraph_neighborhood.c b/tests/benchmarks/igraph_neighborhood.c index a658b8988c..173f923e3d 100644 --- a/tests/benchmarks/igraph_neighborhood.c +++ b/tests/benchmarks/igraph_neighborhood.c @@ -1,6 +1,6 @@ /* IGraph library. - Copyright (C) 2021 The igraph development team + Copyright (C) 2021-2024 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 @@ -31,6 +31,7 @@ typedef struct igraph_lazy_adjlist2_t { igraph_loops_t loops; igraph_multiple_t multiple; } igraph_lazy_adjlist2_t; + igraph_error_t igraph_lazy_adjlist2_init(const igraph_t *graph, igraph_lazy_adjlist2_t *al, igraph_neimode_t mode, @@ -67,8 +68,9 @@ void igraph_lazy_adjlist2_destroy(igraph_lazy_adjlist2_t *al) { IGRAPH_FREE(al->data); } -igraph_vector_int_t *igraph_i_lazy_adjlist2_get_real(igraph_lazy_adjlist2_t *al, - igraph_integer_t pno) { +igraph_vector_int_t *igraph_i_lazy_adjlist2_get_real( + igraph_lazy_adjlist2_t *al, igraph_integer_t pno +) { igraph_integer_t no = pno; igraph_error_t ret; diff --git a/tests/benchmarks/igraph_pagerank.c b/tests/benchmarks/igraph_pagerank.c index 7f5bbecd8d..159e2a83b1 100644 --- a/tests/benchmarks/igraph_pagerank.c +++ b/tests/benchmarks/igraph_pagerank.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_pagerank_weighted.c b/tests/benchmarks/igraph_pagerank_weighted.c index 6837711de0..94149da83c 100644 --- a/tests/benchmarks/igraph_pagerank_weighted.c +++ b/tests/benchmarks/igraph_pagerank_weighted.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_power_law_fit.c b/tests/benchmarks/igraph_power_law_fit.c index 8cb9eb98d0..7f5b272099 100644 --- a/tests/benchmarks/igraph_power_law_fit.c +++ b/tests/benchmarks/igraph_power_law_fit.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include diff --git a/tests/benchmarks/igraph_qsort.c b/tests/benchmarks/igraph_qsort.c index a5a2da8af3..740a1de15f 100644 --- a/tests/benchmarks/igraph_qsort.c +++ b/tests/benchmarks/igraph_qsort.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_random_walk.c b/tests/benchmarks/igraph_random_walk.c index a755013dad..b726cf9208 100644 --- a/tests/benchmarks/igraph_random_walk.c +++ b/tests/benchmarks/igraph_random_walk.c @@ -1,3 +1,21 @@ +/* + IGraph library. + Copyright (C) 2024 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 #include "bench.h" diff --git a/tests/benchmarks/igraph_strength.c b/tests/benchmarks/igraph_strength.c new file mode 100644 index 0000000000..ba4baa13e2 --- /dev/null +++ b/tests/benchmarks/igraph_strength.c @@ -0,0 +1,145 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +#define TOSTR1(x) #x +#define TOSTR(x) TOSTR1(x) + +void rand_weight_vec(igraph_vector_t *vec, const igraph_t *graph) { + igraph_integer_t i, n = igraph_ecount(graph); + igraph_vector_resize(vec, n); + for (i=0; i < n; ++i) { + VECTOR(*vec)[i] = RNG_UNIF(1, 10); + } +} + +int main(void) { + + igraph_t g; + igraph_vector_t strength, weights; + + igraph_rng_seed(igraph_rng_default(), 54); + BENCH_INIT(); + + igraph_vector_init(&strength, 0); + igraph_vector_init(&weights, 0); + + /* igraph_strength() uses an optimized, cache friendly code path when given + * a vertex selector for which igraph_vs_is_all() returns true (this is + * currently igraph_vs_all()). We pass both igraph_vss_all() and + * igraph_vss_range(0, igraph_vcount(graph)) as vertex selectors + * to compare the performance of the two code paths. + * + * NOTE: While currently igraph_vs_is_all() does not return true for + * a range-type vertex selector, this may change in the future. + * An altrenative is to to use igraph_vs_vector(), with the vector + * initialized to a range. + */ + +#define N 1000 +#define M 10 +#define REP 10000 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 1a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 1b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 10000 +#define M 10 +#define REP 1000 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 2a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 2b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 100000 +#define M 10 +#define REP 100 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 3a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 3b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + +#define N 100000 +#define M 100 +#define REP 10 + + igraph_barabasi_game(&g, N, 1, M, NULL, true, 0, IGRAPH_UNDIRECTED, IGRAPH_BARABASI_PSUMTREE_MULTIPLE, NULL); + rand_weight_vec(&weights, &g); + + BENCH(" 4a igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + BENCH(" 4b igraph_strength(), preferential attachment n=" TOSTR(N) ", m=" TOSTR(M) ", " TOSTR(REP) "x", + REPEAT(igraph_strength(&g, &strength, igraph_vss_range(0, igraph_vcount(&g)), IGRAPH_ALL, IGRAPH_LOOPS, &weights), REP) + ); + printf("\n"); + + igraph_destroy(&g); + +#undef N +#undef M +#undef REP + + igraph_vector_destroy(&weights); + igraph_vector_destroy(&strength); + + return 0; +} diff --git a/tests/benchmarks/igraph_transitivity.c b/tests/benchmarks/igraph_transitivity.c index a645ecd99f..ed89712611 100644 --- a/tests/benchmarks/igraph_transitivity.c +++ b/tests/benchmarks/igraph_transitivity.c @@ -1,8 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2013 Gabor Csardi - 334 Harvard st, Cambridge MA, 02139 USA + Copyright (C) 2013-2024 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 diff --git a/tests/benchmarks/igraph_tree_game.c b/tests/benchmarks/igraph_tree_game.c index 80b8e375fa..183fdb5c06 100644 --- a/tests/benchmarks/igraph_tree_game.c +++ b/tests/benchmarks/igraph_tree_game.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 @@ -6,8 +23,13 @@ #define TOSTR1(x) #x #define TOSTR(x) TOSTR1(x) -int main(void) { +void tree_game(igraph_integer_t n, igraph_bool_t directed, igraph_random_tree_t method) { igraph_t g; + igraph_tree_game(&g, n, directed, method); + igraph_destroy(&g); +} + +int main(void) { igraph_rng_seed(igraph_rng_default(), 137); BENCH_INIT(); @@ -16,14 +38,12 @@ int main(void) { #define REP 100000 BENCH(" 1 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); BENCH(" 2 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); #undef VCOUNT #undef DENS @@ -35,14 +55,12 @@ int main(void) { #define REP 10000 BENCH(" 1 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); ); - igraph_destroy(&g); BENCH(" 2 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); ); - igraph_destroy(&g); #undef VCOUNT #undef DENS @@ -54,14 +72,12 @@ int main(void) { #define REP 1000 BENCH(" 3 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); BENCH(" 4 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); #undef VCOUNT #undef DENS @@ -73,14 +89,12 @@ int main(void) { #define REP 100 BENCH(" 3 vcount=" TOSTR(VCOUNT) ", Prufer, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_PRUFER), REP); + ); BENCH(" 4 vcount=" TOSTR(VCOUNT) ", LERW, " TOSTR(REP) "x", - REPEAT(igraph_tree_game(&g, VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); - ); - igraph_destroy(&g); + REPEAT(tree_game(VCOUNT, IGRAPH_UNDIRECTED, IGRAPH_RANDOM_TREE_LERW), REP); + ); #undef VCOUNT #undef DENS diff --git a/tests/benchmarks/igraph_vertex_connectivity.c b/tests/benchmarks/igraph_vertex_connectivity.c index 0d024aa632..8d1f11d4ed 100644 --- a/tests/benchmarks/igraph_vertex_connectivity.c +++ b/tests/benchmarks/igraph_vertex_connectivity.c @@ -1,7 +1,6 @@ -/* -*- mode: C -*- */ /* IGraph library. - Copyright (C) 2023 The igraph development team + Copyright (C) 2024 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,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 diff --git a/tests/benchmarks/igraph_voronoi.c b/tests/benchmarks/igraph_voronoi.c index f1adf31379..74c9e02665 100644 --- a/tests/benchmarks/igraph_voronoi.c +++ b/tests/benchmarks/igraph_voronoi.c @@ -1,3 +1,20 @@ +/* + IGraph library. + Copyright (C) 2024 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 diff --git a/tests/benchmarks/inc_vs_adj.c b/tests/benchmarks/inc_vs_adj.c index 714f89bf56..85cc0ae0b7 100644 --- a/tests/benchmarks/inc_vs_adj.c +++ b/tests/benchmarks/inc_vs_adj.c @@ -1,6 +1,6 @@ /* IGraph library. - Copyright (C) 2013-2022 The igraph development team + Copyright (C) 2013-2024 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 diff --git a/tests/benchmarks/intersection.c b/tests/benchmarks/intersection.c new file mode 100644 index 0000000000..dfb5592e1d --- /dev/null +++ b/tests/benchmarks/intersection.c @@ -0,0 +1,87 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +void rand_vec(igraph_vector_int_t *v, igraph_integer_t n, igraph_integer_t k) { + igraph_vector_int_resize(v, n); + for (igraph_integer_t i=0; i < n; i++) { + VECTOR(*v)[i] = RNG_INTEGER(0, k); + } +} + +void run_bench(int i, int n, int r) { + igraph_vector_int_t a, b; + igraph_integer_t na = n, nb = r*n; + int rep = 300000000 / nb; + char msg[255]; + + igraph_vector_int_init(&a, na); + igraph_vector_int_init(&b, nb); + + rand_vec(&a, na, nb); + igraph_vector_int_sort(&a); + rand_vec(&b, nb, nb); + igraph_vector_int_sort(&b); + + snprintf(msg, sizeof(msg) / sizeof(msg[0]), + "%2d n = %5d, r = %3d, %dx", i, n, r, rep); + + /* 'volatile' is needed to prevent the compiler from optimizing + * away multiple calls to the function with the same parameters within REPEAT. + * This would normally happen due to the use of IGRAPH_FUNCATTR_PURE. + * ATTENTION! 'volatile', when used this way, may not prevent this optimization + * with future compiler versions. */ + volatile igraph_integer_t res; + BENCH(msg, REPEAT(res = igraph_vector_int_intersection_size_sorted(&a, &b), rep)); + (void) res; /* silence unused-but-set-variable warning */ + + igraph_vector_int_destroy(&a); + igraph_vector_int_destroy(&b); +} + +int main(void) { + int i = 0; + + igraph_rng_seed(igraph_rng_default(), 137); + BENCH_INIT(); + +#define BENCHSET(n) \ + run_bench(++i, n, 1); \ + run_bench(++i, n, 3); \ + run_bench(++i, n, 10); \ + run_bench(++i, n, 30); \ + run_bench(++i, n, 100); \ + printf("\n"); + + BENCHSET(1); + BENCHSET(3); + BENCHSET(10); + BENCHSET(30); + BENCHSET(100); + BENCHSET(300); + BENCHSET(1000); + BENCHSET(3000); + BENCHSET(10000); + BENCHSET(30000); + BENCHSET(100000); + + return 0; +} diff --git a/tests/benchmarks/lad.c b/tests/benchmarks/lad.c new file mode 100644 index 0000000000..f9d0ae2752 --- /dev/null +++ b/tests/benchmarks/lad.c @@ -0,0 +1,67 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "bench.h" + +void match(const igraph_t *graph, + const igraph_t patt[], igraph_integer_t n, + igraph_vector_int_list_t *maps) { + + igraph_vector_int_list_clear(maps); + for (igraph_integer_t i=0; i < n; i++) { + igraph_subisomorphic_lad(&patt[i], graph, NULL, NULL, NULL, maps, false, 0); + } +} + +#define NP 8 + +int main(void) { + igraph_t graph; + igraph_t patt[NP]; + igraph_vector_int_list_t maps; + + BENCH_INIT(); + + igraph_vector_int_list_init(&maps, 0); + + for (igraph_integer_t i=0; i < NP; i++) { + igraph_ring(&patt[i], i+1, IGRAPH_DIRECTED, false, true); + } + + igraph_kautz(&graph, 3, 3); + BENCH("1 Kautz(3,3) 10x", REPEAT(match(&graph, patt, NP, &maps), 10)); + igraph_destroy(&graph); + + igraph_kautz(&graph, 3, 4); + BENCH("2 Kautz(3,4) 3x", REPEAT(match(&graph, patt, NP, &maps), 3)); + igraph_destroy(&graph); + + igraph_kautz(&graph, 4, 3); + BENCH("3 Kautz(4,3) 1x", REPEAT(match(&graph, patt, NP, &maps), 1)); + igraph_destroy(&graph); + + for (igraph_integer_t i=0; i < NP; i++) { + igraph_destroy(&patt[i]); + } + + igraph_vector_int_list_destroy(&maps); + + return 0; +} diff --git a/tests/regression/invalid_pajek2.net b/tests/regression/invalid_pajek2.net index 233cc7b02b..293af2b779 100644 --- a/tests/regression/invalid_pajek2.net +++ b/tests/regression/invalid_pajek2.net @@ -1,3 +1,3 @@ *Network TRALALA *vertices 4 1 - -3 "3." + 0 "3." diff --git a/tests/unit/adjlist.out b/tests/unit/adjlist.out index 1c0fc35751..ffe6550e50 100644 --- a/tests/unit/adjlist.out +++ b/tests/unit/adjlist.out @@ -584,10 +584,10 @@ loop not cached multi not cached graph: multiloop, loop: 0 multi: 0, mode: 0 loop cached: 1 -multi cached: 0 +multi cached: 1 graph: multiloop, loop: 0 multi: 0, mode: 1 loop cached: 1 -multi cached: 0 +multi cached: 1 graph: multiloop, loop: 0 multi: 1, mode: 0 loop cached: 1 multi not cached diff --git a/tests/unit/assortativity.c b/tests/unit/assortativity.c index 8e2cde176b..383b059ae0 100644 --- a/tests/unit/assortativity.c +++ b/tests/unit/assortativity.c @@ -349,7 +349,7 @@ int main(void) { 70, 113, 17, 113, 87, 113, 76, 113, 65, 113, 96, 113, 83, 114, 88, 114, 110, 114, 53, 114, 49, 114, 73, 114, 46, 114, 67, 114, 58, 114, 15, 114, 104, 114, -1); - igraph_simplify(&g, /*multiple=*/ true, /*loops=*/ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /*remove_multiple=*/ true, /*remove_loops=*/ true, /*edge_comb=*/ NULL); igraph_vector_int_view(&types, football_types, sizeof(football_types) / sizeof(football_types[0])); igraph_assortativity_nominal(&g, &types, &assort, IGRAPH_UNDIRECTED, /*normalized=*/ true); printf("%g\n", assort); diff --git a/tests/unit/bitset.c b/tests/unit/bitset.c new file mode 100644 index 0000000000..e4fd46dd78 --- /dev/null +++ b/tests/unit/bitset.c @@ -0,0 +1,405 @@ +/* -*- mode: C -*- */ +/* vim:set ts=4 sw=4 sts=4 et: */ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_bitset_t v1, v2, v3; + igraph_integer_t n; + + printf("Initialise empty bitset\n"); + n = 0; + igraph_bitset_init(&v1, n); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == n); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 32\n"); + n = 32; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 64\n"); + n = 64; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Initialise bitset of length 75\n"); + n = 75; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == n); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test IGRAPH_BIT_SET\n"); + n = 35; + igraph_bitset_init(&v1, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 24); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 13); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 17); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 34); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 13); + print_bitset(&v1); + printf("\n"); + + printf("Test IGRAPH_BIT_CLEAR\n"); + IGRAPH_BIT_CLEAR(v1, 33); + print_bitset(&v1); + IGRAPH_BIT_CLEAR(v1, 34); + print_bitset(&v1); + IGRAPH_BIT_CLEAR(v1, 17); + print_bitset(&v1); + printf("\n"); + + printf("Test printing\n"); + igraph_bitset_print(&v1); + printf("\n\n"); + + printf("Test bitset copy constructor\n"); + igraph_bitset_init_copy(&v2, &v1); + print_bitset(&v2); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + printf("\n"); + + printf("Test bitset resize\n"); + igraph_bitset_init(&v1, 0); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 0); + igraph_bitset_resize(&v1, 10); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 10); + IGRAPH_BIT_SET(v1, 3); + print_bitset(&v1); + igraph_bitset_resize(&v1, 64); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 63); + print_bitset(&v1); + igraph_bitset_resize(&v1, 70); + print_bitset(&v1); + IGRAPH_BIT_SET(v1, 64); + print_bitset(&v1); + igraph_bitset_resize(&v1, 64); + print_bitset(&v1); + igraph_bitset_resize(&v1, 63); + print_bitset(&v1); + IGRAPH_ASSERT(igraph_bitset_size(&v1) == 63); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test OR\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_or(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test AND\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_and(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test XOR\n"); + n = 7; + igraph_bitset_init(&v2, n); + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v3, n); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + IGRAPH_BIT_SET(v3, 2); + IGRAPH_BIT_SET(v3, 3); + IGRAPH_BIT_SET(v3, 6); + print_bitset(&v3); + igraph_bitset_xor(&v1, &v2, &v3); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + igraph_bitset_destroy(&v3); + printf("\n"); + + printf("Test NOT\n"); + n = 7; + igraph_bitset_init(&v1, n); + igraph_bitset_init(&v2, n); + IGRAPH_BIT_SET(v1, 1); + print_bitset(&v1); + IGRAPH_BIT_SET(v2, 1); + IGRAPH_BIT_SET(v2, 2); + IGRAPH_BIT_SET(v2, 5); + print_bitset(&v2); + igraph_bitset_not(&v1, &v2); + print_bitset(&v1); + igraph_bitset_destroy(&v1); + igraph_bitset_destroy(&v2); + printf("\n"); + + printf("Test popcount\n"); + n = 75; + igraph_bitset_init(&v1, n); + print_bitset(&v1); + + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + for (igraph_integer_t i = 2; i < n; i++) { + if (i == 2 || i == 3 || i == 5 || i == 7 || i == 11 || !(i%2 == 0 || i%3 == 0 || i%5 == 0 || i%7 == 0 || i%11 == 0)) { + IGRAPH_BIT_SET(v1, i); + } + } + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + IGRAPH_BIT_CLEAR(v1, 67); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + IGRAPH_BIT_SET(v1, 9); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Popcount: %" IGRAPH_PRId "\n", igraph_bitset_popcount(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test count leading zeros\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, 23); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, n - 3); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + IGRAPH_BIT_SET(v1, n - 1); + print_bitset(&v1); + printf("Leading zeros: %" IGRAPH_PRId "\n", igraph_bitset_countl_zero(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + + printf("Test count leading ones\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, n - 3); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + IGRAPH_BIT_CLEAR(v1, n - 1); + print_bitset(&v1); + printf("Leading ones: %" IGRAPH_PRId "\n", igraph_bitset_countl_one(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + + printf("Test count trailing zeros\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 23); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + IGRAPH_BIT_SET(v1, n - 1); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + IGRAPH_BIT_SET(v1, 2); + print_bitset(&v1); + printf("Trailing zeros: %" IGRAPH_PRId "\n", igraph_bitset_countr_zero(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test count trailing ones\n"); + n = 67; + igraph_bitset_init(&v1, n); + igraph_bitset_not(&v1, &v1); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 23); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 0); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_SET(v1, 0); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_SET(v1, 23); + IGRAPH_BIT_CLEAR(v1, n - 1); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + IGRAPH_BIT_CLEAR(v1, 2); + print_bitset(&v1); + printf("Trailing ones: %" IGRAPH_PRId "\n", igraph_bitset_countr_one(&v1)); + igraph_bitset_destroy(&v1); + printf("\n"); + + printf("Test checking all elements\n"); + /* 0000 */ + igraph_bitset_init(&v1, 4); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 1000 */ + IGRAPH_BIT_SET(v1, 3); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 000 */ + /* This resize leaves a set bit in the unused part of the last word of the. + * bitset. This helps test that masking out the unused part is working. */ + igraph_bitset_resize(&v1, 3); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 001 */ + IGRAPH_BIT_SET(v1, 0); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* 111 */ + IGRAPH_BIT_SET(v1, 1); IGRAPH_BIT_SET(v1, 2); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + igraph_bitset_init(&v1, 67); + /* all zeros */ + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* some ones */ + IGRAPH_BIT_SET(v1, 10); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + /* all ones */ + igraph_bitset_fill(&v1, true); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + /* some zeros */ + IGRAPH_BIT_CLEAR(v1, 20); + IGRAPH_ASSERT(! igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + /* Empty bitset */ + igraph_bitset_init(&v1, 0); + IGRAPH_ASSERT(igraph_bitset_is_all_one(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_one(&v1)); + IGRAPH_ASSERT(igraph_bitset_is_all_zero(&v1)); + IGRAPH_ASSERT(! igraph_bitset_is_any_zero(&v1)); + igraph_bitset_destroy(&v1); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/bitset.out b/tests/unit/bitset.out new file mode 100644 index 0000000000..c7c5561a43 --- /dev/null +++ b/tests/unit/bitset.out @@ -0,0 +1,137 @@ +Initialise empty bitset + +Initialise bitset of length 32 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + +Initialise bitset of length 64 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + +Initialise bitset of length 75 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) + +Test IGRAPH_BIT_SET +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test IGRAPH_BIT_CLEAR +( 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test printing +00000000001000000000010000000000001 + +Test bitset copy constructor +( 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 ) + +Test bitset resize +( ) +( 0 0 0 0 0 0 0 0 0 0 ) +( 0 0 0 0 0 0 1 0 0 0 ) +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) +( 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) +( 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) +( 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) +( 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 ) + +Test OR +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 1 1 0 1 1 1 0 ) + +Test AND +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 0 0 0 0 1 0 0 ) + +Test XOR +( 0 0 0 0 0 0 1 ) +( 0 1 0 0 1 1 0 ) +( 1 0 0 1 1 0 0 ) +( 1 1 0 1 0 1 0 ) + +Test NOT +( 0 0 0 0 0 1 0 ) +( 0 1 0 0 1 1 0 ) +( 1 0 1 1 0 0 1 ) + +Test popcount +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Popcount: 0 +( 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 ) +Popcount: 21 +( 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 1 0 0 ) +Popcount: 20 +( 0 1 0 1 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 1 0 0 0 1 0 1 0 0 0 1 0 1 0 1 0 1 0 1 0 1 1 0 0 ) +Popcount: 21 +( 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 0 1 1 1 1 1 0 1 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 1 0 1 0 1 0 1 0 1 0 1 0 0 1 1 ) +Popcount: 54 + +Test count leading zeros +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading zeros: 0 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 67 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Leading zeros: 66 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Leading zeros: 43 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 43 +( 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 2 +( 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Leading zeros: 0 + +Test count leading ones +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 67 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Leading ones: 66 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Leading ones: 43 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 43 +( 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 2 +( 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Leading ones: 0 + +Test count trailing zeros +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing zeros: 0 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Trailing zeros: 67 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Trailing zeros: 0 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 ) +Trailing zeros: 0 +( 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Trailing zeros: 23 +( 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ) +Trailing zeros: 66 +( 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 ) +Trailing zeros: 2 + +Test count trailing ones +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 67 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 23 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ) +Trailing ones: 0 +( 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 23 +( 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ) +Trailing ones: 66 +( 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 ) +Trailing ones: 2 + +Test checking all elements diff --git a/tests/unit/cattributes5.c b/tests/unit/cattributes5.c index 3dd9f1805a..1cdc496267 100644 --- a/tests/unit/cattributes5.c +++ b/tests/unit/cattributes5.c @@ -26,7 +26,7 @@ #include "test_utilities.h" static void simplify_write_destroy(igraph_t *g, igraph_attribute_combination_t *comb) { - igraph_simplify(g, /*multiple=*/ true, /*loops=*/ true, comb); + igraph_simplify(g, /*remove_multiple=*/ true, /*remove_loops=*/ true, comb); igraph_write_graph_graphml(g, stdout, /*prefixattr=*/ true); igraph_attribute_combination_destroy(comb); igraph_destroy(g); diff --git a/tests/unit/erdos_renyi_game_gnm.c b/tests/unit/erdos_renyi_game_gnm.c index 543d7dd9eb..c0c73e1fd1 100644 --- a/tests/unit/erdos_renyi_game_gnm.c +++ b/tests/unit/erdos_renyi_game_gnm.c @@ -53,7 +53,7 @@ int main(void) { IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 10 - 1); IGRAPH_ASSERT(igraph_is_directed(&g)); - igraph_simplify(&g, /*multiple=*/ false, /*loops=*/ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /*remove_multiple=*/ false, /*remove_loops=*/ true, /*edge_comb=*/ NULL); IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 || igraph_ecount(&g) == 10 * 9 - 1); igraph_destroy(&g); @@ -72,7 +72,7 @@ int main(void) { IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 11 / 2 - 1); IGRAPH_ASSERT(! igraph_is_directed(&g)); - igraph_simplify(&g, /*multiple=*/ false, /*loops=*/ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /*remove_multiple=*/ false, /*remove_loops=*/ true, /*edge_comb=*/ NULL); IGRAPH_ASSERT(igraph_ecount(&g) == 10 * 9 / 2 || igraph_ecount(&g) == 10 * 9 / 2 - 1); igraph_destroy(&g); diff --git a/tests/unit/erdos_renyi_game_gnp.c b/tests/unit/erdos_renyi_game_gnp.c index 087b301078..0c86e42797 100644 --- a/tests/unit/erdos_renyi_game_gnp.c +++ b/tests/unit/erdos_renyi_game_gnp.c @@ -20,7 +20,7 @@ #include "test_utilities.h" -int stress_test(void) { +void stress_test(void) { igraph_rng_seed(igraph_rng_default(), 137); for (igraph_integer_t size=2; size < 5; size++) { @@ -62,11 +62,9 @@ int stress_test(void) { } VERIFY_FINALLY_STACK(); - - return 0; } -int test_examples(void) { +void test_examples(void) { igraph_t g; igraph_bool_t simple; @@ -164,20 +162,12 @@ int test_examples(void) { igraph_destroy(&g); VERIFY_FINALLY_STACK(); - - return 0; } int main(void) { - int retval; - retval = test_examples(); - if (retval) { - return retval; - } + test_examples(); + stress_test(); - retval = stress_test(); - if (retval) { - return retval; - } + return 0; } diff --git a/tests/unit/igraph_barabasi_game.c b/tests/unit/igraph_barabasi_game.c index a4d73034d4..4966caedcd 100644 --- a/tests/unit/igraph_barabasi_game.c +++ b/tests/unit/igraph_barabasi_game.c @@ -18,17 +18,50 @@ #include #include "test_utilities.h" +void run_test(igraph_integer_t n, igraph_bool_t directed, const igraph_vector_int_t *outseq, const igraph_t *start) { + igraph_barabasi_algorithm_t algos[] = { IGRAPH_BARABASI_BAG, IGRAPH_BARABASI_PSUMTREE, IGRAPH_BARABASI_PSUMTREE_MULTIPLE }; + igraph_t g; + igraph_bool_t simple; + + for (size_t i=0; i < sizeof(algos) / sizeof(algos[0]); i++) { + igraph_barabasi_algorithm_t algo = algos[i]; + igraph_barabasi_game(&g, n, 1, 2, outseq, true, 1, directed, algo, start); + IGRAPH_ASSERT(igraph_vcount(&g) == n); + IGRAPH_ASSERT(igraph_is_directed(&g) == directed); + if (algo == IGRAPH_BARABASI_PSUMTREE) { + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + } + igraph_destroy(&g); + } +} + int main(void) { igraph_vector_int_t v; igraph_t g; - CHECK_ERROR(igraph_barabasi_game(&g, -10, /*power=*/ 1, 1, 0, 0, /*A=*/ 1, 0, - IGRAPH_BARABASI_BAG, /*start_from=*/ 0), IGRAPH_EINVAL); - CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, -2, 0, 0, /*A=*/ 1, 0, - IGRAPH_BARABASI_BAG, /*start_from=*/ 0), IGRAPH_EINVAL); + run_test(10, true, NULL, NULL); + run_test(10, false, NULL, NULL); + + igraph_ring(&g, 5, IGRAPH_DIRECTED, false, true); + run_test(13, true, NULL, &g); + igraph_to_undirected(&g, IGRAPH_TO_UNDIRECTED_EACH, NULL); + run_test(13, false, NULL, &g); + igraph_destroy(&g); + + igraph_vector_int_init_int(&v, 4, + 1, 2, 1, 3); + run_test(4, true, &v, NULL); + run_test(4, false, &v, NULL); + igraph_vector_int_destroy(&v); + + CHECK_ERROR(igraph_barabasi_game(&g, -10, /*power=*/ 1, 1, NULL, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); + CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, -2, NULL, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); igraph_vector_int_init(&v, 9); - CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, 0, &v, 0, /*A=*/ 1, 0, - IGRAPH_BARABASI_BAG, /*start_from=*/ 0), IGRAPH_EINVAL); + CHECK_ERROR(igraph_barabasi_game(&g, 10, /*power=*/ 1, 0, &v, false, /*A=*/ 1, IGRAPH_UNDIRECTED, + IGRAPH_BARABASI_BAG, /*start_from=*/ NULL), IGRAPH_EINVAL); igraph_vector_int_destroy(&v); VERIFY_FINALLY_STACK(); diff --git a/tests/unit/igraph_betweenness.c b/tests/unit/igraph_betweenness.c index a4bb61b27b..6a55bbc8c3 100644 --- a/tests/unit/igraph_betweenness.c +++ b/tests/unit/igraph_betweenness.c @@ -60,7 +60,7 @@ int main(void) { /* algo= */ IGRAPH_BARABASI_BAG, /* start_from= */ 0); - igraph_simplify(&g, /* multiple= */ true, /* loops= */ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple= */ true, /* remove_loops= */ true, /*edge_comb=*/ NULL); igraph_vector_init(&bet, 0); diff --git a/tests/unit/igraph_betweenness_subset.c b/tests/unit/igraph_betweenness_subset.c index d915d71d2d..0ee4878950 100644 --- a/tests/unit/igraph_betweenness_subset.c +++ b/tests/unit/igraph_betweenness_subset.c @@ -56,7 +56,7 @@ int main(void) { /* algo= */ IGRAPH_BARABASI_BAG, /* start_from= */ 0); - igraph_simplify(&g, /* multiple= */ true, /* loops= */ true, /*edge_comb=*/ NULL); + igraph_simplify(&g, /* remove_multiple= */ true, /* remove_loops= */ true, /*edge_comb=*/ NULL); igraph_vector_init(&bet, 0); igraph_vs_range(&vs_source, 0, 501); diff --git a/tests/unit/igraph_biconnected_components.c b/tests/unit/igraph_biconnected_components.c new file mode 100644 index 0000000000..b8457b5a39 --- /dev/null +++ b/tests/unit/igraph_biconnected_components.c @@ -0,0 +1,63 @@ +/* + IGraph library. + Copyright (C) 2006-2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_vector_int_list_t result; + igraph_vector_int_t ap; + igraph_integer_t no; + + igraph_vector_int_list_init(&result, 0); + igraph_vector_int_init(&ap, 0); + igraph_small(&g, 10 /* extra isolated vertex */, IGRAPH_UNDIRECTED, + 0,1, 1,2, 2,3, 3,0, + 2,4, 4,5, 5,2, + 5,6, + 7,8, + -1); + + printf("Vertices in biconnected components:\n"); + igraph_biconnected_components(&g, &no, NULL, NULL, /* components */ &result, NULL); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Edges in biconnected components:\n"); + igraph_biconnected_components(&g, &no, NULL, /* component_edges */ &result, NULL, NULL); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Edges in biconnected component spanning trees:\n"); + igraph_biconnected_components(&g, &no, /* tree_edges */ &result, NULL, NULL, &ap); + IGRAPH_ASSERT(no == igraph_vector_int_list_size(&result)); + print_vector_int_list(&result); + + printf("Articulation points:\n"); + print_vector_int(&ap); + + igraph_destroy(&g); + igraph_vector_int_destroy(&ap); + igraph_vector_int_list_destroy(&result); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_biconnected_components.out b/tests/unit/igraph_biconnected_components.out new file mode 100644 index 0000000000..38ae9e99aa --- /dev/null +++ b/tests/unit/igraph_biconnected_components.out @@ -0,0 +1,23 @@ +Vertices in biconnected components: +{ + 0: ( 6 5 ) + 1: ( 5 4 2 ) + 2: ( 3 2 1 0 ) + 3: ( 8 7 ) +} +Edges in biconnected components: +{ + 0: ( 7 ) + 1: ( 6 5 4 ) + 2: ( 3 2 1 0 ) + 3: ( 8 ) +} +Edges in biconnected component spanning trees: +{ + 0: ( 7 ) + 1: ( 5 4 ) + 2: ( 2 1 0 ) + 3: ( 8 ) +} +Articulation points: +( 5 2 ) diff --git a/tests/unit/igraph_chung_lu_game.c b/tests/unit/igraph_chung_lu_game.c new file mode 100644 index 0000000000..6727f7288d --- /dev/null +++ b/tests/unit/igraph_chung_lu_game.c @@ -0,0 +1,178 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_vector_t outdeg, indeg; + igraph_bool_t simple, multi; + igraph_t g; + + igraph_rng_seed(igraph_rng_default(), 42); + + /* Zero-legth input */ + + igraph_vector_init(&outdeg, 0); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &outdeg, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_destroy(&g); + + igraph_vector_destroy(&outdeg); + + /* ORIGINAL */ + + /* Must use floating point literals here! 2.0 instead of 2 */ + igraph_vector_init_real_end(&outdeg, -1.0, + 1.0, 0.0, 2.5, 2.0, 3.0, 2.0, 1.5, + -1.0); + igraph_vector_init_real_end(&indeg, -1.0, + 2.0, 2.0, 2.0, 2.0, 0.0, 2.0, 2.0, + -1.0); + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_ORIGINAL); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); + + /* GRG */ + + /* Must use floating point literals here! 2.0 instead of 2 */ + igraph_vector_init_real_end(&outdeg, -1.0, + 189.0, 0.0, 2.5, 12.0, 3.0, 2.0, 1.5, + -1.0); + igraph_vector_init_real_end(&indeg, -1.0, + 2.0, 2.0, 2.0, 2.0, 0.0, 200.0, 2.0, + -1.0); + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_MAXENT); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + /* NR */ + + igraph_chung_lu_game(&g, &outdeg, NULL, false, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, false, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&g); + + igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_NR); + IGRAPH_ASSERT(igraph_is_directed(&g)); + IGRAPH_ASSERT(igraph_vcount(&g) == igraph_vector_size(&outdeg)); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(!multi); + igraph_destroy(&g); + + /* Invalid input */ + + /* Bad variant */ + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, &indeg, true, (igraph_chung_lu_t) -1), IGRAPH_EINVAL); + + /* Inconsistent sum */ + VECTOR(outdeg)[0] = 0; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, &indeg, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + /* Negative weight */ + VECTOR(outdeg)[0] = -1; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + /* Non-finite weight */ + VECTOR(outdeg)[0] = IGRAPH_INFINITY; + CHECK_ERROR(igraph_chung_lu_game(&g, &outdeg, NULL, true, IGRAPH_CHUNG_LU_ORIGINAL), IGRAPH_EINVAL); + + igraph_vector_destroy(&indeg); + igraph_vector_destroy(&outdeg); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_disjoint_union.c b/tests/unit/igraph_disjoint_union.c new file mode 100644 index 0000000000..4da98c8080 --- /dev/null +++ b/tests/unit/igraph_disjoint_union.c @@ -0,0 +1,69 @@ +/* -*- mode: C -*- */ +/* + IGraph library. + Copyright (C) 2006-2024 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t left, right, uni; + igraph_vector_ptr_t glist; + igraph_integer_t i, n; + + igraph_small(&left, 4, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,3, -1); + igraph_small(&right, 5, IGRAPH_UNDIRECTED, 0,1, 1,2, 2,2, 2,4, -1); + + igraph_disjoint_union(&uni, &left, &right); + print_graph(&uni); + printf("\n"); + + igraph_destroy(&left); + igraph_destroy(&right); + igraph_destroy(&uni); + + /* Empty graph list; the result is the directed null graph. */ + igraph_vector_ptr_init(&glist, 0); + igraph_disjoint_union_many(&uni, &glist); + print_graph(&uni); + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + /* Non-empty graph list. */ + igraph_vector_ptr_init(&glist, 10); + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); + igraph_small(VECTOR(glist)[i], 2, IGRAPH_DIRECTED, 0,1, 1,0, -1); + } + igraph_disjoint_union_many(&uni, &glist); + print_graph(&uni); + printf("\n"); + + /* Destroy and free the graph list. */ + n = igraph_vector_ptr_size(&glist); + for (i = 0; i < n; i++) { + igraph_destroy(VECTOR(glist)[i]); + free(VECTOR(glist)[i]); + } + igraph_vector_ptr_destroy(&glist); + igraph_destroy(&uni); + + return 0; +} diff --git a/tests/unit/igraph_disjoint_union.out b/tests/unit/igraph_disjoint_union.out new file mode 100644 index 0000000000..ed670b1fed --- /dev/null +++ b/tests/unit/igraph_disjoint_union.out @@ -0,0 +1,42 @@ +directed: false +vcount: 9 +edges: { +1 0 +2 1 +2 2 +3 2 +5 4 +6 5 +6 6 +8 6 +} + +directed: true +vcount: 0 +edges: { +} +directed: true +vcount: 20 +edges: { +0 1 +1 0 +2 3 +3 2 +4 5 +5 4 +6 7 +7 6 +8 9 +9 8 +10 11 +11 10 +12 13 +13 12 +14 15 +15 14 +16 17 +17 16 +18 19 +19 18 +} + diff --git a/tests/unit/igraph_ecc.c b/tests/unit/igraph_ecc.c index 3370965962..6a0775999d 100644 --- a/tests/unit/igraph_ecc.c +++ b/tests/unit/igraph_ecc.c @@ -180,6 +180,21 @@ int main(void) { test_ecc(&g); igraph_destroy(&g); + /* Large degrees */ + { + igraph_vector_int_t deg; + printf("\nGraph with large degrees:\n"); + igraph_vector_int_init_range(°, 0, 30); + for (igraph_integer_t i=0; i < igraph_vector_int_size(°); i++) { + VECTOR(deg)[i] /= 2; + VECTOR(deg)[i] += 1; + } + igraph_realize_degree_sequence(&g, °, NULL, IGRAPH_SIMPLE_SW, IGRAPH_REALIZE_DEGSEQ_INDEX); + test_ecc(&g); + igraph_destroy(&g); + igraph_vector_int_destroy(°); + } + /* Karate club */ printf("\nZachary karate club:\n"); diff --git a/tests/unit/igraph_ecc.out b/tests/unit/igraph_ecc.out index 7feea7b998..5f957e9587 100644 --- a/tests/unit/igraph_ecc.out +++ b/tests/unit/igraph_ecc.out @@ -58,6 +58,16 @@ k=4 ( 0.0625 0.25 0.25 0.0625 0.25 0.5 0.25 0 NaN NaN 0.125 ) ( 0.0625 0.25 0.25 0.0625 0.25 0.5 0.25 0 NaN NaN 0.125 ) +Graph with large degrees: +k=3 +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) +( NaN NaN 1 1 0 0 0 0.5 0.5 0 0.5 0.5 0.333333 0.666667 0.666667 1 0.666667 1 0.666667 1 0.5 1 0.75 1 0.75 0.75 1 1 0.75 0.5 0.8 1 0.8 0.6 0.4 0.4 0.8 0.6 0.6 0.6 0.4 0.2 0.666667 0.666667 0.5 0.5 0.5 0.166667 0.333333 0.666667 0.5 0.5 0.5 0.333333 0.166667 0.333333 0.571429 0.428571 0.571429 0.428571 0.428571 0.142857 0.428571 0.428571 0.428571 0.285714 0.428571 0.285714 0.428571 0.571429 0.428571 0.571429 0.5 0.25 0.25 0.25 0.25 0.375 0.625 0.625 0.625 0.75 0.375 0.625 0.5 0.375 0.222222 0.444444 0.444444 0.666667 0.444444 0.333333 0.555556 0.444444 0.333333 0.222222 0.7 0.7 0.2 0.6 0.3 0.2 0.3 0.6 0.7 0.818182 0.727273 0.727273 0.363636 0.363636 0.818182 0.727273 0.25 0.666667 0.5 0.416667 0.833333 0.230769 0.538462 0.5 ) + +k=4 +( NaN NaN 0.428571 0.428571 0.538462 0.538462 0.384615 0.5 0.321429 0.357143 0.541667 0.458333 0.285714 0.5 0.555556 0.410256 0.358974 0.47619 0.606061 0.545455 0.326923 0.464286 0.613636 0.590909 0.428571 0.4375 0.5625 0.5 0.575 0.525 0.383333 0.516667 0.507692 0.58 0.56 0.4 0.385714 0.581818 0.636364 0.457143 0.6 0.511111 0.369048 0.530303 0.590909 0.452381 0.555556 0.555556 0.375 0.430556 0.461538 0.516667 0.55 0.423077 0.583333 0.458333 0.392857 0.428571 0.457143 0.528571 0.428571 0.589286 0.482143 0.265306 0.428571 0.467532 0.387755 0.539683 0.52381 0.416667 0.380952 0.296703 0.397727 0.465909 0.375 0.513889 0.527778 0.375 0.275 0.2625 0.288462 0.330357 0.454545 0.465909 0.348214 0.458333 0.411111 0.307692 0.293651 0.234568 0.240741 0.444444 0.324074 0.358974 0.455556 0.367521 0.341667 0.292308 0.214286 0.341667 0.263636 0.327273 0.264286 0.325 0.246154 0.297521 0.24026 0.272727 0.227273 0.300699 0.207792 0.305195 0.269231 0.277778 0.307692 0.217949 0.282051 0.175824 0.192308 0.219388 ) +( NaN NaN 0.428571 0.428571 0.538462 0.538462 0.384615 0.5 0.321429 0.357143 0.541667 0.458333 0.285714 0.5 0.555556 0.410256 0.358974 0.47619 0.606061 0.545455 0.326923 0.464286 0.613636 0.590909 0.428571 0.4375 0.5625 0.5 0.575 0.525 0.383333 0.516667 0.507692 0.58 0.56 0.4 0.385714 0.581818 0.636364 0.457143 0.6 0.511111 0.369048 0.530303 0.590909 0.452381 0.555556 0.555556 0.375 0.430556 0.461538 0.516667 0.55 0.423077 0.583333 0.458333 0.392857 0.428571 0.457143 0.528571 0.428571 0.589286 0.482143 0.265306 0.428571 0.467532 0.387755 0.539683 0.52381 0.416667 0.380952 0.296703 0.397727 0.465909 0.375 0.513889 0.527778 0.375 0.275 0.2625 0.288462 0.330357 0.454545 0.465909 0.348214 0.458333 0.411111 0.307692 0.293651 0.234568 0.240741 0.444444 0.324074 0.358974 0.455556 0.367521 0.341667 0.292308 0.214286 0.341667 0.263636 0.327273 0.264286 0.325 0.246154 0.297521 0.24026 0.272727 0.227273 0.300699 0.207792 0.305195 0.269231 0.277778 0.307692 0.217949 0.282051 0.175824 0.192308 0.219388 ) + Zachary karate club: k=3 ( 0.875 0.555556 1 1 0.666667 0.666667 1 0.25 1 NaN 1 0.75 1 0.5 1 0 0.5 0.8 1 0.75 1 0.5 1 0 0.8 1 0 0 0.111111 0 0.5 0.75 1 1 0.75 0.5 0.5 0.666667 0.5 1 1 0.666667 0.75 0.5 0 0 1 1 1 1 1 1 0 1 1 1 1 0 0.333333 0.5 0.75 0.666667 0.5 0 0.5 0.5 1 1 0.333333 0.5 0.5 0.666667 1 0.666667 0.666667 0.2 0.4 0.909091 ) diff --git a/tests/unit/igraph_intersection2.c b/tests/unit/igraph_intersection.c similarity index 82% rename from tests/unit/igraph_intersection2.c rename to tests/unit/igraph_intersection.c index 5b97759035..9dced9d6d0 100644 --- a/tests/unit/igraph_intersection2.c +++ b/tests/unit/igraph_intersection.c @@ -32,12 +32,11 @@ int main(void) { igraph_vector_ptr_t glist; igraph_star(&star, 11, IGRAPH_STAR_UNDIRECTED, /*center=*/ 10); - igraph_ring(&ring, 10, IGRAPH_UNDIRECTED, /*mutual=*/ 0, /*circular=*/ 1); + igraph_ring(&ring, 10, IGRAPH_UNDIRECTED, /*mutual=*/ false, /*circular=*/ true); - igraph_union(&uni, &star, &ring, /*edge_map1=*/ 0, /*edge_map2=*/ 0); + igraph_union(&uni, &star, &ring, /*edge_map1=*/ NULL, /*edge_map2=*/ NULL); - igraph_intersection(&result, &uni, &star, /*edge_map1*/ 0, - /*edge_map2=*/ 0); + igraph_intersection(&result, &uni, &star, /*edge_map1*/ NULL, /*edge_map2=*/ NULL); print_graph_canon(&result); igraph_destroy(&result); @@ -48,7 +47,7 @@ int main(void) { VECTOR(glist)[0] = &uni; VECTOR(glist)[1] = ☆ - igraph_intersection_many(&result, &glist, /*edgemaps=*/ 0); + igraph_intersection_many(&result, &glist, /*edgemaps=*/ NULL); printf("--\n"); print_graph_canon(&result); diff --git a/tests/unit/igraph_intersection2.out b/tests/unit/igraph_intersection.out similarity index 100% rename from tests/unit/igraph_intersection2.out rename to tests/unit/igraph_intersection.out diff --git a/tests/unit/igraph_is_bigraphical.c b/tests/unit/igraph_is_bigraphical.c index e5dc7eaefb..95ee5a5c17 100644 --- a/tests/unit/igraph_is_bigraphical.c +++ b/tests/unit/igraph_is_bigraphical.c @@ -40,6 +40,14 @@ int main(void) { igraph_vector_int_init_int_end(°2, -1, 2, 3, 1, -1); BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + igraph_vector_int_init_int_end(°1, -1, 5, 2, -1); + igraph_vector_int_init_int_end(°2, -1, 1, 2, 2, 2, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + + igraph_vector_int_init_int_end(°1, -1, -2, 2, 6, -1); + igraph_vector_int_init_int_end(°2, -1, 0, 2, 2, 2, -1); + BIGRAPHICAL_PRINT_DESTROY(deg1, deg2); + VERIFY_FINALLY_STACK(); return 0; diff --git a/tests/unit/igraph_is_bigraphical.out b/tests/unit/igraph_is_bigraphical.out index 9b9ee5a619..1533667184 100644 --- a/tests/unit/igraph_is_bigraphical.out +++ b/tests/unit/igraph_is_bigraphical.out @@ -22,3 +22,11 @@ simple: true, multi: true ( 2 3 1 ) simple: true, multi: true +( 5 2 ) +( 1 2 2 2 ) +simple: false, multi: true + +( -2 2 6 ) +( 0 2 2 2 ) +simple: false, multi: false + diff --git a/tests/unit/igraph_is_bipartite.c b/tests/unit/igraph_is_bipartite.c index bfd54d9093..f608098fc0 100644 --- a/tests/unit/igraph_is_bipartite.c +++ b/tests/unit/igraph_is_bipartite.c @@ -50,6 +50,7 @@ int main(void) { IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); /* Test cache usage */ + bipartite = false; igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); igraph_is_bipartite(&graph, &bipartite, NULL); IGRAPH_ASSERT(bipartite); @@ -63,6 +64,7 @@ int main(void) { IGRAPH_ASSERT(igraph_vector_bool_size(&types) == igraph_vcount(&graph)); /* Test cache usage */ + bipartite = true; igraph_is_forest(&graph, &acyclic, NULL, IGRAPH_ALL); igraph_is_bipartite(&graph, &bipartite, NULL); IGRAPH_ASSERT(! bipartite); diff --git a/tests/unit/igraph_is_clique.c b/tests/unit/igraph_is_clique.c new file mode 100644 index 0000000000..01e7f718d1 --- /dev/null +++ b/tests/unit/igraph_is_clique.c @@ -0,0 +1,145 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +int main(void) { + igraph_t g; + igraph_bool_t is_clique, is_indep_set; + igraph_vector_int_t vids; + + igraph_small(&g, 10, IGRAPH_DIRECTED, + 0, 2, 0, 3, 0, 4, 0, 6, 0, 7, 0, 8, 0, 9, 1, 2, 1, 4, 2, 1, 2, 3, 2, \ + 7, 2, 8, 2, 9, 3, 0, 3, 1, 3, 2, 3, 4, 3, 5, 3, 6, 3, 7, 3, 9, 4, 0, \ + 4, 2, 4, 3, 4, 5, 4, 6, 5, 1, 5, 2, 5, 4, 5, 7, 5, 9, 6, 0, 6, 1, 6, \ + 4, 6, 8, 7, 2, 7, 3, 7, 6, 7, 9, 8, 2, 8, 4, 8, 6, 8, 7, 9, 0, 9, 2, \ + 9, 3, 9, 4, 9, 7, 9, 8, + -1); + + /* Empty set */ + igraph_vector_int_init(&vids, 0); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Singleton set */ + is_clique = false; + igraph_is_clique(&g, igraph_vss_1(0), true, &is_clique); + IGRAPH_ASSERT(is_clique); + + igraph_vector_int_init_int_end(&vids, -1, + 1, 2, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* It is a clique: Duplicates handled? */ + igraph_vector_int_init_int_end(&vids, -1, + 1, 2, 2, 2, 1, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, + 9, 2, 7, 3, -1); + is_clique = false; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Not a clique, in either a directed or undirected sense. */ + igraph_vector_int_init_int_end(&vids, -1, + 3, 8, 5, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), false, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_vector_int_destroy(&vids); + + /* Not a clique: Duplicates handled? */ + igraph_vector_int_init_int_end(&vids, -1, + 5, 3, 4, 3, 4, 5, 5, 5, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_vector_int_destroy(&vids); + + /* This is only an undirected clique, but not a directed one. */ + igraph_vector_int_init_int_end(&vids, -1, + 4, 0, 8, 9, 2, -1); + is_clique = true; + igraph_is_clique(&g, igraph_vss_vector(&vids), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + igraph_is_clique(&g, igraph_vss_vector(&vids), false, &is_clique); + IGRAPH_ASSERT(is_clique); + igraph_vector_int_destroy(&vids); + + /* Complete vertex set */ + + is_clique = true; + igraph_is_clique(&g, igraph_vss_all(), true, &is_clique); + IGRAPH_ASSERT(! is_clique); + + is_clique = true; + igraph_is_clique(&g, igraph_vss_all(), false, &is_clique); + IGRAPH_ASSERT(! is_clique); + + /* Independent sets */ + + /* Empty set */ + igraph_vector_int_init(&vids, 0); + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + igraph_vector_int_destroy(&vids); + + /* Singleton set */ + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_1(5), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + + igraph_vector_int_init_int_end(&vids, -1, + 6, 9, -1); + is_indep_set = false; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(is_indep_set); + igraph_vector_int_destroy(&vids); + + igraph_vector_int_init_int_end(&vids, -1, + 5, 6, 9, -1); + is_indep_set = true; + igraph_is_independent_vertex_set(&g, igraph_vss_vector(&vids), &is_indep_set); + IGRAPH_ASSERT(! is_indep_set); + igraph_vector_int_destroy(&vids); + + is_indep_set = true; + igraph_is_independent_vertex_set(&g, igraph_vss_all(), &is_indep_set); + IGRAPH_ASSERT(! is_indep_set); + + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_is_complete.c b/tests/unit/igraph_is_complete.c index 38b594e2da..be7a84705a 100644 --- a/tests/unit/igraph_is_complete.c +++ b/tests/unit/igraph_is_complete.c @@ -201,5 +201,7 @@ int main(void) check(true, 50); check(true, 51); + VERIFY_FINALLY_STACK(); + return 0; } diff --git a/tests/unit/igraph_is_graphical.c b/tests/unit/igraph_is_graphical.c index ac3f0714c8..ef1b5e84c9 100644 --- a/tests/unit/igraph_is_graphical.c +++ b/tests/unit/igraph_is_graphical.c @@ -137,6 +137,22 @@ int main(void) { igraph_vector_int_init_int_end(&ds, -1, 1, 1, 4, -1); graphical_print_destroy(&ds); + /* Extra cases for undirected simple with single loops */ + igraph_vector_int_init_int_end(&ds, -1, 7, 7, 3, 2, 2, 1, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 7, 7, 3, 3, 2, 2, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 2, 0, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 4, 0, -1); + graphical_print_destroy(&ds); + + igraph_vector_int_init_int_end(&ds, -1, 6, 6, 6, 4, 3, 1, -1); + graphical_print_destroy(&ds); + /* The following two sequences are realizable as simple graphs. * The algorithm that checks this exits the last loop with these * two sequences. An earlier buggy version of the function failed diff --git a/tests/unit/igraph_is_graphical.out b/tests/unit/igraph_is_graphical.out index f4d3e78fe7..a255da27fa 100644 --- a/tests/unit/igraph_is_graphical.out +++ b/tests/unit/igraph_is_graphical.out @@ -49,6 +49,21 @@ simple: false, loops: false, multi: false, multiloops: true ( 1 1 4 ) simple: false, loops: true, multi: false, multiloops: true +( 7 7 3 2 2 1 ) +simple: false, loops: false, multi: true, multiloops: true + +( 7 7 3 3 2 2 ) +simple: false, loops: true, multi: true, multiloops: true + +( 6 6 6 4 2 0 ) +simple: false, loops: false, multi: true, multiloops: true + +( 6 6 6 4 4 0 ) +simple: false, loops: true, multi: true, multiloops: true + +( 6 6 6 4 3 1 ) +simple: false, loops: true, multi: true, multiloops: true + ( 2 2 2 2 4 ) simple: true, loops: true, multi: true, multiloops: true diff --git a/tests/unit/igraph_maximal_cliques.c b/tests/unit/igraph_maximal_cliques.c index ef83dd3c53..ea38ea4533 100644 --- a/tests/unit/igraph_maximal_cliques.c +++ b/tests/unit/igraph_maximal_cliques.c @@ -95,7 +95,7 @@ int main(void) { igraph_destroy(&g); g = g2; } - igraph_simplify(&g, /*multiple=*/ true, /*loop=*/ false, /*edge_comb=*/ NULL); + igraph_simplify(&g, /*remove_multiple=*/ true, /*remove_loop=*/ false, /*edge_comb=*/ NULL); igraph_vector_int_destroy(&perm); igraph_destroy(&cli); diff --git a/tests/unit/igraph_mean_degree.c b/tests/unit/igraph_mean_degree.c new file mode 100644 index 0000000000..703a419c8d --- /dev/null +++ b/tests/unit/igraph_mean_degree.c @@ -0,0 +1,59 @@ +/* + IGraph library. + Copyright (C) 2024 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 +#include + +#include "test_utilities.h" + +int main(void) { + igraph_t graph; + igraph_real_t k; + + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(isnan(k)); + igraph_destroy(&graph); + + igraph_empty(&graph, 10, IGRAPH_UNDIRECTED); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 0); + igraph_destroy(&graph); + + igraph_ring(&graph, 5, IGRAPH_DIRECTED, false, true); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 1); + igraph_destroy(&graph); + + igraph_ring(&graph, 5, IGRAPH_UNDIRECTED, false, true); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 2); + igraph_destroy(&graph); + + igraph_small(&graph, 0, IGRAPH_UNDIRECTED, + 0,1, 1,1, -1); + igraph_mean_degree(&graph, &k, true); + IGRAPH_ASSERT(k == 2); + igraph_mean_degree(&graph, &k, false); + IGRAPH_ASSERT(k == 1); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_modularity.c b/tests/unit/igraph_modularity.c index 4c8e6d16b4..6eb0bd49f1 100644 --- a/tests/unit/igraph_modularity.c +++ b/tests/unit/igraph_modularity.c @@ -95,7 +95,7 @@ int main(void) { igraph_contract_vertices(&graph, &membership, NULL); igraph_vector_int_destroy(&membership); - igraph_simplify(&graph, /* multiple */ true, /* loops */ false, &comb); + igraph_simplify(&graph, /* remove_multiple */ true, /* remove_loops */ false, &comb); igraph_vector_int_init_range(&membership, 0, igraph_vcount(&graph)); EANV(&graph, "weight", &weights); diff --git a/tests/unit/igraph_preference_game.c b/tests/unit/igraph_preference_game.c index 29feabc0b6..341d8b073d 100644 --- a/tests/unit/igraph_preference_game.c +++ b/tests/unit/igraph_preference_game.c @@ -25,25 +25,12 @@ #include "test_utilities.h" -/* How many "true" elements in the Boolean vector? */ -igraph_integer_t vector_bool_count(const igraph_vector_bool_t *vec) { - igraph_integer_t i, n = igraph_vector_bool_size(vec), cnt = 0; - - for (i=0; i < n; ++i) { - if (VECTOR(*vec)[i]) { - cnt++; - } - } - return cnt; -} - int main(void) { igraph_t g; igraph_vector_t type_dist; igraph_matrix_t pref_mat, type_dist_mat; igraph_vector_int_t types, out_types, in_types; igraph_bool_t connected, has_loop, has_multi; - igraph_vector_bool_t is_loop; igraph_integer_t i, j, count; igraph_vector_int_init(&types, 0); @@ -217,10 +204,8 @@ int main(void) { IGRAPH_ASSERT(igraph_ecount(&g) == 10000); IGRAPH_ASSERT(igraph_is_directed(&g)); - igraph_vector_bool_init(&is_loop, 0); - igraph_is_loop(&g, &is_loop, igraph_ess_all(IGRAPH_EDGEORDER_ID)); - IGRAPH_ASSERT(vector_bool_count(&is_loop) == 100); - igraph_vector_bool_destroy(&is_loop); + igraph_count_loops(&g, &count); + IGRAPH_ASSERT(count == 100); igraph_has_multiple(&g, &has_multi); IGRAPH_ASSERT(! has_multi); diff --git a/tests/unit/igraph_sparsemat_which_minmax.c b/tests/unit/igraph_sparsemat_which_minmax.c index 02f86f693b..d757034235 100644 --- a/tests/unit/igraph_sparsemat_which_minmax.c +++ b/tests/unit/igraph_sparsemat_which_minmax.c @@ -140,10 +140,9 @@ int doit(int which) { printf("Random triplet matrix\n"); igraph_sparsemat_init(&A, /*rows=*/ N, /*cols=*/ M, /*nzmax=*/ NZ + 5); for (i = 0; i < NZ; i++) { - int r = igraph_rng_get_integer(igraph_rng_default(), 0, N - 1); - int c = igraph_rng_get_integer(igraph_rng_default(), 0, M - 1); - igraph_real_t x = igraph_rng_get_integer(igraph_rng_default(), - -10, 10); + igraph_integer_t r = RNG_INTEGER(0, N-1); + igraph_integer_t c = RNG_INTEGER(0, M-1); + igraph_real_t x = RNG_INTEGER(-10, 10); IGRAPH_ASSERT(x >= -10 && x <= 10); igraph_sparsemat_entry(&A, r, c, x); } diff --git a/tests/unit/igraph_square_lattice.c b/tests/unit/igraph_square_lattice.c index d71f842fad..26f5cac3b2 100644 --- a/tests/unit/igraph_square_lattice.c +++ b/tests/unit/igraph_square_lattice.c @@ -181,5 +181,52 @@ int main(void) { VERIFY_FINALLY_STACK(); + /* Compare to the hypercube graph. */ + { + igraph_t g1, g2; + igraph_vector_int_t dims; + igraph_bool_t iso; + + /* Q_1 is the singleton graph */ + + igraph_hypercube(&g1, 0, IGRAPH_UNDIRECTED); + IGRAPH_ASSERT(igraph_vcount(&g1) == 1); + IGRAPH_ASSERT(igraph_ecount(&g1) == 0); + IGRAPH_ASSERT(!igraph_is_directed(&g1)); + igraph_destroy(&g1); + + /* Q_4 undirected */ + + igraph_vector_int_init(&dims, 4); + igraph_vector_int_fill(&dims, 2); + + igraph_square_lattice(&g2, &dims, 1, IGRAPH_UNDIRECTED, false, NULL); + igraph_hypercube(&g1, 4, IGRAPH_UNDIRECTED); + igraph_isomorphic(&g1, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_vector_int_destroy(&dims); + + /* Q_5 directed */ + + igraph_vector_int_init(&dims, 5); + igraph_vector_int_fill(&dims, 2); + + igraph_square_lattice(&g2, &dims, 1, IGRAPH_DIRECTED, false, NULL); + igraph_hypercube(&g1, 5, IGRAPH_DIRECTED); + igraph_isomorphic(&g1, &g2, &iso); + IGRAPH_ASSERT(iso); + igraph_destroy(&g1); + igraph_destroy(&g2); + + igraph_vector_int_destroy(&dims); + + CHECK_ERROR(igraph_hypercube(&g1, 128, false), IGRAPH_EINVAL); + } + + VERIFY_FINALLY_STACK(); + return 0; } diff --git a/tests/unit/igraph_transitive_closure.c b/tests/unit/igraph_transitive_closure.c new file mode 100644 index 0000000000..45a0011ab7 --- /dev/null +++ b/tests/unit/igraph_transitive_closure.c @@ -0,0 +1,88 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +void print_closure(const igraph_t *graph) { + igraph_t closure; + igraph_bool_t simple; + + igraph_transitive_closure(graph, &closure); + print_graph_canon(&closure); + IGRAPH_ASSERT(igraph_vcount(graph) == igraph_vcount(&closure)); + IGRAPH_ASSERT(igraph_is_directed(graph) == igraph_is_directed(&closure)); + igraph_is_simple(&closure, &simple); + IGRAPH_ASSERT(simple); + igraph_destroy(&closure); +} + +int main(void) { + igraph_t graph; + + printf("Directed null graph\n"); + igraph_empty(&graph, 0, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nUndirected null graph\n"); + igraph_empty(&graph, 0, IGRAPH_UNDIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nDirected singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nUndirected singleton graph\n"); + igraph_empty(&graph, 1, IGRAPH_UNDIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nEdgeless graph\n"); + igraph_empty(&graph, 3, IGRAPH_DIRECTED); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall DAG\n"); + igraph_small(&graph, 9, IGRAPH_DIRECTED, + 8, 7, 7, 6, 6, 3, 6, 0, 3, 2, 3, 1, 5, 0, 4, 1, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall directed multigraph with cycles \n"); + igraph_small(&graph, 10, IGRAPH_DIRECTED, + 0,1, 1,2, 2,0, 2,0, 0,3, 3,4, 4,3, 3,3, 0,5, 5,6, 6,6, 8,7, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + printf("\nSmall undirected graph\n"); + igraph_small(&graph, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 3,4, + -1); + print_closure(&graph); + igraph_destroy(&graph); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/igraph_transitive_closure.out b/tests/unit/igraph_transitive_closure.out new file mode 100644 index 0000000000..087b94a5f4 --- /dev/null +++ b/tests/unit/igraph_transitive_closure.out @@ -0,0 +1,92 @@ +Directed null graph +directed: true +vcount: 0 +edges: { +} + +Undirected null graph +directed: false +vcount: 0 +edges: { +} + +Directed singleton graph +directed: true +vcount: 1 +edges: { +} + +Undirected singleton graph +directed: false +vcount: 1 +edges: { +} + +Edgeless graph +directed: true +vcount: 3 +edges: { +} + +Small DAG +directed: true +vcount: 9 +edges: { +3 1 +3 2 +4 1 +5 0 +6 0 +6 1 +6 2 +6 3 +7 0 +7 1 +7 2 +7 3 +7 6 +8 0 +8 1 +8 2 +8 3 +8 6 +8 7 +} + +Small directed multigraph with cycles +directed: true +vcount: 10 +edges: { +0 1 +0 2 +0 3 +0 4 +0 5 +0 6 +1 0 +1 2 +1 3 +1 4 +1 5 +1 6 +2 0 +2 1 +2 3 +2 4 +2 5 +2 6 +3 4 +4 3 +5 6 +8 7 +} + +Small undirected graph +directed: false +vcount: 6 +edges: { +0 1 +0 2 +1 2 +3 4 +} diff --git a/tests/unit/igraph_transitive_closure_dag.c b/tests/unit/igraph_transitive_closure_dag.c deleted file mode 100644 index 738830bde2..0000000000 --- a/tests/unit/igraph_transitive_closure_dag.c +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- mode: C -*- */ -/* - IGraph library. - Copyright (C) 2010-2012 Gabor Csardi - 334 Harvard st, 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 - -#include "test_utilities.h" - -int main(void) { - - igraph_t g, g2; - igraph_vector_int_t deg; - - igraph_small(&g, 9, IGRAPH_DIRECTED, - 8, 7, 7, 6, 6, 3, 6, 0, 3, 2, 3, 1, 5, 0, 4, 1, - -1); - igraph_transitive_closure_dag(&g, &g2); - - if (igraph_vcount(&g2) != igraph_vcount(&g)) { - return 1; - } - if (igraph_ecount(&g2) != 19) { - return 1; - } - - igraph_vector_int_init(°, 0); - igraph_degree(&g2, °, igraph_vss_all(), IGRAPH_IN, IGRAPH_LOOPS); - igraph_vector_int_print(°); - igraph_degree(&g2, °, igraph_vss_all(), IGRAPH_OUT, IGRAPH_LOOPS); - igraph_vector_int_print(°); - - igraph_vector_int_destroy(°); - igraph_destroy(&g2); - igraph_destroy(&g); - - VERIFY_FINALLY_STACK(); - - return 0; -} diff --git a/tests/unit/igraph_transitive_closure_dag.out b/tests/unit/igraph_transitive_closure_dag.out deleted file mode 100644 index 051c1a0c8b..0000000000 --- a/tests/unit/igraph_transitive_closure_dag.out +++ /dev/null @@ -1,2 +0,0 @@ -4 5 4 3 0 0 2 1 0 -0 0 0 2 1 1 4 5 6 diff --git a/tests/unit/igraph_union.c b/tests/unit/igraph_union.c index ab0f71b621..9b5c0a1ab2 100644 --- a/tests/unit/igraph_union.c +++ b/tests/unit/igraph_union.c @@ -23,14 +23,14 @@ #include "test_utilities.h" -void print_and_clear_vector_int_list(igraph_vector_int_list_t *list) { - igraph_integer_t l = igraph_vector_int_list_size(list); - printf("---\n"); - for (igraph_integer_t i = 0; i < l; i++) { - igraph_vector_int_print(igraph_vector_int_list_get_ptr(list, i)); - } +void print_and_destroy_graph_and_maps(igraph_t *uni, igraph_vector_int_list_t *list) { + printf("Union graph:\n"); + print_graph(uni); + igraph_destroy(uni); + + printf("Edge maps:\n"); + print_vector_int_list(list); igraph_vector_int_list_clear(list); - printf("===\n"); } int main(void) { @@ -40,6 +40,8 @@ int main(void) { igraph_vector_int_t edge_map1, edge_map2; igraph_vector_int_list_t edgemaps; + printf("BINARY VERSION\n"); + igraph_vector_int_init(&edge_map1, 0); igraph_vector_int_init(&edge_map2, 0); @@ -52,9 +54,11 @@ int main(void) { -1); igraph_union(&uni, &left, &right, &edge_map1, &edge_map2); - igraph_write_graph_edgelist(&uni, stdout); - igraph_vector_int_print(&edge_map1); - igraph_vector_int_print(&edge_map2); + printf("Union graph:\n"); + print_graph(&uni); + printf("Edge maps:\n"); + print_vector_int(&edge_map1); + print_vector_int(&edge_map2); igraph_destroy(&uni); igraph_destroy(&left); @@ -63,18 +67,20 @@ int main(void) { igraph_vector_int_destroy(&edge_map1); igraph_vector_int_destroy(&edge_map2); + printf("\n\nN-ARY VERSION\n"); + /* Empty graph list */ + + printf("\nEmpty graph list:\n"); igraph_vector_ptr_init(&glist, 0); igraph_vector_int_list_init(&edgemaps, 0); igraph_union_many(&uni, &glist, &edgemaps); - if (!igraph_is_directed(&uni) || igraph_vcount(&uni) != 0) { - return 1; - } - print_and_clear_vector_int_list(&edgemaps); + print_and_destroy_graph_and_maps(&uni, &edgemaps); igraph_vector_ptr_destroy(&glist); - igraph_destroy(&uni); /* Non-empty graph list */ + + printf("\nNon-empty directed graph list 1:\n"); igraph_vector_ptr_init(&glist, 10); for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); @@ -83,17 +89,19 @@ int main(void) { } igraph_union_many(&uni, &glist, &edgemaps); - igraph_write_graph_edgelist(&uni, stdout); for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { igraph_destroy(VECTOR(glist)[i]); IGRAPH_FREE(VECTOR(glist)[i]); } - print_and_clear_vector_int_list(&edgemaps); + + print_and_destroy_graph_and_maps(&uni, &edgemaps); igraph_vector_ptr_destroy(&glist); - igraph_destroy(&uni); /* Another non-empty graph list */ + + printf("\nNon-empty directed graph list 2:\n"); + igraph_vector_ptr_init(&glist, 10); for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); @@ -111,11 +119,14 @@ int main(void) { igraph_destroy(VECTOR(glist)[i]); IGRAPH_FREE(VECTOR(glist)[i]); } - print_and_clear_vector_int_list(&edgemaps); + + print_and_destroy_graph_and_maps(&uni, &edgemaps); igraph_vector_ptr_destroy(&glist); - igraph_destroy(&uni); /* Undirected graph list*/ + + printf("\nUndirected graph list:\n"); + igraph_vector_ptr_init(&glist, 10); for (igraph_integer_t i = 0; i < igraph_vector_ptr_size(&glist); i++) { VECTOR(glist)[i] = IGRAPH_CALLOC(1, igraph_t); @@ -133,9 +144,9 @@ int main(void) { igraph_destroy(VECTOR(glist)[i]); IGRAPH_FREE(VECTOR(glist)[i]); } - print_and_clear_vector_int_list(&edgemaps); + + print_and_destroy_graph_and_maps(&uni, &edgemaps); igraph_vector_ptr_destroy(&glist); - igraph_destroy(&uni); igraph_vector_int_list_destroy(&edgemaps); diff --git a/tests/unit/igraph_union.out b/tests/unit/igraph_union.out index c7978d7b0f..e97d17cf93 100644 --- a/tests/unit/igraph_union.out +++ b/tests/unit/igraph_union.out @@ -1,26 +1,54 @@ +BINARY VERSION +Union graph: +directed: true +vcount: 5 +edges: { 0 1 1 2 2 2 2 3 2 4 -0 1 2 3 -0 1 2 4 ---- -=== -0 1 -1 0 ---- -1 0 -1 0 -1 0 -1 0 -1 0 +} +Edge maps: +( 0 1 2 3 ) +( 0 1 2 4 ) + + +N-ARY VERSION + +Empty graph list: +Union graph: +directed: true +vcount: 0 +edges: { +} +Edge maps: +{ +} + +Non-empty directed graph list 1: +Union graph: +directed: true +vcount: 2 +edges: { 1 0 -1 0 -1 0 -1 0 -1 0 -=== +0 1 +} +Edge maps: +{ + 0: ( 1 0 ) + 1: ( 1 0 ) + 2: ( 1 0 ) + 3: ( 1 0 ) + 4: ( 1 0 ) + 5: ( 1 0 ) + 6: ( 1 0 ) + 7: ( 1 0 ) + 8: ( 1 0 ) + 9: ( 1 0 ) +} + +Non-empty directed graph list 2: 0 1 1 0 1 2 @@ -32,18 +60,37 @@ 7 8 8 9 9 10 ---- -10 9 +Union graph: +directed: true +vcount: 11 +edges: { +9 10 8 9 -7 9 -6 9 -5 9 -4 9 -3 9 -2 9 -1 9 -0 9 -=== +7 8 +6 7 +5 6 +4 5 +3 4 +2 3 +1 2 +1 0 +0 1 +} +Edge maps: +{ + 0: ( 10 9 ) + 1: ( 8 9 ) + 2: ( 7 9 ) + 3: ( 6 9 ) + 4: ( 5 9 ) + 5: ( 4 9 ) + 6: ( 3 9 ) + 7: ( 2 9 ) + 8: ( 1 9 ) + 9: ( 0 9 ) +} + +Undirected graph list: 0 1 0 1 1 2 @@ -55,15 +102,32 @@ 7 8 8 9 9 10 ---- +Union graph: +directed: false +vcount: 11 +edges: { 10 9 -8 9 -7 9 -6 9 -5 9 -4 9 -3 9 -2 9 -1 9 -0 9 -=== +9 8 +8 7 +7 6 +6 5 +5 4 +4 3 +3 2 +2 1 +1 0 +1 0 +} +Edge maps: +{ + 0: ( 10 9 ) + 1: ( 8 9 ) + 2: ( 7 9 ) + 3: ( 6 9 ) + 4: ( 5 9 ) + 5: ( 4 9 ) + 6: ( 3 9 ) + 7: ( 2 9 ) + 8: ( 1 9 ) + 9: ( 0 9 ) +} diff --git a/tests/unit/igraph_weighted_adjacency.c b/tests/unit/igraph_weighted_adjacency.c index aca6c229a8..25b15e86e5 100644 --- a/tests/unit/igraph_weighted_adjacency.c +++ b/tests/unit/igraph_weighted_adjacency.c @@ -226,6 +226,86 @@ int main(void) { VERIFY_FINALLY_STACK(); + { + igraph_real_t elem[] = { 0, 1.5, IGRAPH_INFINITY, + IGRAPH_NAN, -IGRAPH_INFINITY, -5.2, + IGRAPH_NAN, 0, IGRAPH_NAN }; + + igraph_real_t elem_sym[] = { 0, IGRAPH_NAN, IGRAPH_INFINITY, + IGRAPH_NAN, -IGRAPH_INFINITY, 0, + IGRAPH_INFINITY, 0, IGRAPH_NAN }; + + igraph_matrix_t am; + igraph_vector_t weights; + igraph_t graph; + + printf("\nTesting NaN and Inf passthrough\n"); + + igraph_vector_init(&weights, 0); + + matrix_init_real_row_major(&am, 3, 3, elem); + + printf("\nIGRAPH_ADJ_DIRECTED\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_DIRECTED, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_MAX\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_MAX, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_MIN\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_MIN, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_LOWER\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_LOWER, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_UPPER\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UPPER, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + printf("\nIGRAPH_ADJ_PLUS\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_PLUS, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + /* Must detect that the matrix is not symmetric and throw an error. */ + CHECK_ERROR( + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UNDIRECTED, &weights, IGRAPH_LOOPS_TWICE), + IGRAPH_EINVAL); + + igraph_matrix_destroy(&am); + + matrix_init_real_row_major(&am, 3, 3, elem_sym); + + /* Must detect that the matrix is symmetric (desite the NaNs) and succeed. */ + printf("\nIGRAPH_ADJ_UNDIRECTED (symmetric)\n"); + igraph_weighted_adjacency(&graph, &am, IGRAPH_ADJ_UNDIRECTED, &weights, IGRAPH_LOOPS_TWICE); + print_graph(&graph); + print_vector(&weights); + igraph_destroy(&graph); + + igraph_matrix_destroy(&am); + + igraph_vector_destroy(&weights); + + printf("\n"); + } + + VERIFY_FINALLY_STACK(); + { printf("Check handling of non-square matrix error.\n"); igraph_real_t e[] = {1, 2, 0}; diff --git a/tests/unit/igraph_weighted_adjacency.out b/tests/unit/igraph_weighted_adjacency.out index c2dafc8cb7..394427e469 100644 --- a/tests/unit/igraph_weighted_adjacency.out +++ b/tests/unit/igraph_weighted_adjacency.out @@ -291,6 +291,92 @@ edges: { 2 2: 2 } + +Testing NaN and Inf passthrough + +IGRAPH_ADJ_DIRECTED +directed: true +vcount: 3 +edges: { +0 1 +0 2 +1 0 +1 1 +1 2 +2 0 +2 2 +} +( 1.5 Inf NaN -Inf -5.2 NaN NaN ) + +IGRAPH_ADJ_MAX +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 2 +} +( NaN NaN -Inf NaN ) + +IGRAPH_ADJ_MIN +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( NaN NaN -Inf -5.2 NaN ) + +IGRAPH_ADJ_LOWER +directed: false +vcount: 3 +edges: { +1 0 +1 1 +2 0 +2 2 +} +( NaN -Inf NaN NaN ) + +IGRAPH_ADJ_UPPER +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( 1.5 Inf -Inf -5.2 NaN ) + +IGRAPH_ADJ_PLUS +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 1 +2 2 +} +( NaN NaN -Inf -5.2 NaN ) + +IGRAPH_ADJ_UNDIRECTED (symmetric) +directed: false +vcount: 3 +edges: { +1 0 +2 0 +1 1 +2 2 +} +( NaN Inf -Inf NaN ) + Check handling of non-square matrix error. Check handling of invalid adjacency mode. Check error for 0x1 matrix. diff --git a/tests/unit/pajek.c b/tests/unit/pajek.c index c2545af236..78d684f2b7 100644 --- a/tests/unit/pajek.c +++ b/tests/unit/pajek.c @@ -27,6 +27,7 @@ int main(void) { igraph_t g; + igraph_bool_t simple; FILE *ifile; /* turn on attribute handling */ @@ -67,6 +68,38 @@ int main(void) { igraph_destroy(&g); + /* File in Arcslist format */ + ifile = fopen("pajek_arcslist.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 18); + IGRAPH_ASSERT(igraph_ecount(&g) == 55); + IGRAPH_ASSERT(igraph_is_directed(&g)); + + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + + /* File in Edgeslist format */ + ifile = fopen("pajek_edgeslist.net", "r"); + IGRAPH_ASSERT(ifile != NULL); + + igraph_read_graph_pajek(&g, ifile); + fclose(ifile); + + IGRAPH_ASSERT(igraph_vcount(&g) == 3); + IGRAPH_ASSERT(igraph_ecount(&g) == 3); + IGRAPH_ASSERT(! igraph_is_directed(&g)); + + igraph_is_simple(&g, &simple); + IGRAPH_ASSERT(simple); + + igraph_destroy(&g); + VERIFY_FINALLY_STACK(); return 0; diff --git a/tests/unit/pajek_arcslist.net b/tests/unit/pajek_arcslist.net new file mode 100644 index 0000000000..366de7e28f --- /dev/null +++ b/tests/unit/pajek_arcslist.net @@ -0,0 +1,40 @@ +This is a simplified version of http://mrvar.fdv.uni-lj.si/pajek/data/sampsonl.net + +*Vertices 18 +1 "ROMUL_10" +2 "BONAVEN_5" +3 "AMBROSE_9" +4 "BERTH_6" +5 "PETER_4" +6 "LOUIS_11" +7 "VICTOR_8" +8 "WINF_12" +9 "JOHN_1" +10 "GREG_2" +11 "HUGH_14" +12 "BONI_15" +13 "MARK_7" +14 "ALBERT_16" +15 "AMAND_13" +16 "BASIL_3" +17 "ELIAS_17" +18 "SIMP_18" +*Arcslist +1 3 5 14 +2 1 7 14 +3 1 2 17 +4 5 6 10 +5 4 11 13 +6 1 4 9 +7 2 8 16 +8 1 2 9 +9 5 8 16 +10 4 8 14 +11 5 8 14 +12 1 2 14 +13 5 7 18 +14 1 11 12 15 +15 1 2 14 +16 1 2 7 +17 3 13 18 +18 1 2 7 diff --git a/tests/unit/pajek_edgeslist.net b/tests/unit/pajek_edgeslist.net new file mode 100644 index 0000000000..4ccf5c186a --- /dev/null +++ b/tests/unit/pajek_edgeslist.net @@ -0,0 +1,8 @@ +*Network "C_3 cycle graph" +*Vertices 3 +1 "Alice" +2 "Bob" +3 "Cedric" +*Edgeslist +1 2 3 +2 3 diff --git a/tests/unit/prop_caching.c b/tests/unit/prop_caching.c index d0ead8b682..44b356dad7 100644 --- a/tests/unit/prop_caching.c +++ b/tests/unit/prop_caching.c @@ -133,10 +133,27 @@ int test_basic_operations_undirected(void) { return 0; } +int test_multi_loops_adjlist_init(void) { + igraph_t g; + igraph_adjlist_t al; + igraph_bool_t multi; + + igraph_small(&g, 1, IGRAPH_UNDIRECTED, + 0,0, 0,0, -1); + igraph_adjlist_init(&g, &al, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); + igraph_has_multiple(&g, &multi); + IGRAPH_ASSERT(multi); + igraph_adjlist_destroy(&al); + igraph_destroy(&g); + + return 0; +} + int main(void) { RUN_TEST(test_basic_operations_directed); RUN_TEST(test_basic_operations_undirected); + RUN_TEST(test_multi_loops_adjlist_init); return 0; } diff --git a/tests/unit/reachability.c b/tests/unit/reachability.c new file mode 100644 index 0000000000..0068f6660b --- /dev/null +++ b/tests/unit/reachability.c @@ -0,0 +1,129 @@ +/* + IGraph library. + Copyright (C) 2024 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 + +#include "test_utilities.h" + +void compute_and_print(const igraph_t *g, igraph_neimode_t mode) { + igraph_vector_int_t membership, csize, reach_counts; + igraph_bitset_list_t reach; + igraph_integer_t no_of_nodes, no_of_components; + + no_of_nodes = igraph_vcount(g); + + igraph_vector_int_init(&membership, 0); + igraph_vector_int_init(&csize, 0); + igraph_bitset_list_init(&reach, 0); + + igraph_reachability(g, &membership, &csize, &no_of_components, &reach, mode); + + igraph_vector_int_init(&reach_counts, no_of_nodes); + + igraph_count_reachable(g, &reach_counts, mode); + + printf("Mode: "); + switch (mode) { + case IGRAPH_OUT: + printf("OUT\n"); break; + case IGRAPH_IN: + printf("IN\n"); break; + case IGRAPH_ALL: + printf("ALL\n"); break; + } + print_vector_int(&membership); + print_vector_int(&csize); + printf("No. of components: %" IGRAPH_PRId "\n", no_of_components); + print_bitset_list(&reach); + print_vector_int(&reach_counts); + + igraph_bitset_list_destroy(&reach); + igraph_vector_int_destroy(&csize); + igraph_vector_int_destroy(&membership); + igraph_vector_int_destroy(&reach_counts); +} + +int main(void) { + igraph_t g; + + /* Component calculations are run twice to exercise the cache */ + + printf("\nNull graph (not connected)\n"); + igraph_empty(&g, 0, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSingleton graph (connected)\n"); + igraph_empty(&g, 1, IGRAPH_DIRECTED); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nKautz graph (connected)\n"); + igraph_kautz(&g, 2, 2); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nDirected 2-path\n"); + igraph_small(&g, 2, IGRAPH_DIRECTED, 0, 1, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nTwo disjoint 3-cycles\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 3, 4, 4, 5, 5, 3, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nPath graph with 6 vertices ascending\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nPath graph with 6 vertices descending\n"); + igraph_small(&g, 6, IGRAPH_DIRECTED, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSmall directed graph\n"); + igraph_small(&g, 13, IGRAPH_DIRECTED, 0, 1, 1, 2, 2, 0, 1, 3, 3, 4, 4, 5, 5, 4, 7, 4, 7, 8, 9, 8, 10, 9, 8, 10, 11, 6, 12, 6, -1); + compute_and_print(&g, IGRAPH_OUT); + compute_and_print(&g, IGRAPH_IN); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + printf("\nSmall undirected graph\n"); + igraph_small(&g, 6, IGRAPH_UNDIRECTED, + 0,1, 1,2, 0,2, + 3,4, + -1); + compute_and_print(&g, IGRAPH_ALL); + igraph_destroy(&g); + + VERIFY_FINALLY_STACK(); + + return 0; +} diff --git a/tests/unit/reachability.out b/tests/unit/reachability.out new file mode 100644 index 0000000000..dd27b85917 --- /dev/null +++ b/tests/unit/reachability.out @@ -0,0 +1,225 @@ + +Null graph (not connected) +Mode: OUT +( ) +( ) +No. of components: 0 +{ +} +( ) +Mode: ALL +( ) +( ) +No. of components: 0 +{ +} +( ) + +Singleton graph (connected) +Mode: OUT +( 0 ) +( 1 ) +No. of components: 1 +{ + 0: ( 1 ) +} +( 1 ) +Mode: ALL +( 0 ) +( 1 ) +No. of components: 1 +{ + 0: ( 1 ) +} +( 1 ) + +Kautz graph (connected) +Mode: OUT +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 1 1 1 1 1 1 ) +} +( 12 12 12 12 12 12 12 12 12 12 12 12 ) +Mode: ALL +( 0 0 0 0 0 0 0 0 0 0 0 0 ) +( 12 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 1 1 1 1 1 1 ) +} +( 12 12 12 12 12 12 12 12 12 12 12 12 ) + +Directed 2-path +Mode: OUT +( 0 1 ) +( 1 1 ) +No. of components: 2 +{ + 0: ( 1 1 ) + 1: ( 1 0 ) +} +( 2 1 ) +Mode: ALL +( 0 0 ) +( 2 ) +No. of components: 1 +{ + 0: ( 1 1 ) +} +( 2 2 ) + +Two disjoint 3-cycles +Mode: OUT +( 1 1 1 0 0 0 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 1 1 1 0 0 0 ) + 1: ( 0 0 0 1 1 1 ) +} +( 3 3 3 3 3 3 ) +Mode: IN +( 1 1 1 0 0 0 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 1 1 1 0 0 0 ) + 1: ( 0 0 0 1 1 1 ) +} +( 3 3 3 3 3 3 ) +Mode: ALL +( 0 0 0 1 1 1 ) +( 3 3 ) +No. of components: 2 +{ + 0: ( 0 0 0 1 1 1 ) + 1: ( 1 1 1 0 0 0 ) +} +( 3 3 3 3 3 3 ) + +Path graph with 6 vertices ascending +Mode: OUT +( 0 1 2 3 4 5 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 1 1 1 1 1 ) + 1: ( 1 1 1 1 1 0 ) + 2: ( 1 1 1 1 0 0 ) + 3: ( 1 1 1 0 0 0 ) + 4: ( 1 1 0 0 0 0 ) + 5: ( 1 0 0 0 0 0 ) +} +( 6 5 4 3 2 1 ) +Mode: IN +( 0 1 2 3 4 5 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 0 0 0 0 0 1 ) + 1: ( 0 0 0 0 1 1 ) + 2: ( 0 0 0 1 1 1 ) + 3: ( 0 0 1 1 1 1 ) + 4: ( 0 1 1 1 1 1 ) + 5: ( 1 1 1 1 1 1 ) +} +( 1 2 3 4 5 6 ) +Mode: ALL +( 0 0 0 0 0 0 ) +( 6 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 ) +} +( 6 6 6 6 6 6 ) + +Path graph with 6 vertices descending +Mode: OUT +( 5 4 3 2 1 0 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 1 1 1 1 1 ) + 1: ( 0 1 1 1 1 1 ) + 2: ( 0 0 1 1 1 1 ) + 3: ( 0 0 0 1 1 1 ) + 4: ( 0 0 0 0 1 1 ) + 5: ( 0 0 0 0 0 1 ) +} +( 1 2 3 4 5 6 ) +Mode: IN +( 5 4 3 2 1 0 ) +( 1 1 1 1 1 1 ) +No. of components: 6 +{ + 0: ( 1 0 0 0 0 0 ) + 1: ( 1 1 0 0 0 0 ) + 2: ( 1 1 1 0 0 0 ) + 3: ( 1 1 1 1 0 0 ) + 4: ( 1 1 1 1 1 0 ) + 5: ( 1 1 1 1 1 1 ) +} +( 6 5 4 3 2 1 ) +Mode: ALL +( 0 0 0 0 0 0 ) +( 6 ) +No. of components: 1 +{ + 0: ( 1 1 1 1 1 1 ) +} +( 6 6 6 6 6 6 ) + +Small directed graph +Mode: OUT +( 5 5 5 6 7 7 4 2 3 3 3 1 0 ) +( 1 1 1 3 1 3 1 2 ) +No. of components: 8 +{ + 0: ( 1 0 0 0 0 0 1 0 0 0 0 0 0 ) + 1: ( 0 1 0 0 0 0 1 0 0 0 0 0 0 ) + 2: ( 0 0 1 1 1 1 0 1 1 0 0 0 0 ) + 3: ( 0 0 1 1 1 0 0 0 0 0 0 0 0 ) + 4: ( 0 0 0 0 0 0 1 0 0 0 0 0 0 ) + 5: ( 0 0 0 0 0 0 0 1 1 1 1 1 1 ) + 6: ( 0 0 0 0 0 0 0 1 1 1 0 0 0 ) + 7: ( 0 0 0 0 0 0 0 1 1 0 0 0 0 ) +} +( 6 6 6 3 2 2 1 6 3 3 3 2 2 ) +Mode: IN +( 5 5 5 6 7 7 4 2 3 3 3 1 0 ) +( 1 1 1 3 1 3 1 2 ) +No. of components: 8 +{ + 0: ( 1 0 0 0 0 0 0 0 0 0 0 0 0 ) + 1: ( 0 1 0 0 0 0 0 0 0 0 0 0 0 ) + 2: ( 0 0 0 0 0 1 0 0 0 0 0 0 0 ) + 3: ( 0 0 1 1 1 1 0 0 0 0 0 0 0 ) + 4: ( 1 1 0 0 0 0 1 0 0 0 0 0 0 ) + 5: ( 0 0 0 0 0 0 0 0 0 0 1 1 1 ) + 6: ( 0 0 0 0 0 0 0 0 0 1 1 1 1 ) + 7: ( 0 0 0 0 0 1 0 1 1 1 1 1 1 ) +} +( 3 3 3 4 7 7 3 1 4 4 4 1 1 ) +Mode: ALL +( 0 0 0 0 0 0 1 0 0 0 0 1 1 ) +( 10 3 ) +No. of components: 2 +{ + 0: ( 0 0 1 1 1 1 0 1 1 1 1 1 1 ) + 1: ( 1 1 0 0 0 0 1 0 0 0 0 0 0 ) +} +( 10 10 10 10 10 10 3 10 10 10 10 3 3 ) + +Small undirected graph +Mode: ALL +( 0 0 0 1 1 2 ) +( 3 2 1 ) +No. of components: 3 +{ + 0: ( 0 0 0 1 1 1 ) + 1: ( 0 1 1 0 0 0 ) + 2: ( 1 0 0 0 0 0 ) +} +( 3 3 3 2 2 1 ) diff --git a/tests/unit/test_utilities.c b/tests/unit/test_utilities.c index 6096f734cb..5f8a2546b5 100644 --- a/tests/unit/test_utilities.c +++ b/tests/unit/test_utilities.c @@ -590,3 +590,21 @@ void record_last_warning(const char *reason, const char *file, int line) { expect_warning_ctx.observed = strdup(reason); } + +void print_bitset(const igraph_bitset_t* bitset) { + printf("("); + for (igraph_integer_t i = bitset->size - 1; i >= 0; --i) { + printf(" %d", !!IGRAPH_BIT_TEST(*bitset, i)); + } + printf(" )\n"); +} + +void print_bitset_list(const igraph_bitset_list_t *v) { + igraph_integer_t i, n = igraph_bitset_list_size(v); + printf("{\n"); + for (i = 0; i < n; ++i) { + printf(" %" IGRAPH_PRId ": ", i); + print_bitset(igraph_bitset_list_get_ptr(v, i)); + } + printf("}\n"); +} diff --git a/tests/unit/test_utilities.h b/tests/unit/test_utilities.h index 8aa2e87867..98d5abb198 100644 --- a/tests/unit/test_utilities.h +++ b/tests/unit/test_utilities.h @@ -150,6 +150,10 @@ void vector_chop(igraph_vector_t *vec, igraph_real_t cutoff); void record_last_warning(const char *reason, const char *file, int line); +void print_bitset(const igraph_bitset_t* bitset); + +void print_bitset_list(const igraph_bitset_list_t* bitset); + #define EXPECT_WARNING(funcall, expected_warning) \ do { \ igraph_warning_handler_t *handler; \ diff --git a/tests/unit/vector2.c b/tests/unit/vector2.c index e89a5d738b..e9f2117385 100644 --- a/tests/unit/vector2.c +++ b/tests/unit/vector2.c @@ -115,6 +115,21 @@ int main(void) { igraph_vector_destroy(&v2); igraph_vector_destroy(&v3); + igraph_vector_init_range(&v1, 0, 50); + igraph_vector_init_range(&v2, 20, 23); + igraph_vector_init(&v3, 0); + + igraph_vector_intersect_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v1, &v2, &v3); + print_vector_format(&v3, stdout, "%g"); + igraph_vector_difference_sorted(&v2, &v1, &v3); + print_vector_format(&v3, stdout, "%g"); + + igraph_vector_destroy(&v1); + igraph_vector_destroy(&v2); + igraph_vector_destroy(&v3); + VERIFY_FINALLY_STACK(); return 0; diff --git a/tests/unit/vector2.out b/tests/unit/vector2.out index f1a106e251..3dc75b4ad3 100644 --- a/tests/unit/vector2.out +++ b/tests/unit/vector2.out @@ -14,3 +14,6 @@ ( 5 7 8 8 10 ) ( 1 12 15 17 20 ) ( ) +( 20 21 22 ) +( 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 ) +( )