--- title: "The low-level interface" author: "Mikkel Meyer Andersen and Søren Højsgaard" date: "`r Sys.Date()`" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{The low-level interface} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) ``` ```{r, message=FALSE} library(Ryacas) ``` The low-level interface consists of these two main functions: * `yac_str(x)`: Evaluate `yacas` command `x` (a string) and get result as **string/character**. * `yac_expr(x)`: Evaluate `yacas` command `x` (a string) and get result as an **`R` expression**. Note, that the `yacas` command `x` is a string and must often be built op using `paste()`/`paste0()`. Examples of this will be shown in multiple examples below. A short summary of often-used `yacas` commands are found in the section "`yacas` reference" in the "Getting started" vignette. A short summary of `Ryacas`'s low-level functions are also found in the section "`Ryacas` low-level reference" at the end of this document. Note that the [yacas documentation](https://yacas.readthedocs.io/en/latest/) is a very useful resource. # Example 1: Simple algebra First, consider this polynomial: ```{r} eq <- "x^2 + 4 + 2*x + 2*x" ``` Now, perform `yacas` operations, and get result as string/character: ```{r} yac_str(eq) # No task was given to yacas, so we simply get the same returned yac_str(paste0("Simplify(", eq, ")")) yac_str(paste0("Factor(", eq, ")")) yac_str(paste0("TeXForm(Factor(", eq, "))")) ``` \[ `r yac_str(paste0("TeXForm(Factor(", eq, "))"))` \] Also see yacas documentation on [Simplify()](https://yacas.readthedocs.io/en/latest/reference_manual/simplify.html#Simplify), [Factor()](https://yacas.readthedocs.io/en/latest/reference_manual/number-theory.html#Factor) and [TeXForm()](https://yacas.readthedocs.io/en/latest/reference_manual/io.html#TeXForm). Instead of the pattern `paste0("Simplify(", eq, ")")` etc., there exists a helper function `y_fn()` that does this: ```{r} y_fn(eq, "Simplify") yac_str(y_fn(eq, "Simplify")) yac_str(y_fn(eq, "Factor")) yac_str(y_fn(y_fn(eq, "Factor"), "TeXForm")) ``` As you see, there are a lot of nested function calls. That can be avoided by using `magrittr`'s pipe `%>%` (automatically available with `Ryacas`) together with the helper function `y_fn()`: ```{r} eq %>% y_fn("Simplify") eq %>% y_fn("Simplify") %>% yac_str() eq %>% y_fn("Factor") %>% yac_str() eq %>% y_fn("Factor") %>% y_fn("TeXForm") %>% yac_str() ``` Below, we will stick to the standard way of calling the functions, and not using the pipe, but now it has been demonstrated if the user prefers that way. We will not use the pipe operator below, but just demonstrate its usage. Now, again perform `yacas` operations, but get result as an `R` expression, e.g. for continued computations: ```{r} eq eq %>% yac_expr() # Alternative to "yac_expr(eq)" cmd <- eq %>% y_fn("Factor") cmd e <- yac_expr(cmd) e eval(e, list(x = 2)) ``` # Example 2: Linear algebra To work with matrices and vectors, you need to realise that `yacas` and `R` has different ways of representing these objects. `yacas` represents vectors as a list, and a matrix as a list of lists (each list is a row). ## Simple example You can work with these directly. Here illutrated with vectors: ```{r} cmd <- "2*{x, x^2, x^3}" cmd %>% yac_str() e <- cmd %>% yac_expr() e eval(e, list(x = 1.5)) ``` And then illutrated with matrices. First with purely numeric contents: ```{r} cmd <- "{{1, 2}, {3, 4}}" yac_str(cmd) y_print(cmd) # Convenience function for prettier print e <- cmd %>% yac_expr() e eval(e) ``` Also in $\LaTeX$ ([yacas documentation on TeXForm()](https://yacas.readthedocs.io/en/latest/reference_manual/io.html#TeXForm)): ```{r} cmd %>% y_fn("TeXForm") %>% yac_str() ``` But it also works with symbolic contents: ```{r} cmd1 <- paste0("a * ", cmd, "") cmd2 <- cmd1 %>% y_fn("Inverse") cmd2 cmd2 %>% yac_str() cmd2 %>% y_fn("TeXForm") %>% yac_str() ``` \[ `r cmd2 %>% y_fn("TeXForm") %>% yac_str()` \] ```{r} paste0(cmd2, "*", cmd1) %>% y_fn("Simplify") %>% yac_str() e2 <- cmd2 %>% yac_expr() eval(e2, list(a = 2.2)) ``` ## Using `R`'s character matrices The above is fine when writing `yacas` vectors and matrices by hand. But often one would want to exploit `R`'s convenient functions to work with matrices. The central idea to make this possible is to work with `R` character matrices. We provide two helper functions to go back and forth between `R` and `yacas`: * `as_y(x)`: Convert `R` character matrix `x` to a `yacas` representation * `as_r(x)`: Convert a `yacas` representation `x` to a `R` character matrix Below, we illustrate the usage of both functions. First, we create a character matrix using `R`: ```{r} Achr <- matrix(0, nrow = 3, ncol = 3) diag(Achr) <- 1 Achr[2, 3] <- "a" Achr[1, 3] <- "a" Achr ``` Note how this is a character matrix. If we want to find it's inverse symbolically using `yacas`, it must first be represented as a `yacas` matrix: ```{r} Ayac <- Achr %>% as_y() Ayac ``` Now, we can find the inverse: ```{r} cmd <- Ayac %>% y_fn("Inverse") cmd %>% yac_str() ``` A nicer representation can be obtained in (at least) four ways: ```{r} way1 <- cmd %>% yac_str() way1 %>% y_print() way2 <- cmd %>% y_fn("TeXForm") %>% yac_str() way2 way3 <- cmd %>% y_fn("PrettyForm") %>% yac_str() way3 %>% cat() # Result of PrettyForm() must be printed way4 <- cmd %>% yac_str() way4 way4 %>% as_r() way4 %>% as_r() %>% print(quote = FALSE) ``` Say we want to subset it to only consider a submatrix. To do that, we can use `R`'s facilities: ```{r} A_inv_yac <- way4 %>% as_r() Bchr <- A_inv_yac[2:3, 2:3] Bchr Bchr %>% as_y() Bchr %>% as_y() %>% y_fn("Inverse") %>% yac_str() %>% as_r() ``` # `yacas` variables `yacas` also has variables. They are assigned by `:=`. Consider this example: ```{r} yac_str("poly := (x-3)*(x+2)") ``` If the output is not necessary, it can be suppressed by using `yac_silent()` instead of `yac_str()`: ```{r} yac_silent("poly := (x-3)*(x+2)") ``` We can now list `yacas` variables (`I` is the imaginary unit): ```{r} yac_str("Variables()") ``` ```{r} yac_str("Expand(poly)") "poly" %>% y_fn("Expand") %>% yac_str() ``` # Sums Yacas can sum an expression. The syntax is `Sum(var, from, to, body)` as described in the [yacas documentation of `Sum()`](https://yacas.readthedocs.io/en/latest/reference_manual/calc.html#Sum). For example we can sum $a^k$ for $k = 0$ to $k = n$ as follows: ```{r} yac_str("Sum(k, 0, n, a^k)") ``` # Limits Yacas can also take the limit of an expression. The syntax is ` Limit(var, val) expr` as described in the [yacas documentation of `Limit()`](https://yacas.readthedocs.io/en/latest/reference_manual/calc.html#Limit). For example we can take the limit of $(1+(1/n))^n$ for $n \to \infty$ as follows: ```{r} cmd <- "Limit(n, Infinity) (1+(1/n))^n" yac_str(cmd) yac_expr(cmd) ``` This can also be used to illustrate taking derivatives, e.g. the derivative of sine: ```{r} yac_str("Limit(h, 0) (Sin(x+h)-Sin(x))/h") ``` # Solving equations Say we want to find roots of `poly`. First note that the equality in `yacas` is `==`. Then: ```{r} cmd <- "Solve(poly == 0, x)" cmd %>% yac_str() ``` Note that the default in yacas's `Solve()` is to find roots if no equality sign is provided: ```{r} cmd <- "Solve(poly, x)" cmd %>% yac_str() ``` If we want the solution without the variable name `x` and the equality symbol `==`, we can use `Ryacas` helper function `y_rmvars()`: ```{r} cmd cmd %>% y_rmvars() %>% yac_str() cmd %>% y_rmvars() %>% yac_expr() ``` We can also use `y_fn()`: ```{r} "poly == 0" %>% y_fn("Solve", "x") "poly" %>% y_fn("Solve", "x") # default is == 0 "poly == 0" %>% y_fn("Solve", "x") %>% y_rmvars() %>% yac_str() ``` # Case: gradient and Hessian for a function Say we have a function: ```{r} f <- function(x, y) 2*x^2 + 3*y + y*x^2 f_body <- body(f) f_body f_body_chr <- as.character(as.expression(body(f))) f_body_chr # or: # f_body_chr <- "2 * x^2 + 3 * y + y * x^2" # f <- function(x, y) NULL # body(f) <- parse(text = f_body_chr, keep.source = FALSE) ``` The gradient can be found using `yacas` (`D(x) expr` is the derivative of `expr` with respect to `x`): ```{r} cmd_g <- paste0("{ D(x) ", f_body_chr, ", D(y) ", f_body_chr, " }") cmd_g g_body <- yac_expr(cmd_g) g_body g <- function(x, y) NULL body(g) <- g_body g g(2, 4) ``` The Hessian matrix can also be found using `yacas`: ```{r} cmd_H <- paste0("HessianMatrix(", f_body_chr, ", {x, y})") cmd_H H_body <- yac_expr(cmd_H) H_body H <- function(x, y) NULL body(H) <- H_body H H(2, 4) ``` # `Ryacas` low-level reference Principle: * `yac_*(x)` functions evaluate/run `yacas` command `x`; the result varies depending on which of the functions used * `y_*(x)` various utility functions (not involving calls to `yacas`) Reference: * Evaluate `yacas` expressions + `yac(x, rettype = c("str", "expr", "silent"))`: Evaluate `yacas` command `x` (a string) and get result determined by `rettype` (default `"str"`). + `yac_expr(x)`: Evaluate `yacas` command `x` (a string) and get result as an **`R` expression**. + `yac_silent(x)`: Evaluate `yacas` command `x` (a string) silently; useful for creating `yacas` variables. **string/character**. + `yac_str(x)`: Same as `yac_expr()`, but get result as **string/character**. * Helper functions + `as_y(x)`: Convert `R` character matrix `x` to a `yacas` representation + `as_r(x)`: Convert a `yacas` representation `x` to an `R` object + `y_fn(x, fn, ...)`: Helper function to prepare a call for `yacas`, e.g. `y_fn("x^2 - 1", "Factor")` is gives `"Factor(x^2 - 1)"`; `...` are additional arguments: `y_fn(x, fn, ...)` gives `fn(x, ...)` + `y_rmvars(x)`: Removes variables such that `{x == 2}` instead gets `{2}`; remember to call `yacas` with e.g. `yac_str()` or `yac_expr()` + `y_print(x)`: Pretty print yacas strings, e.g. a yacas matrix * Lower level functions + `yac_core(x)`: Evaluate `yacas` command `x` (a string) and get both result and side effects; used in the implementation of `yac_expr()`, `yac_silent()`, and `yac_str()`