Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions .github/workflows/R-CMD-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

name: R-CMD-check
name: R-CMD-check.yaml

permissions: read-all

jobs:
R-CMD-check:
Expand All @@ -25,22 +26,22 @@ jobs:
- {os: macos-latest, r: 'release'}

- {os: windows-latest, r: 'release'}
# use 4.1 to check with rtools40's older compiler
- {os: windows-latest, r: '4.1'}
# use 4.0 or 4.1 to check with rtools40's older compiler
- {os: windows-latest, r: 'oldrel-4'}

- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}
- {os: ubuntu-latest, r: 'oldrel-2'}
- {os: ubuntu-latest, r: 'oldrel-3'}
- {os: ubuntu-latest, r: 'oldrel-4'}
- {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel-1'}
- {os: ubuntu-latest, r: 'oldrel-2'}
- {os: ubuntu-latest, r: 'oldrel-3'}
- {os: ubuntu-latest, r: 'oldrel-4'}

env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
R_KEEP_PKG_SOURCE: yes

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

Expand All @@ -58,3 +59,4 @@ jobs:
- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")'
9 changes: 5 additions & 4 deletions .github/workflows/pkgdown.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
release:
types: [published]
workflow_dispatch:

name: pkgdown
name: pkgdown.yaml

permissions: read-all

jobs:
pkgdown:
Expand All @@ -22,7 +23,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-pandoc@v2

Expand All @@ -41,7 +42,7 @@ jobs:

- name: Deploy to GitHub pages 🚀
if: github.event_name != 'pull_request'
uses: JamesIves/github-pages-deploy-action@v4.4.1
uses: JamesIves/github-pages-deploy-action@v4.5.0
with:
clean: false
branch: gh-pages
Expand Down
12 changes: 9 additions & 3 deletions .github/workflows/pr-commands.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ on:
issue_comment:
types: [created]

name: Commands
name: pr-commands.yaml

permissions: read-all

jobs:
document:
Expand All @@ -13,8 +15,10 @@ jobs:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: r-lib/actions/pr-fetch@v2
with:
Expand Down Expand Up @@ -50,8 +54,10 @@ jobs:
runs-on: ubuntu-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: r-lib/actions/pr-fetch@v2
with:
Expand Down
28 changes: 20 additions & 8 deletions .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

name: test-coverage
name: test-coverage.yaml

permissions: read-all

jobs:
test-coverage:
Expand All @@ -15,36 +16,47 @@ jobs:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: r-lib/actions/setup-r@v2
with:
use-public-rspm: true

- uses: r-lib/actions/setup-r-dependencies@v2
with:
extra-packages: any::covr
extra-packages: any::covr, any::xml2
needs: coverage

- name: Test coverage
run: |
covr::codecov(
cov <- covr::package_coverage(
quiet = FALSE,
clean = FALSE,
install_path = file.path(Sys.getenv("RUNNER_TEMP"), "package")
install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package")
)
print(cov)
covr::to_cobertura(cov)
shell: Rscript {0}

- uses: codecov/codecov-action@v5
with:
# Fail if error if not on PR, or if on PR and token is given
fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }}
files: ./cobertura.xml
plugins: noop
disable_search: true
token: ${{ secrets.CODECOV_TOKEN }}

- name: Show testthat output
if: always()
run: |
## --------------------------------------------------------------------
find ${{ runner.temp }}/package -name 'testthat.Rout*' -exec cat '{}' \; || true
find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true
shell: bash

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: coverage-test-failures
path: ${{ runner.temp }}/package
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# fastRG (development version)

- Added option to specify precise number of nodes in each block of a `dcsbm()` or `sbm()` via the `block_sizes` argument. This makes it easier to construct blockmodels with exactly repeated eigenvalues.
- The default behavior of `dcsbm()`, `sbm()` and `planted_partition()` has changed: when `block_sizes` or `pi` is unspecified, the new default is to balance block sizes as evenly as possible. Previously, `pi` was set to a constant vector, balancing block sizes in expectation only.
- Specifying both `k` and `B` in `dcsbm()` and `sbm()` now results in an error; only specify one of these arguments.

# fastRG 0.3.3

- Improve cross-linking to documentation of other packages for CRAN
Expand Down
2 changes: 1 addition & 1 deletion R/expected-degrees.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#'
#' B <- matrix(c(a, b, b, a), nrow = 2)
#'
#' b_model <- sbm(n = n, k = 2, B = B, poisson_edges = FALSE)
#' b_model <- sbm(n = n, B = B, poisson_edges = FALSE)
#'
#' b_model
#'
Expand Down
87 changes: 73 additions & 14 deletions R/undirected_dcsbm.R
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,19 @@ validate_undirected_dcsbm <- function(x) {
#' symmetrized via the update `B := B + t(B)`. Defaults to `NULL`.
#' You must specify either `k` or `B`, but not both.
#'
#' @param pi (relative block probabilities) Relative block
#' @param block_sizes (block sizes) Number of nodes in each block,
#' as a vector of integers. Must match the dimensions of `B`, or `k` and
#' must sum to `n`. Defaults to `NULL`, in which case blocks are made
#' to be as balanced as possible. You can specify either `pi` or
#' `block_sizes`, but not both.
#'
#' @param pi (block sizes) Relative block
#' probabilities. Must be positive, but do not need to sum
#' to one, as they will be normalized internally.
#' Must match the dimensions of `B` or `k`. Defaults to
#' `rep(1 / k, k)`, or a balanced blocks.
#' Must match the dimensions of `B` or `k`. Defaults to `NULL`, in which
#' case the `block_sizes` argument will take precedence. Note that
#' you can specify either `pi` or `block_sizes`, but should not
#' specify both.
#'
#' @param sort_nodes Logical indicating whether or not to sort the nodes
#' so that they are grouped by block and by `theta`. Useful for plotting.
Expand Down Expand Up @@ -239,18 +247,31 @@ validate_undirected_dcsbm <- function(x) {
#' edgelist <- sample_edgelist(custom_dcsbm)
#' edgelist
#'
#'
#' dcsbm_explicit_block_sizes <- dcsbm(
#' theta = rexp(100, 1 / 3) + 1,
#' B = B,
#' block_sizes = c(13, 17, 40, 14, 16),
#' expected_degree = 5
#' )
#'
#' # respects block sizes
#' summary(dcsbm_explicit_block_sizes$z)
#'
#' # efficient eigendecompostion that leverages low-rank structure in
#' # E(A) so that you don't have to form E(A) to find eigenvectors,
#' # as E(A) is typically dense. computation is
#' # handled via RSpectra
#'
#' population_eigs <- eigs_sym(custom_dcsbm)
#'
#'
dcsbm <- function(
n = NULL, theta = NULL,
k = NULL, B = NULL,
...,
pi = rep(1 / k, k),
block_sizes = NULL,
pi = NULL,
sort_nodes = TRUE,
force_identifiability = FALSE,
poisson_edges = TRUE,
Expand All @@ -273,6 +294,8 @@ dcsbm <- function(
theta <- stats::rlnorm(n, meanlog = 2, sdlog = 1)
} else if (is.null(n)) {
n <- length(theta)
} else {
stop("Must specify only one of `n` and `theta`, not both.", call. = FALSE)
}

### mixing matrix
Expand All @@ -297,18 +320,59 @@ dcsbm <- function(
}

k <- nrow(B)
} else {
stop("Must specify only one of `B` and `k`, not both.", call. = FALSE)
}

### block membership

if (length(pi) != nrow(B) || length(pi) != ncol(B)) {
stop("Length of `pi` must match dimensions of `B`.", call. = FALSE)
}
if (is.null(block_sizes) && is.null(pi)) {

base_value <- floor(n / k)
remainder <- n %% k
num_base_values <- k - remainder

upper_values <- rep(base_value + 1, remainder)
base_values <- rep(base_value, num_base_values)

block_sizes <- c(upper_values, base_values)

z <- sample(rep(1:k, times = block_sizes))

# for sorting by block size later
pi <- block_sizes / n
} else if (!is.null(block_sizes)) {

if(sum(block_sizes) != n) {
stop("Sum of `block_sizes` must equal `n` or `length(theta)`.", call. = FALSE)
}

z <- sample(rep(1:k, times = block_sizes))

# for sorting by block size later
pi <- block_sizes / n

} else if (!is.null(pi)) {
if (!is.null(pi) && !is.null(block_sizes)) {
stop("Length of `pi` must match dimensions of `B`.", call. = FALSE)
}

if (length(pi) != nrow(B) || length(pi) != ncol(B)) {
stop("Length of `pi` must match dimensions of `B`.", call. = FALSE)
}

if (any(pi < 0)) {
stop("All elements of `pi` must be >= 0.", call. = FALSE)
}


if (any(pi < 0)) {
stop("All elements of `pi` must be >= 0.", call. = FALSE)
z <- sample(k, n, replace = TRUE, prob = pi)
} else {
stop("Must at most one of `block_sizes` and `pi`.", call. = FALSE)
}

z <- factor(z, levels = 1:k, labels = paste0("block", 1:k))

# order mixing matrix by expected group size

if (k > 1 && sort_nodes) {
Expand All @@ -319,11 +383,6 @@ dcsbm <- function(

pi <- pi / sum(pi)

# sample block memberships

z <- sample(k, n, replace = TRUE, prob = pi)
z <- factor(z, levels = 1:k, labels = paste0("block", 1:k))

if (sort_nodes) {
z <- sort(z)
}
Expand Down
6 changes: 4 additions & 2 deletions R/undirected_planted_partition.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ planted_partition <- function(
between_block = NULL,
a = NULL,
b = NULL,
pi = rep(1 / k, k),
block_sizes = NULL,
pi = NULL,
sort_nodes = TRUE,
poisson_edges = TRUE,
allow_self_loops = TRUE) {
Expand All @@ -144,9 +145,10 @@ planted_partition <- function(

pp <- sbm(
n = n,
k = k,
k = NULL, # implicit in dimensions of B
B = B,
...,
block_sizes = block_sizes,
pi = pi,
sort_nodes = sort_nodes,
poisson_edges = poisson_edges,
Expand Down
Loading