-
Notifications
You must be signed in to change notification settings - Fork 7
/
low-level.Rmd
executable file
·368 lines (275 loc) · 9.99 KB
/
low-level.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
---
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()`