Skip to content

Commit

Permalink
add convertToMaximization and convertToMinimization helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
jakobbossek committed Jan 21, 2016
1 parent 09c2f81 commit 2af5216
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 0 deletions.
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ S3method(shouldBeMinimized,smoof_wrapped_function)
export(addCountingWrapper)
export(addLoggingWrapper)
export(computeExpectedRunningTime)
export(convertToMaximization)
export(convertToMinimization)
export(doesCountEvaluations)
export(filterFunctionsByTags)
export(getAvailableTags)
Expand Down
72 changes: 72 additions & 0 deletions R/conversion.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#' @title
#' Conversion between minimization and maximization problems.
#'
#' @description
#' We can minimize f by maximizing -f. The majority of predefined objective functions
#' in \pkg{smoof} should be minimized by default. However, there is a handful of
#' functions, e.g., Keane or Alpine02, which shall be maximized by default.
#' For benchmarking studies it might be beneficial to inverse the direction.
#' The functions \code{convertToMaximization} and \code{convertToMinimization}
#' do exactly that keeping the attributes.
#'
#' @note
#' Internally no wrapper is put around the original function. Instead the function
#' is copied and the body of the function is manipulated via the \code{body} function.
#' Both functions will quit with an error if multi-objective functions are passed.
#'
#' @param fn [\code{smoof_function}]\cr
#' Smoof function.
#' @return [\code{smoof_function}]
#' @examples
#' # create a function which should be minimized by default
#' fn = makeSphereFunction(1L)
#' print(shouldBeMinimized(fn))
#' # Now invert the objective direction ...
#' fn2 = convertToMaximization(fn)
#' # and invert it again
#' fn3 = convertToMinimization(fn2)
#' # Now to convince ourselves we render some plots
#' opar = par(mfrow = c(1, 3))
#' plot(fn)
#' plot(fn2)
#' plot(fn3)
#' par(opar)
#' @name conversion
#' @rdname conversion
#' @export
convertToMaximization = function(fn) {
convertProblemDirection(fn, minimize.after = FALSE)
}

#' @rdname conversion
#' @export
convertToMinimization = function(fn) {
convertProblemDirection(fn, minimize.after = TRUE)
}

convertProblemDirection = function(fn, minimize.after = TRUE) {
if (isMultiobjective(fn)) {
stopf("Conversion to maximization only supported for single-objective problems
at the moment, but your function '%s' has %i", getName(fn), getNumberOfObjectives(fn))
}

# If both are true, we want to convert min to min
# If both are false, we want to convert max to max
# Otherwise the conversion is ok
if ((shouldBeMinimized(fn) && minimize.after) || (!shouldBeMinimized(fn) && !minimize.after)) {
stopf("Function should already be %s.", (if (minimize.after) "minimized" else "maximized"))
}

# make copy of function
fn2 = fn

# multiply function body with -1
body(fn2) = substitute(-1 * FUN, list(FUN = body(fn)))

# copy attributes (get dropped on body() call)
attributes(fn2) = attributes(fn)

# flip maximization stuff
fn2 = setAttribute(fn2, "minimize", !shouldBeMinimized(fn))
return(fn2)
}
48 changes: 48 additions & 0 deletions man/conversion.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions tests/testthat/test_conversion.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
context("conversion of minimization to maximization problem and vice versa")

test_that("min<->max conversion works as expected", {
# test function set
funs = list(
makeSphereFunction(1L),
makeAckleyFunction(1L),
makeRastriginFunction(1L)
)

checkSumOfMinMaxValuesVanishes = function(fun1, fun2, fun.name) {
# generate some random parameters within the box constraints
lb = getLowerBoxConstraints(fun)
ub = getUpperBoxConstraints(fun)
rnds = runif(50, min = lb, max = ub)

# build sum (since f(x) + (-f(x)) = 0 should hold)
sums = sapply(rnds, function(x) fun1(x) + fun2(x))
expect_true(all(sums < 0.0001), info = sprintf("Function value differences are not equal to 0 for function %s.", getName(fun)))
expect_true(shouldBeMinimized(fun1) != shouldBeMinimized(fun2))
}

for (fun in funs) {
# convert to maximization problem and check if function values changed
fun2 = convertToMaximization(fun)
checkSumOfMinMaxValuesVanishes(fun, fun2, getName(fun))

# convert back
expect_error(convertToMaximization(fun2))
fun3 = convertToMinimization(fun2)
checkSumOfMinMaxValuesVanishes(fun2, fun3, getName(fun))
}
})

0 comments on commit 2af5216

Please sign in to comment.