get-started

Find non-linear formulas that fits your input data. You can systematically explore and memoize the possible formulas and it’s cross-validation performance, in a parallel and incremental fashon. Three interoperable search functions are available:

  • random.search performs a random exploration,
  • genetic.search employs a genetic optimization algorithm
  • exaustive.search explore all the space not already explored (WARNING may take a long time and doesn’t have intermediate save points.)

The library is designed with a massive parallelization use in mind, that’s why you have a base.filepath for shared results among all parallel processors, and a res.filepath with local results only.

Periodically, the multiple res.filepath will be joined together in a shared base.filepath.

Let’s broke this down step by step.

Load the library and set-up the data

library(symbolicr)

x1<-runif(100, min=2, max=67)
x2<-runif(100, min=0.01, max=0.1)

y <- log10(x1^2*x2) + rnorm(100, 0, 0.001)

X <- data.frame(x1=x1, x2=x2)

Define hyper-parameters

l1.filepath <- paste0('regression/regression',type,'.exploration.l1.rData')
# create file
saveRDS(empty.sample(), l1.filepath)

K=15
N=30
max.formula.len=2

Note that you can control the formula space, and the algorithm employed by just setting three variables

  • seed
  • formula.len
  • n.squares

Thus, you can run multiple independent search processes both on your computer or server-side with a command similar to this one

R --no-echo --no-restore --file=find_f.R --args 3 2 1006

That will run genetic search to find formulas of max len 3, and order up to 2.

You just need to get the variables from command-line

options <- commandArgs(trailingOnly = TRUE)

formula.len <- as.integer(options[1])
n.squares <- as.integer(options[2])
seed <- as.integer(options[3])

otherwise, you can hard-code them

best.n.squares <- n.squares <- 1
best.formula.len <- formula.len <- 2
seed=1010

Define rData file for checkpoint save

Used only in genetic and random search. Exaustive search will append the missing formulas to the file pointed by base.filepath.

if(seed > 0){
  res.filepath <- file.path(out.folder, paste0('regression',type,'.exploration.fl.',formula.len,'.ord.',n.squares,'seed.',seed,'.rData'))
}

Define non-linear transformations

  • rdf is the full dataset
  • x is the variable to which we should apply the non-linearity
  • z is a list with min and absmin fields
transformations <- list(
  "log"=function(rdf, x, z){
    log10(x)
  },
  "log10"=function(rdf, x, z){
    log10(0.1+abs(z$min)+x)
  },
  "log_fwhm_fcrn_p"=function(rdf, x, z){
    pos_x1 <- abs(min(rdf$x1, na.rm = T)) + rdf$x1
    log10(0.1 + abs(z$min) + x + pos_x1)
  },
  "my_log10"=function(rdf, x, z){
    # WHATEVER fancy function you like
    mask <- rdf$x1 > 0.1
    mask[mask == 0] <- NA
    x.nyte <- mask * x
    l <- log10(0.1+abs(z$min)+x.nyte)
    l[is.na(l)] <- 0
    return(l)
  },
  "inv"=function(rdf, x, z){
    1/(0.1+abs(z$min)+x)
  },
  "sigmoid"=function(rdf, x,z){
    1 / ( 1 + exp(-x))
  },
  "invsigmoid"=function(rdf, x,z){
    1/(1 + exp(x))
  }
)

Start symbolic regression

Based on seed value, run a different algorithm

if(seed == 0){
  base.filepath <- paste0('regression/regression',type,'.exploration.l',formula.len,'.rData')
  res.new <- exaustive.search(regressors.df, l.Fn,
                         n.squares=n.squares,
                         formula.len=formula.len,
                         K=K, N=N, seed=seed,
                         transformations=transformations,
                         custom.abs.mins=list(),
                         glob.filepath = base.filepath,
                         chunk.size=NULL, cv.norm=T)
}else if(seed < 1000){
  base.filepath <- paste0('regression/regression',type,'.exploration.l',formula.len,'.rData')
  # general "random" search to find good candidates
  new.sample.res <- random.search(regressors.df, l.Fn, n.squares, formula.len,
                                  maxiter=10, K=K, N=N,
                                  transformations=transformations,
                                  glob.filepath = base.filepath,
                                  local.filepath = res.filepath,
                                  memoization=T, cv.norm = T)
}else{
  # genetic can change formula length
  base.filepath <- c(
    l1.filepath,
    l2.filepath,
    l3.filepath#,
    #l4.filepath
  )

  # finetuning procedure using genetic algorithm
  best.vars.l <- list(
    c('log.x1')  )
  best.finetuned <- genetic.search(
    regressors.df,
    l.Fn,
    n.squares=n.squares,
    max.formula.len=max.formula.len,
    maxiter=100,
    transformations=transformations,
    glob.filepath=base.filepath,
    local.filepath=res.filepath,
    memoization=T,
    pcrossover=0.1,
    pmutation=0.9,
    seed=NULL,
    keepBest=T,
    K=K,
    N=N,
    popSize = 100,
    best.vars.l=best.vars.l,
    cv.norm = T
  )
}