# Using the C++ Interface

#### Jonathan Berrisch

#### 2024-09-21

## Introduction

All major parts of `online()` are implemented in C++ for speed. Usually, this comes at the cost of flexibility. However, the profoc package exposes a C++ class `conline` that allows you to gain fine grained control over objects.
`online()` wraps this class and provides a convenient interface for the most common use cases. However, if you need to alter object initialization (i.e. provide custom basis / hat matrices for smoothing) you can use the C++ class directly from R. This vignette shows how to do this.

Note that we will reuse the data from `vignette("profoc")`.

## Online learning with `conline`

First, we need to create a new instance of the c++ class. This can be done by calling `new(conline)`.

    library(profoc)
    model <- new(conline)

Now we need to pass the data to the class instance. The whole list of accessible field can be printed with `names(model)`. Most of them have defaults.

    model$y <- y
    tau <- 1:P / (P + 1)
    model$tau <- tau

The experts array is a bit more complicated. C++ expects us to pass a list of arrays. Thereby, the list itself must have dimension `Tx1` and the elements of the list (the arrays) `D x P x K`. For convenience we can use `init_experts_list()` to create such a list from our experts array. Note that we must pass the true observations as well. They are used to detect whether the data is univariate (`T x 1` matrix) or multivariate (`T x D` matrix).

    experts_list <- init_experts_list(experts, y)
    model$experts <- experts_list

Now suppose we want to alter the smoothing behavior across quantiles. We start by creating a new hat matrix.

    hat <- make_hat_mats(
      x = tau,
      mu = 0.2, # Put more knots in the lower tail
      periodic = TRUE
    )
    str(hat)
    #> List of 2
    #>  $ hat   :List of 1
    #>   ..$ :Formal class 'dgCMatrix' [package "Matrix"] with 6 slots
    #>   .. .. ..@ i       : int [1:99] 0 1 2 3 4 5 6 7 8 9 ...
    #>   .. .. ..@ p       : int [1:100] 0 1 2 3 4 5 6 7 8 9 ...
    #>   .. .. ..@ Dim     : int [1:2] 99 99
    #>   .. .. ..@ Dimnames:List of 2
    #>   .. .. .. ..$ : NULL
    #>   .. .. .. ..$ : NULL
    #>   .. .. ..@ x       : num [1:99] 1 1 1 1 1 1 1 1 1 1 ...
    #>   .. .. ..@ factors : list()
    #>   ..- attr(*, "dim")= int [1:2] 1 1
    #>  $ params: num [1, 1:9] 99 0.2 1 0 1 ...
    #>   ..- attr(*, "dimnames")=List of 2
    #>   .. ..$ : NULL
    #>   .. ..$ : chr [1:9] "n" "mu" "sigma" "nonc" ...

We need a list of sparse matrices which `make_hat_mats()` returns. So we can pass that directly to our class.

    model$hat_pr <- hat$hat

The other smoothing matrices have to be filled with defaults (lists of sparse identity matrices). Usually `online()` takes care of this. But we can do it manually as well.

    model$basis_mv <- list(Matrix::sparseMatrix(i = 1:D, j = 1:D, x = 1))
    model$basis_pr <- list(Matrix::sparseMatrix(i = 1:P, j = 1:P, x = 1))
    model$hat_mv <- list(Matrix::sparseMatrix(i = 1:D, j = 1:D, x = 1))

Now we can specify the parameter grid. We will stick to the defaults here:

    parametergrid <- as.matrix(
      expand.grid(
        forget_regret = 0,
        soft_threshold = -Inf,
        hard_threshold = -Inf,
        fixed_share = 0,
        basis_pr_idx = 1,
        basis_mv_idx = 1,
        hat_pr_idx = 1,
        hat_mv_idx = 1,
        gamma = 1,
        loss_share = 0,
        regret_share = 0
      )
    )

    model$params <- parametergrid

Finally, we can run `model$set_defaults()`. This populates initial states (w0 for weights and R0 for regret).

    model$set_defaults()

Now `model$set_grid_objects()` will create the grid objects (performance, weights, regret etc.)

    model$set_grid_objects()

Finally, we can run `model$learn()` to start the learning process.

    model$learn()

## Accessing the results

The learning process fills the class objects. So we can inspect them using the `$` operator, like we would with any other R object. For example, we can access the weights:

    head(model$weights[[T]][, , 1])
    #> [1] 0.4892732 0.4058720 0.5979021 0.5770314 0.5487374 0.6287706

However, we can also use the post processing function of `online()` to access the results. This will create output that is identical to the output of `online()`:

    names <- list(y = dimnames(y))
    names$experts <- list(
      1:T,
      paste("Marginal", 1:D),
      tau,
      paste("Expert", 1:N)
    )

    output <- post_process_model(model, names)

We can now use `output` in `update()`, `plot()` and others.

At this point, we do not need to keep the model in memory anymore. So we can delete it:

    rm(model)

## Summary

The C++ class `conline` allows you to gain fine grained control over the learning process. However, it is not as convenient as the `online()` wrapper. So you should only use it if you need to alter the default behavior. However, mixing up helper functions from `online()` and the C++ class is possible. So you can compute your combinations using the class interface while still being able to use `update()`, `plot()` and others afterward.