diff --git a/code/ODL.R b/code/ODL.R new file mode 100755 index 0000000000000000000000000000000000000000..99a3c31694bec2762522fd44c3ad9b0eb90f8460 --- /dev/null +++ b/code/ODL.R @@ -0,0 +1,502 @@ +rm(list=ls()) +require(assertthat) +require(lars) +require(ggplot2) +require(corrplot) +require(ggcorrplot) +#setwd("/Volumes/TCDATA/Projets/MNIST") +setwd("~/Documents/Cours/PRR/MNIST") +source("./skeleton.R") + +# ---------------------------------------------------------- +# Plot permettant de visualiser les features de X (mnist$test$x) +plot_mnist_random() + +# ---------------------------------------------------------- +# norm_dico +# +# Require : +# D := un dictionnaire +# +# Ensure : Retourne D normalisé pour que D appartienne à C +# ---------------------------------------------------------- +norm_dico <- function(D) { + dim_D <- dim(D) ; p = dim_D[1] ; k = dim_D[2] + for(j in 1:k) { + dj <- D[,j] + D[,j] <- dj / max(1,sqrt(sum(dj^2))) + } + D +} + +# ---------------------------------------------------------- +# make_dico_1 +# +# Require : +# p := nombre de features du dictionnaire +# k := nombre d'atoms du dictionnaire +# +# Ensure : Retourne un dictionnaire de k atoms exprimés +# par p parameters comme les features de X dont nous +# souhaitons construire un dictionnaire le représentant +# +# Ensure+ : Les atoms sont aléatoires +# ---------------------------------------------------------- +make_dico_1 <- function(p,k) { + norm_dico(matrix(data = rnorm(p*k), nrow = p, ncol = k)) +} + +plot_mnist_choose(t(make_dico_1(784,100)),10) + +# ---------------------------------------------------------- +# make_dico_2 +# +# Require : +# k := nombre d'atoms du dictionnaire +# +# Ensure : Retourne un dictionnaire de k atoms exprimés +# par p parameters comme les features de X dont nous +# souhaitons construire un dictionnaire le représentant +# +# Ensure+ : Les k atoms sont pris dans X = mnist$train$x +# et p = 784 +# ---------------------------------------------------------- +make_dico_2 <- function(k) { + dim_X <- dim(mnist$train$x) ; n = dim_X[1] ; p = dim_X[2] + norm_dico(t(mnist$train$x[sample(1:n,k),])) +} + +plot_mnist_choose(t(make_dico_2(100)),10) + +# ---------------------------------------------------------- +# make_dico_3 +# +# Require : +# m := nombre de features du dictionnaire +# k := nombre d'atoms du dictionnaire +# +# Ensure : Retourne un dictionnaire de k atoms exprimés +# par m parameters comme les features de X dont nous +# souhaitons construire un dictionnaire le représentant +# +# Ensure+ : Les k atoms ont un schéma carré de 9px d'aire +# situé aléatoirement +# ---------------------------------------------------------- +make_dico_3 <- function(m,k) { + D <- matrix(data = 0, nrow = m, ncol = k) + p <- sqrt(m) + for(t in 1:k) { + i = floor(runif(1,2,p))-1 + j = floor(runif(1,2,p)) + D[1+i-1+p*(j-1),t] <- 1 ; D[1+i+p*(j-1),t] <- 1 ; D[1+i+1+p*(j-1),t] <- 1 + D[1+i-1+p*(j),t] <- 1 ; D[1+i+p*(j),t] <- 1 ; D[1+i+1+p*(j),t] <- 1 + D[1+i-1+p*(j-2),t] <- 1 ; D[1+i+p*(j-2),t] <- 1 ; D[1+i+1+p*(j-2),t] <- 1 + } + norm_dico(D) +} + +plot_mnist_choose(t(make_dico_3(784,100)),10) + +# ---------------------------------------------------------- +# make_dico_4 +# +# Require : +# k := 10k est le nombre d'atoms du dictionnaire +# +# Ensure : Retourne un dictionnaire de k atoms exprimés +# par p parameters comme les features de X dont nous +# souhaitons construire un dictionnaire le représentant +# +# Ensure+ : Les 10k atoms sont pris dans X = mnist$train$x +# et p = 784 +# Il y a k atoms de chaque chiffre de 0 à 9 +# ---------------------------------------------------------- +make_dico_4 <- function(k) { + D <- c() + for(i in 0:9) { + ind <- sample(which(mnist$test$y==i),k) + D <- cbind(D,t(mnist$test$x[ind,])) + } + norm_dico(D) +} + +plot_mnist_choose(t(make_dico_4(10)),10) + +make_test <- function() { # like make_dico_4(1) + D <- c() + for(i in 0:9) { + ind <- sample(which(mnist$test$y==i),1) + D <- cbind(D,matrix(data=mnist$test$x[ind,], nrow = 784, ncol = 1)) + } + norm_dico(D) +} + +plot_mnist_choose(t(make_test()),5) + +# ---------------------------------------------------------- +# cost_function +# +# Require : +# +# Ensure : +# ---------------------------------------------------------- +cost_function <- function(X,D,l) { + + fn <- 0 + dim_X <- dim(X) ; n <- dim_X[1] ; N <- n + norm_vec <- function(x) sqrt(sum(x^2)) + cat("0 0.1 0.2 0.3 0.4 ") + cat("0.5 0.6 0.7 0.8 0.9 1\n") + cat ("|") + + for(t in 1:n) { + ################################################################################ + # Extraction d'une feature xt de X + xt <- X[t,] + + ################################################################################ + # Calcul de la décomposition rt de xt + try(model <- lars(D, xt, type = "lasso"), silent = T) + if(is.na(model)) { + N <- N-1 + next + } + ind <- which.min(model$lambda>=l) # max(which(model$lambda>=l)) + if(is.na(ind)) ind <- model$lambda[1] + rt <- coef(model)[ind,] + + ################################################################################ + # Calcul de la représentation yt de xt dans D + yt <- D%*%rt + + ################################################################################ + # Contribution de xt dans fn + fn <- fn + 0.5*norm_vec(xt-yt)^2 + l*norm_vec(rt) + + # Visualisation de l'avancement de l'apprentissage + if(n>=100 & t %% floor(n/100) == 0) cat (".") # cat("Etape t =",t,"\n") + if(n>=10 & t %% floor(n/10) == 0) cat ("|") + } + cat("\n") + + fn/N +} + +# ---------------------------------------------------------- +# ---------------------------------------------------------- +# ODL := Online Dictionary Learning Algorithm +# +# Require : +# X := train dataset (matrix : size = n*p) +# lambda := regularization parameter (double : > 0) +# D := initial dictionary (matrix : size = p*k) +# Tt := update iteration number (int : > 0) +# Tb := batch size (int : > 0) +# Tc := converge iteration number (int : > 0) +# ---------------------------------------------------------- +# ---------------------------------------------------------- + +ODL <- function(X, lambda, D, Tt, Tb, Tc, alea=T) { + + ################################################################################ + # Initialisation des paramètres n, p, k + + # n := nombre de features de X + # p := nombre de parameters de X + dim_X <- dim(X) ; n <- dim_X[1] ; p <- dim_X[2] + # k := nombre d'atoms de D + # p := nombre de parameters des atoms de D + dim_D <- dim(D) ; p2 <- dim_D[1] ; k <- dim(D)[2] + + assert_that(p==p2, msg = "Dimension of X and D not compatible") + + mu <- 1 + + ################################################################################ + # Initialisation des matrices d'informations pour le block-coordinate descent + # At := sum(rt*rt^T) + At <- matrix(data = 0, nrow = k, ncol = k) + # Bt := sum(xt*rt^T) + Bt <- matrix(data = 0, nrow = p, ncol = k) + + ################################################################################ + # Initialisation de nos variables + # Dt := dictionary après t updates + Dt <- D + # xt := t-th feature de X pour la t-th update + xt <- c() + # rt := sparse coding de la t-th feature (xt) + rt <- c() + # cc := compteur + cc <- 0 + # ti := indice de la t-th feature de X + ti <- 0 + # ind_ti := ensemble des indices ti + ind_ti <- c() + + cat("0 0.1 0.2 0.3 0.4 ") + cat("0.5 0.6 0.7 0.8 0.9 1\n") + + ################################################################################ + # Boucle pour le Online Dictionary Learning - t-ème mise à jour + cat ("|") + for(t in 1:Tt) { + ################################################################################ + # Extraction d'une feature xt de X + if(alea) ti <- floor(runif(1,1,n+1)) + else ti <- t + ind_ti <- c(ind_ti,ti) + xt <- X[ti,] + + ################################################################################ + # Sparse Coding Problem : Résolution par la méthode du LARS-Lasso + try(model <- lars(Dt, xt, type = "lasso"), silent = T) + if(is.na(model)) { + cc <- cc - 1 + t <- t - 1 + next + } + ind <- which.min(model$lambda>=lambda) # max(which(model$lambda>=lambda)) + #if(is.na(ind)) {ind <- model$lambda[length(SC$lambda)]} + assert_that(!is.na(ind), msg = "Trouble with value of lambda") + rt <- coef(model)[ind,] + + ################################################################################ + # Mise à jour des matrices d'informations + At <- mu*At + matrix(data = rt, ncol = 1) %*% t(matrix(data = rt, ncol = 1)) + Bt <- mu*Bt + matrix(data = xt, ncol = 1) %*% t(matrix(data = rt, ncol = 1)) + # Mise à jour du compteur + cc <- cc + 1 + + ################################################################################ + # Dictionary Update Problem : Résolution par block-coordinate descent + # with warm restarts + # Condition pour la mise à jour selon le choix de la taille du batch + if(cc == Tb) { + # Boucle pour le choix du nombre d'itération pour la convergence + for(i in 1:Tc) { + # Mise à jour de la j-ème colonne de Dt "with warm restarts" + for(j in 1:k) { + # Si At[j,j] == 0, alors Bt[,j] = 0 et At[,j] = 0 par construction + # Donc la mise à jour est inutile + if(At[j,j]!=0) { + # Mise à jour par Gradient Descent + uj <- (Bt[,j]-Dt %*% At[,j])/At[j,j] + Dt[,j] + # Normalisation pour que Dt appartienne à C (respect de la contrainte sur Dt) + Dt[,j] <- uj / max(1,sqrt(sum(uj^2))) + } + } + } + + # Contrôle sur les coefficients de Dt pour éviter les problèmes rencontrés avec le LARS + for(i in 1:p) { + for(j in 1:k) { + Dt[i,j] <- ifelse(Dt[i,j]<10e5*.Machine$double.eps,0,Dt[i,j]) + } + } + # Réinitialisation du compteur + cc <- 0 + } + + # Visualisation de l'avancement de l'apprentissage + if(Tt>=100 & t %% floor(Tt/100) == 0) cat (".") # cat("Etape t =",t,"\n") + if(Tt>=10 & t %% floor(Tt/10) == 0) cat ("|") + } + cat("\n") + + ################################################################################ + # Return : Le dictionnaire Dt permettant de bien représenter les features de X + res <- list() + res$D <- Dt + res$D_visual <- plot_mnist_choose(t(Dt),floor(sqrt(k)), + title="Atoms Dictionary", + subtitle="Visualization of atoms of final dictionary") + res$fn <- cost_function(X[ind_ti,], Dt, lambda) + res +} + + +# ---------------------------------------------------------- +# ---------------------------------------------------------- +# visual_structure := Visual Classification +# +# Require : +# X := test dataset (matrix : size = n*p) +# l := regularization parameter (double : > 0) +# D := final dictionary (matrix : size = p*k) +# Y := label test dataset (array : size = n) +# Tt := update iteration number (int : > 0) +# nb_rp := nombre de représentants pour une classe (int : > 0) +# ---------------------------------------------------------- +# ---------------------------------------------------------- + +visual_structure <- function(X,Y,D,l,Tt,nb_rp) { + + ################################################################################ + # Initialisation des paramètres n, p, k + + # n := nombre de features de X + # p := nombre de parameters de X + dim_X <- dim(X) ; n <- dim_X[1] ; p <- dim_X[2] + # k := nombre d'atoms de D + # p := nombre de parameters des atoms de D + dim_D <- dim(D) ; p2 <- dim_D[1] ; k <- dim(D)[2] + + assert_that(p==p2, msg = "Dimension of X and D not compatible") + + ############################################################################### + # Initialisation de la matrice de visualisation (ncol = 10 car 10 chiffres) + visual <- matrix(data = 0, nrow = k, ncol = 10) + col_count <- array(data = 0, dim = c(10)) + + cat("0 0.1 0.2 0.3 0.4 ") + cat("0.5 0.6 0.7 0.8 0.9 1\n") + + cat ("|") + for(t in 1:Tt) { + ################################################################################ + # Extraction d'une feature xt de X + xt <- X[t,] + yt <- Y[t] + + ################################################################################ + # Calcul de la décomposition rt de xt + try(model <- lars(D, xt, type = "lasso"), silent = T) + if(is.na(model)) { + next + } + ind <- which.min(model$lambda>=l) + if(is.na(ind)) ind <- model$lambda[1] + rt <- coef(model)[ind,] + + ################################################################################ + # Intégration de la décomposition rt de xt dans la classe k + rt <- ifelse(rt==0,0,1) + for(i in 1:k) { + visual[i,yt+1] <- visual[i,yt+1] + rt[i] + } + col_count[yt+1] <- col_count[yt+1] + 1 + + # Visualisation de l'avancement de l'apprentissage + if(Tt>=100 & t %% floor(Tt/100) == 0) cat (".") # cat("Etape t =",t,"\n") + if(Tt>=10 & t %% floor(Tt/10) == 0) cat ("|") + } + cat("\n") + + for(j in 1:10) { + visual[,j] <- visual[,j]/col_count[j] + } + + repr <- c() + for(j in 1:10) { + repr <- cbind(repr,Dt[,which(visual[,j]>=sort(visual[,j],decreasing = T)[nb_rp])[1:nb_rp]]) + } + + res <- list() + res$matrix <- visual + res$S_visual <- plot_mnist_choose(t(visual),5, + title="Frequency of Atoms Dictionary", + subtitle="Visualization of frequency of atoms dictionary used") + res$R <- repr + res$R_visual <- plot_mnist_choose(t(repr),nb_rp, + title="k-th decomposition for number", + subtitle="Visualization of k-th decomposition for number 0-9") + res +} + +# ---------------------------------------------------------- +# Initialisation de X (train) +{ + X <- mnist$train$x + dim_X <- dim(X) + nx <- dim_X[1] ; p <- dim_X[2] + Xy <- mnist$train$y +} + +# ---------------------------------------------------------- +# Initialisation de X (test) +{ + Y <- mnist$test$x + dim_Y <- dim(Y) + ny <- dim_Y[1] ; p <- dim_Y[2] + Yy <- mnist$test$y +} + +# ---------------------------------------------------------- +# Initialisation de D +{ + k <- 100 + # k <- 200 + + # D <- make_dico_1(p,k) + # D <- make_dico_2(k) + # D <- make_dico_3(p,k) + D <- make_dico_4(10) ; k <- 10*10 +} + +# ---------------------------------------------------------- +# Paramétrisation de l'algorithme +{ + lambda <- 2 + Tt <- 1000 + Tb <- 10 + Tc <- 10 +} + +# ---------------------------------------------------------- +# Run +{ + RES <- ODL(X, lambda, D, Tt, Tb, Tc) + Dt <- RES$D + cat(RES$fn,"\n") + p1 <- RES$D_visual + + V <- visual_structure(X,Xy,Dt,lambda,1000,5) + p2 <- V$S_visual + p3 <- V$R_visual + + multiplot(p1,p2,p3,cols = 2, layout=matrix(c(1,3,2,3),nrow=2,byrow=T)) +} + + { + colnames(V$matrix) <- 0:9 + cor_figures <- cor(V$matrix) + corrplot.mixed(cor_figures, upper = "square", order = "FPC") +} + +{ + fn_train <- c() + fn_test <- c() + llambda <- c(0.01,0.05,0.1,0.25,0.5,1,2) + for(l in llambda) { + RES <- ODL(X, l, D, 200, Tb, Tc, F) + Dt <- RES$D + cat(RES$fn,"\n") + fn <- cost_function(Y[1:Tt,],D,l) + cat(fn,"\n") + fn_train <- c(fn_train,RES$fn) + fn_test <- c(fn_test,fn) + } + df <- data.frame(llambda,fn_train,fn_test) + ggplot(df, aes(llambda)) + # basic graphical object + geom_line(aes(y=fn_train), colour="red") + # first layer + geom_line(aes(y=fn_test ), colour="green") # second layer +} + +# ---------------------------------------------------------- +# Initialisation de l'ensenmble de test unitaire +mnist_test_unitaire <- make_test() + +# ---------------------------------------------------------- +# Test sur un élément aléatoire de Y +{ + x <- Y[floor(runif(1,1,ny)),] + SC <- lars(Dt, x, type = "lasso") + ind <- which.min(SC$lambda>=lambda) # max(which(SC$lambda>=lambda)) + if(is.na(ind)) ind <- SC$lambda[1] + alpha <- coef(SC)[ind,] + y <- Dt%*%alpha + pt <- plot_mnist_choose(matrix(data=c(x,y),nrow = 2, ncol = p, byrow = T),2, subtitle="") + pt +} +k-sum(alpha==0) ; (k-sum(alpha==0))/k diff --git a/code/skeleton.R b/code/skeleton.R new file mode 100755 index 0000000000000000000000000000000000000000..9142ddc5a2f78fb697c80f4c7882b8d7b94a875b --- /dev/null +++ b/code/skeleton.R @@ -0,0 +1,232 @@ +library(ggplot2) +library(reshape2) +#setwd("/Volumes/TCDATA/Projets/MNIST") +setwd("~/Documents/Cours/PRR/MNIST") + +# initialize data directory +data_dir <- "mnist-data" +dir.create(data_dir, recursive = TRUE, showWarnings = FALSE) + +# download the MNIST data sets, and read them into R +sources <- list( + train = list( + x = "https://storage.googleapis.com/cvdf-datasets/mnist/train-images-idx3-ubyte.gz", + y = "https://storage.googleapis.com/cvdf-datasets/mnist/train-labels-idx1-ubyte.gz" + ), + + test = list( + x = "https://storage.googleapis.com/cvdf-datasets/mnist/t10k-images-idx3-ubyte.gz", + y = "https://storage.googleapis.com/cvdf-datasets/mnist/t10k-labels-idx1-ubyte.gz" + ) +) + +# read an MNIST file (encoded in IDX format) +read_idx <- function(file) { + + # create binary connection to file + conn <- gzfile(file, open = "rb") + on.exit(close(conn), add = TRUE) + + # read the magic number as sequence of 4 bytes + magic <- readBin(conn, what = "raw", n = 4, endian = "big") + ndims <- as.integer(magic[[4]]) + + # read the dimensions (32-bit integers) + dims <- readBin(conn, what = "integer", n = ndims, endian = "big") + + # read the rest in as a raw vector + data <- readBin(conn, what = "raw", n = prod(dims), endian = "big") + + # convert to an integer vecto + converted <- as.integer(data) + + # return plain vector for 1-dim array + if (length(dims) == 1) + return(converted) + + # wrap 3D data into matrix + matrix(converted, nrow = dims[1], ncol = prod(dims[-1]), byrow = TRUE) +} + +mnist <- rapply(sources, classes = "character", how = "list", function(url) { + + # download + extract the file at the URL + target <- file.path(data_dir, basename(url)) + if (!file.exists(target)) + download.file(url, target) + + # read the IDX file + read_idx(target) + +}) + +# convert training data intensities to 0-1 range +mnist$train$x <- mnist$train$x / 255 +mnist$test$x <- mnist$test$x / 255 + +# try plotting the pixel intensities for a random sample of 32 images +plot_mnist_random <- function(X=mnist$test$x, n=36, + title="MNIST Image Data", + subtitle="Visualization of a sample of images contained in MNIST data set." + ) { + m <- dim(X)[2] + indices <- sample(nrow(X), size = n) + data <- array(X[indices, ], dim = c(n, sqrt(m), sqrt(m))) + melted <- melt(data, varnames = c("image", "x", "y"), value.name = "intensity") + ggplot(melted, aes(x = x, y = y, fill = intensity)) + + geom_tile() + + scale_fill_continuous(name = "Pixel Intensity") + + scale_y_reverse() + + facet_wrap(~ image, nrow = sqrt(n), ncol = sqrt(n)) + + theme( + strip.background = element_blank(), + strip.text.x = element_blank(), + panel.spacing = unit(0, "lines"), + axis.text = element_blank(), + axis.ticks = element_blank() + ) + + labs( + title = title, + subtitle = subtitle, + x = NULL, + y = NULL + ) +} + +plot_mnist_choose <- function(centers, ncol=5, + title="MNIST Image Data", + subtitle="Visualization of a sample of images contained in MNIST data set." + ) { + n <- dim(centers)[1] + m <- dim(centers)[2] + data <- array(centers, dim = c(n, sqrt(m), sqrt(m))) + melted <- melt(data, varnames = c("image", "x", "y"), value.name = "intensity") + cat("Affichage en cours ...\n") + ggplot(melted, aes(x = x, y = y, fill = intensity)) + + geom_tile() + + scale_fill_continuous(name = "Pixel Intensity") + + scale_y_reverse() + + facet_wrap(~ image, nrow = n/ncol, ncol = ncol) + + theme( + strip.background = element_blank(), + strip.text.x = element_blank(), + panel.spacing = unit(0, "lines"), + axis.text = element_blank(), + axis.ticks = element_blank() + ) + + labs( + title = title, + subtitle = subtitle, + x = NULL, + y = NULL + ) +} + +# plot predictions versus actual for small subset +plot_mnist_pred <- function(data=mnist$test$x, predictions=mnist$test$y, n=30, format=TRUE, + title = "MNIST Image Data", + subtitle = "Visualization of a sample of images contained in MNIST data set." + ) { + indices <- sample(nrow(data), n) + if(!format) { + classes <- vapply(indices, function(i) { + predictions$classes[[i]] + }, character(1)) + } + else { + classes <- as.character(predictions)[indices] + } + + data <- array(data[indices, ], dim = c(n, 28, 28)) + melted <- melt(data, varnames = c("image", "x", "y"), value.name = "intensity") + melted$class <- classes + + image_labels <- setNames( + sprintf("Predicted: %s\nActual: %s", classes, mnist$test$y[indices]), + 1:n + ) + + ggplot(melted, aes(x = x, y = y, fill = intensity)) + + geom_tile() + + scale_y_reverse() + + facet_wrap(~ image, ncol = 5, labeller = labeller(image = image_labels)) + + theme( + panel.spacing = unit(0, "lines"), + axis.text = element_blank(), + axis.ticks = element_blank() + ) + + labs( + title = title, + subtitle = subtitle, + x = NULL, + y = NULL + ) +} + +mnist_reduction <- function(x) { + n <- sqrt(784) + xf <- array(x, dim = c(n, n)) + mnist_red <- matrix(data = 0, nrow = n/2, ncol = n/2) + for(i in 1:(n/2)) { + for(j in 1:(n/2)) { + mnist_red[i,j] <- (xf[1+2*(i-1),1+2*(j-1)] + xf[1+2*(i-1),1+2*(j-1)+1] + + xf[1+2*(i-1)+1,1+2*(j-1)] + xf[1+2*(i-1)+1,1+2*(j-1)+1])/4 + } + } + array(mnist_red, dim = c((n/2)^2)) +} + +mnist_R <- list() +mnist_R$train$x <- array(data = 0, dim = c(dim(mnist$train$x)[1],14^2)) +mnist_R$test$x <- array(data = 0, dim = c(dim(mnist$test$x)[1],14^2)) +mnist_R$train$y <- mnist$train$y +mnist_R$test$y <- mnist$test$y + +for(i in 1:dim(mnist$train$x)[1]) { + mnist_R$train$x[i,] <- mnist_reduction(mnist$train$x[i,]) +} +for(i in 1:dim(mnist$test$x)[1]) { + mnist_R$test$x[i,] <- mnist_reduction(mnist$test$x[i,]) +} + +# Multiple plot function +# +# ggplot objects can be passed in ..., or to plotlist (as a list of ggplot objects) +# - cols: Number of columns in layout +# - layout: A matrix specifying the layout. If present, 'cols' is ignored. +# +# If the layout is something like matrix(c(1,2,3,3), nrow=2, byrow=TRUE), +# then plot 1 will go in the upper left, 2 will go in the upper right, and +# 3 will go all the way across the bottom. +# +multiplot <- function(..., plotlist=NULL, file, cols=1, layout=NULL) { + library(grid) + + # Make a list from the ... arguments and plotlist + plots <- c(list(...), plotlist) + numPlots = length(plots) + + # If layout is NULL, then use 'cols' to determine layout + if (is.null(layout)) { + # Make the panel + # ncol: Number of columns of plots + # nrow: Number of rows needed, calculated from # of cols + layout <- matrix(seq(1, cols * ceiling(numPlots/cols)), + ncol = cols, nrow = ceiling(numPlots/cols)) + } + if (numPlots==1) { + print(plots[[1]]) + } else { + # Set up the page + grid.newpage() + pushViewport(viewport(layout = grid.layout(nrow(layout), ncol(layout)))) + + # Make each plot, in the correct location + for (i in 1:numPlots) { + # Get the i,j matrix positions of the regions that contain this subplot + matchidx <- as.data.frame(which(layout == i, arr.ind = TRUE)) + print(plots[[i]], vp = viewport(layout.pos.row = matchidx$row, + layout.pos.col = matchidx$col)) + } + } +} diff --git a/papers/DL.pdf b/papers/DL.pdf new file mode 100755 index 0000000000000000000000000000000000000000..d696c67ed857f6e3a48c79619f60ae01fbe69345 Binary files /dev/null and b/papers/DL.pdf differ diff --git a/papers/ODLSC.pdf b/papers/ODLSC.pdf new file mode 100755 index 0000000000000000000000000000000000000000..a05623cc5fcf035aacde45a83583c4360e66c29c Binary files /dev/null and b/papers/ODLSC.pdf differ diff --git a/papers/OLMFSC.pdf b/papers/OLMFSC.pdf new file mode 100755 index 0000000000000000000000000000000000000000..2b19b3721486d946dda153c14bee67bcfa4ad3e9 Binary files /dev/null and b/papers/OLMFSC.pdf differ diff --git a/papers/SPCA.pdf b/papers/SPCA.pdf new file mode 100755 index 0000000000000000000000000000000000000000..af29b232f39e7214d356efb435f5714d389f401a Binary files /dev/null and b/papers/SPCA.pdf differ diff --git a/report.pdf b/report.pdf new file mode 100755 index 0000000000000000000000000000000000000000..a8d715495983f9cd682b839dc5409f94eab28999 Binary files /dev/null and b/report.pdf differ