# =========================================================================
# irace: An implementation in R of Iterated Race.
# -------------------------------------------------------------------------
#
# Copyright (C) 2010-2025
# Manuel López-Ibáñez <manuel.lopez-ibanez@manchester.ac.uk>
# Jérémie Dubois-Lacoste <jeremie.dubois-lacoste@ulb.ac.be>
# Leslie Perez Caceres <leslie.perez.caceres@ulb.ac.be>
#
# -------------------------------------------------------------------------
# This program is free software (software libre); you can redistribute
# it and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, you can obtain a copy of the GNU
# General Public License at:
# http://www.gnu.org/copyleft/gpl.html
# or by writing to the Free Software Foundation, Inc., 59 Temple Place,
# Suite 330, Boston, MA 02111-1307 USA
# -------------------------------------------------------------------------
# $Revision$
# =========================================================================
#' irace_license
#'
#' A character string containing the license information of \pkg{irace}.
#'
#' @export
## __VERSION__ below will be replaced by the version defined in R/version.R
## This avoids constant conflicts within this file.
irace_license <-
'#------------------------------------------------------------------------------
# irace: An implementation in R of (Elitist) Iterated Racing
# Version: __VERSION__
# Copyright (C) 2010-2025
# Manuel Lopez-Ibanez <manuel.lopez-ibanez@manchester.ac.uk>
# Jeremie Dubois-Lacoste
# Leslie Perez Caceres <leslie.perez.caceres@ulb.ac.be>
#
# This is free software, and you are welcome to redistribute it under certain
# conditions. See the GNU General Public License for details. There is NO
# WARRANTY; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# irace builds upon previous code from the race package:
# race: Racing methods for the selection of the best
# Copyright (C) 2003 Mauro Birattari
#------------------------------------------------------------------------------
'
cat_irace_license <- function()
cat(sub("__VERSION__", irace_version, irace_license, fixed=TRUE))
#' Higher-level interface to launch irace.
#'
#' @inheritParams defaultScenario
#'
#' @param output.width `integer(1)`\cr The width used for the screen
#' output.
#'
#' @details This function checks the correctness of the scenario, reads the
#' parameter space from \code{scenario$parameterFile}, invokes [irace()],
#' prints its results in various formatted ways, (optionally) calls
#' [psRace()] and, finally, evaluates the best configurations on the test
#' instances (if provided). If you want a lower-level interface that just
#' runs irace, please see function [irace()].
#'
#' @templateVar return_invisible TRUE
#' @template return_irace
#' @seealso
#' \describe{
#' \item{[irace_cmdline()]}{a command-line interface to [irace()].}
#' \item{[readScenario()]}{for reading a configuration scenario from a file.}
#' \item{[readParameters()]}{read the target algorithm parameters from a file.}
#' \item{[defaultScenario()]}{returns the default scenario settings of \pkg{irace}.}
#' }
#' @author Manuel López-Ibáñez and Jérémie Dubois-Lacoste
#' @concept running
#' @export
irace_main <- function(scenario, output.width = 9999L)
irace_common(scenario = scenario, simple=FALSE, output.width = output.width)
#' Test configurations given in the logfile (typically `irace.Rdata`) produced by \pkg{irace}.
#'
#' `testing_fromlog` executes the testing of the target algorithm configurations
#' found by an \pkg{irace} execution.
#'
#' @param logFile `character(1)`\cr Path to the logfile (typically `irace.Rdata`) produced by \pkg{irace}.
#'
#' @param testNbElites Number of (final) elite configurations to test. Overrides
#' the value found in `logFile`.
#'
#' @param testIterationElites `logical(1)`\cr If `FALSE`, only the final
#' `testNbElites` configurations are tested; otherwise, also test the best
#' configurations of each iteration. Overrides the value found in `logFile`.
#'
#' @param testInstancesDir Directory where testing instances are located, either absolute or relative to current directory.
#'
#' @param testInstancesFile File containing a list of test instances and optionally additional parameters for them.
#'
#' @param testInstances Character vector of the instances to be used in the `targetRunner` when executing the testing.
#'
#' @return `logical(1)`\cr `TRUE` if the testing ended successfully otherwise, `FALSE`.
#'
#' @details The function `testing_fromlog` loads the `logFile` and obtains the
#' testing setup and configurations to be tested. Within the `logFile`, the
#' variable `scenario$testNbElites` specifies how many final elite
#' configurations to test and `scenario$testIterationElites` indicates
#' whether test the best configuration of each iteration. The values may be
#' overridden by setting the corresponding arguments in this function. The
#' set of testing instances must appear in `scenario[["testInstances"]]`.
#'
#' @seealso [defaultScenario()] to provide a default scenario for \pkg{irace}.
#' [testing_fromfile()] provides a different interface for testing.
#'
#' @author Manuel López-Ibáñez and Leslie Pérez Cáceres
#' @concept running
#' @export
testing_fromlog <- function(logFile, testNbElites, testIterationElites,
testInstancesDir, testInstancesFile, testInstances)
{
if (is.null.or.empty(logFile)) {
irace_note("No logFile provided to perform the testing of configurations. Skipping testing.\n")
return(FALSE)
}
iraceResults <- read_logfile(logFile)
scenario <- iraceResults[["scenario"]]
instances_changed <- FALSE
if (!missing(testNbElites))
scenario[["testNbElites"]] <- testNbElites
if (!missing(testIterationElites))
scenario$testIterationElites <- testIterationElites
if (!missing(testInstances))
scenario[["testInstances"]] <- testInstances
if (!missing(testInstancesDir)) {
scenario$testInstancesDir <- testInstancesDir
instances_changed <- TRUE
}
if (!missing(testInstancesFile)) {
scenario$testInstancesFile <- testInstancesFile
instances_changed <- TRUE
}
cat("\n\n# Testing of elite configurations:", scenario$testNbElites,
"\n# Testing iteration configurations:", scenario$testIterationElites,"\n")
if (scenario$testNbElites <= 0)
return (FALSE)
# If they are already setup, don't change them.
if (instances_changed || is.null.or.empty(scenario[["testInstances"]])) {
scenario <- setup_test_instances(scenario)
if (is.null.or.empty(scenario[["testInstances"]])) {
irace_note("No test instances, skip testing\n")
return(FALSE)
}
}
# Get configurations that will be tested
if (scenario$testIterationElites)
testing_id <- sapply(iraceResults$allElites,
function(x) x[seq_len(min(length(x), scenario$testNbElites))])
else {
tmp <- iraceResults$allElites[[length(iraceResults$allElites)]]
testing_id <- tmp[seq_len(min(length(tmp), scenario$testNbElites))]
}
testing_id <- unique.default(unlist(testing_id))
configurations <- iraceResults$allConfigurations[testing_id, , drop=FALSE]
irace_note ("Testing configurations (in no particular order): ", paste(testing_id, collapse=" "), "\n")
testing_common(configurations, scenario, iraceResults)
return(TRUE)
}
#' Test configurations given an explicit table of configurations and a scenario file
#'
#' Executes the testing of an explicit list of configurations given in
#' `filename` (same format as in [readConfigurationsFile()]). A `logFile` is
#' created unless disabled in `scenario`. This may overwrite an existing one!
#'
#' @param filename `character(1)`\cr Path to a file containing configurations: one configuration
#' per line, one parameter per column, parameter names in header.
#'
#' @inheritParams defaultScenario
#'
#' @return iraceResults
#'
#' @seealso [testing_fromlog()] provides a different interface for testing.
#'
#' @author Manuel López-Ibáñez
#' @concept running
#' @export
testing_fromfile <- function(filename, scenario)
{
irace_note ("Checking scenario.\n")
scenario <- checkScenario(scenario)
if (!scenario$quiet) printScenario(scenario)
configurations <- readConfigurationsFile(filename, scenario$parameters)
configurations <- cbind(.ID. = seq_nrow(configurations), configurations, .PARENT. = NA_integer_)
rownames(configurations) <- configurations[[".ID."]]
num <- nrow(configurations)
configurations <- checkForbidden(configurations, scenario$parameters$forbidden)
if (nrow(configurations) < num) {
irace_warning("Some of the configurations in the configurations file were forbidden",
"and, thus, discarded.")
}
# To save the logs
iraceResults <- list(scenario = scenario,
irace_version = irace_version,
allConfigurations = configurations)
irace_note ("Testing configurations (in the order given as input): \n")
testing_common(configurations, scenario, iraceResults)
}
testing_common <- function(configurations, scenario, iraceResults)
{
verbose <- !scenario$quiet
if (verbose) configurations_print(configurations)
iraceResults$testing <- testConfigurations(configurations, scenario)
save_irace_logfile(iraceResults, logfile = scenario$logFile)
irace_note ("Testing results (column number is configuration ID in no particular order):\n")
if (verbose) print(cbind(seeds = iraceResults$testing$seeds,
as.data.frame(iraceResults$testing$experiments)))
irace_note ("Finished testing\n")
iraceResults
}
#' Test that the given irace scenario can be run.
#'
#' Test that the given irace scenario can be run by checking the scenario
#' settings provided and trying to run the target-algorithm.
#'
#' @inheritParams defaultScenario
#'
#' @return returns `TRUE` if successful and gives an error and returns `FALSE`
#' otherwise.
#'
#' @details If the `parameters` argument is missing, then the parameters
#' will be read from the file `parameterFile` given by `scenario`. If
#' `parameters` is provided, then `parameterFile` will not be read. This function will
#' try to execute the target-algorithm.
#'
#' @seealso
#' \describe{
#' \item{\code{\link{readScenario}}}{for reading a configuration scenario from a file.}
#' \item{\code{\link{printScenario}}}{prints the given scenario.}
#' \item{\code{\link{defaultScenario}}}{returns the default scenario settings of \pkg{irace}.}
#' \item{\code{\link{checkScenario}}}{to check that the scenario is valid.}
#' }
#' @author Manuel López-Ibáñez and Jérémie Dubois-Lacoste
#' @export
checkIraceScenario <- function(scenario)
{
irace_note ("Checking scenario\n")
scenario$debugLevel <- 2L
scenario <- checkScenario(scenario)
if (!scenario$quiet) printScenario(scenario)
irace_note("Checking target runner.\n")
if (checkTargetFiles(scenario = scenario)) {
irace_note("Check successful.\n")
return(TRUE)
}
irace_error("Check unsuccessful.\n")
return(FALSE)
}
init <- function()
{
irace_note("Initializing working directory...\n")
libPath <- system.file(package = "irace")
tmplFiles <- list.files(file.path(libPath, "templates"))
for (file in tmplFiles) {
if (grepl(".tmpl", file) && (file != "target-evaluator.tmpl")) {
newFile <- gsub(".tmpl", "", file)
if (file == "target-runner.tmpl" && .Platform$OS.type == 'windows') {
file.copy(file.path(libPath, "templates", "windows", "target-runner.bat"), file.path(getwd(), "target-runner.bat"), overwrite = FALSE)
} else {
file.copy(file.path(libPath, "templates", file), file.path(getwd(), newFile), overwrite = FALSE)
}
}
}
}
#' Launch `irace` with command-line options.
#'
#' Calls [irace_main()] using command-line options, maybe parsed from the
#' command line used to invoke R.
#'
#' @param argv `character()`\cr The arguments
#' provided on the R command line as a character vector, e.g.,
#' `c("--scenario", "scenario.txt", "-p", "parameters.txt")`.
#' Using the default value (not providing the parameter) is the
#' easiest way to call [irace_cmdline()].
#'
#' @details The function reads the parameters given on the command line
#' used to invoke R, finds the name of the scenario file,
#' initializes the scenario from the file (with the function
#' [readScenario()]) and possibly from parameters passed in
#' the command line. It finally starts \pkg{irace} by calling
#' [irace_main()].
#'
#' List of command-line options:
#' ```{r echo=FALSE,comment=NA}
#' cmdline_usage(.irace.params.def)
#' ```
#'
#' @templateVar return_invisible TRUE
#' @template return_irace
#'
#' @seealso
#' [irace_main()] to start \pkg{irace} with a given scenario.
#' @examples
#' irace_cmdline("--version")
#' @author Manuel López-Ibáñez and Jérémie Dubois-Lacoste
#' @concept running
#' @export
irace_cmdline <- function(argv = commandArgs(trailingOnly = TRUE))
{
parser <- CommandArgsParser$new(argv = argv, argsdef = .irace.params.def)
quiet <- !is.null(parser$readArg (short = "-q", long = "--quiet"))
if (quiet) {
op <- options(.irace.quiet = TRUE)
on.exit(options(op))
} else {
cat_irace_license()
cat("# installed at: ", system.file(package="irace"), "\n",
"# called with: ", paste(argv, collapse = " "), "\n", sep = "")
}
if (!is.null(parser$readArg(short = "-h", long = "--help"))) {
parser$cmdline_usage()
return(invisible(NULL))
}
if (!is.null(parser$readArg(short = "-v", long = "--version"))) {
print(utils::citation(package="irace"))
return(invisible(NULL))
}
if (!is.null(parser$readArg(short = "-i", long = "--init"))) {
init()
return(invisible(NULL))
}
# Read the scenario file and the command line
scenarioFile <- parser$readCmdLineParameter ("scenarioFile", default = "")
scenario <- readScenario(scenarioFile)
for (param in .irace.params.names) {
scenario[[param]] <-
parser$readCmdLineParameter(paramName = param, default = scenario[[param]])
}
if (quiet) scenario$quiet <- TRUE
# Check scenario
if (!is.null(parser$readArg (short = "-c", long = "--check"))) {
checkIraceScenario(scenario)
return(invisible(NULL))
}
# Only do testing
testFile <- parser$readArg (long = "--only-test")
if (!is.null(testFile)) {
return(invisible(testing_fromfile(testFile, scenario)))
}
if (length(parser$argv))
irace_error ("Unknown command-line options: ", paste(parser$argv, collapse = " "))
irace_common(scenario = scenario, simple=FALSE)
}
#' @rdname irace_cmdline
#' @export
irace.cmdline <- function(argv = commandArgs(trailingOnly = TRUE))
{
.Deprecated("irace.cmdline")
irace_cmdline(argv = argv)
}
## Check targetRunner execution
checkTargetFiles <- function(scenario)
{
## Create two random configurations
configurations <- sampleUniform(scenario$parameters, 2L,
repair = scenario$repairConfiguration)
set(configurations, j = ".ID.", value = seq_nrow(configurations))
setcolorder(configurations, ".ID.", before = 1L)
# Read initial configurations provided by the user.
initConfigurations <- allConfigurationsInit(scenario)
setDT(initConfigurations)
if (nrow(initConfigurations) > 0L) {
irace_assert(all(colnames(configurations) == colnames(initConfigurations)))
configurations <- rbindlist(list(initConfigurations, configurations))
set(configurations, j = ".ID.", value = seq_nrow(configurations))
}
bounds <- rep(scenario$boundMax, nrow(configurations))
instances_ID <- if (scenario$sampleInstances)
sample.int(length(scenario$instances), 1L) else 1L
setDF(configurations)
experiments <- createExperimentList(
configurations, scenario$parameters, instances = scenario$instances,
instances_ID = instances_ID, seeds = 1234567L, bounds = bounds)
race_state <- RaceState$new(scenario)
race_state$start_parallel(scenario)
on.exit(race_state$stop_parallel(), add = TRUE)
# FIXME: Create a function try.call(err.msg,warn.msg, fun, ...)
# Executing targetRunner
irace_note("Executing targetRunner (", nrow(configurations), " times)...\n")
result <- TRUE
# We cannot let targetRunner or targetEvaluator modify our random seed, so we save it.
withr::local_preserve_seed()
output <- withCallingHandlers(
tryCatch(execute_experiments(race_state, experiments, scenario),
error = function(e) {
cat(sep = "\n",
"\n# Error occurred while executing targetRunner:",
paste0(conditionMessage(e), collapse="\n"))
result <<- FALSE
NULL
}), warning = function(w) {
cat(sep = "\n",
"\n# Warning occurred while executing targetRunner:",
paste0(conditionMessage(w), collapse="\n"))
invokeRestart("muffleWarning")})
if (scenario$debugLevel >= 1L) {
cat("# targetRunner returned:\n")
print(output, digits = 15L)
}
irace_assert(is.null(scenario$targetEvaluator) == is.null(race_state$target_evaluator))
if (!result) return(FALSE)
if (!is.null(scenario$targetEvaluator)) {
irace_note("Executing targetEvaluator...\n")
output <- withCallingHandlers(
tryCatch(execute_evaluator(race_state$target_evaluator, experiments, scenario, output),
error = function(e) {
cat(sep = "\n",
"\n# Error ocurred while executing targetEvaluator:",
paste0(conditionMessage(e), collapse="\n"))
result <<- FALSE
NULL
}), warning = function(w) {
cat(sep = "\n",
"\n# Warning ocurred while executing targetEvaluator:",
paste0(conditionMessage(w), collapse="\n"))
invokeRestart("muffleWarning")})
if (scenario$debugLevel >= 1L) {
cat("# targetEvaluator returned:\n")
print(output, digits = 15L)
}
}
result
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.