R/043_atoms_affine_reshape.R

Defines functions reshape_expr

Documented in reshape_expr

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/atoms/affine/reshape.R
#####

## CVXPY SOURCE: atoms/affine/reshape.py
## Reshape -- reshape an expression to a new shape
##
## Vectorizes the expression then unvectorizes into the new shape.
## Entries are stored in column-major (Fortran) order by default.
## R matrices are naturally column-major, so 'F' order is the default.


Reshape <- new_class("Reshape", parent = AffAtom, package = "CVXR",
  properties = list(
    order = class_character   # "F" or "C"
  ),
  constructor = function(expr, shape, order = "F") {
    expr <- as_expr(expr)
    if (is.numeric(shape) && length(shape) == 1L) {
      shape <- c(as.integer(shape), 1L)
    }
    shape <- as.integer(shape)
    if (length(shape) > 2L) {
      cli_abort("Expressions of dimension greater than 2 are not supported.")
    }

    ## Handle -1 dimension inference
    ## CVXPY SOURCE: reshape.py lines 74-89
    if (any(shape == -1L)) {
      n_neg <- sum(shape == -1L)
      if (n_neg != 1L) {
        cli_abort("Only one dimension can be -1.")
      }
      total_size <- expr_size(expr)
      neg_idx <- which(shape == -1L)
      other_idx <- which(shape != -1L)
      if (length(other_idx) == 0L) {
        shape[neg_idx] <- total_size
      } else {
        specified <- shape[other_idx]
        if (specified <= 0L) {
          cli_abort("Specified dimension must be positive.")
        }
        inferred <- total_size %/% specified
        if (total_size %% specified != 0L) {
          cli_abort("Cannot reshape expression of size {total_size} into shape ({paste(shape, collapse = ', ')}).")
        }
        shape[neg_idx] <- inferred
      }
    }

    if (!is.character(order) || !(order %in% c("F", "C"))) {
      cli_abort("order must be {.val F} or {.val C}.")
    }

    ## Validate same number of elements
    old_size <- expr_size(expr)
    new_size <- as.integer(prod(shape))
    if (old_size != new_size) {
      cli_abort("Invalid reshape dimensions ({paste(shape, collapse = ', ')}): size {new_size} does not match expression size {old_size}.")
    }

    new_object(S7_object(),
      id    = next_expr_id(),
      .cache = new.env(parent = emptyenv()),
      args  = list(expr),
      shape = shape,
      order = order
    )
  }
)

# -- shape_from_args --------------------------------------------------
## CVXPY SOURCE: reshape.py lines 117-120

method(shape_from_args, Reshape) <- function(x) x@shape

# -- sign_from_args ---------------------------------------------------
## Inherits from AffAtom: sum_signs(args)

# -- is_atom_log_log_convex / concave ---------------------------------
## CVXPY SOURCE: reshape.py lines 91-99

method(is_atom_log_log_convex, Reshape) <- function(x) TRUE
method(is_atom_log_log_concave, Reshape) <- function(x) TRUE

# -- get_data ---------------------------------------------------------
## CVXPY SOURCE: reshape.py lines 122-125

method(get_data, Reshape) <- function(x) {
  list(x@shape, x@order)
}

# -- numeric_value ---------------------------------------------------
## CVXPY SOURCE: reshape.py lines 101-105

method(numeric_value, Reshape) <- function(x, values, ...) {
  val <- values[[1L]]
  if (inherits(val, "sparseMatrix")) val <- as.matrix(val)
  if (!is.matrix(val)) val <- as.matrix(val)

  if (x@order == "F") {
    ## Column-major (R default)
    matrix(as.vector(val), nrow = x@shape[1L], ncol = x@shape[2L])
  } else {
    ## Row-major: read elements in C-order, fill target shape row-by-row
    matrix(as.vector(t(val)), nrow = x@shape[1L], ncol = x@shape[2L],
           byrow = TRUE)
  }
}

# -- validate_arguments ----------------------------------------------
## CVXPY SOURCE: reshape.py lines 107-115
## Already validated in constructor; just re-check sizes

method(validate_arguments, Reshape) <- function(x) {
  old_size <- expr_size(x@args[[1L]])
  new_size <- as.integer(prod(x@shape))
  if (old_size != new_size) {
    cli_abort("Invalid reshape dimensions ({paste(x@shape, collapse = ', ')}).")
  }
  invisible(NULL)
}

# -- graph_implementation --------------------------------------------
## CVXPY SOURCE: reshape.py lines 127-155

method(graph_implementation, Reshape) <- function(x, arg_objs, shape, data = NULL, ...) {
  arg <- arg_objs[[1L]]
  order <- data[[2L]]
  if (order == "F") {
    list(reshape_linop(arg, shape), list())
  } else {
    ## C-order: transpose -> reshape(reversed) -> transpose
    arg_t <- transpose_linop(arg)
    if (length(shape) <= 1L) {
      list(reshape_linop(arg_t, shape), list())
    } else {
      result <- reshape_linop(arg_t, rev(shape))
      list(transpose_linop(result), list())
    }
  }
}

# -- expr_name --------------------------------------------------------

method(expr_name, Reshape) <- function(x) {
  sprintf("Reshape(%s, c(%s))", expr_name(x@args[[1L]]),
          paste(x@shape, collapse = ", "))
}

# -- Convenience function ----------------------------------------------

#' Reshape an expression to a new shape
#'
#' @param x An Expression or numeric value.
#' @param dim Integer vector of length 2: the target shape c(nrow, ncol).
#'   A single integer is treated as c(dim, 1). Use -1 to infer a dimension.
#' @param order Character: "F" (column-major, default) or "C" (row-major).
#' @returns A Reshape expression.
#' @export
reshape_expr <- function(x, dim, order = "F") {
  Reshape(x, shape = dim, order = order)
}

Try the CVXR package in your browser

Any scripts or data that you put into this service are public.

CVXR documentation built on March 6, 2026, 9:10 a.m.