From faa4fdf630196d0ff07a21c5812d2a9cbd45b998 Mon Sep 17 00:00:00 2001 From: andreadelprete <andre.delprete@gmail.com> Date: Wed, 28 Oct 2015 19:42:15 +0100 Subject: [PATCH] Implement LP solver using coin-or LP. Add in test_LP_solver some small LP tests. Implement primal and dual LP algorithm to compute robustness of a given com position. Test algorithm against PP: primal works always, while dual works only sometimes (need to investigate). --- CMakeLists.txt | 3 + cmake2/FindCLP.cmake | 26 ++ include/robust-equilibrium-lib/config.hh | 5 + include/robust-equilibrium-lib/logger.hh | 175 ++++++++ .../solver_LP_abstract.hh | 50 ++- .../robust-equilibrium-lib/solver_LP_clp.hh | 58 +++ .../solver_LP_qpoases.hh | 4 + .../static_equilibrium.hh | 9 +- include/robust-equilibrium-lib/util.hh | 5 +- src/CMakeLists.txt | 9 +- src/logger.cpp | 118 +++++ src/solver_LP_abstract.cpp | 27 ++ src/solver_LP_clp.cpp | 103 +++++ src/solver_LP_qpoases.cpp | 4 + src/static_equilibrium.cpp | 139 +++++- src/util.cpp | 77 +--- test/CMakeLists.txt | 1 + test/test_LP_solvers.cpp | 424 +++++++++++++++++- test/test_static_equilibrium.cpp | 62 ++- 19 files changed, 1189 insertions(+), 110 deletions(-) create mode 100644 cmake2/FindCLP.cmake create mode 100644 include/robust-equilibrium-lib/logger.hh create mode 100644 include/robust-equilibrium-lib/solver_LP_clp.hh create mode 100644 src/logger.cpp create mode 100644 src/solver_LP_abstract.cpp create mode 100644 src/solver_LP_clp.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 56a2770..9569217 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,8 +31,10 @@ MESSAGE( STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS} ) SET(${PROJECT_NAME}_HEADERS include/robust-equilibrium-lib/config.hh include/robust-equilibrium-lib/util.hh + include/robust-equilibrium-lib/logger.hh include/robust-equilibrium-lib/solver_LP_abstract.hh include/robust-equilibrium-lib/solver_LP_qpoases.hh + include/robust-equilibrium-lib/solver_LP_clp.hh include/robust-equilibrium-lib/static_equilibrium.hh ) @@ -45,6 +47,7 @@ else() endif() find_package(CDD REQUIRED) +find_package(CLP REQUIRED) add_subdirectory (src) add_subdirectory (test) diff --git a/cmake2/FindCLP.cmake b/cmake2/FindCLP.cmake new file mode 100644 index 0000000..c8f2549 --- /dev/null +++ b/cmake2/FindCLP.cmake @@ -0,0 +1,26 @@ +# - Try to find libcdd +# Once done this will define +# CDD_FOUND - System has CDD +# CDD_INCLUDE_DIRS - The CDD include directories +# CDD_LIBRARIES - The libraries needed to use CDD +# CDD_DEFINITIONS - Compiler switches required for using CDD + +# /usr/include/coin, /usr/lib/libClp.so + +find_path(CLP_INCLUDE_DIR coin/ClpSimplex.hpp + HINTS ${CLP_INCLUDEDIR} + PATH_SUFFIXES CLP ) + +find_library(CLP_LIBRARY NAMES libclp + HINTS ${CLP_LIBDIR} ${CLP_LIBRARY_DIRS} ) + +set(CLP_LIBRARIES ${CLP_LIBRARY} ) +set(CLP_INCLUDE_DIRS ${CLP_INCLUDE_DIR} ) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set CDD_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(CLP DEFAULT_MSG + CLP_LIBRARY CLP_INCLUDE_DIR) + +mark_as_advanced(CLP_INCLUDE_DIR CLP_LIBRARY ) diff --git a/include/robust-equilibrium-lib/config.hh b/include/robust-equilibrium-lib/config.hh index 850e82d..6f042ad 100644 --- a/include/robust-equilibrium-lib/config.hh +++ b/include/robust-equilibrium-lib/config.hh @@ -1,3 +1,8 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #ifndef _ROBUST_EQUILIBRIUM_LIB_CONFIG_HH #define _ROBUST_EQUILIBRIUM_LIB_CONFIG_HH diff --git a/include/robust-equilibrium-lib/logger.hh b/include/robust-equilibrium-lib/logger.hh new file mode 100644 index 0000000..98180c8 --- /dev/null +++ b/include/robust-equilibrium-lib/logger.hh @@ -0,0 +1,175 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#ifndef __robust_equilibrium_lib_logger_H__ +#define __robust_equilibrium_lib_logger_H__ + +/* --------------------------------------------------------------------- */ +/* --- INCLUDE --------------------------------------------------------- */ +/* --------------------------------------------------------------------- */ + +#include <robust-equilibrium-lib/config.hh> +#include <sstream> +#include <map> +#include <initializer_list> +#include "boost/assign.hpp" + +namespace robust_equilibrium +{ + + //#define LOGGER_VERBOSITY_INFO_WARNING_ERROR +#define LOGGER_VERBOSITY_ALL + +#define SEND_MSG(msg,type) getLogger().sendMsg(msg,type,__FILE__,__LINE__) + +#ifdef LOGGER_VERBOSITY_ERROR +#define SEND_DEBUG_MSG(msg) +#define SEND_INFO_MSG(msg) +#define SEND_WARNING_MSG(msg) +#define SEND_ERROR_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR) +#define SEND_DEBUG_STREAM_MSG(msg) +#define SEND_INFO_STREAM_MSG(msg) +#define SEND_WARNING_STREAM_MSG(msg) +#define SEND_ERROR_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR_STREAM) +#endif + +#ifdef LOGGER_VERBOSITY_WARNING_ERROR +#define SEND_DEBUG_MSG(msg) +#define SEND_INFO_MSG(msg) +#define SEND_WARNING_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING) +#define SEND_ERROR_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR) +#define SEND_DEBUG_STREAM_MSG(msg) +#define SEND_INFO_STREAM_MSG(msg)\ +#define SEND_WARNING_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING_STREAM) +#define SEND_ERROR_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR_STREAM) +#endif + +#ifdef LOGGER_VERBOSITY_INFO_WARNING_ERROR +#define SEND_DEBUG_MSG(msg) +#define SEND_INFO_MSG(msg) SEND_MSG(msg,MSG_TYPE_INFO) +#define SEND_WARNING_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING) +#define SEND_ERROR_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR) +#define SEND_DEBUG_STREAM_MSG(msg) +#define SEND_INFO_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_INFO_STREAM) +#define SEND_WARNING_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING_STREAM) +#define SEND_ERROR_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR_STREAM) +#endif + +#ifdef LOGGER_VERBOSITY_ALL +#define SEND_DEBUG_MSG(msg) SEND_MSG(msg,MSG_TYPE_DEBUG) +#define SEND_INFO_MSG(msg) SEND_MSG(msg,MSG_TYPE_INFO) +#define SEND_WARNING_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING) +#define SEND_ERROR_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR) +#define SEND_DEBUG_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_DEBUG_STREAM) +#define SEND_INFO_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_INFO_STREAM) +#define SEND_WARNING_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_WARNING_STREAM) +#define SEND_ERROR_STREAM_MSG(msg) SEND_MSG(msg,MSG_TYPE_ERROR_STREAM) +#endif + + /** Enum representing the different kind of messages. + */ + enum ROBUST_EQUILIBRIUM_DLLAPI MsgType + { + MSG_TYPE_DEBUG =0, + MSG_TYPE_INFO =1, + MSG_TYPE_WARNING =2, + MSG_TYPE_ERROR =3, + MSG_TYPE_DEBUG_STREAM =4, + MSG_TYPE_INFO_STREAM =5, + MSG_TYPE_WARNING_STREAM =6, + MSG_TYPE_ERROR_STREAM =7 + }; + + template<typename T> + std::string toString(const T& v) + { + std::stringstream ss; + ss<<v; + return ss.str(); + } + + template<typename T> + std::string toString(const std::vector<T>& v, const std::string separator=", ") + { + std::stringstream ss; + for(int i=0; i<v.size()-1; i++) + ss<<v[i]<<separator; + ss<<v[v.size()-1]; + return ss.str(); + } + + enum ROBUST_EQUILIBRIUM_DLLAPI LoggerVerbosity + { + VERBOSITY_ALL, + VERBOSITY_INFO_WARNING_ERROR, + VERBOSITY_WARNING_ERROR, + VERBOSITY_ERROR, + VERBOSITY_NONE + }; + + /** A simple class for logging messages + */ + class ROBUST_EQUILIBRIUM_DLLAPI Logger + { + public: + + /** Constructor */ + Logger(double timeSample=0.001, double streamPrintPeriod=1.0); + + /** Destructor */ + ~Logger(){} + + /** Method to be called at every control iteration + * to decrement the internal Logger's counter. */ + void countdown(); + + /** Print the specified message on standard output if the verbosity level + * allows it. The file name and the line number are used to identify + * the point where sendMsg is called so that streaming messages are + * printed only every streamPrintPeriod iterations. + */ + void sendMsg(std::string msg, MsgType type, const char* file="", int line=0); + + /** Set the sampling time at which the method countdown() + * is going to be called. */ + bool setTimeSample(double t); + + /** Set the time period for printing of streaming messages. */ + bool setStreamPrintPeriod(double s); + + /** Set the verbosity level of the logger. */ + void setVerbosity(LoggerVerbosity lv); + + protected: + LoggerVerbosity m_lv; /// verbosity of the logger + double m_timeSample; /// specify the period of call of the countdown method + double m_streamPrintPeriod; /// specify the time period of the stream prints + double m_printCountdown; /// every time this is < 0 (i.e. every _streamPrintPeriod sec) print stuff + + /** Pointer to the dynamic structure which holds the collection of streaming messages */ + std::map<std::string, double> m_stream_msg_counters; + + bool isStreamMsg(MsgType m) + { return m==MSG_TYPE_ERROR_STREAM || m==MSG_TYPE_DEBUG_STREAM || m==MSG_TYPE_INFO_STREAM || m==MSG_TYPE_WARNING_STREAM; } + + bool isDebugMsg(MsgType m) + { return m==MSG_TYPE_DEBUG_STREAM || m==MSG_TYPE_DEBUG; } + + bool isInfoMsg(MsgType m) + { return m==MSG_TYPE_INFO_STREAM || m==MSG_TYPE_INFO; } + + bool isWarningMsg(MsgType m) + { return m==MSG_TYPE_WARNING_STREAM || m==MSG_TYPE_WARNING; } + + bool isErrorMsg(MsgType m) + { return m==MSG_TYPE_ERROR_STREAM || m==MSG_TYPE_ERROR; } + }; + + /** Method to get the logger (singleton). */ + Logger& getLogger(); + +} // namespace robust_equilibrium + +#endif // #ifndef __sot_torque_control_trajectory_generators_H__ diff --git a/include/robust-equilibrium-lib/solver_LP_abstract.hh b/include/robust-equilibrium-lib/solver_LP_abstract.hh index 41ad038..9f03bbe 100644 --- a/include/robust-equilibrium-lib/solver_LP_abstract.hh +++ b/include/robust-equilibrium-lib/solver_LP_abstract.hh @@ -1,31 +1,69 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #ifndef ROBUST_EQUILIBRIUM_LIB_SOLVER_LP_ABSTRACT_HH #define ROBUST_EQUILIBRIUM_LIB_SOLVER_LP_ABSTRACT_HH #include <Eigen/Dense> #include <robust-equilibrium-lib/config.hh> +#include <robust-equilibrium-lib/util.hh> namespace robust_equilibrium { enum ROBUST_EQUILIBRIUM_DLLAPI SolverLP { + SOLVER_LP_CLP, SOLVER_LP_QPOASES }; -class ROBUST_EQUILIBRIUM_DLLAPI SolverLPAbstract +enum ROBUST_EQUILIBRIUM_DLLAPI LP_status { -private: - std::string m_name; + LP_STATUS_UNKNOWN=-1, //before solve or if postSolve says not optimal + LP_STATUS_OPTIMAL=0, + LP_STATUS_INFEASIBLE=1, + LP_STATUS_DUAL_INFEASIBLE=2, + LP_STATUS_MAX_ITER_REACHED=3, + LP_STATUS_ERROR=4 +}; +class ROBUST_EQUILIBRIUM_DLLAPI Solver_LP_abstract +{ public: - SolverLPAbstract(std::string name); + Solver_LP_abstract(){} + + static Solver_LP_abstract* getNewSolver(SolverLP solverType); /** Solve the linear program * minimize c' x - * subject to lb <= A x <= ub + * subject to Alb <= A x <= Aub + * lb <= x <= ub */ - virtual bool solve(Cref_vectorX c, Cref_matrixXX A, Cref_vectorX lb, Cref_vectorX ub) = 0; + virtual LP_status solve(Cref_vectorX c, Cref_vectorX lb, Cref_vectorX ub, + Cref_matrixXX A, Cref_vectorX Alb, Cref_vectorX Aub, + Ref_vectorX sol) = 0; + + /** Get the status of the solver. */ + virtual LP_status getStatus() = 0; + + /** Get the objective value of the last solved problem. */ + virtual double getObjectiveValue() = 0; + + /** Get the current maximum number of iterations performed + * by the solver. + */ + unsigned int getMaximumIterations(); + + /** Set the current maximum number of iterations performed + * by the solver. + */ + bool setMaximumIterations(unsigned int maxIter); + + /** Set the maximum time allowed to solve a problem. */ + bool setMaximumTime(double seconds); }; diff --git a/include/robust-equilibrium-lib/solver_LP_clp.hh b/include/robust-equilibrium-lib/solver_LP_clp.hh new file mode 100644 index 0000000..2478a43 --- /dev/null +++ b/include/robust-equilibrium-lib/solver_LP_clp.hh @@ -0,0 +1,58 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#ifndef ROBUST_EQUILIBRIUM_LIB_SOLVER_LP_CLP_HH +#define ROBUST_EQUILIBRIUM_LIB_SOLVER_LP_CLP_HH + +#include <robust-equilibrium-lib/config.hh> +#include <robust-equilibrium-lib/util.hh> +#include <robust-equilibrium-lib/solver_LP_abstract.hh> +#include "ClpSimplex.hpp" + +namespace robust_equilibrium +{ + +class ROBUST_EQUILIBRIUM_DLLAPI Solver_LP_clp: public Solver_LP_abstract +{ +private: + ClpSimplex m_model; + +public: + + Solver_LP_clp(); + + /** Solve the linear program + * minimize c' x + * subject to Alb <= A x <= Aub + * lb <= x <= ub + */ + LP_status solve(Cref_vectorX c, Cref_vectorX lb, Cref_vectorX ub, + Cref_matrixXX A, Cref_vectorX Alb, Cref_vectorX Aub, + Ref_vectorX sol); + + /** Get the status of the solver. */ + LP_status getStatus(); + + /** Get the objective value of the last solved problem. */ + double getObjectiveValue(); + + /** Get the current maximum number of iterations performed + * by the solver. + */ + unsigned int getMaximumIterations(); + + /** Set the current maximum number of iterations performed + * by the solver. + */ + bool setMaximumIterations(unsigned int maxIter); + + /** Set the maximum time allowed to solve a problem. */ + bool setMaximumTime(double seconds); + +}; + +} // end namespace robust_equilibrium + +#endif //ROBUST_EQUILIBRIUM_LIB_SOLVER_LP_CLP_HH diff --git a/include/robust-equilibrium-lib/solver_LP_qpoases.hh b/include/robust-equilibrium-lib/solver_LP_qpoases.hh index e69de29..ed3b145 100644 --- a/include/robust-equilibrium-lib/solver_LP_qpoases.hh +++ b/include/robust-equilibrium-lib/solver_LP_qpoases.hh @@ -0,0 +1,4 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ diff --git a/include/robust-equilibrium-lib/static_equilibrium.hh b/include/robust-equilibrium-lib/static_equilibrium.hh index 78604c8..8c1e94e 100644 --- a/include/robust-equilibrium-lib/static_equilibrium.hh +++ b/include/robust-equilibrium-lib/static_equilibrium.hh @@ -1,3 +1,8 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #ifndef ROBUST_EQUILIBRIUM_LIB_STATIC_EQUILIBRIUM_H #define ROBUST_EQUILIBRIUM_LIB_STATIC_EQUILIBRIUM_H @@ -20,8 +25,10 @@ enum ROBUST_EQUILIBRIUM_DLLAPI StaticEquilibriumAlgorithm class ROBUST_EQUILIBRIUM_DLLAPI StaticEquilibrium { private: + static bool m_is_cdd_initialized; + StaticEquilibriumAlgorithm m_algorithm; - SolverLPAbstract* m_solver; + Solver_LP_abstract* m_solver; unsigned int m_generatorsPerContact; double m_mass; diff --git a/include/robust-equilibrium-lib/util.hh b/include/robust-equilibrium-lib/util.hh index 8131b54..ad048ab 100644 --- a/include/robust-equilibrium-lib/util.hh +++ b/include/robust-equilibrium-lib/util.hh @@ -34,6 +34,7 @@ typedef Eigen::Matrix <value_type, 3, Eigen::Dynamic, Eigen::RowMajor> typedef Eigen::Matrix <value_type, 4, 3, Eigen::RowMajor> Matrix43; typedef Eigen::Matrix <value_type, 6, Eigen::Dynamic, Eigen::RowMajor> Matrix6X; typedef Eigen::Matrix <value_type, 6, 2, Eigen::RowMajor> Matrix62; +typedef Eigen::Matrix <value_type, Eigen::Dynamic, 6, Eigen::RowMajor> MatrixX6; typedef Eigen::Matrix <value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXX; //define Eigen ref if available @@ -89,9 +90,9 @@ dd_MatrixPtr cone_span_eigen_to_cdd(Cref_matrixXX input); Rotation crossMatrix(Cref_vector3 x); -void init_library(); +void init_cdd_library(); -void release_library(); +void release_cdd_library(); void uniform(Cref_matrixXX lower_bounds, Cref_matrixXX upper_bounds, Ref_matrixXX out); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e964689..1be3e52 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories("${SRC_DIR}") include_directories("${INCLUDE_DIR}") include_directories("${EIGEN3_INCLUDE_DIR}") include_directories("${CDD_INCLUDE_DIR}") +include_directories("${CLP_INCLUDE_DIR}") SET(LIBRARY_NAME ${PROJECT_NAME}) @@ -11,10 +12,14 @@ SET(${LIBRARY_NAME}_SOURCES ${INCLUDE_DIR}/robust-equilibrium-lib/config.hh ${INCLUDE_DIR}/robust-equilibrium-lib/solver_LP_abstract.hh ${INCLUDE_DIR}/robust-equilibrium-lib/solver_LP_qpoases.hh + ${INCLUDE_DIR}/robust-equilibrium-lib/solver_LP_clp.hh ${INCLUDE_DIR}/robust-equilibrium-lib/static_equilibrium.hh static_equilibrium.cpp + solver_LP_abstract.cpp solver_LP_qpoases.cpp + solver_LP_clp.cpp util.cpp + logger.cpp ) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src) @@ -24,7 +29,9 @@ if ( MSVC ) SET(CMAKE_DEBUG_POSTFIX d) endif ( MSVC ) -TARGET_LINK_LIBRARIES(robust-equilibrium-lib ${CDD_LIBRARY}) +TARGET_LINK_LIBRARIES(robust-equilibrium-lib ${CDD_LIBRARY} + ${CLP_LIBRARY} + /usr/lib/libCoinUtils.so) SET_TARGET_PROPERTIES(robust-equilibrium-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") SET_TARGET_PROPERTIES(robust-equilibrium-lib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}") diff --git a/src/logger.cpp b/src/logger.cpp new file mode 100644 index 0000000..6f325a6 --- /dev/null +++ b/src/logger.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#ifndef WIN32 +#include <sys/time.h> +#else +#include <Windows.h> +#endif + +#include <stdio.h> +#include <iostream> +#include <iomanip> // std::setprecision +#include <boost/algorithm/string.hpp> +#include <robust-equilibrium-lib/logger.hh> + +namespace robust_equilibrium +{ + using namespace std; + + Logger& getLogger() + { + static Logger l(0.001, 1.0); + return l; + } + + Logger::Logger(double timeSample, double streamPrintPeriod) + : m_timeSample(timeSample), + m_streamPrintPeriod(streamPrintPeriod), + m_printCountdown(0.0) + { +#ifdef LOGGER_VERBOSITY_ERROR + m_lv = VERBOSITY_ERROR; +#endif +#ifdef LOGGER_VERBOSITY_WARNING_ERROR + m_lv = VERBOSITY_WARNING_ERROR; +#endif +#ifdef LOGGER_VERBOSITY_INFO_WARNING_ERROR + m_lv = VERBOSITY_INFO_WARNING_ERROR; +#endif +#ifdef LOGGER_VERBOSITY_ALL + m_lv = VERBOSITY_ALL; +#endif + } + + void Logger::countdown() + { + if(m_printCountdown<0.0) + m_printCountdown = m_streamPrintPeriod; + m_printCountdown -= m_timeSample; + } + + void Logger::sendMsg(string msg, MsgType type, const char* file, int line) + { +// if(m_lv==VERBOSITY_NONE || +// (m_lv==VERBOSITY_ERROR && !isErrorMsg(type)) || +// (m_lv==VERBOSITY_WARNING_ERROR && !(isWarningMsg(type) || isErrorMsg(type))) || +// (m_lv==VERBOSITY_INFO_WARNING_ERROR && isDebugMsg(type))) +// return; + + // if print is allowed by current verbosity level + if(isStreamMsg(type)) + { + // check whether counter already exists + string id = file+toString(line); + map<string,double>::iterator it = m_stream_msg_counters.find(id); + if(it == m_stream_msg_counters.end()) + { + // if counter doesn't exist then add one + m_stream_msg_counters.insert(make_pair(id, 0.0)); + it = m_stream_msg_counters.find(id); + } + + // if counter is greater than 0 then decrement it and do not print + if(it->second>0.0) + { + it->second -= m_timeSample; + return; + } + else // otherwise reset counter and print + it->second = m_streamPrintPeriod; + } + + vector<string> fields; + boost::split(fields, file, boost::is_any_of("/")); + const char* file_name = fields[fields.size()-1].c_str(); + + if(isErrorMsg(type)) + printf("[ERROR %s %d] %s\n", file_name, line, msg.c_str()); + else if(isWarningMsg(type)) + printf("[WARNING %s %d] %s\n", file_name, line, msg.c_str()); + else if(isInfoMsg(type)) + printf("[INFO %s %d] %s\n", file_name, line, msg.c_str()); + else + printf("[DEBUG %s %d] %s\n", file_name, line, msg.c_str()); + + fflush(stdout); // Prints to screen or whatever your standard out is + } + + bool Logger::setTimeSample(double t) + { + if(t<=0.0) + return false; + m_timeSample = t; + return true; + } + + bool Logger::setStreamPrintPeriod(double s) + { + if(s<=0.0) + return false; + m_streamPrintPeriod = s; + return true; + } + +} // namespace robust_equilibrium + diff --git a/src/solver_LP_abstract.cpp b/src/solver_LP_abstract.cpp new file mode 100644 index 0000000..bad6cba --- /dev/null +++ b/src/solver_LP_abstract.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#include <robust-equilibrium-lib/solver_LP_abstract.hh> +#include <robust-equilibrium-lib/solver_LP_clp.hh> +#include <robust-equilibrium-lib/logger.hh> +#include <iostream> + +using namespace std; + +namespace robust_equilibrium +{ + +Solver_LP_abstract* Solver_LP_abstract::getNewSolver(SolverLP solverType) +{ + if(solverType==SOLVER_LP_CLP) + return new Solver_LP_clp(); + + SEND_ERROR_MSG("Specified solver type not recognized: "+toString(solverType)); + return NULL; +} + + + +} // end namespace robust_equilibrium diff --git a/src/solver_LP_clp.cpp b/src/solver_LP_clp.cpp new file mode 100644 index 0000000..cb3a0b8 --- /dev/null +++ b/src/solver_LP_clp.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#include <robust-equilibrium-lib/solver_LP_clp.hh> +#include "CoinBuild.hpp" + +namespace robust_equilibrium +{ + +Solver_LP_clp::Solver_LP_clp(): Solver_LP_abstract() +{ + m_model.setLogLevel(0); +} + +LP_status Solver_LP_clp::solve(Cref_vectorX c, Cref_vectorX lb, Cref_vectorX ub, + Cref_matrixXX A, Cref_vectorX Alb, Cref_vectorX Aub, + Ref_vectorX sol) +{ + int n = (int)c.size(); // number of variables + int m = (int)A.rows(); // number of constraints + assert(lb.size()==n); + assert(ub.size()==n); + assert(A.cols()==n); + assert(Alb.size()==m); + assert(Aub.size()==m); + + m_model.resize(0, n); + int* rowIndex = new int[n]; + + for(int i=0; i<n; i++) + { + m_model.setObjectiveCoefficient(i, c(i)); + m_model.setColumnLower(i, lb(i)); + m_model.setColumnUpper(i, ub(i)); + rowIndex[i] = i; + } + +// m_model.allSlackBasis(); + + // This is not the most efficient way to pass the data to the problem + // but it is the best compromise between efficiency and simplicity. + // We could be more efficient by using CoinPackedMatrix and ClpPackedMatrix + // as shown in the example file "addRows.cpp" + CoinBuild buildObject; + for (int i=0; i<m; i++) + { + buildObject.addRow(n, rowIndex, A.row(i).data(), Alb(i), Aub(i)); + } + m_model.addRows(buildObject); + + // solve the problem + m_model.primal(); +// m_model.dual(); + + if(m_model.isProvenOptimal()) + { + const double *solution = m_model.getColSolution(); + for(int i=0; i<n; i++) + sol(i) = solution[i]; + } + + return getStatus(); +} + +LP_status Solver_LP_clp::getStatus() +{ + int status = m_model.status(); + if(status<5) + return (LP_status)status; + return LP_STATUS_ERROR; +} + +double Solver_LP_clp::getObjectiveValue() +{ + return m_model.objectiveValue(); +} + +unsigned int Solver_LP_clp::getMaximumIterations() +{ + int integerValue; + m_model.getIntParam(ClpMaxNumIteration, integerValue); + return integerValue; +} + +bool Solver_LP_clp::setMaximumIterations(unsigned int maxIter) +{ + if(maxIter==0) + return false; + m_model.setMaximumIterations(maxIter); + return true; +} + +bool Solver_LP_clp::setMaximumTime(double seconds) +{ + if(seconds<=0.0) + return false; + m_model.setMaximumSeconds(seconds); + return true; +} + +} // end namespace robust_equilibrium diff --git a/src/solver_LP_qpoases.cpp b/src/solver_LP_qpoases.cpp index e69de29..ed3b145 100644 --- a/src/solver_LP_qpoases.cpp +++ b/src/solver_LP_qpoases.cpp @@ -0,0 +1,4 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ diff --git a/src/static_equilibrium.cpp b/src/static_equilibrium.cpp index de73350..3200612 100644 --- a/src/static_equilibrium.cpp +++ b/src/static_equilibrium.cpp @@ -1,4 +1,10 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #include <robust-equilibrium-lib/static_equilibrium.hh> +#include <robust-equilibrium-lib/logger.hh> #include <iostream> #include <vector> @@ -7,9 +13,18 @@ using namespace std; namespace robust_equilibrium { +bool StaticEquilibrium::m_is_cdd_initialized = false; StaticEquilibrium::StaticEquilibrium(double mass, unsigned int generatorsPerContact, SolverLP solver_type) { + if(!m_is_cdd_initialized) + { + init_cdd_library(); + m_is_cdd_initialized = true; + } + + m_solver = Solver_LP_abstract::getNewSolver(solver_type); + m_generatorsPerContact = generatorsPerContact; m_mass = mass; m_gravity.setZero(); @@ -27,6 +42,19 @@ bool StaticEquilibrium::setNewContacts(Cref_matrixX3 contactPoints, Cref_matrixX assert(contactPoints.rows()==contactNormals.rows()); assert(contactPoints.rows()==frictionCoefficients.rows()); + if(alg==STATIC_EQUILIBRIUM_ALGORITHM_IP) + { + SEND_ERROR_MSG("Algorithm IP not implemented yet"); + return false; + } + if(alg==STATIC_EQUILIBRIUM_ALGORITHM_DIP) + { + SEND_ERROR_MSG("Algorithm DIP not implemented yet"); + return false; + } + + m_algorithm = alg; + // compute tangent directions long int c = contactPoints.rows(); int cg = m_generatorsPerContact; @@ -82,25 +110,115 @@ bool StaticEquilibrium::setNewContacts(Cref_matrixX3 contactPoints, Cref_matrixX m_G_centr.block(0,cg*i,6,cg) = m_A.block<6,3>(0,3*i) * m_G.block(0,cg*i,3,cg); // cout<<"G_centr:\n"<<m_G_centr.transpose()<<"\n"; - if(!computePolytopeProjection(m_G_centr)) - return false; - - m_HD = m_H * m_D; - m_Hd = m_H * m_d; + if(m_algorithm==STATIC_EQUILIBRIUM_ALGORITHM_PP) + { + if(!computePolytopeProjection(m_G_centr)) + return false; + m_HD = m_H * m_D; + m_Hd = m_H * m_d; + } return true; } double StaticEquilibrium::computeEquilibriumRobustness(Cref_vector2 com) { - return 0.0; + /*Compute the robustness measure of the equilibrium of a specified CoM position. + The operation amounts to solving the following dual LP: + find v + minimize (d+D*com)' v + subject to G' v >= 0 + 1' G' v = 1 + where + -(d+D c)' v is the robustness measure + c is the CoM position + G is the matrix whose columns are the gravito-inertial wrench generators + */ + if(m_algorithm!=STATIC_EQUILIBRIUM_ALGORITHM_LP) + { + SEND_ERROR_MSG("checkRobustEquilibrium is only implemented for the LP algorithm"); + return false; + } + + double robustness_dual, robustness_primal; + LP_status lpStatus_primal, lpStatus_dual; + long m = m_G_centr.cols(); // number of gravito-inertial wrench generators + Vector6 v; + VectorX b_b0(m+1); + + { + + Vector6 c = m_D*com + m_d; + Vector6 lb = Vector6::Ones()*-1e100; + Vector6 ub = Vector6::Ones()*1e100; + VectorX Alb = VectorX::Zero(m+1); + Alb(m) = 1.0; + VectorX Aub = VectorX::Ones(m+1)*1e100; + Aub(m) = 1.0; + MatrixX6 A(m+1,6); + A.topRows(m) = m_G_centr.transpose(); + A.bottomRows<1>() = (m_G_centr*VectorX::Ones(m)).transpose(); + + lpStatus_dual = m_solver->solve(c, lb, ub, A, Alb, Aub, v); + if(lpStatus_dual==LP_STATUS_OPTIMAL) + robustness_dual = m_solver->getObjectiveValue(); + else + SEND_ERROR_MSG("Dual LP problem could not be solved: "+toString(lpStatus_dual)); + } + + { + /* The operation amounts to solving the following LP: + find b, b0 + minimize -b0 + subject to D c + d <= G b <= D c + d + 0 <= b - b0 <= Inf + where + b are the coefficient of the contact force generators (f = V b) + b0 is the robustness measure + c is the CoM position + G is the matrix whose columns are the gravito-inertial wrench generators + */ + long m = m_G_centr.cols(); // number of gravito-inertial wrench generators + VectorX c = VectorX::Zero(m+1); + c(m) = -1.0; + VectorX lb = -VectorX::Ones(m+1)*1e10; + VectorX ub = VectorX::Ones(m+1)*1e10; + VectorX Alb = VectorX::Zero(6+m); + VectorX Aub = VectorX::Ones(6+m)*1e100; + MatrixXX A = MatrixXX::Zero(6+m, m+1); + Alb.head<6>() = m_D * com + m_d; + Aub.head<6>() = Alb.head<6>(); + A.topLeftCorner(6,m) = m_G_centr; + A.bottomLeftCorner(m,m) = MatrixXX::Identity(m,m); + A.bottomRightCorner(m,1) = -VectorX::Ones(m); + + lpStatus_primal = m_solver->solve(c, lb, ub, A, Alb, Aub, b_b0); + if(lpStatus_primal==LP_STATUS_OPTIMAL) + robustness_primal = -1.0*m_solver->getObjectiveValue(); + else + SEND_ERROR_MSG("Primal LP problem could not be solved: "+toString(lpStatus_primal)); + } + +// if(lpStatus_primal==LP_STATUS_OPTIMAL && lpStatus_dual==LP_STATUS_OPTIMAL) +// { +// if(fabs(robustness_dual-robustness_primal)>1e-3) +// SEND_ERROR_MSG("Primal and dual solutions differ: "+toString(robustness_primal)+" != "+ +// toString(robustness_dual)+" b="+toString(b_b0.transpose())); +// } + + return robustness_primal; } bool StaticEquilibrium::checkRobustEquilibrium(Cref_vector2 com, double e_max) { if(e_max!=0.0) { - std::cerr<<"ERROR checkRobustEquilibrium with e_max!=0 not implemented yet!"; + SEND_ERROR_MSG("checkRobustEquilibrium with e_max!=0 not implemented yet"); + return false; + } + if(m_algorithm!=STATIC_EQUILIBRIUM_ALGORITHM_PP) + { + SEND_ERROR_MSG("checkRobustEquilibrium is only implemented for the PP algorithm"); return false; } @@ -113,11 +231,13 @@ bool StaticEquilibrium::checkRobustEquilibrium(Cref_vector2 com, double e_max) double StaticEquilibrium::findExtremumOverLine(Cref_vector2 a, double b, double e_max) { + SEND_ERROR_MSG("findExtremumOverLine not implemented yet"); return 0.0; } double StaticEquilibrium::findExtremumInDirection(Cref_vector2 direction, double e_max) { + SEND_ERROR_MSG("findExtremumInDirection not implemented yet"); return 0.0; } @@ -128,8 +248,7 @@ bool StaticEquilibrium::computePolytopeProjection(Cref_matrix6X v) dd_PolyhedraPtr H_= dd_DDMatrix2Poly(V, &error); if(error != dd_NoError) { -// if(dd_debug) - cout << ("numerical instability in cddlib. ill formed polytope") << endl; + SEND_ERROR_MSG("numerical instability in cddlib. ill formed polytope"); return false; } @@ -142,7 +261,7 @@ bool StaticEquilibrium::computePolytopeProjection(Cref_matrix6X v) eq_rows.push_back(elem); } int rowsize = (int)b_A->rowsize; -// cout<<"Inequality matrix has "<<rowsize<<" rows and "<<b_A->colsize-1<<" columns\n"; + SEND_DEBUG_MSG("Inequality matrix has "+toString(rowsize)+" rows and "+toString(b_A->colsize-1)+" columns"); m_H.resize(rowsize + eq_rows.size(), (int)b_A->colsize-1); m_h.resize(rowsize + eq_rows.size()); for(int i=0; i < rowsize; ++i) diff --git a/src/util.cpp b/src/util.cpp index 27de0bf..fa1cede 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,74 +1,16 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #ifndef _ROBUST_EQUILIBRIUM_LIB_CONFIG_HH #define _ROBUST_EQUILIBRIUM_LIB_CONFIG_HH -#include <Eigen/Dense> -#include <Eigen/src/Core/util/Macros.h> - -#include "cdd/cddmp.h" -#include "cdd/setoper.h" -#include "cdd/cddtypes.h" -#include "cdd/cdd.h" +#include <robust-equilibrium-lib/util.hh> namespace robust_equilibrium { -//#define USE_FLOAT 1; -#ifdef USE_FLOAT -typedef float value_type; -#else -typedef double value_type; -#endif - -typedef Eigen::Matrix <value_type, 2, 1> Vector2; -typedef Eigen::Matrix <value_type, 1, 2> RVector2; -typedef Eigen::Matrix <value_type, 3, 1> Vector3; -typedef Eigen::Matrix <value_type, 1, 3> RVector3; -typedef Eigen::Matrix <value_type, Eigen::Dynamic, 1> VectorX; -typedef Eigen::Matrix <value_type, 1, Eigen::Dynamic> RVectorX; -typedef Eigen::Matrix <value_type, 3, 3, Eigen::RowMajor> Rotation; -typedef Eigen::Matrix <value_type, 3, 3, Eigen::RowMajor> Matrix3; -typedef Eigen::Matrix <value_type, Eigen::Dynamic, 3, Eigen::RowMajor> MatrixX3; -typedef Eigen::Matrix <value_type, 4, 3, Eigen::RowMajor> Matrix43; -typedef Eigen::Matrix <value_type, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> MatrixXX; - -//define Eigen ref if available -#if EIGEN_VERSION_AT_LEAST(3,2,0) -typedef Eigen::Ref<Vector2> Ref_vector2; -typedef Eigen::Ref<Vector3> Ref_vector3; -typedef Eigen::Ref<VectorX> Ref_vectorX; -typedef Eigen::Ref<Rotation> Ref_rotation; -typedef Eigen::Ref<MatrixX3> Ref_matrixX3; -typedef Eigen::Ref<Matrix43> Ref_matrix43; -typedef Eigen::Ref<MatrixXX> Ref_matrixXX; - -typedef const Eigen::Ref<const Vector2> & Cref_vector2; -typedef const Eigen::Ref<const Vector3> & Cref_vector3; -typedef const Eigen::Ref<const VectorX> & Cref_vectorX; -typedef const Eigen::Ref<const Rotation> & Cref_rotation; -typedef const Eigen::Ref<const MatrixX3> & Cref_matrixX3; -typedef const Eigen::Ref<const Matrix43> & Cref_matrix43; -typedef const Eigen::Ref<const MatrixXX> & Cref_matrixXX; -#else -typedef vector2_t & Ref_vector2; -typedef vector3_t & Ref_vector3; -typedef vector_t & Ref_vector; -typedef rotation_t & Ref_rotation; -typedef T_rotation_t& Ref_matrixX3; -typedef T_rotation_t& Ref_matrix43; -typedef matrix_t & Ref_matrixXX; - -typedef const vector2_t & Cref_vector2; -typedef const vector3_t & Cref_vector3; -typedef const vector_t & Cref_vector; -typedef const rotation_t & Cref_rotation; -typedef const T_rotation_t& Cref_matrixX3; -typedef const T_rotation_t& Cref_matrix43; -typedef const matrix_t & Cref_matrixXX; -#endif - -typedef Eigen::AngleAxis<value_type> angle_axis_t; - - dd_MatrixPtr cone_span_eigen_to_cdd(Cref_matrixXX input) { dd_debug = false; @@ -101,12 +43,13 @@ dd_MatrixPtr cone_span_eigen_to_cdd(Cref_matrixXX input) } -void init_library() +void init_cdd_library() { - dd_set_global_constants();dd_debug = false; + dd_set_global_constants(); + dd_debug = false; } -void release_library() +void release_cdd_library() { //dd_free_global_constants(); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d52109d..c657967 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,6 +4,7 @@ include_directories("${SRC_DIR}") include_directories("${INCLUDE_DIR}") include_directories("${EIGEN3_INCLUDE_DIR}") include_directories("${CDD_INCLUDE_DIR}") +include_directories("${CLP_INCLUDE_DIR}") PROJECT(robust-equilibrium-lib) diff --git a/test/test_LP_solvers.cpp b/test/test_LP_solvers.cpp index d56f6b7..567c464 100644 --- a/test/test_LP_solvers.cpp +++ b/test/test_LP_solvers.cpp @@ -1,5 +1,425 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + +#include "ClpSimplex.hpp" +#include "CoinTime.hpp" +#include "CoinBuild.hpp" +#include "CoinModel.hpp" + +#include <robust-equilibrium-lib/solver_LP_clp.hh> + +#include <iostream> +#include <iomanip> + +using namespace std; +using namespace robust_equilibrium; + +/** Example addRows.cpp */ +void test_addRows() +{ + const int N_ROWS_TO_ADD = 30000; + try + { + // Empty model + ClpSimplex model; + + // Objective - just nonzeros + int objIndex[] = {0, 2}; + double objValue[] = {1.0, 4.0}; + // Upper bounds - as dense vector + double upper[] = {2.0, COIN_DBL_MAX, 4.0}; + + // Create space for 3 columns + model.resize(0, 3); + // Fill in + int i; + // Virtuous way + // First objective + for (i = 0; i < 2; i++) + model.setObjectiveCoefficient(objIndex[i], objValue[i]); + // Now bounds (lower will be zero by default but do again) + for (i = 0; i < 3; i++) { + model.setColumnLower(i, 0.0); + model.setColumnUpper(i, upper[i]); + } + /* + We could also have done in non-virtuous way e.g. + double * objective = model.objective(); + and then set directly + */ + // Faster to add rows all at once - but this is easier to show + // Now add row 1 as >= 2.0 + int row1Index[] = {0, 2}; + double row1Value[] = {1.0, 1.0}; + model.addRow(2, row1Index, row1Value, + 2.0, COIN_DBL_MAX); + // Now add row 2 as == 1.0 + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -5.0, 1.0}; + model.addRow(3, row2Index, row2Value, + 1.0, 1.0); + // solve + model.dual(); + + /* + Adding one row at a time has a significant overhead so let's + try a more complicated but faster way + + First time adding in 10000 rows one by one + */ + model.allSlackBasis(); + ClpSimplex modelSave = model; + double time1 = CoinCpuTime(); + int k; + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -5.0, 1.0}; + model.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + printf("Time for 10000 addRow is %g\n", CoinCpuTime() - time1); + model.dual(); + model = modelSave; + // Now use build + CoinBuild buildObject; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -5.0, 1.0}; + buildObject.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.addRows(buildObject); + printf("Time for 10000 addRow using CoinBuild is %g\n", CoinCpuTime() - time1); + model.dual(); + model = modelSave; + int del[] = {0, 1, 2}; + model.deleteRows(2, del); + // Now use build +-1 + CoinBuild buildObject2; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -1.0, 1.0}; + buildObject2.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.addRows(buildObject2, true); + printf("Time for 10000 addRow using CoinBuild+-1 is %g\n", CoinCpuTime() - time1); + model.dual(); + model = modelSave; + model.deleteRows(2, del); + // Now use build +-1 + CoinModel modelObject2; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -1.0, 1.0}; + modelObject2.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.addRows(modelObject2, true); + printf("Time for 10000 addRow using CoinModel+-1 is %g\n", CoinCpuTime() - time1); + model.dual(); + model = ClpSimplex(); + // Now use build +-1 + CoinModel modelObject3; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -1.0, 1.0}; + modelObject3.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.loadProblem(modelObject3, true); + printf("Time for 10000 addRow using CoinModel load +-1 is %g\n", CoinCpuTime() - time1); + model.writeMps("xx.mps"); + model.dual(); + model = modelSave; + // Now use model + CoinModel modelObject; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -5.0, 1.0}; + modelObject.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.addRows(modelObject); + printf("Time for 10000 addRow using CoinModel is %g\n", CoinCpuTime() - time1); + model.dual(); + model.writeMps("b.mps"); + // Method using least memory - but most complicated + time1 = CoinCpuTime(); + // Assumes we know exact size of model and matrix + // Empty model + ClpSimplex model2; + { + // Create space for 3 columns and 10000 rows + int numberRows = N_ROWS_TO_ADD; + int numberColumns = 3; + // This is fully dense - but would not normally be so + int numberElements = numberRows * numberColumns; + // Arrays will be set to default values + model2.resize(numberRows, numberColumns); + double * elements = new double [numberElements]; + CoinBigIndex * starts = new CoinBigIndex [numberColumns+1]; + int * rows = new int [numberElements];; + int * lengths = new int[numberColumns]; + // Now fill in - totally unsafe but .... + // no need as defaults to 0.0 double * columnLower = model2.columnLower(); + double * columnUpper = model2.columnUpper(); + double * objective = model2.objective(); + double * rowLower = model2.rowLower(); + double * rowUpper = model2.rowUpper(); + // Columns - objective was packed + for (k = 0; k < 2; k++) { + int iColumn = objIndex[k]; + objective[iColumn] = objValue[k]; + } + for (k = 0; k < numberColumns; k++) + columnUpper[k] = upper[k]; + // Rows + for (k = 0; k < numberRows; k++) { + rowLower[k] = 1.0; + rowUpper[k] = 1.0; + } + // Now elements + double row2Value[] = {1.0, -5.0, 1.0}; + CoinBigIndex put = 0; + for (k = 0; k < numberColumns; k++) { + starts[k] = put; + lengths[k] = numberRows; + double value = row2Value[k]; + for (int i = 0; i < numberRows; i++) { + rows[put] = i; + elements[put] = value; + put++; + } + } + starts[numberColumns] = put; + // assign to matrix + CoinPackedMatrix * matrix = new CoinPackedMatrix(true, 0.0, 0.0); + matrix->assignMatrix(true, numberRows, numberColumns, numberElements, + elements, rows, starts, lengths); + ClpPackedMatrix * clpMatrix = new ClpPackedMatrix(matrix); + model2.replaceMatrix(clpMatrix, true); + printf("Time for 10000 addRow using hand written code is %g\n", CoinCpuTime() - time1); + // If matrix is really big could switch off creation of row copy + // model2.setSpecialOptions(256); + } + model2.dual(); + model2.writeMps("a.mps"); + // Print column solution + int numberColumns = model.numberColumns(); + + // Alternatively getColSolution() + double * columnPrimal = model.primalColumnSolution(); + // Alternatively getReducedCost() + double * columnDual = model.dualColumnSolution(); + // Alternatively getColLower() + double * columnLower = model.columnLower(); + // Alternatively getColUpper() + double * columnUpper = model.columnUpper(); + // Alternatively getObjCoefficients() + double * columnObjective = model.objective(); + + int iColumn; + + std::cout << " Primal Dual Lower Upper Cost" + << std::endl; + + for (iColumn = 0; iColumn < numberColumns; iColumn++) { + double value; + std::cout << std::setw(6) << iColumn << " "; + value = columnPrimal[iColumn]; + if (fabs(value) < 1.0e5) + std::cout << setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; + else + std::cout << setiosflags(std::ios::scientific) << std::setw(14) << value; + value = columnDual[iColumn]; + if (fabs(value) < 1.0e5) + std::cout << setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; + else + std::cout << setiosflags(std::ios::scientific) << std::setw(14) << value; + value = columnLower[iColumn]; + if (fabs(value) < 1.0e5) + std::cout << setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; + else + std::cout << setiosflags(std::ios::scientific) << std::setw(14) << value; + value = columnUpper[iColumn]; + if (fabs(value) < 1.0e5) + std::cout << setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; + else + std::cout << setiosflags(std::ios::scientific) << std::setw(14) << value; + value = columnObjective[iColumn]; + if (fabs(value) < 1.0e5) + std::cout << setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value; + else + std::cout << setiosflags(std::ios::scientific) << std::setw(14) << value; + + std::cout << std::endl; + } + std::cout << "--------------------------------------" << std::endl; + // Test CoinAssert + std::cout << "If Clp compiled without NDEBUG below should give assert, if with NDEBUG or COIN_ASSERT CoinError" << std::endl; + model = modelSave; + model.deleteRows(2, del); + // Deliberate error + model.deleteColumns(1, del + 2); + // Now use build +-1 + CoinBuild buildObject3; + time1 = CoinCpuTime(); + for (k = 0; k < N_ROWS_TO_ADD; k++) { + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -1.0, 1.0}; + buildObject3.addRow(3, row2Index, row2Value, + 1.0, 1.0); + } + model.addRows(buildObject3, true); + } catch (CoinError e) { + e.print(); + if (e.lineNumber() >= 0) + std::cout << "This was from a CoinAssert" << std::endl; + } +} + +void test_small_LP() +{ + ClpSimplex model; +// model.setLogLevel(1); + + // Objective - just nonzeros + int objIndex[] = {0, 2}; + double objValue[] = {1.0, 4.0}; + // Upper bounds - as dense vector + double upper[] = {2.0, COIN_DBL_MAX, 4.0}; + + // Create space for 3 columns + model.resize(0, 3); + + // Can also use maximumIterations + int integerValue; + model.getIntParam(ClpMaxNumIteration, integerValue); + cout << "Value of ClpMaxNumIteration is " << integerValue << endl; + model.setMaximumIterations(integerValue); + + // Fill in + int i; + // Virtuous way + // First objective + for (i = 0; i < 2; i++) + model.setObjectiveCoefficient(objIndex[i], objValue[i]); + // Now bounds (lower will be zero by default but do again) + for (i = 0; i < 3; i++) { + model.setColumnLower(i, 0.0); + model.setColumnUpper(i, upper[i]); + } + /* + We could also have done in non-virtuous way e.g. + double * objective = model.objective(); + and then set directly + */ + // Faster to add rows all at once - but this is easier to show + // Now add row 1 as >= 2.0 + int row1Index[] = {0, 2}; + double row1Value[] = {1.0, 1.0}; + model.addRow(2, row1Index, row1Value, + 2.0, COIN_DBL_MAX); + // Now add row 2 as == 1.0 + int row2Index[] = {0, 1, 2}; + double row2Value[] = {1.0, -5.0, 1.0}; + model.addRow(3, row2Index, row2Value, + 1.0, 1.0); + + int n = model.getNumCols(); + int m = model.getNumRows(); + cout<<"Problem has "<<n<<" variables and "<<m<<" constraints.\n"; + + // solve + model.dual(); + + // Check the solution + if ( model.isProvenOptimal() ) + { + cout << "Found optimal solution!" << endl; + cout << "Objective value is " << model.getObjValue() << endl; + cout << "Model status is " << model.status() << " after " + << model.numberIterations() << " iterations - objective is " + << model.objectiveValue() << endl; + const double *solution; + solution = model.getColSolution(); + // We could then print the solution or examine it. + cout<<"Solution is: "; + for(int i=0; i<n; i++) + cout<<solution[i]<<", "; + cout<<endl; + } + else + cout << "Didn’t find optimal solution." << endl; +} + int main() { - int ret = 0; - return ret; + cout <<"Test LP Solvers\n"; + +// test_addRows(); + test_small_LP(); + + Solver_LP_abstract *solver = Solver_LP_abstract::getNewSolver(SOLVER_LP_CLP); + Vector3 c, lb, ub, x; + MatrixXX A(2,3); + Vector2 Alb, Aub; + c << 1.0, 0.0, 4.0; + lb << 0.0, 0.0, 0.0; + ub << 2.0, COIN_DBL_MAX, 4.0; + A << 1.0, 0.0, 1.0, + 1.0, -5.0, 1.0; + Alb << 2.0, 1.0; + Aub << COIN_DBL_MAX, 1.0; + if(solver->solve(c, lb, ub, A, Alb, Aub, x)==LP_STATUS_OPTIMAL) + { + cout<<"solver_LP_clp solved the problem\n"; + cout<<"The solution is "<<x.transpose()<<endl; + } + else + cout<<"solver_LP_clp failed to solve the problem\n"; + +// char x[81]; +// int iRow; +// // get row copy +// CoinPackedMatrix rowCopy = *model.matrix(); +// rowCopy.reverseOrdering(); +// const int * column = rowCopy.getIndices(); +// const int * rowLength = rowCopy.getVectorLengths(); +// const CoinBigIndex * rowStart = rowCopy.getVectorStarts(); +// x[n] = '\0'; +// for (iRow = 0; iRow < m; iRow++) { +// memset(x, ' ', n); +// for (int k = rowStart[iRow]; k < rowStart[iRow] + rowLength[iRow]; k++) { +// int iColumn = column[k]; +// x[iColumn] = 'x'; +// } +// cout<<x<<endl; +// } +// cout<<endl; + +// // Now matrix +// CoinPackedMatrix * matrix = model.matrix(); +// const double * element = matrix->getElements(); +// const int * row = matrix->getIndices(); +// const int * start = matrix->getVectorStarts(); +// const int * length = matrix->getVectorLengths(); +// for (int iColumn = 0; iColumn < n; iColumn++) +// { +// cout << "Column " << iColumn; +// int j; +// for (j = start[iColumn]; j < start[iColumn] + length[iColumn]; j++) +// cout << " ( " << row[j] << ", " << element[j] << ")"; +// cout << endl; +// } + + return 1; } diff --git a/test/test_static_equilibrium.cpp b/test/test_static_equilibrium.cpp index 1dfaa63..ee02e8c 100644 --- a/test/test_static_equilibrium.cpp +++ b/test/test_static_equilibrium.cpp @@ -1,6 +1,12 @@ +/* + * Copyright 2015, LAAS-CNRS + * Author: Andrea Del Prete + */ + #include <vector> #include <iostream> #include <robust-equilibrium-lib/static_equilibrium.hh> +#include <robust-equilibrium-lib/logger.hh> using namespace robust_equilibrium; using namespace Eigen; @@ -10,7 +16,7 @@ using namespace std; int main() { - init_library(); +// init_cdd_library(); srand ((unsigned int)(time(NULL))); int ret = 0; @@ -31,9 +37,10 @@ int main() RPY_UPPER_BOUNDS << +0.0*gamma, +0.0*gamma, +M_PI; double X_MARG = 0.07; double Y_MARG = 0.07; - const int GRID_SIZE = 25; + const int GRID_SIZE = 20; - StaticEquilibrium solver(mass, generatorsPerContact, SOLVER_LP_QPOASES); + StaticEquilibrium solver_PP(mass, generatorsPerContact, SOLVER_LP_CLP); + StaticEquilibrium solver_LP(mass, generatorsPerContact, SOLVER_LP_CLP); MatrixXX contact_pos = MatrixXX::Zero(N_CONTACTS, 3); MatrixXX contact_rpy = MatrixXX::Zero(N_CONTACTS, 3); MatrixXX p = MatrixXX::Zero(4*N_CONTACTS,3); // contact points @@ -50,21 +57,21 @@ int main() bool collision; for(unsigned int i=0; i<N_CONTACTS; i++) { -// while(true) // generate contact position -// { -// uniform(CONTACT_POINT_LOWER_BOUNDS, CONTACT_POINT_UPPER_BOUNDS, contact_pos.row(i)); -// if(i==0) -// break; -// collision = false; -// for(unsigned int j=0; j<i-1; j++) -// if((contact_pos.row(i)-contact_pos.row(j)).norm() < MIN_FEET_DISTANCE) -// collision = true; -// if(collision==false) -// break; -// } - -// // generate contact orientation -// uniform(RPY_LOWER_BOUNDS, RPY_UPPER_BOUNDS, contact_rpy.row(i)); + while(true) // generate contact position + { + uniform(CONTACT_POINT_LOWER_BOUNDS, CONTACT_POINT_UPPER_BOUNDS, contact_pos.row(i)); + if(i==0) + break; + collision = false; + for(unsigned int j=0; j<i-1; j++) + if((contact_pos.row(i)-contact_pos.row(j)).norm() < MIN_FEET_DISTANCE) + collision = true; + if(collision==false) + break; + } + + // generate contact orientation + uniform(RPY_LOWER_BOUNDS, RPY_UPPER_BOUNDS, contact_rpy.row(i)); generate_rectangle_contacts(LX, LY, contact_pos.row(i), contact_rpy.row(i), p.middleRows<4>(i*4), N.middleRows<4>(i*4)); @@ -79,7 +86,13 @@ int main() printf("Normal (%.3f,%.3f,%.3f)\n", N(i,0), N(i,1), N(i,2)); } - if(!solver.setNewContacts(p, N, frictionCoefficients, STATIC_EQUILIBRIUM_ALGORITHM_PP)) + if(!solver_LP.setNewContacts(p, N, frictionCoefficients, STATIC_EQUILIBRIUM_ALGORITHM_LP)) + { + printf("Error while setting new contacts"); + return -1; + } + + if(!solver_PP.setNewContacts(p, N, frictionCoefficients, STATIC_EQUILIBRIUM_ALGORITHM_PP)) { printf("Error while setting new contacts"); return -1; @@ -108,14 +121,21 @@ int main() { // uniform(com_LB, com_UB, com); com(0) = x_range(j); - if(solver.checkRobustEquilibrium(com, 0.0)) + double rob = solver_LP.computeEquilibriumRobustness(com); + if(solver_PP.checkRobustEquilibrium(com, 0.0)) { equilibrium(i,j) = 1.0; - printf("1 "); + if(rob<=0) + SEND_ERROR_MSG("PP says com is in equilibrium but LP find negative robustness "+toString(rob)); + if(rob>9.0) + rob = 9.0; + printf("%d ", (int)rob); } else { equilibrium(i,j) = 0.0; + if(rob>0) + SEND_ERROR_MSG("PP says com is NOT in equilibrium but LP find positive robustness "+toString(rob)); printf("- "); } -- GitLab