Skip to content

Commit c45a1f5

Browse files
Improved Error Handling
1 parent c98c865 commit c45a1f5

File tree

10 files changed

+289
-28
lines changed

10 files changed

+289
-28
lines changed

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: ParBayesianOptimization
22
Title: Parallel Bayesian Optimization of Hyperparameters
3-
Version: 1.1.0
3+
Version: 1.2.0
44
Authors@R: person("Samuel", "Wilson", email = "[email protected]", role = c("aut", "cre"))
55
Description: Fast, flexible framework for implementing Bayesian optimization of model
66
hyperparameters according to the methods described in Snoek et al. <arXiv:1206.2944>.
@@ -15,6 +15,6 @@ Suggests: knitr, rmarkdown, xgboost, doParallel, testthat
1515
License: GPL-2
1616
Encoding: UTF-8
1717
LazyData: true
18-
RoxygenNote: 7.0.2
18+
RoxygenNote: 7.1.0
1919
VignetteBuilder: knitr
2020
Maintainer: Samuel Wilson <[email protected]>

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ importFrom(data.table,copy)
1919
importFrom(data.table,data.table)
2020
importFrom(data.table,fintersect)
2121
importFrom(data.table,is.data.table)
22+
importFrom(data.table,rbindlist)
2223
importFrom(data.table,setDT)
2324
importFrom(data.table,setcolorder)
2425
importFrom(data.table,setnames)

NEWS.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
## ParBayesianOptimization 1.1.0
1+
## ParBayesianOptimization 1.2.0
2+
3+
# Changes
4+
Improved the way error handling works - any errors encountered in initialization will be returned.
25

6+
## ParBayesianOptimization 1.1.0
37

48
# Changes
59
Changed Gaussian Process package to DiceKriging. predict method is much faster.

R/addIterations.R

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,10 +207,26 @@ addIterations <- function(
207207
)
208208
)
209209

210-
if (!any(class(Result) %in% c("simpleError","error","condition"))) {
211-
return(data.table(nextPars[get("iter"),], Elapsed = Elapsed[[3]], as.data.table(Result),errorMessage = NA))
212-
} else {
210+
# Handle the Result.
211+
if (any(class(Result) %in% c("simpleError","error","condition"))) {
213212
return(data.table(nextPars[get("iter"),], Elapsed = Elapsed[[3]],Score = NA, errorMessage = conditionMessage(Result)))
213+
} else {
214+
215+
if (any(lengths(Result) != 1)) {
216+
stop(
217+
paste0(
218+
"FUN returned list with elements of length > 1. Cannot collapse into a data.table, so this is a fatal error. Parameters used were <"
219+
, paste(names(Params),"=",Params,collapse = ", ")
220+
, ">"
221+
)
222+
)
223+
}
224+
225+
if (!is.numeric(Result$Score)) {
226+
return(data.table(nextPars[get("iter"),], Elapsed = Elapsed[[3]], as.data.table(Result),errorMessage = "Score returned from FUN was not numeric."))
227+
} else {
228+
return(data.table(nextPars[get("iter"),], Elapsed = Elapsed[[3]], as.data.table(Result),errorMessage = NA))
229+
}
214230
}
215231

216232
}

R/bayesOpt.R

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@
176176
#' , verbose = 1
177177
#' )
178178
#' }
179-
#' @importFrom data.table data.table setDT setcolorder := as.data.table copy .I setnames is.data.table
179+
#' @importFrom data.table data.table setDT setcolorder := as.data.table copy .I setnames is.data.table rbindlist
180180
#' @importFrom utils head tail
181181
#' @export
182182
bayesOpt <- function(
@@ -271,7 +271,7 @@ bayesOpt <- function(
271271
scoreSummary <- foreach(
272272
iter = 1:nrow(initGrid)
273273
, .options.multicore = list(preschedule=FALSE)
274-
, .combine = rbind
274+
, .combine = list
275275
, .multicombine = TRUE
276276
, .inorder = FALSE
277277
, .errorhandling = 'pass'
@@ -280,18 +280,45 @@ bayesOpt <- function(
280280
) %op% {
281281

282282
Params <- initGrid[get("iter"),]
283-
Elapsed <- system.time(Result <- do.call(what = FUN, args = as.list(Params)))
284-
if(!any(names(Result) == "Score")) stop("FUN must return list with element 'Score' at a minimum.")
283+
Elapsed <- system.time(
284+
Result <- tryCatch(
285+
{
286+
do.call(what = FUN, args = as.list(Params))
287+
}
288+
, error = function(e) e
289+
)
290+
)
291+
292+
# Make sure everything was returned in the correct format. Any errors here will be passed.
293+
if (any(class(Result) %in% c("simpleError","error","condition"))) return(Result)
294+
if (class(Result) != "list") stop("Object returned from FUN was not a list.")
295+
resLengths <- lengths(Result)
296+
if (!any(names(Result) == "Score")) stop("FUN must return list with element 'Score' at a minimum.")
297+
if (!is.numeric(Result$Score)) stop("Score returned from FUN was not numeric.")
298+
if(any(resLengths != 1)) {
299+
badReturns <- names(Result)[which(resLengths != 1)]
300+
stop("FUN returned these elements with length > 1: ",paste(badReturns,collapse = ","))
301+
}
302+
285303
data.table(Params,Elapsed = Elapsed[[3]],as.data.table(Result))
286304

287305
}
288306
)[[3]]
289307
while (sink.number() > 0) sink()
290308
if (verbose > 0) cat(" ",tm,"seconds\n")
291309

292-
# foreach passes errors as a vector.
293-
if (!is.data.table(scoreSummary)) {
294-
stop(paste0("FUN failed to run on initial try. First error returned was <<",scoreSummary[[1]],">>"))
310+
# Scan our list for any simpleErrors. If any exist, stop the process and return the errors.
311+
se <- which(sapply(scoreSummary,function(cl) any(class(cl) %in% c("simpleError","error","condition"))))
312+
if(length(se) > 0) {
313+
print(
314+
data.table(
315+
initGrid[se,]
316+
, errorMessage = sapply(scoreSummary[se],function(x) x$message)
317+
)
318+
)
319+
stop("Errors encountered in initialization are listed above.")
320+
} else {
321+
scoreSummary <- rbindlist(scoreSummary)
295322
}
296323

297324
# Format scoreSummary table. Initial iteration is set to 0

README.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Build
55
Status](https://api.travis-ci.org/AnotherSamWilson/ParBayesianOptimization.svg)](https://travis-ci.org/AnotherSamWilson/ParBayesianOptimization)
66
[![CRAN\_Status\_Badge](http://www.r-pkg.org/badges/version/ParBayesianOptimization)](https://CRAN.R-project.org/package=ParBayesianOptimization)
7-
[![DEV\_Version\_Badge](https://img.shields.io/badge/Dev-1.1.0-blue.svg)](https://CRAN.R-project.org/package=ParBayesianOptimization)
7+
[![DEV\_Version\_Badge](https://img.shields.io/badge/Dev-1.2.0-blue.svg)](https://CRAN.R-project.org/package=ParBayesianOptimization)
88
[![CRAN\_Downloads](https://cranlogs.r-pkg.org/badges/ParBayesianOptimization)](https://CRAN.R-project.org/package=ParBayesianOptimization)
99
[![Coverage
1010
Status](https://codecov.io/gh/AnotherSamWilson/ParBayesianOptimization/branch/master/graph/badge.svg)](https://codecov.io/gh/AnotherSamWilson/ParBayesianOptimization/branch/master)
@@ -315,14 +315,14 @@ to see the results:
315315
``` r
316316
optObj$scoreSummary
317317
#> Epoch Iteration max_depth min_child_weight subsample gpUtility acqOptimum inBounds Elapsed Score nrounds errorMessage
318-
#> 1: 0 1 2 1.670129 0.7880670 NA FALSE TRUE 0.12 0.9777163 2 NA
319-
#> 2: 0 2 2 14.913213 0.8763154 NA FALSE TRUE 0.28 0.9763760 15 NA
320-
#> 3: 0 3 4 18.833690 0.3403900 NA FALSE TRUE 0.46 0.9931657 18 NA
321-
#> 4: 0 4 4 8.639925 0.5499186 NA FALSE TRUE 0.27 0.9981437 7 NA
322-
#> 5: 1 5 4 21.871937 1.0000000 0.5857961 TRUE TRUE 0.14 0.9945933 1 NA
323-
#> 6: 2 6 4 0.000000 0.9439879 0.6668303 TRUE TRUE 0.27 0.9990567 7 NA
324-
#> 7: 3 7 5 1.395119 0.7071802 0.2973497 TRUE TRUE 0.23 0.9984577 4 NA
325-
#> 8: 4 8 5 0.000000 0.2500000 0.3221660 TRUE TRUE 0.38 0.9994020 10 NA
318+
#> 1: 0 1 2 1.670129 0.7880670 NA FALSE TRUE 0.07 0.9777163 2 NA
319+
#> 2: 0 2 2 14.913213 0.8763154 NA FALSE TRUE 0.15 0.9763760 15 NA
320+
#> 3: 0 3 4 18.833690 0.3403900 NA FALSE TRUE 0.20 0.9931657 18 NA
321+
#> 4: 0 4 4 8.639925 0.5499186 NA FALSE TRUE 0.12 0.9981437 7 NA
322+
#> 5: 1 5 4 21.871937 1.0000000 0.5857961 TRUE TRUE 0.08 0.9945933 1 NA
323+
#> 6: 2 6 4 0.000000 0.9439879 0.6668303 TRUE TRUE 0.12 0.9990567 7 NA
324+
#> 7: 3 7 5 1.395119 0.7071802 0.2973497 TRUE TRUE 0.11 0.9984577 4 NA
325+
#> 8: 4 8 5 0.000000 0.2500000 0.3221660 TRUE TRUE 0.16 0.9994020 10 NA
326326
```
327327

328328
``` r
@@ -381,10 +381,10 @@ optimization steps, versus the 4 performed in the sequential example:
381381
``` r
382382
tWithPar
383383
#> user system elapsed
384-
#> 0.89 0.04 6.82
384+
#> 0.96 0.05 7.47
385385
tNoPar
386386
#> user system elapsed
387-
#> 21.92 2.19 21.47
387+
#> 23.33 0.83 22.74
388388
```
389389

390390
## Sampling Multiple Promising Points at Once

cran-comments.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
## Test environments
3-
* local Windows 10 x64, R 3.6.1
3+
* local Windows 10 x64, R 4.0.0
44
* Windows Server 2008 R2 SP1 32/64 bit (Rhub)
55
* Ubuntu Linux 16.04 LTS (Rhub)
66
* Fedora Linux (Rhub)
@@ -14,6 +14,4 @@ There were no ERRORs, WARNINGs or NOTEs
1414
There are no downstream dependencies.
1515

1616
## Changes
17-
Changed Gaussian Process package to DiceKriging. predict method is much faster.
18-
Added errorHandling parameter - bayesOpt() and addIterations() should now return results no matter what, unless errorHandling = 'stop'
19-
Added otherHalting parameter.
17+
Improve error handling. Errors are now always returned without exception.

man/figures/README-plotObj-1.png

1 Byte
Loading

tests/testthat/test-errorHandling.R

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,76 @@ testthat::test_that(
7575
}
7676

7777
)
78+
79+
testthat::test_that(
80+
81+
"1D Error Handling"
82+
83+
, {
84+
85+
skip_on_cran()
86+
set.seed(14)
87+
sf <- function(x) 1000 - x^2
88+
89+
FUN <- function(x) {
90+
if (runif(1) > 0.5) stop("You foo'd when you should have bar'd.")
91+
return(list(Score = sf(x)))
92+
}
93+
94+
bounds = list(
95+
x = c(-1000,1000)
96+
)
97+
98+
optObj <- bayesOpt(
99+
FUN
100+
, bounds
101+
, initPoints = 3
102+
, iters.n = 8
103+
, errorHandling = 2
104+
, verbose = 1
105+
)
106+
107+
optObj$scoreSummary
108+
109+
expect_equal(
110+
optObj$stopStatus
111+
, ParBayesianOptimization:::makeStopEarlyMessage("Errors from FUN exceeded errorHandling limit")
112+
)
113+
114+
}
115+
116+
)
117+
118+
testthat::test_that(
119+
120+
"Malformed FUN Return"
121+
122+
, {
123+
124+
skip_on_cran()
125+
set.seed(14)
126+
sf <- function(x) 1000 - x^2
127+
128+
FUN <- function(x) {
129+
ot <- if (runif(1) > 0.75) c(0,1) else 1
130+
return(list(Score = sf(x), ot = ot))
131+
}
132+
133+
bounds = list(
134+
x = c(-1000,1000)
135+
)
136+
137+
expect_error(
138+
bayesOpt(
139+
FUN
140+
, bounds
141+
, initPoints = 3
142+
, iters.n = 8
143+
, errorHandling = 2
144+
, verbose = 1
145+
)
146+
)
147+
148+
}
149+
150+
)

0 commit comments

Comments
 (0)