Skip to content

Commit 8a5961c

Browse files
authored
Merge pull request #58 from jwood000/distinct_compositions
Distinct compositions
2 parents 7553740 + 3b6f89e commit 8a5961c

File tree

155 files changed

+10049
-2416
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

155 files changed

+10049
-2416
lines changed

.Rbuildignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,7 @@ scripts/*
1010
create_md.R
1111
SO_Answers/*
1212
^codecov\.yml$
13+
^ai_git_helpers$
1314
^_pkgdown\.yml$
14-
^CRAN_RELEASE_CHECKLIST.md$
15+
^CRAN_RELEASE_CHECKLIST\.md$
16+

.codacy.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
exclude_paths:
3+
- "ai_git_helpers/**"
4+
- "docs/**"
5+
- "vignettes/**"
6+
- "man/**"

.github/workflows/test-coverage.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
env:
1515
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
16+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
1617

1718
steps:
1819
- uses: actions/checkout@v4

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: RcppAlgos
2-
Version: 2.9.5
2+
Version: 2.10.0
33
Title: High Performance Tools for Combinatorics and Computational Mathematics
44
Description: Provides optimized functions and flexible iterators implemented in
55
C++ for solving problems in combinatorics and computational mathematics.

NEWS.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,31 @@
1+
# RcppAlgos 2.10.0
2+
3+
## New Features:
4+
5+
* Added parallel capabilities to all ranking functions via the new `nThreads` argument (e.g. `partitionsRank(..., nThreads = 4)`, `comboRank(..., nThreads = 4)`).
6+
* Implemented a next-lexicographical algorithm for generating distinct integer compositions, enabling efficient large-scale generation such as `compositionsGeneral(50, 8)`.
7+
* Added accompanying algorithms for the distinct integer composition case, exposed through: `compositionsSample`, `compositionsRank`, and `compositionsIter`.
8+
* Enhanced `permuteCount()` to count permutations of partitions when called with `constraintFun = "sum"` and `comparisonFun = "=="`, allowing optimized counting in cases that reduce to partition/composition counting.
9+
10+
## Bug Fixes:
11+
12+
* Improved input validation for constraint-based calls by requiring `comparisonFun` to be a character vector (now errors early with a clearer message).
13+
* Fixed edge-case handling in partition iteration logic where boundary-derived indices could become negative, preventing incorrect behavior in some partition/multiset scenarios.
14+
15+
## Improvements:
16+
17+
* Added a package load-time check that validates the loaded shared library matches the installed package version, producing a clear reinstall/restart error instead of potential crashes from stale binaries.
18+
* Improved handling of singleton `v` with singleton `freqs` so that numeric values are interpreted correctly in some constrained/ranking paths.
19+
* Added nonexported `permutePartsDesign()` to inspect the partition-design/counting setup used by `permuteCount()` when it reduces to a partition/composition counting problem.
20+
21+
## Performance:
22+
23+
* General performance improvements for ranking and composition-related algorithms, including multi-threaded ranking support.
24+
25+
## Internal:
26+
27+
* Added developer tooling and expanded internal type/class infrastructure to support the new composition and counting paths.
28+
129
# RcppAlgos 2.9.5
230

331
## Other:
@@ -131,7 +159,7 @@
131159

132160
## Bug Fixes:
133161

134-
* Now checking class of input vector for partition funcitons.
162+
* Now checking class of input vector for partition functions.
135163

136164
* Now when `partitionsCount` returns 0, the number of results is zero. Before, we were checking for count of partitions to be greater than zero, otherwise we would use the standard combinatorial counting functions to determine the number of results. This lead to strange results with elements not present in the original vector.
137165

R/CheckLinkedVersion.R

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# ------------------------------------------------------------------------------
2+
# Linked DLL version check (adapted from rlang)
3+
#
4+
# This file contains a slightly modified version of the standalone
5+
# `check_linked_version()` implementation from the rlang package.
6+
#
7+
# Original source:
8+
# https://github.com/r-lib/rlang/blob/main/R/standalone-linked-version.R
9+
#
10+
# Copyright:
11+
# rlang authors
12+
#
13+
# License:
14+
# Unlicense (https://unlicense.org)
15+
#
16+
# Modifications:
17+
# Adapted for use in RcppAlgos to verify that the loaded shared library
18+
# matches the installed package version and to prevent crashes caused by
19+
# stale compiled binaries during development or installation issues.
20+
#
21+
# Full credit goes to the rlang team for the original implementation and design.
22+
# ------------------------------------------------------------------------------
23+
#
24+
# nocov start
25+
26+
CheckLinkedVersion <- local({
27+
# Keep in sync with standalone-downstream-deps.R
28+
howto_reinstall_msg <- function(pkg) {
29+
os <- tolower(Sys.info()[["sysname"]])
30+
31+
if (os == "windows") {
32+
url <- "https://github.com/jennybc/what-they-forgot/issues/62"
33+
c(
34+
i = sprintf("Please update %s to the latest version.", pkg),
35+
i = sprintf(
36+
"Updating packages on Windows requires precautions:\n <%s>",
37+
url
38+
)
39+
)
40+
} else {
41+
c(
42+
i = sprintf(
43+
"Please update %s with `install.packages(\"%s\")` and restart R.",
44+
pkg,
45+
pkg
46+
)
47+
)
48+
}
49+
}
50+
51+
function(pkg) {
52+
ver <- utils::packageVersion(pkg)
53+
54+
ns <- asNamespace(pkg)
55+
linked_ver_ptr <- ns[[paste0("_", pkg, "_linked_version")]]
56+
if (is.null(linked_ver_ptr)) {
57+
linked_ver <- ""
58+
} else {
59+
# Construct call to avoid NOTE when argument to `.Call()` is not
60+
# statically analysable
61+
linked_ver <- do.call(".Call", list(linked_ver_ptr))
62+
}
63+
64+
if (nzchar(linked_ver) && ver == linked_ver) {
65+
return(invisible(NULL))
66+
}
67+
68+
header <- sprintf("The %s package is not properly installed.", pkg)
69+
70+
if (nzchar(linked_ver)) {
71+
msg <- c(
72+
x = sprintf(
73+
"The DLL version (%s) does not correspond to the package version (%s).",
74+
linked_ver,
75+
ver
76+
)
77+
)
78+
} else {
79+
# Package does not have a version pointer. This happens when DLL
80+
# updating fails for the first version that includes the pointer.
81+
msg <- c(
82+
x = "The DLL version does not correspond to the package version."
83+
)
84+
}
85+
86+
msg <- c(msg, howto_reinstall_msg(pkg))
87+
msg <- paste(c(header, msg), collapse = "\n")
88+
stop(msg, call. = FALSE)
89+
}
90+
})
91+
92+
# nocov end

R/ComboPermuteUtility.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ComboPermuteGen <- function(
2525
if (RetValue == 1) {
2626
return(.Call(`_RcppAlgos_CombinatoricsStndrd`, v, m, repetition,
2727
freqs, lower, upper, Parallel, nThreads,
28-
pkgEnv$nThreads, IsComb))
28+
pkgEnv$maxThreads, IsComb))
2929
} else if (RetValue == 2) {
3030
return(.Call(`_RcppAlgos_CombinatoricsApply`, v, m,
3131
repetition, freqs, lower, upper,
@@ -34,7 +34,7 @@ ComboPermuteGen <- function(
3434
return(.Call(`_RcppAlgos_CombinatoricsCnstrt`, v, m, repetition,
3535
freqs, lower, upper, constraintFun, comparisonFun,
3636
limitConstraints, IsComb, keepResults, Parallel,
37-
nThreads, pkgEnv$nThreads, tolerance, FALSE, FALSE))
37+
nThreads, pkgEnv$maxThreads, tolerance, FALSE, FALSE))
3838
}
3939
}
4040

@@ -55,7 +55,7 @@ ComboPermuteSample <- function(
5555
return(.Call(
5656
`_RcppAlgos_SampleCombPerm`, v, m, repetition, freqs, sampleVec,
5757
IsComb, seed, n, sample, FUN, new.env(), Parallel, nThreads,
58-
pkgEnv$nThreads, namedSample, FUN.VALUE
58+
pkgEnv$maxThreads, namedSample, FUN.VALUE
5959
))
6060
}
6161

@@ -82,7 +82,7 @@ ComboPermuteIter <- function(
8282
IsCnstrd <- .Call(`_RcppAlgos_CheckConstrndCpp`, constraintFun,
8383
comparisonFun, limitConstraints)
8484
InitVals <- .Call(`_RcppAlgos_GetClassVals`, v, m, repetition,
85-
freqs, IsComb, FUN, nThreads, pkgEnv$nThreads,
85+
freqs, IsComb, FUN, nThreads, pkgEnv$maxThreads,
8686
IsCnstrd, FALSE, FALSE, NULL, NULL, NULL)
8787

8888
if (RetValue == 1) {

R/NumberTheory.R

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,47 @@
11
primeFactorizeSieve <- function(bound1, bound2 = NULL,
22
namedList = FALSE, nThreads = NULL) {
33
return(.Call(`_RcppAlgos_MotleyContainer`, bound1, bound2, FALSE, namedList,
4-
nThreads, pkgEnv$nThreads))
4+
nThreads, pkgEnv$maxThreads))
55
}
66

77
eulerPhiSieve <- function(bound1, bound2 = NULL,
88
namedVector = FALSE, nThreads = NULL) {
99
return(.Call(`_RcppAlgos_MotleyContainer`, bound1, bound2, TRUE, namedVector,
10-
nThreads, pkgEnv$nThreads))
10+
nThreads, pkgEnv$maxThreads))
1111
}
1212

1313
primeSieve <- function(bound1, bound2 = NULL, nThreads = NULL) {
1414
return(.Call(`_RcppAlgos_PrimeSieveCpp`, bound1, bound2, nThreads,
15-
pkgEnv$nCores, pkgEnv$nThreads))
15+
pkgEnv$nCores, pkgEnv$maxThreads))
1616
}
1717

1818
divisorsSieve <- function(bound1, bound2 = NULL,
1919
namedList = FALSE, nThreads = NULL) {
2020
return(.Call(`_RcppAlgos_DivNumSieveCpp`, bound1, bound2, TRUE, namedList,
21-
nThreads, pkgEnv$nThreads))
21+
nThreads, pkgEnv$maxThreads))
2222
}
2323

2424
numDivisorSieve <- function(bound1, bound2 = NULL,
2525
namedVector = FALSE, nThreads = NULL) {
2626
return(.Call(`_RcppAlgos_DivNumSieveCpp`, bound1, bound2, FALSE, namedVector,
27-
nThreads, pkgEnv$nThreads))
27+
nThreads, pkgEnv$maxThreads))
2828
}
2929

3030
primeFactorize <- function(v, namedList = FALSE, nThreads = NULL) {
3131
return(.Call(`_RcppAlgos_PollardRhoContainer`, v, namedList, TRUE,
32-
FALSE, nThreads, pkgEnv$nThreads))
32+
FALSE, nThreads, pkgEnv$maxThreads))
3333
}
3434

3535
divisorsRcpp <- function(v, namedList = FALSE, nThreads = NULL) {
3636
return(.Call(`_RcppAlgos_PollardRhoContainer`, v, namedList, FALSE,
37-
TRUE, nThreads, pkgEnv$nThreads))
37+
TRUE, nThreads, pkgEnv$maxThreads))
3838
}
3939

4040
isPrimeRcpp <- function(v, namedVector = FALSE, nThreads = NULL) {
4141
return(.Call(`_RcppAlgos_PollardRhoContainer`, v, namedVector, FALSE,
42-
FALSE, nThreads, pkgEnv$nThreads))
42+
FALSE, nThreads, pkgEnv$maxThreads))
4343
}
4444

4545
primeCount <- function(n, nThreads = NULL) {
46-
return(.Call(`_RcppAlgos_PrimeCountCpp`, n, nThreads, pkgEnv$nThreads))
46+
return(.Call(`_RcppAlgos_PrimeCountCpp`, n, nThreads, pkgEnv$maxThreads))
4747
}

R/S4Classes.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ setMethod(
214214
function(.Object, Rlist, nThreads, Return_DF) {
215215
.Object@ptr <- .Call(
216216
`_RcppAlgos_CartClassNew`, Rlist, nThreads,
217-
pkgEnv$nThreads, Return_DF
217+
pkgEnv$maxThreads, Return_DF
218218
)
219219
eval(str2expression(text = ALGOS_METHODS_SAN_PREV))
220220
}

R/Utility.R

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ ResolveVFreqs <- function(v) {
2626
##
2727
## N.B. We are unable to get back factors.
2828
##
29-
## N.B. We don't test for raws as table doesn't support raws
29+
## N.B. We don't test for class raw as table doesn't support them
3030
## Error in order(y) : unimplemented type 'raw' in 'orderVector1'
3131
conv_v <- if (identical(nms, as.character(as.integer(nms)))) {
3232
as.integer(nms)
@@ -51,8 +51,8 @@ GetTarget <- function(v, target) {
5151
return(target)
5252
}
5353

54-
GetRank <- function(..., v, repetition = FALSE,
55-
freqs = NULL, IsComb = TRUE) {
54+
GetRank <- function(..., v, repetition = FALSE, freqs = NULL,
55+
IsComb = TRUE, nThreads = NULL) {
5656

5757
n_args <- length(arg_s <- list(...))
5858

@@ -75,24 +75,29 @@ GetRank <- function(..., v, repetition = FALSE,
7575
idx <- match(if (is.matrix(obj)) t(obj) else obj, v)
7676
if (any(is.na(idx))) stop(msg)
7777
.Call(`_RcppAlgos_RankCombPerm`, idx, v, repetition, freqs,
78-
if (is.matrix(obj)) ncol(obj) else length(obj), IsComb)
78+
if (is.matrix(obj)) ncol(obj) else length(obj), IsComb,
79+
nThreads, pkgEnv$maxThreads)
7980
}, input)
8081
)
8182
} else if (is.matrix(input)) {
8283
idx <- match(t(input), v)
8384
if (any(is.na(idx))) stop(msg)
8485
return(.Call(`_RcppAlgos_RankCombPerm`, idx, v,
85-
repetition, freqs, ncol(input), IsComb));
86+
repetition, freqs, ncol(input), IsComb,
87+
nThreads, pkgEnv$maxThreads));
8688
} else {
8789
idx <- match(input, v)
8890
if (any(is.na(idx))) stop(msg)
8991
return(.Call(`_RcppAlgos_RankCombPerm`, idx, v,
90-
repetition, freqs, length(input), IsComb));
92+
repetition, freqs, length(input), IsComb,
93+
nThreads, pkgEnv$maxThreads));
9194
}
9295
}
9396

94-
GetRankPart <- function(..., v, repetition = FALSE, freqs = NULL,
95-
target = NULL, IsComposition = FALSE, weak = FALSE) {
97+
GetRankPart <- function(
98+
..., v, repetition = FALSE, freqs = NULL, target = NULL,
99+
IsComposition = FALSE, weak = FALSE, nThreads = NULL
100+
) {
96101

97102
n_args <- length(arg_s <- list(...))
98103
target <- GetTarget(v, target)
@@ -119,13 +124,20 @@ GetRankPart <- function(..., v, repetition = FALSE, freqs = NULL,
119124
return(
120125
Map(function(obj) {
121126
if (!is.numeric(obj)) stop(msg_cls)
122-
if ((is.matrix(obj) && any(rowSums(obj) != target)) ||
123-
sum(obj) != target) stop(msg_part)
127+
128+
if (is.matrix(obj)) {
129+
if (any(rowSums(obj) != target)) stop(msg_part)
130+
} else {
131+
if (sum(obj) != target) stop(msg_part)
132+
}
133+
124134
idx <- match(if (is.matrix(obj)) t(obj) else obj, v)
125135
if (any(is.na(idx))) stop(msg_sub)
136+
126137
.Call(`_RcppAlgos_RankPartitionMain`, idx, v, repetition, freqs,
127138
if (is.matrix(obj)) ncol(obj) else length(obj),
128-
"==", target, NULL, IsComposition, weak)
139+
"==", target, NULL, IsComposition, weak,
140+
nThreads, pkgEnv$maxThreads)
129141
}, input)
130142
)
131143
} else if (!is.numeric(input)) {
@@ -136,14 +148,14 @@ GetRankPart <- function(..., v, repetition = FALSE, freqs = NULL,
136148
if (any(is.na(idx))) stop(msg_sub)
137149
return(.Call(`_RcppAlgos_RankPartitionMain`, idx, v,
138150
repetition, freqs, ncol(input), "==", target,
139-
NULL, IsComposition, weak));
151+
NULL, IsComposition, weak, nThreads, pkgEnv$maxThreads));
140152
} else {
141153
if (sum(input) != target) stop(msg_part)
142154
idx <- match(input, v)
143155
if (any(is.na(idx))) stop(msg_sub)
144156
return(.Call(`_RcppAlgos_RankPartitionMain`, idx, v,
145157
repetition, freqs, length(input), "==", target,
146-
NULL, IsComposition, weak));
158+
NULL, IsComposition, weak, nThreads, pkgEnv$maxThreads));
147159
}
148160
}
149161

@@ -204,3 +216,32 @@ GridInputs <- function(...) {
204216
)
205217
)
206218
}
219+
220+
PermuteArgs <- function(...) {
221+
n_args <- length(arg_s <- list(...))
222+
223+
if (n_args > 0) {
224+
nm <- names(arg_s)
225+
tar_nm <- c("constraintFun", "comparisonFun", "limitConstraints")
226+
227+
if (all(tar_nm %in% nm) &&
228+
identical(as.character(arg_s[["constraintFun"]]), "sum") &&
229+
identical(as.character(arg_s[["comparisonFun"]]), "==") &&
230+
is.numeric(arg_s[["limitConstraints"]])) {
231+
232+
return(
233+
list(
234+
is_part = TRUE,
235+
target = arg_s[["limitConstraints"]]
236+
)
237+
)
238+
}
239+
}
240+
241+
return(
242+
list(
243+
is_part = FALSE,
244+
target = NULL
245+
)
246+
)
247+
}

0 commit comments

Comments
 (0)