From 06c7605011e085241d917e8608928c20eeebb52a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:26:20 +0000 Subject: [PATCH] r/R/generate.R, r/vignettes/articles/generate.Rmd, r/NEWS.md: Apply Python generate_ndset changes Co-authored-by: MLopez-Ibanez <2620021+MLopez-Ibanez@users.noreply.github.com> Agent-Logs-Url: https://github.com/multi-objective/moocore/sessions/90414f69-8c2c-4246-b119-24d3f1f502f8 --- r/NEWS.md | 1 + r/R/generate.R | 49 ++++++++++++++++++++++--------- r/vignettes/articles/generate.Rmd | 48 +++++++++++++++++++++++------- 3 files changed, 74 insertions(+), 24 deletions(-) diff --git a/r/NEWS.md b/r/NEWS.md index 671e98fc5..1c27c11ba 100644 --- a/r/NEWS.md +++ b/r/NEWS.md @@ -1,5 +1,6 @@ # moocore 0.2.0.900 + * `generate_ndset()`: New shapes `"inverted-simplex"` and `"concave-simplex"`. Shape `"convex-simplex"` is now equivalent to `generate_ndset(..., method="simplex")^2`, which is slightly more uniform than the previous approach. * `r2_exact()` implements the exact computation of the R2 indicator for bi-objective solution sets. * `hypervolume()` is significantly faster for more than four dimensions (Andreia P. Guerreiro). * `hypervolume()` now handles 1D inputs and provides a clear error for 0D inputs (#58). diff --git a/r/R/generate.R b/r/R/generate.R index b2a056ee5..c234ecaad 100644 --- a/r/R/generate.R +++ b/r/R/generate.R @@ -28,11 +28,21 @@ #' standard simplex. #' * `"concave-sphere"`, `"sphere"`, or `"C"`: Uniformly samples points #' on the positive orthant of the unit hypersphere (concave for minimisation). -#' * `"convex-sphere"` or `"X"`: Equivalent to `1 - generate_ndset(..., method="concave-sphere")`, -#' which is convex for minimisation. -#' * `"convex-simplex"`: Equivalent to `generate_ndset(..., method="concave-sphere")^4`, +#' * `"convex-simplex"`: Equivalent to `generate_ndset(..., method="simplex")^2`, #' which is convex for minimisation. Such a set cannot be obtained by any -#' affine transformation of a subset of the hyper-sphere. +#' affine transformation of a subset of the hyper-sphere. This sampling is +#' *not* uniform. +#' * `"convex-sphere"` or `"X"`: Equivalent to `1 - generate_ndset(..., +#' method="concave-sphere")`, which is convex for minimisation. This shape +#' has also been called *inverted convex* \citep{IshHeSha2019regular}. This +#' sampling is uniform. +#' * `"inverted-simplex"` or `"inverted-linear"`: Equivalent to `1 - +#' generate_ndset(..., method="simplex")`. This sampling is uniform. +#' * `"concave-simplex"`: Equivalent to `1 - generate_ndset(..., +#' method="convex-simplex")`, which is concave for minimisation. This shape +#' has also been called *inverted concave* \citep{IshHeSha2019regular}. This +#' sampling is *not* uniform because `method="convex-simplex"` is also not +#' uniform. #' #' Method `"simplex"` uniformly samples points on the standard #' \eqn{(d-1)}-simplex defined by \eqn{\{x \in R_+^d : \sum_i x_i = 1\}}. This @@ -46,7 +56,7 @@ #' #' Sampling from either the standard normal distribution #' \citep{GueFonPaq2021hv} or the uniform distribution \citep{LacKlaFon2017box} -#' does not produce a uniform distribution when projected into the simplex. +#' does not produce a uniform distribution when projected onto the simplex. #' #' Method `"concave-sphere"` uniformly samples points on the positive orthant #' of the hyper-sphere, which is concave when all objectives are minimised. @@ -56,19 +66,26 @@ #' then dividing each value by the l2-norm of the vector, \eqn{z_i = #' \frac{|x_i|}{\|\vec{x}\|_2}} \citep{Muller1959sphere}. The absolute value in #' the numerator ensures that points are sampled on the positive orthant of the -#' hyper-sphere. Sampling from the uniform distribution -#' \citep{LacKlaFon2017box} does not result in a uniform sampling when -#' projected onto the surface of the hyper-sphere. +#' hyper-sphere. Sampling from the uniform distribution \citep{LacKlaFon2017box} would +#' not result in a uniform sampling when projected onto the surface of the +#' hyper-sphere. +#' +#' Method `"convex-simplex"` is equivalent to `generate_ndset(..., +#' method="simplex")^2`, which is convex for minimisation problems. The +#' corresponding surface is equivalent to a simplex curved towards the +#' origin. Although the sampling on the simplex is uniform, the transformed +#' points are not. #' #' Method `"convex-sphere"` is equivalent to `1 - generate_ndset(..., #' method="concave-sphere")`, which is convex for minimisation problems. It #' corresponds to translating points from the negative orthant of the -#' hyper-sphere to the positive orthant. +#' hyper-sphere to the positive orthant. Thus, the sampling remains uniform. #' -#' Method `"convex-simplex"` is equivalent to `generate_ndset(..., -#' method="concave-sphere")^4`, which is convex for minimisation problems. -#' The corresponding surface is equivalent to a simplex curved towards the -#' origin. +#' Methods `"inverted-simplex"` and `"concave-simplex"` are similar +#' translations of `"simplex"` and `"convex-simplex"`, respectively. +#' These translations have been called `inverted` shapes in the literature and +#' have different properties than their `regular` counterparts +#' \citep{IshHeSha2019regular}. #' #' @references #' @@ -102,13 +119,17 @@ generate_ndset <- function(n, d, method, seed = NULL, integer = FALSE) } sample_convex_sphere <- function() 1. - sample_sphere() - sample_convex_simplex <- function() sample_sphere()^4L + sample_convex_simplex <- function() sample_simplex()^2 + sample_inverted_simplex <- function() 1. - sample_simplex() + sample_concave_simplex <- function() 1. - sample_convex_simplex() sample_fun <- ( 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 == "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 else stop("Unknown method =", method) ) diff --git a/r/vignettes/articles/generate.Rmd b/r/vignettes/articles/generate.Rmd index 6b29ecc81..5e4cbd705 100644 --- a/r/vignettes/articles/generate.Rmd +++ b/r/vignettes/articles/generate.Rmd @@ -36,9 +36,10 @@ mutually nondominated points. ```{r generate_rnd_2d} n <- 100 -methods <- c("simplex", "concave-sphere", "convex-sphere", "convex-simplex") -colors <- c("red", "blue", "green", "purple") -shapes <- c(16, 15, 17, 18) # ggplot2 shape codes +methods <- c("simplex", "concave-sphere", "convex-sphere", "convex-simplex", + "inverted-simplex", "concave-simplex") +colors <- c("red", "blue", "green", "purple", "orange", "yellow") +shapes <- c(16, 15, 17, 18, 16, 15) # ggplot2 shape codes df_all <- do.call(rbind.data.frame, lapply(methods, function(method) { points <- moocore::generate_ndset(n, 2, method = method, seed = 42) @@ -86,9 +87,9 @@ ggplot(df_int, aes(x = x, y = y, color = method, shape = method)) + A popular way to generate a convex nondominated set is to translate the negative orthant of the hyper-sphere to the unit hyper-cube (`convex-sphere`). However, there are other possible convex sets with -different properties. For example the method `convex-simplex` applies a -non-linear transformation to a `concave-sphere` set, resulting in a convex -transformation of the standard simplex. +different properties. For example the method `convex-simplex` squares the +points generated by method `simplex`, resulting in a convex transformation +of the standard simplex. First, let's define a few functions useful for plotting:
(show plotting code) @@ -179,11 +180,38 @@ plotly_side_by_side <- function(fig1, fig2, height="500px") { ```{r generate_3d_convex} n <- 2000 -points1 <- moocore::generate_ndset(n, 3, "convex-sphere", seed = 42) -fig1 <- plot_3d("simplex", points1, title = "Convex-sphere") +fig1 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "convex-sphere", seed = 42), title = 'method="convex-sphere"') +fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "convex-simplex", seed = 42), title = 'method="convex-simplex"') -points2 <- moocore::generate_ndset(n, 3, "convex-simplex", seed = 42) -fig2 <- plot_3d("simplex", points2, title = "Convex-simplex") +plotly_side_by_side(fig1, fig2) +``` + +# Inverted shapes + +@IshHeSha2019regular analyze the differences between *regular* and +*inverted* shapes, shown below on the left and right figures, +respectively. The differences between `simplex` and `inverted-simplex` +are not noticeable in 2D, but are significant in higher dimensions. + +```{r generate_3d_inverted_simplex} +n <- 2000 + +fig1 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "simplex", seed = 42), title = 'method="simplex"') +fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "inverted-simplex", seed = 42), title = 'method="inverted-simplex"') + +plotly_side_by_side(fig1, fig2) +``` + +```{r generate_3d_concave_convex_sphere} +fig1 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "concave-sphere", seed = 42), title = 'method="concave-sphere"') +fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "convex-sphere", seed = 42), title = 'method="convex-sphere"') + +plotly_side_by_side(fig1, fig2) +``` + +```{r generate_3d_convex_concave_simplex} +fig1 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "convex-simplex", seed = 42), title = 'method="convex-simplex"') +fig2 <- plot_3d("simplex", moocore::generate_ndset(n, 3, "concave-simplex", seed = 42), title = 'method="concave-simplex"') plotly_side_by_side(fig1, fig2) ```