MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)


目录

  • 相关教程
  • 相关文献
  • CMake工程构建(能访问GitHub方法)
  • CMake工程构建(不能访问GitHub方法)
  • CLion debug源码
  • CMake打包Cbc.so:

Cbc(Coin或branch and cut)是一个开源的混合整数线性规划求解器,用C++编写。它可以用作可调用库或使用独立的可执行文件。它可以通过各种建模系统、包等以多种方式使用。Clp(Coin或线性规划)是一个开源线性规划求解器。它主要是用来作为一个可调用的库,但也有一个基本的、独立的可执行版本。本文将介绍如何用CLion(CMake)构建工程并能debug源码。
相关教程
  • 【从零开始】在Windows中使用Linux——在WSL使用CLion、IDEA、PyCharm(安装到建立工程)——更新于2021.12
  • WSL2 运行 CLion/IDEA/PyCharm等GUI应用——更新于2022.01
  • 【从零开始】Google OR-Tools源码构建debug(CLion/CMake)
相关文献
  • COIN-OR Branch-and-Cut solver(GitHub)
CMake工程构建(能访问GitHub方法) 博主的环境是Ubuntu 20.04 LTS。
首先打开CLion创建新工程:
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

修改CMakeLists.txt:
cmake_minimum_required(VERSION 3.21) project(CbcProject)set(PROJECT_ROOT_PATH "${PROJECT_SOURCE_DIR}") # 可以把cbc打包后的.so文件放进lib目录下 set(LIBRARY_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/lib/") # 可以把输出文件放进bin目录下 set(EXECUTABLE_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/bin/") # Build Cbc dependencies. set(BUILD_DEPS ON)if (BUILD_DEPS) set(BUILD_CoinUtils ON) set(BUILD_Osi ON) set(BUILD_Clp ON) set(BUILD_Cgl ON) set(BUILD_Cbc ON) endif ()include(FetchContent) # 填充期间的日志输出可能非常冗长,使得配置阶段非常嘈杂。此缓存选项(ON默认情况下)隐藏所有填充输出,除非遇到错误。如果遇到挂起下载的问题, # 暂时关闭此选项可能有助于诊断导致问题的内容群体。 set(FETCHCONTENT_QUIET OFF)# 避免每次下载编译及其所有依赖 get_filename_component(_deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") # 在大多数情况下,保存的详细信息没有指定与用于内部子构建、最终源和构建区域的目录相关的任何选项。通常最好将这些决定留给FetchContent 模块来代表项目处理。 # 缓存变量控制收集所有内容填充目录的FETCHCONTENT_BASE_DIR 点,但在大多数情况下,开发人员不需要更改它。 # 默认位置是${CMAKE_BINARY_DIR}/_deps,但如果开发人员更改此值,他们应该致力于保持路径短且刚好低于构建树的顶层,以避免在 Windows 上遇到路径长度问题。 set(FETCHCONTENT_BASE_DIR ${_deps})# ############################################################################## # Coinutils # ############################################################################## # Coin-OR does not support C++17/C++20 (use of 'register' storage class specifier) set(CMAKE_CXX_STANDARD 11)if (BUILD_CoinUtils) message(CHECK_START "Fetching CoinUtils") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( CoinUtils GIT_REPOSITORY "https://github.com/Mizux/CoinUtils.git" GIT_TAG "cmake/2.11.6" ) FetchContent_MakeAvailable(CoinUtils) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Osi # ############################################################################## if (BUILD_Osi) message(CHECK_START "Fetching Osi") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Osi GIT_REPOSITORY "https://github.com/Mizux/Osi.git" GIT_TAG "cmake/0.108.7" ) FetchContent_MakeAvailable(Osi) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Clp # ############################################################################## if (BUILD_Clp) message(CHECK_START "Fetching Clp") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Clp GIT_REPOSITORY "https://github.com/Mizux/Clp.git" GIT_TAG "cmake/1.17.7" ) FetchContent_MakeAvailable(Clp) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Cgl # ############################################################################## if (BUILD_Cgl) message(CHECK_START "Fetching Cgl") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Cgl GIT_REPOSITORY "https://github.com/Mizux/Cgl.git" GIT_TAG "cmake/0.60.5" ) FetchContent_MakeAvailable(Cgl) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Cbc # ############################################################################## if (BUILD_Cbc) message(CHECK_START "Fetching Cbc") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Cbc GIT_REPOSITORY "https://github.com/Mizux/Cbc.git" GIT_TAG "cmake/2.10.7" ) FetchContent_MakeAvailable(Cbc) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()add_executable(${PROJECT_NAME} main.cpp)if (BUILD_DEPS) set(COINOR_DEPS Coin::CbcSolver Coin::OsiCbc Coin::ClpSolver Coin::OsiClp) target_link_libraries(${PROJECT_NAME} PUBLIC ${COINOR_DEPS}) endif ()

然后你会看到:
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

CMake工程构建(不能访问GitHub方法) 博主的环境是Ubuntu 20.04 LTS。
首先打开CLion创建新工程:
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

下载:coin-or/CoinUtils-cmake-2.11.6 Osi-cmake-0.108.7 Clp-cmake-1.17.7 Cgl-cmake-0.60.5 Cbc-cmake-2.10.7源码
解压到:
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

修改CMakeLists.txt:
cmake_minimum_required(VERSION 3.21) project(CbcProject)set(PROJECT_ROOT_PATH "${PROJECT_SOURCE_DIR}") # 可以把cbc打包后的.so文件放进lib目录下 set(LIBRARY_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/lib/") # 可以把输出文件放进bin目录下 set(EXECUTABLE_OUTPUT_PATH "${PROJECT_ROOT_PATH}/${OUTDIRS}/bin/")# Build Cbc dependencies. set(BUILD_DEPS ON)if (BUILD_DEPS) set(BUILD_CoinUtils ON) set(BUILD_Osi ON) set(BUILD_Clp ON) set(BUILD_Cgl ON) set(BUILD_Cbc ON) endif ()include(FetchContent) # 填充期间的日志输出可能非常冗长,使得配置阶段非常嘈杂。此缓存选项(ON默认情况下)隐藏所有填充输出,除非遇到错误。如果遇到挂起下载的问题, # 暂时关闭此选项可能有助于诊断导致问题的内容群体。 set(FETCHCONTENT_QUIET OFF)# 避免每次下载编译及其所有依赖 get_filename_component(_deps "../_deps" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") # 在大多数情况下,保存的详细信息没有指定与用于内部子构建、最终源和构建区域的目录相关的任何选项。通常最好将这些决定留给FetchContent 模块来代表项目处理。 # 缓存变量控制收集所有内容填充目录的FETCHCONTENT_BASE_DIR 点,但在大多数情况下,开发人员不需要更改它。 # 默认位置是${CMAKE_BINARY_DIR}/_deps,但如果开发人员更改此值,他们应该致力于保持路径短且刚好低于构建树的顶层,以避免在 Windows 上遇到路径长度问题。 set(FETCHCONTENT_BASE_DIR ${_deps})# ############################################################################## # Coinutils # ############################################################################## # Coin-OR does not support C++17/C++20 (use of 'register' storage class specifier) set(CMAKE_CXX_STANDARD 11)if (BUILD_CoinUtils) message(CHECK_START "Fetching CoinUtils") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( CoinUtils URL "${PROJECT_SOURCE_DIR}/deps/CoinUtils-cmake-2.11.6.zip" ) FetchContent_MakeAvailable(CoinUtils) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Osi # ############################################################################## if (BUILD_Osi) message(CHECK_START "Fetching Osi") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Osi URL "${PROJECT_SOURCE_DIR}/deps/Osi-cmake-0.108.7.zip" ) FetchContent_MakeAvailable(Osi) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Clp # ############################################################################## if (BUILD_Clp) message(CHECK_START "Fetching Clp") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Clp URL "${PROJECT_SOURCE_DIR}/deps/Clp-cmake-1.17.7.zip" ) FetchContent_MakeAvailable(Clp) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Cgl # ############################################################################## if (BUILD_Cgl) message(CHECK_START "Fetching Cgl") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Cgl URL "${PROJECT_SOURCE_DIR}/deps/Cgl-cmake-0.60.5.zip" ) FetchContent_MakeAvailable(Cgl) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()# ############################################################################## # Cbc # ############################################################################## if (BUILD_Cbc) message(CHECK_START "Fetching Cbc") list(APPEND CMAKE_MESSAGE_INDENT "") FetchContent_Declare( Cbc URL "${PROJECT_SOURCE_DIR}/deps/Cbc-cmake-2.10.7.zip" ) FetchContent_MakeAvailable(Cbc) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") endif ()add_executable(${PROJECT_NAME} main.cpp) if (BUILD_DEPS) set(COINOR_DEPS Coin::CbcSolver Coin::OsiCbc Coin::ClpSolver Coin::OsiClp) target_link_libraries(${PROJECT_NAME} PUBLIC ${COINOR_DEPS}) endif ()

CLion debug源码 main.cpp:
#include #include "ClpSimplex.hpp" #include "CoinBuild.hpp" #include "CoinHelperFunctions.hpp" #include "CoinModel.hpp" #include "CoinTime.hpp" #include int main() { // 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 wayFirst time adding in 10000 rows one by one */ model.allSlackBasis(); ClpSimplex modelSave = model; double time1 = CoinCpuTime(); int k; for (k = 0; k < 10000; 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 < 10000; 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 < 10000; 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 < 10000; 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 < 10000; 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 < 10000; 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 = 10000; 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 = https://www.it610.com/article/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 << "PrimalDualLowerUpperCost" << std::endl; for (iColumn = 0; iColumn < numberColumns; iColumn++) { double value; std::cout << std::setw(6) << iColumn << " "; value = https://www.it610.com/article/columnPrimal[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value << std::resetiosflags(std::ios::fixed); else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value << std::resetiosflags(std::ios::scientific); value = columnDual[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value << std::resetiosflags(std::ios::fixed); else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value << std::resetiosflags(std::ios::scientific); value = columnLower[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value << std::resetiosflags(std::ios::fixed); else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value << std::resetiosflags(std::ios::scientific); value = columnUpper[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value << std::resetiosflags(std::ios::fixed); else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value << std::resetiosflags(std::ios::scientific); value = columnObjective[iColumn]; if (fabs(value) < 1.0e5) std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14) << value << std::resetiosflags(std::ios::fixed); else std::cout << std::setiosflags(std::ios::scientific) << std::setw(14) << value << std::resetiosflags(std::ios::scientific); std::cout << std::endl; } std::cout <<"--------------------------------------" << std::endl; return 0; }

最后尽情地开始Debug吧!!!(上一张成果图):
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

CMake打包Cbc.so: 【MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)】其实通过上述步骤编译后你就会发现多了一个lib目录:
MILP|【从零开始】coin-or/CoinUtils Osi Clp Cgl Cbc源码构建debug(CLion/CMake)
文章图片

    推荐阅读