(General Linear Model). For more details see chapter 3 in the PhD thesis of
Mor-Absa Loum: <http://www.theses.fr/s156435>, available here
<https://tel.archives-ouvertes.fr/tel-01877796/document>.
-Version: 1.0-0
+Version: 1.0-2
Author: Benjamin Auder <Benjamin.Auder@u-psud.fr> [aut,cre],
Mor-Absa Loum <Mor-Absa.Loum@u-psud.fr> [aut]
Maintainer: Benjamin Auder <Benjamin.Auder@u-psud.fr>
#' @param K Number of populations.
#' @param link The link type, 'logit' or 'probit'.
#' @param M the empirical cross-moments between X and Y (optional)
+#' @param nc Number of cores (default: 0 to use all)
#'
#' @return An object 'op' of class OptimParams, initialized so that
#' \code{op$run(θ0)} outputs the list of optimized parameters
#' o$f( o$linArgs(par1) )}
#'
#' @export
-optimParams <- function(X, Y, K, link=c("logit","probit"), M=NULL)
+optimParams <- function(X, Y, K, link=c("logit","probit"), M=NULL, nc=0)
{
# Check arguments
if (!is.matrix(X) || any(is.na(X)))
# Build and return optimization algorithm object
methods::new("OptimParams", "li"=link, "X"=X,
- "Y"=as.integer(Y), "K"=as.integer(K), "Mhat"=as.double(M))
+ "Y"=as.integer(Y), "K"=as.integer(K), "Mhat"=as.double(M), "nc"=as.integer(nc))
}
# Encapsulated optimization for p (proportions), β and b (regression parameters)
# @field K Number of populations
# @field n Number of sample points
# @field d Number of dimensions
+# @field nc Number of cores (OpenMP //)
# @field W Weights matrix (initialized at identity)
#
setRefClass(
K = "integer",
n = "integer",
d = "integer",
+ nc = "integer",
# Weights matrix (generalized least square)
W = "matrix"
),
callSuper(...)
if (!hasArg("X") || !hasArg("Y") || !hasArg("K")
- || !hasArg("li") || !hasArg("Mhat"))
+ || !hasArg("li") || !hasArg("Mhat") || !hasArg("nc"))
{
stop("Missing arguments")
}
M <- Moments(θ)
Omega <- matrix( .C("Compute_Omega",
X=as.double(X), Y=as.integer(Y), M=as.double(M),
- pn=as.integer(n), pd=as.integer(d),
+ pnc=as.integer(nc), pn=as.integer(n), pd=as.integer(d),
W=as.double(W), PACKAGE="morpheus")$W, nrow=dd, ncol=dd )
MASS::ginv(Omega)
},
-# Override R weird defaults:
-# https://stackoverflow.com/questions/23414448/r-makevars-file-to-overwrite-r-cmds-default-g-options
+PKG_CFLAGS = $(SHLIB_OPENMP_CFLAGS)
+PKG_LIBS = $(SHLIB_OPENMP_CFLAGS)
+
#%.o: %.c
-# $(CC) -std=c11 -I"$(R_INCLUDE_DIR)" -DNDEBUG -fPIC -O2 -c $< -o $@
+# $(CC) -std=c11 -I"$(R_INCLUDE_DIR)" -DNDEBUG -fPIC -fopenmp -O2 -c $< -o $@
#include <stdlib.h>
+#include <omp.h>
// Index matrix (by columns)
#define mi(i, j, d1, d2) (j*d1 + i)
// W = 1/N sum( t(g(Zi,theta)) g(Zi,theta) )
// with g(Zi, theta) = i-th contribution to all moments (size dim) - real moments
-void Compute_Omega(double* X, int* Y, double* M, int* pn, int* pd, double* W)
+void Compute_Omega(double* X, int* Y, double* M, int* pnc, int* pn, int* pd, double* W)
{
- int n=*pn, d=*pd;
+ int nc=*pnc, n=*pn, d=*pd;
int dim = d + d*d + d*d*d;
//double* W = (double*)malloc(dim*dim*sizeof(double));
W[j*dim+k] = 0.0;
}
double* g = (double*)malloc(dim*sizeof(double));
+ omp_set_num_threads(nc >= 1 ? nc : omp_get_num_procs());
+ #pragma omp parallel for
for (int i=0; i<n; i++)
{
// g == gi:
g[j] += Y[i] * X[mi(i,idx1,n,d)]*X[mi(i,idx2,n,d)]*X[mi(i,idx3,n,d)] - M[j];
}
// Add 1/n t(gi) %*% gi to W
- for (int j=0; j<dim; j++)
+ for (int j=dim-1; j>=0; j--)
{
- for (int k=0; k<dim; k++)
- W[j*dim+k] += g[j] * g[k] / n;
+ // This final nested loop is very costly. Some basic optimisations:
+ double gj = g[j];
+ int baseIdx = j * dim;
+ #pragma GCC unroll 100
+ for (int k=dim-1; k>=0; k--)
+ W[baseIdx+k] += gj * g[k];
}
}
+ // Normalize W: x 1/n
+ for (int j=0; j<dim; j++)
+ {
+ for (int k=0; k<dim; k++)
+ W[mi(j,k,dim,dim)] /= n;
+ }
free(g);
}
extern void hungarianAlgorithm(void*, void*, void*);
extern void Moments_M2(void*, void*, void*, void*, void*);
extern void Moments_M3(void*, void*, void*, void*, void*);
-extern void Compute_Omega(void*, void*, void*, void*, void*, void*);
+extern void Compute_Omega(void*, void*, void*, void*, void*, void*, void*);
static const R_CMethodDef CEntries[] = {
{"hungarianAlgorithm", (DL_FUNC) &hungarianAlgorithm, 3},
{"Moments_M2", (DL_FUNC) &Moments_M2, 5},
{"Moments_M3", (DL_FUNC) &Moments_M3, 5},
- {"Compute_Omega", (DL_FUNC) &Compute_Omega, 6},
+ {"Compute_Omega", (DL_FUNC) &Compute_Omega, 7},
{NULL, NULL, 0}
};
K <- ncol(fargs$beta)
M <- computeMoments(fargs$X, fargs$Y)
mu <- computeMu(fargs$X, fargs$Y, list(K=K, M=M))
- op <- optimParams(fargs$X, fargs$Y, K, fargs$link, M)
+ op <- optimParams(fargs$X, fargs$Y, K, fargs$link, M, 1) #only 1 OpenMP core
x_init <- list(p=rep(1/K,K-1), beta=mu, b=rep(0,K))
res2 <- NULL
tryCatch({
library(morpheus)
K <- ncol(fargs$beta)
mu <- computeMu(fargs$X, fargs$Y, list(K=K, M=fargs$M))
- op <- optimParams(fargs$X, fargs$Y, K, fargs$link, fargs$M)
+ op <- optimParams(fargs$X, fargs$Y, K, fargs$link, fargs$M, 1)
x_init <- list(p=rep(1/K,K-1), beta=mu, b=rep(0,K))
res2 <- NULL
tryCatch({
#!/bin/bash
-#PBS -l nodes=1:ppn=16,mem=8gb,pmem=512mb
-#PBS -j oe
-
-#PBS -o .output
+#$ -N morpheus
+#$ -wd /workdir2/auder/morpheus/reports
+#$ -m abes
+#$ -M benjamin@auder.net
+#$ -pe make 50
rm -f .output
-
-WORKDIR=/workdir2/auder/morpheus/reports
-cd $WORKDIR
+#$ -o .output
+#$ -j y
module load R
+N=1000
+nc=50
+link="logit"
+
# arg --vanilla maybe possible on cluster
-R --slave --args N=1000 nc=16 link=logit <timings.R >out 2>&1
+R --slave --args N=$N nc=$nc link=$link <timings.R >out 2>&1
{
M <- computeMoments(X, Y)
mu <- computeMu(X, Y, list(K=K,M=M))
- x_init = c(1/2, as.double(mu), c(0,0))
- optimParams(K, link, list(M=M))$run(x_init)
+ x_init = list(p=rep(1/K,K-1), beta=mu, b=rep(0,K))
+ optimParams(X, Y, K, link, M, 1)$run(x_init)
NULL
}