R/225_reductions_discrete2mixedint_valinvec2mixedint.R

Defines functions .finite_set_canon .exprval_in_vec_eq .exprval_in_vec_ineq

#####
## DO NOT EDIT THIS FILE!! EDIT THE SOURCE INSTEAD: rsrc_tree/reductions/discrete2mixedint/valinvec2mixedint.R
#####

## CVXPY SOURCE: reductions/discrete2mixedint/valinvec2mixedint.py
## Valinvec2mixedint -- canonicalize FiniteSet constraints to MIP form
##
## Two formulations:
## 1. Inequality form: sorted differences + binary ordering variables
## 2. Equality form (default): one-hot binary indicators (MOSEK cookbook)


# -- Inequality formulation --------------------------------------
## CVXPY SOURCE: valinvec2mixedint.py lines 27-41
.exprval_in_vec_ineq <- function(expr_flat, vec_val) {
  n_entries <- expr_size(expr_flat)

  vec_sorted <- sort(vec_val)
  d <- diff(vec_sorted)
  len_d <- length(d)

  ## repeated_d: (n_entries, len_d) matrix -- each row is d
  repeated_d <- matrix(rep(d, each = n_entries), nrow = n_entries, ncol = len_d)
  z <- Variable(c(n_entries, len_d), boolean = TRUE)

  ## main: expr == vec_sorted[1] + sum_per_row(d * z)
  main_con <- expr_flat == vec_sorted[1L] + sum_entries(multiply(Constant(repeated_d), z), axis = 1L)

  ## aux: z[, 2:] <= z[, 1:(end-1)] (ordering constraint)
  if (len_d > 1L) {
    aux_cons <- list(z[, 2:len_d] <= z[, 1:(len_d - 1L)])
  } else {
    aux_cons <- list()
  }

  list(main_con = main_con, aux_cons = aux_cons)
}

# -- Equality formulation ----------------------------------------
## CVXPY SOURCE: valinvec2mixedint.py lines 44-54
## Reference: https://docs.mosek.com/modeling-cookbook/mio.html#fixed-set-of-values
.exprval_in_vec_eq <- function(expr_flat, vec_val) {
  n_entries <- expr_size(expr_flat)
  len_vec <- length(vec_val)

  ## repeated_vec: (n_entries, len_vec) matrix -- each row is vec_val
  repeated_vec <- matrix(rep(vec_val, each = n_entries), nrow = n_entries, ncol = len_vec)
  z <- Variable(c(n_entries, len_vec), boolean = TRUE)

  ## main: sum_per_row(vec * z) == expr
  main_con <- sum_entries(multiply(Constant(repeated_vec), z), axis = 1L) == expr_flat

  ## aux: sum_per_row(z) == 1 (exactly one selected)
  aux_cons <- list(sum_entries(z, axis = 1L) == 1)

  list(main_con = main_con, aux_cons = aux_cons)
}

# -- Canonicalizer -----------------------------------------------
## CVXPY SOURCE: valinvec2mixedint.py lines 64-73
.finite_set_canon <- function(expr, args, ...) {
  vec_val <- as.numeric(value(expr@vec))
  ## Deduplicate
  vec_val <- unique(vec_val)

  if (length(vec_val) == 1L) {
    ## Single element: simple equality
    return(list(expr@expre == vec_val[1L], list()))
  }

  ## Flatten expression
  expr_flat <- Reshape(expr@expre, c(expr_size(expr@expre), 1L))

  ## Choose formulation
  formulation_fn <- if (expr@.ineq_form) .exprval_in_vec_ineq else .exprval_in_vec_eq
  result <- formulation_fn(expr_flat, vec_val)

  list(result$main_con, result$aux_cons)
}

# -- Valinvec2mixedint class -------------------------------------
## CVXPY SOURCE: valinvec2mixedint.py lines 76-87

## Register FiniteSet canonicalizer via S7 dispatch.
## FiniteSet is NOT a DCP atom, so has_dcp_canon remains FALSE.
method(dcp_canonicalize, FiniteSet) <- .finite_set_canon

Valinvec2mixedint <- new_class("Valinvec2mixedint", parent = Canonicalization, package = "CVXR",
  constructor = function(problem = NULL) {
    id <- as.integer(next_expr_id())
    new_object(S7_object(),
      id             = id,
      .cache         = new.env(parent = emptyenv()),
      problem        = problem
    )
  }
)

## accepts: TRUE if any FiniteSet constraint present
## CVXPY SOURCE: valinvec2mixedint.py lines 77-78
method(reduction_accepts, Valinvec2mixedint) <- function(x, problem, ...) {
  any(vapply(problem@constraints, function(c) S7_inherits(c, FiniteSet), logical(1)))
}

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.