From 1499403f93e54d2c1f24234aa219dec209c639a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABlle=20Salmon?= Date: Thu, 9 Jan 2025 19:06:29 +0100 Subject: [PATCH 01/28] chore!: remove deprecated `neimode` parameter from `bfs()` and `dfs()` (#1526) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Kirill Müller --- R/structural.properties.R | 10 ++-------- tests/testthat/_snaps/structural.properties.md | 8 ++++---- tests/testthat/test-structural.properties.R | 4 ++-- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/R/structural.properties.R b/R/structural.properties.R index ca1029e142..30cb7d930d 100644 --- a/R/structural.properties.R +++ b/R/structural.properties.R @@ -2140,14 +2140,11 @@ bfs <- function( ensure_igraph(graph) if (lifecycle::is_present(neimode)) { - lifecycle::deprecate_warn( + lifecycle::deprecate_stop( "1.3.0", "bfs(neimode)", "bfs(mode)" ) - if (missing(mode)) { - mode <- neimode - } } if (length(root) == 1) { @@ -2315,14 +2312,11 @@ dfs <- function(graph, root, mode = c("out", "in", "all", "total"), rho = parent.frame(), neimode = deprecated()) { ensure_igraph(graph) if (lifecycle::is_present(neimode)) { - lifecycle::deprecate_warn( + lifecycle::deprecate_stop( "1.3.0", "dfs(neimode)", "dfs(mode)" ) - if (missing(mode)) { - mode <- neimode - } } root <- as_igraph_vs(graph, root) - 1 diff --git a/tests/testthat/_snaps/structural.properties.md b/tests/testthat/_snaps/structural.properties.md index d8f926aeba..55f0821624 100644 --- a/tests/testthat/_snaps/structural.properties.md +++ b/tests/testthat/_snaps/structural.properties.md @@ -3,8 +3,8 @@ Code d <- dfs(g, root = 2, unreachable = FALSE, neimode = "out") Condition - Warning: - The `neimode` argument of `dfs()` is deprecated as of igraph 1.3.0. + Error: + ! The `neimode` argument of `dfs()` was deprecated in igraph 1.3.0 and is now defunct. i Please use the `mode` argument instead. --- @@ -13,8 +13,8 @@ b <- bfs(g, root = 2, neimode = "out", unreachable = FALSE, order = TRUE, rank = TRUE, father = TRUE, pred = TRUE, succ = TRUE, dist = TRUE) Condition - Warning: - The `neimode` argument of `bfs()` is deprecated as of igraph 1.3.0. + Error: + ! The `neimode` argument of `bfs()` was deprecated in igraph 1.3.0 and is now defunct. i Please use the `mode` argument instead. # bfs() works diff --git a/tests/testthat/test-structural.properties.R b/tests/testthat/test-structural.properties.R index 15089e98d3..de12134cf1 100644 --- a/tests/testthat/test-structural.properties.R +++ b/tests/testthat/test-structural.properties.R @@ -15,7 +15,7 @@ test_that("dfs() does not pad order", { test_that("bfs() deprecated argument", { g <- make_star(3) - expect_snapshot({ + expect_snapshot(error = TRUE, { d <- dfs( g, root = 2, @@ -199,7 +199,7 @@ test_that("bfs() works", { test_that("bfs() deprecated argument", { g <- graph_from_literal(a - +b - +c, z - +a, d) - expect_snapshot({ + expect_snapshot(error = TRUE, { b <- bfs( g, root = 2, From 8c2c1725c9cf6f3ccaea7fd320edf4903f351da4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 02:20:58 +0000 Subject: [PATCH 02/28] fledge: Bump version to 2.1.3.9002 (#1656) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5e691c226c..8468fc7c9d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.3.9001 +Version: 2.1.3.9002 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 6f3d4f08b8..8829fc1c10 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,22 @@ +# igraph 2.1.3.9002 + +## Chore + +- Breaking change: remove deprecated `neimode` parameter from `bfs()` and `dfs()` (#1105, #1526). + +- Adapt handling of optional parameters to interface definition changes in the C core (#1567). + +- Breaking change: stricter deprecation of non-functional parameters of `layout_with_kk()` and `layout_with_fr()` (#1108, #1628). + +## Breaking changes + +- Breaking change: remove deprecated `neimode` parameter from `bfs()` and `dfs()` (#1105, #1526). + +- Breaking change: stricter deprecation of non-functional parameters of `layout_with_kk()` and `layout_with_fr()` (#1108, #1628). + + # igraph 2.1.3.9001 ## Bug fixes From c258ae511a92bcfaa1bb1d3e8a974920222d00b2 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Tue, 14 Jan 2025 07:17:31 +0100 Subject: [PATCH 03/28] refactor: removed `for` loops from `get.incidence.dense()` (#1483) (#1655) --- R/conversion.R | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/R/conversion.R b/R/conversion.R index ec548d0129..2129e22f8c 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -378,7 +378,6 @@ as_adjacency_matrix <- function(graph, type = c("both", "upper", "lower"), as_adj <- function(graph, type = c("both", "upper", "lower"), attr = NULL, edges = deprecated(), names = TRUE, sparse = igraph_opt("sparsematrices")) { - lifecycle::deprecate_soft("2.1.0", "as_adj()", "as_adjacency_matrix()") as_adjacency_matrix( @@ -887,18 +886,22 @@ get.incidence.dense <- function(graph, types, names, attr) { res <- matrix(0, n1, n2) recode <- numeric(vc) + # move from 1..n indexing to 1..n1 row indices for type == FALSE + # and 1..n2 col indices for type == TRUE + # recode holds the mapping [1..n] -> [1..n1,1..n2] recode[!types] <- seq_len(n1) recode[types] <- seq_len(n2) - for (i in seq(length.out = ecount(graph))) { - eo <- ends(graph, i, names = FALSE) - e <- recode[eo] - if (!types[eo[1]]) { - res[e[1], e[2]] <- edge_attr(graph, attr, i) - } else { - res[e[2], e[1]] <- edge_attr(graph, attr, i) - } - } + el <- as_edgelist(graph, names = FALSE) + idx <- types[el[, 1]] + el[] <- recode[el] + + # switch order of source/target such that nodes with + # type == FALSE are in el[ ,1] + el[idx, ] <- el[idx, 2:1] + # el[ ,1] only holds values 1..n1 and el[ ,2] values 1..n2 + # and we can populate the matrix + res[el] <- edge_attr(graph, attr) if (names && "name" %in% vertex_attr_names(graph)) { rownames(res) <- V(graph)$name[which(!types)] From fe3b5b89ec6f7daa7f08d595f1a2a55b3cdd8eae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 02:10:51 +0000 Subject: [PATCH 04/28] fledge: Bump version to 2.1.3.9003 (#1657) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8468fc7c9d..a537f03c9e 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.3.9002 +Version: 2.1.3.9003 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 8829fc1c10..2f1d9e0b35 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ +# igraph 2.1.3.9003 + +- Refactor: removed `for` loops from `get.incidence.dense()` (#1483) (#1655). + + # igraph 2.1.3.9002 ## Chore From 4ce86845bfed495cb7cc1ba777daebb79e584e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 22 Jan 2025 18:11:57 +0100 Subject: [PATCH 05/28] ci: Sync vendor workflow with duckdb --- .github/workflows/vendor.yaml | 59 ++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/vendor.yaml b/.github/workflows/vendor.yaml index a9ee8eb737..9f544bd4f8 100644 --- a/.github/workflows/vendor.yaml +++ b/.github/workflows/vendor.yaml @@ -3,14 +3,14 @@ on: branches: - main paths: - - ".github/workflows/vendor.yml" + - ".github/workflows/vendor.yaml" + - "vendor-one.sh" workflow_dispatch: schedule: - - cron: "* 2 * * *" + - cron: "*/5 * * * *" concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-${{ github.head_ref || github.sha }}-${{ github.base_ref || '' }} - cancel-in-progress: true + group: ${{ github.workflow }} name: vendor @@ -24,6 +24,8 @@ jobs: steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 - uses: actions/checkout@v4 with: @@ -33,30 +35,29 @@ jobs: - uses: ./.github/workflows/git-identity - - uses: ./.github/workflows/custom/before-install - if: hashFiles('.github/workflows/custom/before-install/action.yml') != '' - - - uses: ./.github/workflows/install - with: - token: ${{ secrets.GITHUB_TOKEN }} - install-r: false - cache-version: vendor - needs: check - extra-packages: any::rcmdcheck, cpp11, decor - - - uses: ./.github/workflows/custom/after-install - if: hashFiles('.github/workflows/custom/after-install/action.yml') != '' - - - run: | - git checkout -- . - git clean -fdx - tools/update-cigraph.sh .git/igraph + - name: Vendor sources + id: vendor + run: | + git pull --rebase + ./vendor-one.sh .git/igraph rm -rf .git/igraph - - - uses: ./.github/workflows/check - with: - results: ${{ runner.os }}-smoke-test - - - if: github.event_name != 'pull_request' + git push --dry-run + # Check if ahead of upstream branch + # If yes, set a step output + if [ $(git rev-list HEAD...origin/main --count) -gt 0 ]; then + # Avoid set-output, it's deprecated + echo "vendor=ok" >> "$GITHUB_OUTPUT" + fi + + - name: Create PR + if: steps.vendor.outputs.vendor != '' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - git push -u origin HEAD + set -x + git checkout -b vendor + if git push -u origin HEAD; then + gh pr create --fill-first + gh workflow run rcc -f ref=vendor + gh pr merge --auto --squash + fi From bb5a1bcab82b529d222a1fe8293779275756cf21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Wed, 22 Jan 2025 20:32:28 +0100 Subject: [PATCH 06/28] fledge: Release igraph 2.1.4 --- DESCRIPTION | 2 +- NEWS.md | 7 +++++++ tests/testthat/test-graphNEL.R | 4 +--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a537f03c9e..02986f432c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.3.9003 +Version: 2.1.4.9001 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 2f1d9e0b35..8a1dcc4028 100644 --- a/NEWS.md +++ b/NEWS.md @@ -336,6 +336,13 @@ - Switching to development version. +# igraph 2.1.4 + +## Testing + +- Tweak tests that use the graph package. + + # igraph 2.1.3 ## Features diff --git a/tests/testthat/test-graphNEL.R b/tests/testthat/test-graphNEL.R index 78ebf7371a..f0f4145707 100644 --- a/tests/testthat/test-graphNEL.R +++ b/tests/testthat/test-graphNEL.R @@ -1,15 +1,13 @@ test_that("graphNEL conversion works", { skip_if_not_installed("graph") - suppressPackageStartupMessages(library(graph, warn.conflicts = FALSE)) + set.seed(20250122) g <- sample_gnp(100, 5 / 100) N <- as_graphnel(g) g2 <- graph_from_graphnel(N) gi <- isomorphic(g, g2, method = "vf2") expect_true(gi) - expect_equal(gi$map12, 1:vcount(g)) - expect_equal(gi$map21, 1:vcount(g)) ## Attributes From 368f0870afee873ad6605d39ba376d38a4f24582 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 02:11:26 +0000 Subject: [PATCH 07/28] fledge: Bump version to 2.1.4.9002 (#1664) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 330 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 02986f432c..281f6a4feb 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9001 +Version: 2.1.4.9002 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 8a1dcc4028..ca315d1e38 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,334 @@ +# igraph 2.1.4.9002 + +## Bug fixes + +- Temporarily disable generating an interface for `igraph_simple_cycles_callback()`. + as the framework for handling callback functions is not yet present + +## Chore + +- Breaking change: remove deprecated `neimode` parameter from `bfs()` and `dfs()` (#1105, #1526). + +- Adapt handling of optional parameters to interface definition changes in the C core (#1567). + +- Breaking change: stricter deprecation of non-functional parameters of `layout_with_kk()` and `layout_with_fr()` (#1108, #1628). + +## Continuous integration + +- Sync vendor workflow with duckdb. + +## Refactoring + +- Prepare for C core interface standardizing 'type' -\> 'types' in igraph_is_bipartite. + +## Breaking changes + +- Breaking change: remove deprecated `neimode` parameter from `bfs()` and `dfs()` (#1105, #1526). + +- Breaking change: stricter deprecation of non-functional parameters of `layout_with_kk()` and `layout_with_fr()` (#1108, #1628). + +## vendor + +- Update vendored sources to igraph/igraph@5216243786a5368ab14db9649008d1caca75f5af. + + refactor: cleaner workaround in plfit for Windows 11 SDK bug where NAN is defined in a non-constant manner + +- Update vendored sources to igraph/igraph@622f331dc9249b050bc6afbad2032e2c4e78de9e. + + fix: remove unused converage exclusion + +- Update vendored sources to igraph/igraph@c675c805a6ed436887ec0e6f9c6e18ca6d13ecad. + + chore: update CodeCoverage.cmake from upstream https://github.com/bilke/cmake-modules/blob/master/CodeCoverage.cmake + +- Update vendored sources to igraph/igraph@b25eda2dba6ccbcf5a47fe1cb77796a7f0f524d4. + + fix: UBSan warning about negative shift when generating Q_0 hypercube graph + +- Update vendored sources to igraph/igraph@e0dbe374e30adc654ad55a50d5aaf47ebc753908. + + chore: update plfit to 1.0.0 + +- Update vendored sources to igraph/igraph@2cdc20a37fe048143ac54a3fa8288bb017402e7c. + + chore: update plfit + fuzzer: use libxml2 2.13.5 + chore(deps): bump codecov/codecov-action from 4 to 5 + +- Update vendored sources to igraph/igraph@082e2c6ba1a96d34d05a4e75a59c4f690aeca58d. + + chore: update changelog \[skip ci\] + +- Update vendored sources to igraph/igraph@8d242741a1cdc2686f180838d28f80cb96985c74. + + chore: update plfit + +- Update vendored sources to igraph/igraph@6c57e8c955d3c4d81160281d018dd3fa0a4052d8. + + refactor: minor cleanup in maximal cliques + +- Update vendored sources to igraph/igraph@d6348668ba511735110758dc4ee0423a983bf1ac. + + refactor: remove extraneous whitespace before continuation backslashes in max clique template code + +- Update vendored sources to igraph/igraph@9f06e4c34d8b1d533ba331ae581844a2ea9b565f. + + chore: punctuate som error messages + +- Update vendored sources to igraph/igraph@befc831fff74e4bae39743b1f83b38e6b3af0390. + + fix: correct some implicit integer conversions + +- Update vendored sources to igraph/igraph@5e3c56c6a10cb954b78c7441c5460a3c6ad4f5b7. + + fix: eliminate some unused parameter warnings + +- Update vendored sources to igraph/igraph@8cdfd787c2be8886cc4eac014f3004055a8c3ca0. + + refactor: improved const-correctness in vector implementation + +- Update vendored sources to igraph/igraph@ab123f04e487bc8bc1b93b9258e577e5f05d7ce9. + + chore: update plfit, works around non-compile-time-constant NAN bug in recent Windows 11 SDKs + +- Update vendored sources to igraph/igraph@833672f02161051010d1b633b23be53765663372. + + chore: restrict workaround to CMake 3.31.0 USE_TERMINAL bug to the single affected CMake version + +- Update vendored sources to igraph/igraph@5e734a2c1211d017088a882ed121f25956e20189. + + ci: support for controlling the use of plfit and improve alpine jon + +- Update vendored sources to igraph/igraph@ed43183a2eeb9e604addf1dc672908b7b845cec7. + + refactor: some cleanup in shortest path implementations + +- Update vendored sources to igraph/igraph@2412652489721e41617e3d9719b02923da31ab0d. + + chore: devcontainer update + +- Update vendored sources to igraph/igraph@48ff9407b74cca8e50f1d256b039452e40662985. + + chore: link to relevant CMake bug regarding policy CMP0175 + +- Update vendored sources to igraph/igraph@3631b1d5a0f3b7e49463f89cbbba762b24d9383d. + + refactor: clean up igraph_path_length_hist() + +- Update vendored sources to igraph/igraph@6fd3903c84d741d4bed0284c85db277836a4af79. + + refactor: clean up igraph_vertex_path_from_edge_path() and clarify that it is suitable for any walk, not just paths + +- Update vendored sources to igraph/igraph@379ef39bb89f41ba6d0ff39a86039571796e3bde. + + refactor: eliminate redundant pointer variable in GraphML reader + +- Update vendored sources to igraph/igraph@af14d843991f9e4f2894477a8644cfbd27a13d11. + + fix: mark global variables in GraphML parser as static + +- Update vendored sources to igraph/igraph@05979d3b2c85c12334ce5d3508dd473cdfa3c6f5. + + doc: improve maximal clique docs + +- Update vendored sources to igraph/igraph@41d855d470c7e5cca40bb8a2e294656504aa2a56. + + chore: update changelog + +- Update vendored sources to igraph/igraph@a2109c50681c0b444285cf40d09a602047141ea1. + + docs: more cross-referencing + +- Update vendored sources to igraph/igraph@62881320e766e8df227839a67078dd22e909cd2d. + + docs: fix typo + +- Update vendored sources to igraph/igraph@3673a0a9c3b3604ab1975432f6bbeed697dc9f7c. + + doc: more cross-linking + +- Update vendored sources to igraph/igraph@e842e658993df7305aec9d805baf36008aec9e4d. + + fix: compatibility with CMake 3.31 + +- Update vendored sources to igraph/igraph@2f0e27c9c19e8e7eb9b3b2df01ae3186f1e62470. + + ci: use modern CMake features for CTest parallelization + +- Update vendored sources to igraph/igraph@a16619502b6c21d4883eeee992794b271e1035d9. + + chore: updated changelog + +- Update vendored sources (tag 0.10.15) to igraph/igraph@635b432eff0a89580ac9bb98068d2fbc8ef374f2. + + chore: updated changelog + +- Update vendored sources to igraph/igraph@563aa5e8569b85a73680752bd8ad5624575610b6. + + chore: run pre-commit hooks + +- Update vendored sources to igraph/igraph@f32e621f3833556e361a3078ab9e046f6acdd9b8. + + chore: bump VERSION property in shared library + +- Update vendored sources to igraph/igraph@26d3718b364167dd0528c63a35f9e5e513df6845. + + refactor: do not use vector_get() in simple cycle finder + +- Update vendored sources to igraph/igraph@cd7bcd9d9b9bfd9acd83dace6438507947297ce2. + + refactor: cleaner parameter ordering in igraph_i_simple_cycles_circuit() + +- Update vendored sources to igraph/igraph@dafe829b811f9344fb1f5a8412a100b586817e3f. + + refactor: support specifying a minimum cycle length in `igraph_simple_cycles()` + +- Update vendored sources to igraph/igraph@c263ce7541e904f71dbcb7e06ca0dfb08cbf2ed7. + + interface: loops=True default for igraph_degree() + +- Update vendored sources to igraph/igraph@f6ec07e8a9425f09259bd04b0fefe1ebdfe64067. + + chore: updated contributors list + +- Update vendored sources to igraph/igraph@a6441e7a00118988aeac528910d6b9d417aec364. + + chore: trim trailing whitespace in interface file + +- Update vendored sources to igraph/igraph@042a2c5230e8008b976a5ad0f04fe2b3e00cde97. + + interface: add missing interface for igraph_get_isomorphisms_vf2_callback() + +- Update vendored sources to igraph/igraph@797ab4fa6c9a119ff12af1f5d6080de8d1e4f142. + + interface: mark all callback extra parameters and some callback functions as optional + +- Update vendored sources to igraph/igraph@518fcc4165c420b9f4958d1496a607d325d69826. + + interface: add default parameter values for fundamental_cycles() and minimum_cycle_basis() + +- Update vendored sources to igraph/igraph@d9c328f61e31d93681e2c0894ace18ad59a5dba2. + + interface: even more missing OPTIONAL markers + +- Update vendored sources to igraph/igraph@3b9cb6ff078cf4e7fdb2be593811e50b97232fea. + + interface: add more missing OPTIONAL markers + +- Update vendored sources to igraph/igraph@5512e864ecddc76af4a7c93b7e4ebfd7030cc404. + + fix: some weights parameters were incorrectly marked as OPTIONAL in functions.yaml + +- Update vendored sources to igraph/igraph@4b208d877de74a730e8707e94ebad5318cf308ae. + + fix: mark some optional weight parameters as OPTIONAL in functions.yaml + +- Update vendored sources to igraph/igraph@d0616a433134dcb6ae6f3195c5a72e456f5a4809. + + fix: type -\> types in is_bipartite interface + +- Update vendored sources to igraph/igraph@c3f9a823d8bc7fa210df4e1225c86226195e1f16. + + fix: some BIPARTITE_TYPES parameters were incorrectly marked as optional + +- Update vendored sources to igraph/igraph@34f8d72f3c702aa490644244ebee98b836ac45d1. + + refactor: use OPTIONAL instead of =NULL in interfaces + +- Update vendored sources to igraph/igraph@5c74801197212e9908f561139af3141410cf49d2. + + feat: functionality for listing all simple cycles + +- Update vendored sources to igraph/igraph@f5d79f2eb90c2597ea0e33c60d0e6a083efc4abc. + + interface: default OUT mode for igraph_find_cycle() + +- Update vendored sources to igraph/igraph@992f77abdbe675c2d0dd2926536c57496b720c81. + + refactor: better error reporting for igraph_find_cycle() + +- Update vendored sources to igraph/igraph@207f0bb1624bb73a9d1f2f979bf2b2bfde60626f. + + chore: some copyright header cleanup in public headers + +- Update vendored sources to igraph/igraph@8eaa07489a27f55cdf8421bbbb8e34b60eacd1bf. + + refactor: minor readability improvements + +- Update vendored sources to igraph/igraph@5cd6552bf00d6a774cdd64b5fabf2c79524ccfc7. + + fix: add some missing IGRAPH_CHECKs + +- Update vendored sources to igraph/igraph@82d35eebd8c73a9476d5e2d205a851b4ebfc8eda. + + refactor: minor readability cleanup in matching functions + +- Update vendored sources to igraph/igraph@01db8e25a80dd8f93ccb9d7a6f39379a57fdc3ad. + + fix: `igraph_bipartite_projection_size()` now validates the bipartite `types` vector + +- Update vendored sources to igraph/igraph@7284ee015d2662ca4e1ab62e3ce17ff34df24430. + + chore: fix some spelling mistakes + +- Update vendored sources to igraph/igraph@1d6ef7c417e38c881ef325d8c553ccf767db6b6f. + + refactor: minor readability cleanup in igraph_decompose() + +- Update vendored sources to igraph/igraph@0878f577d45ff5fdc33e0ecacc542277a8024af9. + + doc: more documentation improvements + +- Update vendored sources to igraph/igraph@bd7b3a82bb9db038ebfcc046319947c5ea196660. + + doc: several documentation improvements and cross-referencing + +- Update vendored sources to igraph/igraph@764954eda823c40341d302ac290629d96d5bd9bf. + + chore: fix typo in error message + +- Update vendored sources to igraph/igraph@6c0a4310474e9935acc557673e1a2f9ec0dd6db3. + + docs: community_optimal_modulairty() does support directed graphs + +- Update vendored sources to igraph/igraph@59d71d001308d424782a386fea3069cf880d82a2. + + chore: do not warn about unknown warning options with legacy Intel compiler + +- Update vendored sources to igraph/igraph@7a0d4918abfd34c38eb2fdeec4460b2370cea1c9. + + fix: validate sample size in igraph_motifs_randesu_estimate() + +- Update vendored sources to igraph/igraph@e7820cf969a8c63d6564452f73246c044ee99ba9. + + chore: fix typo in comment + +## hack + +- Provide NULL default for types argument of `bipartite_projection_size()`. + This parameter is optional in R, but not in C. Therefore the C interface definition doesn't provide a default or OPTIONAL marker. + +## Uncategorized + +- Refactor: removed `for` loops from `get.incidence.dense()` (#1483) (#1655). + +- Vendor: Update vendored sources to igraph/igraph@ae93a3a2431bb6fd8a1feb687cac32feef63730b. + +- Vendor: Update vendored sources to igraph/igraph@e940ab737938dfdd4518164c866b2599af40856f. + +- Vendor: Update vendored sources to igraph/igraph@3ac79b81bdf835460445ca6163f25e6559ec9958. + +- Vendor: Update vendored sources to igraph/igraph@f7b77f9e9cb4d670886b7e15e876c5549250befa. + +- Vendor: Update vendored sources to igraph/igraph@a662f79d46970bbcc2888e69dce5daa3fee8906e. + +- Vendor: Update vendored sources to igraph/igraph@a1389c191ac42b5ed3fcaf341ff3235c9f6f19f3. + +- Vendor: Update vendored sources to igraph/igraph@12299c4928f9f463c06d0b46752df69a6d67c289. + + # igraph 2.1.3.9003 - Refactor: removed `for` loops from `get.incidence.dense()` (#1483) (#1655). From cee57e1a69001c40ad9995fe6c7c3c89ec6ce3ba Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 23 Jan 2025 21:08:47 +0100 Subject: [PATCH 08/28] fix!: Subset assignment of a graph avoids addition of double edges and ignores loops unless the new `loops` argument is set to `TRUE` (#1661) --- R/indexing.R | 56 ++++++++------ tests/testthat/test-indexing2.R | 128 +++++++++++++++++++++++++++++++- 2 files changed, 158 insertions(+), 26 deletions(-) diff --git a/R/indexing.R b/R/indexing.R index 83716da5ce..a91c081b52 100644 --- a/R/indexing.R +++ b/R/indexing.R @@ -1,4 +1,3 @@ - ## IGraph library. ## Copyright (C) 2010-2012 Gabor Csardi ## 334 Harvard street, Cambridge, MA 02139 USA @@ -329,14 +328,27 @@ length.igraph <- function(x) { vcount(x) } +expand.grid.unordered <- function(i, j, loops = FALSE, directed = FALSE) { + grid <- vctrs::vec_expand_grid(i = i, j = j) + if (!directed) { + grid <- vctrs::vec_unique(data.frame( + i = pmin(grid$i, grid$j), + j = pmax(grid$i, grid$j) + )) + } + if (!loops) { + grid <- grid[grid[, 1] != grid[, 2], ] + } + grid +} + #' @method [<- igraph #' @family functions for manipulating graph structure #' @export `[<-.igraph` <- function(x, i, j, ..., from, to, attr = if (is_weighted(x)) "weight" else NULL, + loops = FALSE, value) { - ## TODO: rewrite this in C to make it faster - ################################################################ ## Argument checks if ((!missing(from) || !missing(to)) && @@ -373,16 +385,16 @@ length.igraph <- function(x) { (is.logical(value) && !value) || (is.null(attr) && is.numeric(value) && value == 0)) { ## Delete edges - todel <- x[from = from, to = to, ..., edges = TRUE] + todel <- get_edge_ids(x, c(rbind(from, to))) x <- delete_edges(x, todel) } else { ## Addition or update of an attribute (or both) - ids <- x[from = from, to = to, ..., edges = TRUE] + ids <- get_edge_ids(x, c(rbind(from, to))) if (any(ids == 0)) { x <- add_edges(x, rbind(from[ids == 0], to[ids == 0])) } if (!is.null(attr)) { - ids <- x[from = from, to = to, ..., edges = TRUE] + ids <- get_edge_ids(x, c(rbind(from, to))) x <- set_edge_attr(x, attr, ids, value = value) } } @@ -391,13 +403,15 @@ length.igraph <- function(x) { (is.null(attr) && is.numeric(value) && value == 0)) { ## Delete edges if (missing(i) && missing(j)) { - todel <- unlist(x[[, , ..., edges = TRUE]]) + todel <- seq_len(ecount(x)) } else if (missing(j)) { - todel <- unlist(x[[i, , ..., edges = TRUE]]) + todel <- unlist(incident_edges(x, v = i, mode = "out")) } else if (missing(i)) { - todel <- unlist(x[[, j, ..., edges = TRUE]]) + todel <- unlist(incident_edges(x, v = j, mode = "in")) } else { - todel <- unlist(x[[i, j, ..., edges = TRUE]]) + edge_pairs <- expand.grid(i, j) + edge_ids <- get_edge_ids(x, c(rbind(edge_pairs[, 1], edge_pairs[, 2]))) + todel <- edge_ids[edge_ids != 0] } x <- delete_edges(x, todel) } else { @@ -405,23 +419,19 @@ length.igraph <- function(x) { i <- if (missing(i)) as.numeric(V(x)) else as_igraph_vs(x, i) j <- if (missing(j)) as.numeric(V(x)) else as_igraph_vs(x, j) if (length(i) != 0 && length(j) != 0) { - ## Existing edges, and their endpoints - exe <- lapply(x[[i, j, ..., edges = TRUE]], as.vector) - exv <- lapply(x[[i, j, ...]], as.vector) - toadd <- unlist(lapply(seq_along(exv), function(idx) { - to <- setdiff(j, exv[[idx]]) - if (length(to != 0)) { - rbind(i[idx], setdiff(j, exv[[idx]])) - } else { - numeric() - } - })) - ## Do the changes + edge_pairs <- expand.grid.unordered(i, j, loops = loops, directed = is_directed(x)) + + edge_ids <- get_edge_ids(x, c(rbind(edge_pairs[, 1], edge_pairs[, 2]))) + toadd <- c(rbind(edge_pairs[edge_ids == 0, 1], edge_pairs[edge_ids == 0, 2])) + if (is.null(attr)) { + if (value > 1) { + cli::cli_abort("value greater than one but graph is not weighted and no attribute was specified.") + } x <- add_edges(x, toadd) } else { x <- add_edges(x, toadd, attr = structure(list(value), names = attr)) - toupdate <- unlist(exe) + toupdate <- edge_ids[edge_ids != 0] x <- set_edge_attr(x, attr, toupdate, value) } } diff --git a/tests/testthat/test-indexing2.R b/tests/testthat/test-indexing2.R index 03294063b3..b771fff68a 100644 --- a/tests/testthat/test-indexing2.R +++ b/tests/testthat/test-indexing2.R @@ -34,7 +34,7 @@ test_that("[ can set weights and delete weighted edges", { A[1, 2] <- g[1, 2] <- 3 expect_equal(canonicalize_matrix(g[]), A) - A[1:2, 2:3] <- g[1:2, 2:3] <- -1 + A[1:2, 2:3] <- g[1:2, 2:3, loops = TRUE] <- -1 expect_equal(canonicalize_matrix(g[]), A) g[1, 2] <- NULL @@ -52,12 +52,12 @@ test_that("[ can add edges and ste weights via vertex names", { A["b", "c"] <- g["b", "c"] <- TRUE expect_equal(canonicalize_matrix(g[]), canonicalize_matrix(A)) - A[c("a", "f"), c("f", "a")] <- g[c("a", "f"), c("f", "a")] <- TRUE + A[c("a", "f"), c("f", "a")] <- g[c("a", "f"), c("f", "a"), loops = TRUE] <- TRUE expect_equal(canonicalize_matrix(g[]), canonicalize_matrix(A)) A[A == 1] <- NA A[c("a", "c", "h"), c("a", "b", "c")] <- - g[c("a", "c", "h"), c("a", "b", "c"), attr = "weight"] <- 3 + g[c("a", "c", "h"), c("a", "b", "c"), attr = "weight", loops = TRUE] <- 3 expect_equal(canonicalize_matrix(g[]), canonicalize_matrix(A)) }) @@ -105,3 +105,125 @@ test_that("[ and from-to with multiple values", { ) expect_equal(canonicalize_matrix(g[]), canonicalize_matrix(A)) }) + +test_that("[ manipulation works as intended for unweighted", { + # see issue https://github.com/igraph/rigraph/issues/1662 + g1 <- make_empty_graph(n = 10, directed = FALSE) + A1 <- matrix(0, 10, 10) + A1[1:5, ] <- A1[, 1:5] <- g1[1:5, ] <- 1 + diag(A1) <- 0 + expect_equal(canonicalize_matrix(g1[]), A1) + + g2 <- make_empty_graph(n = 10, directed = FALSE) + A2 <- matrix(0, 10, 10) + A2[1:5, ] <- A2[, 1:5] <- g2[, 1:5] <- 1 + diag(A2) <- 0 + expect_equal(canonicalize_matrix(g2[]), A2) + + g3 <- make_empty_graph(n = 10, directed = TRUE) + A3 <- matrix(0, 10, 10) + A3[1:5, ] <- g3[1:5, ] <- 1 + diag(A3) <- 0 + expect_equal(canonicalize_matrix(g3[]), A3) + + g4 <- make_empty_graph(n = 10, directed = TRUE) + A4 <- matrix(0, 10, 10) + A4[, 1:5] <- g4[, 1:5] <- 1 + diag(A4) <- 0 + expect_equal(canonicalize_matrix(g4[]), A4) + + g5 <- make_empty_graph(n = 10, directed = TRUE) + A5 <- matrix(0, 10, 10) + g5[1, 2] <- g5[2, 1] <- A5[1, 2] <- A5[2, 1] <- 1 + expect_equal(canonicalize_matrix(g5[]), A5) + + g6 <- make_empty_graph(n = 10, directed = FALSE) + A6 <- matrix(0, 10, 10) + A6[6:10, 1:5] <- A6[1:5, 6:10] <- g6[6:10, 1:5] <- 1 + expect_equal(canonicalize_matrix(g6[]), A6) + + g7 <- make_empty_graph(n = 10, directed = TRUE) + A7 <- matrix(0, 10, 10) + g7[6:10, 1:5] <- A7[6:10, 1:5] <- 1 + diag(A7) <- 0 + expect_equal(canonicalize_matrix(g7[]), A7) + + g8 <- make_empty_graph(n = 10, directed = TRUE) + A8 <- matrix(0, 10, 10) + g8[1:5, 6:10] <- A8[1:5, 6:10] <- 1 + diag(A8) <- 0 + expect_equal(canonicalize_matrix(g8[]), A8) +}) + +test_that("[ manipulation works as intended for weighted", { + # see issue https://github.com/igraph/rigraph/issues/1662 + + g1 <- make_empty_graph(n = 10, directed = FALSE) + A1 <- matrix(0, 10, 10) + A1[1:5, 1:5] <- g1[1:5, 1:5, attr = "weight"] <- 2 + diag(A1) <- 0 + expect_equal(canonicalize_matrix(g1[]), A1) + + g2 <- make_empty_graph(n = 10, directed = FALSE) + E(g2)$weight <- 1 + A2 <- matrix(0, 10, 10) + A2[1:3, 1:3] <- g2[1:3, 1:3] <- -2 + diag(A2) <- 0 + expect_equal(canonicalize_matrix(g2[]), A2) +}) + +test_that("[ manipulation handles errors properly", { + g1 <- make_empty_graph(n = 10, directed = FALSE) + expect_error(g1[1:5, ] <- 2) +}) + +test_that("[ deletion works as intended", { + # see issue https://github.com/igraph/rigraph/issues/1662 + g1 <- make_full_graph(n = 10, directed = FALSE) + A1 <- matrix(1, 10, 10) + diag(A1) <- 0 + A1[1:5, ] <- A1[, 1:5] <- g1[1:5, ] <- 0 + expect_equal(canonicalize_matrix(g1[]), A1) + + g2 <- make_full_graph(n = 10, directed = FALSE) + A2 <- matrix(1, 10, 10) + diag(A2) <- 0 + A2[1:5, ] <- A2[, 1:5] <- g2[, 1:5] <- 0 + expect_equal(canonicalize_matrix(g2[]), A2) + + g3 <- make_full_graph(n = 10, directed = TRUE) + A3 <- matrix(1, 10, 10) + diag(A3) <- 0 + A3[1:5, ] <- g3[1:5, ] <- 0 + expect_equal(canonicalize_matrix(g3[]), A3) + + g4 <- make_full_graph(n = 10, directed = TRUE) + A4 <- matrix(1, 10, 10) + diag(A4) <- 0 + A4[, 1:5] <- g4[, 1:5] <- 0 + expect_equal(canonicalize_matrix(g4[]), A4) + + g5 <- make_full_graph(n = 10, directed = TRUE) + A5 <- matrix(1, 10, 10) + diag(A5) <- 0 + g5[1, 2] <- g5[2, 1] <- A5[1, 2] <- A5[2, 1] <- 0 + expect_equal(canonicalize_matrix(g5[]), A5) + + g6 <- make_full_graph(n = 10, directed = FALSE) + A6 <- matrix(1, 10, 10) + diag(A6) <- 0 + A6[6:10, 1:5] <- A6[1:5, 6:10] <- g6[6:10, 1:5] <- 0 + expect_equal(canonicalize_matrix(g6[]), A6) + + g7 <- make_full_graph(n = 10, directed = TRUE) + A7 <- matrix(1, 10, 10) + diag(A7) <- 0 + g7[6:10, 1:5] <- A7[6:10, 1:5] <- 0 + expect_equal(canonicalize_matrix(g7[]), A7) + + g8 <- make_full_graph(n = 10, directed = TRUE) + A8 <- matrix(1, 10, 10) + diag(A8) <- 0 + g8[1:5, 6:10] <- A8[1:5, 6:10] <- 0 + expect_equal(canonicalize_matrix(g8[]), A8) +}) From fac3b37c0b494868dd6187c86ca0e09ace8a0356 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 02:11:40 +0000 Subject: [PATCH 09/28] fledge: Bump version to 2.1.4.9003 (#1665) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 281f6a4feb..b79dc61c8c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9002 +Version: 2.1.4.9003 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index ca315d1e38..471bfd5d6c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,16 @@ +# igraph 2.1.4.9003 + +## Bug fixes + +- Breaking change: Subset assignment of a graph avoids addition of double edges and ignores loops unless the new `loops` argument is set to `TRUE` (@schochastics, #1662, #1661). + +## Breaking changes + +- Breaking change: Subset assignment of a graph avoids addition of double edges and ignores loops unless the new `loops` argument is set to `TRUE` (@schochastics, #1662, #1661). + + # igraph 2.1.4.9002 ## Bug fixes From 5686be7d6e37486ced599994ccb2d58ba0a67415 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Sat, 25 Jan 2025 20:10:57 +0100 Subject: [PATCH 10/28] refactor: forward `get.adjacency.dense()` to `get.adjacency.sparse()` if attributes are present (#1483) (#1653) --- R/conversion.R | 88 ++++++++--------------------- tests/testthat/_snaps/conversion.md | 12 ++-- tests/testthat/test-conversion.R | 78 +++++++++++++++++++------ 3 files changed, 88 insertions(+), 90 deletions(-) diff --git a/R/conversion.R b/R/conversion.R index 2129e22f8c..808d19ab96 100644 --- a/R/conversion.R +++ b/R/conversion.R @@ -1,4 +1,3 @@ - #' Convert igraph graphs to graphNEL objects from the graph package #' #' @description @@ -159,11 +158,6 @@ get.adjacency.dense <- function(graph, type = c("both", "upper", "lower"), ensure_igraph(graph) type <- igraph.match.arg(type) - type <- switch(type, - "upper" = 0, - "lower" = 1, - "both" = 2 - ) if (is.logical(loops)) { loops <- ifelse(loops, "once", "ignore") @@ -183,66 +177,28 @@ get.adjacency.dense <- function(graph, type = c("both", "upper", "lower"), if (is.null(attr)) { on.exit(.Call(R_igraph_finalizer)) + type <- switch(type, + "upper" = 0, + "lower" = 1, + "both" = 2 + ) res <- .Call( R_igraph_get_adjacency, graph, as.numeric(type), weights, loops ) } else { - attr <- as.character(attr) - if (!attr %in% edge_attr_names(graph)) { - stop("no such edge attribute") - } - exattr <- edge_attr(graph, attr) - if (is.logical(exattr)) { - res <- matrix(FALSE, nrow = vcount(graph), ncol = vcount(graph)) - } else if (is.numeric(exattr)) { - res <- matrix(0, nrow = vcount(graph), ncol = vcount(graph)) - } else { - stop( - "Matrices must be either numeric or logical, ", - "and the edge attribute is not" - ) - } - if (is_directed(graph)) { - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[e[1], e[2]] <- exattr[i] - } - } else { - if (type == 0) { - ## upper - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[min(e), max(e)] <- exattr[i] - } - } else if (type == 1) { - ## lower - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[max(e), min(e)] <- exattr[i] - } - } else if (type == 2) { - ## both - for (i in seq(length.out = ecount(graph))) { - e <- ends(graph, i, names = FALSE) - res[e[1], e[2]] <- exattr[i] - if (e[1] != e[2]) { - res[e[2], e[1]] <- exattr[i] - } - } - } - } + # faster than a specialized implementation + res <- as.matrix(get.adjacency.sparse(graph, type = type, attr = attr, names = names, call = rlang::caller_env())) } if (names && "name" %in% vertex_attr_names(graph)) { colnames(res) <- rownames(res) <- V(graph)$name } - res } get.adjacency.sparse <- function(graph, type = c("both", "upper", "lower"), - attr = NULL, names = TRUE) { + attr = NULL, names = TRUE, call = rlang::caller_env()) { ensure_igraph(graph) type <- igraph.match.arg(type) @@ -255,13 +211,13 @@ get.adjacency.sparse <- function(graph, type = c("both", "upper", "lower"), if (!is.null(attr)) { attr <- as.character(attr) if (!attr %in% edge_attr_names(graph)) { - stop("no such edge attribute") + cli::cli_abort("No such edge attribute", call = call) } value <- edge_attr(graph, name = attr) if (!is.numeric(value) && !is.logical(value)) { - stop( - "Matrices must be either numeric or logical, ", - "and the edge attribute is not" + cli::cli_abort( + "Matrices must be either numeric or logical, and the edge attribute is not", + call = call ) } } else { @@ -860,7 +816,7 @@ as_graphnel <- function(graph) { res } -get.incidence.dense <- function(graph, types, names, attr) { +get.incidence.dense <- function(graph, types, names, attr, call = rlang::caller_env()) { if (is.null(attr)) { on.exit(.Call(R_igraph_finalizer)) ## Function call @@ -877,7 +833,7 @@ get.incidence.dense <- function(graph, types, names, attr) { } else { attr <- as.character(attr) if (!attr %in% edge_attr_names(graph)) { - stop("no such edge attribute") + cli::cli_abort("No such edge attribute", call = call) } vc <- vcount(graph) @@ -915,15 +871,15 @@ get.incidence.dense <- function(graph, types, names, attr) { } } -get.incidence.sparse <- function(graph, types, names, attr) { +get.incidence.sparse <- function(graph, types, names, attr, call = rlang::caller_env()) { vc <- vcount(graph) if (length(types) != vc) { - stop("Invalid types vector") + cli::cli_abort("Invalid types vector", call = call) } el <- as_edgelist(graph, names = FALSE) if (any(types[el[, 1]] == types[el[, 2]])) { - stop("Invalid types vector, not a bipartite graph") + cli::cli_abort("Invalid types vector, not a bipartite graph", call = call) } n1 <- sum(!types) @@ -943,7 +899,7 @@ get.incidence.sparse <- function(graph, types, names, attr) { if (!is.null(attr)) { attr <- as.character(attr) if (!attr %in% edge_attr_names(graph)) { - stop("no such edge attribute") + cli::cli_abort("No such edge attribute", call = call) } value <- edge_attr(graph, name = attr) } else { @@ -1007,7 +963,7 @@ get.incidence.sparse <- function(graph, types, names, attr) { #' as_biadjacency_matrix(g) #' as_biadjacency_matrix <- function(graph, types = NULL, attr = NULL, - names = TRUE, sparse = FALSE) { + names = TRUE, sparse = FALSE) { # Argument checks ensure_igraph(graph) types <- handle_vertex_type_arg(types, graph) @@ -1016,7 +972,7 @@ as_biadjacency_matrix <- function(graph, types = NULL, attr = NULL, sparse <- as.logical(sparse) if (sparse) { - get.incidence.sparse(graph, types = types, names = names, attr = attr) + get.incidence.sparse(graph, types = types, names = names, attr = attr, call = rlang::caller_env()) } else { get.incidence.dense(graph, types = types, names = names, attr = attr) } @@ -1036,8 +992,8 @@ as_biadjacency_matrix <- function(graph, types = NULL, attr = NULL, #' this naming to avoid confusion with the edge-vertex incidence matrix. #' @export as_incidence_matrix <- function(...) { # nocov start - lifecycle::deprecate_soft("1.6.0", "as_incidence_matrix()", "as_biadjacency_matrix()") - as_biadjacency_matrix(...) + lifecycle::deprecate_soft("1.6.0", "as_incidence_matrix()", "as_biadjacency_matrix()") + as_biadjacency_matrix(...) } # nocov end #' @rdname graph_from_data_frame #' @param x An igraph object. diff --git a/tests/testthat/_snaps/conversion.md b/tests/testthat/_snaps/conversion.md index 12b0bc0c7f..901eeddd70 100644 --- a/tests/testthat/_snaps/conversion.md +++ b/tests/testthat/_snaps/conversion.md @@ -25,15 +25,15 @@ Code as_adjacency_matrix(g, attr = "bla") Condition - Error in `get.adjacency.sparse()`: - ! no such edge attribute + Error in `as_adjacency_matrix()`: + ! No such edge attribute --- Code as_adjacency_matrix(g, attr = "bla") Condition - Error in `get.adjacency.sparse()`: + Error in `as_adjacency_matrix()`: ! Matrices must be either numeric or logical, and the edge attribute is not # as_adjacency_matrix() errors well -- dense @@ -41,14 +41,14 @@ Code as_adjacency_matrix(g, attr = "bla", sparse = FALSE) Condition - Error in `get.adjacency.dense()`: - ! no such edge attribute + Error in `as_adjacency_matrix()`: + ! No such edge attribute --- Code as_adjacency_matrix(g, attr = "bla", sparse = FALSE) Condition - Error in `get.adjacency.dense()`: + Error in `as_adjacency_matrix()`: ! Matrices must be either numeric or logical, and the edge attribute is not diff --git a/tests/testthat/test-conversion.R b/tests/testthat/test-conversion.R index aa8ca51fe7..176b8ff9f0 100644 --- a/tests/testthat/test-conversion.R +++ b/tests/testthat/test-conversion.R @@ -71,7 +71,7 @@ test_that("as_undirected() keeps attributes", { }) test_that("as_adjacency_matrix() works -- sparse", { - g <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + g <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) basic_adj_matrix <- as_adjacency_matrix(g) expect_s4_class(basic_adj_matrix, "dgCMatrix") expected_matrix <- matrix( @@ -93,17 +93,19 @@ test_that("as_adjacency_matrix() works -- sparse", { E(g)$weight <- c(1.2, 3.4, 2.7, 5.6, 6.0, 0.1, 6.1, 3.3, 4.3) weight_adj_matrix <- as_adjacency_matrix(g, attr = "weight") expect_s4_class(weight_adj_matrix, "dgCMatrix") - expect_equal(as.matrix(weight_adj_matrix), + expect_equal( + as.matrix(weight_adj_matrix), matrix( c(0, 3.4, 0, 0, 1.2, 2.7, 0, 13.7, 0, 0, 11.6, 0, 0, 0, 0.1, 0), nrow = 4L, ncol = 4L, dimnames = list(c("a", "b", "c", "d"), c("a", "b", "c", "d")) - )) + ) + ) }) test_that("as_adjacency_matrix() works -- sparse + not both", { - dg <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + dg <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) g <- as_undirected(dg, mode = "each") lower_adj_matrix <- as_adjacency_matrix(g, type = "lower") @@ -128,16 +130,15 @@ test_that("as_adjacency_matrix() works -- sparse + not both", { }) test_that("as_adjacency_matrix() errors well -- sparse", { - g <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + g <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) expect_snapshot(as_adjacency_matrix(g, attr = "bla"), error = TRUE) E(g)$bla <- letters[1:ecount(g)] expect_snapshot(as_adjacency_matrix(g, attr = "bla"), error = TRUE) - }) test_that("as_adjacency_matrix() works -- sparse undirected", { - dg <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + dg <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) ug <- as_undirected(dg, mode = "each") adj_matrix <- as_adjacency_matrix(ug) expect_s4_class(adj_matrix, "dgCMatrix") @@ -155,7 +156,7 @@ test_that("as_adjacency_matrix() works -- sparse undirected", { }) test_that("as_adjacency_matrix() works -- dense", { - g <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + g <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) basic_adj_matrix <- as_adjacency_matrix(g, sparse = FALSE) expected_matrix <- matrix( @@ -175,7 +176,11 @@ test_that("as_adjacency_matrix() works -- dense", { expect_equal( weight_adj_matrix, matrix( - c(0, 3.4, 0, 0, 1.2, 2.7, 0, 4.3, 0, 0, 6, 0, 0, 0, 0.1, 0), + c(0, 3.4, 0, 0, 1.2, 2.7, 0, 13.7, 0, 0, 11.6, 0, 0, 0, 0.1, 0), + # below is wrong test result due to a bug (#1551). Weights of ties + # between the same node pair should be aggregated and not only the last + # weight should be considered. The above is consistent with the sparse case + # c(0, 3.4, 0, 0, 1.2, 2.7, 0, 4.3, 0, 0, 6, 0, 0, 0, 0.1, 0), nrow = 4L, ncol = 4L, dimnames = list(c("a", "b", "c", "d"), c("a", "b", "c", "d")) @@ -184,20 +189,20 @@ test_that("as_adjacency_matrix() works -- dense", { }) test_that("as_adjacency_matrix() errors well -- dense", { - g <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + g <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) expect_snapshot(as_adjacency_matrix(g, attr = "bla", sparse = FALSE), error = TRUE) E(g)$bla <- letters[1:ecount(g)] expect_snapshot(as_adjacency_matrix(g, attr = "bla", sparse = FALSE), error = TRUE) - }) test_that("as_adjacency_matrix() works -- dense undirected", { - dg <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + dg <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) ug <- as_undirected(dg, mode = "each") # no different treatment than undirected if no attribute?! adj_matrix <- as_adjacency_matrix(ug, sparse = FALSE) + dimnames(adj_matrix) <- NULL expect_equal( adj_matrix, matrix( @@ -208,10 +213,15 @@ test_that("as_adjacency_matrix() works -- dense undirected", { E(ug)$weight <- c(1.2, 3.4, 2.7, 5.6, 6.0, 0.1, 6.1, 3.3, 4.3) weight_adj_matrix <- as_adjacency_matrix(ug, sparse = FALSE, attr = "weight") + dimnames(weight_adj_matrix) <- NULL expect_equal( weight_adj_matrix, matrix( - c(0, 3.4, 0, 0, 3.4, 2.7, 0, 4.3, 0, 0, 6, 0.1, 0, 4.3, 0.1, 0), + c(0, 4.6, 0, 0, 4.6, 2.7, 0, 13.7, 0, 0, 11.6, 0.1, 0, 13.7, 0.1, 0), + # below is wrong test result due to a bug (#1551). Weights of ties + # between the same node pair should be aggregated and not only the last + # weight should be considered. The above is consistent with the sparse case + # c(0, 3.4, 0, 0, 3.4, 2.7, 0, 4.3, 0, 0, 6, 0.1, 0, 4.3, 0.1, 0), nrow = 4L, ncol = 4L ) @@ -219,7 +229,7 @@ test_that("as_adjacency_matrix() works -- dense undirected", { }) test_that("as_adjacency_matrix() works -- dense + not both", { - dg <- make_graph(c(1,2, 2,1, 2,2, 3,3, 3,3, 3,4, 4,2, 4,2, 4,2), directed = TRUE) + dg <- make_graph(c(1, 2, 2, 1, 2, 2, 3, 3, 3, 3, 3, 4, 4, 2, 4, 2, 4, 2), directed = TRUE) g <- as_undirected(dg, mode = "each") E(g)$attribute <- c(1.2, 3.4, 2.7, 5.6, 6.0, 0.1, 6.1, 3.3, 4.3) @@ -229,29 +239,61 @@ test_that("as_adjacency_matrix() works -- dense + not both", { sparse = FALSE, attr = "attribute" ) + dimnames(lower_adj_matrix) <- NULL expect_equal( lower_adj_matrix, matrix( - c(0, 3.4, 0, 0, 0, 2.7, 0, 4.3, 0, 0, 6, 0.1, 0, 0, 0, 0), + c(0, 4.6, 0, 0, 0, 2.7, 0, 13.7, 0, 0, 11.6, 0.1, 0, 0, 0, 0), + # below is wrong test result due to a bug (#1551). Weights of ties + # between the same node pair should be aggregated and not only the last + # weight should be considered. The above is consistent with the sparse case + # c(0, 3.4, 0, 0, 0, 2.7, 0, 4.3, 0, 0, 6, 0.1, 0, 0, 0, 0), nrow = 4L, ncol = 4L ) ) - upper_adj_matrix <- as_adjacency_matrix( + upper_adj_matrix <- as_adjacency_matrix( g, type = "upper", sparse = FALSE, attr = "attribute" ) - + dimnames(upper_adj_matrix) <- NULL expect_equal( upper_adj_matrix, matrix( - c(0, 0, 0, 0, 3.4, 2.7, 0, 0, 0, 0, 6, 0, 0, 4.3, 0.1, 0), + c(0, 0, 0, 0, 4.6, 2.7, 0, 0, 0, 0, 11.6, 0, 0, 13.7, 0.1, 0), + # below is wrong test result due to a bug (#1551). Weights of ties + # between the same node pair should be aggregated and not only the last + # weight should be considered. The above is consistent with the sparse case + # c(0, 0, 0, 0, 3.4, 2.7, 0, 0, 0, 0, 6, 0, 0, 4.3, 0.1, 0), nrow = 4L, ncol = 4L ) ) }) + +test_that("as_adjacency_matrix() works -- dense + weights", { + g <- make_full_graph(5, directed = FALSE) + E(g)$weight <- 1:10 + mat <- matrix(0, 5, 5) + mat[lower.tri(mat)] <- 1:10 + mat <- mat + t(mat) + A <- as_adjacency_matrix(g, attr = "weight", sparse = FALSE) + expect_equal(canonicalize_matrix(A), mat) +}) + +test_that("as_biadjacency_matrix() works -- dense + weights", { + g <- make_bipartite_graph(c(0, 1, 0, 1, 0, 0), c(1, 2, 2, 3, 3, 4)) + E(g)$weight <- c(2, 4, 6) + A <- as_biadjacency_matrix(g, attr = "weight", sparse = FALSE) + mat <- matrix( + c(2, 4, 0, 0, 0, 6, 0, 0), + nrow = 4L, + ncol = 2L, + dimnames = list(c("1", "3", "5", "6"), c("2", "4")) + ) + expect_equal(canonicalize_matrix(A), canonicalize_matrix(mat)) +}) From 0d33d45e0d238911108a0f333c52750edcfd4c78 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2025 02:18:34 +0000 Subject: [PATCH 11/28] fledge: Bump version to 2.1.4.9004 (#1666) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index b79dc61c8c..7c9e0560c4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9003 +Version: 2.1.4.9004 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 471bfd5d6c..7c647442af 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,10 @@ +# igraph 2.1.4.9004 + +- Refactor: forward `get.adjacency.dense()` to `get.adjacency.sparse()` if attributes are present (#1483) (#1653). + + # igraph 2.1.4.9003 ## Bug fixes From 453fa1d65bb8cfa72d89f8a681c6bce6d64c15e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Thu, 30 Jan 2025 21:14:29 +0100 Subject: [PATCH 12/28] feat: `get_edge_ids()` accepts data frames and matrices (#1663) Co-authored-by: schochastics --- R/interface.R | 44 ++++++++++++++++++-- tests/testthat/_snaps/interface.md | 16 ++++++++ tests/testthat/test-interface.R | 64 ++++++++++++++++++++++++------ 3 files changed, 107 insertions(+), 17 deletions(-) diff --git a/R/interface.R b/R/interface.R index fdae8dcf45..687f45a9fa 100644 --- a/R/interface.R +++ b/R/interface.R @@ -1,4 +1,3 @@ - #' Check whether a graph is directed #' #' @description @@ -467,6 +466,41 @@ get.edges <- function(graph, es) { ends(graph, es, names = FALSE) } +el_to_vec <- function(x, call = rlang::caller_env()) { + if (is.data.frame(x)) { + if (typeof(x[[1]]) == typeof(x[[2]])) { + c(rbind(x[[1]], x[[2]])) + } else { + cli::cli_abort("The columns of the data.frame are of different type ({typeof(x[[1]])} and {typeof(x[[2]])}) ") + } + } else if (inherits(x, "matrix")) { + dimx <- dim(x) + nrow <- dimx[[1]] + ncol <- dimx[[2]] + if (nrow == 2 && ncol == 2) { + lifecycle::deprecate_stop( + "2.1.5", + "get_edge_ids(vp = 'is not allowed to be a 2 times 2 matrix')" + ) + } else if (nrow == 2) { + lifecycle::deprecate_warn( + "2.1.5", + "get_edge_ids(vp = 'supplied as a matrix should be a n times 2 matrix, not 2 times n')", + details = "either transpose the matrix with t() or convert it to a data.frame with two columns." + ) + c(x) + } else if (ncol == 2) { + c(t(x)) + } else { + cli::cli_abort("{.args vp} was supplied as a {dimx[1]} times {dimx[2]} matrix. Only n times 2 matrices are allowed") + } + } else if (is.vector(x)) { + x + } else { + cli::cli_abort("Only two-column data.frames and matrices, and vectors are allowed for {.args vp}", call = call) + } +} + #' Find the edge ids based on the incident vertices of the edges #' @@ -482,8 +516,9 @@ get.edges <- function(graph, es) { #' vertices. #' #' @param graph The input graph. -#' @param vp The incident vertices, given as vertex ids or symbolic vertex -#' names. They are interpreted pairwise, i.e. the first and second are used for +#' @param vp The incident vertices, given as a two-column data frame, two-column matrix, +#' or vector of vertex ids or symbolic vertex names. +#' For a vector, the values are interpreted pairwise, i.e. the first and second are used for #' the first edge, the third and fourth for the second, etc. #' @param directed Logical scalar, whether to consider edge directions in #' directed graphs. This argument is ignored for undirected graphs. @@ -519,6 +554,8 @@ get_edge_ids <- function(graph, error = FALSE) { ensure_igraph(graph) + vp <- el_to_vec(vp, call = rlang::caller_env()) + on.exit(.Call(R_igraph_finalizer)) .Call( R_igraph_get_eids, graph, as_igraph_vs(graph, vp) - 1, @@ -543,7 +580,6 @@ get.edge.ids <- function(graph, directed = TRUE, error = FALSE, multi = deprecated()) { - if (lifecycle::is_present(multi)) { if (isTRUE(multi)) { lifecycle::deprecate_stop("2.0.0", "get.edge.ids(multi = )") diff --git a/tests/testthat/_snaps/interface.md b/tests/testthat/_snaps/interface.md index f7a83c5564..d394032f7a 100644 --- a/tests/testthat/_snaps/interface.md +++ b/tests/testthat/_snaps/interface.md @@ -17,3 +17,19 @@ Error: ! The `multi` argument of `get.edge.ids()` was deprecated in igraph 2.0.0 and is now defunct. +# get_edge_id() errors correctly for wrong vp + + Code + get_edge_ids(g, el_g) + Condition + Error: + ! Only two-column data.frames and matrices, and vectors are allowed for vp + +--- + + Code + get_edge_ids(g, df) + Condition + Error in `el_to_vec()`: + ! The columns of the data.frame are of different type (character and double) + diff --git a/tests/testthat/test-interface.R b/tests/testthat/test-interface.R index 6b0463a1c5..91669ee58a 100644 --- a/tests/testthat/test-interface.R +++ b/tests/testthat/test-interface.R @@ -124,7 +124,6 @@ test_that("adjacent_vertices works", { for (i in seq_along(test_vertices)) { expect_setequal(adj_vertices[[i]], al[[test_vertices[i]]]) } - }) @@ -156,18 +155,18 @@ test_that("delete_edges works", { g2 <- delete_edges(g, E(g, P = c("D", "E"))) expected_matrix <- matrix( - c( - 0, 0, 0, 1, 1, 1, - 0, 0, 0, 1, 1, 1, - 0, 0, 0, 1, 1, 1, - 1, 1, 1, 0, 0, 0, - 1, 1, 1, 0, 0, 1, - 1, 1, 1, 0, 1, 0 - ), - nrow = 6L, - ncol = 6L, - dimnames = list(c("A", "B", "C", "D", "E", "F"), c("A", "B", "C", "D", "E", "F")) - ) + c( + 0, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, + 0, 0, 0, 1, 1, 1, + 1, 1, 1, 0, 0, 0, + 1, 1, 1, 0, 0, 1, + 1, 1, 1, 0, 1, 0 + ), + nrow = 6L, + ncol = 6L, + dimnames = list(c("A", "B", "C", "D", "E", "F"), c("A", "B", "C", "D", "E", "F")) + ) expect_equal(as.matrix(g2[]), expected_matrix) }) @@ -184,3 +183,42 @@ test_that("get.edge.ids() deprecation", { expect_snapshot(get.edge.ids(g, 1:2)) expect_snapshot(get.edge.ids(g, 1:2, multi = TRUE), error = TRUE) }) + +test_that("get_edge_id() works with data frame", { + g <- make_full_graph(3, directed = FALSE) + el_df <- data.frame(from = c(1, 1), to = c(2, 3)) + expect_equal(get_edge_ids(g, el_df), c(1, 2)) +}) + +test_that("get_edge_id() works with matrices", { + g <- make_full_graph(10) + mat <- matrix(c(1, 2, 1, 3, 1, 4), 3, 2, byrow = TRUE) + expect_equal(get_edge_ids(g, mat), c(1, 2, 3)) + + mat <- matrix(c(1, 2), 1, 2) + expect_equal(get_edge_ids(g, mat), 1) +}) + +test_that("get_edge_id() errors correctly for wrong vp", { + g <- make_full_graph(3, directed = FALSE) + el_g <- make_empty_graph() + expect_snapshot(error = TRUE, { + get_edge_ids(g, el_g) + }) + expect_error(get_edge_ids(g, NULL)) + expect_error(get_edge_ids(g, NA)) + + V(g)$name <- letters[1:3] + df <- data.frame(from = c("a", "b"), to = c(1, 2)) + expect_snapshot(error = TRUE, { + get_edge_ids(g, df) + }) +}) + +test_that("get_edge_id() errors correctly for wrong matrices", { + g <- make_full_graph(10) + mat <- matrix(c(1, 2, 3, 4), nrow = 2, ncol = 2) + lifecycle::expect_defunct(get_edge_ids(g, mat)) + mat <- matrix(c(1, 2, 1, 3, 1, 4), nrow = 2, ncol = 3) + lifecycle::expect_deprecated(get_edge_ids(g, mat)) +}) From 2a84bc2db90e6c5055e44925f00e14b7670883cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 31 Jan 2025 02:11:37 +0000 Subject: [PATCH 13/28] fledge: Bump version to 2.1.4.9005 (#1668) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 7c9e0560c4..ed27ead2b6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9004 +Version: 2.1.4.9005 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 7c647442af..3b79df2dc4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,12 @@ +# igraph 2.1.4.9005 + +## Features + +- `get_edge_ids()` accepts data frames and matrices (#1663). + + # igraph 2.1.4.9004 - Refactor: forward `get.adjacency.dense()` to `get.adjacency.sparse()` if attributes are present (#1483) (#1653). From bab3709a6ab541b786e5b98677fea8dc2b77972a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 2 Feb 2025 18:13:11 +0100 Subject: [PATCH 14/28] ci: Fix vendoring workflow --- .github/workflows/vendor.yaml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vendor.yaml b/.github/workflows/vendor.yaml index 9f544bd4f8..bf9e148d98 100644 --- a/.github/workflows/vendor.yaml +++ b/.github/workflows/vendor.yaml @@ -29,18 +29,23 @@ jobs: - uses: actions/checkout@v4 with: - repository: igraph/igraph - path: .git/igraph + repository: duckdb/duckdb + path: .git/duckdb fetch-depth: 0 - uses: ./.github/workflows/git-identity + - uses: ./.github/workflows/install + with: + cache-version: rcc-vendor + token: ${{ secrets.GITHUB_TOKEN }} + - name: Vendor sources id: vendor run: | git pull --rebase - ./vendor-one.sh .git/igraph - rm -rf .git/igraph + ./vendor-one.sh .git/duckdb + rm -rf .git/duckdb git push --dry-run # Check if ahead of upstream branch # If yes, set a step output From a707ecb15e301f46f15f67e1283d35d64ac0e957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kirill=20M=C3=BCller?= Date: Sun, 2 Feb 2025 18:24:57 +0100 Subject: [PATCH 15/28] ci: Use igraph repo for vendoring --- .github/workflows/vendor.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/vendor.yaml b/.github/workflows/vendor.yaml index bf9e148d98..dc306a2b49 100644 --- a/.github/workflows/vendor.yaml +++ b/.github/workflows/vendor.yaml @@ -29,8 +29,8 @@ jobs: - uses: actions/checkout@v4 with: - repository: duckdb/duckdb - path: .git/duckdb + repository: igraph/igraph + path: .git/igraph fetch-depth: 0 - uses: ./.github/workflows/git-identity @@ -44,8 +44,8 @@ jobs: id: vendor run: | git pull --rebase - ./vendor-one.sh .git/duckdb - rm -rf .git/duckdb + ./vendor-one.sh .git/igraph + rm -rf .git/igraph git push --dry-run # Check if ahead of upstream branch # If yes, set a step output From ff08743ebef8de7741b078cc468a1bfa0b071edd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 17:49:25 +0000 Subject: [PATCH 16/28] vendor: Update vendored sources to igraph/igraph@2ab6b87947187a0b046fa3cbb10a30ec370843aa (#1670) Co-authored-by: krlmlr --- src/rinterface.c | 6 ++---- src/vendor/cigraph/interfaces/functions.yaml | 2 +- src/vendor/cigraph/interfaces/types.yaml | 7 ------- src/vendor/igraph_version.h | 4 ++-- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/rinterface.c b/src/rinterface.c index a86469744e..0374a61fee 100644 --- a/src/rinterface.c +++ b/src/rinterface.c @@ -11106,10 +11106,8 @@ SEXP R_igraph_eulerian_cycle(SEXP graph) { R_SEXP_to_igraph(graph, &c_graph); IGRAPH_R_CHECK(igraph_vector_int_init(&c_edge_res, 0)); IGRAPH_FINALLY(igraph_vector_int_destroy, &c_edge_res); - if (0 != igraph_vector_int_init(&c_vertex_res, 0)) { - igraph_error("", __FILE__, __LINE__, IGRAPH_ENOMEM); - } - IGRAPH_FINALLY_PV(igraph_vector_int_destroy, &c_vertex_res); + IGRAPH_R_CHECK(igraph_vector_int_init(&c_vertex_res, 0)); + IGRAPH_FINALLY(igraph_vector_int_destroy, &c_vertex_res); /* Call igraph */ IGRAPH_R_CHECK(igraph_eulerian_cycle(&c_graph, &c_edge_res, &c_vertex_res)); diff --git a/src/vendor/cigraph/interfaces/functions.yaml b/src/vendor/cigraph/interfaces/functions.yaml index 163f863bcd..e6e923e805 100644 --- a/src/vendor/cigraph/interfaces/functions.yaml +++ b/src/vendor/cigraph/interfaces/functions.yaml @@ -2563,7 +2563,7 @@ igraph_eulerian_path: DEPS: edge_res ON graph, vertex_res ON graph igraph_eulerian_cycle: - PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES_PV vertex_res + PARAMS: GRAPH graph, OPTIONAL OUT EDGE_INDICES edge_res, OPTIONAL OUT VERTEX_INDICES vertex_res DEPS: edge_res ON graph, vertex_res ON graph ####################################### diff --git a/src/vendor/cigraph/interfaces/types.yaml b/src/vendor/cigraph/interfaces/types.yaml index f726e1f59b..4865b99292 100644 --- a/src/vendor/cigraph/interfaces/types.yaml +++ b/src/vendor/cigraph/interfaces/types.yaml @@ -157,13 +157,6 @@ VERTEX_INDICES: CTYPE: igraph_vector_int_t FLAGS: BY_REF -# Temporary, for https://github.com/igraph/rigraph/pull/1630 -# We should call the `_PV` versions for all types in the future. -VERTEX_INDICES_PV: - # An integer vector containing vertex indices. - CTYPE: igraph_vector_int_t - FLAGS: BY_REF - VERTEX_INDEX_PAIRS: # An integer vector containing pairs of vertex indices, in a flattened # representation diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 3bad40e7ff..60eec6ee76 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.15-47-g521624378" +#define IGRAPH_VERSION "0.10.15-48-g2ab6b8794" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 15 -#define IGRAPH_VERSION_PRERELEASE "47-g521624378" +#define IGRAPH_VERSION_PRERELEASE "48-g2ab6b8794" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major, From 16896de6bafd8d6ce17ba4b7c7a4491266bbd981 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 02:15:30 +0000 Subject: [PATCH 17/28] fledge: Bump version to 2.1.4.9006 (#1671) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index ed27ead2b6..a1a3d78bd8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9005 +Version: 2.1.4.9006 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 3b79df2dc4..9bb0de284f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,18 @@ +# igraph 2.1.4.9006 + +## Continuous integration + +- Use igraph repo for vendoring. + +- Fix vendoring workflow. + +## vendor + +- Update vendored sources to igraph/igraph@2ab6b87947187a0b046fa3cbb10a30ec370843aa (#1670). + + # igraph 2.1.4.9005 ## Features From d9ef523678eec8899ee6b01a450f1b6804e69581 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szabolcs=20Horva=CC=81t?= Date: Mon, 3 Feb 2025 12:59:04 +0000 Subject: [PATCH 18/28] doc: clarify weights use in `layout_with_kk()` --- R/layout.R | 6 +++--- man/get.edge.ids.Rd | 5 +++-- man/get_edge_ids.Rd | 5 +++-- man/layout_with_kk.Rd | 6 +++--- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/R/layout.R b/R/layout.R index 83d665fe86..e0f859acc7 100644 --- a/R/layout.R +++ b/R/layout.R @@ -1491,9 +1491,9 @@ with_graphopt <- function(...) layout_spec(layout_with_graphopt, ...) #' iterations. #' @param kkconst Numeric scalar, the Kamada-Kawai vertex attraction constant. #' Typical (and default) value is the number of vertices. -#' @param weights Edge weights, larger values will result longer edges. -#' Note that this is opposite to [layout_with_fr()]. Weights must -#' be positive. +#' @param weights Edge weights, larger values will result in longer edges. +#' Note that this is the opposite of [layout_with_fr()], which produces +#' shorter edges for larger weights. Weights must be positive. #' @param minx If not `NULL`, then it must be a numeric vector that gives #' lower boundaries for the \sQuote{x} coordinates of the vertices. The length #' of the vector must match the number of vertices in the graph. diff --git a/man/get.edge.ids.Rd b/man/get.edge.ids.Rd index b57e9280cd..292bf6bce5 100644 --- a/man/get.edge.ids.Rd +++ b/man/get.edge.ids.Rd @@ -9,8 +9,9 @@ get.edge.ids(graph, vp, directed = TRUE, error = FALSE, multi = deprecated()) \arguments{ \item{graph}{The input graph.} -\item{vp}{The incident vertices, given as vertex ids or symbolic vertex -names. They are interpreted pairwise, i.e. the first and second are used for +\item{vp}{The incident vertices, given as a two-column data frame, two-column matrix, +or vector of vertex ids or symbolic vertex names. +For a vector, the values are interpreted pairwise, i.e. the first and second are used for the first edge, the third and fourth for the second, etc.} \item{directed}{Logical scalar, whether to consider edge directions in diff --git a/man/get_edge_ids.Rd b/man/get_edge_ids.Rd index 6c0c6a8bf8..f3cb5b74d8 100644 --- a/man/get_edge_ids.Rd +++ b/man/get_edge_ids.Rd @@ -9,8 +9,9 @@ get_edge_ids(graph, vp, directed = TRUE, error = FALSE) \arguments{ \item{graph}{The input graph.} -\item{vp}{The incident vertices, given as vertex ids or symbolic vertex -names. They are interpreted pairwise, i.e. the first and second are used for +\item{vp}{The incident vertices, given as a two-column data frame, two-column matrix, +or vector of vertex ids or symbolic vertex names. +For a vector, the values are interpreted pairwise, i.e. the first and second are used for the first edge, the third and fourth for the second, etc.} \item{directed}{Logical scalar, whether to consider edge directions in diff --git a/man/layout_with_kk.Rd b/man/layout_with_kk.Rd index 1ec7fa2639..89a0d89171 100644 --- a/man/layout_with_kk.Rd +++ b/man/layout_with_kk.Rd @@ -50,9 +50,9 @@ iterations.} \item{kkconst}{Numeric scalar, the Kamada-Kawai vertex attraction constant. Typical (and default) value is the number of vertices.} -\item{weights}{Edge weights, larger values will result longer edges. -Note that this is opposite to \code{\link[=layout_with_fr]{layout_with_fr()}}. Weights must -be positive.} +\item{weights}{Edge weights, larger values will result in longer edges. +Note that this is the opposite of \code{\link[=layout_with_fr]{layout_with_fr()}}, which produces +shorter edges for larger weights. Weights must be positive.} \item{minx}{If not \code{NULL}, then it must be a numeric vector that gives lower boundaries for the \sQuote{x} coordinates of the vertices. The length From 5027f29124182289fd6f4d4ce64ad1588845863c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 13:17:30 +0000 Subject: [PATCH 19/28] vendor: Update vendored sources to igraph/igraph@27624a9b65031aabd3bb976efe944524d9e7dd43 (#1672) Co-authored-by: krlmlr --- .../cigraph/src/layout/fruchterman_reingold.c | 18 ++++++++++-------- src/vendor/cigraph/src/layout/kamada_kawai.c | 12 ++++++++---- src/vendor/igraph_version.h | 4 ++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/vendor/cigraph/src/layout/fruchterman_reingold.c b/src/vendor/cigraph/src/layout/fruchterman_reingold.c index 1b2aafdda8..ebbb95697a 100644 --- a/src/vendor/cigraph/src/layout/fruchterman_reingold.c +++ b/src/vendor/cigraph/src/layout/fruchterman_reingold.c @@ -332,10 +332,11 @@ static igraph_error_t igraph_layout_i_grid_fr( * IGRAPH_LAYOUT_AUTOGRID. The last one uses the grid based * version only for large graphs, currently the ones with * more than 1000 vertices. - * \param weights Pointer to a vector containing edge weights, - * the attraction along the edges will be multiplied by these. - * Weights must be positive. - * It will be ignored if it is a null-pointer. + * \param weights Pointer to a vector containing edge weights. Weights must + * be positive. If \c NULL, all edges are assumed to have weight 1. + * The attraction along the edges will be multiplied by the weights, + * resulting in vertices connected by a high-weight edge being placed + * closer together. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote x \endquote coordinate for every vertex. @@ -441,10 +442,11 @@ igraph_error_t igraph_layout_fruchterman_reingold(const igraph_t *graph, * of movement alloved along one axis, within one step, for a * vertex. Currently it is decreased linearly to zero during * the iteration. - * \param weights Pointer to a vector containing edge weights, - * the attraction along the edges will be multiplied by these. - * Weights must be positive. - * It will be ignored if it is a null-pointer. + * \param weights Pointer to a vector containing edge weights. Weights must + * be positive. If \c NULL, all edges are assumed to have weight 1. + * The attraction along the edges will be multiplied by the weights, + * resulting in vertices connected by a high-weight edge being placed + * closer together. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum * \quote x \endquote coordinate for every vertex. diff --git a/src/vendor/cigraph/src/layout/kamada_kawai.c b/src/vendor/cigraph/src/layout/kamada_kawai.c index 26a377acf2..8ba083dcff 100644 --- a/src/vendor/cigraph/src/layout/kamada_kawai.c +++ b/src/vendor/cigraph/src/layout/kamada_kawai.c @@ -78,8 +78,10 @@ * and then \p maxiter iterations are performed. * \param kkconst The Kamada-Kawai vertex attraction constant. * Typical value: number of vertices. - * \param weights Edge weights, larger values will result longer edges. - * Weights must be positive. Pass \c NULL to assume unit weights + * \param weights A vector of edge weights. Weights are interpreted as edge + * \em lengths in the shortest path calculation used by the + * Kamada-Kawai algorithm. Therefore, vertices connected by high-weight + * edges will be placed further apart. Pass \c NULL to assume unit weights * for all edges. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum @@ -383,8 +385,10 @@ igraph_error_t igraph_layout_kamada_kawai(const igraph_t *graph, igraph_matrix_t * and then \p maxiter iterations are performed. * \param kkconst The Kamada-Kawai vertex attraction constant. * Typical value: number of vertices. - * \param weights Edge weights, larger values will result longer edges. - * Weights must be positive. Pass \c NULL to assume unit weights + * \param weights A vector of edge weights. Weights are interpreted as edge + * \em lengths in the shortest path calculation used by the + * Kamada-Kawai algorithm. Therefore, vertices connected by high-weight + * edges will be placed further apart. Pass \c NULL to assume unit weights * for all edges. * \param minx Pointer to a vector, or a \c NULL pointer. If not a * \c NULL pointer then the vector gives the minimum diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 60eec6ee76..81b133ca60 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.15-48-g2ab6b8794" +#define IGRAPH_VERSION "0.10.15-50-g27624a9b6" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 15 -#define IGRAPH_VERSION_PRERELEASE "48-g2ab6b8794" +#define IGRAPH_VERSION_PRERELEASE "50-g27624a9b6" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major, From f399b65d7f77238f1cf76a00e50380aa59074e38 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 4 Feb 2025 02:14:14 +0000 Subject: [PATCH 20/28] fledge: Bump version to 2.1.4.9007 (#1673) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index a1a3d78bd8..4b765803fc 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9006 +Version: 2.1.4.9007 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index 9bb0de284f..f61543c4df 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,16 @@ +# igraph 2.1.4.9007 + +## vendor + +- Update vendored sources to igraph/igraph@27624a9b65031aabd3bb976efe944524d9e7dd43 (#1672). + +## doc + +- Clarify weights use in `layout_with_kk()`. + + # igraph 2.1.4.9006 ## Continuous integration From 723e7d1334c6f9424d03470c9e94795b2ef25a92 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 6 Feb 2025 15:19:28 +0100 Subject: [PATCH 21/28] perf: Faster single bracket querying of a graph (#1658) --- R/indexing.R | 132 +++++++++++++++++++++++++-------- tests/testthat/test-indexing.R | 51 +++++++++++++ 2 files changed, 153 insertions(+), 30 deletions(-) diff --git a/R/indexing.R b/R/indexing.R index a91c081b52..453392e158 100644 --- a/R/indexing.R +++ b/R/indexing.R @@ -53,6 +53,60 @@ # - G[1:3,2,eid=TRUE] # create an edge sequence +get_adjacency_submatrix <- function(x, i, j, attr = NULL) { + # If i or j is NULL, assume all nodes + # if not NULL make sure to handle duplicates correctly + if (missing(i)) { + i_seq <- seq_len(vcount(x)) + has_i <- FALSE + } else{ + i_seq <- i + has_i <- TRUE + } + if (missing(j)) { + j_seq <- seq_len(vcount(x)) + has_j <- FALSE + } else { + j_seq <- j + has_j <- TRUE + } + + adj <- adjacent_vertices(x, i_seq, mode = "out") + i_degree <- map_int(adj, length) + + from_id <- rep(i_seq, i_degree) + to_id <- unlist(adj) + + edge_list <- data.frame(from = as.integer(from_id), to = as.integer(to_id)) + if(has_j){ + edge_list <- edge_list[edge_list$to %in% j_seq, ] + } + + row_indices <- edge_list[[1]] + col_indices <- edge_list[[2]] + + values <- if (is.null(attr)) { + 1 + } else { + valid_edges <- get_edge_ids(x, edge_list) + edge_attr(x, attr, valid_edges) + } + + res <- Matrix::sparseMatrix( + i = if (has_i) match(row_indices, i_seq) else row_indices, + j = if (has_j) match(col_indices, j_seq) else col_indices, + x = values, + dims = c(length(i_seq), length(j_seq)) + ) + + if ("name" %in% vertex_attr_names(x) && !is.null(dim(res))) { + rownames(res) <- vertex_attr(x, "name", i_seq) + colnames(res) <- vertex_attr(x, "name", j_seq) + } + + res +} + #' Query and manipulate a graph as it were an adjacency matrix #' @@ -151,12 +205,11 @@ #' #' @method [ igraph #' @export -`[.igraph` <- function(x, i, j, ..., from, to, - sparse = igraph_opt("sparsematrices"), - edges = FALSE, drop = TRUE, - attr = if (is_weighted(x)) "weight" else NULL) { - ## TODO: make it faster, don't need the whole matrix usually - +`[.igraph` <- function( + x, i, j, ..., from, to, + sparse = igraph_opt("sparsematrices"), + edges = FALSE, drop = TRUE, + attr = if (is_weighted(x)) "weight" else NULL) { ################################################################ ## Argument checks if ((!missing(from) || !missing(to)) && @@ -182,7 +235,7 @@ ################################################################## if (!missing(from)) { - res <- get_edge_ids(x, rbind(from, to), error = FALSE) + res <- get_edge_ids(x, data.frame(from, to), error = FALSE) if (edges) { ## nop } else if (!is.null(attr)) { @@ -192,32 +245,51 @@ } else { res <- as.logical(res) + 0 } - res - } else if (missing(i) && missing(j)) { - if (missing(edges)) { - as_adjacency_matrix(x, sparse = sparse, attr = attr) - } else { - as_adjacency_matrix(x, sparse = sparse, attr = attr, edges = edges) - } - } else if (missing(j)) { - if (missing(edges)) { - as_adjacency_matrix(x, sparse = sparse, attr = attr)[i, , drop = drop] - } else { - as_adjacency_matrix(x, sparse = sparse, attr = attr, edges = edges)[i, , drop = drop] - } - } else if (missing(i)) { - if (missing(edges)) { - as_adjacency_matrix(x, sparse = sparse, attr = attr)[, j, drop = drop] - } else { - as_adjacency_matrix(x, sparse = sparse, attr = attr, edges = edges)[, j, drop = drop] + return(res) + } + + if (missing(i) && missing(j)) { + return(as_adjacency_matrix(x, sparse = sparse, attr = attr)) + } + + # convert logical, character or negative i/j to proper vertex ids + # also check if any vertex is duplicated and record a mapping + i_has_dupes <- FALSE + j_has_dupes <- FALSE + + if (!missing(i)) { + i <- as_igraph_vs(x, i) + if (anyDuplicated(i)) { + i_has_dupes <- TRUE + i_dupl <- i + i <- unique(i) + i_map <- match(i_dupl, i) } - } else { - if (missing(edges)) { - as_adjacency_matrix(x, sparse = sparse, attr = attr)[i, j, drop = drop] - } else { - as_adjacency_matrix(x, sparse = sparse, attr = attr, edges = edges)[i, j, drop = drop] + } + if (!missing(j)) { + j <- as_igraph_vs(x, j) + if (anyDuplicated(j)) { + j_has_dupes <- TRUE + j_dupl <- j + j <- unique(j) + j_map <- match(j_dupl, j) } } + + sub_adjmat <- get_adjacency_submatrix(x, i = i, j = j, attr = attr) + if (i_has_dupes) { + sub_adjmat <- sub_adjmat[i_map, , drop = FALSE] + } + if (j_has_dupes) { + sub_adjmat <- sub_adjmat[, j_map, drop = FALSE] + } + + if (!sparse) { + as.matrix(sub_adjmat[, , drop = drop]) + } else{ + sub_adjmat[, , drop = drop] + } + } #' Query and manipulate a graph as it were an adjacency list diff --git a/tests/testthat/test-indexing.R b/tests/testthat/test-indexing.R index db4f36d26c..2bc8a2894f 100644 --- a/tests/testthat/test-indexing.R +++ b/tests/testthat/test-indexing.R @@ -1,4 +1,5 @@ test_that("[ indexing works", { + skip_if_not_installed("Matrix", minimum_version = "1.6.0") g <- make_tree(20) ## Are these vertices connected? expect_equal(g[1, 2], 1) @@ -9,6 +10,7 @@ test_that("[ indexing works", { }) test_that("[ indexing works with symbolic names", { + skip_if_not_installed("Matrix", minimum_version = "1.6.0") g <- make_test_named_tree() expect_equal(g["a", "b"], 1) @@ -28,6 +30,7 @@ test_that("[ indexing works with symbolic names", { }) test_that("[ indexing works with logical vectors", { + skip_if_not_installed("Matrix", minimum_version = "1.6.0") g <- make_test_named_tree() lres <- structure( @@ -70,6 +73,7 @@ test_that("[ indexing works with negative indices", { }) test_that("[ indexing works with weighted graphs", { + skip_if_not_installed("Matrix", minimum_version = "1.6.0") g <- make_test_weighted_tree() expect_equal(g[1, 2], 2) @@ -83,6 +87,7 @@ test_that("[ indexing works with weighted graphs", { }) test_that("[ indexing works with weighted graphs and symbolic names", { + skip_if_not_installed("Matrix", minimum_version = "1.6.0") g <- make_test_weighted_tree() expect_equal(g["a", "b"], 2) @@ -294,3 +299,49 @@ test_that("[[ handles from and to properly even if the graph has conflicting ver expect_true(is_igraph_vs(g[[1:3, 2:6]][[1]])) expect_true(is_igraph_vs(g[[from = 1:3, to = 2:6]][[1]])) }) + +test_that("[ handles errors in input parameters well", { + g <- make_full_graph(10) + expect_error(g[from = 1, to = 1, i = 1, j = 1]) + expect_error(g[from = 1]) + expect_error(g[to = 1]) + expect_error(g[from = NA, to = 2]) + expect_error(g[from = 1, to = NA]) + expect_error(g[from = 1, to = c(1, 2)]) +}) + +test_that("[ handles all combinations of i and/or j", { + A <- matrix( + rep( + c(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), + c( + 4L, 1L, 4L, 1L, 2L, 1L, 5L, 2L, 3L, 1L, 10L, 3L, 9L, 1L, 1L, 1L, 3L, 1L, 1L, + 1L, 1L, 1L, 10L, 1L, 1L, 1L, 1L, 5L, 11L, 1L, 2L, 1L, 5L, 1L, 3L + ) + ), + nrow = 10L, + ncol = 10L + ) + g <- graph_from_adjacency_matrix(A, "directed") + expect_equal(canonicalize_matrix(g[1:3, ]), A[1:3, ]) + expect_equal(canonicalize_matrix(g[, 4:7]), A[, 4:7]) + expect_equal(canonicalize_matrix(g[1:3, 4:7]), A[1:3, 4:7]) +}) + +test_that("[ handles duplicated i/j well", { + A <- matrix( + rep( + c(0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0), + c( + 4L, 1L, 4L, 1L, 2L, 1L, 5L, 2L, 3L, 1L, 10L, 3L, 9L, 1L, 1L, 1L, 3L, 1L, 1L, + 1L, 1L, 1L, 10L, 1L, 1L, 1L, 1L, 5L, 11L, 1L, 2L, 1L, 5L, 1L, 3L + ) + ), + nrow = 10L, + ncol = 10L + ) + g <- graph_from_adjacency_matrix(A, "directed") + expect_equal(canonicalize_matrix(g[c(1, 2, 2), ]), A[c(1, 2, 2), ]) + expect_equal(canonicalize_matrix(g[, c(3, 3, 4, 4)]), A[, c(3, 3, 4, 4)]) + expect_equal(canonicalize_matrix(g[c(1, 2, 2), c(3, 3, 4, 4)]), A[c(1, 2, 2), c(3, 3, 4, 4)]) +}) From 178bb743a020c5532e077eab2d23f2706fd91f33 Mon Sep 17 00:00:00 2001 From: David Schoch Date: Thu, 6 Feb 2025 21:00:59 +0100 Subject: [PATCH 22/28] refactor: enhance readability and performance of graph.adjacency.sparse (#1667) --- R/adjacency.R | 186 +++++++++----------------------- tests/testthat/test-adjacency.R | 61 ++++++++++- 2 files changed, 109 insertions(+), 138 deletions(-) diff --git a/R/adjacency.R b/R/adjacency.R index c24ad085a1..930790eb47 100644 --- a/R/adjacency.R +++ b/R/adjacency.R @@ -1,4 +1,3 @@ - #' Create graphs from adjacency matrices #' #' @description @@ -119,15 +118,15 @@ graph.adjacency <- function(adjmatrix, mode = c("directed", "undirected", "max", #' @examples #' #' g1 <- sample( -#' x = 0:1, size = 100, replace = TRUE, -#' prob = c(0.9, 0.1) -#' ) %>% +#' x = 0:1, size = 100, replace = TRUE, +#' prob = c(0.9, 0.1) +#' ) %>% #' matrix(ncol = 10) %>% #' graph_from_adjacency_matrix() #' #' g2 <- sample( -#' x = 0:5, size = 100, replace = TRUE, -#' prob = c(0.9, 0.02, 0.02, 0.02, 0.02, 0.02) +#' x = 0:5, size = 100, replace = TRUE, +#' prob = c(0.9, 0.02, 0.02, 0.02, 0.02, 0.02) #' ) %>% #' matrix(ncol = 10) %>% #' graph_from_adjacency_matrix(weighted = TRUE) @@ -193,8 +192,8 @@ graph.adjacency <- function(adjmatrix, mode = c("directed", "undirected", "max", #' x #' } #' expected_g8_weights <- non_zero_sort( -#' halve_diag(adj_matrix + t(adj_matrix) -#' )[lower.tri(adj_matrix, diag = TRUE)]) +#' halve_diag(adj_matrix + t(adj_matrix))[lower.tri(adj_matrix, diag = TRUE)] +#' ) #' actual_g8_weights <- sort(E(g8)$weight) #' all(expected_g8_weights == actual_g8_weights) #' @@ -229,7 +228,6 @@ graph_from_adjacency_matrix <- function(adjmatrix, ), weighted = NULL, diag = TRUE, add.colnames = NULL, add.rownames = NA) { - mode <- igraph.match.arg(mode) if (!is.matrix(adjmatrix) && !inherits(adjmatrix, "Matrix")) { @@ -377,157 +375,68 @@ mysummary <- function(x) { result } +pmax_AB <- function(A,B) { + change <- A < B + A[change] <- B[change] + A +} -graph.adjacency.sparse <- function(adjmatrix, mode, weighted = NULL, diag = TRUE) { +pmin_AB <- function(A, B) { + change <- A > B + A[change] <- B[change] + A +} + +graph.adjacency.sparse <- function(adjmatrix, mode, weighted = NULL, diag = TRUE, call = rlang::caller_env()) { if (!is.null(weighted)) { if (is.logical(weighted) && weighted) { weighted <- "weight" } if (!is.character(weighted)) { - stop("invalid value supplied for `weighted' argument, please see docs.") + cli::cli_abort("Invalid value supplied for `weighted' argument, please see docs.", call = call) } } if (nrow(adjmatrix) != ncol(adjmatrix)) { - stop("not a square matrix") + cli::cli_abort("Not a square matrix", call = call) } vc <- nrow(adjmatrix) + # Exit early for empty graphs + if (vc == 1 || Matrix::nnzero(adjmatrix) == 0) { + return(make_empty_graph(n = vc, directed = (mode == "directed"))) + } - ## to remove non-redundancies that can persist in a dgtMatrix if (inherits(adjmatrix, "dgTMatrix")) { adjmatrix <- as(adjmatrix, "CsparseMatrix") } else if (inherits(adjmatrix, "ddiMatrix")) { adjmatrix <- as(adjmatrix, "CsparseMatrix") } - if (mode == "directed") { - ## DIRECTED - el <- mysummary(adjmatrix) - if (!diag) { - el <- el[el[, 1] != el[, 2], ] - } - } else if (mode == "undirected") { - ## UNDIRECTED, must be symmetric if weighted + if (mode == "undirected") { if (!is.null(weighted) && !Matrix::isSymmetric(adjmatrix)) { - stop("Please supply a symmetric matrix if you want to create a weighted graph with mode=UNDIRECTED.") - } - if (diag) { - adjmatrix <- Matrix::tril(adjmatrix) - } else { - if (vc == 1) { - # Work around Matrix glitch - adjmatrix <- as(matrix(0), "dgCMatrix") - } else { - adjmatrix <- Matrix::tril(adjmatrix, -1) - } - } - el <- mysummary(adjmatrix) - rm(adjmatrix) - } else if (mode == "max") { - ## MAXIMUM - el <- mysummary(adjmatrix) - rm(adjmatrix) - if (!diag) { - el <- el[el[, 1] != el[, 2], ] - } - el <- el[el[, 3] != 0, ] - w <- el[, 3] - el <- el[, 1:2] - el <- cbind(pmin(el[, 1], el[, 2]), pmax(el[, 1], el[, 2])) - o <- order(el[, 1], el[, 2]) - el <- el[o, , drop = FALSE] - w <- w[o] - if (nrow(el) > 1) { - dd <- el[2:nrow(el), 1] == el[1:(nrow(el) - 1), 1] & - el[2:nrow(el), 2] == el[1:(nrow(el) - 1), 2] - dd <- which(dd) - if (length(dd) > 0) { - mw <- pmax(w[dd], w[dd + 1]) - w[dd] <- mw - w[dd + 1] <- mw - el <- el[-dd, , drop = FALSE] - w <- w[-dd] - } - } - el <- cbind(el, w) - } else if (mode == "upper") { - ## UPPER - if (diag) { - adjmatrix <- Matrix::triu(adjmatrix) - } else { - adjmatrix <- Matrix::triu(adjmatrix, 1) - } - el <- mysummary(adjmatrix) - rm(adjmatrix) - if (!diag) { - el <- el[el[, 1] != el[, 2], ] + cli::cli_abort( + "Please supply a symmetric matrix if you want to create a weighted graph with mode=UNDIRECTED.", + call = call + ) } + adjmatrix <- Matrix::tril(adjmatrix) } else if (mode == "lower") { - ## LOWER - if (diag) { - adjmatrix <- Matrix::tril(adjmatrix) - } else { - if (vc == 1) { - # Work around Matrix glitch - adjmatrix <- as(matrix(0), "dgCMatrix") - } else { - adjmatrix <- Matrix::tril(adjmatrix, -1) - } - } - el <- mysummary(adjmatrix) - rm(adjmatrix) - if (!diag) { - el <- el[el[, 1] != el[, 2], ] - } - } else if (mode == "min") { - ## MINIMUM - adjmatrix <- sign(adjmatrix) * sign(Matrix::t(adjmatrix)) * adjmatrix - el <- mysummary(adjmatrix) - rm(adjmatrix) - if (!diag) { - el <- el[el[, 1] != el[, 2], ] - } - el <- el[el[, 3] != 0, ] - w <- el[, 3] - el <- el[, 1:2] - el <- cbind(pmin(el[, 1], el[, 2]), pmax(el[, 1], el[, 2])) - o <- order(el[, 1], el[, 2]) - el <- el[o, ] - w <- w[o] - if (nrow(el) > 1) { - dd <- el[2:nrow(el), 1] == el[1:(nrow(el) - 1), 1] & - el[2:nrow(el), 2] == el[1:(nrow(el) - 1), 2] - dd <- which(dd) - if (length(dd) > 0) { - mw <- pmin(w[dd], w[dd + 1]) - w[dd] <- mw - w[dd + 1] <- mw - el <- el[-dd, ] - w <- w[-dd] - } - } - el <- cbind(el, w) + adjmatrix <- Matrix::tril(adjmatrix) + } else if (mode == "upper") { + adjmatrix <- Matrix::triu(adjmatrix) } else if (mode == "plus") { - ## PLUS adjmatrix <- adjmatrix + Matrix::t(adjmatrix) - if (diag) { - adjmatrix <- Matrix::tril(adjmatrix) - } else { - if (vc == 1) { - # Work around Matrix glitch - adjmatrix <- as(matrix(0), "dgCMatrix") - } else { - adjmatrix <- Matrix::tril(adjmatrix, -1) - } - } - el <- mysummary(adjmatrix) - rm(adjmatrix) - if (diag) { - loop <- el[, 1] == el[, 2] - el[loop, 3] <- el[loop, 3] / 2 - } - el <- el[el[, 3] != 0, ] + adjmatrix <- Matrix::tril(adjmatrix) + } else if (mode == "max") { + adjmatrix <- pmax_AB(Matrix::tril(adjmatrix), Matrix::t(Matrix::triu(adjmatrix))) + } else if (mode == "min") { + adjmatrix <- pmin_AB(Matrix::tril(adjmatrix), Matrix::t(Matrix::triu(adjmatrix))) + adjmatrix <- Matrix::drop0(adjmatrix) + } + el <- mysummary(adjmatrix) + if (!diag) { + el <- el[el[, 1] != el[, 2], ] } if (!is.null(weighted)) { @@ -536,7 +445,12 @@ graph.adjacency.sparse <- function(adjmatrix, mode, weighted = NULL, diag = TRUE names(weight) <- weighted res <- add_edges(res, edges = t(as.matrix(el[, 1:2])), attr = weight) } else { - edges <- unlist(apply(el, 1, function(x) rep(unname(x[1:2]), x[3]))) + if (max(el[, 3]) == 1) { + edges <- as.vector(t(el[, 1:2])) + } else { + row_repeats <- rep(seq_len(nrow(el)), times = el[, 3]) + edges <- as.vector(t(el[row_repeats, 1:2])) + } res <- make_graph(n = vc, edges, directed = (mode == "directed")) } res diff --git a/tests/testthat/test-adjacency.R b/tests/testthat/test-adjacency.R index d29544bb49..f072b456f1 100644 --- a/tests/testthat/test-adjacency.R +++ b/tests/testthat/test-adjacency.R @@ -302,7 +302,7 @@ test_that("graph_from_adjacency_matrix() works", { }) test_that("graph_from_adjacency_matrix() works -- dgCMatrix", { - skip_if_not_installed("Matrix") + skip_if_not_installed("Matrix",minimum_version = "1.6.0") M1 <- rbind( c(0, 0, 1, 1), @@ -597,7 +597,7 @@ test_that("graph_from_adjacency_matrix() snapshot", { }) test_that("graph_from_adjacency_matrix() snapshot for sparse matrices", { - skip_if_not_installed("Matrix") + skip_if_not_installed("Matrix", minimum_version = "1.6.0") rlang::local_options(lifecycle_verbosity = "warning") @@ -657,3 +657,60 @@ test_that("weighted graph_from_adjacency_matrix() works on integer matrices", { g <- graph_from_adjacency_matrix(data, weighted = TRUE) expect_equal(as.matrix(g[]), data) }) + +test_that("sparse/dense matrices no loops works",{ + skip_if_not_installed("Matrix", minimum_version = "1.6.0") + A <- diag(1, 5) + A[1, 2] <- 1 + g <- graph_from_adjacency_matrix(A, diag = FALSE) + expect_equal(ecount(g), 1) + expect_equal(get_edge_ids(g, c(1, 2)), 1) + + A <- as(A, "dgCMatrix") + g <- graph_from_adjacency_matrix(A, diag = FALSE) + expect_equal(ecount(g), 1) + expect_equal(get_edge_ids(g,c(1, 2)), 1) + +}) + +test_that("sparse/dense matrices multiple works",{ + skip_if_not_installed("Matrix", minimum_version = "1.6.0") + A <- matrix(0, 5, 5) + A[1, 2] <- 3 + g <- graph_from_adjacency_matrix(A, diag = FALSE, weighted = FALSE) + expect_equal(ecount(g), 3) + expect_equal(as_edgelist(g), matrix(c(1, 2), 3, 2, byrow = TRUE)) + + A <- as(A,"dgCMatrix") + g <- graph_from_adjacency_matrix(A,diag = FALSE) + expect_equal(ecount(g), 3) + expect_equal(as_edgelist(g), matrix(c(1, 2), 3, 2, byrow = TRUE)) + +}) + +test_that("sparse/dense matrices min/max/plus",{ + skip_if_not_installed("Matrix", minimum_version = "1.6.0") + A <- matrix(0, 5, 5) + A[1, 2] <- 3 + A[2, 1] <- 2 + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "max", weighted = TRUE) + expect_equal(E(g)$weight[1], 3) + + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "min", weighted = TRUE) + expect_equal(E(g)$weight[1], 2) + + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "plus", weighted = TRUE) + expect_equal(E(g)$weight[1], 5) + + A <- as(A,"dgCMatrix") + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "max", weighted = TRUE) + expect_equal(E(g)$weight[1], 3) + + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "min", weighted = TRUE) + expect_equal(E(g)$weight[1], 2) + + g <- graph_from_adjacency_matrix(A, diag = FALSE, mode = "plus", weighted = TRUE) + expect_equal(E(g)$weight[1], 5) + + +}) \ No newline at end of file From 602c42886db06a4de77c22e99a0e1306845c0005 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 7 Feb 2025 02:18:04 +0000 Subject: [PATCH 23/28] fledge: Bump version to 2.1.4.9008 (#1674) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 4b765803fc..8afa5050ad 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9007 +Version: 2.1.4.9008 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index f61543c4df..b0a17ca2d5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,16 @@ +# igraph 2.1.4.9008 + +## Refactoring + +- Enhance readability and performance of graph.adjacency.sparse (@schochastics, #1667). + +## Performance + +- Faster single bracket querying of a graph (@schochastics, #1465, #1658). + + # igraph 2.1.4.9007 ## vendor From 9dfb9b3a8713d9ff21b3a282b1893679ee2af985 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 10 Feb 2025 13:50:23 +0000 Subject: [PATCH 24/28] vendor: Update vendored sources to igraph/igraph@199fdbf0a511294c7444eaa25c1861e784b3aa8a (#1677) Co-authored-by: krlmlr --- src/vendor/cigraph/src/games/citations.c | 47 +++++++++++------------- src/vendor/igraph_version.h | 4 +- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/vendor/cigraph/src/games/citations.c b/src/vendor/cigraph/src/games/citations.c index cdb90be950..6fe4bb9526 100644 --- a/src/vendor/cigraph/src/games/citations.c +++ b/src/vendor/cigraph/src/games/citations.c @@ -50,8 +50,10 @@ static void igraph_i_citing_cited_type_game_free ( * based on the last time they were cited. Time is measured by the * addition of vertices and it is binned into \p agebins bins. * So if the current time step is \c t and the last citation to a - * given \c i vertex was made in time step \c t0, then \c - * (t-t0)/binwidth is calculated where binwidth is \c nodes/agebins+1, + * given \c i vertex was made in time step \c t0, then + * (t-t0) / binwidth + * is calculated where binwidth is + * nodes/agebins + 1, * in the last expression '/' denotes integer division, so the * fraction part is omitted. * @@ -74,7 +76,7 @@ static void igraph_i_citing_cited_type_game_free ( * step. * \param agebins The number of age bins to use. * \param preference Pointer to an initialized vector of length - * \c agebins+1. This contains the "attractivity" of the various + * agebins + 1. This contains the "attractivity" of the various * age bins, the last element is the attractivity of the vertices * which were never cited, and it should be greater than zero. * It is a good idea to have all positive values in this vector. @@ -97,7 +99,6 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, igraph_integer_t no_of_nodes = nodes; igraph_psumtree_t sumtree; igraph_vector_int_t edges; - igraph_integer_t i, j, k; igraph_integer_t *lastcit; igraph_integer_t *index; igraph_integer_t binwidth; @@ -109,27 +110,27 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, agebins, igraph_vector_size(preference)); } if (nodes < 0) { - IGRAPH_ERRORF("Number of nodes should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_ERRORF("Number of nodes must not be negative, received %" IGRAPH_PRId ".", IGRAPH_EINVAL, nodes); } if (edges_per_node < 0) { - IGRAPH_ERRORF("Number of edges per node should be non-negative, received %" IGRAPH_PRId ".", + IGRAPH_ERRORF("Number of edges per node must not be negative, received %" IGRAPH_PRId ".", IGRAPH_EINVAL, edges_per_node); } if (agebins < 1) { - IGRAPH_ERRORF("Number of age bins should be at least 1, received %" IGRAPH_PRId ".", + IGRAPH_ERRORF("Number of age bins must be at least 1, received %" IGRAPH_PRId ".", IGRAPH_EINVAL, agebins); } if (VECTOR(*preference)[agebins] <= 0) { - IGRAPH_ERRORF("The last element of the `preference' vector needs to be positive, but is %g.", + IGRAPH_ERRORF("The last element of the `preference' vector must be strictly positive, but is %g.", IGRAPH_EINVAL, VECTOR(*preference)[agebins]); } if (igraph_vector_min(preference) < 0) { - IGRAPH_ERRORF("The preference vector must contain only non-negative values, but found %g.", + IGRAPH_ERRORF("The preference vector must not contain negative values, but found %g.", IGRAPH_EINVAL, igraph_vector_min(preference)); } @@ -143,15 +144,11 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); lastcit = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - if (!lastcit) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(lastcit, "Insufficient memory for lastcit game."); IGRAPH_FINALLY(igraph_free, lastcit); index = IGRAPH_CALLOC(no_of_nodes + 1, igraph_integer_t); - if (!index) { - IGRAPH_ERROR("lastcit game failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(index, "Insufficient memory for lastcit game."); IGRAPH_FINALLY(igraph_free, index); IGRAPH_CHECK(igraph_psumtree_init(&sumtree, nodes)); @@ -165,12 +162,12 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, RNG_BEGIN(); - for (i = 1; i < no_of_nodes; i++) { + for (igraph_integer_t i = 1; i < no_of_nodes; i++) { /* Add new edges */ - for (j = 0; j < edges_per_node; j++) { + for (igraph_integer_t j = 0; j < edges_per_node; j++) { igraph_integer_t to; - igraph_real_t sum = igraph_psumtree_sum(&sumtree); + const igraph_real_t sum = igraph_psumtree_sum(&sumtree); if (sum == 0) { /* If none of the so-far added nodes have positive weight, * we choose one uniformly to connect to. */ @@ -190,11 +187,11 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, /* Update the preference of some vertices if they got to another bin. We need to know the citations of some older vertices, this is in the index. */ - for (k = 1; i - binwidth * k >= 1; k++) { - igraph_integer_t shnode = i - binwidth * k; - igraph_integer_t m = index[shnode], n = index[shnode + 1]; - for (j = 2 * m; j < 2 * n; j += 2) { - igraph_integer_t cnode = VECTOR(edges)[j + 1]; + for (igraph_integer_t k = 1; i - binwidth * k >= 1; k++) { + const igraph_integer_t shnode = i - binwidth * k; + const igraph_integer_t m = index[shnode], n = index[shnode + 1]; + for (igraph_integer_t j = 2 * m; j < 2 * n; j += 2) { + const igraph_integer_t cnode = VECTOR(edges)[j + 1]; if (lastcit[cnode] == shnode + 1) { IGRAPH_CHECK(igraph_psumtree_update(&sumtree, cnode, VECTOR(*preference)[k])); } @@ -206,8 +203,8 @@ igraph_error_t igraph_lastcit_game(igraph_t *graph, RNG_END(); igraph_psumtree_destroy(&sumtree); - igraph_free(index); - igraph_free(lastcit); + IGRAPH_FREE(index); + IGRAPH_FREE(lastcit); IGRAPH_FINALLY_CLEAN(3); IGRAPH_CHECK(igraph_create(graph, &edges, nodes, directed)); diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 81b133ca60..c3a40a6834 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.15-50-g27624a9b6" +#define IGRAPH_VERSION "0.10.15-51-g199fdbf0a" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 15 -#define IGRAPH_VERSION_PRERELEASE "50-g27624a9b6" +#define IGRAPH_VERSION_PRERELEASE "51-g199fdbf0a" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major, From fb7cb55853a05e70bb8cc39476727c8ae05f8c70 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 02:14:45 +0000 Subject: [PATCH 25/28] fledge: Bump version to 2.1.4.9009 (#1678) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 8afa5050ad..84532bfdba 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9008 +Version: 2.1.4.9009 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index b0a17ca2d5..f9976a41c5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,12 @@ +# igraph 2.1.4.9009 + +## vendor + +- Update vendored sources to igraph/igraph@199fdbf0a511294c7444eaa25c1861e784b3aa8a (#1677). + + # igraph 2.1.4.9008 ## Refactoring From 841782848b9c8193505bb73b66300efbf36113e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 12:02:24 +0000 Subject: [PATCH 26/28] vendor: Update vendored sources to igraph/igraph@7955e08f37feda6c61f684428063e13e5a71ff5f (#1679) Co-authored-by: krlmlr --- src/vendor/cigraph/src/properties/triangles.c | 26 ++++++++----------- src/vendor/igraph_version.h | 4 +-- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/vendor/cigraph/src/properties/triangles.c b/src/vendor/cigraph/src/properties/triangles.c index c306911669..3d9091c2ab 100644 --- a/src/vendor/cigraph/src/properties/triangles.c +++ b/src/vendor/cigraph/src/properties/triangles.c @@ -518,9 +518,8 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, igraph_real_t *res, igraph_transitivity_mode_t mode) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); igraph_real_t triples = 0, triangles = 0; - igraph_integer_t node, nn; igraph_integer_t maxdegree; igraph_integer_t *neis; igraph_vector_int_t order; @@ -529,7 +528,7 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, igraph_adjlist_t allneis; igraph_vector_int_t *neis1, *neis2; - igraph_integer_t i, j, neilen1, neilen2; + igraph_integer_t neilen1, neilen2; if (no_of_nodes == 0) { *res = mode == IGRAPH_TRANSITIVITY_ZERO ? 0.0 : IGRAPH_NAN; @@ -539,8 +538,7 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, IGRAPH_VECTOR_INT_INIT_FINALLY(&order, no_of_nodes); IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, no_of_nodes); - IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, - IGRAPH_LOOPS)); + IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL, IGRAPH_LOOPS)); maxdegree = igraph_vector_int_max(°ree) + 1; IGRAPH_CHECK(igraph_vector_int_order1(°ree, &order, maxdegree)); @@ -548,7 +546,7 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, IGRAPH_FINALLY_CLEAN(1); IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes); - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { VECTOR(rank)[ VECTOR(order)[i] ] = no_of_nodes - i - 1; } @@ -556,31 +554,29 @@ igraph_error_t igraph_transitivity_undirected(const igraph_t *graph, IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); - if (! neis) { - IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ - } + IGRAPH_CHECK_OOM(neis, "Insufficient memory for undirected global transitivity."); IGRAPH_FINALLY(igraph_free, neis); - for (nn = no_of_nodes - 1; nn >= 0; nn--) { - node = VECTOR(order)[nn]; + for (igraph_integer_t nn = no_of_nodes - 1; nn >= 0; nn--) { + const igraph_integer_t node = VECTOR(order)[nn]; IGRAPH_ALLOW_INTERRUPTION(); neis1 = igraph_adjlist_get(&allneis, node); neilen1 = igraph_vector_int_size(neis1); - triples += (igraph_real_t)neilen1 * (neilen1 - 1); + triples += (igraph_real_t) neilen1 * (neilen1 - 1); /* Mark the neighbors of 'node' */ - for (i = 0; i < neilen1; i++) { + for (igraph_integer_t i = 0; i < neilen1; i++) { igraph_integer_t nei = VECTOR(*neis1)[i]; neis[nei] = node + 1; } - for (i = 0; i < neilen1; i++) { + for (igraph_integer_t i = 0; i < neilen1; i++) { igraph_integer_t nei = VECTOR(*neis1)[i]; /* If 'nei' is not ready yet */ if (VECTOR(rank)[nei] > VECTOR(rank)[node]) { neis2 = igraph_adjlist_get(&allneis, nei); neilen2 = igraph_vector_int_size(neis2); - for (j = 0; j < neilen2; j++) { + for (igraph_integer_t j = 0; j < neilen2; j++) { igraph_integer_t nei2 = VECTOR(*neis2)[j]; if (neis[nei2] == node + 1) { triangles += 1.0; diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index c3a40a6834..4cac6132a4 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.15-51-g199fdbf0a" +#define IGRAPH_VERSION "0.10.15-52-g7955e08f3" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 15 -#define IGRAPH_VERSION_PRERELEASE "51-g199fdbf0a" +#define IGRAPH_VERSION_PRERELEASE "52-g7955e08f3" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major, From 9aa78e05bd754b465f2a23f0795e166f1bd2519c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 11 Feb 2025 19:13:04 +0000 Subject: [PATCH 27/28] vendor: Update vendored sources to igraph/igraph@6981deb377ddd9ca9f098d683a743c3dd793e563 (#1680) Co-authored-by: krlmlr --- src/vendor/cigraph/src/math/safe_intop.c | 8 ++++---- src/vendor/cigraph/src/misc/bipartite.c | 6 +++--- src/vendor/cigraph/src/misc/chordality.c | 6 ++---- src/vendor/cigraph/src/properties/triangles_template.h | 4 +--- src/vendor/cigraph/src/properties/triangles_template1.h | 4 +--- src/vendor/igraph_version.h | 4 ++-- 6 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/vendor/cigraph/src/math/safe_intop.c b/src/vendor/cigraph/src/math/safe_intop.c index ec6b8be8a4..15ac4e4d1f 100644 --- a/src/vendor/cigraph/src/math/safe_intop.c +++ b/src/vendor/cigraph/src/math/safe_intop.c @@ -33,9 +33,9 @@ igraph_error_t igraph_i_safe_mult(igraph_integer_t a, igraph_integer_t b, igraph /* Overflow-safe sum of integer vector elements. */ igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igraph_integer_t *res) { - igraph_integer_t i, n = igraph_vector_int_size(vec); + const igraph_integer_t n = igraph_vector_int_size(vec); igraph_integer_t sum = 0; - for (i=0; i < n; ++i) { + for (igraph_integer_t i=0; i < n; ++i) { IGRAPH_SAFE_ADD(sum, VECTOR(*vec)[i], &sum); } *res = sum; @@ -44,9 +44,9 @@ igraph_error_t igraph_i_safe_vector_int_sum(const igraph_vector_int_t *vec, igra /* Overflow-safe product of integer vector elements. */ igraph_error_t igraph_i_safe_vector_int_prod(const igraph_vector_int_t *vec, igraph_integer_t *res) { - igraph_integer_t i, n = igraph_vector_int_size(vec); + const igraph_integer_t n = igraph_vector_int_size(vec); igraph_integer_t prod = 1; - for (i=0; i < n; ++i) { + for (igraph_integer_t i=0; i < n; ++i) { IGRAPH_SAFE_MULT(prod, VECTOR(*vec)[i], &prod); } *res = prod; diff --git a/src/vendor/cigraph/src/misc/bipartite.c b/src/vendor/cigraph/src/misc/bipartite.c index c1812450c1..0bc14ee24f 100644 --- a/src/vendor/cigraph/src/misc/bipartite.c +++ b/src/vendor/cigraph/src/misc/bipartite.c @@ -125,7 +125,7 @@ igraph_error_t igraph_bipartite_projection_size(const igraph_t *graph, neilen1 = igraph_vector_int_size(neis1); for (igraph_integer_t j = 0; j < neilen1; j++) { igraph_integer_t neilen2, nei = VECTOR(*neis1)[j]; - igraph_vector_int_t *neis2 = igraph_adjlist_get(&adjlist, nei); + const igraph_vector_int_t *neis2 = igraph_adjlist_get(&adjlist, nei); if (IGRAPH_UNLIKELY(VECTOR(*types)[i] == VECTOR(*types)[nei])) { IGRAPH_ERROR("Non-bipartite edge found in bipartite projection.", IGRAPH_EINVAL); @@ -179,7 +179,7 @@ static igraph_error_t igraph_i_bipartite_projection(const igraph_t *graph, igraph_vector_int_t vertex_perm, vertex_index; igraph_vector_int_t edges; igraph_adjlist_t adjlist; - igraph_vector_int_t *neis1, *neis2; + const igraph_vector_int_t *neis1, *neis2; igraph_integer_t neilen1, neilen2; igraph_vector_int_t added; igraph_vector_int_t mult; @@ -354,7 +354,7 @@ igraph_error_t igraph_bipartite_projection(const igraph_t *graph, igraph_vector_int_t *multiplicity2, igraph_integer_t probe1) { - igraph_integer_t no_of_nodes = igraph_vcount(graph); + const igraph_integer_t no_of_nodes = igraph_vcount(graph); /* t1 is -1 if proj1 is omitted, it is 0 if it belongs to type zero, it is 1 if it belongs to type one. The same for t2 */ diff --git a/src/vendor/cigraph/src/misc/chordality.c b/src/vendor/cigraph/src/misc/chordality.c index 7cbcffc210..85056b7566 100644 --- a/src/vendor/cigraph/src/misc/chordality.c +++ b/src/vendor/cigraph/src/misc/chordality.c @@ -319,18 +319,16 @@ igraph_error_t igraph_is_chordal(const igraph_t *graph, (igraph_vector_int_t*) my_alpha, (igraph_vector_int_t*) my_alpham1)); } else if (alpha && !alpham1) { - igraph_integer_t v; IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpham1, no_of_nodes); my_alpham1 = &v_alpham1; - for (v = 0; v < no_of_nodes; v++) { + for (igraph_integer_t v = 0; v < no_of_nodes; v++) { igraph_integer_t i = VECTOR(*my_alpha)[v]; VECTOR(*my_alpham1)[i] = v; } } else if (!alpha && alpham1) { - igraph_integer_t i; IGRAPH_VECTOR_INT_INIT_FINALLY(&v_alpha, no_of_nodes); my_alpha = &v_alpha; - for (i = 0; i < no_of_nodes; i++) { + for (igraph_integer_t i = 0; i < no_of_nodes; i++) { igraph_integer_t v = VECTOR(*my_alpham1)[i]; VECTOR(*my_alpha)[v] = i; } diff --git a/src/vendor/cigraph/src/properties/triangles_template.h b/src/vendor/cigraph/src/properties/triangles_template.h index 8b83da7317..77dfc6edc3 100644 --- a/src/vendor/cigraph/src/properties/triangles_template.h +++ b/src/vendor/cigraph/src/properties/triangles_template.h @@ -71,9 +71,7 @@ for (i = 0; i < no_of_nodes; i++) { IGRAPH_CHECK(igraph_i_trans4_al_simplify(&allneis, &rank)); neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); -if (neis == 0) { - IGRAPH_ERROR("undirected local transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ -} +IGRAPH_CHECK_OOM(neis, "Insufficient memory to count triangles."); IGRAPH_FINALLY(igraph_free, neis); #ifndef TRIANGLES diff --git a/src/vendor/cigraph/src/properties/triangles_template1.h b/src/vendor/cigraph/src/properties/triangles_template1.h index 25329ea032..f495b5ff68 100644 --- a/src/vendor/cigraph/src/properties/triangles_template1.h +++ b/src/vendor/cigraph/src/properties/triangles_template1.h @@ -44,9 +44,7 @@ if (nodes_to_calc == 0) { } neis = IGRAPH_CALLOC(no_of_nodes, igraph_integer_t); -if (neis == 0) { - IGRAPH_ERROR("local undirected transitivity failed", IGRAPH_ENOMEM); /* LCOV_EXCL_LINE */ -} +IGRAPH_CHECK_OOM(neis, "Insufficient memory to count triangles."); IGRAPH_FINALLY(igraph_free, neis); IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); diff --git a/src/vendor/igraph_version.h b/src/vendor/igraph_version.h index 4cac6132a4..2cc971dcd2 100644 --- a/src/vendor/igraph_version.h +++ b/src/vendor/igraph_version.h @@ -28,11 +28,11 @@ __BEGIN_DECLS -#define IGRAPH_VERSION "0.10.15-52-g7955e08f3" +#define IGRAPH_VERSION "0.10.15-53-g6981deb37" #define IGRAPH_VERSION_MAJOR 0 #define IGRAPH_VERSION_MINOR 10 #define IGRAPH_VERSION_PATCH 15 -#define IGRAPH_VERSION_PRERELEASE "52-g7955e08f3" +#define IGRAPH_VERSION_PRERELEASE "53-g6981deb37" IGRAPH_EXPORT void igraph_version(const char **version_string, int *major, From da62f3d157fe05010f4a5a955f0a00a0780b80fa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Feb 2025 02:19:15 +0000 Subject: [PATCH 28/28] fledge: Bump version to 2.1.4.9010 (#1681) Co-authored-by: krlmlr --- DESCRIPTION | 2 +- NEWS.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 84532bfdba..8d8940f617 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: igraph Title: Network Analysis and Visualization -Version: 2.1.4.9009 +Version: 2.1.4.9010 Authors@R: c( person("Gábor", "Csárdi", , "csardi.gabor@gmail.com", role = "aut", comment = c(ORCID = "0000-0001-7098-9676")), diff --git a/NEWS.md b/NEWS.md index f9976a41c5..b1e475b7d8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,14 @@ +# igraph 2.1.4.9010 + +## vendor + +- Update vendored sources to igraph/igraph@6981deb377ddd9ca9f098d683a743c3dd793e563 (#1680). + +- Update vendored sources to igraph/igraph@7955e08f37feda6c61f684428063e13e5a71ff5f (#1679). + + # igraph 2.1.4.9009 ## vendor