Add weights handling (experimental)
[morpheus.git] / pkg / R / optimParams.R
index 9185efb..4f886ac 100644 (file)
@@ -10,6 +10,7 @@
 #'     \item 'M' : list of moments of order 1,2,3: will be computed if not provided.
 #'     \item 'X,Y' : input/output, mandatory if moments not given
 #'     \item 'exact': use exact formulas when available?
+#'     \item weights Weights on moments when minimizing sum of squares
 #'   }
 #'
 #' @return An object 'op' of class OptimParams, initialized so that \code{op$run(x0)}
@@ -56,14 +57,14 @@ optimParams = function(K, link=c("logit","probit"), optargs=list())
                M <- computeMoments(optargs$X,optargs$Y)
        }
 
-       # TODO: field?!
-       exactComp <<- optargs$exact
-       if (is.null(exactComp))
-               exactComp <<- FALSE
+  weights <- optargs$weights
+  if (is.null(weights))
+    weights <- rep(1, K)
 
        # Build and return optimization algorithm object
        methods::new("OptimParams", "li"=link, "M1"=as.double(M[[1]]),
-               "M2"=as.double(M[[2]]), "M3"=as.double(M[[3]]), "K"=as.integer(K))
+               "M2"=as.double(M[[2]]), "M3"=as.double(M[[3]]),
+    "weights"=weights, "K"=as.integer(K))
 }
 
 # Encapsulated optimization for p (proportions), β and b (regression parameters)
@@ -72,6 +73,7 @@ optimParams = function(K, link=c("logit","probit"), optargs=list())
 # @field M1 Estimated first-order moment
 # @field M2 Estimated second-order moment (flattened)
 # @field M3 Estimated third-order moment (flattened)
+# @field weights Vector of moments' weights
 # @field K Number of populations
 # @field d Number of dimensions
 #
@@ -137,9 +139,9 @@ setRefClass(
                        β3 <- apply(β, 2, function(col) col %o% col %o% col)
 
                        return(
-                               sum( ( β  %*% (p * .G(li,1,λ,b)) - M1 )^2 ) +
-                               sum( ( β2 %*% (p * .G(li,2,λ,b)) - M2 )^2 ) +
-                               sum( ( β3 %*% (p * .G(li,3,λ,b)) - M3 )^2 ) )
+                               weights[1] * sum( ( β  %*% (p * .G(li,1,λ,b)) - M1 )^2 ) +
+                               weights[2] * sum( ( β2 %*% (p * .G(li,2,λ,b)) - M2 )^2 ) +
+                               weights[3] * sum( ( β3 %*% (p * .G(li,3,λ,b)) - M3 )^2 ) )
                },
 
                grad_f = function(x)
@@ -171,9 +173,9 @@ setRefClass(
 
                        km1 = 1:(K-1)
                        grad <- #gradient on p
-                               t( sweep(as.matrix(β [,km1]), 2, G1[km1], '*') - G1[K] * β [,K] ) %*% F1 +
-                               t( sweep(as.matrix(β2[,km1]), 2, G2[km1], '*') - G2[K] * β2[,K] ) %*% F2 +
-                               t( sweep(as.matrix(β3[,km1]), 2, G3[km1], '*') - G3[K] * β3[,K] ) %*% F3
+                               weights[1] * t( sweep(as.matrix(β [,km1]), 2, G1[km1], '*') - G1[K] * β [,K] ) %*% F1 +
+                               weights[2] * t( sweep(as.matrix(β2[,km1]), 2, G2[km1], '*') - G2[K] * β2[,K] ) %*% F2 +
+                               weights[3] * t( sweep(as.matrix(β3[,km1]), 2, G3[km1], '*') - G3[K] * β3[,K] ) %*% F3
 
                        grad_β <- matrix(nrow=d, ncol=K)
                        for (i in 1:d)
@@ -202,14 +204,17 @@ setRefClass(
                                dβ3_right[block,] <- dβ3_right[block,] + β2
                                dβ3 <- dβ3_left + sweep(dβ3_right, 2, p * G3, '*')
 
-                               grad_β[i,] <- t(dβ) %*% F1 + t(dβ2) %*% F2 + t(dβ3) %*% F3
+                               grad_β[i,] <-
+          weights[1] * t(dβ) %*% F1 +
+          weights[2] * t(dβ2) %*% F2 +
+          weights[3] * t(dβ3) %*% F3
                        }
                        grad <- c(grad, as.double(grad_β))
 
                        grad = c(grad, #gradient on b
-                               t( sweep(β,  2, p * G2, '*') ) %*% F1 +
-                               t( sweep(β2, 2, p * G3, '*') ) %*% F2 +
-                               t( sweep(β3, 2, p * G4, '*') ) %*% F3 )
+                               weights[1] * t( sweep(β,  2, p * G2, '*') ) %*% F1 +
+                               weights[2] * t( sweep(β2, 2, p * G3, '*') ) %*% F2 +
+                               weights[3] * t( sweep(β3, 2, p * G4, '*') ) %*% F3 )
 
                        grad
                },
@@ -218,13 +223,23 @@ setRefClass(
                {
                        "Run optimization from x0 with solver..."
 
-                       if (!is.numeric(x0) || any(is.na(x0)) || length(x0) != (d+2)*K-1
-                               || any(x0[1:(K-1)] < 0) || sum(x0[1:(K-1)]) > 1)
-                       {
-                               stop("x0: numeric vector, no NA, length (d+2)*K-1, sum(x0[1:(K-1) >= 0]) <= 1")
-                       }
-
-                       op_res = constrOptim( x0, .self$f, .self$grad_f,
+           if (!is.list(x0))
+                   stop("x0: list")
+      if (is.null(x0$β))
+        stop("At least x0$β must be provided")
+                       if (!is.matrix(x0$β) || any(is.na(x0$β)) || ncol(x0$β) != K)
+                               stop("x0$β: matrix, no NA, ncol == K")
+      if (is.null(x0$p))
+        x0$p = rep(1/K, K-1)
+      else if (length(x0$p) != K-1 || sum(x0$p) > 1)
+        stop("x0$p should contain positive integers and sum to < 1")
+      # Next test = heuristic to detect missing b (when matrix is called "beta")
+      if (is.null(x0$b) || all(x0$b == x0$β))
+        x0$b = rep(0, K)
+      else if (any(is.na(x0$b)))
+        stop("x0$b cannot have missing values")
+
+                       op_res = constrOptim( linArgs(x0), .self$f, .self$grad_f,
                                ui=cbind(
                                        rbind( rep(-1,K-1), diag(K-1) ),
                                        matrix(0, nrow=K, ncol=(d+1)*K) ),
@@ -249,6 +264,8 @@ setRefClass(
        # link="probit"; order=2; λ=c(531.8099,586.8893,523.5816); b=c(-118.512674,-3.488020,2.109969)
        # Switch to pracma package for that (but it seems slow...)
 
+       exactComp <- FALSE #TODO: global, or argument...
+
        if (exactComp && link == "probit")
        {
                # Use exact computations