-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGradAdaptTechnoReproducibleExample.R
More file actions
2896 lines (2768 loc) · 181 KB
/
GradAdaptTechnoReproducibleExample.R
File metadata and controls
2896 lines (2768 loc) · 181 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Technometrics requires a reproducible example from the paper.
# This makes the plots for the 2D limnonpoly comparison between our method and standard IMSE
# I pulled all of my functions out of their packages into here.
# Only DoE.base is not included, it needs to be loaded with `library`.
# install.packages("DoE.base")
library(DoE.base)
# Test desirability functions
# A des func for finding large gradient
#' @param return_se whether the se prediction should be returned along with
#' the des, all will be returned in data.frame, this will save
#' time if calculating the werror function since it is faster
#' to predict both at once instead of separately
des_func_grad_norm2_mean <- function(mod, XX, return_se=F) {
if ("IGP_GauPro_kernel" %in% class(mod)) {
# des <- mod$mod$grad_norm2_dist(XX=XX)$mean
des <- mod$mod$grad_norm2_mean(XX=XX)
} else if ("IGP_laGP_GauPro_kernel" %in% class(mod)) {
# des <- mod$mod.extra$GauPro$mod$grad_norm2_dist(XX=XX)$mean
des <- mod$mod.extra$GauPro$mod$grad_norm2_mean(XX=XX)
} else if ("IGP_LOOEC_GauPro_kernel" %in% class(mod)) {
des <- mod$mod$grad_norm2_mean(XX=XX)
} else {
stop("des_func_grad_norm2_mean only works with GauPro_kernel_model or laGP_GauPro_kernel")
}
des
}
msfunc <- function(func1,lims,pow=1L,batch=F, n=1e3) {
# Find mean square of function over limits using grid sample
#X1 <- simple.grid(10,nrow(lims),scaledto=lims) # Too big in high dimensions, switching to just random points
d <- nrow(lims)
X1 <- simple.random(n=n, d=d, scaledto=lims)
if(batch) {return(mean(func1(X1)^pow))}
mean(apply(X1,1,func1)^pow)
}
simple.random <- function(n,d,scaledto=NULL) {
m <- matrix(runif(n*d), ncol=d, nrow=n)
if(!is.null(scaledto)) {
m <- m * matrix(scaledto[,2]-scaledto[,1],nrow=nrow(m),ncol=ncol(m),byrow=T) + matrix(scaledto[,1],nrow=nrow(m),ncol=ncol(m),byrow=T)
}
m
}
msecalc <- function(truefunc, guessfunc,lims, n=500) {
#X1 <- simple.grid(20,nrow(lims),scaledto=lims)
#X1 <- lhs::maximinLHS(n, nrow(lims))
d <- nrow(lims)
X1 <- matrix(runif(n*d), n, d)
mean((apply(X1,1,function(xx){truefunc(xx) - guessfunc(xx)}))^2)
}
simple.LHS <- function(n,d,scaled=TRUE,centered=FALSE) {
m <- matrix(rep(1:n,d),n,d)
m <- apply(m,2,function(xx){sample(xx)})
if(scaled) m <- (m - runif(n*d) ) / n
if(centered) m <- m - ifelse(scaled,.5,n/2+.5)
m
}
#' Close all open screens
#'
#' Closes the screens open, which happens
#' when plotting with `split.screen` is interrupted.
#' It often happens when there is a error while plotting.
#' When you try to plot
#' the next thing it gives an error.
#' Running this function will reset the plot screen.
#' It just does `close.screen(all.screens=TRUE)` but is faster to type.
#'
#' @param silent Should the output of `close.screen` not be returned?
#'
#' @examples
#' # Split screen into fourths
#' split.screen(c(2,2))
#' hist(rnorm(100))
#' screen(2)
#' hist(runif(100))
#' # Use csa() to go back to normal plotting
#' csa()
#' hist(rexp(100))
#' @export
csa <- function(silent=FALSE) {
if (silent) { # Suppresses "FALSE" if already closed
invisible(close.screen(all.screens=TRUE))
} else {
close.screen(all.screens=TRUE)
}
}
#' Makes filled contour plot from function
#'
#' A contour plot of the given function without sidebar by default.
#' It calls the function `cf_grid` to make the actual plot.
#'
#' @param fn0 function to plot, first argument must be two-dimensional
#' @param n number of points in each dimension
#' @param xlim x limits for the contour plot
#' @param ylim y limits for the contour plot
#' @param xylim x and y limits for the contour plot, use when both are same
#' #@param mainminmax whether the min and max values should be shown in the title of plot
#' @param batchmax number of datapoints that can be computed at a time
#' @param out.col.name if a column needs to be selected from the function, specify it
#' @param out.name Selects with a $ the name from output to be used, for lists and data frames
#' #@param pretitle Text to be preappended to end of plot title
#' #@param posttitle Text to be appended to end of plot title
#' #@param title Title for the plot
#' #@param mainminmax_minmax Whether [min,max]= should be shown in title or just the numbers
#' @param pts Points to plot on top of contour
#' @param use_lines If the contour should be made with lines. Otherwise is made
#' using colors. Defaults to colors.
#' @param ... Passed to cf_grid
#' @examples
#' cf_func(function(x){x[1]*x[2]})
#' cf_func(function(x)(exp(-(x[1]-.5)^2-5*(x[2]-.5)^2)))
#' cf_func(function(xx){exp(-sum((xx-.5)^2/.1))}, bar=TRUE)
#' cf_func(function(xx){exp(-sum((xx-.5)^2/.1))}, bar=TRUE, mainminmax=TRUE)
#' cf_func(function(x)(exp(-(x[1]-.5)^2-5*(x[2]-.5)^2)), use_lines=TRUE)
#' @references
#' [1] filled.contour R function, copied function but removed part for sidebar
#' @references
#' [2] http://stackoverflow.com/questions/16774928/removing-part-of-a-graphic-in-r, answer by P Lapointe
#' @export
cf_func <- function(fn0, n=100,
xlim=c(0,1), ylim=c(0,1), xylim=NULL,
batchmax=1, out.col.name=NULL,
out.name=NULL,
pts=NULL,
use_lines=FALSE,
...) {
if(!is.null(out.col.name)) {
fn <- function(xx){fn0(xx)[,out.col.name]}
} else if (!is.null(out.name)) {
fn <- function(xx){fn0(xx)[[out.name]]}
} else {
fn <- fn0
}
if (!is.null(xylim)) {xlim <- ylim <- xylim}
x <- seq(xlim[1],xlim[2],length.out = n)
y <- seq(ylim[1],ylim[2],length.out = n)
z <- eval_over_grid_with_batch(x, y, fn, batchmax)
if (use_lines) {
contour(x, y, z, ...)
points(pts, pch=19)
} else
cf_grid(x,y,z, pts=pts, ...)
}
#' Evaluate function over grid of points
#'
#' `batchmax` gives how many can be evaluated at a time.
#' If more than 1, then the input is given to the function
#' as rows of a matrix.
#'
#'
#' @param x Vector of x values to evaluate
#' @param y Vector of y values to evaluate
#' @param fn Function that takes in a length two vector if `batchmax` is 1
#' or a matrix with two columns if greater than 1.
#' @param batchmax Number of points that can evaluated simultaneously.
#' If 1, points are passed to `fn` as a vector of length two.
#' If greater than 1, points are passed to `fn` as rows of a matrix.
#'
#' @return Matrix of size `length(x)` by `length(y)`
#' @export
#'
#' @examples
#' eval_over_grid_with_batch(c(0,.5,1), c(10,20,30), function(a)a[1]+a[2], batchmax=1)
#' eval_over_grid_with_batch(c(0,.5,1), c(10,20,30), function(a)a[,1]+a[,2], batchmax=Inf)
eval_over_grid_with_batch <- function(x, y, fn, batchmax) {
nx <- length(x)
ny <- length(y)
if(batchmax<=1) { # calculate single Z value at a time
#for(xi in 1:n) for(yi in 1:n) {z[xi,yi] <- fn(c(x[xi],y[yi]))}
fn_outer <- Vectorize(function(xi, yi) {fn(c(x[xi], y[yi]))})
z <- outer(1:nx, 1:ny, fn_outer)
} else {
z <- matrix(NA,nx,ny)
inbatch <- 0
for(xi in 1:nx) {
for(yi in 1:ny) {
if(inbatch==0) XYi <- matrix(c(xi,yi),ncol=2)
else XYi <- rbind(XYi,matrix(c(xi,yi),ncol=2))
inbatch <- inbatch + 1
if(inbatch == batchmax | (xi==nx & yi==ny)) {
Zbatch <- fn(matrix(c(x[XYi[,1]],y[XYi[,2]]),ncol=2,byrow=F))
for(rowbatch in 1:length(Zbatch)) {
z[XYi[rowbatch,1],XYi[rowbatch,2]] <- Zbatch[rowbatch]
}
inbatch <- 0
rm(XYi)
}
}
}
}
z
}
#' Create a contour plot from a grid of data
#'
#' Makes filled contour plot with an optional sidebar, essentially filled.contour function.
#' This version uses the split.screen() function to add the sidebar if bar is TRUE.
#' By default it won't show the bar but will show the min and max values in the plot title
#' along with their colors.
#' Using this function will make other functions such as points() called afterwards not put points
#' where you expect. Pass anything you want added to the plot area to afterplotfunc
#' as a function to get it to work properly.
#'
#' @param x x values, must form grid with y. If not given, it is assumed to be from 0 to 1.
#' @param y y values, must form grid with x. If not given, it is assumed to be from 0 to 1.
#' @param z z values at grid locations
#' @param xlim x limits for the plot.
#' @param ylim y limits for the plot.
#' @param zlim z limits for the plot.
#' @param levels a set of levels which are used to partition the range of z. Must be strictly increasing (and finite). Areas with z values between consecutive levels are painted with the same color.
#' @param nlevels if levels is not specified, the range of z, values is divided into approximately this many levels.
#' @param color.palette a color palette function to be used to assign colors
#' in the plot. Defaults to cm.colors. Other options include rainbow,
#' heat.colors, terrain.colors, topo.colors, and function(x) {gray((1:x)/x)}.
#' @param col an explicit set of colors to be used in the plot. This argument overrides any palette function specification. There should be one less color than levels
#' @param plot.title statements which add titles to the main plot.
#' @param plot.axes statements which draw axes (and a box) on the main plot. This overrides the default axes.
#' @param key.title statements which add titles for the plot key.
#' @param key.axes statements which draw axes on the plot key. This overrides the default axis.
#' @param asp the y/x aspect ratio, see plot.window.
#' @param xaxs the x axis style. The default is to use internal labeling.
#' @param yaxs the y axis style. The default is to use internal labeling.
#' @param las the style of labeling to be used. The default is to use horizontal labeling.
#' @param axes logical indicating if axes should be drawn, as in plot.default.
#' @param frame.plot logical indicating if a box should be drawn, as in plot.default.
#' @param bar Should a bar showing the output range and colors be shown on the right?
#' @param pts Points to plot on top of contour
#' @param reset.par Should the graphical parameters be reset before exiting? Usually should be
#' unless you need to add something to the plot afterwards and bar is TRUE.
#' @param pretitle Text to be preappended to end of plot title
#' @param posttitle Text to be appended to end of plot title
#' @param main Title for the plot
#' @param mainminmax whether the min and max values should be shown in the title of plot
#' @param mainminmax_minmax Whether [min,max]= should be shown in title or just the numbers
#' @param afterplotfunc Function to call after plotting, such as adding points or lines.
#' @param cex.main The size of the main title. 1.2 is default.
#' @param par.list List of options to pass to par
#' @param xaxis Should x axis be added?
#' @param yaxis Should y axis be added?
#' @param ... additional graphical parameters, currently only passed to title().
#' @importFrom grDevices cm.colors
#' @importFrom graphics .filled.contour
#' @importFrom graphics Axis
#' @importFrom graphics box
#' @importFrom graphics plot.new
#' @importFrom graphics plot.window
#' @importFrom graphics points
#' @importFrom graphics title
#' @importFrom graphics par
#' @importFrom graphics axis layout lcm rect
#' @importFrom graphics split.screen screen close.screen
#' @examples
#' x <- y <- seq(-4*pi, 4*pi, len = 27)
#' r <- sqrt(outer(x^2, y^2, "+"))
#' cf_grid(cos(r^2)*exp(-r/(2*pi)))
#' cf_grid(r, color.palette=heat.colors, bar=TRUE)
#' cf_grid(r, color.palette=function(x) {gray((1:x)/x)}, bar=TRUE)
#' @references
#' [1] filled.contour R function, copied function but removed part for sidebar
#' @references
#' [2] http://stackoverflow.com/questions/16774928/removing-part-of-a-graphic-in-r, answer by P Lapointe
#' @export
cf_grid <-
function (x = seq(0, 1, length.out = nrow(z)),
y = seq(0, 1,length.out = ncol(z)), z, xlim = range(x, finite = TRUE),
ylim = range(y, finite = TRUE), zlim = range(z, finite = TRUE),
levels = pretty(zlim, nlevels), nlevels = 20, color.palette = cm.colors,
col = color.palette(length(levels) - 1), plot.title, plot.axes,
key.title, key.axes, asp = NA, xaxs = "i", yaxs = "i", las = 1,
axes = TRUE, frame.plot = axes, bar=F, pts=NULL, reset.par=TRUE,
pretitle="", posttitle="", main=NULL,
mainminmax=!bar, mainminmax_minmax=TRUE,
afterplotfunc=NULL,
cex.main=par()$cex.main,
par.list=NULL,
xaxis=TRUE, yaxis=TRUE,
...)
{
# filled.contour gives unnecessary legend, this function removes it
# Used P Lapointe's solution from here: http://stackoverflow.com/questions/16774928/removing-part-of-a-graphic-in-r
# also had to changed .Internal(fillcontour) to .filled.contour
# and change layout to layout(matrix(c(1, 1), ncol = 1L), widths = c(1, lcm(w)))
# Created 3/28/16 by Collin Erickson
if (missing(z)) {
if (!missing(x)) {
if (is.list(x)) {
z <- x$z
y <- x$y
x <- x$x
}
else {
z <- x
x <- seq.int(0, 1, length.out = nrow(z))
}
}
else stop("no 'z' matrix specified")
}
else if (is.list(x)) {
y <- x$y
x <- x$x
}
if (any(diff(x) <= 0) || any(diff(y) <= 0))
stop("increasing 'x' and 'y' values expected")
if (any(diff(x) <= 0) || any(diff(y) <= 0))
stop("increasing 'x' and 'y' values expected")
# mar.orig <- (par.orig <- par(c("mar", "las", "mfrow")))$mar
#if (reset.par) {on.exit({par(par.orig);close.screen(1)})}#all=TRUE)})}
# This allows user to pass in par.list and it will return it after plotting
par.names.to.save <- c("mar", "las", "mfrow", names(par.list))
mar.orig <- (par.orig <- par(par.names.to.save))$mar
if (!is.null(par.list)) {
par(par.list)
}
#on.exit(close.screen(all=TRUE))
w <- (3 + mar.orig[2L]) * par("csi") * 2.54
#layout(matrix(c(if(bar) 2 else 1, 1), ncol = 2L), widths = c(1, lcm(w)))
par(las = las)
start.screen.number <- screen()
if (bar) {
#split.screen(c(1,2))
screen.numbers <- split.screen(matrix(c(0,.85,0,1,.85,1,0,1), ncol=4, byrow=T))
screen1 <- screen.numbers[1]
screen2 <- screen.numbers[2]
screen(screen2)
mar <- mar.orig
mar[4L] <- 2.5#mar[2L] # right
mar[1] <- 2.2 # bottom
mar[3] <- if (mainminmax | !is.null(main)) 1.3 else .3 #1.3#1.3 # top
mar[2L] <- 0#1 # left
par(mar = mar)
plot.new()
plot.window(xlim = c(0, 1), ylim = range(levels), xaxs = "i",
yaxs = "i")
rect(0, levels[-length(levels)], 1, levels[-1L], col = col)
if (missing(key.axes)) {
if (axes)
axis(4)
}
else key.axes
box()
if (!missing(key.title))
key.title
mar <- mar.orig
mar[4L] <- 1 # right # Why is this here?
close.screen(screen2)
screen(screen1)
mar[1L] <- 2.2 # bottom
mar[2L] <- 2.5 # left
mar[3L] <- if (mainminmax | !is.null(main)) 1.3 else .3 #1.3# 1.3 # top
}
if (!bar) {
# Using screen even with 1 screen to avoid error. Adding points after didn't show up properly
screen.numbers <- split.screen(c(1,1))
screen1 <- screen.numbers[1]
screen(screen1)
# Changing the margin to get bigger and square
mar <- mar.orig #<- par()$mar
mar[1] <- 2.2 # bottom
mar[2] <- 2.5 # left
mar[3] <- if (mainminmax | !is.null(main)) 1.3 else .3 # top
mar[4] <- 1 # right
# if (!missing(plot.axes)) {
# # TODO I shouldn't use plot.axes like this, FIX THIS
# mar[1] <- .3 # bottom
# mar[2] <- .3 # left
# mar[4] <- .3 # 1 # right
# }
if (!xaxis && !yaxis) {
mar[1] <- .3 # bottom
mar[2] <- .3 # left
mar[3] <- if (mainminmax | !is.null(main)) 1.3 else .3 # top
mar[4] <- .3 # right
} else if (!xaxis) {
mar[1] <- 1-.7 # bottom
mar[3] <- if (mainminmax | !is.null(main)) 1.3 else .3 # top
mar[4] <- .3 # right
} else if (!yaxis) {
mar[2] <- 1-.7 # left
mar[3] <- if (mainminmax | !is.null(main)) 1.3 else .3 # top
mar[4] <- .3 # right
}
}
par(mar = mar)
# par(cex.axis = 2)
plot.new()
plot.window(xlim, ylim, "", xaxs = xaxs, yaxs = yaxs, asp = asp)
if (!is.matrix(z) || nrow(z) <= 1L || ncol(z) <= 1L)
stop("no proper 'z' matrix specified")
if (!is.double(z))
storage.mode(z) <- "double"
#.Internal(filledcontour(as.double(x), as.double(y), z, as.double(levels),
# col = col))
.filled.contour(as.double(x), as.double(y), z, as.double(levels),
col = col)
# Something like this will remove axis numbers and ticks
# Axis(x, side=1, labels=F, tick=F)
if (missing(plot.axes)) {
if (axes) {
# title(main = "", xlab = "", ylab = "")
if (xaxis) Axis(x, side = 1)
if (yaxis) Axis(y, side = 2)
}
}
else plot.axes
if (frame.plot)
box()
#if (missing(plot.title))
# title(...)
#else plot.title
if (mainminmax | !is.null(main)) {
make.multicolor.title(main=main, z=z, pretitle=pretitle, posttitle=posttitle, mainminmax_minmax=mainminmax_minmax, cex.main=cex.main)
}
if (!is.null(pts)) {
if (!is.matrix(pts)) { # if not a matrix, make it a matrix by row
if (is.numeric(pts) && (length(pts)%%2==0)) {
pts <- matrix(pts, ncol=2, byrow = T)
}
}
points(pts, pch=19)
}
if (!is.null(afterplotfunc)) {
afterplotfunc()
}
reset.par.func <- function() {
if (T) {close.screen(screen1)}
if (start.screen.number != FALSE) {screen(start.screen.number, new=FALSE)}
par(par.orig)
}
if (reset.par) {# Either reset parameters
reset.par.func()
invisible()
} else { # or return it so user can do it later
return(reset.par.func)
}
}
make.multicolor.title <- function(main, z, pretitle, posttitle, mainminmax_minmax, cex.main=par()$cex.main) {
if(is.null(main)) {
title_text <- c(pretitle)
title_color <- c(1)
if (mainminmax_minmax) {
title_text <- c(title_text, '[','min', ', ','max', '] = ')
title_color <- c(title_color,1, "#80FFFFFF",1, "#FF80FFFF",1)
}
title_text <- c(title_text, "[",signif(min(z),3),', ',signif(max(z),3),']',posttitle)
title_color <- c(title_color,1, 1, 1, 1, 1, 1)
multicolor.title(title_text,title_color, cex.main=cex.main)
} else {
multicolor.title(main, 1, cex.main=cex.main)
}
}
#' Makes plot title using specified colors for the text
#' @param main Text to put in main title of plot
#' @param col.main Colors to use for the text
#' @param collapse What to put between elements of main, defaults to "" but " " might be appropriate
#' @param cex.main The size of the main title. 1.2 is default.
#' @examples
#' plot(1:4)
#' multicolor.title(c('Black, ','red, ','green'),c(1,2,3))
#' @export
multicolor.title <- function(main,col.main, collapse='', cex.main=par()$cex.main) {
if (length(main) != length(col.main)) {stop('main and col must have same length')}
n <- length(main)
if(n==1) {
title(bquote(.(main[1])),col.main=col.main[1], cex.main=cex.main)
} else {
# print first
title(bquote(.(main[1]) * phantom(.(paste0(main[2:n],collapse=collapse)))),col.main=col.main[1], cex.main=cex.main)
# print middle
if(n > 2) {
for(i in 2:(n-1)) {
title(bquote(phantom(.(paste0(main[1:(i-1)],collapse=collapse))) * .(main[i]) * phantom(.(paste0(main[(i+1):n],collapse=collapse)))),col.main=col.main[i], cex.main=cex.main)
}
}
# print last
title(bquote(phantom(.(paste0(main[1:(n-1)],collapse=collapse))) * .(main[n])),col.main=col.main[n], cex.main=cex.main)
}
}
#' Simpler function for making contours with cf package.
#' Won't give argument completion, so all must be specified
#'
#' @param ... Arguments to be passed to cf_func or cf_data based on
#' data type of first argument. If D is given as argument, then it
#' is passed to cf_highdim.
#'
#' @return Whatever is returned from other function, probably nothing
#' @export
#'
#' @examples
#' cf(function(x){x[1]^2 - x[2]})
#' x <- runif(20)
#' y <- runif(20)
#' z <- exp(-(x-.5)^2-5*(y-.5)^2)# + rnorm(20,0,.05)
#' cf(x,y,z)
#' cf(function(x){x[1]^2 - x[2]}, D=3)
cf <- function(...) {
dots <- list(...)
if (is.function(dots[[1]])) {
if ("D" %in% names(dots)) {
cf_highdim(...)
} else {
cf_func(...)
}
} else if (is.numeric(dots[[1]])) {
cf_data(...)
} else {
stop("Data not recognized. Use cf_func for function or
cf_data for data or cf_grid for full grid of data.")
}
}
#' limnonpoly: 2 dimensional function.
#' Equation 28 from Lim et al 2002.
#'
#' @references Lim, Yong B., Jerome Sacks, W. J. Studden, and William J. Welch.
#' "Design and analysis of computer experiments when the output is highly
#' correlated over the input space."
#' Canadian Journal of Statistics 30, no. 1 (2002): 109-126.
#' @export
#' @rdname test_func_apply
#' @examples
#' limnonpoly(runif(2))
limnonpoly <- function(x, scale_it=F, scale_low = c(0,0), scale_high = c(1,1), noise=0) {
if (is.matrix(x)) apply(x, 1, TF_limnonpoly)
else TF_limnonpoly(x)
}
#' TF_limnonpoly: A function taking in a single vector.
#' 2 dimensional function.
#' See corresponding function with "TF_" for more details.
#' @export
#' @rdname TF_OTL_Circuit
#' @examples
#' TF_limnonpoly(runif(2))
TF_limnonpoly <- function(x) {
((30+5*x[1]*sin(5*x[1]))*(4+exp(-5*x[2])) - 100) / 6
}
#' IGP general function
#'
#' @param package Package to use
#' @param X Design matrix
#' @param Z Response matrix or vector
#' @param ... Arguments passed on to IGP_<package>
#'
#' @return IGP model
#' @export
#'
#' @examples
#' x <- seq(0,1,l=10)
#' y <- abs(sin(2*pi*x))
#' IGP(x,y,'DiceKriging')
IGP <- function(X=NULL, Z=NULL, package=NULL, ...) {
if (length(package)==0) {
stop("No package specified Error # 5792324572")
} else if (package %in% c("laGP", "laGP")) {
u <- IGP_laGP$new(X=X, Z=Z, ...)
} else if (package %in% c("GauPro_kernel", "gaupro_kernel")) {
u <- IGP_GauPro_kernel$new(X=X, Z=Z, ...)
} else if (tolower(package) %in% c("lagp_gaupro_kernel")) {
u <- IGP_laGP_GauPro_kernel$new(X=X, Z=Z, ...)
} else {
stop("Package not recognized")
}
u$package <- package
u
}
#' UGP
#' Class providing object with methods for fitting a GP model
#'
#' @docType class
#' @importFrom R6 R6Class
#' @export
#' @keywords data, kriging, Gaussian process, regression
#' @return Object of \code{\link{R6Class}} with methods for fitting GP model.
#' @format \code{\link{R6Class}} object.
#' @examples
#' n <- 40
#' d <- 2
#' n2 <- 20
#' f1 <- function(x) {sin(2*pi*x[1]) + sin(2*pi*x[2])}
#' X1 <- matrix(runif(n*d),n,d)
#' Z1 <- apply(X1,1,f1) + rnorm(n, 0, 1e-3)
#' X2 <- matrix(runif(n2*d),n2,d)
#' Z2 <- apply(X2,1,f1)
#' XX1 <- matrix(runif(10),5,2)
#' ZZ1 <- apply(XX1, 1, f1)
#' u <- IGP(package='laGP',X=X1,Z=Z1, corr="gauss")
#' cbind(u$predict(XX1), ZZ1)
#' u$predict.se(XX1)
#' u$update(Xnew=X2,Znew=Z2)
#' u$predict(XX1)
#' u$delete()
#' @field X Design matrix
#' @field Z Responses
#' @field N Number of data points
#' @field D Dimension of data
#' @section Methods:
#' \describe{
#' \item{Documentation}{For full documentation of each method go to https://github.com/CollinErickson/UGP/}
#' \item{\code{new(X=NULL, Z=NULL, package=NULL, corr="gauss",
#' estimate.nugget=T, nugget0=F, ...)}}{This method
#' is used to create object of this class with \code{X} and \code{Z} as the data.
#' The package tells it which package to fit the GP model.}
#' \item{\code{Xall=NULL, Zall=NULL, Xnew=NULL, Znew=NULL, ...}}{This method
#' updates the model, adding new data if given, then running optimization again.}}
IGP_base <- R6::R6Class(classname = "IGP",
public = list(
X = NULL, #"matrix",
Z = NULL, #"numeric",
package = NULL, #"character",
.init = function(...){stop("This function must be overwritten by subclass")}, #"function",
.update = function(...){stop("This function must be overwritten by subclass")}, #"function",
.predict = function(...){stop("This function must be overwritten by subclass")}, #"function",
.predict.se = function(...){stop("This function must be overwritten by subclass")}, #"function",
.predict.var = function(...){stop("This function must be overwritten by subclass")}, #"function",
#.grad = function(...){stop("This function must be overwritten by subclass")},
.delete = function(...){self$mod <- NULL}, #"function",
#.theta = function(...){stop("This function must be overwritten by subclass")}, #"function",
#.nugget = function(...){stop("This function must be overwritten by subclass")}, #"function",
#.mean = function(...){stop("This function must be overwritten by subclass")}, # function that gives mean
mod = NULL, #"list", # First element is model
mod.extra = list(), #"list", # list to store additional data needed for model
n.at.last.update = NULL, #"numeric", # count how many in update, must be at end of X
corr = NULL, #"numeric",
estimate.nugget = NULL, #"logical", Should the nugget be estimated?
nugget0 = NULL, #"numeric" # What value should the nugget be set to? NOT logical. If estimate.nugget==TRUE, then it's the starting value
initialize = function(X=NULL, Z=NULL, package=NULL, corr="gauss", estimate.nugget=TRUE, nugget0=1e-8, ...) {#browser()
if (!is.null(X)) {self$X <- if (is.matrix(X)) X else matrix(X, ncol=1)} # Add else for 1D data passed as vector
if (!is.null(Z)) {self$Z <- if (is.matrix(Z)) c(Z) else Z}
self$package <- package
self$n.at.last.update <- 0
self$corr <- corr
self$estimate.nugget <- estimate.nugget
self$nugget0 <- nugget0
#if(length(self$X) != 0 & length(self$Z) != 0 & length(self$package) != 0) {
if(length(self$X) != 0 & length(self$Z) != 0) {
self$init(...)
}
}, # end initialize
init = function(X=NULL, Z=NULL, ...) {#browser()
if (!is.null(X)) {self$X <- X}
if (!is.null(Z)) {self$Z <- Z}
if (length(self$X) == 0 | length(self$Z) == 0) {stop("X or Z not set")}
self$n.at.last.update <- nrow(self$X)
if (max(self$Z) - min(self$Z) < 1e-8) {warning("Z values are too close, adding noise"); self$Z <- self$Z + rnorm(length(self$Z), 0, 1e-6)}
self$.init(...)
}, # end init
update = function(Xall=NULL, Zall=NULL, Xnew=NULL, Znew=NULL, ...) {#browser()
if (self$n.at.last.update == 0) {
#self$init(X = if(!is.null(Xall)) Xall else Xnew, Z = if (!is.null(Zall)) Zall else Znew)
x <- if(!is.null(Xall)) Xall else Xnew
z <- if (!is.null(Zall)) Zall else Znew
self$init(X = x, Z = z)
} else {
if (!is.null(Xall)) {self$X <- Xall} else if (!is.null(Xnew)) {self$X <- rbind(self$X, Xnew)}
if (!is.null(Zall)) {self$Z <- Zall} else if (!is.null(Znew)) {self$Z <- c(self$Z, Znew)}
self$.update(...)
}
self$n.at.last.update <- nrow(self$X)
}, # end update
predict = function(XX, se.fit = FALSE, ...) {#browser()
if(!is.matrix(XX)) XX <- matrix(XX,nrow=1)
self$.predict(XX, se.fit=se.fit, ...)
},
predict.se = function(XX, ...) {
if(!is.matrix(XX)) XX <- matrix(XX,nrow=1)
self$.predict.se(XX, ...=...)
},
predict.var = function(XX, ...) {
if(!is.matrix(XX)) XX <- matrix(XX,nrow=1)
self$.predict.var(XX, ...=...)
},
grad = function (XX, num=FALSE) {#browser() # NUMERICAL GRAD IS OVER 10 TIMES SLOWER
if (!is.matrix(XX)) {
if (ncol(self$X) == 1) XX <- matrix(XX, ncol=1)
else if (length(XX) == ncol(self$X)) XX <- matrix(XX, nrow=1)
else stop('Predict input should be matrix')
} else {
if (ncol(XX) != ncol(self$X)) {stop("Wrong dimension input")}
}
if (is.null(self$.grad) | num) { # if no method, use numerical
#print('using num')
self$grad_num(XX)
} else {#print('using package')
self$.grad(XX)
}
},
grad_num = function (XX) {
grad.func <- function(xx) self$predict(xx)
grad.apply.func <- function(xx) numDeriv::grad(grad.func, xx)
grad1 <- apply(XX, 1, grad.apply.func)
if (ncol(self$X) == 1) return(grad1)
t(grad1)
},
grad_from_theta = function(XX, theta) {
if (missing(theta)) {
theta <- self$theta()
if (is.null(theta)) {
stop("Need theta for grad_from_theta")
}
}
mu <- self$mean()
D <- ncol(self$X)
N <- nrow(self$X)
if (!is.matrix(XX)) {
if (D == 1) XX <- matrix(XX, ncol=1)
else if (length(XX) == D) XX <- matrix(XX, nrow=1)
else stop('Predict input should be matrix')
} else {
if (ncol(XX) != D) {stop("Wrong dimension input")}
}
# kx.xx <- self$corr_func(self$X, XX, theta=self$theta)
kx.xx <- GauPro::corr_gauss_matrix(self$X, XX, theta)
Kx <- GauPro::corr_gauss_matrix_symC(self$X, theta)
Kx_nug <- Kx + diag(self$nugget(), nrow(Kx))
Kinv_Z_minus_mu <- solve(Kx_nug, self$Z - mu)
grad1 <- vapply(1:nrow(XX),
Vectorize(
function(k) {
t(-2 * outer(1:N, 1:D, Vectorize(function(i,j) {theta[j] * (XX[k, j] - self$X[i, j]) * kx.xx[i, k]}))
) %*%Kinv_Z_minus_mu
}
)
, numeric(D)
)
if (D == 1) return(grad1)
t(grad1)
},
grad_norm = function (XX) {#browser()
grad1 <- self$grad(XX)
if (!is.matrix(grad1)) return(abs(grad1))
apply(grad1,1, function(xx) {sqrt(sum(xx^2))})
},
sample = function(XX, n=1) {
if (length(XX) != ncol(self$X)) {stop("Can only sample one point at a time right now error 23537898")}
XX.pred <- self$predict(XX=XX, se.fit=T)
rnorm(n=n, mean=XX.pred$fit, sd=XX.pred$se.fit)
},
theta = function() {
self$.theta()
},
nugget = function() {
self$.nugget()
},
s2 = function() {
self$.s2()
},
mean = function() {
if (!is.null(self$.mean)) {
self$.mean()
} else {
self$predict(matrix(rep(max(abs(self$X)) * 10,ncol(self$X)), nrow=1))
}
},
max.var = function() {
self$predict.var(matrix(rep(max(abs(self$X)) * 10,ncol(self$X)), nrow=1))
},
at.max.var = function(X, val=.9) {#browser() #logical if pred var at least 90% of max var
maxvar = c(self$max.var())
self$predict.var(X) > val * maxvar
},
prop.at.max.var =function(Xlims = matrix(c(0,1), nrow=ncol(self$X), ncol=2, byrow=T), n = 200, val=.9) {#browser()
maxvar = c(self$max.var())
X <- apply(Xlims, 1, function(Xlim) {runif(n, Xlim[1], Xlim[2])})
sum(self$predict.var(X) > val * maxvar) / n
},
plot = function() {#browser()
minx <- min(self$X)
maxx <- max(self$X)
minxeval <- minx - .03 * (maxx - minx)
maxxeval <- maxx + .03 * (maxx - minx)
if (ncol(self$X) == 1) {
XX <- matrix(seq(minxeval,maxxeval,length.out = 300), ncol=1)
pp <- self$predict(XX=XX, se.fit=TRUE)
pm <- pp$fit
ps <- pp$se.fit
phigh <- pm + 2 * ps
plow <- pm - 2 * ps
plot(XX, pm, col='white', ylim=c(min(plow), max(phigh)),
xlab="X", ylab="Z")
points(XX, phigh, type='l', col=2, lwd=2)
points(XX, plow, type='l', col=2, lwd=2)
points(XX, pm, type='l', lwd=3)
points(self$X, self$Z, pch=19, cex=2)
} else {
stop("No plot method for higher than 1D")
}
},
delete = function(...) {
self$.delete(...=...)
},
finalize = function(...) {
self$delete() # Mostly for laGP to delete, Python should close connection
}
)
)
#' IGP R6 object for fitting GauPro model
#'
#' Class providing object with methods for fitting a GP model
#'
#' @docType class
#' @importFrom R6 R6Class
#' @export
#' @keywords data, kriging, Gaussian process, regression
#' @return Object of \code{\link{R6Class}} with methods for fitting GP model.
#' @format \code{\link{R6Class}} object.
#' @examples
#' n <- 40
#' d <- 2
#' n2 <- 20
#' f1 <- function(x) {sin(2*pi*x[1]) + sin(2*pi*x[2])}
#' X1 <- matrix(runif(n*d),n,d)
#' Z1 <- apply(X1,1,f1) + rnorm(n, 0, 1e-3)
#' X2 <- matrix(runif(n2*d),n2,d)
#' Z2 <- apply(X2,1,f1)
#' XX1 <- matrix(runif(10),5,2)
#' ZZ1 <- apply(XX1, 1, f1)
#' u <- IGP_GauPro_kernel$new(X=X1,Z=Z1, parallel=FALSE)
#' cbind(u$predict(XX1), ZZ1)
#' u$predict.se(XX1)
#' u$update(Xnew=X2,Znew=Z2)
#' u$predict(XX1)
#' u$delete()
#' @field X Design matrix
#' @field Z Responses
#' @field N Number of data points
#' @field D Dimension of data
#' @section Methods:
#' \describe{
#' \item{Documentation}{For full documentation of each method go to https://github.com/CollinErickson/IGP/}
#' \item{\code{new(X=NULL, Z=NULL, package=NULL,
#' estimate.nugget=T, nugget0=F, ...)}}{This method
#' is used to create object of this class with \code{X} and \code{Z} as the data.
#' The package tells it which package to fit the GP model.}
#' \item{\code{update(Xall=NULL, Zall=NULL, Xnew=NULL, Znew=NULL, ...)}}{This method
#' updates the model, adding new data if given, then running optimization again.}}
IGP_GauPro_kernel <- R6::R6Class(
classname = "IGP_GauPro_kernel",
inherit = IGP_base,
public = list(
.init = function(..., kernel=NULL, theta=NULL) {
if (!is.null(kernel)) {
# kernel will be passed in
} else if (any(c("R6ClassGenerator", "GauPro_kernel")%in% class(self$corr))) {
kernel <- self$corr
} else if (self$corr[[1]] == "gauss") {
kernel <- GauPro::Gaussian$new(D=ncol(self$X))
} else if (self$corr[[1]] == "matern32") {
kernel <- GauPro::Matern32$new(D=ncol(self$X))
} else if (self$corr[[1]] == "matern52") {
kernel <- GauPro::Matern52$new(D=ncol(self$X))
} else if (self$corr[[1]] == "exponential") {
kernel <- GauPro::Exponential$new(D=ncol(self$X))
} else if (self$corr[[1]] == "periodic") {
kernel <- GauPro::periodic$new(D=ncol(self$X))
} else if (self$corr[[1]] == "rationalquadratic") {
kernel <- GauPro::RatQuad$new(D=ncol(self$X))
} else {
stop("Corr/kernel not recognized in IGP_GauPro_kernel")
}
if (!is.null(theta)) {kernel$beta <- log(theta, 10)}
m <- GauPro::GauPro_kernel_model$new(X=self$X, Z=self$Z, kernel=kernel, nug.est=self$estimate.nugget, nug=self$nugget0, ...)
self$mod <- m
}, #"function to initialize model with data
.update = function(...) {
self$mod$update(Xall=self$X, Zall=self$Z, ...)
}, #"function to add data to model or reestimate params
.predict = function(XX, se.fit, ...) {
if (se.fit) {
preds <- self$mod$pred(XX=XX, se.fit=T)
list(fit=preds$mean, se.fit=preds$se)
} else {
c(self$mod$pred(XX=XX))
}
}, #"function to predict at new values
.predict.se = function(XX, ...) {self$mod$pred(XX=XX, se.fit=T)$se}, #"function predict the standard error/dev
.predict.var = function(XX, ...) {self$mod$pred(XX=XX, se.fit=T)$s2}, #"function to predict the variance
.grad = function(XX) {self$mod$grad(XX=XX)}, # function to calculate the gradient
.delete = function(...){self$mod <- NULL}, #"function to delete model beyond simple deletion
.theta = function() {10 ^ self$mod$kernel$beta}, #"function to get theta, exp(-theta*(x-x)^2)
.nugget = function() {self$mod$nug}, #"function to get nugget
.s2 = function() {self$mod$s2_hat},
.mean = function() {self$mod$trend$m} # function that gives mean (constant, other functions not implemented)
)
)
#' IGP R6 object for fitting laGP model
#'
#' Class providing object with methods for fitting a GP model
#'
#' @docType class
#' @importFrom R6 R6Class
#' @export
#' @keywords data, kriging, Gaussian process, regression
#' @return Object of \code{\link{R6Class}} with methods for fitting GP model.
#' @format \code{\link{R6Class}} object.
#' @examples
#' n <- 40
#' d <- 2
#' n2 <- 20
#' f1 <- function(x) {sin(2*pi*x[1]) + sin(2*pi*x[2])}
#' X1 <- matrix(runif(n*d),n,d)
#' Z1 <- apply(X1,1,f1) + rnorm(n, 0, 1e-3)
#' X2 <- matrix(runif(n2*d),n2,d)
#' Z2 <- apply(X2,1,f1)
#' XX1 <- matrix(runif(10),5,2)
#' ZZ1 <- apply(XX1, 1, f1)
#' u <- IGP_laGP$new(X=X1,Z=Z1)
#' cbind(u$predict(XX1), ZZ1)
#' u$predict.se(XX1)
#' u$update(Xnew=X2,Znew=Z2)
#' u$predict(XX1)
#' u$delete()
#' @field X Design matrix
#' @field Z Responses
#' @field N Number of data points
#' @field D Dimension of data
#' @section Methods:
#' \describe{
#' \item{Documentation}{For full documentation of each method go to https://github.com/CollinErickson/IGP/}
#' \item{\code{new(X=NULL, Z=NULL, package=NULL,
#' estimate.nugget=T, nugget0=F, ...)}}{This method
#' is used to create object of this class with \code{X} and \code{Z} as the data.
#' The package tells it which package to fit the GP model.}
#' \item{\code{update(Xall=NULL, Zall=NULL, Xnew=NULL, Znew=NULL, ...)}}{This method
#' updates the model, adding new data if given, then running optimization again.}}
IGP_laGP <- R6::R6Class(classname = "IGP_laGP", inherit = IGP_base,
public = list(
.init = function(..., d=NULL, g=NULL, theta=NULL, nugget=NULL, no_update=FALSE) {
if (self$corr[[1]] != "gauss") {
stop("laGP only uses Gaussian correlation")
}
if (is.null(d) & !is.null(theta)) {d <- 1/theta}
if (is.null(g) && is.null(nugget) && !is.null(self$nugget0)) {g <- self$nugget0}
if (is.null(g) & !is.null(nugget)) {g <- nugget}
if (no_update) {
if (is.null(d)) {stop("d or theta must be given when using no_update")}
if (is.null(g)) {stop("g or nugget must be given when using no_update")}
da_start <- d
ga_start <- g
} else { # Estimating params
da <- laGP::darg(list(mle=TRUE), X=self$X)
ga.try <- try(ga <- laGP::garg(list(mle=TRUE), y=self$Z), silent = T)
if (inherits(ga.try, "try-error")) {
# warning("Adding noise to ga in laGP"); # Not too important a warning
# Sometimes first try doesn't work, so looping with bigger eps
eps_ga <- 1e-2
while (TRUE) {
ga <- try(laGP::garg(list(mle=TRUE),
y=self$Z+rnorm(length(self$Z),0,eps_ga)),
silent=T)
if (!inherits(ga, "try-error")) {break()}
eps_ga <- 2 * eps_ga
}
}
# Follow recommendations for small samples, otherwise use bigger range
drange <- if (nrow(self$X)<20) c(da$min, da$max) else c(1e-3,1e4) #c(da$min, da$max), # Don't like these small ranges
grange <- c(ga$min, ga$max)
# da_start <- if (!is.null(d)) d else da$start
# ga_start <- if (!is.null(g)) g else ga$start
# Need to make sure starting values are in ranges
if (!is.null(d)) {
da_start <- d
drange <- c(min(drange[1], d), max(drange[2], d))
} else {