The low-level interface

Mikkel Meyer Andersen and Søren Højsgaard

2019-08-29

library(Ryacas)

The low-level interface consists of these two main functions:

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.

Example 1: Simple algebra

First, consider this polynomial:

eq <- "x^2 + 4 + 2*x + 2*x"

Now, perform yacas operations, and get result as string/character:

yac_str(eq) # No task was given to yacas, so we simply get the same returned
## [1] "x^2+2*x+2*x+4"
yac_str(paste0("Simplify(", eq, ")"))
## [1] "x^2+4*x+4"
yac_str(paste0("Factor(", eq, ")"))
## [1] "(x+2)^2"
yac_str(paste0("TeXForm(Factor(", eq, "))"))
## [1] "\\left( x + 2\\right)  ^{2}"

\[ \left( x + 2\right) ^{2} \]

Instead of the pattern paste0("Simplify(", eq, ")") etc., there exists a helper function y_fn() that does this:

y_fn(eq, "Simplify")
## [1] "Simplify(x^2 + 4 + 2*x + 2*x)"
yac_str(y_fn(eq, "Simplify"))
## [1] "x^2+4*x+4"
yac_str(y_fn(eq, "Factor"))
## [1] "(x+2)^2"
yac_str(y_fn(y_fn(eq, "Factor"), "TeXForm"))
## [1] "\\left( x + 2\\right)  ^{2}"

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():

eq %>% y_fn("Simplify")
## [1] "Simplify(x^2 + 4 + 2*x + 2*x)"
eq %>% y_fn("Simplify") %>% yac_str()
## [1] "x^2+4*x+4"
eq %>% y_fn("Factor") %>% yac_str()
## [1] "(x+2)^2"
eq %>% y_fn("Factor") %>% y_fn("TeXForm") %>% yac_str()
## [1] "\\left( x + 2\\right)  ^{2}"

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:

eq
## [1] "x^2 + 4 + 2*x + 2*x"
eq %>% yac_expr() # Alternative to "yac_expr(eq)"
## expression(x^2 + 2 * x + 2 * x + 4)
cmd <- eq %>% y_fn("Factor")
cmd
## [1] "Factor(x^2 + 4 + 2*x + 2*x)"
e <- yac_expr(cmd)
e
## expression((x + 2)^2)
eval(e, list(x = 2))
## [1] 16

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:

## [1] "{2*x,2*x^2,2*x^3}"
## expression(c(2 * x, 2 * x^2, 2 * x^3))
## [1] 3.00 4.50 6.75

And then illutrated with matrices. First with purely numeric contents:

## [1] "{{1,2},{3,4}}"
## {{1, 2},
##  {3, 4}}
## expression(rbind(c(1, 2), c(3, 4)))
##      [,1] [,2]
## [1,]    1    2
## [2,]    3    4

Also in \(\LaTeX\):

## [1] "\\left( \\begin{array}{cc} 1 & 2 \\\\ 3 & 4 \\end{array} \\right) "

But it also works with symbolic contents:

## [1] "Inverse(a * {{1, 2}, {3, 4}})"
## [1] "{{1/a+(6*a^2)/(a^2*(4*a-(6*a^2)/a)),(-(2*a)/a)/(4*a-(6*a^2)/a)},{(-(3*a)/a)/(4*a-(6*a^2)/a),1/(4*a-(6*a^2)/a)}}"
## [1] "\\left( \\begin{array}{cc} \\frac{1}{a}  + \\frac{6 a ^{2}}{a ^{2} \\left( 4 a - \\frac{6 a ^{2}}{a} \\right) }  & \\frac{ - \\frac{2 a}{a} }{4 a - \\frac{6 a ^{2}}{a} }  \\\\ \\frac{ - \\frac{3 a}{a} }{4 a - \\frac{6 a ^{2}}{a} }  & \\frac{1}{4 a - \\frac{6 a ^{2}}{a} }  \\end{array} \\right) "

\[ \left( \begin{array}{cc} \frac{1}{a} + \frac{6 a ^{2}}{a ^{2} \left( 4 a - \frac{6 a ^{2}}{a} \right) } & \frac{ - \frac{2 a}{a} }{4 a - \frac{6 a ^{2}}{a} } \\ \frac{ - \frac{3 a}{a} }{4 a - \frac{6 a ^{2}}{a} } & \frac{1}{4 a - \frac{6 a ^{2}}{a} } \end{array} \right) \]

## [1] "{{1,0},{0,1}}"
##            [,1]       [,2]
## [1,] -0.9090909  0.4545455
## [2,]  0.6818182 -0.2272727

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:

Below, we illustrate the usage of both functions.

First, we create a character matrix using R:

##      [,1] [,2] [,3]
## [1,] "1"  "0"  "a" 
## [2,] "0"  "1"  "a" 
## [3,] "0"  "0"  "1"

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:

## [1] "{{1, 0, a}, {0, 1, a}, {0, 0, 1}}"

Now, we can find the inverse:

## [1] "{{1,0,-a},{0,1,-a},{0,0,1}}"

A nicer representation can be obtained in (at least) four ways:

## {{ 1,  0, -a},
##  { 0,  1, -a},
##  { 0,  0,  1}}
## [1] "\\left( \\begin{array}{ccc} 1 & 0 &  - a \\\\ 0 & 1 &  - a \\\\ 0 & 0 & 1 \\end{array} \\right) "
## 
## /                         \
## | ( 1 ) ( 0 ) ( -( a ) )  |
## |                         |
## | ( 0 ) ( 1 ) ( -( a ) )  |
## |                         |
## | ( 0 ) ( 0 ) ( 1 )       |
## \                         /
## [1] "{{1,0,-a},{0,1,-a},{0,0,1}}"
##      [,1] [,2] [,3]
## [1,] "1"  "0"  "-a"
## [2,] "0"  "1"  "-a"
## [3,] "0"  "0"  "1"
##      [,1] [,2] [,3]
## [1,] 1    0    -a  
## [2,] 0    1    -a  
## [3,] 0    0    1

Say we want to subset it to only consider a submatrix. To do that, we can use R’s facilities:

##      [,1] [,2]
## [1,] "1"  "-a"
## [2,] "0"  "1"
## [1] "{{1, -a}, {0, 1}}"
##      [,1] [,2]
## [1,] "1"  "a" 
## [2,] "0"  "1"

yacas variables

yacas also has variables. They are assigned by :=.

Consider this example:

yac_str("poly := (x-3)*(x+2)")
## [1] "(x-3)*(x+2)"

If the output is not necessary, it can be suppressed by using yac_silent() instead of yac_str():

yac_silent("poly := (x-3)*(x+2)")

We can now list yacas variables (I is the imaginary unit):

yac_str("Variables()")
## [1] "{I,poly,rformBitwiseOps,j}"
yac_str("Expand(poly)")
## [1] "x^2-x-6"
"poly" %>% y_fn("Expand") %>% yac_str()
## [1] "x^2-x-6"

Sums

yac_str("Sum(k, 0, n, a^k)")
## [1] "(1-a^(n+1))/(1-a)"

Limits

cmd <- "Limit(n, Infinity) (1+(1/n))^n"
yac_str(cmd)
## [1] "Exp(1)"
yac_expr(cmd)
## expression(exp(1))
yac_str("Limit(h, 0) (Sin(x+h)-Sin(x))/h")
## [1] "Cos(x)"

Solving equations

Say we want to find roots of poly. First note that the equality in yacas is ==. Then_

cmd <- "Solve(poly == 0, x)"
cmd %>% yac_str()
## [1] "{x==(-2),x==3}"

If we want the solution without the variable name x and the equality symbol ==, we can use Ryacas helper function y_rmvars():

cmd
## [1] "Solve(poly == 0, x)"
cmd %>% y_rmvars() %>% yac_str()
## [1] "{-2,3}"
cmd %>% y_rmvars() %>% yac_expr()
## expression(c(-2, 3))

We can also use y_fn():

"poly == 0" %>% y_fn("Solve", "x")
## [1] "Solve(poly == 0, x)"
"poly == 0" %>% y_fn("Solve", "x") %>% y_rmvars() %>% yac_str()
## [1] "{-2,3}"

Case: gradient and Hessian for a function

Say we have a function:

f <- function(x, y) 2*x^2 + 3*y + y*x^2
f_body <- body(f)
f_body
## 2 * x^2 + 3 * y + y * x^2
f_body_chr <- as.character(as.expression(body(f)))
f_body_chr
## [1] "2 * x^2 + 3 * y + y * x^2"
# 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):

cmd_g <- paste0("{ D(x) ", f_body_chr, ", D(y) ", f_body_chr, " }")
cmd_g
## [1] "{ D(x) 2 * x^2 + 3 * y + y * x^2, D(y) 2 * x^2 + 3 * y + y * x^2 }"
g_body <- yac_expr(cmd_g)
g_body
## expression(c(4 * x + y * 2 * x, x^2 + 3))
g <- function(x, y) NULL
body(g) <- g_body
g
## function (x, y) 
## c(4 * x + y * 2 * x, x^2 + 3)
g(2, 4)
## [1] 24  7

The Hessian matrix can also be found using yacas:

cmd_H <- paste0("HessianMatrix(", f_body_chr, ", {x, y})")
cmd_H
## [1] "HessianMatrix(2 * x^2 + 3 * y + y * x^2, {x, y})"
H_body <- yac_expr(cmd_H)
H_body
## expression(rbind(c(2 * y + 4, 2 * x), c(2 * x, 0)))
H <- function(x, y) NULL
body(H) <- H_body
H
## function (x, y) 
## rbind(c(2 * y + 4, 2 * x), c(2 * x, 0))
H(2, 4)
##      [,1] [,2]
## [1,]   12    4
## [2,]    4    0

Ryacas low-level reference

Principle:

Reference: