Skip to content

Commit b9ed5a6

Browse files
committed
Fixes codex comment on assigning into an already existing variable and ensureing dimensions are compatible.
1 parent 1dbdbab commit b9ed5a6

2 files changed

Lines changed: 128 additions & 20 deletions

File tree

R/sub-r2f-matrix.R

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -172,13 +172,38 @@ unwrap_transpose_arg <- function(arg, scope, ..., hoist) {
172172
}
173173

174174
# Whether it's safe and useful to write into dest (no aliasing with inputs)
175-
can_use_output <- function(dest, left, right) {
175+
assert_dest_dims_compatible <- function(dest, expected_dims, context) {
176+
if (is.null(dest) || is.null(expected_dims)) {
177+
return(invisible(TRUE))
178+
}
179+
expected_rank <- length(expected_dims)
180+
if (dest@rank != expected_rank) {
181+
stop("assignment target has incompatible rank for ", context, call. = FALSE)
182+
}
183+
for (i in seq_len(expected_rank)) {
184+
dest_dim <- dest@dims[[i]]
185+
expected_dim <- expected_dims[[i]]
186+
if (is_wholenumber(dest_dim) && is_wholenumber(expected_dim)) {
187+
if (!identical(as.integer(dest_dim), as.integer(expected_dim))) {
188+
stop(
189+
"assignment target has incompatible dimensions for ",
190+
context,
191+
call. = FALSE
192+
)
193+
}
194+
}
195+
}
196+
invisible(TRUE)
197+
}
198+
199+
can_use_output <- function(dest, left, right, expected_dims = NULL, context) {
176200
if (is.null(dest)) {
177201
return(FALSE)
178202
}
179203
if (!identical(dest@mode, "double")) {
180204
return(FALSE)
181205
}
206+
assert_dest_dims_compatible(dest, expected_dims, context)
182207
output_name <- dest@name
183208
# check output name is not the same as left or right
184209
!identical(output_name, as.character(left)) &&
@@ -230,15 +255,24 @@ gemm <- function(
230255
ldc_expr,
231256
scope,
232257
hoist,
233-
dest = NULL
258+
dest = NULL,
259+
context = "gemm"
234260
) {
235261
if (!inherits(hoist, "environment")) {
236262
stop("internal: hoist must be a hoist environment")
237263
}
238264
A_name <- ensure_blas_operand_name(left, hoist)
239265
B_name <- ensure_blas_operand_name(right, hoist)
240266

241-
if (can_use_output(dest, left, right)) {
267+
if (
268+
can_use_output(
269+
dest,
270+
left,
271+
right,
272+
expected_dims = list(m, n),
273+
context = context
274+
)
275+
) {
242276
hoist$emit(glue(
243277
"call dgemm('{opA}','{opB}', {blas_int(m)}, {blas_int(n)}, {blas_int(k)}, 1.0_c_double, {A_name}, {blas_int(lda)}, {B_name}, {blas_int(ldb)}, 0.0_c_double, {dest@name}, {blas_int(ldc_expr)})"
244278
))
@@ -268,15 +302,24 @@ gemv <- function(
268302
out_dims,
269303
scope,
270304
hoist,
271-
dest = NULL
305+
dest = NULL,
306+
context = "gemv"
272307
) {
273308
if (!inherits(hoist, "environment")) {
274309
stop("internal: hoist must be a hoist environment")
275310
}
276311
A_name <- ensure_blas_operand_name(A, hoist)
277312
x_name <- ensure_blas_operand_name(x, hoist)
278313

279-
if (can_use_output(dest, A, x)) {
314+
if (
315+
can_use_output(
316+
dest,
317+
A,
318+
x,
319+
expected_dims = out_dims,
320+
context = context
321+
)
322+
) {
280323
# Assign output to output destination
281324
hoist$emit(glue(
282325
"call dgemv('{transA}', {blas_int(m)}, {blas_int(n)}, 1.0_c_double, {A_name}, {blas_int(lda)}, {x_name}, 1, 0.0_c_double, {dest@name}, 1)"
@@ -302,7 +345,8 @@ syrk <- function(
302345
X,
303346
scope,
304347
hoist,
305-
dest = NULL
348+
dest = NULL,
349+
context = "syrk"
306350
) {
307351
if (!inherits(hoist, "environment")) {
308352
stop("internal: hoist must be a hoist environment")
@@ -323,7 +367,15 @@ syrk <- function(
323367
lda <- x_dims$rows
324368

325369
# Output is symmetric n x n matrix
326-
if (can_use_output(dest, X, X)) {
370+
if (
371+
can_use_output(
372+
dest,
373+
X,
374+
X,
375+
expected_dims = list(n, n),
376+
context = context
377+
)
378+
) {
327379
hoist$emit(glue(
328380
"call dsyrk('U', '{trans}', {blas_int(n)}, {blas_int(k)}, 1.0_c_double, {X_name}, {blas_int(lda)}, 0.0_c_double, {dest@name}, {blas_int(n)})"
329381
))
@@ -361,7 +413,14 @@ end do"
361413
Fortran(output_var@name, output_var)
362414
}
363415

364-
outer_mul <- function(x, y, scope, hoist, dest = NULL) {
416+
outer_mul <- function(
417+
x,
418+
y,
419+
scope,
420+
hoist,
421+
dest = NULL,
422+
context = "outer"
423+
) {
365424
if (!inherits(hoist, "environment")) {
366425
stop("internal: hoist must be a hoist environment")
367426
}
@@ -379,7 +438,15 @@ outer_mul <- function(x, y, scope, hoist, dest = NULL) {
379438
x_name <- ensure_blas_operand_name(x, hoist)
380439
y_name <- ensure_blas_operand_name(y, hoist)
381440

382-
if (can_use_output(dest, x, y)) {
441+
if (
442+
can_use_output(
443+
dest,
444+
x,
445+
y,
446+
expected_dims = list(m, n),
447+
context = context
448+
)
449+
) {
383450
hoist$emit(glue("{dest@name} = 0.0_c_double"))
384451
hoist$emit(glue(
385452
"call dger({blas_int(m)}, {blas_int(n)}, 1.0_c_double, {x_name}, 1, {y_name}, 1, {dest@name}, {blas_int(m)})"
@@ -405,7 +472,8 @@ triangular_solve <- function(
405472
diag,
406473
scope,
407474
hoist,
408-
dest = NULL
475+
dest = NULL,
476+
context = "triangular solve"
409477
) {
410478
if (!inherits(hoist, "environment")) {
411479
stop("internal: hoist must be a hoist environment")
@@ -438,7 +506,15 @@ triangular_solve <- function(
438506

439507
A_name <- ensure_blas_operand_name(A, hoist)
440508

441-
if (can_use_output(dest, A, B)) {
509+
if (
510+
can_use_output(
511+
dest,
512+
A,
513+
B,
514+
expected_dims = B@value@dims,
515+
context = context
516+
)
517+
) {
442518
hoist$emit(glue("{dest@name} = {B}"))
443519
B_name <- dest@name
444520
out_var <- dest
@@ -492,7 +568,8 @@ crossprod_like <- function(
492568
X = x,
493569
scope = scope,
494570
hoist = hoist,
495-
dest = dest
571+
dest = dest,
572+
context = context
496573
))
497574
}
498575

@@ -526,7 +603,8 @@ crossprod_like <- function(
526603
ldc_expr = ldc_expr,
527604
scope = scope,
528605
hoist = hoist,
529-
dest = dest
606+
dest = dest,
607+
context = context
530608
)
531609
}
532610

@@ -595,7 +673,8 @@ r2f_handlers[["%*%"]] <- function(args, scope, ..., hoist = NULL, dest = NULL) {
595673
out_dims = list(out_len, 1L),
596674
scope = scope,
597675
hoist = hoist,
598-
dest = dest
676+
dest = dest,
677+
context = "%*%"
599678
))
600679
}
601680
# Vector-Matrix: use GEMV with transpose
@@ -614,7 +693,8 @@ r2f_handlers[["%*%"]] <- function(args, scope, ..., hoist = NULL, dest = NULL) {
614693
out_dims = list(1L, out_len),
615694
scope = scope,
616695
hoist = hoist,
617-
dest = dest
696+
dest = dest,
697+
context = "%*%"
618698
))
619699
}
620700

@@ -634,7 +714,8 @@ r2f_handlers[["%*%"]] <- function(args, scope, ..., hoist = NULL, dest = NULL) {
634714
ldc_expr = ldc_expr,
635715
scope = scope,
636716
hoist = hoist,
637-
dest = dest
717+
dest = dest,
718+
context = "%*%"
638719
)
639720
}
640721

@@ -725,7 +806,14 @@ r2f_handlers[["outer"]] <- function(
725806
}
726807
x <- r2f(x_arg, scope, ..., hoist = hoist)
727808
y <- r2f(y_arg, scope, ..., hoist = hoist)
728-
outer_mul(x, y, scope = scope, hoist = hoist, dest = dest)
809+
outer_mul(
810+
x,
811+
y,
812+
scope = scope,
813+
hoist = hoist,
814+
dest = dest,
815+
context = "outer"
816+
)
729817
}
730818

731819
r2f_handlers[["%o%"]] <- function(
@@ -738,7 +826,14 @@ r2f_handlers[["%o%"]] <- function(
738826
stopifnot(length(args) == 2L)
739827
x <- r2f(args[[1L]], scope, ..., hoist = hoist)
740828
y <- r2f(args[[2L]], scope, ..., hoist = hoist)
741-
outer_mul(x, y, scope = scope, hoist = hoist, dest = dest)
829+
outer_mul(
830+
x,
831+
y,
832+
scope = scope,
833+
hoist = hoist,
834+
dest = dest,
835+
context = "%o%"
836+
)
742837
}
743838

744839
r2f_handlers[["forwardsolve"]] <- function(
@@ -777,7 +872,8 @@ r2f_handlers[["forwardsolve"]] <- function(
777872
diag = if (diag_unit) "U" else "N",
778873
scope = scope,
779874
hoist = hoist,
780-
dest = dest
875+
dest = dest,
876+
context = "forwardsolve"
781877
)
782878
}
783879

@@ -807,7 +903,8 @@ r2f_handlers[["backsolve"]] <- function(
807903
diag = if (diag_unit) "U" else "N",
808904
scope = scope,
809905
hoist = hoist,
810-
dest = dest
906+
dest = dest,
907+
context = "backsolve"
811908
)
812909
}
813910

tests/testthat/test-matrix-mul.R

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ test_that("matrix multiplication errors on non-conformable arguments", {
191191
expect_error(quick(matmul_bad), "non-conformable arguments in %*%")
192192
})
193193

194+
test_that("matrix multiplication rejects incompatible destinations", {
195+
dest_mismatch <- function() {
196+
declare(type(x = double(2)))
197+
a <- matrix(1.5, 2L, 2L)
198+
x <- a %*% a
199+
x
200+
}
201+
202+
expect_error(quick(dest_mismatch), "incompatible rank for %\\*%")
203+
})
204+
194205
test_that("crossprod and tcrossprod match R", {
195206
cross_fun <- function(x, y) {
196207
declare(

0 commit comments

Comments
 (0)