[R] Architecting an optimization with external calls

Ross Boylan ross at biostat.ucsf.edu
Wed Nov 5 03:09:57 CET 2003


On Tue, 2003-11-04 at 13:12, Prof Brian Ripley wrote:
> Look into external pointers.  That is how we have tackled this, e.g. in 
> the ts package.
I got the following to work.  Any comments?  I indicated some areas of
uncertainty in the comments.  I'm also unsure about the differences, if
any, between the DOUBLE_DATA(x)[0] = val style and SET_ELEMENT(x, i,
val) style.

The R code was the best way I could think of to retain the state while
using a function that worked with optim.  The C code remembers the
argument from the previous call and prints it out when invoked.

The driving R code was
loglika <- function(initialparms) {
  opaque <- NULL # holds the data returned by the C function
  innerloglik <- function(parms){
    # If .C can handle externalptr, it would be easier
    result <- .Call("loglik", as.double(parms), opaque)
    opaque <<- result$opaque
    return(result$loglik)
  }
  result <- optim(initialparms, innerloglik)
  .Call("cleanup", opaque)
  opaque <- NULL
}

And the C:
#include <R.h>
#include <Rdefines.h>

SEXP loglik(SEXP params, SEXP data){
  double *pd;
  SEXP returnLik, returnVal, returnNames;

  if (TYPEOF(data) == EXTPTRSXP) {
    pd = (double *) EXTPTR_PTR(data);
    Rprintf("I've been here with %f\n", *pd);
  } else {
    pd = malloc(sizeof(double));
    data = R_MakeExternalPtr(pd, R_NilValue, R_NilValue);
    /* do I need PROTECT(data)?  if so, argument to
     UNPROTECT is variable */
  }
  
  *pd = DOUBLE_DATA(params)[0];

  /* Real computation would go here */

  PROTECT(returnLik = NEW_NUMERIC(1));
  NUMERIC_POINTER(returnLik)[0] = (*pd)*(*pd); /* our log likelihood */
     /* When I just set it = 5.0 I got a segfault.  Maybe because I
	rebuilt the library at the same time.... */

  /* update opaque data with first input parameter */
  Rprintf("Just recorded input parameter %f\n", *pd);

  /* construct return list object */
  PROTECT(returnVal = NEW_LIST(2));
  PROTECT(returnNames = NEW_CHARACTER(2));
  SET_STRING_ELT(returnNames, 0, mkChar("loglik"));
  SET_STRING_ELT(returnNames, 1, mkChar("opaque"));
  SET_NAMES(returnVal, returnNames);
  SET_ELEMENT(returnVal, 0, returnLik);
  SET_ELEMENT(returnVal, 1, data);
  UNPROTECT(3);
  return returnVal;
}

SEXP cleanup(SEXP data){
  if (TYPEOF(data) == EXTPTRSXP) {
    free(EXTPTR_PTR(data));
  } else {
    error("cleanup called without an External Pointer argument");
  }
  return R_NilValue;
}

I was going on a lot of hunches, even with the docs and code together.




More information about the R-help mailing list