From a8aeb94a3b4b913dc7fd47ad9767b70ebb7bd9c8 Mon Sep 17 00:00:00 2001 From: "konrad.kraemer" Date: Mon, 9 Sep 2024 15:38:08 +0200 Subject: [PATCH] added batch fct for tsf --- Dockerfile | 46 ++++---- Tests/IDA/Rplots.pdf | Bin 18642 -> 36634 bytes Tests/IDA/TestBatch.R | 29 +++++ tsf/DESCRIPTION | 2 +- tsf/NAMESPACE | 9 +- tsf/R/Batch.R | 74 ++---------- tsf/R/RunBatch.R | 134 +++++++++++++++++++++ tsf/R/communicator.R | 43 ------- tsf/R/parameterSensitivity.R | 2 +- tsf/R/pso.R | 39 ++++--- tsf/R/runApp.R | 4 +- tsf/inst/examples/IDABatch.csv | 104 +++++++++++++++++ tsf/inst/tinytest/test_all.R | 206 ++++++++++++++++++++++----------- tsf/man/Communicator.Rd | 174 ---------------------------- tsf/man/batch.Rd | 78 +++++++++++++ tsf/man/pso.Rd | 8 +- 16 files changed, 545 insertions(+), 407 deletions(-) create mode 100644 Tests/IDA/TestBatch.R create mode 100644 tsf/R/RunBatch.R create mode 100644 tsf/inst/examples/IDABatch.csv delete mode 100644 tsf/man/Communicator.Rd create mode 100644 tsf/man/batch.Rd diff --git a/Dockerfile b/Dockerfile index 3e2a086..cfff8bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,34 @@ FROM rocker/shiny:4.3.1 RUN apt-get update && apt-get install -y \ - --no-install-recommends \ - git-core \ - libssl-dev \ - curl \ - libcurl4-gnutls-dev \ - libsodium-dev \ - libxml2-dev \ - libicu-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* + --no-install-recommends \ + git-core \ + libssl-dev \ + curl \ + libcurl4-gnutls-dev \ + libsodium-dev \ + libxml2-dev \ + libicu-dev \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* ENV _R_SHLIB_STRIP_=true ENV SHINY_LOG_STDERR=1 RUN install2.r --error --skipinstalled \ - shiny \ - DT \ - shinydashboard \ - shinyWidgets \ - shinyjs \ - rootSolve \ - ggplot2 \ - patchwork \ - R6 \ - sensitivity \ - openxlsx \ - future \ - promises + shiny \ + DT \ + shinydashboard \ + shinyWidgets \ + shinyjs \ + rootSolve \ + ggplot2 \ + patchwork \ + R6 \ + sensitivity \ + openxlsx \ + future \ + promises COPY ./tsf/ /home/tsf diff --git a/Tests/IDA/Rplots.pdf b/Tests/IDA/Rplots.pdf index 3f796cc3134faf6eff4003cc072027efc5e4d8e3..cc51826f19d0c1dc8949b6e5def306a7ab4aae80 100644 GIT binary patch delta 32877 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 Dy(ajF diff --git a/Tests/IDA/TestBatch.R b/Tests/IDA/TestBatch.R new file mode 100644 index 0000000..8d6fe88 --- /dev/null +++ b/Tests/IDA/TestBatch.R @@ -0,0 +1,29 @@ +setwd("/home/konrad/Documents/Thermosimfit/Tests/IDA") +library(tsf) + +# Test several opti calls in parallel +path <- "idaBatch.csv" +lowerBounds <- c( + kG = 1000, + I0 = 0, + IHD = 0, + ID = 0 +) +upperBounds <- c( + kG = 10^8, + I0 = 100, + IHD = 10^7, + ID = 10^7 +) +additionalParameters <- c( + host = 1e-6, + dye = 1e-6, + kHD = 3e6 +) + +tsf::batch( + "ida", + lowerBounds, upperBounds, + path, additionalParameters, + ngen = 20 +) diff --git a/tsf/DESCRIPTION b/tsf/DESCRIPTION index 6e1298a..a1460a6 100644 --- a/tsf/DESCRIPTION +++ b/tsf/DESCRIPTION @@ -27,7 +27,7 @@ Imports: plotly Suggests: knitr, rmarkdown, tinytest VignetteBuilder: knitr -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Encoding: UTF-8 Roxygen: list(markdown = TRUE) URL: https://complat.github.io/Thermosimfit/ diff --git a/tsf/NAMESPACE b/tsf/NAMESPACE index be593ca..ca14fa3 100644 --- a/tsf/NAMESPACE +++ b/tsf/NAMESPACE @@ -1,7 +1,7 @@ # Generated by roxygen2: do not edit by hand -export(Communicator) export(ErrorClass) +export(batch) export(convertToNum) export(createPolynom) export(opti) @@ -10,15 +10,14 @@ export(runApp) export(sensitivity) import(DT) import(R6) -import(future) +import(callr) import(ggplot2) import(openxlsx) import(patchwork) -import(promises) +import(plotly, except=c(last_plot)) import(rootSolve) -import(sensitivity) +import(sensitivity, except=c(print.src)) import(shiny, except=c(dataTableOutput, renderDataTable, runExample)) import(shinyWidgets, except=c(alert)) -import(plotly, except=c(last_plot)) import(shinydashboard) import(shinyjs) diff --git a/tsf/R/Batch.R b/tsf/R/Batch.R index fbd6797..1bf39ad 100644 --- a/tsf/R/Batch.R +++ b/tsf/R/Batch.R @@ -100,72 +100,10 @@ seperate_batch_results <- function(list) { ) } -batch <- function(case, - lowerBounds, upperBounds, - path, - additionalParameters, - seed = NULL, npop = 40, ngen = 200, Topology = "random", - errorThreshold = -Inf, num_rep = 1) { - # import data - list_df <- importDataBatch(path) - if (!is.list(list_df)) { - return(ErrorClass$new("Could not import data")) - } - for (i in seq_along(list_df)) { - if (!is.data.frame(list_df[[i]])) { - return(list_df[[i]]) - } - } - # seed case - seed_original <- seed - seed_from <- 1:1e6 - # size calculations - num_data_sets <- length(list_df) - result <- vector("list", length = num_data_sets * num_rep) - seeds <- numeric(length = num_data_sets * num_rep) - counter <- 1 - - # run optimization - for (i in seq_len(num_data_sets)) { - if (is.null(seed)) { - seed <- sample(seed_from, 1) - } else { - seed <- seed_original - } - # seeds[counter] <- seed - result[[counter]] <- opti( - case = case, lowerBounds = lowerBounds, upperBounds = upperBounds, - list_df[[i]], additionalParameters, seed = seed, npop = npop, ngen = ngen, - Topology = Topology, errorThreshold = errorThreshold - ) - counter <- counter + 1 - if (num_rep > 1) { - for (j in seq_len(num_rep - 1)) { - seed <- sample(seed_from, 1) - # seeds[counter] <- seed - result[[counter]] <- opti( - case = case, lowerBounds = lowerBounds, upperBounds = upperBounds, - list_df[[i]], additionalParameters, seed = seed, npop = npop, ngen = ngen, - Topology = Topology, errorThreshold = errorThreshold - ) - counter <- counter + 1 - } - } - } - - list <- seperate_batch_results(result) - list( - list, - plotStates(list), - plotParams(list), - plotMetrices(list) - ) -} - call_several_opti <- function(case, lb, ub, - df_list, ap, seed_list, - npop, ngen, topo, - et, messages, env) { + df_list, ap, seed_list, + npop, ngen, topo, + et, messages, env) { env <- new.env() env$intermediate_results <- vector("list", length(df_list)) tryCatch( @@ -199,8 +137,10 @@ call_several_opti_in_bg <- function(case, lb, ub, df_list, ap, seed_list, npop, ngen, topo, et, messages) { env <- new.env() - env$intermediate_results <- lapply(seq_len(length((df_list))), - function(x) x) + env$intermediate_results <- lapply( + seq_len(length((df_list))), + function(x) x + ) for (i in seq_len(length(df_list))) { tryCatch( diff --git a/tsf/R/RunBatch.R b/tsf/R/RunBatch.R new file mode 100644 index 0000000..eea5806 --- /dev/null +++ b/tsf/R/RunBatch.R @@ -0,0 +1,134 @@ +#' Runs a batch of optimization tasks +#' +#' @export +#' @param case is a character argument which specifies the optimization case. +#' Either "dba_dye_const", "dba_host_const", "ida" or "gda" +#' @param lowerBounds is a numeric vector with the lower bounds for the optimization +#' @param upperBounds is a numeric vector with the upper bounds for the optimization +#' @param path is a character argument which specifies the path to the data +#' @param additionalParameters is a numeric vector with additional parameters +#' In case of *dba_dye_const* or *dba_host_const the order of the parameters is: *khd*, *I0*, *IHD* and *ID* +#' In case of *ida* and *ga* the order of the parameters is: *kg*, *I0*, *IHD* and *ID*. +#' @param seed is an optional integer argument defining the seed which is set directly for the optimization. In case the argument is not set the current time is used as seed. +#' @param npop is an optional integer argument defining the number of particles during optimization. The default value is set to 40. +#' @param ngen is an optional integer argument defining the number of generations of the particle swarm optimization. The default value is set to 200. +#' @param Topology is an optional character argument defining which topology should be used by the particle swarm algorithm. The options are "star" and "random". The default topology is the "random" topology. +#' @param errorThreshold is an optional numeric argument defining a sufficient small error which acts as a stop signal for the particle swarm algorithm. The default value is set to -Inf. +#' @param num_rep is an optional integer argument defining the number of replicates for each dataset +#' @param num_cores is an optional integer argument defining the maximum number of cores which should be used for the optimization +#' @examples +#' path <- paste0(system.file("examples", package = "tsf"), "/IDABatch.csv") +#' lowerBounds <- c( +#' kG = 1000, +#' I0 = 0, +#' IHD = 0, +#' ID = 0 +#' ) +#' upperBounds <- c( +#' kG = 10^8, +#' I0 = 100, +#' IHD = 10^7, +#' ID = 10^7 +#' ) +#' additionalParameters <- c( +#' host = 1e-6, +#' dye = 1e-6, +#' kHD = 3e6 +#' ) +#' tsf::batch( +#' "ida", +#' lowerBounds, upperBounds, +#' path, additionalParameters, +#' ngen = 20 +#' ) +batch <- function(case, + lowerBounds, upperBounds, + path, + additionalParameters, + seed = NA, npop = 40, ngen = 200, Topology = "random", + errorThreshold = -Inf, num_rep = 1, num_cores = 1) { + # import data + list_df <- importDataBatch(path) + if (!is.list(list_df)) { + return(ErrorClass$new("Could not import data")) + } + for (i in seq_along(list_df)) { + if (!is.data.frame(list_df[[i]])) { + return(ErrorClass$new("Found non data.frame entry")) + } + } + + # num cores calculation + size <- length(list_df) * num_rep + if (num_cores > size) { + num_cores <- size + } + + # seed case and generation of seeds + seed_case <- determine_seed_case(seed, num_rep) + seed_origin <- NULL + if (seed_case == 3) { + seed_origin <- seed + } + seeds <- numeric(size) + seeds_from <- 1:1e6 + for (i in seq_len(size)) { + if (seed_case == 1) { + seed <- sample(seeds_from, 1) + } else if (seed_case == 3) { + if (i %in% seq(1, size, num_rep)) { + seed <- seed_origin + } else { + seed <- sample(seeds_from, 1) + } + } else if (seed_case == 2) { + seed <- seed # TODO: check is this correct + } + seeds[i] <- seed + } + + # create message for each optimization + messages <- character(size) + counter_messages <- 1 + for (i in seq_len(length(list_df))) { + for (j in seq_len(num_rep)) { + messages[counter_messages] <- + paste0("Dataset = ", i, "; Replicate = ", j) + counter_messages <- counter_messages + 1 + } + } + + # 3. Fill task queue + dfs <- rep(list_df, each = num_rep) + tq <- TaskQueue$new( + case, + lowerBounds, upperBounds, dfs, + additionalParameters, seeds, + npop, ngen, Topology, errorThreshold, + messages, num_cores + ) + + # 4. assign tasks + tq$assign() + old_status <- "" + + while (TRUE) { + new_status <- tq$get_status(old_status) + cat(new_status) + cat("\n") + old_status <- new_status + if (!tq$queue_empty() && tq$check()) { + tq$assign() + } + if (tq$queue_empty()) break + Sys.sleep(1) + } + + list <- tq$seperate_results() + list( + list, + plotStates(list), + plotParams(list), + plotMetrices(list) + ) +} diff --git a/tsf/R/communicator.R b/tsf/R/communicator.R index deebd38..6efaa46 100644 --- a/tsf/R/communicator.R +++ b/tsf/R/communicator.R @@ -1,67 +1,31 @@ -#' Communicator class -#' @description a class for communicating via a temporary file -#' -#' @export -#' @import R6 Communicator <- R6::R6Class("Communicator", public = list( - #' @field file is a file which contains the current status file = NULL, - - #' @field result is a file in which data can be written or read information. result = NULL, - - #' @description - #' create a new Communicator Object initialize = function() { self$file <- tempfile() self$result <- tempfile() write("Ready", self$file) write("", self$result) }, - - #' @description - #' get the current status getStatus = function() { scan(self$file, what = "character", sep = "\n") }, - - #' @description - #' write a status to the status file - #' @param msg is the message which should be set in the file setStatus = function(msg) { write(msg, self$file) }, - - #' @description - #' write data to the result file - #' @param data is a string which should be written to the result file setData = function(data) { write(data, self$result) }, - - #' @description - #' get the current data from the result file getData = function() { scan(self$result, what = "character", sep = "\n") }, - - #' @description - #' set the status to "interrupt" interrupt = function() { self$setStatus("interrupt") }, - - #' @description - #' set the status to "ready" ready = function() { self$setStatus("ready") }, - - #' @description - #' write a status to the status file. - #' @param percComplete is the message which should be set in the file. - #' If percComplete is not passed than the message is set to "Running..." running = function(percComplete) { msg <- "Running..." if (!missing(percComplete)) { @@ -69,16 +33,9 @@ Communicator <- R6::R6Class("Communicator", } self$setStatus(msg) }, - - #' @description - #' Checks if the current status is "interrupt" isInterrupted = function() { self$getStatus() == "interrupt" }, - - #' @description - #' removes the temporary files. - #' \strong{This method has to be called at the end of the lifetime of the object!} destroy = function() { if (file.exists(self$file)) unlink(self$file) if (file.exists(self$result)) unlink(self$result) diff --git a/tsf/R/parameterSensitivity.R b/tsf/R/parameterSensitivity.R index d1323a6..bf53afe 100644 --- a/tsf/R/parameterSensitivity.R +++ b/tsf/R/parameterSensitivity.R @@ -45,7 +45,7 @@ sobolVariance <- function(lossFct, env, lb, ub, parameterNames, runAsShiny) { #' @export #' @import rootSolve #' @import ggplot2 -#' @import sensitivity +#' @rawNamespace import(sensitivity, except=c(print.src)) #' @param case is a character describing which system should be investigated. Either: #' "dba_host_const", "dba_dye_const", "ida" or "gda". #' @param parameters is a numeric vector containing already optimized parameter. diff --git a/tsf/R/pso.R b/tsf/R/pso.R index 632e795..034ad58 100644 --- a/tsf/R/pso.R +++ b/tsf/R/pso.R @@ -8,8 +8,6 @@ #' @import shinydashboard #' @import shinyjs #' @import openxlsx -#' @import future -#' @import promises #' @import DT #' @param env is something that is passed to the loss function in addition to the parameters which get optimized #' @param lb is a numeric vector defining the lower boundaries of the parameter @@ -132,21 +130,28 @@ pso <- function(env, lb, ub, loss, ngen, npop, error_threshold, global = FALSE, memory <- matrix(0, nrow = ngen * npop, ncol = npar) error_memory <- numeric(ngen * npop) - lb <- ifelse(lb == 0, 10^-15, lb) - ub <- ifelse(ub == 0, 10^-15, ub) - lb <- log(lb) - ub <- log(ub) - - for (i in seq(npop)) { - swarm[i, ] <- runif(npar, min = lb, max = ub) - swarm_errors[i] <- loss_fct(exp(swarm[i, ]), env) - swarm_bests[i] <- swarm_errors[i] - } + if (any(lb <= 0) || any(ub <= 0)) { + for (i in seq(npop)) { + swarm[i, ] <- runif(npar, min = lb, max = ub) + swarm_errors[i] <- loss_fct(swarm[i, ], env) + swarm_bests[i] <- swarm_errors[i] + } + } else { + lb <- ifelse(lb <= 0, 10^-15, lb) + ub <- ifelse(ub <= 0, 10^-15, ub) + lb <- log(lb) + ub <- log(ub) - swarm <- exp(swarm) - lb <- exp(lb) - ub <- exp(ub) + for (i in seq(npop)) { + swarm[i, ] <- runif(npar, min = lb, max = ub) + swarm_errors[i] <- loss_fct(exp(swarm[i, ]), env) + swarm_bests[i] <- swarm_errors[i] + } + swarm <- exp(swarm) + lb <- exp(lb) + ub <- exp(ub) + } global_best <- which.min(swarm_bests) global_best_vec <- swarm[global_best, ] global_best_error <- swarm_bests[global_best] @@ -220,12 +225,12 @@ pso <- function(env, lb, ub, loss, ngen, npop, error_threshold, global = FALSE, if (save_swarm) error_memory[((iter * npop) + i)] <- error if (!is.infinite(error) && !is.na(error) && - error < swarm_bests[i]) { + error < swarm_bests[i]) { swarm_bests[i] <- error swarm_best_params[i, ] <- swarm[i, ] } if (!is.infinite(error) && !is.na(error) && - error < global_best_error) { + error < global_best_error) { global_best <- i global_best_vec <- swarm[i, ] global_best_error <- error diff --git a/tsf/R/runApp.R b/tsf/R/runApp.R index d730317..9a58a51 100644 --- a/tsf/R/runApp.R +++ b/tsf/R/runApp.R @@ -5,12 +5,12 @@ #' @param port is a number defining the port to use. #' @rawNamespace import(shiny, except=c(dataTableOutput, renderDataTable, runExample)) #' @rawNamespace import(shinyWidgets, except=c(alert)) +#' @rawNamespace import(plotly, except=c(last_plot)) #' @import shinydashboard #' @import shinyjs #' @import openxlsx -#' @import future -#' @import promises #' @import DT +#' @import callr #' @examples #' \donttest{ #' tsf::runApp() diff --git a/tsf/inst/examples/IDABatch.csv b/tsf/inst/examples/IDABatch.csv new file mode 100644 index 0000000..cea56e8 --- /dev/null +++ b/tsf/inst/examples/IDABatch.csv @@ -0,0 +1,104 @@ +guest,signal +0,0.652593163 +1.00E-07,0.625830834 +2.00E-07,0.59760106 +3.00E-07,0.568336413 +4.00E-07,0.538605992 +5.00E-07,0.509076212 +6.00E-07,0.480442809 +7.00E-07,0.453345361 +8.00E-07,0.428286524 +9.00E-07,0.40558051 +1.00E-06,0.385344585 +1.10E-06,0.367529159 +1.20E-06,0.351968717 +1.30E-06,0.338434288 +1.40E-06,0.3266753 +1.50E-06,0.316447156 +1.60E-06,0.307526286 +1.70E-06,0.299716359 +1.80E-06,0.292849146 +1.90E-06,0.286782597 +2.00E-06,0.281397733 +2.10E-06,0.276595271 +2.20E-06,0.272292426 +2.30E-06,0.268420086 +2.40E-06,0.264920412 +2.50E-06,0.261744829 +2.60E-06,0.258852387 +2.70E-06,0.256208419 +2.80E-06,0.253783444 +2.90E-06,0.251552283 +3.00E-06,0.249493336 +3.10E-06,0.24758799 +3.20E-06,0.245820143 +3.30E-06,0.244175803 +3.40E-06,0.242642769 +3.50E-06,0.241210354 +3.60E-06,0.23986917 +3.70E-06,0.238610935 +3.80E-06,0.237428323 +3.90E-06,0.236314829 +4.00E-06,0.235264662 +4.10E-06,0.23427265 +4.20E-06,0.23333416 +4.30E-06,0.232445033 +4.40E-06,0.231601522 +4.50E-06,0.230800248 +4.60E-06,0.230038151 +4.70E-06,0.229312458 +4.80E-06,0.228620648 +4.90E-06,0.227960426 +6.00E-06,0.227329698 +guest,signal +0,0.652593163 +0.0000001,0.625830834 +0.0000002,0.59760106 +0.0000003,0.568336413 +0.0000004,0.538605992 +0.0000005,0.509076212 +0.0000006,0.480442809 +0.0000007,0.453345361 +0.0000008,0.428286524 +0.0000009,0.40558051 +0.000001,0.385344585 +0.0000011,0.367529159 +0.0000012,0.351968717 +0.0000013,0.338434288 +0.0000014,0.3266753 +0.0000015,0.316447156 +0.0000016,0.307526286 +0.0000017,0.299716359 +0.0000018,0.292849146 +0.0000019,0.286782597 +0.000002,0.281397733 +0.0000021,0.276595271 +0.0000022,0.272292426 +0.0000023,0.268420086 +0.0000024,0.264920412 +0.0000025,0.261744829 +0.0000026,0.258852387 +0.0000027,0.256208419 +0.0000028,0.253783444 +0.0000029,0.251552283 +0.000003,0.249493336 +0.0000031,0.24758799 +0.0000032,0.245820143 +0.0000033,0.244175803 +0.0000034,0.242642769 +0.0000035,0.241210354 +0.0000036,0.23986917 +0.0000037,0.238610935 +0.0000038,0.237428323 +0.0000039,0.236314829 +0.000004,0.235264662 +0.0000041,0.23427265 +0.0000042,0.23333416 +0.0000043,0.232445033 +0.0000044,0.231601522 +0.0000045,0.230800248 +0.0000046,0.230038151 +0.0000047,0.229312458 +0.0000048,0.228620648 +0.0000049,0.227960426 +0.000005,0.227329698 diff --git a/tsf/inst/tinytest/test_all.R b/tsf/inst/tinytest/test_all.R index ec4095b..6188082 100644 --- a/tsf/inst/tinytest/test_all.R +++ b/tsf/inst/tinytest/test_all.R @@ -1,5 +1,7 @@ library(tinytest) library(tsf) + +# Expect errors getError <- function(error) { error$message } @@ -10,28 +12,31 @@ f <- function(a, b, c) { } } b <- body(f)[[2]] -expect_equal( getError(tsf:::getAST(b)), paste0("Error: function ", "for" ," not allowed") ) +expect_equal(getError(tsf:::getAST(b)), paste0("Error: function ", "for", " not allowed")) path <- paste0(system.file("examples", package = "tsf"), "/IDA.txt") -expect_equal( is.data.frame(tsf:::importData(path)), TRUE) +expect_equal(is.data.frame(tsf:::importData(path)), TRUE) path <- paste0(system.file("examples", package = "tsf"), "/ImportFailsHere.txt") expect_equal(getError(tsf:::importData(path)), "Could not identify seperator in file") +# test pso rosenbrock <- function(parameter, env, Ignore) { value <- 0 for (i in 1:(length(parameter) - 1)) { - value <- value + - 100*(parameter[i + 1] - parameter[i]^2)^2 + + value <- value + + 100 * (parameter[i + 1] - parameter[i]^2)^2 + (1 - parameter[i])^2 } return(value) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 3), rep(10, 3), rosenbrock, 1000, 40, - 0.00001, TRUE, FALSE) -expect_equal( sum(res[[2]] - rep(1, 3)) < 1e-9, TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 3), rep(10, 3), rosenbrock, 1000, 40, + 0.00001, TRUE, FALSE +) +expect_equal(sum(res[[2]] - rep(1, 3)) < 1e-9, TRUE) rastrigin <- function(x, env, Ignore) { A <- 10 @@ -40,75 +45,91 @@ rastrigin <- function(x, env, Ignore) { return(A * n + sum_val) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 3), rep(10, 3), rastrigin, 1000, 120, - 10^-14, TRUE, FALSE) -expect_equal( sum(res[[2]]) < 1e-9, TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 3), rep(10, 3), rastrigin, 1000, 120, + 10^-14, TRUE, FALSE +) +expect_equal(sum(res[[2]]) < 1e-9, TRUE) sphere <- function(x, env, Ignore) { # x = (0, 0, ... 0) return(sum(x^2)) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 3), rep(10, 3), sphere, 1000, 120, - 10^-14, TRUE, FALSE) -expect_equal( sum(res[[2]]) < 1e-9, TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 3), rep(10, 3), sphere, 1000, 120, + 10^-14, TRUE, FALSE +) +expect_equal(sum(res[[2]]) < 1e-9, TRUE) ackley <- function(x, env, Ignore) { # x = (0, 0, ... 0) n <- length(x) sum1 <- sum(x^2) sum2 <- sum(cos(2 * pi * x)) - return(-20 * exp(-0.2 * sqrt(sum1/n)) - exp(sum2/n) + 20 + exp(1)) + return(-20 * exp(-0.2 * sqrt(sum1 / n)) - exp(sum2 / n) + 20 + exp(1)) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 3), rep(10, 3), ackley, 6500, 120, - 10^-14, TRUE, FALSE) # here the random topology is worse than star but far better than for sphere, rastrigin or rosenbrock -expect_equal( sum(res[[2]]) < 1e-9, TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 3), rep(10, 3), ackley, 6500, 120, + 10^-14, TRUE, FALSE +) # here the random topology is worse than star but far better than for sphere, rastrigin or rosenbrock +expect_equal(sum(res[[2]]) < 1e-9, TRUE) -michalewicz <- function(xx, env, Ignore) { +michalewicz <- function(xx, env, Ignore) { # 2D global min = -1.8013 at x(2.2, 1.57) - m = 10 + m <- 10 ii <- c(1:length(xx)) - sum <- sum(sin(xx) * (sin(ii*xx^2/pi))^(2*m)) + sum <- sum(sin(xx) * (sin(ii * xx^2 / pi))^(2 * m)) y <- -sum return(y) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(0, 2), rep(5, 2), michalewicz, 1200, 120, - -Inf, TRUE, FALSE) -expect_equal( sum(res[[2]] - c(2.2, 1.57)) < 1e-2 , TRUE) +res <- tsf:::pso( + new.env(), rep(0, 2), rep(5, 2), michalewicz, 1200, 120, + -Inf, TRUE, FALSE +) +expect_equal(sum(res[[2]] - c(2.2, 1.57)) < 1e-2, TRUE) set.seed(1234) -res <- tsf:::pso(new.env(), rep(0, 2), rep(5, 2), michalewicz, 1200, 120, - -Inf, FALSE, FALSE) -expect_equal( sum(res[[2]] - c(2.2, 1.57)) < 1e-2 , TRUE) +res <- tsf:::pso( + new.env(), rep(0, 2), rep(5, 2), michalewicz, 1200, 120, + -Inf, FALSE, FALSE +) +expect_equal(sum(res[[2]] - c(2.2, 1.57)) < 1e-2, TRUE) schwefel_222 <- function(x, env, Ignore) { # x = (0, 0, ... 0) return(max(abs(x))) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 2), rep(10, 2), schwefel_222, 2200, 80, - 1e-14, TRUE, FALSE) -expect_equal( sum(abs(res[[2]])) < 1e-12 , TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 2), rep(10, 2), schwefel_222, 2200, 80, + 1e-14, TRUE, FALSE +) +expect_equal(sum(abs(res[[2]])) < 1e-12, TRUE) griewank <- function(x, env, Ignore) { # x = (0, 0, ... 0) n <- length(x) - sum1 <- sum(x^2)/4000 - prod1 <- prod(cos(x/sqrt(1:n))) + sum1 <- sum(x^2) / 4000 + prod1 <- prod(cos(x / sqrt(1:n))) return(sum1 - prod1 + 1) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 2), rep(10, 2), griewank, 2800, 120, - 1e-32, TRUE, FALSE) -expect_equal( sum(abs(res[[1]])) < 1e-12 , TRUE) -expect_equal( sum(abs(res[[2]])) < 1e-6 , TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 2), rep(10, 2), griewank, 2800, 120, + 1e-32, TRUE, FALSE +) +expect_equal(sum(abs(res[[1]])) < 1e-12, TRUE) +expect_equal(sum(abs(res[[2]])) < 1e-6, TRUE) easom <- function(x, env, Ignore) { # global min -1 with x at pi, pi return(-cos(x[1]) * cos(x[2]) * exp(-((x[1] - pi)^2 + (x[2] - pi)^2))) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-10, 2), rep(10, 2), easom, 2800, 120, - -1, TRUE, FALSE) -expect_equal( sum(res[[1]] + 1) < 1e-12 , TRUE) -expect_equal( sum(abs(res[[2]]) - c(pi, pi)) < 1e-6 , TRUE) +res <- tsf:::pso( + new.env(), rep(-10, 2), rep(10, 2), easom, 2800, 120, + -1, TRUE, FALSE +) +expect_equal(sum(res[[1]] + 1) < 1e-12, TRUE) +expect_equal(sum(abs(res[[2]]) - c(pi, pi)) < 1e-6, TRUE) egg_holder <- function(xx, env, Ignore) { # Global Minimum: f(x)≈−959.6407f(x)≈−959.6407 at x=(512,404.2319)x=(512,404.2319) @@ -120,17 +141,22 @@ egg_holder <- function(xx, env, Ignore) { return(y) } set.seed(1234) -res <- tsf:::pso(new.env(), rep(-512, 2), rep(512, 2), egg_holder, 2800, 120, - -959, FALSE, FALSE) -expect_equal( sum(res[[1]] + 1) < 1e-12 , TRUE) -expect_equal( sum(abs(res[[2]]) - c(512.404, 404.2319)) < 1e-6 , TRUE) +res <- tsf:::pso( + new.env(), rep(-512, 2), rep(512, 2), egg_holder, 2800, 120, + -959, FALSE, FALSE +) +expect_equal(sum(res[[1]] + 1) < 1e-12, TRUE) +expect_equal(sum(abs(res[[2]]) - c(512.404, 404.2319)) < 1e-6, TRUE) set.seed(1234) -res <- tsf:::pso(new.env(), rep(-512, 2), rep(512, 2), egg_holder, 2800, 120, - -959, TRUE, FALSE) -expect_equal( sum(res[[1]] + 1) < 1e-12 , TRUE) -expect_equal( sum(abs(res[[2]]) - c(512.404, 404.2319)) < 1e-6 , TRUE) +res <- tsf:::pso( + new.env(), rep(-512, 2), rep(512, 2), egg_holder, 2800, 120, + -959, TRUE, FALSE +) +expect_equal(sum(res[[1]] + 1) < 1e-12, TRUE) +expect_equal(sum(abs(res[[2]]) - c(512.404, 404.2319)) < 1e-6, TRUE) +# test sensitivity test_sensitivity_valid_input <- function() { path <- paste0(system.file("examples", package = "tsf"), "/IDA.txt") optimP <- data.frame(80699337.884, 0.000, 1251.928, 0.000) @@ -148,11 +174,12 @@ test_sensitivity_invalid_case <- function() { } test_sensitivity_invalid_case() +# test create polynom test_createPolynom_valid_input <- function() { f <- function() { - h + hd + -h0 = 0 - d + hd -d0 = 0 - hd / (h*d) -kd = 0 + h + hd + -h0 <- 0 + d + hd - d0 <- 0 + hd / (h * d) - kd <- 0 } elimVars <- c("h", "d") result <- createPolynom(f, elimVars) @@ -173,9 +200,9 @@ test_createPolynom_invalid_function() test_createPolynom_invalid_elimVars <- function() { f <- function() { - h + hd + -h0 = 0 - d + hd - d0 = 0 - hd / (h*d) - kd = 0 + h + hd + -h0 <- 0 + d + hd - d0 <- 0 + hd / (h * d) - kd <- 0 } elimVars <- "not_a_character_vector" result <- createPolynom(f, elimVars) @@ -185,8 +212,8 @@ test_createPolynom_invalid_elimVars() test_createPolynom_another_valid_input <- function() { f <- function() { # x = 0 and y = 2 - 3*y + 2*x - 6 = 0 - 5 * y - 2*x - 10 = 0 + 3 * y + 2 * x - 6 <- 0 + 5 * y - 2 * x - 10 <- 0 } elimVars <- c("y", "x") resultX <- createPolynom(f, elimVars) @@ -196,6 +223,7 @@ test_createPolynom_another_valid_input <- function() { } test_createPolynom_another_valid_input() +# test loss functions test_lossFctHG_valid_input <- function() { parameter <- c(0.5, 1, 2, 3) env <- new.env() @@ -233,6 +261,7 @@ test_lossFctGDA_valid_input <- function() { } test_lossFctGDA_valid_input() +# test opti test_hg <- function() { path <- paste0(system.file("examples", package = "tsf"), "/IDA.txt") df <- read.csv(path, header = FALSE, sep = "\t") @@ -246,10 +275,12 @@ test_hg <- function() { file <- tempfile(fileext = ".txt") write.csv(df, file, quote = FALSE, row.names = FALSE) set.seed(1234) - res <- tsf::opti("hg", c(1, 0, 0, 0), c(10^9, 1, rep(10^5, 2)), file, env$h0, - 40, 100) - expect_true(res[[4]]$r2 > 0.99) - expect_true( (res[[2]]$IHD - 1000) < 1) + res <- tsf::opti( + "dba_host_const", c(1, 0, 0, 0), c(10^9, 1, rep(10^5, 2)), file, env$h0, + 40, 100 + ) + expect_true(res[[4]]$R2 > 0.99) + expect_true((res[[2]][3] - 1000) < 1) } test_hg() @@ -268,19 +299,21 @@ test_ida <- function() { file <- tempfile(fileext = ".txt") write.csv(df, file, quote = FALSE, row.names = FALSE) set.seed(1234) - res <- tsf::opti("ida", c(1, 0, 0, 0), c(10^9, 1, rep(10^5, 2)), file, c(env$h0, env$d0, env$kd), - 40, 150) - expect_true(res[[4]]$r2 > 0.99) - expect_true( (res[[2]]$IHD - 1000) < 10) + res <- tsf::opti( + "ida", c(1, 0, 0, 0), c(10^9, 1, rep(10^5, 2)), file, c(env$h0, env$d0, env$kd), + 40, 150 + ) + expect_true(res[[4]]$R2 > 0.99) + expect_true((res[[2]][3] - 1000) < 10) } test_ida() test_gda <- function() { env <- new.env() env$h0 <- 1.65 - env$kd <- 1.7*10^7 + env$kd <- 1.7 * 10^7 env$ga0 <- 1.8 - parameter <- c(1857463, 0, 3456.443, 0) + parameter <- c(1857463, 0, 3456.443, 0) path <- paste0(system.file("examples", package = "tsf"), "/GDA.txt") df <- read.csv(path, header = TRUE, sep = ",") env$dye <- df[, 1] @@ -290,10 +323,43 @@ test_gda <- function() { file <- tempfile(fileext = ".txt") write.csv(df, file, quote = FALSE, row.names = FALSE) set.seed(1234) - res <- tsf::opti("gda", c(1, 0, 0, 0), c(10^9, 1, rep(10^6, 2)), file, - additionalParameters = c(env$h0, env$ga0, env$kd), - 40, 175) - expect_true(res[[4]]$r2 > 0.99) - expect_true( abs(res[[2]]$IHD - 3456) < 100) + res <- tsf::opti("gda", c(1, 0, 0, 0), c(10^9, 1, rep(10^6, 2)), file, + additionalParameters = c(env$h0, env$ga0, env$kd), + 40, 175 + ) + expect_true(res[[4]]$R2 > 0.99) + expect_true(abs(res[[2]][3] - 3456) < 100) } test_gda() + +# tests batch +test_batch <- function() { + path <- paste0(system.file("examples", package = "tsf"), "/IDABatch.csv") + lowerBounds <- c( + kG = 1000, + I0 = 0, + IHD = 0, + ID = 0 + ) + upperBounds <- c( + kG = 10^8, + I0 = 100, + IHD = 10^7, + ID = 10^7 + ) + additionalParameters <- c( + host = 1e-6, + dye = 1e-6, + kHD = 3e6 + ) + res <- tsf::batch( + "ida", + lowerBounds, upperBounds, + path, additionalParameters, + ngen = 20, + num_cores = 2 + ) + lapply(res[[1]], function(x) { + expect_true(x[[4]]$R2 > 0.99) + }) +} diff --git a/tsf/man/Communicator.Rd b/tsf/man/Communicator.Rd deleted file mode 100644 index 7347cd4..0000000 --- a/tsf/man/Communicator.Rd +++ /dev/null @@ -1,174 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/communicator.R -\name{Communicator} -\alias{Communicator} -\title{Communicator class} -\description{ -a class for communicating via a temporary file -} -\section{Public fields}{ -\if{html}{\out{
}} -\describe{ -\item{\code{file}}{is a file which contains the current status} - -\item{\code{result}}{is a file in which data can be written or read information.} -} -\if{html}{\out{
}} -} -\section{Methods}{ -\subsection{Public methods}{ -\itemize{ -\item \href{#method-Communicator-new}{\code{Communicator$new()}} -\item \href{#method-Communicator-getStatus}{\code{Communicator$getStatus()}} -\item \href{#method-Communicator-setStatus}{\code{Communicator$setStatus()}} -\item \href{#method-Communicator-setData}{\code{Communicator$setData()}} -\item \href{#method-Communicator-getData}{\code{Communicator$getData()}} -\item \href{#method-Communicator-interrupt}{\code{Communicator$interrupt()}} -\item \href{#method-Communicator-ready}{\code{Communicator$ready()}} -\item \href{#method-Communicator-running}{\code{Communicator$running()}} -\item \href{#method-Communicator-isInterrupted}{\code{Communicator$isInterrupted()}} -\item \href{#method-Communicator-destroy}{\code{Communicator$destroy()}} -\item \href{#method-Communicator-clone}{\code{Communicator$clone()}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-new}{}}} -\subsection{Method \code{new()}}{ -create a new Communicator Object -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$new()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-getStatus}{}}} -\subsection{Method \code{getStatus()}}{ -get the current status -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$getStatus()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-setStatus}{}}} -\subsection{Method \code{setStatus()}}{ -write a status to the status file -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$setStatus(msg)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{msg}}{is the message which should be set in the file} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-setData}{}}} -\subsection{Method \code{setData()}}{ -write data to the result file -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$setData(data)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{data}}{is a string which should be written to the result file} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-getData}{}}} -\subsection{Method \code{getData()}}{ -get the current data from the result file -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$getData()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-interrupt}{}}} -\subsection{Method \code{interrupt()}}{ -set the status to "interrupt" -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$interrupt()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-ready}{}}} -\subsection{Method \code{ready()}}{ -set the status to "ready" -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$ready()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-running}{}}} -\subsection{Method \code{running()}}{ -write a status to the status file. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$running(percComplete)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{percComplete}}{is the message which should be set in the file. -If percComplete is not passed than the message is set to "Running..."} -} -\if{html}{\out{
}} -} -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-isInterrupted}{}}} -\subsection{Method \code{isInterrupted()}}{ -Checks if the current status is "interrupt" -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$isInterrupted()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-destroy}{}}} -\subsection{Method \code{destroy()}}{ -removes the temporary files. -\strong{This method has to be called at the end of the lifetime of the object!} -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$destroy()}\if{html}{\out{
}} -} - -} -\if{html}{\out{
}} -\if{html}{\out{}} -\if{latex}{\out{\hypertarget{method-Communicator-clone}{}}} -\subsection{Method \code{clone()}}{ -The objects of this class are cloneable with this method. -\subsection{Usage}{ -\if{html}{\out{
}}\preformatted{Communicator$clone(deep = FALSE)}\if{html}{\out{
}} -} - -\subsection{Arguments}{ -\if{html}{\out{
}} -\describe{ -\item{\code{deep}}{Whether to make a deep clone.} -} -\if{html}{\out{
}} -} -} -} diff --git a/tsf/man/batch.Rd b/tsf/man/batch.Rd new file mode 100644 index 0000000..f4be24e --- /dev/null +++ b/tsf/man/batch.Rd @@ -0,0 +1,78 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RunBatch.R +\name{batch} +\alias{batch} +\title{Runs a batch of optimization tasks} +\usage{ +batch( + case, + lowerBounds, + upperBounds, + path, + additionalParameters, + seed = NA, + npop = 40, + ngen = 200, + Topology = "random", + errorThreshold = -Inf, + num_rep = 1, + num_cores = 1 +) +} +\arguments{ +\item{case}{is a character argument which specifies the optimization case. +Either "dba_dye_const", "dba_host_const", "ida" or "gda"} + +\item{lowerBounds}{is a numeric vector with the lower bounds for the optimization} + +\item{upperBounds}{is a numeric vector with the upper bounds for the optimization} + +\item{path}{is a character argument which specifies the path to the data} + +\item{additionalParameters}{is a numeric vector with additional parameters +In case of \emph{dba_dye_const} or *dba_host_const the order of the parameters is: \emph{khd}, \emph{I0}, \emph{IHD} and \emph{ID} +In case of \emph{ida} and \emph{ga} the order of the parameters is: \emph{kg}, \emph{I0}, \emph{IHD} and \emph{ID}.} + +\item{seed}{is an optional integer argument defining the seed which is set directly for the optimization. In case the argument is not set the current time is used as seed.} + +\item{npop}{is an optional integer argument defining the number of particles during optimization. The default value is set to 40.} + +\item{ngen}{is an optional integer argument defining the number of generations of the particle swarm optimization. The default value is set to 200.} + +\item{Topology}{is an optional character argument defining which topology should be used by the particle swarm algorithm. The options are "star" and "random". The default topology is the "random" topology.} + +\item{errorThreshold}{is an optional numeric argument defining a sufficient small error which acts as a stop signal for the particle swarm algorithm. The default value is set to -Inf.} + +\item{num_rep}{is an optional integer argument defining the number of replicates for each dataset} + +\item{num_cores}{is an optional integer argument defining the maximum number of cores which should be used for the optimization} +} +\description{ +Runs a batch of optimization tasks +} +\examples{ +path <- paste0(system.file("examples", package = "tsf"), "/IDABatch.csv") +lowerBounds <- c( + kG = 1000, + I0 = 0, + IHD = 0, + ID = 0 +) +upperBounds <- c( + kG = 10^8, + I0 = 100, + IHD = 10^7, + ID = 10^7 +) +additionalParameters <- c( + host = 1e-6, + dye = 1e-6, + kHD = 3e6 +) +tsf::batch( + "ida", + lowerBounds, upperBounds, + path, additionalParameters, + ngen = 20 +) +} diff --git a/tsf/man/pso.Rd b/tsf/man/pso.Rd index e84581c..4465c1d 100644 --- a/tsf/man/pso.Rd +++ b/tsf/man/pso.Rd @@ -13,8 +13,8 @@ pso( npop, error_threshold, global = FALSE, - saveSwarm = FALSE, - runAsShiny = FALSE, + save_swarm = FALSE, + run_as_shiny = FALSE, add_message = "" ) } @@ -40,9 +40,9 @@ a neighberhood which contains K neighbours where K is between 0 and 3. From the drawn randomly. From the neighberhood the best particle is used for comparison. The neighberhood is calculated for each generation.} -\item{saveSwarm}{is a logical value defining whether the entire optimization should be saved.} +\item{save_swarm}{is a logical value defining whether the entire optimization should be saved.} -\item{runAsShiny}{is an internal parameter which is used when running the shiny app interface.} +\item{run_as_shiny}{is an internal parameter which is used when running the shiny app interface.} \item{add_message}{is an optional character argument which is printed during optimization} }