Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/lexer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ on:
push:
branches:
- equality-glyph-aliases
- tensor-product-glyph
pull_request:
paths:
- code/src/main/gram.c
- code/src/main/gram.y
- code/src/include/Rinlinedfuns.h
- code/src/library/base/R/kronecker.R
- code/tests/reg-encodings.R
- code/tests/ir-unicode-lexer.R
- tensor-product-example.R
- .github/workflows/lexer.yml

jobs:
Expand Down Expand Up @@ -46,3 +49,6 @@ jobs:

- name: Test Unicode lexer priority
run: build/bin/R --vanilla < code/tests/ir-unicode-lexer.R

- name: Test tensor-product example
run: build/bin/R --vanilla < tensor-product-example.R
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
### fn λ → ÷ ≟ ←
### fn λ → ÷ ≟ ←

→ and ← can now be used for assignment, not just `<-` and `->`.

`function(x) x**3` can now be written `fn(x) x**3`, `λ(x) x**3`, or `ƒ(x) x**3`.

÷ means division.

⊗ means the Kronecker/tensor product. It matches R's existing `%x%` operator; `%o%` remains R's outer product.

Existing R spelling remains available: `<-`, `<<-`, `->`, `->>`, `==`, and `function` still work.

Existing R spelling remains available: `<-`, `<<-`, `->`, `->>`, `==`, `function`, and `%x%` still work.



Expand All @@ -24,7 +26,17 @@ The last line returns

because ≟ tests equality.

A matrix tensor product can be written directly:

```r
A ← matrix(c(1, 2,
3, 4), nrow = 2, byrow = TRUE)
B ← matrix(c(0, 5,
6, 7), nrow = 2, byrow = TRUE)
A ⊗ B
```

which produces the same 4 × 4 matrix as `A %x% B` and `kronecker(A, B)`.



Expand All @@ -37,6 +49,7 @@ because ≟ tests equality.
| `left ≟ right` | test equality |
| `fn(x) expression`, `λ(x) expression`, or `ƒ(x) expression` | construct a function |
| `left ÷ right` | divide |
| `left ⊗ right` | Kronecker/tensor product |



Expand Down
3 changes: 2 additions & 1 deletion code/src/library/base/R/kronecker.R
Original file line number Diff line number Diff line change
Expand Up @@ -73,5 +73,6 @@ kronecker <- function (X, Y, FUN = "*", make.dimnames = FALSE, ...)
opobj
}

## Binary operator, hence don't simply do "%x%" <- kronecker.
## Binary operators, hence don't simply alias their names to kronecker.
`%x%` <- function(X, Y) kronecker(X, Y)
`⊗` <- function(X, Y) kronecker(X, Y)
33 changes: 28 additions & 5 deletions code/tests/ir-unicode-lexer.R
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ if (!UTF8) {
COMPOSE <- intToUtf8(0x2218) # ∘
EQUALITY <- intToUtf8(0x225f) # ≟
INVERTED_Q <- intToUtf8(0x00bf) # ¿
GENERIC <- intToUtf8(0x2297) # ⊗
TENSOR <- intToUtf8(0x2297) # ⊗
GENERIC <- intToUtf8(0x2299) # ⊙

stopifnot(
identical(PI, "π"),
Expand All @@ -36,7 +37,8 @@ if (!UTF8) {
identical(COMPOSE, "∘"),
identical(EQUALITY, "≟"),
identical(INVERTED_Q, "¿"),
identical(GENERIC, "⊗")
identical(TENSOR, "⊗"),
identical(GENERIC, "⊙")
)

## U+03C0 is not merely displayed as π here: make π a real function name
Expand All @@ -62,9 +64,30 @@ if (!UTF8) {
identical(parse1(paste0("1 ", INVERTED_Q, "=? 1")), quote(1 == 1))
)

## Unreserved glyphs still use the broad generic infix path. Define one
## as an ordinary function, then prove the same glyph parses and evaluates
## infix rather than being rejected merely because it is Unicode.
## ⊗ is IR's Kronecker/tensor-product operator. Exercise a real matrix
## product rather than treating the glyph as an arbitrary infix placeholder.
A <- matrix(c(1, 2,
3, 4), nrow = 2, byrow = TRUE)
B <- matrix(c(0, 5,
6, 7), nrow = 2, byrow = TRUE)
expectedTensor <- matrix(c(0, 5, 0, 10,
6, 7, 12, 14,
0, 15, 0, 20,
18, 21, 24, 28),
nrow = 4, byrow = TRUE)
tensorCall <- parse1(paste("A", TENSOR, "B"))
assign("A", A, envir = syntaxEnv)
assign("B", B, envir = syntaxEnv)
stopifnot(
identical(as.character(tensorCall[[1L]]), TENSOR),
identical(eval(tensorCall, syntaxEnv), expectedTensor),
identical(A ⊗ B, expectedTensor),
identical(A ⊗ B, A %x% B),
identical(A ⊗ B, kronecker(A, B))
)

## Unreserved glyphs still use the broad generic infix path. Keep that
## lexer test separate from ⊗ so the tensor glyph has mathematical meaning.
eval(parse1(paste0(GENERIC, " ", LEFT, " ", LAMBDA,
"(a, b) a + b")), syntaxEnv)
genericCall <- parse1(paste("2", GENERIC, "3"))
Expand Down
30 changes: 30 additions & 0 deletions tensor-product-example.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# IR tensor-product examples.
# ⊗ is the Kronecker product, matching R's existing %x% operator.
# R's %o% remains the outer product.

A ← matrix(c(1, 2,
3, 4), nrow = 2, byrow = TRUE)

B ← matrix(c(0, 5,
6, 7), nrow = 2, byrow = TRUE)

A ⊗ B

expected ← matrix(c(0, 5, 0, 10,
6, 7, 12, 14,
0, 15, 0, 20,
18, 21, 24, 28),
nrow = 4, byrow = TRUE)

stopifnot(
identical(A ⊗ B, expected),
identical(A ⊗ B, A %x% B),
identical(A ⊗ B, kronecker(A, B))
)

# Column-vector example: |0> ⊗ |1>.
e0 ← matrix(c(1, 0), ncol = 1)
e1 ← matrix(c(0, 1), ncol = 1)
expected_basis ← matrix(c(0, 1, 0, 0), ncol = 1)

stopifnot(identical(e0 ⊗ e1, expected_basis))
Loading