scaffolder
PackageThe scaffolder package provides a comprehensive set of tools to automate the process of scaffolding interfaces to modules, classes, functions, and documentations written in other programming languages.
The following example requires the installation of TensorFlow 2.0 for demonstration purposes. Please check out the instruction here.
Once it’s installed, we can then execute
scaffold_py_function_wrapper()
to generate the R wrapper to
tensorflow.nn.top_k()
function:
library(scaffolder)
library(tensorflow)
scaffold_py_function_wrapper("tf$nn$top_k")
This will generate the following R wrapper that could serve as a good starting point when building R interfaces to Python modules including all the function parameters, default values, and the associated doc-strings for each parameters.
#' @title top_k
#'
#' @description Finds values and indices of the `k` largest entries for the last dimension.
#'
#' @details If the input is a vector (rank=1), finds the `k` largest entries in the vector
#' and outputs their values and indices as vectors. Thus `values[j]` is the
#' `j`-th largest entry in `input`, and its index is `indices[j]`. For matrices (resp. higher rank input), computes the top `k` entries in each
#' row (resp. vector along the last dimension). Thus, values.shape = indices.shape = input.shape[:-1] + [k] If two elements are equal, the lower-index element appears first.
#'
#' @param input 1-D or higher `Tensor` with last dimension at least `k`.
#' @param k 0-D `int32` `Tensor`. Number of top elements to look for along the last dimension (along each row for matrices).
#' @param sorted If true the resulting `k` elements will be sorted by the values in descending order.
#' @param name Optional name for the operation.
#'
#' @return values: The `k` largest elements along each last dimensional slice. indices: The indices of `values` within the last dimension of `input`.
#'
#' @export
top_k <- function(input, k = 1L, sorted = TRUE, name = NULL) {
python_function_result <- tf$nn$top_k(
input = input,
k = k,
sorted = sorted,
name = name
)
}
Note that the generated wrapper will often require additional editing
(e.g. to convert Python list literals in the docs to R lists, to massage
R numeric values to Python integers via as.integer
where
required, etc.) so is really intended as an starting point for an R
wrapper rather than a wrapper that can be used without modification.
We can customize the scaffolding process in order to avoid the additional editing that we mentioned above. For example, we can implement a function to cast parameters with default values that contains “L” to integers. This is often useful when the package maintainer wants to automate the generation of the R wrapper and reduce the maintenance efforts required in the future, e.g. make sure doc-strings and default values for all the parameters are up-to-date.
library(stringr)
process_int_param_fn <- function(param, docs) {
# Extract the list of parameters that have integer values as default
int_params <- gsub(
" = [-]?[0-9]+L",
"",
str_extract_all(docs$signature, "[A-z]+ = [-]?[0-9]+L")[[1]])
# Explicitly cast parameter in the list obtained above to integer
if (param %in% int_params) {
param <- paste0("as.integer(", param, ")")
}
param
}
Note that since the default value of parameter k
is
1L
. It is wrapped by as.integer()
to ensure
it’s casted to integer before sending it to tf$nn$top_k
for
execution. We then print out the python function result.
library(scaffolder)
library(tensorflow)
custom_scaffold_py_function_wrapper(
"tf$nn$top_k",
r_function = "top_k",
process_param_fn = process_int_param_fn,
postprocess_fn = function() { "return(python_function_result)" })
This will generate the following R code:
#' @title top_k
#'
#' @description Finds values and indices of the `k` largest entries for the last dimension.
#'
#' @details If the input is a vector (rank=1), finds the `k` largest entries in the vector
#' and outputs their values and indices as vectors. Thus `values[j]` is the
#' `j`-th largest entry in `input`, and its index is `indices[j]`. For matrices (resp. higher rank input), computes the top `k` entries in each
#' row (resp. vector along the last dimension). Thus, values.shape = indices.shape = input.shape[:-1] + [k] If two elements are equal, the lower-index element appears first.
#'
#' @param input 1-D or higher `Tensor` with last dimension at least `k`.
#' @param k 0-D `int32` `Tensor`. Number of top elements to look for along the last dimension (along each row for matrices).
#' @param sorted If true the resulting `k` elements will be sorted by the values in descending order.
#' @param name Optional name for the operation.
#'
#' @return values: The `k` largest elements along each last dimensional slice. indices: The indices of `values` within the last dimension of `input`.
#'
#' @export
top_k <- function(input, k = 1L, sorted = TRUE, name = NULL) {
python_function_result <- tf$nn$top_k(
input = input,
k = as.integer(k),
sorted = sorted,
name = name
)
return(python_function_result)
}
This is the same R code as what we generated previously but with two differences:
k
is casted to integer
automatically.python_function_result
from executing
the underlying Python function is being returned as a result of this
generated wrapper.There are also several other different parts where users can
customize the scaffolding of the R wrapper functions. Please check out
the documentation via ?custom_scaffold_py_function_wrapper
for more details.