Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 0.6.3 #35

Merged
merged 5 commits into from
Oct 3, 2023
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
7 changes: 7 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ libosqp.a
^cran-comments\.md$
^CRAN-RELEASE$
^\.github$
.github
src/osqp_sources/CITATION.cff
src/osqp_sources/docs
src/osqp_sources/site
src/osqp_sources/tests
src/osqp_sources/lin_sys/direct/qdldl/qdldl_sources/tests

46 changes: 0 additions & 46 deletions .travis.yml

This file was deleted.

12 changes: 7 additions & 5 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: osqp
Title: Quadratic Programming Solver using the 'OSQP' Library
Version: 0.6.0.8
Date: 2023-01-30
Version: 0.6.3
Date: 2023-10-02
Authors@R: c(
person("Bartolomeo", "Stellato", role = c("aut", "ctb", "cph"),
email = "[email protected]"),
Expand All @@ -16,11 +16,13 @@ Copyright: file COPYRIGHT
Description: Provides bindings to the 'OSQP' solver. The 'OSQP' solver is a numerical optimization package or solving convex quadratic programs written in 'C' and based on the alternating direction method of multipliers. See <arXiv:1711.08013> for details.
License: Apache License 2.0 | file LICENSE
SystemRequirements: C++17
Imports: Rcpp (>= 0.12.14), methods, Matrix, R6
Imports: Rcpp (>= 0.12.14), methods, Matrix (>= 1.6.1), R6
LinkingTo: Rcpp
RoxygenNote: 7.2.1
Collate: 'RcppExports.R' 'osqp-package.R' 'solve.R' 'osqp.R' 'params.R'
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Collate: 'RcppExports.R' 'osqp-package.R' 'sparse.R' 'solve.R' 'osqp.R' 'params.R'
NeedsCompilation: yes
Suggests: testthat
Encoding: UTF-8
BugReports: https://github.com/osqp/osqp-r/issues
URL: https://osqp.org
7 changes: 7 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# Version 0.6.3

* Sync up to version [0.6.3 of OSQP release](https://github.com/osqp/osqp/releases/tag/v0.6.3)
* Fix `params.R` ([issue #18](https://github.com/osqp/osqp-r/issues/18))
* Added `time-limit` settings parameter per [PG's code](https://github.com/osqp/osqp-r/pull/24)
* Added check for lower bounds not exceeding upper bounds ([issue 29](https://github.com/osqp/osqp-r/issues/29))

# Version 0.6.0.8

* Fix prototype of `main` in `qdldl_sources/examplaes/example.c`
Expand Down
28 changes: 16 additions & 12 deletions R/osqp.R
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#' @importFrom Matrix triu
#' @importFrom methods as
#' @importFrom R6 R6Class
#' @param P,A sparse matrices of class dgCMatrix or coercible into such, with P positive semidefinite.
#' @param P,A sparse matrices of class dgCMatrix or coercible into such, with P positive semidefinite. (In the interest of efficiency, only the upper triangular part of P is used)
#' @param q,l,u Numeric vectors, with possibly infinite elements in l and u
#' @param pars list with optimization parameters, conveniently set with the function
#' \code{\link{osqpSettings}}. For \code{osqpObject$UpdateSettings(newPars)} only a subset of the settings
Expand Down Expand Up @@ -64,7 +64,7 @@
#' # Update model and solve again
#' model$Update(q = q_new)
#' res <- model$Solve()
#'
#' @importFrom Matrix sparseMatrix
#' @export
osqp = function(P=NULL, q=NULL, A=NULL, l=NULL, u=NULL, pars = osqpSettings()) {

Expand All @@ -73,14 +73,17 @@ osqp = function(P=NULL, q=NULL, A=NULL, l=NULL, u=NULL, pars = osqpSettings()) {

if (is.null(P))
n = length(q)
else
n = dim(P)[1]

else {
dimP <- dim(P)
n = dim(P)[1L]
if (dimP[2L] != n) stop("P must be symmetric!")
}

if (is.null(P)){
P = sparseMatrix(integer(), integer(), x = numeric(), dims = c(n, n))
P = Matrix::sparseMatrix(integer(), integer(), x = numeric(), dims = c(n, n))
} else {
P = triu(as(P, "dgCMatrix"))
## P = triu(as(P, "dgCMatrix"))
P <- ensure_dtc_matrix(P)
}

if (is.null(q))
Expand All @@ -91,11 +94,12 @@ osqp = function(P=NULL, q=NULL, A=NULL, l=NULL, u=NULL, pars = osqpSettings()) {

if (is.null(A)) {
m = 0
A = sparseMatrix(integer(), integer(), x = numeric(), dims = c(m, n))
A = Matrix::sparseMatrix(integer(), integer(), x = numeric(), dims = c(m, n))
u = l = numeric()
} else {
A = as(A, "dgCMatrix")
m = nrow(A)
## A = as(A, "dgCMatrix")
A <- ensure_dgc_matrix(A)
if (is.null(u))
u = rep_len(Inf, m)
else
Expand All @@ -107,11 +111,11 @@ osqp = function(P=NULL, q=NULL, A=NULL, l=NULL, u=NULL, pars = osqpSettings()) {
l = as.numeric(l)
}

stopifnot(dim(P) == c(n, n),
length(q) == n,
stopifnot(length(q) == n,
dim(A) == c(m, n),
length(l) == m,
length(u) == m)
length(u) == m,
l <= u)

R6Class("osqp_model",
public =
Expand Down
42 changes: 15 additions & 27 deletions R/params.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,27 @@
#' @param adaptive_rho_interval Number of iterations between rho adaptations rho. If 0, it is automatic
#' @param adaptive_rho_tolerance Tolerance X for adapting rho. The new rho has to be X times larger or 1/X times smaller than the current one to trigger a new factorization
#' @param adaptive_rho_fraction Interval for adapting rho (fraction of the setup time)
#' @param time_limit run time limit with 0 indicating no limit
#' @export
osqpSettings = function(rho = 0.1, sigma = 1e-06, max_iter = 4000L, eps_abs = 0.001,
eps_rel = 0.001, eps_prim_inf = 1e-04, eps_dual_inf = 1e-04,
alpha = 1.6, linsys_solver = c(QDLDL_SOLVER=0L),
delta = 1e-06, polish = FALSE, polish_refine_iter = 3L, verbose = TRUE,
scaled_termination = FALSE, check_termination = 25L, warm_start = TRUE,
scaling = 10L, adaptive_rho = 1L, adaptive_rho_interval = 0L,
adaptive_rho_tolerance = 5, adaptive_rho_fraction = 0.4) {
inpars = as.list(match.call())[-1]
pars = sapply(simplify = FALSE, USE.NAMES = TRUE, names(inpars), function(nm) {
checkpar(inpars[[nm]], defaultOsqpSettings[[nm]])
})
pars
}



defaultOsqpSettings = list(rho = 0.1, sigma = 1e-06, max_iter = 4000L, eps_abs = 0.001,
eps_rel = 0.001, eps_prim_inf = 1e-04, eps_dual_inf = 1e-04,
alpha = 1.6, linsys_solver = c(QDLDL_SOLVER=0L),
delta = 1e-06, polish = FALSE, polish_refine_iter = 3L, verbose = TRUE,
scaled_termination = FALSE, check_termination = 25L, warm_start = TRUE,
scaling = 10L, adaptive_rho = 1L, adaptive_rho_interval = 0L,
adaptive_rho_tolerance = 5, adaptive_rho_fraction = 0.4)


checkpar = function(l, r) {
adaptive_rho_tolerance = 5, adaptive_rho_fraction = 0.4, time_limit = 0.0) {
given_args <- as.list(environment()) ## all params with current values
call_arg_names <- names(match.call()[-1]) ## lose the function name at index 1
default_args <- formals() ## this is the default list of arg values
given_args <- given_args[call_arg_names] ## restrict to specified args

l = switch(typeof(r),
integer=as.integer(l),
double=as.numeric(l),
logical=as.logical(l))
if(length(l) != 1 || is.na(l))
return (r)
l
for (name in call_arg_names) {
given <- given_args[[name]]
if (length(given) != 1 || is.na(given)) {
given_args[[name]] <- default_args[[name]] ## force default
} else {
storage.mode(given_args[[name]]) <- storage.mode(eval(default_args[[name]])) #eval default arg
}
}
given_args
}
57 changes: 57 additions & 0 deletions R/sparse.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
## Created by @naras to address coercion considerations with Matrix package
##
## Inspired by Rsymphony package from
## Kurt Hornik and Stefan Theussl and Reinhard Harter
##

#' Ensure that a matrix is column-sparse format (class `dgCMatrix`)
#' @param m a C sparse matrix or coercible object such as a matrix or simple triplet matrix
#' @return a sparse matrix of class `dgCMatrix`
#' @importFrom Matrix sparseMatrix
#' @importFrom methods as
#' @noRd
ensure_dgc_matrix <- function(m) {
if (inherits(m, "dgCMatrix")) {
m
} else if (inherits(m, "matrix")) {
Matrix::.m2sparse(m, "dgCMatrix")
} else if(inherits(m, "simple_triplet_matrix")) { ## e.g. from package slam
## The matrix method assumes that indices for non-zero entries are
## in row-major order, but the simple_triplet_matrix() constructor
## currently does not canonicalize accordingly ...
ind <- order(m$j, m$i)
Matrix::sparseMatrix(p = c(0L, cumsum(tabulate(m$j[ind], m$ncol))),
i = m$i[ind] - 1L,
values = m$v[ind], dims = dim(m), index1 = FALSE)
} else {
## Resort to brute force
as(as(as(m, "CsparseMatrix"), "generalMatrix"), "dMatrix")
}
}

#' Ensure that a matrix is column-sparse format upper triangular (class `dtCMatrix`)
#' @param m a C sparse upper triangular matrix or coercible object such as a matrix or simple triplet matrix
#' @return a sparse matrix of class `dgCMatrix`
#' @importFrom Matrix sparseMatrix triu
#' @importFrom methods as
#' @noRd
ensure_dtc_matrix <- function(m) {
if (inherits(m, "dgCMatrix")) {
m
} else if (inherits(m, "matrix")) {
Matrix::.m2sparse(m, "dtCMatrix")
} else if(inherits(m, "simple_triplet_matrix")) { ## e.g. from package slam
ind <- which(m$i <= m$j)
x <- list(i = m$i[ind] + 1L, j = m$j[ind] + 1L) ##make it 1-based
values <- m$v[ind]
ind <- order(x$j, x$i) ## may not be needed
Matrix::sparseMatrix(p = c(0L, cumsum(tabulate(x$j[ind], m$ncol))),
i = x$i[ind] - 1L,
values = values,
dims = dim(x), index1 = FALSE,
triangular = TRUE)
} else {
## Resort to brute force
Matrix::triu(as(as(as(m, "CsparseMatrix"), "generalMatrix"), "dMatrix"))
}
}
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
# R interface for OSQP

<!-- [![image](https://travis-ci.org/oxfordcontrol/osqp-r.svg?branch=master)](https://travis-ci.org/oxfordcontrol/osqp-r) -->

<!-- [![image](https://ci.appveyor.com/api/projects/status/bx1navxa474nhlpd/branch/master?svg=true)](https://ci.appveyor.com/project/goulart-paul/osqp-r/branch/master) -->

<!-- badges: start -->
[![R-CMD-check](https://github.com/osqp/osqp-r/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/osqp/osqp-r/actions/workflows/R-CMD-check.yaml)
[![CRAN\_Status\_Badge](https://www.r-pkg.org/badges/version/osqp)](https://cran.r-project.org/package=osqp)
[![](https://cranlogs.r-pkg.org/badges/osqp)](https://CRAN.R-project.org/package=osqp)
<!-- badges: end -->

Provides R-bindings to [OSQP](https://osqp.org/): the Operator
Expand Down
2 changes: 1 addition & 1 deletion configure
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ LDFLAGS=`"${R_HOME}/bin/R" CMD config LDFLAGS`

if [ -x "$(command -v cmake)" ]; then
echo "Making fixes to osqp_sources for CRAN"
(cd src && ${R_HOME}/bin/Rscript ../inst/26ce409b_fixes/make_fixes.R)
(cd src && ${R_HOME}/bin/Rscript ../inst/58f00bd_fixes/make_fixes.R)
echo "-- Trying to build libosqp.a via cmake ..."
cd src/osqp_sources
mkdir -p build
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#' @param path relative file path of source
#' @param line_no line numbers to be replaced (integer vector)
#' @param replacement the corresponding replacements (character vector)
#' @param comment_prefix the comment prefix, default C/C++
#' @param comment_prefix the comment prefix, default C/C++
replace_lines <- function(path, line_no, replacement, comment_prefix = "//") {
stopifnot(length(line_no) == length(replacement))
FIXED_FLAG <- paste(comment_prefix, "CRAN FIXES DONE")
Expand All @@ -20,24 +20,24 @@ replace_lines <- function(path, line_no, replacement, comment_prefix = "//") {
}
invisible(TRUE)
}

# Fix pardiso_interface.c
replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_interface.c",
line_no = 35,
replacement = "c_int mkl_get_max_threads(void);")

# Fix pardiso_loader.h
replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_loader.h",
line_no = 22,
replacement = "c_int lh_unload_pardiso(void);")

# Fix pardiso_loader.c
replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_loader.c",
line_no = c(25, 58, 90),
replacement = c("typedef int (*mkl_get_mt_t)(void);",
"c_int mkl_get_max_threads(void) {",
"c_int lh_unload_pardiso(void) {"))


## # Fix pardiso_interface.c
## replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_interface.c",
## line_no = 35,
## replacement = "c_int mkl_get_max_threads(void);")

## # Fix pardiso_loader.h
## replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_loader.h",
## line_no = 22,
## replacement = "c_int lh_unload_pardiso(void);")

## # Fix pardiso_loader.c
## replace_lines(path = "osqp_sources/lin_sys/direct/pardiso/pardiso_loader.c",
## line_no = c(25, 58, 90),
## replacement = c("typedef int (*mkl_get_mt_t)(void);",
## "c_int mkl_get_max_threads(void) {",
## "c_int lh_unload_pardiso(void) {"))
# Fix example.c
replace_lines(path = "osqp_sources/lin_sys/direct/qdldl/qdldl_sources/examples/example.c",
line_no = 17,
Expand All @@ -47,7 +47,13 @@ replace_lines(path = "osqp_sources/lin_sys/direct/qdldl/qdldl_sources/examples/e
file.rename(from = "osqp_sources/include/proj.h", to = "osqp_sources/include/osqp_proj.h")

## Fix CMakeLists.txt
replace_lines("osqp_sources/include/CMakeLists.txt", 11, ' "${CMAKE_CURRENT_SOURCE_DIR}/osqp_proj.h"',
replace_lines("osqp_sources/CMakeLists.txt", 2, 'cmake_minimum_required (VERSION 3.5)',
comment_prefix = "#")

replace_lines("osqp_sources/include/CMakeLists.txt", 12, ' "${CMAKE_CURRENT_SOURCE_DIR}/osqp_proj.h"',
comment_prefix = "#")

replace_lines("osqp_sources/lin_sys/direct/qdldl/qdldl_sources/CMakeLists.txt", 2, 'cmake_minimum_required (VERSION 3.5)',
comment_prefix = "#")

## Fix auxil.c
Expand Down
7 changes: 3 additions & 4 deletions man/osqp.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading