diff --git a/.ci/install_debian.sh b/.ci/install_debian.sh
deleted file mode 100644
index f405be9..0000000
--- a/.ci/install_debian.sh
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/sh
-set -e
-
-apt-get update
-
-# noninteractive tzdata ( https://stackoverflow.com/questions/44331836/apt-get-install-tzdata-noninteractive )
-export DEBIAN_FRONTEND=noninteractive
-
-# CI specific packages
-apt-get install -y clang wget unzip build-essential cmake libeigen3-dev git
diff --git a/.ci/install_debian_and_script.sh b/.ci/install_debian_and_script.sh
deleted file mode 100644
index fc44203..0000000
--- a/.ci/install_debian_and_script.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-set -e
-
-DIR=$(dirname "$(readlink -f "$0")")
-
-sh $DIR/install_debian.sh
-sh $DIR/script.sh
diff --git a/.ci/script.sh b/.ci/script.sh
deleted file mode 100644
index cbf69a0..0000000
--- a/.ci/script.sh
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/bin/sh
-set -e
-
-# google test
-wget https://github.com/google/googletest/archive/release-1.8.0.zip
-unzip release-1.8.0.zip
-cd googletest-release-1.8.0
-mkdir build
-cd build
-cmake -G"${TRAVIS_CMAKE_GENERATOR}" -DBUILD_GTEST=ON -DBUILD_SHARED_LIBS=ON ..
-cmake --build . --config ${TRAVIS_BUILD_TYPE} --target install
-cd ../..
-
-# osqp
-git clone --recursive https://github.com/oxfordcontrol/osqp.git
-cd osqp
-mkdir build
-cd build
-cmake -G"${TRAVIS_CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=${TRAVIS_BUILD_TYPE} -DUNITTESTS=OFF ..
-cmake --build . --config ${TRAVIS_BUILD_TYPE} --target install
-cd ../..
-
-# Build, test and install osqp-eigen
-cd $TRAVIS_BUILD_DIR
-mkdir build
-cd build
-cmake -G"${TRAVIS_CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=${TRAVIS_BUILD_TYPE} -DBUILD_TESTING=ON ..
-cmake --build . --config ${TRAVIS_BUILD_TYPE} --target install
-ctest --output-on-failure --build-config ${TRAVIS_BUILD_TYPE}
-
-# Build osqp-eigen example
-cd ../example
-mkdir build
-cd build
-cmake -G"${TRAVIS_CMAKE_GENERATOR}" -DCMAKE_BUILD_TYPE=${TRAVIS_BUILD_TYPE} ..
-cmake --build . --config ${TRAVIS_BUILD_TYPE}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..4c174ec
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,176 @@
+name: C++ CI Workflow
+
+on:
+ push:
+ pull_request:
+ schedule:
+ # * is a special character in YAML so you have to quote this string
+ # Execute a "nightly" build at 2 AM UTC
+ - cron: '0 2 * * *'
+
+env:
+ osqp_TAG: v0.6.0
+ vcpkg_robotology_TAG: v0.0.3
+ Catch2_TAG: v2.12.1
+
+# Test with different operating systems
+jobs:
+ build:
+ name: '[${{ matrix.os }}@${{ matrix.build_type }}]'
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ build_type: [Debug, Release]
+ os: [ubuntu-latest, macOS-latest, windows-latest]
+ fail-fast: false
+
+ # operating system dependences
+ steps:
+ - uses: actions/checkout@master
+
+ # Print environment variables to simplify development and debugging
+ - name: Environment Variables
+ shell: bash
+ run: env
+
+ # ============
+ # DEPENDENCIES
+ # ============
+
+ # Remove apt repos that are known to break from time to time
+ # See https://github.com/actions/virtual-environments/issues/323
+ - name: Remove broken apt repos [Ubuntu]
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done
+
+ - name: Dependencies [Windows]
+ if: matrix.os == 'windows-latest'
+ run: |
+ # To avoid spending a huge time compiling vcpkg dependencies, we download a root that comes precompiled with all the ports that we need
+ choco install -y wget unzip
+ # To avoid problems with non-relocatable packages, we unzip the archive exactly in the same C:/robotology/vcpkg
+ # that has been used to create the pre-compiled archive
+ cd C:/
+ md C:/robotology
+ md C:/robotology/vcpkg
+ wget https://github.com/robotology/robotology-superbuild-dependencies-vcpkg/releases/download/${env:vcpkg_robotology_TAG}/vcpkg-robotology.zip
+ unzip vcpkg-robotology.zip -d C:/robotology/vcpkg
+ # Overwrite the VCPKG_INSTALLATION_ROOT env variable defined by GitHub Actions to point to our vcpkg
+ echo "::set-env name=VCPKG_INSTALLATION_ROOT::C:/robotology/vcpkg"
+
+ # Install Catch2
+ cd C:/robotology/vcpkg
+ ./vcpkg.exe install --triplet x64-windows catch2
+
+ - name: Dependencies [macOS]
+ if: matrix.os == 'macOS-latest'
+ run: |
+ brew install eigen catch2
+
+ - name: Dependencies [Ubuntu]
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ sudo apt-get update
+ sudo apt-get install git build-essential cmake libeigen3-dev valgrind
+
+ - name: Cache Source-based Dependencies
+ id: cache-source-deps
+ uses: actions/cache@v1
+ with:
+ path: ${{ github.workspace }}/install/deps
+ key: source-deps-${{ runner.os }}-vcpkg-robotology-${{ env.vcpkg_robotology_TAG }}-osqp-${{ env.osqp_TAG }}-catch2-${{ env.Catch2_TAG }}
+
+ - name: Source-based Dependencies [Windows]
+ if: steps.cache-source-deps.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
+ shell: bash
+ run: |
+ # osqp
+ cd ${GITHUB_WORKSPACE}
+ git clone --recursive -b ${osqp_TAG} https://github.com/oxfordcontrol/osqp
+ cd osqp
+ mkdir -p build
+ cd build
+ cmake -A x64 -DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake \
+ -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install/deps ..
+
+ cmake --build . --config ${{ matrix.build_type }} --target INSTALL
+
+ - name: Source-based Dependencies [Ubuntu/macOS]
+ if: steps.cache-source-deps.outputs.cache-hit != 'true' && (matrix.os == 'ubuntu-latest' || matrix.os == 'macOS-latest')
+ shell: bash
+ run: |
+ # osqp
+ cd ${GITHUB_WORKSPACE}
+ git clone --recursive -b ${osqp_TAG} https://github.com/oxfordcontrol/osqp
+ cd osqp
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install/deps ..
+ cmake --build . --config ${{ matrix.build_type }} --target install
+
+
+ - name: Source-based Dependencies [Ubuntu]
+ if: steps.cache-source-deps.outputs.cache-hit != 'true' && matrix.os == 'ubuntu-latest'
+ shell: bash
+ run: |
+ git clone -b ${Catch2_TAG} https://github.com/catchorg/Catch2.git
+ cd Catch2
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \
+ -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install/deps \
+ -DBUILD_TESTING=OFF ..
+ cmake --build . --config ${{ matrix.build_type }} --target install
+
+ # ===================
+ # CMAKE-BASED PROJECT
+ # ===================
+
+ - name: Configure [Windows]
+ if: matrix.os == 'windows-latest'
+ shell: bash
+ run: |
+ mkdir -p build
+ cd build
+ cmake -A x64 -DCMAKE_TOOLCHAIN_FILE=${VCPKG_INSTALLATION_ROOT}/scripts/buildsystems/vcpkg.cmake \
+ -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install \
+ -DBUILD_TESTING:BOOL=ON ..
+
+ - name: Configure [Ubuntu]
+ if: matrix.os == 'ubuntu-latest'
+ shell: bash
+ run: |
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \
+ -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install \
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
+ -DBUILD_TESTING:BOOL=ON \
+ -DOSQPEIGEN_RUN_Valgrind_tests:BOOL=ON ..
+
+ - name: Configure [macOS]
+ if: matrix.os == 'macOS-latest'
+ shell: bash
+ run: |
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \
+ -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install \
+ -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
+ -DBUILD_TESTING:BOOL=ON ..
+
+ - name: Build
+ shell: bash
+ run: |
+ cd build
+ export PATH=$PATH:/d/a/osqp-eigen/osqp-eigen/install/bin:/d/a/osqp-eigen/osqp-eigen/install/deps/bin:/c/robotology/vcpkg/installed/x64-windows/bin:/c/robotology/vcpkg/installed/x64-windows/debug/bin
+ cmake --build . --config ${{ matrix.build_type }}
+
+ - name: Test
+ shell: bash
+ run: |
+ cd build
+ export PATH=$PATH:/d/a/osqp-eigen/osqp-eigen/install/bin:/d/a/osqp-eigen/osqp-eigen/install/deps/bin:/c/robotology/vcpkg/installed/x64-windows/bin:/c/robotology/vcpkg/installed/x64-windows/debug/bin
+ ctest --output-on-failure -C ${{ matrix.build_type }} .
diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
new file mode 100644
index 0000000..4e3a13e
--- /dev/null
+++ b/.github/workflows/gh-pages.yml
@@ -0,0 +1,50 @@
+name: GitHub Pages
+
+on:
+ push:
+ branches:
+ - 'master'
+
+jobs:
+ docs:
+ name: "Deploy"
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@master
+ - uses: webfactory/ssh-agent@v0.3.0
+ with:
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_GH_PAGES }}
+ - name: Dependencies
+ run: |
+ sudo apt update
+ sudo apt install -y doxygen graphviz
+
+ - name: Check remote
+ run: git ls-remote --heads --exit-code https://github.com/${{ github.repository }}.git gh-pages
+
+ - name: Configure Git
+ run: |
+ git config --global push.default upstream
+ git config --global user.name "GitHub Actions"
+ git config --global user.email "actions@github.com"
+
+ - name: Clone and rebase
+ run: |
+ cd ${GITHUB_WORKSPACE}
+ git clone git@github.com:${{ github.repository }}.git gh-pages
+ cd gh-pages
+ git checkout gh-pages
+ git rebase master
+
+ - name: Build Doxygen
+ run: |
+ cd ${GITHUB_WORKSPACE}/gh-pages/doxygen
+ doxygen ./generate.txt
+
+ - name: Commit and push
+ run: |
+ cd ${GITHUB_WORKSPACE}/gh-pages
+ git add .
+ git commit --amend --no-edit
+ git push --force-with-lease
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 19a3c66..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,95 +0,0 @@
-dist: trusty
-language: cpp
-services: docker
-
-os: linux
-
-cache:
- directories:
- - $HOME/.ccache
- - $HOME/Library/Caches/Homebrew
-
-stages:
- - test # Default stage with job matrix
- - osx
-
-compiler:
- - gcc
-
-env:
- global:
- - TRAVIS_CMAKE_GENERATOR="Unix Makefiles"
- matrix:
- - TRAVIS_BUILD_TYPE="Release" UBUNTU="xenial"
- - TRAVIS_BUILD_TYPE="Debug" UBUNTU="xenial"
- - TRAVIS_BUILD_TYPE="Release" UBUNTU="bionic"
- - TRAVIS_BUILD_TYPE="Debug" UBUNTU="bionic"
-
-# ===================
-# STAGE: test (linux)
-# ===================
-
-before_script:
- - docker pull ubuntu:$UBUNTU
-
-script:
- - >-
- docker run -it \
- -v $TRAVIS_BUILD_DIR:$TRAVIS_BUILD_DIR \
- -v $HOME/.ccache:$HOME/.ccache \
- -w $TRAVIS_BUILD_DIR \
- --env CC \
- --env CXX \
- --env TRAVIS_BUILD_DIR \
- --env TRAVIS_BUILD_TYPE \
- --env TRAVIS_CMAKE_GENERATOR \
- ubuntu:$UBUNTU \
- sh .ci/install_debian_and_script.sh
-
-# ==========
-# STAGE: osx
-# ==========
-
-stage_osx:
- install: &osx_install
- # Setup ccache
- - brew update
- - brew install ccache
- - export PATH="/usr/local/opt/ccache/libexec:$PATH"
- # Install dependencies
- - brew install eigen pkg-config
- script: &osx_script
- - cd $TRAVIS_BUILD_DIR/.ci
- - sh ./script.sh
-
-# ======================
-# BUILD JOBS FROM STAGES
-# ======================
-
-jobs:
- include:
- # ---------
- # STAGE OSX
- # ---------
- - &osx_template
- stage: osx
- os: osx
- osx_image: xcode9.4
- before_install: skip
- install: *osx_install
- before_script: skip
- script: *osx_script
- after_failure: skip
- after_success: skip
- after_script: skip
- env:
- TRAVIS_CMAKE_GENERATOR="Xcode"
- TRAVIS_BUILD_TYPE="Debug"
- - <<: *osx_template
- compiler: clang
- env:
- TRAVIS_CMAKE_GENERATOR="Unix Makefiles"
- TRAVIS_BUILD_TYPE="Debug"
-
-notifications:
- email: false
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2494459..cb86134 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,10 @@
# CopyPolicy: Released under the terms of the LGPLv2.1 or later
# Set cmake mimimun version
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.5)
+
+project(OsqpEigen
+ VERSION 0.6.1)
# ouptut paths
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}")
@@ -20,14 +23,14 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
option(BUILD_SHARED_LIBS "Build libraries as shared as opposed to static" ON)
-# Enable C++14
+# Disable C and C++ compiler extensions.
+# C/CXX_EXTENSIONS are ON by default to allow the compilers to use extended
+# variants of the C/CXX language.
+# However, this could expose cross-platform bugs in user code or in the headers
+# of third-party dependencies and thus it is strongly suggested to turn
+# extensions off.
+set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS OFF)
-set(CMAKE_CXX_STANDARD 14)
-set(CMAKE_CXX_STANDARD_REQUIRED 11)
-
-project(OsqpEigen
- LANGUAGES CXX
- VERSION 0.6.0)
# add GNU dirs
include(GNUInstallDirs)
@@ -53,12 +56,21 @@ if(NOT CMAKE_CONFIGURATION_TYPES)
endif()
endif()
-# find Eigen
-find_package(Eigen3 REQUIRED)
-include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR})
+option(BUILD_TESTING "Create tests using CMake" OFF)
+include(CTest)
-# add OSQP library
-find_package(osqp REQUIRED)
+# Check OsqpEigen dependencies, find necessary libraries.
+include(OsqpEigenDependencies)
+
+# Set default build type to "Release" in single-config generators
+if(NOT CMAKE_CONFIGURATION_TYPES)
+ if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release" CACHE STRING
+ "Choose the type of build, recommanded options are: Debug or Release" FORCE)
+ endif()
+ set(OSQPEIGEN_BUILD_TYPES "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${OSQPEIGEN_BUILD_TYPES})
+endif()
set(LIBRARY_TARGET_NAME OsqpEigen)
@@ -83,8 +95,7 @@ add_library(${LIBRARY_TARGET_NAME} ${${LIBRARY_TARGET_NAME}_SRC} ${${LIBRARY_TAR
target_include_directories(${LIBRARY_TARGET_NAME} PUBLIC "$"
"$")
-target_link_libraries(${LIBRARY_TARGET_NAME} PRIVATE Eigen3::Eigen)
-target_link_libraries(${LIBRARY_TARGET_NAME} PUBLIC osqp::osqp)
+target_link_libraries(${LIBRARY_TARGET_NAME} PUBLIC osqp::osqp Eigen3::Eigen)
add_library(OsqpEigen::OsqpEigen ALIAS OsqpEigen)
@@ -92,9 +103,11 @@ set_target_properties(${LIBRARY_TARGET_NAME} PROPERTIES
VERSION ${PROJECT_VERSION}
PUBLIC_HEADER "${${LIBRARY_TARGET_NAME}_HDR}")
+target_compile_features(${LIBRARY_TARGET_NAME} PUBLIC cxx_std_14)
+
# List exported CMake package dependencies
set(OSQP_EIGEN_EXPORTED_DEPENDENCIES "")
-list(APPEND OSQP_EIGEN_EXPORTED_DEPENDENCIES osqp)
+list(APPEND OSQP_EIGEN_EXPORTED_DEPENDENCIES osqp "Eigen3 CONFIG")
install(TARGETS ${LIBRARY_TARGET_NAME}
EXPORT ${PROJECT_NAME}
@@ -106,6 +119,7 @@ install(TARGETS ${LIBRARY_TARGET_NAME}
include(InstallBasicPackageFiles)
install_basic_package_files(${PROJECT_NAME}
+ NAMESPACE OsqpEigen::
VERSION ${${PROJECT_NAME}_VERSION}
COMPATIBILITY SameMajorVersion
VARS_PREFIX ${PROJECT_NAME}
@@ -113,12 +127,7 @@ install_basic_package_files(${PROJECT_NAME}
DEPENDENCIES ${OSQP_EIGEN_EXPORTED_DEPENDENCIES})
## Testing
-option(BUILD_TESTING "Create tests using CMake" OFF)
-option(RUN_VALGRIND_TESTS "Run tests with Valgrind" FALSE)
-mark_as_advanced(RUN_VALGRIND_TESTS)
-include(CTest)
-if(BUILD_TESTING)
- add_subdirectory(tests)
-endif()
+include(AddOsqpEigenUnitTest)
+add_subdirectory(tests)
include(AddUninstallTarget)
diff --git a/README.md b/README.md
index 5d4129d..f468394 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,22 @@
-# osqp-eigen
-Simple C++ wrapper for [osqp](http://osqp.readthedocs.io/en/latest/index.html) library.
+
+
osqp-eigen
+
+
+
+
+
+
+
+
-| System | Status |
-| ------------- | :-------------: |
-| Linux / OSX | [![Build Status](https://travis-ci.org/robotology/osqp-eigen.svg?branch=master)](https://travis-ci.org/robotology/osqp-eigen) |
-| Windows | [![Build status](https://ci.appveyor.com/api/projects/status/1uecfmyvxb2dujt9/branch/master?svg=true)](https://ci.appveyor.com/project/robotology/osqp-eigen/branch/master) |
+Simple C++ wrapper for [osqp](http://osqp.readthedocs.io/en/latest/index.html) library.
## Dependeces
- [osqp](http://osqp.readthedocs.io/en/latest/index.html) of course :smile:;
- [Eigen3](http://eigen.tuxfamily.org/index.php?title=Main_Page);
- [cmake](https://cmake.org/);
-- [googletest](https://github.com/google/googletest) (only for testing).
+- [Catch2](https://github.com/catchorg/Catch2) (only for testing).
## Build the library and the application
### Linux / macOs
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index ad07af6..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-version: 1.0.{build}
-
-clone_folder: c:\projects\osqp-eigen
-
-environment:
- Eigen3_DIR: C:/Program Files (x86)/Eigen/lib/cmake/eigen3/
-
-os:
- - Visual Studio 2015
- - Visual Studio 2017
-
-install:
- # Check env variables
- - cmd: echo CMAKE_PREFIX_PATH %CMAKE_PREFIX_PATH%
- - cmd: echo PATH %PATH%
-build:
-
-build_script:
- # download and build osqp
- - cd c:\projects
- - git clone --recursive --depth 1 https://github.com/oxfordcontrol/osqp.git
- - cd osqp
- - md build
- - cd build
- - cmake ..
- - cmake --build . --config Release
- - cmake --build . --config Release --target INSTALL
- # download and install eigen3
- - cd c:\projects
- - hg clone https://bitbucket.org/eigen/eigen
- - cd eigen
- - hg checkout 3.3-beta2
- - md build
- - cd build
- - cmake ..
- - cmake --build . --config Release --target INSTALL
- # compile osqp-eigen
- - cd c:\projects\osqp-eigen
- - md build
- - cd build
- - cmake .. -DEIGEN3_INCLUDE_DIR="C:/Program Files (x86)/Eigen/include/eigen3"
- - cmake --build . --config Release
- - cmake --build . --config Release --target INSTALL
diff --git a/cmake/AddOsqpEigenUnitTest.cmake b/cmake/AddOsqpEigenUnitTest.cmake
new file mode 100644
index 0000000..7cbae44
--- /dev/null
+++ b/cmake/AddOsqpEigenUnitTest.cmake
@@ -0,0 +1,73 @@
+# Copyright (C) 2020 Istituto Italiano di Tecnologia (IIT). All rights reserved.
+# This software may be modified and distributed under the terms of the
+# GNU Lesser General Public License v2.1 or any later version.
+#
+# This software may be modified and distributed under the terms of the
+# BSD-3-Clause license. See the accompanying LICENSE file for details.
+
+osqpeigen_dependent_option(OSQPEIGEN_COMPILE_tests
+ "Compile tests?" ON
+ "OSQPEIGEN_HAS_Catch2;BUILD_TESTING" OFF)
+
+osqpeigen_dependent_option(OSQPEIGEN_RUN_Valgrind_tests
+ "Run Valgrind tests?" OFF
+ "OSQPEIGEN_COMPILE_tests;VALGRIND_FOUND" OFF)
+
+if (OSQPEIGEN_RUN_Valgrind_tests)
+ set(CTEST_MEMORYCHECK_COMMAND ${VALGRIND_PROGRAM})
+ set(MEMORYCHECK_COMMAND ${VALGRIND_PROGRAM})
+ if (APPLE)
+ set(MEMORYCHECK_SUPPRESSIONS "--suppressions=${PROJECT_SOURCE_DIR}/cmake/valgrind-macos.supp")
+ else ()
+ set(MEMORYCHECK_SUPPRESSIONS "")
+ endif ()
+ set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --error-exitcode=1 ${MEMORYCHECK_SUPPRESSIONS}" CACHE STRING "Options to pass to the memory checker")
+ mark_as_advanced(MEMORYCHECK_COMMAND_OPTIONS)
+ set(MEMCHECK_COMMAND_COMPLETE "${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS}")
+ separate_arguments(MEMCHECK_COMMAND_COMPLETE)
+endif()
+
+if (OSQPEIGEN_COMPILE_tests)
+ configure_file(cmake/Catch2Main.cpp.in ${CMAKE_BINARY_DIR}/Testing/Catch2Main.cpp)
+ add_library(CatchTestMain ${CMAKE_BINARY_DIR}/Testing/Catch2Main.cpp)
+ target_link_libraries(CatchTestMain PUBLIC Catch2::Catch2)
+endif()
+
+
+function(add_osqpeigen_test)
+
+ if(OSQPEIGEN_COMPILE_tests)
+
+ set(options)
+ set(oneValueArgs NAME)
+ set(multiValueArgs SOURCES LINKS COMPILE_DEFINITIONS)
+
+ set(prefix "osqp_eigen")
+
+ cmake_parse_arguments(${prefix}
+ "${options}"
+ "${oneValueArgs}"
+ "${multiValueArgs}"
+ ${ARGN})
+
+ set(name ${${prefix}_NAME})
+ set(unit_test_files ${${prefix}_SOURCES})
+
+ set(targetname ${name}UnitTests)
+ add_executable(${targetname}
+ "${unit_test_files}")
+
+ target_link_libraries(${targetname} PRIVATE CatchTestMain ${${prefix}_LINKS})
+ target_compile_definitions(${targetname} PRIVATE CATCH_CONFIG_FAST_COMPILE CATCH_CONFIG_DISABLE_MATCHERS)
+ target_compile_features(${targetname} PUBLIC cxx_std_14)
+
+ add_test(NAME ${targetname} COMMAND ${targetname})
+ target_compile_definitions(${targetname} PRIVATE ${${prefix}_COMPILE_DEFINITIONS})
+
+ if(OSQPEIGEN_RUN_Valgrind_tests)
+ add_test(NAME memcheck_${targetname} COMMAND ${MEMCHECK_COMMAND_COMPLETE} $)
+ endif()
+
+ endif()
+
+endfunction()
diff --git a/cmake/Catch2Main.cpp.in b/cmake/Catch2Main.cpp.in
new file mode 100644
index 0000000..dc9329e
--- /dev/null
+++ b/cmake/Catch2Main.cpp.in
@@ -0,0 +1,9 @@
+/**
+ * @file Catch2Main.cpp(.in)
+ * @authors Stefano Dafarra
+ * @copyright 2020 Istituto Italiano di Tecnologia (IIT). This software may be modified and
+ * distributed under the terms of the GNU Lesser General Public License v2.1 or any later version.
+ */
+
+#define CATCH_CONFIG_MAIN
+#include
diff --git a/cmake/FindEigen3.cmake b/cmake/FindEigen3.cmake
deleted file mode 100644
index aa05f75..0000000
--- a/cmake/FindEigen3.cmake
+++ /dev/null
@@ -1,107 +0,0 @@
-# - Try to find Eigen3 lib
-#
-# This module supports requiring a minimum version, e.g. you can do
-# find_package(Eigen3 3.1.2)
-# to require version 3.1.2 or newer of Eigen3.
-#
-# Once done this will define
-#
-# EIGEN3_FOUND - system has eigen lib with correct version
-# EIGEN3_INCLUDE_DIR - the eigen include directory
-# EIGEN3_VERSION - eigen version
-#
-# and the following imported target:
-#
-# Eigen3::Eigen - The header-only Eigen library
-#
-# This module reads hints about search locations from
-# the following environment variables:
-#
-# EIGEN3_ROOT
-# EIGEN3_ROOT_DIR
-
-# Copyright (c) 2006, 2007 Montel Laurent,
-# Copyright (c) 2008, 2009 Gael Guennebaud,
-# Copyright (c) 2009 Benoit Jacob
-# Redistribution and use is allowed according to the terms of the 2-clause BSD license.
-
-if(NOT Eigen3_FIND_VERSION)
- if(NOT Eigen3_FIND_VERSION_MAJOR)
- set(Eigen3_FIND_VERSION_MAJOR 2)
- endif(NOT Eigen3_FIND_VERSION_MAJOR)
- if(NOT Eigen3_FIND_VERSION_MINOR)
- set(Eigen3_FIND_VERSION_MINOR 91)
- endif(NOT Eigen3_FIND_VERSION_MINOR)
- if(NOT Eigen3_FIND_VERSION_PATCH)
- set(Eigen3_FIND_VERSION_PATCH 0)
- endif(NOT Eigen3_FIND_VERSION_PATCH)
-
- set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}")
-endif(NOT Eigen3_FIND_VERSION)
-
-macro(_eigen3_check_version)
- file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header)
-
- string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}")
- set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}")
- string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}")
- set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}")
- string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}")
- set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}")
-
- set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION})
- if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
- set(EIGEN3_VERSION_OK FALSE)
- else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
- set(EIGEN3_VERSION_OK TRUE)
- endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION})
-
- if(NOT EIGEN3_VERSION_OK)
-
- message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, "
- "but at least version ${Eigen3_FIND_VERSION} is required")
- endif(NOT EIGEN3_VERSION_OK)
-endmacro(_eigen3_check_version)
-
-if (EIGEN3_INCLUDE_DIR)
-
- # in cache already
- _eigen3_check_version()
- set(EIGEN3_FOUND ${EIGEN3_VERSION_OK})
- set(Eigen3_FOUND ${EIGEN3_VERSION_OK})
-
-else (EIGEN3_INCLUDE_DIR)
-
- # search first if an Eigen3Config.cmake is available in the system,
- # if successful this would set EIGEN3_INCLUDE_DIR and the rest of
- # the script will work as usual
- find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET)
-
- if(NOT EIGEN3_INCLUDE_DIR)
- find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library
- HINTS
- ENV EIGEN3_ROOT
- ENV EIGEN3_ROOT_DIR
- PATHS
- ${CMAKE_INSTALL_PREFIX}/include
- ${KDE4_INCLUDE_DIR}
- PATH_SUFFIXES eigen3 eigen
- )
- endif(NOT EIGEN3_INCLUDE_DIR)
-
- if(EIGEN3_INCLUDE_DIR)
- _eigen3_check_version()
- endif(EIGEN3_INCLUDE_DIR)
-
- include(FindPackageHandleStandardArgs)
- find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK)
-
- mark_as_advanced(EIGEN3_INCLUDE_DIR)
-
-endif(EIGEN3_INCLUDE_DIR)
-
-if(EIGEN3_FOUND AND NOT TARGET Eigen3::Eigen)
- add_library(Eigen3::Eigen INTERFACE IMPORTED)
- set_target_properties(Eigen3::Eigen PROPERTIES
- INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_INCLUDE_DIR}")
-endif()
\ No newline at end of file
diff --git a/cmake/OsqpEigenDependencies.cmake b/cmake/OsqpEigenDependencies.cmake
new file mode 100644
index 0000000..53a5561
--- /dev/null
+++ b/cmake/OsqpEigenDependencies.cmake
@@ -0,0 +1,21 @@
+# Copyright (C) 2019 Istituto Italiano di Tecnologia (IIT). All rights reserved.
+# This software may be modified and distributed under the terms of the
+# GNU Lesser General Public License v2.1 or any later version.
+#
+# This software may be modified and distributed under the terms of the
+# BSD-3-Clause license. See the accompanying LICENSE file for details.
+
+include(OsqpEigenFindOptionalDependencies)
+
+#---------------------------------------------
+## Required Dependencies
+find_package(Eigen3 3.2.92 REQUIRED)
+find_package(osqp REQUIRED)
+
+#---------------------------------------------
+## Optional Dependencies
+find_package(Catch2 QUIET)
+checkandset_optional_dependency(Catch2)
+
+find_package(VALGRIND QUIET)
+checkandset_optional_dependency(VALGRIND)
diff --git a/cmake/OsqpEigenFindOptionalDependencies.cmake b/cmake/OsqpEigenFindOptionalDependencies.cmake
new file mode 100644
index 0000000..4bcb6e6
--- /dev/null
+++ b/cmake/OsqpEigenFindOptionalDependencies.cmake
@@ -0,0 +1,115 @@
+# Copyright (C) 2019 Istituto Italiano di Tecnologia (IIT). All rights reserved.
+# This software may be modified and distributed under the terms of the
+# GNU Lesser General Public License v2.1 or any later version.
+#
+# This software may be modified and distributed under the terms of the
+# BSD-3-Clause license. See the accompanying LICENSE file for details.
+
+# This module checks if all the dependencies are installed and if the
+# dependencies to build some parts are satisfied.
+# For every dependency, it creates the following variables:
+#
+# OSQPEIGEN_USE_${Package}: Can be disabled by the user if he doesn't want to use that
+# dependency.
+# OSQPEIGEN_HAS_${Package}: Internal flag. It should be used to check if a part of
+# OSQPEIGEN should be built. It is on if OSQPEIGEN_USE_${Package} is
+# on and either the package was found or will be built.
+# OSQPEIGEN_BUILD_${Package}: Internal flag. Used to check if OSQPEIGEN has to build an
+# external package.
+# OSQPEIGEN_BUILD_DEPS_${Package}: Internal flag. Used to check if dependencies
+# required to build the package are available.
+# OSQPEIGEN_HAS_SYSTEM_${Package}: Internal flag. Used to check if the package is
+# available on the system.
+# OSQPEIGEN_USE_SYSTEM_${Package}: This flag is shown only for packages in the
+# extern folder that were also found on the system
+# (TRUE by default). If this flag is enabled, the
+# system installed library will be used instead of
+# the version shipped within the framework.
+
+
+include(CMakeDependentOption)
+
+# Check if a package is installed and set some cmake variables
+macro(checkandset_optional_dependency package)
+
+ set(PREFIX "OSQPEIGEN")
+
+ string(TOUPPER ${package} PKG)
+
+ # OSQPEIGEN_HAS_SYSTEM_${package}
+ if(${package}_FOUND OR ${PKG}_FOUND)
+ set(${PREFIX}_HAS_SYSTEM_${package} TRUE)
+ else()
+ set(${PREFIX}_HAS_SYSTEM_${package} FALSE)
+ endif()
+
+ # OSQPEIGEN_USE_${package}
+ cmake_dependent_option(${PREFIX}_USE_${package} "Use package ${package}" TRUE
+ ${PREFIX}_HAS_SYSTEM_${package} FALSE)
+ mark_as_advanced(${PREFIX}_USE_${package})
+
+ # OSQPEIGEN_USE_SYSTEM_${package}
+ set(${PREFIX}_USE_SYSTEM_${package} ${${PREFIX}_USE_${package}} CACHE INTERNAL "Use system-installed ${package}, rather than a private copy (recommended)" FORCE)
+ if(NOT "${package}" STREQUAL "${PKG}")
+ unset(${PREFIX}_USE_SYSTEM_${PKG} CACHE)
+ endif()
+
+ # OSQPEIGEN_HAS_${package}
+ if(${${PREFIX}_HAS_SYSTEM_${package}})
+ set(${PREFIX}_HAS_${package} ${${PREFIX}_USE_${package}})
+ else()
+ set(${PREFIX}_HAS_${package} FALSE)
+ endif()
+
+endmacro()
+
+macro(OSQPEIGEN_DEPENDENT_OPTION _option _doc _default _deps _force)
+
+ if(DEFINED ${_option})
+ get_property(_option_strings_set CACHE ${_option} PROPERTY STRINGS SET)
+ if(_option_strings_set)
+ # If the user thinks he is smarter than the machine, he deserves an error
+ get_property(_option_strings CACHE ${_option} PROPERTY STRINGS)
+ list(GET _option_strings 0 _option_strings_first)
+ string(REGEX REPLACE ".+\"(.+)\".+" "\\1" _option_strings_first "${_option_strings_first}")
+ list(LENGTH _option_strings _option_strings_length)
+ math(EXPR _option_strings_last_index "${_option_strings_length} - 1")
+ list(GET _option_strings ${_option_strings_last_index} _option_strings_last)
+ if("${${_option}}" STREQUAL "${_option_strings_last}")
+ message(SEND_ERROR "That was a trick, you cannot outsmart me! I will never let you win! ${_option} stays OFF until I say so! \"${_option_strings_first}\" is needed to enable ${_option}. Now stop bothering me, and install your dependencies, if you really want to enable this option.")
+ endif()
+ unset(${_option} CACHE)
+ endif()
+ endif()
+
+ cmake_dependent_option(${_option} "${_doc}" ${_default} "${_deps}" ${_force})
+
+ unset(_missing_deps)
+ foreach(_dep ${_deps})
+ string(REGEX REPLACE " +" ";" _depx "${_dep}")
+ if(NOT (${_depx}))
+ list(APPEND _missing_deps "${_dep}")
+ endif()
+ endforeach()
+
+ if(DEFINED _missing_deps)
+ set(${_option}_disable_reason " (dependencies unsatisfied: \"${_missing_deps}\")")
+ # Set a value that can be visualized on ccmake and on cmake-gui, but
+ # still evaluates to false
+ set(${_option} "OFF - Dependencies unsatisfied: '${_missing_deps}' - ${_option}-NOTFOUND" CACHE STRING "${_option_doc}" FORCE)
+ string(REPLACE ";" "\;" _missing_deps "${_missing_deps}")
+ set_property(CACHE ${_option}
+ PROPERTY STRINGS "OFF - Dependencies unsatisfied: '${_missing_deps}' - ${_option}-NOTFOUND"
+ "OFF - You can try as much as you want, but '${_missing_deps}' is needed to enable ${_option} - ${_option}-NOTFOUND"
+ "OFF - Are you crazy or what? '${_missing_deps}' is needed to enable ${_option} - ${_option}-NOTFOUND"
+ "OFF - Didn't I already tell you that '${_missing_deps}' is needed to enable ${_option}? - ${_option}-NOTFOUND"
+ "OFF - Stop it! - ${_option}-NOTFOUND"
+ "OFF - This is insane! Leave me alone! - ${_option}-NOTFOUND"
+ "ON - All right, you win. The option is enabled. Are you happy now? You just broke the build.")
+ # Set non-cache variable that will override the value in current scope
+ # For parent scopes, the "-NOTFOUND ensures that the variable still
+ # evaluates to false
+ set(${_option} ${_force})
+ endif()
+
+endmacro()
diff --git a/include/OsqpEigen/Settings.hpp b/include/OsqpEigen/Settings.hpp
index 9e33c93..a2dab09 100644
--- a/include/OsqpEigen/Settings.hpp
+++ b/include/OsqpEigen/Settings.hpp
@@ -169,6 +169,12 @@ namespace OsqpEigen
*/
void setWarmStart(const bool warmStart);
+ /**
+ * Set the maximum number of seconds allowed to solve the problem.
+ * @param timeLimit is the time limit in seconds. If 0, then disabled.
+ */
+ void setTimeLimit(const double timeLimit);
+
/**
* Get a pointer to Settings struct.
* @return a const pointer to OSQPSettings struct.
diff --git a/include/OsqpEigen/Solver.hpp b/include/OsqpEigen/Solver.hpp
index 8875ba3..3f483af 100644
--- a/include/OsqpEigen/Solver.hpp
+++ b/include/OsqpEigen/Solver.hpp
@@ -37,6 +37,7 @@ namespace OsqpEigen
Eigen::Matrix m_primalVariables;
Eigen::Matrix m_dualVariables;
Eigen::VectorXd m_solution;
+ Eigen::VectorXd m_dualSolution;
std::vector m_hessianNewIndices;
std::vector m_hessianNewValues;
@@ -123,6 +124,12 @@ namespace OsqpEigen
*/
const Eigen::VectorXd &getSolution();
+ /**
+ * Get the dual optimization problem solution.
+ * @return an Eigen::Vector contating the optimization result.
+ */
+ const Eigen::VectorXd &getDualSolution();
+
/**
* Update the linear part of the cost function (Gradient).
* @param gradient is the Gradient vector.
@@ -224,6 +231,12 @@ namespace OsqpEigen
* @return the pointer to Data object.
*/
const std::unique_ptr& data() const;
+
+ /**
+ * Get the pointer to the OSQP workspace.
+ * @return the pointer to Workspace object.
+ */
+ const std::unique_ptr>& workspace() const;
};
#include
diff --git a/include/OsqpEigen/Solver.tpp b/include/OsqpEigen/Solver.tpp
index 26e1647..d0325eb 100644
--- a/include/OsqpEigen/Solver.tpp
+++ b/include/OsqpEigen/Solver.tpp
@@ -89,7 +89,11 @@ bool OsqpEigen::Solver::updateHessianMatrix(const Eigen::SparseCompressedBase colMajorCopy; //Copying into a new sparse matrix to be sure to use a CSC matrix
+ colMajorCopy = eigenSparseMatrix; //This may perform merory allocation, but this is already the case for allocating the osqpSparseMatrix
// get number of row, columns and nonZeros from Eigen SparseMatrix
- c_int rows = eigenSparseMatrix.rows();
- c_int cols = eigenSparseMatrix.cols();
- c_int numberOfNonZeroCoeff = eigenSparseMatrix.nonZeros();
+ c_int rows = colMajorCopy.rows();
+ c_int cols = colMajorCopy.cols();
+ c_int numberOfNonZeroCoeff = colMajorCopy.nonZeros();
// get innerr and outer index
- const int* innerIndexPtr = eigenSparseMatrix.innerIndexPtr();
- const int* outerIndexPtr = eigenSparseMatrix.outerIndexPtr();
- const int* innerNonZerosPtr = eigenSparseMatrix.innerNonZeroPtr();
-
- // get nonzero values
- auto valuePtr = eigenSparseMatrix.valuePtr();
+ const int* outerIndexPtr = colMajorCopy.outerIndexPtr();
+ const int* innerNonZerosPtr = colMajorCopy.innerNonZeroPtr();
// instantiate csc matrix
// MEMORY ALLOCATION!!
@@ -35,7 +33,7 @@ bool OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(const Eigen::SparseCo
int innerOsqpPosition = 0;
for(int k = 0; k < cols; k++) {
- if (eigenSparseMatrix.isCompressed()) {
+ if (colMajorCopy.isCompressed()) {
osqpSparseMatrix->p[k] = static_cast(outerIndexPtr[k]);
} else {
if (k == 0) {
@@ -44,7 +42,7 @@ bool OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(const Eigen::SparseCo
osqpSparseMatrix->p[k] = osqpSparseMatrix->p[k-1] + innerNonZerosPtr[k-1];
}
}
- for (typename Eigen::SparseCompressedBase::InnerIterator it(eigenSparseMatrix,k); it; ++it) {
+ for (typename Eigen::SparseMatrix::InnerIterator it(colMajorCopy,k); it; ++it) {
osqpSparseMatrix->i[innerOsqpPosition] = static_cast(it.row());
osqpSparseMatrix->x[innerOsqpPosition] = static_cast(it.value());
innerOsqpPosition++;
diff --git a/src/Settings.cpp b/src/Settings.cpp
index 01daca0..8958801 100644
--- a/src/Settings.cpp
+++ b/src/Settings.cpp
@@ -6,6 +6,9 @@
*/
#include
+#include
+
+template inline void unused(Args&&...) {}
OsqpEigen::Settings::Settings()
{
@@ -40,22 +43,47 @@ void OsqpEigen::Settings::setScaling(const int scaling)
void OsqpEigen::Settings::setAdaptiveRho(const bool isRhoStepSizeAdactive)
{
+# if EMBEDDED != 1
m_settings->adaptive_rho = (c_int)isRhoStepSizeAdactive;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setAdaptiveRho] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(isRhoStepSizeAdactive);
+# endif
}
void OsqpEigen::Settings::setAdaptiveRhoInterval(const int rhoInterval)
{
+# if EMBEDDED != 1
m_settings->adaptive_rho_interval = (c_int)rhoInterval;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setAdaptiveRhoInterval] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(rhoInterval);
+# endif
}
void OsqpEigen::Settings::setAdaptiveRhoTolerance(const double adaptiveRhoTolerance)
{
+# if EMBEDDED != 1
m_settings->adaptive_rho_tolerance = (c_float)adaptiveRhoTolerance;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setAdaptiveRhoTolerance] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(adaptiveRhoTolerance);
+# endif
}
void OsqpEigen::Settings::setAdaptiveRhoFraction(const double adaptiveRhoFraction)
{
+# if EMBEDDED != 1
+# ifdef PROFILING
m_settings->adaptive_rho_fraction = (c_float)adaptiveRhoFraction;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setAdaptiveRhoFraction] OSPQ has been set without PROFILING, hence this setting is disabled." << std::endl;
+ unused(adaptiveRhoFraction);
+# endif //ifdef PROFILING
+# else //# if EMBEDDED != 1
+ std::cerr<< "[OsqpEigen::Settings::setAdaptiveRhoFraction] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(adaptiveRhoFraction);
+# endif //# if EMBEDDED != 1
}
void OsqpEigen::Settings::setMaxIteraction(const int maxIteration)
{
@@ -94,22 +122,42 @@ void OsqpEigen::Settings::setLinearSystemSolver(const int linsysSolver)
void OsqpEigen::Settings::setDelta(const double delta)
{
+# ifndef EMBEDDED
m_settings->delta = (c_float)delta;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setDelta] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(delta);
+# endif
}
void OsqpEigen::Settings::setPolish(const bool polish)
{
+# ifndef EMBEDDED
m_settings->polish = (c_int)polish;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setPolish] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(polish);
+# endif
}
void OsqpEigen::Settings::setPolishRefineIter(const int polishRefineIter)
{
+# ifndef EMBEDDED
m_settings->polish_refine_iter = (c_int)polishRefineIter;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setPolishRefineIter] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(polishRefineIter);
+# endif
}
void OsqpEigen::Settings::setVerbosity(const bool isVerbose)
{
+#ifndef EMBEDDED
m_settings->verbose = (c_int)isVerbose;
+#else
+ std::cerr<< "[OsqpEigen::Settings::setVerbosity] OSPQ has been set to EMBEDDED, hence this setting is disabled." << std::endl;
+ unused(isVerbose);
+#endif
}
void OsqpEigen::Settings::setScaledTerimination(const bool scaledTermination)
@@ -127,6 +175,16 @@ void OsqpEigen::Settings::setWarmStart(const bool warmStart)
m_settings->warm_start = (c_int)warmStart;
}
+void OsqpEigen::Settings::setTimeLimit(const double timeLimit)
+{
+# ifdef PROFILING
+ m_settings->time_limit = (c_float)timeLimit;
+# else
+ std::cerr<< "[OsqpEigen::Settings::setTimeLimit] OSPQ has been set without PROFILING, hence this setting is disabled." << std::endl;
+ unused(timeLimit);
+# endif
+}
+
OSQPSettings* const & OsqpEigen::Settings::getSettings() const
{
return m_settings;
diff --git a/src/Solver.cpp b/src/Solver.cpp
index 5426b9f..e1961aa 100644
--- a/src/Solver.cpp
+++ b/src/Solver.cpp
@@ -140,6 +140,15 @@ const Eigen::VectorXd &OsqpEigen::Solver::getSolution()
return m_solution;
}
+const Eigen::VectorXd &OsqpEigen::Solver::getDualSolution()
+{
+ // copy data from an array to Eigen vector
+ c_float* solution = m_workspace->solution->y;
+ m_dualSolution = Eigen::Map(solution, m_workspace->data->m, 1);
+
+ return m_dualSolution;
+}
+
const std::unique_ptr& OsqpEigen::Solver::settings() const
{
return m_settings;
@@ -150,6 +159,11 @@ const std::unique_ptr& OsqpEigen::Solver::data() const
return m_data;
}
+const std::unique_ptr>& OsqpEigen::Solver::workspace() const
+{
+ return m_workspace;
+}
+
bool OsqpEigen::Solver::updateGradient(const Eigen::Ref>& gradient)
{
// check if the dimension of the gradient is correct
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 68f0ab1..7665f2b 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -1,64 +1,29 @@
# Authors: Giulio Romualdi
# CopyPolicy: Released under the terms of the LGPLv2.1 or later
-cmake_minimum_required(VERSION 3.1)
-
-set (CMAKE_CXX_STANDARD 11)
-
-project(OsqpEigen-Test)
-
-if(NOT TARGET OsqpEigen::OsqpEigen)
- find_package(osqp REQUIRED)
- find_package(OsqpEigen REQUIRED)
- find_package(Eigen3 REQUIRED)
- include_directories(SYSTEM ${EIGEN3_INCLUDE_DIR})
-endif()
-
-find_package(GTest REQUIRED)
-include_directories(${GTEST_INCLUDE_DIRS})
-
-# adding support for checking the tests with valgrind
-if(RUN_VALGRIND_TESTS)
- find_package(VALGRIND REQUIRED)
- if(VALGRIND_FOUND)
- set(CTEST_MEMORYCHECK_COMMAND ${VALGRIND_PROGRAM})
- set(MEMORYCHECK_COMMAND ${VALGRIND_PROGRAM})
- if (APPLE)
- set(MEMORYCHECK_SUPPRESSIONS "--suppressions=${PROJECT_SOURCE_DIR}/cmake/valgrind-macos.supp")
- else ()
- set(MEMORYCHECK_SUPPRESSIONS "")
- endif ()
- set(MEMORYCHECK_COMMAND_OPTIONS "--leak-check=full --error-exitcode=1 ${MEMORYCHECK_SUPPRESSIONS}" CACHE STRING "Options to pass to the memory checker")
- mark_as_advanced(MEMORYCHECK_COMMAND_OPTIONS)
- set(MEMCHECK_COMMAND_COMPLETE "${MEMORYCHECK_COMMAND} ${MEMORYCHECK_COMMAND_OPTIONS}")
- separate_arguments(MEMCHECK_COMMAND_COMPLETE)
- endif()
-endif()
-
-macro(add_osqpeigen_test classname)
- set(testsrc ${classname}Test.cpp)
- set(testbinary ${classname}Test)
- set(testname ${classname}Test)
- add_executable(${testbinary} ${testsrc})
- target_link_libraries(${testbinary} PRIVATE OsqpEigen::OsqpEigen osqp::osqp Eigen3::Eigen ${GTEST_LIBRARIES} pthread)
- add_test(NAME ${testname} COMMAND ${testbinary})
-
- if(RUN_VALGRIND_TESTS)
- add_test(NAME memcheck_${testname} COMMAND ${MEMCHECK_COMMAND_COMPLETE} $)
- endif()
-endmacro()
-
-# QPTest
-add_osqpeigen_test(SparseMatrix)
-
-# QPTest
-add_osqpeigen_test(QP)
-
-# Update matrix
-add_osqpeigen_test(UpdateMatrices)
-
-# MPCTest
-add_osqpeigen_test(MPC)
-
-# MPCTest update matrix
-add_osqpeigen_test(MPCUpdateMatrices)
+add_osqpeigen_test(
+ NAME SparseMatrix
+ SOURCES SparseMatrixTest.cpp
+ LINKS OsqpEigen::OsqpEigen)
+
+add_osqpeigen_test(
+ NAME QP
+ SOURCES QPTest.cpp
+ LINKS OsqpEigen::OsqpEigen)
+
+add_osqpeigen_test(
+ NAME UpdateMatrices
+ SOURCES UpdateMatricesTest.cpp
+ LINKS OsqpEigen::OsqpEigen)
+
+add_osqpeigen_test(
+ NAME MPC
+ SOURCES MPCTest.cpp
+ LINKS OsqpEigen::OsqpEigen
+ COMPILE_DEFINITIONS _USE_MATH_DEFINES)
+
+add_osqpeigen_test(
+ NAME MPCUpdateMatrices
+ SOURCES MPCUpdateMatricesTest.cpp
+ LINKS OsqpEigen::OsqpEigen
+ COMPILE_DEFINITIONS _USE_MATH_DEFINES)
diff --git a/tests/MPCTest.cpp b/tests/MPCTest.cpp
index 45cab42..db2a3b9 100644
--- a/tests/MPCTest.cpp
+++ b/tests/MPCTest.cpp
@@ -5,8 +5,8 @@
* @date 2018
*/
-// gtest
-#include
+// Catch2
+#include
// OsqpEigen
#include
@@ -14,8 +14,9 @@
// eigen
#include
-#include
+#include
#include
+#include
// colors
#define ANSI_TXT_GRN "\033[0;32m"
@@ -213,7 +214,7 @@ double getErrorNorm(const Eigen::Matrix &x,
}
-TEST(MPCTest,)
+TEST_CASE("MPCTest")
{
// open the ofstream
std::ofstream dataStream;
@@ -272,14 +273,14 @@ TEST(MPCTest,)
// set the initial data of the QP solver
solver.data()->setNumberOfVariables(12 * (mpcWindow + 1) + 4 * mpcWindow);
solver.data()->setNumberOfConstraints(2 * 12 * (mpcWindow + 1) + 4 * mpcWindow);
- ASSERT_TRUE(solver.data()->setHessianMatrix(hessian));
- ASSERT_TRUE(solver.data()->setGradient(gradient));
- ASSERT_TRUE(solver.data()->setLinearConstraintsMatrix(linearMatrix));
- ASSERT_TRUE(solver.data()->setLowerBound(lowerBound));
- ASSERT_TRUE(solver.data()->setUpperBound(upperBound));
+ REQUIRE(solver.data()->setHessianMatrix(hessian));
+ REQUIRE(solver.data()->setGradient(gradient));
+ REQUIRE(solver.data()->setLinearConstraintsMatrix(linearMatrix));
+ REQUIRE(solver.data()->setLowerBound(lowerBound));
+ REQUIRE(solver.data()->setUpperBound(upperBound));
// instantiate the solver
- ASSERT_TRUE(solver.initSolver());
+ REQUIRE(solver.initSolver());
// controller input and QPSolution vector
Eigen::Vector4d ctr;
@@ -296,7 +297,7 @@ TEST(MPCTest,)
startTime = clock();
// solve the QP problem
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.solve());
// get the controller input
QPSolution = solver.getSolution();
@@ -313,7 +314,7 @@ TEST(MPCTest,)
// update the constraint bound
updateConstraintVectors(x0, lowerBound, upperBound);
- ASSERT_TRUE(solver.updateBounds(lowerBound, upperBound));
+ REQUIRE(solver.updateBounds(lowerBound, upperBound));
endTime = clock();
@@ -326,12 +327,5 @@ TEST(MPCTest,)
std::cout << COUT_GTEST_MGT << "Avarage time = " << avarageTime / numberOfSteps
<< " seconds." << ANSI_TXT_DFT << std::endl;
- ASSERT_LE(getErrorNorm(x0, xRef), 0.001);
-}
-
-
-int main(int argc, char **argv)
-{
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ REQUIRE(getErrorNorm(x0, xRef) <= 0.001);
}
diff --git a/tests/MPCUpdateMatricesTest.cpp b/tests/MPCUpdateMatricesTest.cpp
index b249f93..248f19b 100644
--- a/tests/MPCUpdateMatricesTest.cpp
+++ b/tests/MPCUpdateMatricesTest.cpp
@@ -2,11 +2,11 @@
* @file UpdateMatricesTest.cpp
* @author Giulio Romualdi
* @copyright Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
- * @date 2018
+ * @date 2020
*/
-// gtest
-#include
+// Catch2
+#include
// OsqpEigen
#include
@@ -14,6 +14,7 @@
// eigen
#include
+#include
#include
#include
@@ -183,7 +184,7 @@ void updateConstraintVectors(const Eigen::Matrix &x0,
upperBound.block(0,0,2,1) = -x0;
}
-TEST(MPCTest,)
+TEST_CASE("MPCTest Update matrices")
{
// open the ofstream
std::ofstream dataStream;
@@ -236,14 +237,14 @@ TEST(MPCTest,)
// set the initial data of the QP solver
solver.data()->setNumberOfVariables(2 * (mpcWindow + 1) + 1 * mpcWindow);
solver.data()->setNumberOfConstraints(2 * (mpcWindow + 1));
- ASSERT_TRUE(solver.data()->setHessianMatrix(hessian));
- ASSERT_TRUE(solver.data()->setGradient(gradient));
- ASSERT_TRUE(solver.data()->setLinearConstraintsMatrix(linearMatrix));
- ASSERT_TRUE(solver.data()->setLowerBound(lowerBound));
- ASSERT_TRUE(solver.data()->setUpperBound(upperBound));
+ REQUIRE(solver.data()->setHessianMatrix(hessian));
+ REQUIRE(solver.data()->setGradient(gradient));
+ REQUIRE(solver.data()->setLinearConstraintsMatrix(linearMatrix));
+ REQUIRE(solver.data()->setLowerBound(lowerBound));
+ REQUIRE(solver.data()->setUpperBound(upperBound));
// instantiate the solver
- ASSERT_TRUE(solver.initSolver());
+ REQUIRE(solver.initSolver());
// controller input and QPSolution vector
Eigen::VectorXd ctr;
@@ -262,17 +263,17 @@ TEST(MPCTest,)
setDynamicsMatrices(a, b, c, i * T);
// update the constraint bound
- ASSERT_TRUE(updateHessianMatrix(solver, Q, R, mpcWindow, i));
- ASSERT_TRUE(updateLinearConstraintsMatrix(solver, mpcWindow, i));
+ REQUIRE(updateHessianMatrix(solver, Q, R, mpcWindow, i));
+ REQUIRE(updateLinearConstraintsMatrix(solver, mpcWindow, i));
castMPCToQPGradient(Q, yRef, mpcWindow, i, gradient);
- ASSERT_TRUE(solver.updateGradient(gradient));
+ REQUIRE(solver.updateGradient(gradient));
updateConstraintVectors(x0, lowerBound, upperBound);
- ASSERT_TRUE(solver.updateBounds(lowerBound, upperBound));
+ REQUIRE(solver.updateBounds(lowerBound, upperBound));
// solve the QP problem
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.solve());
// get the controller input
QPSolution = solver.getSolution();
@@ -299,10 +300,3 @@ TEST(MPCTest,)
std::cout << COUT_GTEST_MGT << "Avarage time = " << avarageTime / numberOfSteps
<< " seconds." << ANSI_TXT_DFT << std::endl;
}
-
-
-int main(int argc, char **argv)
-{
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
-}
diff --git a/tests/QPTest.cpp b/tests/QPTest.cpp
index 6d385ec..ccb4703 100644
--- a/tests/QPTest.cpp
+++ b/tests/QPTest.cpp
@@ -2,15 +2,15 @@
* @file QPTest.cpp
* @author Giulio Romualdi
* @copyright Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
- * @date 2018
+ * @date 2020
*/
-// gtest
-#include
+// Catch2
+#include
#include
-TEST(QPProblem, )
+TEST_CASE("QPProblem")
{
Eigen::Matrix2d H;
H << 4, 1,
@@ -37,37 +37,22 @@ TEST(QPProblem, )
OsqpEigen::Solver solver;
solver.settings()->setVerbosity(false);
- ASSERT_FALSE(solver.data()->setHessianMatrix(H_s));
+ REQUIRE_FALSE(solver.data()->setHessianMatrix(H_s));
solver.data()->setNumberOfVariables(2);
solver.data()->setNumberOfConstraints(3);
- ASSERT_TRUE(solver.data()->setHessianMatrix(H_s));
- ASSERT_TRUE(solver.data()->setGradient(gradient));
- ASSERT_TRUE(solver.data()->setLinearConstraintsMatrix(A_s));
- ASSERT_TRUE(solver.data()->setLowerBound(lowerBound));
- ASSERT_TRUE(solver.data()->setUpperBound(upperBound));
+ REQUIRE(solver.data()->setHessianMatrix(H_s));
+ REQUIRE(solver.data()->setGradient(gradient));
+ REQUIRE(solver.data()->setLinearConstraintsMatrix(A_s));
+ REQUIRE(solver.data()->setLowerBound(lowerBound));
+ REQUIRE(solver.data()->setUpperBound(upperBound));
- ASSERT_TRUE(solver.initSolver());
+ REQUIRE(solver.initSolver());
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.solve());
std::cerr << "The solution of the QP problem is" << std::endl;
std::cerr << "[ " << solver.getSolution() << " ]"
<< std::endl;
-};
-
-int main(int argc, char **argv)
-{
- testing::InitGoogleTest(&argc, argv);
- clock_t startTime, endTime;
-
- startTime = clock();
- bool outcome = RUN_ALL_TESTS();
- endTime = clock();
-
- std::cerr << "Total time " << (static_cast(endTime - startTime) / CLOCKS_PER_SEC)
- << " seconds." << std::endl;
-
- return outcome;
}
diff --git a/tests/SparseMatrixTest.cpp b/tests/SparseMatrixTest.cpp
index 9b847e4..e14bf87 100644
--- a/tests/SparseMatrixTest.cpp
+++ b/tests/SparseMatrixTest.cpp
@@ -2,11 +2,11 @@
* @file SparseMatrixTest.cpp
* @author Giulio Romualdi
* @copyright Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
- * @date 2018
+ * @date 2020
*/
-// gtest
-#include
+// Catch2
+#include
#include
#include
@@ -14,7 +14,7 @@
template
bool computeTest(const Eigen::Matrix &mEigen)
{
- Eigen::SparseMatrix matrix, newMatrix;
+ Eigen::SparseMatrix matrix, newMatrix, newMatrixFromCSR;
matrix = mEigen.sparseView();
csc* osqpSparseMatrix = nullptr;
@@ -22,72 +22,84 @@ bool computeTest(const Eigen::Matrix &mEigen)
if(!OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(matrix, osqpSparseMatrix))
return false;
+ Eigen::SparseMatrix csrMatrix;
+ csrMatrix = matrix;
+ csc* otherOsqpSparseMatrix = nullptr;
+ if(!OsqpEigen::SparseMatrixHelper::createOsqpSparseMatrix(csrMatrix, otherOsqpSparseMatrix))
+ return false;
+
if(!OsqpEigen::SparseMatrixHelper::osqpSparseMatrixToEigenSparseMatrix(osqpSparseMatrix, newMatrix))
return false;
+
+ if(!OsqpEigen::SparseMatrixHelper::osqpSparseMatrixToEigenSparseMatrix(otherOsqpSparseMatrix, newMatrixFromCSR))
+ return false;
+
+ if (!newMatrixFromCSR.isApprox(newMatrix))
+ return false;
+
std::vector> tripletListCsc;
if(!OsqpEigen::SparseMatrixHelper::osqpSparseMatrixToTriplets(osqpSparseMatrix, tripletListCsc))
return false;
- for(auto a: tripletListCsc)
- std::cout<> tripletListEigen;
OsqpEigen::SparseMatrixHelper::eigenSparseMatrixToTriplets(matrix, tripletListEigen);
- std::cout<<"***********************************************\n";
- for(auto a: tripletListEigen)
- std::cout< m;
- m << 0, 0, 0, 4,
- 0, 0, 0, 0;
-
- ASSERT_TRUE(computeTest(m));
+ return outcome;
}
-
-int main(int argc, char **argv)
+TEST_CASE("SparseMatrix")
{
- testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
+ SECTION("Data type - double")
+ {
+
+ Eigen::Matrix3d m;
+ m << 0, 1.002311, 0,
+ 0, 0, 0,
+ 0, 0.90835435,0;
+
+ REQUIRE(computeTest(m));
+ }
+
+ SECTION("Data type - float")
+ {
+ Eigen::Matrix3f m;
+ m << 0, 1, 0,
+ 0, 0, 0,
+ 0, 1,0;
+
+ REQUIRE(computeTest(m));
+ }
+
+ SECTION("Data type - int")
+ {
+
+ Eigen::Matrix3i m;
+ m << 0, 1, 0,
+ 0, 0, 0,
+ 0, 1,0;
+
+ REQUIRE(computeTest(m));
+ }
+
+ SECTION("Data type - double")
+ {
+ Eigen::Matrix m;
+ m << 0, 0, 0, 4,
+ 0, 0, 0, 0;
+
+ REQUIRE(computeTest(m));
+ }
}
diff --git a/tests/UpdateMatricesTest.cpp b/tests/UpdateMatricesTest.cpp
index eaf5ceb..8fa219f 100644
--- a/tests/UpdateMatricesTest.cpp
+++ b/tests/UpdateMatricesTest.cpp
@@ -2,11 +2,11 @@
* @file UpdateMatricesTest.cpp
* @author Giulio Romualdi
* @copyright Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
- * @date 2018
+ * @date 2020
*/
-// gtest
-#include
+// Catch2
+#include
// OsqpEigen
#include
@@ -29,7 +29,7 @@ Eigen::Vector3d upperBound;
OsqpEigen::Solver solver;
-TEST(QPProblem, FirstRun)
+TEST_CASE("QPProblem - FirstRun")
{
// hessian matrix
H << 4, 0,
@@ -52,22 +52,22 @@ TEST(QPProblem, FirstRun)
solver.data()->setNumberOfVariables(2);
solver.data()->setNumberOfConstraints(3);
solver.settings()->setScaling(0);
- ASSERT_TRUE(solver.data()->setHessianMatrix(H_s));
- ASSERT_TRUE(solver.data()->setGradient(gradient));
- ASSERT_TRUE(solver.data()->setLinearConstraintsMatrix(A_s));
- ASSERT_TRUE(solver.data()->setLowerBound(lowerBound));
- ASSERT_TRUE(solver.data()->setUpperBound(upperBound));
+ REQUIRE(solver.data()->setHessianMatrix(H_s));
+ REQUIRE(solver.data()->setGradient(gradient));
+ REQUIRE(solver.data()->setLinearConstraintsMatrix(A_s));
+ REQUIRE(solver.data()->setLowerBound(lowerBound));
+ REQUIRE(solver.data()->setUpperBound(upperBound));
- ASSERT_TRUE(solver.initSolver());
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.initSolver());
+ REQUIRE(solver.solve());
auto solution = solver.getSolution();
std::cout << COUT_GTEST_MGT << "Solution [" << solution(0) << " "
<< solution(1) << "]"
<< ANSI_TXT_DFT << std::endl;
-};
+}
-TEST(QPProblem, SparsityConstant)
+TEST_CASE("QPProblem - SparsityConstant")
{
// update hessian matrix
H << 4, 0,
@@ -78,9 +78,9 @@ TEST(QPProblem, SparsityConstant)
0, 1;
A_s = A.sparseView();
- ASSERT_TRUE(solver.updateHessianMatrix(H_s));
- ASSERT_TRUE(solver.updateLinearConstraintsMatrix(A_s));
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.updateHessianMatrix(H_s));
+ REQUIRE(solver.updateLinearConstraintsMatrix(A_s));
+ REQUIRE(solver.solve());
auto solution = solver.getSolution();
std::cout << COUT_GTEST_MGT << "Solution [" << solution(0) << " "
@@ -88,7 +88,7 @@ TEST(QPProblem, SparsityConstant)
<< ANSI_TXT_DFT << std::endl;
};
-TEST(QPProblem, SparsityChange)
+TEST_CASE("QPProblem - SparsityChange")
{
// update hessian matrix
H << 1, 1,
@@ -99,27 +99,12 @@ TEST(QPProblem, SparsityChange)
0, 1;
A_s = A.sparseView();
- ASSERT_TRUE(solver.updateHessianMatrix(H_s));
- ASSERT_TRUE(solver.updateLinearConstraintsMatrix(A_s));
- ASSERT_TRUE(solver.solve());
+ REQUIRE(solver.updateHessianMatrix(H_s));
+ REQUIRE(solver.updateLinearConstraintsMatrix(A_s));
+ REQUIRE(solver.solve());
auto solution = solver.getSolution();
std::cout << COUT_GTEST_MGT << "Solution [" << solution(0) << " "
<< solution(1) << "]"
<< ANSI_TXT_DFT << std::endl;
};
-
-int main(int argc, char **argv)
-{
- testing::InitGoogleTest(&argc, argv);
- clock_t startTime, endTime;
-
- startTime = clock();
- bool outcome = RUN_ALL_TESTS();
- endTime = clock();
-
- std::cerr << "Total time " << (static_cast(endTime - startTime) / CLOCKS_PER_SEC)
- << " seconds." << std::endl;
-
- return outcome;
-}