From 0bdbf9beaa67a659a08261ed15a42093e81e7803 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 21 Sep 2026 10:42:57 +0100 Subject: [PATCH 1/3] Port `generate_ndset()` cliff shapes to the R package --- r/NEWS.md | 1 + r/R/generate.R | 46 +++++++++++++++++++++++++++++-- r/man/generate_ndset.Rd | 26 +++++++++++++++++ r/tests/testthat/test-generate.R | 20 ++++++++++++++ r/vignettes/articles/generate.Rmd | 13 +++++++++ 5 files changed, 103 insertions(+), 3 deletions(-) diff --git a/r/NEWS.md b/r/NEWS.md index 90caebaad..0e00692d6 100644 --- a/r/NEWS.md +++ b/r/NEWS.md @@ -1,6 +1,7 @@ # moocore (development) * `is_nondominated` is up to 10x faster in some inputs thanks to a customized radixsort implementation. + * New shapes `"cliff-concave"` and `"cliff-convex"` added to `generate_ndset()`. # moocore 0.3.2 diff --git a/r/R/generate.R b/r/R/generate.R index 0af289232..486e57143 100644 --- a/r/R/generate.R +++ b/r/R/generate.R @@ -70,6 +70,31 @@ #' hypersphere to the positive orthant. Thus, the sampling remains uniform. #' #' } +#' \item{`'cliff-concave'`}{ +#' +#' Equivalent to generating a 2D set using +#' `method='concave-sphere'`, then generating the other `d-2` columns +#' uniformly at random within the unit hypercube +#' \citep{EmmFon2011emo,GueFon2017hv4d}. This method does not make sense +#' for `d=2`. +#' +#' In the resulting set, the first two columns are mutually nondominated, +#' that is, no point can dominate another regardless of the other +#' objectives, while the remaining `d-2` columns do not provide any +#' ordering. These sets are adversarial for algorithms that aim to exploit +#' dominance structure. +#' +#' The dimensions that are generated uniformly at random should be chosen +#' adversarially according to the algorithm being tested. Alternatively, the +#' columns may be randomly shuffled. +#' +#' } +#' \item{`'cliff-convex'`}{ +#' +#' Equivalent to `1 - generate_ndset(..., method='cliff-concave')`. +#' This method does not make sense for `d=2`. +#' +#' } #' \item{`'convex-simplex'`}{ #' #' Equivalent to `generate_ndset(..., method='simplex')^2`, which is convex @@ -114,6 +139,7 @@ #' generate_ndset(5, 3, "simplex", seed = 42, integer = TRUE) #' generate_ndset(4, 2, "sphere", seed = 123) #' generate_ndset(3, 5, "convex-sphere", seed = 123) +#' generate_ndset(3, 5, "cliff-convex", seed = 42) #' generate_ndset(4, 4, "convex-simplex", seed = 123) #' #' @export @@ -129,14 +155,23 @@ generate_ndset <- function(n, d, method, seed = NULL, integer = FALSE) x } - sample_sphere <- function() { - x <- abs(rnorm(n * d)) - dim(x) <- c(n, d) + sample_sphere <- function(dims = d) { + x <- abs(rnorm(n * dims)) + dim(x) <- c(n, dims) x <- x / sqrt(rowSums(x * x)) x } + sample_cliff_concave <- function() { + x <- matrix(0, nrow = n, ncol = d) + x[, 1:2] <- sample_sphere(2L) + cols <- seq.int(3L, d) + x[, cols] <- matrix(runif(n * (d - 2L)), nrow = n, ncol = d - 2L) + x + } + sample_convex_sphere <- function() 1. - sample_sphere() + sample_cliff_convex <- function() 1. - sample_cliff_concave() sample_convex_simplex <- function() sample_simplex()^2 sample_inverted_simplex <- function() 1. - sample_simplex() sample_concave_simplex <- function() 1. - sample_convex_simplex() @@ -145,6 +180,11 @@ generate_ndset <- function(n, d, method, seed = NULL, integer = FALSE) if (method %in% c("simplex", "linear", "L")) sample_simplex else if (method %in% c("concave-sphere", "sphere", "C")) sample_sphere else if (method %in% c("convex-sphere", "X")) sample_convex_sphere + else if (method %in% c("cliff-concave", "cliff-convex")) { + if (d <= 2L) + stop("method='", method, "' requires at least 3 dimensions") + if (method == "cliff-concave") sample_cliff_concave else sample_cliff_convex + } else if (method == "convex-simplex") sample_convex_simplex else if (method %in% c("inverted-simplex", "inverted-linear")) sample_inverted_simplex else if (method == "concave-simplex") sample_concave_simplex diff --git a/r/man/generate_ndset.Rd b/r/man/generate_ndset.Rd index 778b03194..774a0908d 100644 --- a/r/man/generate_ndset.Rd +++ b/r/man/generate_ndset.Rd @@ -81,6 +81,31 @@ convex} \citep{IshHeSha2019regular}. This sampling is uniform. It corresponds to translating points from the negative orthant of the hypersphere to the positive orthant. Thus, the sampling remains uniform. +} +\item{\code{'cliff-concave'}}{ + +Equivalent to generating a 2D set using +\code{method='concave-sphere'}, then generating the other \code{d-2} columns +uniformly at random within the unit hypercube +\citep{EmmFon2011emo,GueFon2017hv4d}. This method does not make sense +for \code{d=2}. + +In the resulting set, the first two columns are mutually nondominated, +that is, no point can dominate another regardless of the other +objectives, while the remaining \code{d-2} columns do not provide any +ordering. These sets are adversarial for algorithms that aim to exploit +dominance structure. + +The dimensions that are generated uniformly at random should be chosen +adversarially according to the algorithm being tested. Alternatively, the +columns may be randomly shuffled. + +} +\item{\code{'cliff-convex'}}{ + +Equivalent to \code{1 - generate_ndset(..., method='cliff-concave')}. +This method does not make sense for \code{d=2}. + } \item{\code{'convex-simplex'}}{ @@ -122,6 +147,7 @@ generate_ndset(5, 3, "simplex", seed = 42) generate_ndset(5, 3, "simplex", seed = 42, integer = TRUE) generate_ndset(4, 2, "sphere", seed = 123) generate_ndset(3, 5, "convex-sphere", seed = 123) +generate_ndset(3, 5, "cliff-convex", seed = 42) generate_ndset(4, 4, "convex-simplex", seed = 123) } diff --git a/r/tests/testthat/test-generate.R b/r/tests/testthat/test-generate.R index 38fbb17df..0b24b4411 100644 --- a/r/tests/testthat/test-generate.R +++ b/r/tests/testthat/test-generate.R @@ -4,10 +4,30 @@ test_that("generate_ndset", { one <- replicate(n,1) for (dim in seq(5,10)) { points <- generate_ndset(n, dim, "simplex") + expect_false(any_dominated(points)) expect_equal(rowSums(points), one) points <- generate_ndset(n, dim, "concave-sphere") + expect_false(any_dominated(points)) expect_equal(rowSums(points**2), one) points <- generate_ndset(n, dim, "convex-simplex") + expect_false(any_dominated(points)) expect_equal(rowSums(sqrt(points)), one) + points <- generate_ndset(n, dim, "cliff-concave") + expect_false(any_dominated(points)) + expect_equal(rowSums(points[, 1:2]^2), one) + expect_true(all(points[, 3:dim] >= 0 & points[, 3:dim] <= 1)) + points <- generate_ndset(n, dim, "cliff-convex") + expect_false(any_dominated(points)) + expect_equal(rowSums((1 - points[, 1:2])^2), one) + expect_true(all(points[, 3:dim] >= 0 & points[, 3:dim] <= 1)) } + + expect_error( + generate_ndset(n, 2L, "cliff-concave"), + "requires at least 3 dimensions" + ) + expect_error( + generate_ndset(n, 2L, "cliff-convex"), + "requires at least 3 dimensions" + ) }) diff --git a/r/vignettes/articles/generate.Rmd b/r/vignettes/articles/generate.Rmd index ff0642eed..55741d60f 100644 --- a/r/vignettes/articles/generate.Rmd +++ b/r/vignettes/articles/generate.Rmd @@ -229,6 +229,19 @@ fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "concave-simplex", seed plotly_side_by_side(fig1, fig2) ``` +# Cliff sets (3D) + +The `cliff` type is often used to test the performance of algorithms for +computing the hypervolume [@EmmFon2011emo; @GueFon2017hv4d], due to its +particular structure. + +```{r generate_3d_cliff} +fig1 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "cliff-concave", seed = 42), title = 'method="cliff-concave"') +fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "cliff-convex", seed = 42), title = 'method="cliff-convex"') + +plotly_side_by_side(fig1, fig2) +``` + # Uniform sampling (moocore) vs projections of uniform samples (naive) Naive methods for sampling such sets usually sample points uniformly in the From b00852dc935d026e6e6155414b470dfac70cc902 Mon Sep 17 00:00:00 2001 From: MLopez-Ibanez <2620021+MLopez-Ibanez@users.noreply.github.com> Date: Mon, 21 Sep 2026 11:17:37 +0100 Subject: [PATCH 2/3] r/NEWS.md: Add development version number so that is shows on the webpage. --- r/NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/r/NEWS.md b/r/NEWS.md index 0e00692d6..eaab85def 100644 --- a/r/NEWS.md +++ b/r/NEWS.md @@ -1,4 +1,4 @@ -# moocore (development) +# moocore 0.3.2.900 * `is_nondominated` is up to 10x faster in some inputs thanks to a customized radixsort implementation. * New shapes `"cliff-concave"` and `"cliff-convex"` added to `generate_ndset()`. From d15c2f02b2ca5811f632f03d7cce089ff1bb634b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 21 Sep 2026 17:03:23 +0000 Subject: [PATCH 3/3] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/lorenzwalthert/precommit: v0.4.3.9029 → v0.4.3.9030](https://github.com/lorenzwalthert/precommit/compare/v0.4.3.9029...v0.4.3.9030) - [github.com/tox-dev/tox-ini-fmt: 1.7.2 → 1.9.0](https://github.com/tox-dev/tox-ini-fmt/compare/1.7.2...1.9.0) - [github.com/tox-dev/pyproject-fmt: v2.25.2 → v2.29.4](https://github.com/tox-dev/pyproject-fmt/compare/v2.25.2...v2.29.4) - [github.com/astral-sh/ruff-pre-commit: v0.15.21 → v0.16.8](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.21...v0.16.8) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 99eccc218..6355ed3e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,7 @@ ci: repos: - repo: https://github.com/lorenzwalthert/precommit - rev: v0.4.3.9029 + rev: v0.4.3.9030 hooks: - id: parsable-R - id: no-browser-statement @@ -49,19 +49,19 @@ repos: exclude: '(\.Rd|python/doc/source/reference/.*|test-doctest-.*)' - repo: https://github.com/tox-dev/tox-ini-fmt - rev: 1.7.2 + rev: 1.9.0 hooks: - id: tox-ini-fmt - repo: https://github.com/tox-dev/pyproject-fmt - rev: v2.25.2 + rev: v2.29.4 hooks: - id: pyproject-fmt additional_dependencies: ["tox>=4.12.1"] - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.21 + rev: v0.16.8 hooks: # Run the formatter. - id: ruff-format