├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── main.yml │ └── pages.yml ├── .gitignore ├── CMakeLists.txt ├── Doxyfile ├── LICENSE ├── changelog.md ├── code_of_conduct.md ├── codecov.yml ├── conanfile.py ├── include └── scippp │ ├── constant_coefficient.hpp │ ├── initial_solution.hpp │ ├── lin_expr.hpp │ ├── lin_ineq.hpp │ ├── model.hpp │ ├── param.hpp │ ├── parameters.hpp │ ├── solution.hpp │ ├── solving_statistics.hpp │ ├── statistics.hpp │ ├── var.hpp │ ├── var_type.hpp │ └── version.hpp ├── readme.md ├── source ├── initial_solution.cpp ├── lin_expr.cpp ├── lin_ineq.cpp ├── model.cpp ├── var.cpp └── version.cpp.in ├── test ├── CMakeLists.txt ├── examples.cpp ├── main.cpp ├── test_add_var.cpp ├── test_initial_solution.cpp ├── test_io.cpp ├── test_objscip.cpp └── test_var.cpp └── utils ├── CMakeLists.txt ├── extract_solvingstats.py ├── gen_constexpr_parameters.cpp └── requirements.txt /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: WebKit 4 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: '-*,readability-identifier-naming,modernize-use-using,modernize-concat-nested-namespaces,readability-magic-numbers,bugprone-branch-clone,bugprone-incorrect-roundings,bugprone-lambda-function-name,bugprone-reserved-identifier,bugprone-use-after-move,cppcoreguidelines-avoid-goto,misc-static-assert,misc-unused-parameters,modernize-deprecated-headers,modernize-make-unique,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-default,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,performance-*,readability-braces-around-statements,readability-container-size-empty,readability-convert-member-functions-to-static,readability-string-compare' 2 | WarningsAsErrors: '-*,readability-identifier-naming,modernize-use-using,modernize-concat-nested-namespaces,readability-magic-numbers,readability-braces-around-statements' 3 | FormatStyle: 'file' 4 | CheckOptions: 5 | - { key: readability-identifier-naming.ClassCase, value: CamelCase } 6 | - { key: readability-identifier-naming.ClassConstantCase, value: UPPER_CASE } 7 | - { key: readability-identifier-naming.ClassConstantPrefix, value: 's_' } 8 | - { key: readability-identifier-naming.ClassMemberCase, value: camelBack } 9 | - { key: readability-identifier-naming.ClassMemberPrefix, value: 's_' } 10 | - { key: readability-identifier-naming.ConstantCase, value: UPPER_CASE } 11 | - { key: readability-identifier-naming.ConstantMemberCase, value: UPPER_CASE } 12 | - { key: readability-identifier-naming.ConstantMemberPrefix, value: 'm_' } 13 | - { key: readability-identifier-naming.ConstantParameterCase, value: UPPER_CASE } 14 | - { key: readability-identifier-naming.ConstantPointerParameterCase, value: UPPER_CASE } 15 | - { key: readability-identifier-naming.EnumCase, value: CamelCase } 16 | - { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE } 17 | - { key: readability-identifier-naming.FunctionCase, value: camelBack } 18 | - { key: readability-identifier-naming.GlobalConstantCase, value: UPPER_CASE } 19 | - { key: readability-identifier-naming.GlobalFunctionCase, value: camelBack } 20 | - { key: readability-identifier-naming.LocalConstantCase, value: UPPER_CASE } 21 | - { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE } 22 | - { key: readability-identifier-naming.MacroDefinitionPrefix, value: 'SCIPPP_' } 23 | - { key: readability-identifier-naming.MemberCase, value: camelBack } 24 | - { key: readability-identifier-naming.MemberPrefix, value: 'm_' } 25 | - { key: readability-identifier-naming.MethodCase, value: camelBack } 26 | - { key: readability-identifier-naming.NamespaceCase, value: lower_case } 27 | - { key: readability-identifier-naming.ParameterCase, value: camelBack } 28 | - { key: readability-identifier-naming.PrivateMemberCase, value: camelBack } 29 | - { key: readability-identifier-naming.PrivateMemberPrefix, value: 'm_' } 30 | - { key: readability-identifier-naming.ProtectedMemberCase, value: camelBack } 31 | - { key: readability-identifier-naming.ProtectedMemberPrefix, value: 'm_' } 32 | - { key: readability-identifier-naming.PublicMemberCase, value: camelBack } 33 | - { key: readability-identifier-naming.PublicMemberPrefix, value: '' } 34 | - { key: readability-identifier-naming.PublicMethodCase, value: camelBack } 35 | - { key: readability-identifier-naming.StaticConstantCase, value: UPPER_CASE } 36 | - { key: readability-identifier-naming.TemplateParameterCase, value: CamelCase } 37 | - { key: readability-identifier-naming.TypeTemplateParameterIgnoredRegexp, value: expr-type } 38 | - { key: readability-identifier-naming.StructCase, value: CamelCase } 39 | - { key: readability-identifier-naming.UnionCase, value: CamelCase } 40 | - { key: readability-identifier-naming.VariableCase, value: camelBack } 41 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: "Build & Test" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | coverage: 11 | runs-on: ubuntu-22.04 12 | env: 13 | CC: gcc-11 14 | CXX: g++-11 15 | steps: 16 | - uses: actions/checkout@v3 17 | - uses: turtlebrowser/get-conan@main 18 | - name: Run Conan Install 19 | run: | 20 | conan profile detect 21 | conan profile detect --name debug 22 | echo "&:build_type=Debug" >> $(conan profile path debug) 23 | pushd ~/.conan2/profiles 24 | sed -i 's/gnu17/17/g' * 25 | popd 26 | conan install -of . -pr debug -o with_tests=True . 27 | - name: Run CMake 28 | run: cmake --preset conan-debug -DUSE_COVERAGE=ON . 29 | - name: Compile 30 | run: cmake --build build/Debug --target tests 31 | - name: Run Tests 32 | run: ./build/Debug/test/tests --log_format=JUNIT --log_level=all --log_sink=boosttest.xml 33 | - name: Install GCovr 34 | run: pip install gcovr 35 | - name: Extract Coverage 36 | run: gcovr -r . -e ./test --object-directory=./build/Debug/CMakeFiles --exclude-unreachable-branches --exclude-throw-branches -b --sonarqube coverage.xml --xml cobertura.xml 37 | - uses: codecov/codecov-action@v3 38 | with: 39 | token: ${{ secrets.CODECOV_TOKEN }} 40 | files: ./boosttest.xml,./coverage.xml,./cobertura.xml 41 | verbose: true # optional (default = false) 42 | fail_ci_if_error: true 43 | test_release_win: 44 | runs-on: windows-2019 45 | steps: 46 | - uses: actions/checkout@v3 47 | - uses: turtlebrowser/get-conan@main 48 | - uses: ilammy/msvc-dev-cmd@v1 49 | - name: Run Conan Install 50 | run: | 51 | conan profile detect 52 | powershell -Command "(gc $(conan profile path default)) -replace 'compiler.cppstd=14', 'compiler.cppstd=17' | Out-File -encoding ASCII $(conan profile path default)" 53 | conan install -of . -o with_tests=True . 54 | - name: Run CMake 55 | run: cmake --preset conan-default . 56 | - name: Compile 57 | run: cmake --build build --preset conan-release --target tests 58 | - name: Run Tests 59 | run: ./build/test/Release/tests.exe 60 | test_without_conan: 61 | runs-on: ubuntu-22.04 62 | env: 63 | CC: gcc-11 64 | CXX: g++-11 65 | steps: 66 | - uses: actions/checkout@v3 67 | - name: Install parallelism library for C++ 68 | run: sudo apt update && sudo apt install libtbb12 69 | - uses: MarkusJx/install-boost@v2.4.1 70 | with: 71 | boost_version: 1.84.0 72 | - name: Install SCIP 73 | run: | 74 | wget https://github.com/scipopt/scip/releases/download/v920/SCIPOptSuite-9.2.0-Linux-ubuntu22.sh 75 | chmod +x SCIPOptSuite-9.2.0-Linux-ubuntu22.sh 76 | ./SCIPOptSuite-9.2.0-Linux-ubuntu22.sh --skip-license 77 | - name: Build SCIP++ 78 | run: | 79 | CMAKE_PREFIX_PATH=./lib/cmake/scip:./boost/boost/lib/cmake/Boost-1.84.0 cmake -DBUILD_TESTS=ON . 80 | make -j tests 81 | - name: Run tests 82 | run: ./test/tests 83 | test_release_mac: 84 | runs-on: macos-13 85 | steps: 86 | - uses: actions/checkout@v3 87 | - uses: turtlebrowser/get-conan@main 88 | - name: Run Conan Install 89 | run: | 90 | conan profile detect 91 | pushd ~/.conan2/profiles 92 | sed -i'' -e 's/gnu17/17/g' * 93 | popd 94 | conan install -of . -o with_tests=True --build=missing . 95 | - name: Run CMake 96 | run: cmake --preset conan-release . 97 | - name: Compile 98 | run: cmake --build build/Release --target tests 99 | - name: Run Tests 100 | run: ./build/Release/test/tests 101 | test_release_linux: 102 | runs-on: ubuntu-22.04 103 | env: 104 | CC: gcc-11 105 | CXX: g++-11 106 | steps: 107 | - uses: actions/checkout@v3 108 | - uses: turtlebrowser/get-conan@main 109 | - name: Run Conan Install 110 | run: | 111 | conan profile detect 112 | pushd ~/.conan2/profiles 113 | sed -i'' -e 's/gnu17/17/g' * 114 | popd 115 | conan install -of . -o with_tests=True --build=missing . 116 | - name: Run CMake 117 | run: cmake --preset conan-release . 118 | - name: Compile 119 | run: cmake --build build/Release --target tests 120 | - name: Run Tests 121 | run: ./build/Release/test/tests 122 | clang_tidy: 123 | runs-on: ubuntu-22.04 124 | env: 125 | CC: gcc-11 126 | CXX: g++-11 127 | steps: 128 | - uses: actions/checkout@v3 129 | - uses: turtlebrowser/get-conan@main 130 | - name: Run Conan Install 131 | run: | 132 | conan profile detect 133 | pushd ~/.conan2/profiles 134 | sed -i 's/gnu17/17/g' * 135 | popd 136 | conan install -of . . 137 | - name: Create CMake Compilation Database 138 | run: cmake --preset conan-release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . 139 | - name: Run Clang-Tidy 140 | run: clang-tidy-14 -p build/Release source/*.cpp include/**/*.hpp 141 | clang_format: 142 | runs-on: ubuntu-22.04 143 | steps: 144 | - uses: actions/checkout@v3 145 | - uses: DoozyX/clang-format-lint-action@v0.16.2 146 | with: 147 | source: source include test 148 | extensions: 'hpp,cpp' 149 | clangFormatVersion: 17 150 | doxygen: 151 | runs-on: ubuntu-22.04 152 | steps: 153 | - uses: actions/checkout@v3 154 | - uses: ssciwr/doxygen-install@v1 155 | with: 156 | version: "1.12.0" 157 | - uses: ts-graphviz/setup-graphviz@v1 158 | - name: Prepare Doxygen Config 159 | run: echo "PROJECT_NUMBER = ${GITHUB_REF}" >> Doxyfile 160 | - name: Run Doxygen 161 | run: doxygen 162 | - name: Zip HTML Docu 163 | run: zip -q -9 -r html.zip html 164 | - name: Store HTML Docu 165 | uses: actions/upload-artifact@v4 166 | with: 167 | name: doxygen-html 168 | path: html.zip 169 | retention-days: 2 170 | -------------------------------------------------------------------------------- /.github/workflows/pages.yml: -------------------------------------------------------------------------------- 1 | name: "Pages" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | 7 | jobs: 8 | github_pages: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: ssciwr/doxygen-install@v1 13 | with: 14 | version: "1.12.0" 15 | - uses: ts-graphviz/setup-graphviz@v1 16 | - name: Prepare Doxygen Config 17 | run: echo "PROJECT_NUMBER = ${GITHUB_REF}" >> Doxyfile 18 | - name: Run Doxygen 19 | run: doxygen 20 | - run: | 21 | cd html 22 | git init 23 | git add -A 24 | git config --local user.email "action@github.com" 25 | git config --local user.name "GitHub Action" 26 | git commit -m 'deploy' 27 | - uses: ad-m/github-push-action@v0.6.0 28 | with: 29 | github_token: ${{ secrets.GITHUB_TOKEN }} 30 | branch: gh-pages 31 | force: true 32 | directory: ./html 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Doxygen output 2 | html 3 | 4 | # CLion build folders 5 | cmake-build-debug 6 | cmake-build-release 7 | 8 | # conan build folder 9 | build 10 | 11 | # CMake presets generated by Conan 12 | CMakeUserPresets.json 13 | 14 | # CLion IDE files (seem to store local (non-shareable) settings since Nova is out) 15 | **/.idea 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15.7) 2 | project(ScipPP LANGUAGES CXX) 3 | set(CMAKE_CXX_STANDARD 17) 4 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) 5 | 6 | option(USE_COVERAGE "Create coverage report." OFF) 7 | option(BUILD_TESTS "Build tests." OFF) 8 | option(BUILD_UTILS "Build utils." OFF) 9 | 10 | message(STATUS "${PROJECT_NAME} version derived from git is ${scippp_version}") 11 | configure_file( 12 | ${CMAKE_CURRENT_SOURCE_DIR}/source/version.cpp.in 13 | ${CMAKE_CURRENT_BINARY_DIR}/source/version.cpp) 14 | 15 | find_package(scip CONFIG REQUIRED) 16 | if (TARGET libscip) 17 | add_library(scip::scip ALIAS libscip) 18 | endif () 19 | 20 | # enable coverage analysis by setting USE_COVERAGE to ON 21 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") 22 | set(COVERAGE_COMPILER_FLAGS "-fprofile-instr-generate -fcoverage-mapping" CACHE INTERNAL "") 23 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 24 | set(COVERAGE_COMPILER_FLAGS "-fprofile-arcs -ftest-coverage -fPIC -O0 -g" CACHE INTERNAL "") 25 | else () 26 | message(WARNING "We do not support coverage with this compiler: ${CMAKE_CXX_COMPILER_ID}") 27 | endif () 28 | function(append_coverage_compiler_flags) 29 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 30 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE) 31 | message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}") 32 | endfunction() 33 | if (USE_COVERAGE) 34 | append_coverage_compiler_flags() 35 | endif () 36 | 37 | # Lib 38 | file(GLOB SCIPPP_SRC source/*.cpp ${CMAKE_CURRENT_BINARY_DIR}/source/version.cpp) 39 | file(GLOB SCIPPP_HDR include/**/*.hpp) 40 | add_library(ScipPP ${SCIPPP_SRC}) 41 | target_include_directories(ScipPP PUBLIC include) 42 | target_link_libraries(ScipPP PUBLIC scip::scip) 43 | 44 | # install 45 | include(GNUInstallDirs) 46 | install(TARGETS ScipPP LIBRARY DESTINATION lib) 47 | set_target_properties(ScipPP PROPERTIES PUBLIC_HEADER "${SCIPPP_HDR}") 48 | install(TARGETS ScipPP PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/scippp) 49 | 50 | if (BUILD_TESTS) 51 | add_subdirectory(test) 52 | endif () 53 | 54 | if (BUILD_UTILS) 55 | add_subdirectory(utils) 56 | endif () 57 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.9.6 2 | # 3 | # and optionally PROJECT_LOGO, PROJECT_NUMBER, PROJECT_BRIEF 4 | 5 | #--------------------------------------------------------------------------- 6 | # Project related configuration options 7 | #--------------------------------------------------------------------------- 8 | INPUT = readme.md code_of_conduct.md changelog.md LICENSE include 9 | USE_MDFILE_AS_MAINPAGE = readme.md 10 | PROJECT_NAME = "SCIP++" 11 | DOXYFILE_ENCODING = UTF-8 12 | OUTPUT_DIRECTORY = . 13 | CREATE_SUBDIRS = NO 14 | CREATE_SUBDIRS_LEVEL = 8 15 | ALLOW_UNICODE_NAMES = NO 16 | OUTPUT_LANGUAGE = English 17 | BRIEF_MEMBER_DESC = YES 18 | REPEAT_BRIEF = YES 19 | ABBREVIATE_BRIEF = "The $name class" \ 20 | "The $name widget" \ 21 | "The $name file" \ 22 | is \ 23 | provides \ 24 | specifies \ 25 | contains \ 26 | represents \ 27 | a \ 28 | an \ 29 | the 30 | ALWAYS_DETAILED_SEC = NO 31 | INLINE_INHERITED_MEMB = NO 32 | FULL_PATH_NAMES = NO 33 | STRIP_FROM_PATH = 34 | STRIP_FROM_INC_PATH = 35 | SHORT_NAMES = NO 36 | JAVADOC_AUTOBRIEF = YES 37 | JAVADOC_BANNER = NO 38 | QT_AUTOBRIEF = NO 39 | MULTILINE_CPP_IS_BRIEF = NO 40 | PYTHON_DOCSTRING = YES 41 | INHERIT_DOCS = YES 42 | SEPARATE_MEMBER_PAGES = NO 43 | TAB_SIZE = 4 44 | ALIASES = 45 | OPTIMIZE_OUTPUT_FOR_C = NO 46 | OPTIMIZE_OUTPUT_JAVA = NO 47 | OPTIMIZE_FOR_FORTRAN = NO 48 | OPTIMIZE_OUTPUT_VHDL = NO 49 | OPTIMIZE_OUTPUT_SLICE = NO 50 | EXTENSION_MAPPING = 51 | MARKDOWN_SUPPORT = YES 52 | TOC_INCLUDE_HEADINGS = 5 53 | AUTOLINK_SUPPORT = YES 54 | BUILTIN_STL_SUPPORT = YES 55 | CPP_CLI_SUPPORT = NO 56 | SIP_SUPPORT = NO 57 | IDL_PROPERTY_SUPPORT = YES 58 | DISTRIBUTE_GROUP_DOC = NO 59 | GROUP_NESTED_COMPOUNDS = NO 60 | SUBGROUPING = YES 61 | INLINE_GROUPED_CLASSES = NO 62 | INLINE_SIMPLE_STRUCTS = NO 63 | TYPEDEF_HIDES_STRUCT = NO 64 | LOOKUP_CACHE_SIZE = 0 65 | NUM_PROC_THREADS = 8 66 | #--------------------------------------------------------------------------- 67 | # Build related configuration options 68 | #--------------------------------------------------------------------------- 69 | EXTRACT_ALL = NO 70 | EXTRACT_PRIVATE = NO 71 | EXTRACT_PRIV_VIRTUAL = YES 72 | EXTRACT_PACKAGE = YES 73 | EXTRACT_STATIC = YES 74 | EXTRACT_LOCAL_CLASSES = YES 75 | EXTRACT_LOCAL_METHODS = YES 76 | EXTRACT_ANON_NSPACES = YES 77 | RESOLVE_UNNAMED_PARAMS = YES 78 | HIDE_UNDOC_MEMBERS = NO 79 | HIDE_UNDOC_CLASSES = NO 80 | HIDE_FRIEND_COMPOUNDS = NO 81 | HIDE_IN_BODY_DOCS = NO 82 | INTERNAL_DOCS = YES 83 | CASE_SENSE_NAMES = NO 84 | HIDE_SCOPE_NAMES = NO 85 | HIDE_COMPOUND_REFERENCE= NO 86 | SHOW_HEADERFILE = YES 87 | SHOW_INCLUDE_FILES = YES 88 | SHOW_GROUPED_MEMB_INC = NO 89 | FORCE_LOCAL_INCLUDES = NO 90 | INLINE_INFO = YES 91 | SORT_MEMBER_DOCS = YES 92 | SORT_BRIEF_DOCS = YES 93 | SORT_MEMBERS_CTORS_1ST = YES 94 | SORT_GROUP_NAMES = YES 95 | SORT_BY_SCOPE_NAME = YES 96 | STRICT_PROTO_MATCHING = NO 97 | GENERATE_TODOLIST = YES 98 | GENERATE_TESTLIST = YES 99 | GENERATE_BUGLIST = YES 100 | GENERATE_DEPRECATEDLIST= YES 101 | ENABLED_SECTIONS = 102 | MAX_INITIALIZER_LINES = 30 103 | SHOW_USED_FILES = YES 104 | SHOW_FILES = YES 105 | SHOW_NAMESPACES = YES 106 | FILE_VERSION_FILTER = 107 | LAYOUT_FILE = 108 | CITE_BIB_FILES = 109 | #--------------------------------------------------------------------------- 110 | # Configuration options related to warning and progress messages 111 | #--------------------------------------------------------------------------- 112 | QUIET = YES 113 | WARNINGS = YES 114 | WARN_IF_UNDOCUMENTED = YES 115 | WARN_IF_DOC_ERROR = YES 116 | WARN_IF_INCOMPLETE_DOC = YES 117 | WARN_NO_PARAMDOC = YES 118 | WARN_IF_UNDOC_ENUM_VAL = YES 119 | WARN_AS_ERROR = YES 120 | WARN_FORMAT = "$file:$line: $text" 121 | WARN_LINE_FORMAT = "at line $line of file $file" 122 | WARN_LOGFILE = 123 | #--------------------------------------------------------------------------- 124 | # Configuration options related to the input files 125 | #--------------------------------------------------------------------------- 126 | INPUT_ENCODING = UTF-8 127 | FILE_PATTERNS = *.cpp \ 128 | *.hpp 129 | RECURSIVE = YES 130 | EXCLUDE = 131 | EXCLUDE_SYMLINKS = NO 132 | EXCLUDE_PATTERNS = 133 | EXCLUDE_SYMBOLS = 134 | EXAMPLE_PATH = 135 | EXAMPLE_PATTERNS = * 136 | EXAMPLE_RECURSIVE = NO 137 | IMAGE_PATH = 138 | INPUT_FILTER = 139 | FILTER_PATTERNS = 140 | FILTER_SOURCE_FILES = NO 141 | FILTER_SOURCE_PATTERNS = 142 | FORTRAN_COMMENT_AFTER = 72 143 | #--------------------------------------------------------------------------- 144 | # Configuration options related to source browsing 145 | #--------------------------------------------------------------------------- 146 | SOURCE_BROWSER = YES 147 | INLINE_SOURCES = NO 148 | STRIP_CODE_COMMENTS = YES 149 | REFERENCED_BY_RELATION = NO 150 | REFERENCES_RELATION = YES 151 | REFERENCES_LINK_SOURCE = YES 152 | SOURCE_TOOLTIPS = YES 153 | USE_HTAGS = NO 154 | VERBATIM_HEADERS = YES 155 | #--------------------------------------------------------------------------- 156 | # Configuration options related to the alphabetical class index 157 | #--------------------------------------------------------------------------- 158 | ALPHABETICAL_INDEX = YES 159 | IGNORE_PREFIX = 160 | #--------------------------------------------------------------------------- 161 | # Configuration options related to the HTML output 162 | #--------------------------------------------------------------------------- 163 | GENERATE_HTML = YES 164 | HTML_OUTPUT = html 165 | HTML_FILE_EXTENSION = .html 166 | HTML_HEADER = 167 | HTML_FOOTER = 168 | HTML_STYLESHEET = 169 | HTML_EXTRA_STYLESHEET = 170 | HTML_EXTRA_FILES = 171 | HTML_COLORSTYLE = AUTO_LIGHT 172 | HTML_COLORSTYLE_HUE = 220 173 | HTML_COLORSTYLE_SAT = 100 174 | HTML_COLORSTYLE_GAMMA = 80 175 | HTML_TIMESTAMP = YES 176 | HTML_DYNAMIC_MENUS = YES 177 | HTML_DYNAMIC_SECTIONS = NO 178 | HTML_INDEX_NUM_ENTRIES = 100 179 | GENERATE_DOCSET = NO 180 | DOCSET_FEEDNAME = "Doxygen generated docs" 181 | DOCSET_FEEDURL = 182 | DOCSET_BUNDLE_ID = org.doxygen.Project 183 | DOCSET_PUBLISHER_ID = org.doxygen.Publisher 184 | DOCSET_PUBLISHER_NAME = Publisher 185 | GENERATE_HTMLHELP = NO 186 | CHM_FILE = 187 | HHC_LOCATION = 188 | GENERATE_CHI = NO 189 | CHM_INDEX_ENCODING = 190 | BINARY_TOC = NO 191 | TOC_EXPAND = NO 192 | GENERATE_QHP = NO 193 | QCH_FILE = 194 | QHP_NAMESPACE = org.doxygen.Project 195 | QHP_VIRTUAL_FOLDER = doc 196 | QHP_CUST_FILTER_NAME = 197 | QHP_CUST_FILTER_ATTRS = 198 | QHP_SECT_FILTER_ATTRS = 199 | QHG_LOCATION = 200 | GENERATE_ECLIPSEHELP = NO 201 | ECLIPSE_DOC_ID = org.doxygen.Project 202 | DISABLE_INDEX = YES 203 | GENERATE_TREEVIEW = YES 204 | FULL_SIDEBAR = YES 205 | ENUM_VALUES_PER_LINE = 4 206 | TREEVIEW_WIDTH = 250 207 | EXT_LINKS_IN_WINDOW = NO 208 | OBFUSCATE_EMAILS = YES 209 | HTML_FORMULA_FORMAT = png 210 | FORMULA_FONTSIZE = 10 211 | FORMULA_MACROFILE = 212 | USE_MATHJAX = NO 213 | MATHJAX_VERSION = MathJax_2 214 | MATHJAX_FORMAT = HTML-CSS 215 | MATHJAX_RELPATH = 216 | MATHJAX_EXTENSIONS = 217 | MATHJAX_CODEFILE = 218 | SEARCHENGINE = YES 219 | SERVER_BASED_SEARCH = NO 220 | EXTERNAL_SEARCH = NO 221 | SEARCHENGINE_URL = 222 | SEARCHDATA_FILE = searchdata.xml 223 | EXTERNAL_SEARCH_ID = 224 | EXTRA_SEARCH_MAPPINGS = 225 | #--------------------------------------------------------------------------- 226 | # Configuration options related to the LaTeX output 227 | #--------------------------------------------------------------------------- 228 | GENERATE_LATEX = NO 229 | #--------------------------------------------------------------------------- 230 | # Configuration options related to the RTF output 231 | #--------------------------------------------------------------------------- 232 | GENERATE_RTF = NO 233 | #--------------------------------------------------------------------------- 234 | # Configuration options related to the man page output 235 | #--------------------------------------------------------------------------- 236 | GENERATE_MAN = NO 237 | #--------------------------------------------------------------------------- 238 | # Configuration options related to the XML output 239 | #--------------------------------------------------------------------------- 240 | GENERATE_XML = NO 241 | #--------------------------------------------------------------------------- 242 | # Configuration options related to the DOCBOOK output 243 | #--------------------------------------------------------------------------- 244 | GENERATE_DOCBOOK = NO 245 | #--------------------------------------------------------------------------- 246 | # Configuration options for the AutoGen Definitions output 247 | #--------------------------------------------------------------------------- 248 | GENERATE_AUTOGEN_DEF = NO 249 | #--------------------------------------------------------------------------- 250 | # Configuration options related to the Perl module output 251 | #--------------------------------------------------------------------------- 252 | GENERATE_PERLMOD = NO 253 | #--------------------------------------------------------------------------- 254 | # Configuration options related to the preprocessor 255 | #--------------------------------------------------------------------------- 256 | ENABLE_PREPROCESSING = YES 257 | MACRO_EXPANSION = NO 258 | EXPAND_ONLY_PREDEF = NO 259 | SEARCH_INCLUDES = YES 260 | INCLUDE_PATH = 261 | INCLUDE_FILE_PATTERNS = 262 | PREDEFINED = 263 | EXPAND_AS_DEFINED = 264 | SKIP_FUNCTION_MACROS = YES 265 | #--------------------------------------------------------------------------- 266 | # Configuration options related to external references 267 | #--------------------------------------------------------------------------- 268 | TAGFILES = 269 | GENERATE_TAGFILE = 270 | ALLEXTERNALS = NO 271 | EXTERNAL_GROUPS = YES 272 | EXTERNAL_PAGES = YES 273 | #--------------------------------------------------------------------------- 274 | # Configuration options related to the dot tool 275 | #--------------------------------------------------------------------------- 276 | DIA_PATH = 277 | HIDE_UNDOC_RELATIONS = YES 278 | HAVE_DOT = Yes 279 | DOT_NUM_THREADS = 0 280 | DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10" 281 | DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10" 282 | DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4" 283 | DOT_FONTPATH = 284 | CLASS_GRAPH = YES 285 | COLLABORATION_GRAPH = YES 286 | GROUP_GRAPHS = YES 287 | UML_LOOK = NO 288 | UML_LIMIT_NUM_FIELDS = 10 289 | DOT_UML_DETAILS = NO 290 | DOT_WRAP_THRESHOLD = 17 291 | TEMPLATE_RELATIONS = NO 292 | INCLUDE_GRAPH = YES 293 | INCLUDED_BY_GRAPH = YES 294 | CALL_GRAPH = NO 295 | CALLER_GRAPH = NO 296 | GRAPHICAL_HIERARCHY = YES 297 | DIRECTORY_GRAPH = YES 298 | DIR_GRAPH_MAX_DEPTH = 1 299 | DOT_IMAGE_FORMAT = svg 300 | INTERACTIVE_SVG = YES 301 | DOT_PATH = 302 | DOTFILE_DIRS = 303 | MSCFILE_DIRS = 304 | DIAFILE_DIRS = 305 | PLANTUML_JAR_PATH = 306 | PLANTUML_CFG_FILE = 307 | PLANTUML_INCLUDE_PATH = 308 | DOT_GRAPH_MAX_NODES = 50 309 | MAX_DOT_GRAPH_DEPTH = 0 310 | DOT_MULTI_TARGETS = NO 311 | GENERATE_LEGEND = YES 312 | DOT_CLEANUP = YES 313 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 3 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) since its version 1.0.0. 4 | 5 | ## [Unreleased] - [Doc:Unreleased] 6 | 7 | ### Changed 8 | 9 | - [PR27](https://github.com/scipopt/SCIPpp/pull/27) Update to SCIP 9.2.0. 10 | 11 | ## Added 12 | 13 | - [PR31](https://github.com/scipopt/SCIPpp/pull/31) Add `InitialSolution` and `Model::addSolution`. 14 | - [PR28](https://github.com/scipopt/SCIPpp/pull/28) Add `Var::getVar`. 15 | 16 | ## [1.2.0] - 2024-05-21 17 | 18 | ### Changed 19 | 20 | - [PR20](https://github.com/scipopt/SCIPpp/pull/20) `LinExpr` can be constructed from any arithmetic type without 21 | requiring a `static_cast`. 22 | 23 | ### Added 24 | 25 | - [PR22](https://github.com/scipopt/SCIPpp/pull/22) Add `Model::getSolvingStatistic`. 26 | - [PR18](https://github.com/scipopt/SCIPpp/pull/18) Add `Var::isVoid`. 27 | 28 | ### Deprecated 29 | 30 | - [PR22](https://github.com/scipopt/SCIPpp/pull/22) 31 | `Model::getPrimalbound()`, use `Model::getSolvingStatistic(statistics::PRIMALBOUND)` instead. 32 | 33 | ## [1.1.0] - 2023-10-15 34 | 35 | ### Changed 36 | 37 | - [PR14](https://github.com/scipopt/SCIPpp/pull/14) Using SCIP 8.0.4 now. 38 | 39 | ### Added 40 | 41 | - [PR12](https://github.com/scipopt/SCIPpp/pull/12) 42 | Expose SCIP counterparts via `Model::epsilon`, `Model::round`, and `Model::isZero`. 43 | - [PR11](https://github.com/scipopt/SCIPpp/pull/11) 44 | IO methods `Model::writeOrigProblem` to write a model to a file or standard output. 45 | 46 | ### Fixed 47 | 48 | - [PR12](https://github.com/scipopt/SCIPpp/pull/12) 49 | Added more const-correctness 50 | 51 | ## [1.0.2] - 2023-08-12 52 | 53 | ### Fixed 54 | 55 | - [PR7](https://github.com/scipopt/SCIPpp/pull/7) 56 | Export symbols on Windows. 57 | 58 | ## [1.0.1] - 2023-08-10 59 | 60 | ### Added 61 | 62 | - [PR6](https://github.com/scipopt/SCIPpp/pull/6) 63 | Added attributes `description`, `package_type`, `topics`, `license`, and `homepage` to `conanfile.py`. 64 | 65 | ### Fixed 66 | 67 | - [PR6](https://github.com/scipopt/SCIPpp/pull/6) 68 | Downgraded minimum CMake version from 3.16 to 3.15.7 69 | 70 | ## [1.0.0] - 2023-08-09 71 | 72 | Initial release 73 | 74 | [Doc:Unreleased]: https://scipopt.github.io/SCIPpp/ 75 | [Unreleased]: https://github.com/scipopt/SCIPpp/compare/1.2.0...main 76 | [1.2.0]: https://github.com/scipopt/SCIPpp/releases/tag/1.2.0 77 | [1.1.0]: https://github.com/scipopt/SCIPpp/releases/tag/1.1.0 78 | [1.0.2]: https://github.com/scipopt/SCIPpp/releases/tag/1.0.2 79 | [1.0.1]: https://github.com/scipopt/SCIPpp/releases/tag/1.0.1 80 | [1.0.0]: https://github.com/scipopt/SCIPpp/releases/tag/1.0.0 81 | -------------------------------------------------------------------------------- /code_of_conduct.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | `sospo (at) dbschenker (dot) com`. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0]. 121 | 122 | Community Impact Guidelines were inspired by 123 | [Mozilla's code of conduct enforcement ladder][Mozilla CoC]. 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | [https://www.contributor-covenant.org/faq][FAQ]. Translations are available 127 | at [https://www.contributor-covenant.org/translations][translations]. 128 | 129 | [homepage]: https://www.contributor-covenant.org 130 | [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html 131 | [Mozilla CoC]: https://github.com/mozilla/diversity 132 | [FAQ]: https://www.contributor-covenant.org/faq 133 | [translations]: https://www.contributor-covenant.org/translations 134 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "test" 3 | comment: 4 | layout: "diff, files" 5 | behavior: default 6 | require_changes: false 7 | require_base: false 8 | require_head: true 9 | -------------------------------------------------------------------------------- /conanfile.py: -------------------------------------------------------------------------------- 1 | from conan import ConanFile 2 | from conan.errors import ConanInvalidConfiguration 3 | from conan.tools.build import check_min_cppstd 4 | from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout 5 | from conan.tools.microsoft import check_min_vs, is_msvc 6 | from conan.tools.scm import Git, Version 7 | 8 | 9 | class ScipPlusPlus(ConanFile): 10 | name = "scippp" 11 | settings = "os", "compiler", "build_type", "arch" 12 | generators = "CMakeDeps" 13 | exports_sources = "CMakeLists.txt", "source/*", "include/*" 14 | description = "SCIP++ is a C++ wrapper for SCIP's C interface" 15 | package_type = "library" 16 | topics = ("mip", "solver", "linear", "programming") 17 | license = "Apache-2.0" 18 | homepage = "https://github.com/scipopt/SCIPpp" 19 | options = { 20 | "with_tests": [True, False], 21 | "with_utils": [True, False], 22 | "shared": [True, False], 23 | "fPIC": [True, False] 24 | } 25 | default_options = { 26 | "with_tests": False, 27 | "with_utils": False, 28 | "shared": False, 29 | "fPIC": True 30 | } 31 | 32 | @property 33 | def _min_cppstd(self): 34 | return 17 35 | 36 | @property 37 | def _compilers_minimum_version(self): 38 | return { 39 | "gcc": "8", 40 | "clang": "7", 41 | "apple-clang": "11", 42 | "msvc": "192" 43 | } 44 | 45 | def validate(self): 46 | if self.settings.compiler.cppstd: 47 | check_min_cppstd(self, self._min_cppstd) 48 | check_min_vs(self, 192) 49 | if not is_msvc(self): 50 | minimum_version = self._compilers_minimum_version.get(str(self.settings.compiler), False) 51 | if minimum_version and Version(self.settings.compiler.version) < minimum_version: 52 | raise ConanInvalidConfiguration( 53 | f"{self.ref} requires C++{self._min_cppstd}, which your compiler does not support." 54 | ) 55 | 56 | def config_options(self): 57 | if self.settings.os == "Windows": 58 | del self.options.fPIC 59 | 60 | def configure(self): 61 | if self.options.shared: 62 | self.options.rm_safe("fPIC") 63 | 64 | def set_version(self): 65 | if self.version is None: 66 | git = Git(self, folder=self.recipe_folder) 67 | try: 68 | self.version = git.run("describe --tags --dirty=-d").strip() 69 | except: 70 | self.version = "1.3.0-alpha" 71 | 72 | def layout(self): 73 | cmake_layout(self) 74 | 75 | def requirements(self): 76 | self.requires("scip/9.2.0", transitive_headers=True) 77 | if self.options.with_tests: 78 | self.requires("boost/[>=1.84.0 <2]") # required only for tests 79 | 80 | def generate(self): 81 | tc = CMakeToolchain(self) 82 | tc.variables[self.name + "_version"] = self.version 83 | tc.variables["BUILD_TESTS"] = self.options.with_tests 84 | tc.variables["BUILD_UTILS"] = self.options.with_utils 85 | tc.generate() 86 | 87 | def build(self): 88 | cmake = CMake(self) 89 | cmake.configure() 90 | cmake.build() 91 | 92 | def package(self): 93 | cmake = CMake(self) 94 | cmake.install() 95 | 96 | def package_info(self): 97 | self.cpp_info.libs = ["ScipPP"] 98 | -------------------------------------------------------------------------------- /include/scippp/constant_coefficient.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace scippp { 6 | 7 | /** 8 | * A class which an index operator that always returns a constant value. 9 | * @since 1.0.0 10 | */ 11 | class ConstantCoefficient { 12 | //! Constant value to return. 13 | const double m_COEFF; 14 | 15 | public: 16 | /** 17 | * Stores the constant value that will be returned by the index operator. 18 | * @since 1.0.0 19 | * @param c Constant value to return. 20 | */ 21 | explicit constexpr ConstantCoefficient(double c) 22 | : m_COEFF { c } 23 | { 24 | } 25 | /** 26 | * Index operator that always returns the same constant value. 27 | * @since 1.0.0 28 | * @return the constant value. 29 | */ 30 | inline double operator[](std::size_t) const 31 | { 32 | return m_COEFF; 33 | } 34 | }; 35 | 36 | /** 37 | * An object which index operator always returns 0. 38 | * @since 1.0.0 39 | */ 40 | static constexpr ConstantCoefficient COEFF_ZERO { 0 }; 41 | /** 42 | * An object which index operator always returns 1. 43 | * @since 1.0.0 44 | */ 45 | static constexpr ConstantCoefficient COEFF_ONE { 1 }; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /include/scippp/initial_solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace scippp { 6 | 7 | // forward declare 8 | class Var; 9 | 10 | /** 11 | * A primal solution to be added to %SCIP's solution pool. 12 | * 13 | * @since 1.3.0 14 | */ 15 | class InitialSolution { 16 | friend class Model; 17 | //! Variable assignment in the initial solution. 18 | std::map m_values {}; 19 | 20 | public: 21 | /** 22 | * Sets the value for a variable in the solution. 23 | * 24 | * @since 1.3.0 25 | * @param var Variable to assign a value to. 26 | * @param value to assign to the variable. 27 | */ 28 | void setValue(const Var& var, double value); 29 | 30 | /** 31 | * Access the mutable value assigned to the variable. 32 | * 33 | * Initializes the assigned value to 0 if no value was assigned to the variable so far. 34 | * 35 | * @since 1.3.0 36 | * @param var Variable to manipulate the value in the solution for. 37 | * @return Mutable value assigned to the variable. 38 | */ 39 | double& operator()(const Var& var); 40 | }; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /include/scippp/lin_expr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "scippp/var.hpp" 6 | 7 | namespace scippp { 8 | 9 | /** 10 | * Represents a linear combination of variables plus a constant term. 11 | * @since 1.0.0 12 | */ 13 | class LinExpr { 14 | friend class Model; 15 | //! Constant term. 16 | double m_constant { 0.0 }; 17 | //! Variables in the linear combination. 18 | std::vector m_vars {}; 19 | //! Coefficients in the linear combination. 20 | std::vector m_coeffs {}; 21 | 22 | public: 23 | /** 24 | * Sets constant term and linear combination to zero. 25 | * @since 1.0.0 26 | */ 27 | LinExpr() = default; 28 | /** 29 | * Creates a linear expression with no variables. 30 | * @since 1.0.0 31 | * @remark This is on purpose not an explicit c'tor to allow expressions like x <= 1. 32 | * @tparam Arithmetic type that will be casted to \c double. 33 | * @warning \c Arithmetic is casted to \c double without creating a narrowing warning! 34 | * @param constant Constant term to set. 35 | */ 36 | template , bool> = true> 37 | LinExpr(Arithmetic constant) 38 | : m_constant { static_cast(constant) } 39 | { 40 | } 41 | /** 42 | * Creates a linear expression with zero as constant the given variable with coefficient one. 43 | * @since 1.0.0 44 | * @remark This is on purpose not an explicit c'tor to allow expressions like x <= 1. 45 | * @param var Variable to store with coefficient one in the expression. 46 | */ 47 | LinExpr(const Var& var); 48 | 49 | /** 50 | * Returns the constant term of the expression. 51 | * @since 1.0.0 52 | * @return the constant term of the expression. 53 | */ 54 | [[nodiscard]] double getConstant() const; 55 | 56 | /** 57 | * Add another linear expression to this. 58 | * @since 1.0.0 59 | * @param expr Other linear expression to add. 60 | * @return Updated sum. 61 | */ 62 | LinExpr& operator+=(const LinExpr& expr); 63 | /** 64 | * Subtract another expression from this. 65 | * @since 1.0.0 66 | * @param expr Right-hand-side of the subtraction. 67 | * @return Updated expression. 68 | */ 69 | LinExpr& operator-=(const LinExpr& expr); 70 | /** 71 | * Multiply all coefficients. 72 | * @since 1.0.0 73 | * @param factor to multiply all coefficients with. 74 | * @return Scaled expression. 75 | */ 76 | LinExpr& operator*=(double factor); 77 | }; 78 | 79 | /** 80 | * Scales a linear expression by a factor. 81 | * @since 1.0.0 82 | * @param factor to scale the expression with. 83 | * @param rhs expression to scale. 84 | * @return scaled expression. 85 | */ 86 | LinExpr operator*(double factor, LinExpr rhs); 87 | 88 | /** 89 | * Creates a new linear expression as the sum of two. 90 | * @since 1.0.0 91 | * @param lhs First summand. 92 | * @param rhs Second summand. 93 | * @return Sum of both expressions. 94 | */ 95 | LinExpr operator+(LinExpr lhs, const LinExpr& rhs); 96 | 97 | /** 98 | * Creates the new linear expression \p lhs - \p rhs. 99 | * @since 1.0.0 100 | * @param lhs minuend. 101 | * @param rhs subtrahend. 102 | * @return minuend minus subtrahend. 103 | */ 104 | LinExpr operator-(LinExpr lhs, const LinExpr& rhs); 105 | 106 | } 107 | -------------------------------------------------------------------------------- /include/scippp/lin_ineq.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "scippp/lin_expr.hpp" 7 | 8 | namespace scippp { 9 | 10 | /** 11 | * Represents a linear inequality: lhs <= expr <= rhs. 12 | * 13 | * Can be constructed only by operators. 14 | * @since 1.0.0 15 | */ 16 | class LinIneq { 17 | //! Left-hand-side of the inequality: lhs <= expr <= rhs. 18 | double m_lhs; 19 | //! Reft-hand-side of the inequality: lhs <= expr <= rhs. Non-present values are replaced by infinity later 20 | std::optional m_rhs; 21 | //! Linear expression of the inequality: lhs <= expr <= rhs. 22 | LinExpr m_linExpr; 23 | friend class Model; 24 | friend LinIneq operator<=(const LinExpr& lhs, const LinExpr& rhs); 25 | friend LinIneq operator==(const LinExpr& lhs, const LinExpr& rhs); 26 | //! Private c'tor so that such objects cannot be created by the user manually. 27 | LinIneq() = default; 28 | }; 29 | 30 | /** 31 | * Creates the inequality \p lhs <= \p rhs. 32 | * @since 1.0.0 33 | * @param lhs Left-hand-side of the less-or-equal inequality. 34 | * @param rhs Right-hand-side of the less-or-equal inequality. 35 | * @return Inequality less-or-equal. 36 | */ 37 | LinIneq operator<=(const LinExpr& lhs, const LinExpr& rhs); 38 | 39 | /** 40 | * Creates the equality \p lhs == \p rhs. 41 | * @since 1.0.0 42 | * @param lhs Left-hand-side of the equality. 43 | * @param rhs Right-hand-side of the equality. 44 | * @return Equality inequality. 45 | */ 46 | LinIneq operator==(const LinExpr& lhs, const LinExpr& rhs); 47 | 48 | /** 49 | * Creates the inequality \p lhs >= \p rhs. 50 | * @since 1.0.0 51 | * @param lhs Left-hand-side of the greater-or-equal inequality. 52 | * @param rhs Right-hand-side of the greater-or-equal inequality. 53 | * @return Inequality greater-or-equal. 54 | */ 55 | LinIneq operator>=(const LinExpr& lhs, const LinExpr& rhs); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /include/scippp/model.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "scippp/constant_coefficient.hpp" 14 | #include "scippp/initial_solution.hpp" 15 | #include "scippp/lin_expr.hpp" 16 | #include "scippp/lin_ineq.hpp" 17 | #include "scippp/param.hpp" 18 | #include "scippp/solution.hpp" 19 | #include "scippp/statistics.hpp" 20 | #include "scippp/var.hpp" 21 | #include "scippp/var_type.hpp" 22 | 23 | //! C++ wrapper for %SCIP. 24 | namespace scippp { 25 | 26 | //! Optimization goal. 27 | enum class Sense { 28 | //! Maximize 29 | MAXIMIZE = SCIP_OBJSENSE_MAXIMIZE, 30 | //! Minimize 31 | MINIMIZE = SCIP_OBJSENSE_MINIMIZE 32 | }; 33 | 34 | /** 35 | * A %SCIP optimization model. 36 | * 37 | * Variables and constraints are automatically released when the model is destructed. 38 | * @since 1.0.0 39 | */ 40 | class Model { 41 | //! Pointer to the underlying %SCIP object. 42 | Scip* m_scip; 43 | //! \c true if %SCIPcreate was called in the c'tor and %SCIPfree has to be called in the d'tor. 44 | bool m_weCreatedANewScipObject { false }; 45 | //! Variables. 46 | std::vector m_vars {}; 47 | //! Constraints. 48 | std::vector m_cons {}; 49 | //! Stores the return of the last %SCIP call when the default call wrapper is used. 50 | SCIP_Retcode m_lastReturnCode; 51 | //! Wrapper for every call to %SCIP's %C %API. 52 | std::function m_scipCallWrapper; 53 | 54 | public: 55 | /** 56 | * Creates an empty problem and sets the optimization goal to Sense::MINIMIZE. 57 | * 58 | * By default, all calls to the underlying %C %API are wrapped and the last return code is stored. 59 | * 60 | * @since 1.0.0 61 | * @param name for the problem. 62 | * @param scip to create the problem in. If \c nullptr, a new %SCIP data structure will be created. 63 | * @param includeDefaultPlugins if \c true, the default plugins are added to \p scip. 64 | */ 65 | explicit Model(const std::string& name, SCIP* scip = nullptr, bool includeDefaultPlugins = true); 66 | 67 | /** 68 | * Releases the variables and constraints. 69 | * @since 1.0.0 70 | */ 71 | ~Model(); 72 | 73 | /** 74 | * Gets the return code of the last call to %SCIP's %C %API when the default call wrapper is used. 75 | * @since 1.0.0 76 | * @return return code of the last call to %SCIP's %C %API. 77 | */ 78 | [[nodiscard]] SCIP_Retcode getLastReturnCode() const; 79 | 80 | /** 81 | * Replace the current wrapper for every call to %SCIP's %C %API. 82 | * @since 1.0.0 83 | * @param wrapper New wrapper tp use. 84 | */ 85 | void setScipCallWrapper(std::function wrapper); 86 | 87 | /** 88 | * Adds a variable to the model. 89 | * @since 1.0.0 90 | * @param name of the variable when the model is written. 91 | * @param coeff Coefficient in the objective function. 92 | * @param varType variable type. 93 | * @param lb lower bound. \c std::nullopt is interpreted as -infinity. 94 | * @param ub upper bound. \c std::nullopt is interpreted as infinity. 95 | * @return Reference to the newly created variable. 96 | */ 97 | Var& addVar( 98 | const std::string& name, 99 | SCIP_Real coeff = 0.0, 100 | VarType varType = VarType::CONTINUOUS, 101 | std::optional lb = 0.0, 102 | std::optional ub = 1.0); 103 | 104 | /** 105 | * Adds multiple variables to the model. 106 | * @since 1.0.0 107 | * @tparam CoeffType Type of the object holding the coefficients. They are accessed via \c [i] where i goes from 0 108 | * to \p numVars - 1. 109 | * @param prefix to construct variable names from: prefix + index. 110 | * @param numVars number of variables to create. 111 | * @param coeffs Object holding the coefficients for the objective function. 112 | * @param varType variable type. 113 | * @param lb lower bound. \c std::nullopt is interpreted as -infinity. 114 | * @param ub upper bound. \c std::nullopt is interpreted as infinity. 115 | * @return Vector of variables. 116 | */ 117 | template 118 | std::vector addVars( 119 | const std::string& prefix, 120 | size_t numVars, 121 | const CoeffType& coeffs = COEFF_ZERO, 122 | VarType varType = VarType::CONTINUOUS, 123 | std::optional lb = 0.0, 124 | std::optional ub = 1.0) 125 | { 126 | std::vector result; 127 | result.reserve(numVars); 128 | for (size_t index { 0 }; index < numVars; index++) { 129 | result.push_back(addVar(prefix + std::to_string(index), coeffs[index], varType, lb, ub)); 130 | } 131 | return result; 132 | } 133 | 134 | /** 135 | * Adds multiple variables to the model. 136 | * 137 | * This method can be used when the number of variables to add is known at compile time. The result can be used in a 138 | * structured binding. 139 | * 140 | * @since 1.0.0 141 | * @tparam NumVars Number of variables to add. 142 | * @tparam CoeffType Type of the object holding the coefficients. They are accessed via \c [i] where i goes from 0 143 | * to \p NumVars - 1. 144 | * @param prefix to construct variable names from: prefix + index. 145 | * @param coeffs Object holding the coefficients for the objective function. 146 | * @param varType variable type. 147 | * @param lb lower bound. 148 | * @param ub upper bound. 149 | * @return Array of variables. 150 | */ 151 | template 152 | std::array addVars( 153 | const std::string& prefix, 154 | const CoeffType& coeffs = COEFF_ZERO, 155 | VarType varType = VarType::CONTINUOUS, 156 | std::optional lb = 0.0, 157 | std::optional ub = 1.0) 158 | { 159 | std::array result; 160 | auto vec { addVars(prefix, NumVars, coeffs, varType, lb, ub) }; 161 | std::copy_n(std::make_move_iterator(vec.begin()), NumVars, result.begin()); 162 | return result; 163 | } 164 | 165 | /** 166 | * Adds a constraint to the model. 167 | * @since 1.0.0 168 | * @param ineq linear inequality to add. 169 | * @param name for the constraint when the model is written. 170 | */ 171 | void addConstr(const LinIneq& ineq, const std::string& name); 172 | 173 | /** 174 | * Infinity according the %SCIP config. To be used in variable bounds and constants in constraints. 175 | * @since 1.0.0 176 | * @return infinity according the %SCIP config. 177 | */ 178 | [[nodiscard]] SCIP_Real infinity() const; 179 | 180 | /** 181 | * Value treated as zero. 182 | * @since 1.1.0 183 | * @return value treated as zero. 184 | */ 185 | [[nodiscard]] SCIP_Real epsilon() const; 186 | 187 | /** 188 | * Rounds value to the nearest integer with epsilon tolerance. 189 | * @since 1.1.0 190 | * @param value to round 191 | * @return nearest integer as double 192 | */ 193 | [[nodiscard]] SCIP_Real round(SCIP_Real value) const; 194 | 195 | /** 196 | * Checks, if value is in range epsilon of 0.0. 197 | * @since 1.1.0 198 | * @param value to check 199 | * @return \c true iff the value is in range epsilon of 0.0. 200 | */ 201 | [[nodiscard]] bool isZero(SCIP_Real value) const; 202 | 203 | /** 204 | * Solve the model. 205 | * @since 1.0.0 206 | */ 207 | void solve(); 208 | 209 | /** 210 | * Set objective goal. 211 | * @since 1.0.0 212 | * @param objsense Minimize or Maximize. 213 | */ 214 | void setObjsense(Sense objsense); 215 | 216 | /** 217 | * Returns the solution status. 218 | * @since 1.0.0 219 | * @return solution status. 220 | */ 221 | [[nodiscard]] SCIP_Status getStatus() const; 222 | 223 | /** 224 | * Query statistics about the solving process. 225 | * 226 | * @tparam T Type of the statistics value. 227 | * @since 1.2.0 228 | * @param statistic Statistics value to access. 229 | * @return Statistics value. 230 | */ 231 | template 232 | [[nodiscard]] T getSolvingStatistic(const statistics::Statistic& statistic) const 233 | { 234 | return statistic(m_scip); 235 | } 236 | 237 | /** 238 | * Returns the number of feasible primal solutions stored in the solution storage. 239 | * @since 1.0.0 240 | * @return number of solutions. 241 | */ 242 | [[nodiscard]] int getNSols() const; 243 | 244 | /** 245 | * Returns the best feasible primal solution found so far or best solution candidate. 246 | * @since 1.0.0 247 | * @return best feasible primal solution. 248 | */ 249 | [[nodiscard]] Solution getBestSol() const; 250 | 251 | /** 252 | * Returns the objective value of best solution. 253 | * @since 1.0.0 254 | * @deprecated since 1.2.0, use getSolvingStatistic with statistics::PRIMALBOUND instead. 255 | * @return objective value of best solution. 256 | */ 257 | [[deprecated("use getSolvingStatistic with statistics::PRIMALBOUND instead")]] // 258 | [[nodiscard]] double 259 | getPrimalbound() const; 260 | 261 | /** 262 | * Sets a parameter. 263 | * 264 | * See the namespace scippp::params for a list of parameters, or create new ones using params::Param. 265 | * 266 | * @since 1.0.0 267 | * @tparam T Type of the value. 268 | * @tparam PT Value type the parameter expects 269 | * @param parameter to set. 270 | * @param value to set the parameter to. 271 | */ 272 | template 273 | void setParam(params::Param parameter, T value) 274 | { 275 | auto ptValue { static_cast(value) }; 276 | const auto* cName { parameter.scipName.data() }; 277 | if constexpr (std::is_same_v) { 278 | m_scipCallWrapper(SCIPsetIntParam(m_scip, cName, ptValue)); 279 | } else if constexpr (std::is_same_v) { 280 | m_scipCallWrapper(SCIPsetRealParam(m_scip, cName, ptValue)); 281 | } else if constexpr (std::is_same_v) { 282 | m_scipCallWrapper(SCIPsetCharParam(m_scip, cName, value)); 283 | } else if constexpr (std::is_same_v) { 284 | m_scipCallWrapper(SCIPsetBoolParam(m_scip, cName, value)); 285 | } else if constexpr (std::is_same_v) { 286 | m_scipCallWrapper(SCIPsetLongintParam(m_scip, cName, value)); 287 | } else if constexpr (std::is_same_v) { 288 | m_scipCallWrapper(SCIPsetStringParam(m_scip, cName, value.c_str())); 289 | } else { 290 | // make this not compile 291 | // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2593r0.html#workarounds 292 | static_assert(sizeof(T) == 0); 293 | } 294 | } 295 | 296 | /** 297 | * Writes original problem to file. 298 | * 299 | * @since 1.1.0 300 | * @param filename output file name including extension 301 | * @param genericNames using generic variable (x0, x1, ...) and constraint names (c0, c1, ...) instead of the 302 | * user-given names? 303 | * @attention Do not use an std::string or std::filesystem::path as argument \p filename, 304 | * as this will call the other overload instead! 305 | */ 306 | void writeOrigProblem(const std::filesystem::directory_entry& filename, bool genericNames = false) const; 307 | 308 | /** 309 | * Writes original problem to standard output. 310 | * 311 | * @since 1.1.0 312 | * @param extension file extension to derive the output format from 313 | * @param genericNames using generic variable (x0, x1, ...) and constraint names (c0, c1, ...) instead of the 314 | * user-given names? 315 | */ 316 | void writeOrigProblem(const std::string& extension, bool genericNames = false) const; 317 | 318 | /** 319 | * Returns a pointer to the underlying %SCIP object. 320 | * 321 | * @since 1.0.0 322 | * @attention Use this to access the raw SCIP object. That is required only for use-cases not supported by SCIP++. 323 | * Consider adding the feature you are using to SCIP++! 324 | * @return the underlying %SCIP object. 325 | */ 326 | [[deprecated(R"(Use this to access the raw SCIP object. 327 | That is only required for use-cases not supported by SCIP++. 328 | Consider adding the feature you are using to SCIP++!)")]] [[nodiscard]] Scip* 329 | scip() const; 330 | 331 | /** 332 | * Adds a solution to %SCIP's solution pool. 333 | * 334 | * @since 1.3.0 335 | * @param initialSolution to add to the solution pool. 336 | * @param printReason Should all reasons of violations be printed? 337 | * @param completely Should all violations be checked if \p printReason is true? 338 | * @param checkBounds Should the bounds of the variables be checked? 339 | * @param checkIntegrality Should integrality be checked? 340 | * @param checkLpRows Do constraints represented by rows in the current LP have to be checked? 341 | * @return \c true iff the solution was feasible and stored in the solution storage (i.e, good enough to keep). 342 | */ 343 | bool addSolution( 344 | const InitialSolution& initialSolution, 345 | bool printReason = true, 346 | bool completely = true, 347 | bool checkBounds = true, 348 | bool checkIntegrality = true, 349 | bool checkLpRows = true); 350 | }; 351 | } 352 | -------------------------------------------------------------------------------- /include/scippp/param.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //! Enum equivalent of SCIP's string parameters. 6 | namespace scippp::params { 7 | 8 | /** 9 | * Stores the argument type and string representation of a parameter. 10 | * @since 1.0.0 11 | * @tparam baseType Parameter value type. 12 | */ 13 | template 14 | struct Param { 15 | //! Original name of the parameter in %SCIP. 16 | std::string_view scipName; 17 | /** 18 | * C'tor that stores the original parameter name and its type as template parameter. 19 | * @since 1.0.0 20 | * @param name Original name of the parameter in %SCIP. 21 | */ 22 | constexpr explicit Param(const std::string_view& name) 23 | : scipName { name } 24 | { 25 | } 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /include/scippp/solution.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // forward declare 4 | struct Scip; 5 | struct SCIP_Sol; 6 | 7 | namespace scippp { 8 | 9 | /** 10 | * Wrapper for a %SCIP solution. 11 | * @since 1.0.0 12 | */ 13 | struct Solution { 14 | //! Pointer to the underlying %SCIP object. 15 | Scip* scip { nullptr }; 16 | //! Pointer to the underlying %SCIP solution. 17 | SCIP_Sol* sol { nullptr }; 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /include/scippp/solving_statistics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "scippp/statistics.hpp" 4 | 5 | #include 6 | 7 | // the content of this file is auto-generated by extract_solvingstats.py 8 | // updated for SCIP 9.2.0 9 | 10 | namespace scippp::statistics { 11 | 12 | //! number of branch and bound runs performed, including the current run 13 | static const Statistic N_RUNS { &SCIPgetNRuns }; 14 | //! number of reoptimization runs performed, including the current run 15 | static const Statistic N_REOPT_RUNS { &SCIPgetNReoptRuns }; 16 | //! number of processed nodes in current run, including the focus node 17 | static const Statistic N_NODES { &SCIPgetNNodes }; 18 | //! total number of processed nodes in all runs, including the focus node 19 | static const Statistic N_TOTAL_NODES { &SCIPgetNTotalNodes }; 20 | //! number of leaf nodes processed with feasible relaxation solution 21 | static const Statistic N_FEASIBLE_LEAVES { &SCIPgetNFeasibleLeaves }; 22 | //! number of infeasible leaf nodes processed 23 | static const Statistic N_INFEASIBLE_LEAVES { &SCIPgetNInfeasibleLeaves }; 24 | //! number of processed leaf nodes that hit LP objective limit 25 | static const Statistic N_OBJLIM_LEAVES { &SCIPgetNObjlimLeaves }; 26 | //! number of global bound changes 27 | static const Statistic N_ROOTBOUND_CHGS { &SCIPgetNRootboundChgs }; 28 | //! number of global bound changes 29 | static const Statistic N_ROOTBOUND_CHGS_RUN { &SCIPgetNRootboundChgsRun }; 30 | //! number of times a selected node was from a cut off subtree 31 | static const Statistic N_DELAYED_CUTOFFS { &SCIPgetNDelayedCutoffs }; 32 | //! total number of LPs solved so far 33 | static const Statistic N_L_PS { &SCIPgetNLPs }; 34 | //! total number of iterations used so far in primal and dual simplex and barrier algorithm 35 | static const Statistic N_LP_ITERATIONS { &SCIPgetNLPIterations }; 36 | //! number of active non-zeros in the current transformed problem 37 | static const Statistic N_N_ZS { &SCIPgetNNZs }; 38 | //! total number of iterations used so far in primal and dual simplex and barrier algorithm for the root node 39 | static const Statistic N_ROOT_LP_ITERATIONS { &SCIPgetNRootLPIterations }; 40 | //! total number of iterations used in primal and dual simplex and barrier algorithm for the first root LP 41 | static const Statistic N_ROOT_FIRST_LP_ITERATIONS { &SCIPgetNRootFirstLPIterations }; 42 | //! total number of primal LPs solved so far 43 | static const Statistic N_PRIMAL_L_PS { &SCIPgetNPrimalLPs }; 44 | //! total number of iterations used so far in primal simplex 45 | static const Statistic N_PRIMAL_LP_ITERATIONS { &SCIPgetNPrimalLPIterations }; 46 | //! total number of dual LPs solved so far 47 | static const Statistic N_DUAL_L_PS { &SCIPgetNDualLPs }; 48 | //! total number of iterations used so far in dual simplex 49 | static const Statistic N_DUAL_LP_ITERATIONS { &SCIPgetNDualLPIterations }; 50 | //! total number of barrier LPs solved so far 51 | static const Statistic N_BARRIER_L_PS { &SCIPgetNBarrierLPs }; 52 | //! total number of iterations used so far in barrier algorithm 53 | static const Statistic N_BARRIER_LP_ITERATIONS { &SCIPgetNBarrierLPIterations }; 54 | //! total number of LPs solved so far that were resolved from an advanced start basis 55 | static const Statistic N_RESOLVE_L_PS { &SCIPgetNResolveLPs }; 56 | //! total number of simplex iterations used so far in primal and dual simplex calls where an advanced start 57 | static const Statistic N_RESOLVE_LP_ITERATIONS { &SCIPgetNResolveLPIterations }; 58 | //! total number of primal LPs solved so far that were resolved from an advanced start basis 59 | static const Statistic N_PRIMAL_RESOLVE_L_PS { &SCIPgetNPrimalResolveLPs }; 60 | //! total number of simplex iterations used so far in primal simplex calls where an advanced start 61 | static const Statistic N_PRIMAL_RESOLVE_LP_ITERATIONS { &SCIPgetNPrimalResolveLPIterations }; 62 | //! total number of dual LPs solved so far that were resolved from an advanced start basis 63 | static const Statistic N_DUAL_RESOLVE_L_PS { &SCIPgetNDualResolveLPs }; 64 | //! total number of simplex iterations used so far in dual simplex calls where an advanced start 65 | static const Statistic N_DUAL_RESOLVE_LP_ITERATIONS { &SCIPgetNDualResolveLPIterations }; 66 | //! total number of LPs solved so far for node relaxations 67 | static const Statistic N_NODE_L_PS { &SCIPgetNNodeLPs }; 68 | //! total number of LPs solved with 0 iteratins for node relaxations 69 | static const Statistic N_NODE_ZERO_ITERATION_L_PS { &SCIPgetNNodeZeroIterationLPs }; 70 | //! total number of simplex iterations used so far for node relaxations 71 | static const Statistic N_NODE_LP_ITERATIONS { &SCIPgetNNodeLPIterations }; 72 | //! total number of LPs solved so far for initial LP in node relaxations 73 | static const Statistic N_NODE_INIT_L_PS { &SCIPgetNNodeInitLPs }; 74 | //! total number of simplex iterations used so far for initial LP in node relaxations 75 | static const Statistic N_NODE_INIT_LP_ITERATIONS { &SCIPgetNNodeInitLPIterations }; 76 | //! total number of LPs solved so far during diving and probing 77 | static const Statistic N_DIVING_L_PS { &SCIPgetNDivingLPs }; 78 | //! total number of simplex iterations used so far during diving and probing 79 | static const Statistic N_DIVING_LP_ITERATIONS { &SCIPgetNDivingLPIterations }; 80 | //! total number of times, strong branching was called (each call represents solving two LPs) 81 | static const Statistic N_STRONGBRANCHS { &SCIPgetNStrongbranchs }; 82 | //! total number of simplex iterations used so far in strong branching 83 | static const Statistic N_STRONGBRANCH_LP_ITERATIONS { &SCIPgetNStrongbranchLPIterations }; 84 | //! total number of times, strong branching was called at the root node (each call represents solving two LPs) 85 | static const Statistic N_ROOT_STRONGBRANCHS { &SCIPgetNRootStrongbranchs }; 86 | //! total number of simplex iterations used so far in strong branching at the root node 87 | static const Statistic N_ROOT_STRONGBRANCH_LP_ITERATIONS { &SCIPgetNRootStrongbranchLPIterations }; 88 | //! number of pricing rounds performed so far at the current node 89 | static const Statistic N_PRICE_ROUNDS { &SCIPgetNPriceRounds }; 90 | //! current number of variables in the pricing store 91 | static const Statistic N_PRICEVARS { &SCIPgetNPricevars }; 92 | //! total number of pricing variables found so far 93 | static const Statistic N_PRICEVARS_FOUND { &SCIPgetNPricevarsFound }; 94 | //! total number of pricing variables applied to the LPs 95 | static const Statistic N_PRICEVARS_APPLIED { &SCIPgetNPricevarsApplied }; 96 | //! number of separation rounds performed so far at the current node 97 | static const Statistic N_SEPA_ROUNDS { &SCIPgetNSepaRounds }; 98 | //! total number of cuts found so far 99 | static const Statistic N_CUTS_FOUND { &SCIPgetNCutsFound }; 100 | //! number of cuts found so far in current separation round 101 | static const Statistic N_CUTS_FOUND_ROUND { &SCIPgetNCutsFoundRound }; 102 | //! total number of cuts applied to the LPs 103 | static const Statistic N_CUTS_APPLIED { &SCIPgetNCutsApplied }; 104 | //! total number of constraints found in conflict analysis (conflict and reconvergence constraints) 105 | static const Statistic N_CONFLICT_CONSS_FOUND { &SCIPgetNConflictConssFound }; 106 | //! number of conflict constraints found so far at the current node 107 | static const Statistic N_CONFLICT_CONSS_FOUND_NODE { &SCIPgetNConflictConssFoundNode }; 108 | //! total number of conflict constraints added to the problem 109 | static const Statistic N_CONFLICT_CONSS_APPLIED { &SCIPgetNConflictConssApplied }; 110 | //! total number of dual proof constraints added to the problem 111 | static const Statistic N_CONFLICT_DUALPROOFS_APPLIED { &SCIPgetNConflictDualproofsApplied }; 112 | //! maximal depth of all processed nodes in current branch and bound run (excluding probing nodes) 113 | static const Statistic MAX_DEPTH { &SCIPgetMaxDepth }; 114 | //! maximal depth of all processed nodes over all branch and bound runs 115 | static const Statistic MAX_TOTAL_DEPTH { &SCIPgetMaxTotalDepth }; 116 | //! total number of backtracks, i.e. number of times, the new node was selected from the leaves queue 117 | static const Statistic N_BACKTRACKS { &SCIPgetNBacktracks }; 118 | //! total number of active constraints at the current node 119 | static const Statistic N_ACTIVE_CONSS { &SCIPgetNActiveConss }; 120 | //! total number of enabled constraints at the current node 121 | static const Statistic N_ENABLED_CONSS { &SCIPgetNEnabledConss }; 122 | //! average dual bound of all unprocessed nodes for original problem 123 | static const Statistic AVG_DUALBOUND { &SCIPgetAvgDualbound }; 124 | //! average lower (dual) bound of all unprocessed nodes in transformed problem 125 | static const Statistic AVG_LOWERBOUND { &SCIPgetAvgLowerbound }; 126 | //! global dual bound 127 | static const Statistic DUALBOUND { &SCIPgetDualbound }; 128 | //! global lower (dual) bound in transformed problem 129 | static const Statistic LOWERBOUND { &SCIPgetLowerbound }; 130 | //! dual bound of the root node for the original problem 131 | static const Statistic DUALBOUND_ROOT { &SCIPgetDualboundRoot }; 132 | //! lower (dual) bound in transformed problem of the root node 133 | static const Statistic LOWERBOUND_ROOT { &SCIPgetLowerboundRoot }; 134 | //! dual bound for the original problem of the first LP solve at the root node 135 | static const Statistic FIRST_LP_DUALBOUND_ROOT { &SCIPgetFirstLPDualboundRoot }; 136 | //! lower (dual) bound in transformed problem obtained by first LP solve at the root node 137 | static const Statistic FIRST_LP_LOWERBOUND_ROOT { &SCIPgetFirstLPLowerboundRoot }; 138 | //! primal bound of the very first solution 139 | static const Statistic FIRST_PRIMAL_BOUND { &SCIPgetFirstPrimalBound }; 140 | //! global primal bound (objective value of best solution or user objective limit) for the original problem 141 | static const Statistic PRIMALBOUND { &SCIPgetPrimalbound }; 142 | //! global upper (primal) bound in transformed problem (objective value of best solution or user objective limit) 143 | static const Statistic UPPERBOUND { &SCIPgetUpperbound }; 144 | //! global cutoff bound in transformed problem 145 | static const Statistic CUTOFFBOUND { &SCIPgetCutoffbound }; 146 | //! current gap |(primalbound - dualbound)/min(|primalbound|,|dualbound|)| if both bounds have same sign, 147 | static const Statistic GAP { &SCIPgetGap }; 148 | //! current gap |(upperbound - lowerbound)/min(|upperbound|,|lowerbound|)| in transformed problem if both bounds 149 | static const Statistic TRANS_GAP { &SCIPgetTransGap }; 150 | //! number of feasible primal solutions found so far 151 | static const Statistic N_SOLS_FOUND { &SCIPgetNSolsFound }; 152 | //! number of feasible primal solutions respecting the objective limit found so far 153 | static const Statistic N_LIM_SOLS_FOUND { &SCIPgetNLimSolsFound }; 154 | //! number of feasible primal solutions found so far, that improved the primal bound at the time they were found 155 | static const Statistic N_BEST_SOLS_FOUND { &SCIPgetNBestSolsFound }; 156 | //! average pseudo cost score value over all variables, assuming a fractionality of 0.5 157 | static const Statistic AVG_PSEUDOCOST_SCORE { &SCIPgetAvgPseudocostScore }; 158 | //! average pseudo cost score value over all variables, assuming a fractionality of 0.5, 159 | static const Statistic AVG_PSEUDOCOST_SCORE_CURRENT_RUN { &SCIPgetAvgPseudocostScoreCurrentRun }; 160 | //! average conflict score value over all variables 161 | static const Statistic AVG_CONFLICT_SCORE { &SCIPgetAvgConflictScore }; 162 | //! average conflict score value over all variables, only using the conflict information of the current run 163 | static const Statistic AVG_CONFLICT_SCORE_CURRENT_RUN { &SCIPgetAvgConflictScoreCurrentRun }; 164 | //! average inference score value over all variables 165 | static const Statistic AVG_CONFLICTLENGTH_SCORE { &SCIPgetAvgConflictlengthScore }; 166 | //! average conflictlength score value over all variables, only using the conflictlength information of the 167 | static const Statistic AVG_CONFLICTLENGTH_SCORE_CURRENT_RUN { &SCIPgetAvgConflictlengthScoreCurrentRun }; 168 | //! average inference score value over all variables 169 | static const Statistic AVG_INFERENCE_SCORE { &SCIPgetAvgInferenceScore }; 170 | //! average inference score value over all variables, only using the inference information of the 171 | static const Statistic AVG_INFERENCE_SCORE_CURRENT_RUN { &SCIPgetAvgInferenceScoreCurrentRun }; 172 | //! average cutoff score value over all variables 173 | static const Statistic AVG_CUTOFF_SCORE { &SCIPgetAvgCutoffScore }; 174 | //! average cutoff score value over all variables, only using the cutoff information of the current run 175 | static const Statistic AVG_CUTOFF_SCORE_CURRENT_RUN { &SCIPgetAvgCutoffScoreCurrentRun }; 176 | //! average normalized efficacy of a GMI cut over all variables 177 | static const Statistic AVG_G_M_IEFF { &SCIPgetAvgGMIeff }; 178 | //! total number of LPs solved so far 179 | static const Statistic DETERMINISTIC_TIME { &SCIPgetDeterministicTime }; 180 | //! total number of implications between variables that are stored in the implication graph 181 | static const Statistic N_IMPLICATIONS { &SCIPgetNImplications }; 182 | //! recomputes and returns the primal dual gap stored in the stats 183 | static const Statistic PRIMAL_DUAL_INTEGRAL { &SCIPgetPrimalDualIntegral }; 184 | 185 | } 186 | -------------------------------------------------------------------------------- /include/scippp/statistics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // forward declare 6 | struct Scip; 7 | 8 | /** 9 | * Parameters to access statistics with. 10 | * 11 | * since 1.2.0 12 | */ 13 | namespace scippp::statistics { 14 | 15 | /** 16 | * Storage for a function pointer. 17 | * @since 1.2.0 18 | * @tparam T Type of the statistics value. 19 | */ 20 | template 21 | struct Statistic { 22 | //! Type of the function pointer to store the original call to %SCIP. 23 | using fptr = T (*)(Scip*); 24 | //! Function pointer to the original %SCIP call. 25 | const fptr m_PLAIN_CALL; 26 | /** 27 | * Returns the value of the statistics. 28 | * 29 | * @param s Raw pointer to a %C-SCIP object. 30 | * @return Value that the original call stored in the pointer returns. 31 | */ 32 | T operator()(Scip* s) const 33 | { 34 | return m_PLAIN_CALL(s); 35 | } 36 | }; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /include/scippp/var.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // forward declare 4 | struct SCIP_Var; 5 | 6 | namespace scippp { 7 | 8 | // forward declare 9 | struct Solution; 10 | 11 | /** 12 | * Wrapper for a %SCIP variable. 13 | * 14 | * It does not own the memory. Memory management is done by the Model. 15 | * @since 1.0.0 16 | */ 17 | struct Var { 18 | /** 19 | * Pointer to the underlying %SCIP variable. 20 | * @deprecated since 1.3.0: Use #getVar() instead 21 | */ 22 | SCIP_Var* var { nullptr }; 23 | 24 | /** 25 | * Pointer to the underlying %SCIP variable. 26 | * @since 1.3.0 27 | * @return underlying %SCIP variable. 28 | */ 29 | [[nodiscard]] SCIP_Var* getVar(); 30 | 31 | /** 32 | * Pointer to the underlying %SCIP variable. 33 | * 34 | * @since 1.3.0 35 | * @return underlying %SCIP variable. 36 | */ 37 | [[nodiscard]] SCIP_Var* const getVar() const; 38 | 39 | /** 40 | * Get the assigned value in the solution. 41 | * @since 1.0.0 42 | * @param solution Primal %CIP solution. 43 | * @return value of this variable in primal %CIP solution. 44 | */ 45 | [[nodiscard]] double getSolVal(const Solution& solution) const; 46 | 47 | /** 48 | * Gets the assigned value in the solution and converts it to int. 49 | * @since 1.1.0 50 | * @param solution Primal %CIP solution. 51 | * @return value of this variable in primal %CIP solution as integer. 52 | */ 53 | [[nodiscard]] int getSolValAsInt(const Solution& solution) const; 54 | 55 | /** 56 | * Gets the assigned value in the solution and converts it to long long. 57 | * @since 1.1.0 58 | * @param solution Primal %CIP solution. 59 | * @return value of this variable in primal %CIP solution as long integer. 60 | */ 61 | [[nodiscard]] long long getSolValAsLongInt(const Solution& solution) const; 62 | 63 | /** 64 | * Checks whether the value of this variable in the primal solution is in range epsilon of 0.0. 65 | * @since 1.1.0 66 | * @param solution Primal %CIP solution. 67 | * @return \c true iff the value of this variable in the primal solution is in range epsilon of 0.0. 68 | */ 69 | [[nodiscard]] bool isZero(const Solution& solution) const; 70 | 71 | /** 72 | * Checks whether an existing %SCIP variable is wrapped or the wrapper is empty. 73 | * @since 1.2.0 74 | * @return \c true iff the wrapper is empty 75 | */ 76 | [[nodiscard]] bool isVoid() const; 77 | }; 78 | 79 | } 80 | -------------------------------------------------------------------------------- /include/scippp/var_type.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace scippp { 6 | 7 | /** 8 | * Type of a variable. 9 | * @since 1.0.0 10 | */ 11 | enum class VarType { 12 | /** 13 | * Binary variable. 14 | * @since 1.0.0 15 | */ 16 | BINARY = SCIP_VARTYPE_BINARY, 17 | /** 18 | * Integral variable. 19 | * @since 1.0.0 20 | */ 21 | INTEGER = SCIP_VARTYPE_INTEGER, 22 | /** 23 | * Implicit integral variable. 24 | * @since 1.0.0 25 | */ 26 | IMPL_INT = SCIP_VARTYPE_IMPLINT, 27 | /** 28 | * Continuous variable. 29 | * @since 1.0.0 30 | */ 31 | CONTINUOUS = SCIP_VARTYPE_CONTINUOUS 32 | }; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /include/scippp/version.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace scippp { 4 | 5 | /** 6 | * Returns the version number. 7 | * 8 | * @since 1.0.0 9 | * @return Version number in semver-format. 10 | */ 11 | std::string_view getVersion(); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # SCIP++: A C++ wrapper for SCIP 2 | 3 | ![CI Status](https://github.com/scipopt/SCIPpp/actions/workflows/main.yml/badge.svg) 4 | [![Coverage](https://img.shields.io/codecov/c/github/scipopt/SCIPpp)](https://app.codecov.io/github/scipopt/SCIPpp) 5 | [![Doxygen](https://img.shields.io/badge/documentation-Doxygen-blue)](https://scipopt.github.io/SCIPpp/) 6 | [![Conan Center](https://img.shields.io/conan/v/scippp)](https://conan.io/center/recipes/scippp) 7 | 8 | SCIP++ is a C++ wrapper for SCIP's C interface. 9 | It automatically manages the memory, and provides a simple interface to create linear expressions and inequalities. 10 | 11 | ## Usage 12 | 13 | The documentation can be found at https://scipopt.github.io/SCIPpp/ 14 | 15 | Here is a simple example where we create a new model, add two variables, add a linear inequality as constraint, and ask 16 | SCIP to solve the maximization problem. 17 | 18 | ```cpp 19 | #include 20 | using namespace scippp; 21 | int main() { 22 | Model model("Simple"); 23 | auto x1 = model.addVar("x_1", 1); 24 | auto x2 = model.addVar("x_2", 1); 25 | model.addConstr(3 * x1 + 2 * x2 <= 1, "capacity"); 26 | model.setObjsense(Sense::MAXIMIZE); 27 | model.solve(); 28 | } 29 | ``` 30 | 31 | ### Model Creation 32 | 33 | A model can be created 34 | 35 | * without an existing SCIP environment, 36 | * with an existing SCIP environment where all default plugins should be added to, and 37 | * with an existing SCIP environment where no additional plugins should be added to. 38 | 39 | ```cpp 40 | Model m1("ModelWithoutExistingSCIPEnvironment"); 41 | 42 | SCIP* scip2; 43 | SCIPcreate(&scip2); 44 | Model m2("ModelWithExistingSCIPEnvironmentWhereDefaultPluginsWillBeAdded", scip2); 45 | 46 | SCIP* scip3; 47 | SCIPcreate(&scip3); 48 | SCIPincludeDefaultPlugins(scip3); 49 | Model m3("ModelWithExistingSCIPEnvironmentWhereNoPluginsWillBeAdded", scip3, false); 50 | ``` 51 | 52 | ### Adding Variables 53 | 54 | Variables can be added 55 | 56 | * one at a time, 57 | * multiple in a vector, and 58 | * multiple when the number is known at compile time. 59 | 60 | ```cpp 61 | Model model("Example"); 62 | 63 | auto x = model.addVar("x"); 64 | auto vec = model.addVars("x_", 42); 65 | const auto& [x0, x1] = model.addVars<2>("x_"); 66 | ``` 67 | 68 | When adding multiple variables simultaneously to the model, they all have a coefficient of zero in the objective 69 | function by default. 70 | This can be changed to one by `scippp::COEFF_ONE`: 71 | ```cpp 72 | const auto& [x1, x2] = model.addVars<2>("x_", COEFF_ONE); 73 | ``` 74 | 75 | For the coefficient, any object providing an index operator can be used: 76 | ```cpp 77 | double operator[](std::size_t index) const 78 | ``` 79 | 80 | ### Adding Constraints 81 | 82 | Linear inequalities can be added to the model. They can be built from linear expressions: 83 | 84 | ```cpp 85 | const auto& [x0, x1, x2, x3] = model.addVars<4>("x_"); 86 | 87 | LinExpr sum1; 88 | sum1 += 42 * x0; 89 | sum1 += x1; 90 | model.addConstr(sum1 <= 0.5, "constraint1"); 91 | 92 | LinExpr sum2 = x1 + x2; 93 | model.addConstr(sum2 == 1.25, "constraint2"); 94 | 95 | model.addConstr(1 <= x2 + 2 * x3, "constraint3"); 96 | ``` 97 | 98 | ### Setting Parameters 99 | 100 | The namespace `scippp::params` contains all parameters shown https://www.scipopt.org/doc/html/PARAMETERS.php in the 101 | header `parameters.hpp`. They are strongly typed, so that no string can be set as the value for a parameter expecting an 102 | integer. Instead of using the predefined parameters, one can also use `scippp::params::Param` directly. 103 | 104 | ```cpp 105 | model.setParam(params::LIMITS::MAXSOL, 1); 106 | model.setParam(params::DISPLAY::VERBLEVEL, 0); 107 | model.setParam(params::Param("write/printzeros"), true); 108 | ``` 109 | 110 | The optimization goal can be changed by: 111 | ```cpp 112 | model.setObjsense(Sense::MAXIMIZE); 113 | ``` 114 | 115 | ### Accessing a Solution 116 | 117 | A model can be asked for the status and the number of solutions. 118 | A variable can be asked for its value in a given solution as 119 | 120 | * floating point number via `getSolVal(sol)`. 121 | * integer via `getSolValAsInt(sol)`. 122 | * long integer via `getSolValAsLongInt(sol)`, and 123 | * it can be checked for zero via `isZero(sol)`. 124 | 125 | ```cpp 126 | const auto& [x0, x1] = model.addVars<2>("x_"); 127 | model.solve(); 128 | if (model.getNSols() > 0 && model.getStatus() == SCIP_STATUS_OPTIMAL) { 129 | Solution sol { model.getBestSol() }; 130 | cout << "x0 + x1 =" << x0.getSolVal(sol) + x1.getSolVal(sol) << endl; 131 | } 132 | ``` 133 | 134 | ### IO 135 | 136 | A model can be written to file via `Model::writeOrigProblem` if a `std::filesystem::directory_entry` is given as 137 | argument. If it is just a string representing a file extension, it is written to standard output. 138 | 139 | ### Numerics 140 | 141 | The model exposes 142 | 143 | * `SCIPepsilon` via `epsilon()`, 144 | * `SCIPround` via `round(double)`, and 145 | * `SCIPisZero` via `isZero(double)` 146 | 147 | ### Access Solving Statistics 148 | 149 | Use the `Statistics` objects from the header [solving_statistics.hpp](include/scippp/solving_statistics.hpp) to 150 | access solving statistics in a type-safe way: 151 | 152 | ```cpp 153 | ... 154 | model.solve(); 155 | auto pb { model.getSolvingStatistic(statistics::PRIMALBOUND) }; 156 | ``` 157 | 158 | ### Features Not Yet Supported 159 | 160 | For features not yet supported by SCIP++, one can access the underlying raw SCIP object via 161 | 162 | ```cpp 163 | SCIP* scip = model.scip(); 164 | ``` 165 | 166 | ## Build 167 | 168 | ### Without Conan 169 | 170 | We use [Conan](https://conan.io/center/) as package manager. 171 | That is not required! As long as `find_package(scip CONFIG REQUIRED)` (and `find_package(Boost CONFIG REQUIRED)` for 172 | the tests) work(s), any kind of dependency management system can be used. 173 | 174 | Build and install: 175 | 176 | ```bash 177 | cmake . 178 | make ScipPP 179 | make install 180 | ``` 181 | 182 | Build and run tests: 183 | 184 | ```bash 185 | cmake -DBUILD_TESTS=ON . 186 | make tests 187 | ./test/tests 188 | ``` 189 | 190 | ### With Conan v2 and CMake v3.19 or later 191 | 192 | Build and install: 193 | 194 | ```bash 195 | conan install . 196 | cmake --preset conan-release . 197 | cmake --build build/Release --target ScipPP 198 | cmake --install build/Release 199 | ``` 200 | 201 | Build and run tests: 202 | 203 | ```bash 204 | conan install -o with_tests=True . 205 | cmake --preset conan-release . 206 | cmake --build build/Release --target tests 207 | build/Release/test/tests 208 | ``` 209 | 210 | If your setting of OS, compiler, C++ or stdlib version is one where conan-center does not host pre-compiled binaries, 211 | add `--build=missing` when you run `conan install`. The dependencies will then be built from source (don't worry, they 212 | will be available only for projects using conan, they do not interfere with versions you might already have installed 213 | on the system). So, when you see an error message like 214 | 215 | ``` 216 | ERROR: Missing prebuilt package for 'bliss/0.77', 'boost/1.81.0', 'bzip2/1.0.8', 'gmp/6.2.1', 'libbacktrace/cci.20210118', 'scip/8.0.3', 'soplex/6.0.3', 'zlib/1.2.13' 217 | Check the available packages using 'conan list bliss/0.77:* -r=remote' 218 | or try to build locally from sources using the '--build=missing' argument 219 | ``` 220 | 221 | change the install-command to 222 | 223 | ```bash 224 | conan install --build=missing . 225 | ``` 226 | 227 | ### With Conan v2 and CMake v3.18 or earlier 228 | 229 | When CMake presets are not support, use the toolchain file that conan generates. 230 | 231 | Build and install: 232 | 233 | ```bash 234 | conan install . 235 | cmake . -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=./build/Release/generators/conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release 236 | make ScipPP 237 | make install 238 | ``` 239 | 240 | Build and run tests: 241 | 242 | ```bash 243 | conan install -o with_tests=True . 244 | cmake . -G "Unix Makefiles" -DCMAKE_TOOLCHAIN_FILE=./build/Release/generators/conan_toolchain.cmake -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=Release 245 | make tests 246 | ./build/Release/test/tests 247 | ``` 248 | 249 | ## Utils 250 | 251 | Use `gen_constexpr_parameters` to transform all SCIP parameters into constexpr `scippp::params::Param` objects which 252 | can be added to the `parameters.hpp` header. 253 | 254 | Use `extract_solvingstats` to transform all SCIP methods that access solving statistics into static const 255 | `scippp::statistics::Statistic` objects which can be added to the `solving_statistics.hpp` header. 256 | 257 | ## Maintainer 258 | 259 | This project is maintained by Tilo Wiedera `tilo (dot) wiedera (at) dbschenker (dot) com`. 260 | 261 | ## Code of Conduct 262 | 263 | SCIP++ follows the Contributor Covenant Code of Conduct v2, see [code_of_conduct.md](code_of_conduct.md). 264 | 265 | ## Contributor License Agreement 266 | 267 | This project does not use a CLA. 268 | 269 | ## License 270 | 271 | SCIP++ is licensed under the Apache-2 license, see [LICENSE](LICENSE). 272 | -------------------------------------------------------------------------------- /source/initial_solution.cpp: -------------------------------------------------------------------------------- 1 | #include "scippp/initial_solution.hpp" 2 | 3 | namespace scippp { 4 | 5 | void InitialSolution::setValue(const Var& var, double value) 6 | { 7 | m_values[&var] = value; 8 | } 9 | 10 | double& InitialSolution::operator()(const Var& var) 11 | { 12 | return m_values[&var]; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /source/lin_expr.cpp: -------------------------------------------------------------------------------- 1 | #include "scippp/lin_expr.hpp" 2 | 3 | namespace scippp { 4 | 5 | LinExpr::LinExpr(const Var& var) 6 | : m_vars { var } 7 | , m_coeffs(1, 1) 8 | { 9 | } 10 | 11 | double LinExpr::getConstant() const 12 | { 13 | return m_constant; 14 | } 15 | 16 | LinExpr& LinExpr::operator-=(const LinExpr& expr) 17 | { 18 | m_vars.insert(m_vars.end(), expr.m_vars.begin(), expr.m_vars.end()); 19 | for (auto coeff : expr.m_coeffs) { 20 | m_coeffs.push_back(-coeff); 21 | } 22 | m_constant -= expr.m_constant; 23 | return *this; 24 | } 25 | 26 | LinExpr& LinExpr::operator+=(const LinExpr& expr) 27 | { 28 | m_vars.insert(m_vars.end(), expr.m_vars.begin(), expr.m_vars.end()); 29 | m_coeffs.insert(m_coeffs.end(), expr.m_coeffs.begin(), expr.m_coeffs.end()); 30 | m_constant += expr.m_constant; 31 | return *this; 32 | } 33 | 34 | LinExpr& LinExpr::operator*=(double factor) 35 | { 36 | m_constant *= factor; 37 | for (auto& coeff : m_coeffs) { 38 | coeff *= factor; 39 | } 40 | return *this; 41 | } 42 | 43 | LinExpr operator*(double factor, LinExpr rhs) 44 | { 45 | rhs *= factor; 46 | return rhs; 47 | } 48 | 49 | LinExpr operator+(LinExpr lhs, const LinExpr& rhs) 50 | { 51 | lhs += rhs; 52 | return lhs; 53 | } 54 | 55 | LinExpr operator-(LinExpr lhs, const LinExpr& rhs) 56 | { 57 | lhs -= rhs; 58 | return lhs; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /source/lin_ineq.cpp: -------------------------------------------------------------------------------- 1 | #include "scippp/lin_ineq.hpp" 2 | 3 | namespace scippp { 4 | 5 | LinIneq operator<=(const LinExpr& lhs, const LinExpr& rhs) 6 | { 7 | LinIneq result; 8 | result.m_linExpr = rhs - lhs; 9 | result.m_lhs = -result.m_linExpr.getConstant(); 10 | // rhs infinity 11 | return result; 12 | } 13 | 14 | LinIneq operator==(const LinExpr& lhs, const LinExpr& rhs) 15 | { 16 | LinIneq result; 17 | result.m_linExpr = rhs - lhs; 18 | result.m_lhs = -result.m_linExpr.getConstant(); 19 | result.m_rhs = -result.m_linExpr.getConstant(); 20 | return result; 21 | } 22 | 23 | LinIneq operator>=(const LinExpr& lhs, const LinExpr& rhs) 24 | { 25 | return rhs <= lhs; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /source/model.cpp: -------------------------------------------------------------------------------- 1 | #include "scippp/model.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | namespace scippp { 8 | 9 | Var& Model::addVar( 10 | const std::string& name, 11 | SCIP_Real coeff, 12 | VarType varType, 13 | std::optional lb, 14 | std::optional ub) 15 | { 16 | SCIP_VAR* var { nullptr }; 17 | m_scipCallWrapper(SCIPcreateVarBasic( 18 | m_scip, /* SCIP environment */ 19 | &var, /* reference to the variable */ 20 | name.c_str(), /* name of the variable */ 21 | lb != std::nullopt ? lb.value() : -SCIPinfinity(m_scip), /* lower bound of the variable */ 22 | ub != std::nullopt ? ub.value() : SCIPinfinity(m_scip), /* upper bound of the variable */ 23 | coeff, /* obj. coefficient. */ 24 | static_cast(varType) /* variable is binary */ 25 | )); 26 | m_scipCallWrapper(SCIPaddVar(m_scip, var)); 27 | m_vars.emplace_back(Var { var }); 28 | return m_vars.back(); 29 | } 30 | 31 | Model::Model(const std::string& name, SCIP* scip, bool withDefaultPlugins) 32 | { 33 | m_scipCallWrapper = [this](SCIP_Retcode r) { m_lastReturnCode = r; }; 34 | if (scip) { 35 | m_scip = scip; 36 | } else { 37 | m_scipCallWrapper(SCIPcreate(&m_scip)); 38 | m_weCreatedANewScipObject = true; 39 | } 40 | if (withDefaultPlugins) { 41 | m_scipCallWrapper(SCIPincludeDefaultPlugins(m_scip)); 42 | } 43 | m_scipCallWrapper(SCIPcreateProbBasic(m_scip, name.c_str())); 44 | } 45 | 46 | SCIP_Retcode Model::getLastReturnCode() const 47 | { 48 | return m_lastReturnCode; 49 | } 50 | 51 | void Model::setScipCallWrapper(std::function wrapper) 52 | { 53 | m_scipCallWrapper = std::move(wrapper); 54 | } 55 | 56 | Model::~Model() 57 | { 58 | for (auto& var : m_vars) { 59 | m_scipCallWrapper(SCIPreleaseVar(m_scip, &var.var)); 60 | } 61 | for (auto* cons : m_cons) { 62 | m_scipCallWrapper(SCIPreleaseCons(m_scip, &cons)); 63 | } 64 | if (m_weCreatedANewScipObject) { 65 | m_scipCallWrapper(SCIPfree(&m_scip)); 66 | } 67 | } 68 | 69 | void Model::addConstr(const scippp::LinIneq& ineq, const std::string& name) 70 | { 71 | SCIP_CONS* con { nullptr }; 72 | m_scipCallWrapper(SCIPcreateConsBasicLinear( 73 | m_scip, 74 | &con, /* pointer to hold the created constraint */ 75 | name.c_str(), /* name of constraint */ 76 | 0, /* number of nonzeros in the constraint */ 77 | nullptr, /* array with variables of constraint entries */ 78 | nullptr, /* array with coefficients of constraint entries */ 79 | ineq.m_lhs, /* left hand side of constraint */ 80 | ineq.m_rhs.has_value() ? ineq.m_rhs.value() : infinity())); 81 | for (size_t index { 0 }; index < ineq.m_linExpr.m_vars.size(); index++) { 82 | m_scipCallWrapper(SCIPaddCoefLinear(m_scip, con, ineq.m_linExpr.m_vars.at(index).getVar(), ineq.m_linExpr.m_coeffs.at(index))); 83 | } 84 | m_scipCallWrapper(SCIPaddCons(m_scip, con)); 85 | m_cons.push_back(con); 86 | } 87 | 88 | SCIP_Real Model::infinity() const 89 | { 90 | return SCIPinfinity(m_scip); 91 | } 92 | 93 | SCIP_Real Model::epsilon() const 94 | { 95 | return SCIPepsilon(m_scip); 96 | } 97 | 98 | SCIP_Real Model::round(SCIP_Real value) const 99 | { 100 | return SCIPround(m_scip, value); 101 | } 102 | 103 | bool Model::isZero(SCIP_Real value) const 104 | { 105 | return SCIPisZero(m_scip, value); 106 | } 107 | 108 | void Model::solve() 109 | { 110 | m_scipCallWrapper(SCIPsolve(m_scip)); 111 | } 112 | 113 | void Model::setObjsense(Sense objsense) 114 | { 115 | m_scipCallWrapper(SCIPsetObjsense(m_scip, static_cast(objsense))); 116 | } 117 | 118 | void Model::writeOrigProblem(const std::filesystem::directory_entry& filename, bool genericNames) const 119 | { 120 | m_scipCallWrapper(SCIPwriteOrigProblem(m_scip, filename.path().string().c_str(), nullptr, genericNames)); 121 | } 122 | 123 | void Model::writeOrigProblem(const std::string& extension, bool genericNames) const 124 | { 125 | m_scipCallWrapper(SCIPwriteOrigProblem(m_scip, nullptr, extension.data(), genericNames)); 126 | } 127 | 128 | Scip* Model::scip() const 129 | { 130 | return m_scip; 131 | } 132 | 133 | SCIP_STATUS Model::getStatus() const 134 | { 135 | return SCIPgetStatus(m_scip); 136 | } 137 | 138 | int Model::getNSols() const 139 | { 140 | return SCIPgetNSols(m_scip); 141 | } 142 | 143 | Solution Model::getBestSol() const 144 | { 145 | return Solution { m_scip, SCIPgetBestSol(m_scip) }; 146 | } 147 | 148 | double Model::getPrimalbound() const 149 | { 150 | return SCIPgetPrimalbound(m_scip); 151 | } 152 | 153 | bool Model::addSolution( 154 | const InitialSolution& initialSolution, 155 | bool printReason, 156 | bool completely, 157 | bool checkBounds, 158 | bool checkIntegrality, 159 | bool checkLpRows) 160 | { 161 | SCIP_Sol* sol { nullptr }; 162 | m_scipCallWrapper(SCIPcreateSol(m_scip, &sol, nullptr)); 163 | for (const auto& [var, value] : initialSolution.m_values) { 164 | m_scipCallWrapper(SCIPsetSolVal(m_scip, sol, var->getVar(), value)); 165 | } 166 | SCIP_Bool isFeasible { false }; 167 | m_scipCallWrapper(SCIPcheckSol( 168 | m_scip, sol, printReason, completely, checkBounds, checkIntegrality, checkLpRows, &isFeasible)); 169 | SCIP_Bool isStored { false }; 170 | if (isFeasible) { 171 | m_scipCallWrapper(SCIPaddSolFree(m_scip, &sol, &isStored)); 172 | } else { 173 | m_scipCallWrapper(SCIPfreeSol(m_scip, &sol)); 174 | } 175 | return isStored; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /source/var.cpp: -------------------------------------------------------------------------------- 1 | #include "scippp/var.hpp" 2 | 3 | #include "scippp/solution.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace scippp { 9 | 10 | SCIP_Var* Var::getVar() 11 | { 12 | return var; 13 | } 14 | 15 | SCIP_Var* const Var::getVar() const 16 | { 17 | return var; 18 | } 19 | 20 | double Var::getSolVal(const Solution& sol) const 21 | { 22 | return SCIPgetSolVal(sol.scip, sol.sol, var); 23 | } 24 | 25 | int Var::getSolValAsInt(const Solution& solution) const 26 | { 27 | return SCIPconvertRealToInt(solution.scip, getSolVal(solution)); 28 | } 29 | 30 | long long Var::getSolValAsLongInt(const Solution& solution) const 31 | { 32 | return SCIPconvertRealToLongint(solution.scip, getSolVal(solution)); 33 | } 34 | 35 | bool Var::isZero(const Solution& solution) const 36 | { 37 | return SCIPisZero(solution.scip, getSolVal(solution)); 38 | } 39 | 40 | bool Var::isVoid() const 41 | { 42 | return var == nullptr; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /source/version.cpp.in: -------------------------------------------------------------------------------- 1 | #include "scippp/version.hpp" 2 | 3 | namespace scippp { 4 | 5 | static constexpr std::string_view VERSION = "@scippp_version@"; 6 | 7 | std::string_view getVersion() 8 | { 9 | return VERSION; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Boost CONFIG REQUIRED COMPONENTS unit_test_framework filesystem) 2 | if (NOT TARGET Boost::unit_test_framework) 3 | add_library(Boost::unit_test_framework IMPORTED INTERFACE) 4 | set_property(TARGET Boost::unit_test_framework PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR}) 5 | set_property(TARGET Boost::unit_test_framework PROPERTY INTERFACE_LINK_LIBRARIES ${Boost_LIBRARIES}) 6 | endif () 7 | if (NOT TARGET Boost::filesystem) 8 | add_library(Boost::filesystem IMPORTED INTERFACE) 9 | set_property(TARGET Boost::filesystem PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR}) 10 | set_property(TARGET Boost::filesystem PROPERTY INTERFACE_LINK_LIBRARIES ${Boost_LIBRARIES}) 11 | endif () 12 | 13 | file(GLOB TEST_SOURCES *.cpp) 14 | add_executable(tests ${TEST_SOURCES}) 15 | target_include_directories(tests SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}) 16 | target_link_libraries(tests PRIVATE ScipPP Boost::unit_test_framework Boost::filesystem) 17 | -------------------------------------------------------------------------------- /test/examples.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scippp/model.hpp" 4 | #include "scippp/parameters.hpp" 5 | #include "scippp/solving_statistics.hpp" 6 | 7 | using namespace scippp; 8 | using namespace std; 9 | 10 | BOOST_AUTO_TEST_SUITE(Examples) 11 | 12 | BOOST_AUTO_TEST_CASE(Knapsack) 13 | { 14 | vector w { 3, 4, 3, 3, 3, 3, 4, 2, 4, 1 }; 15 | vector v { 230, 134, 52, 60, 151, 95, 201, 245, 52, 55 }; 16 | int c { 14 }; 17 | 18 | Model model("KNAPSACK"); 19 | auto x { model.addVars("x_", w.size(), v, VarType::BINARY) }; 20 | LinExpr weight; 21 | for (size_t i { 0 }; i < w.size(); i++) { 22 | weight += w.at(i) * x.at(i); 23 | } 24 | model.addConstr(weight <= c, "weight"); 25 | model.setObjsense(Sense::MAXIMIZE); 26 | model.setParam(params::LIMITS::MAXSOL, 1); 27 | model.setParam(params::DISPLAY::VERBLEVEL, 0); 28 | 29 | model.solve(); 30 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 882); 31 | BOOST_TEST(model.getNSols() == 1); 32 | } 33 | 34 | BOOST_AUTO_TEST_SUITE_END() 35 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MAIN // NOLINT(readability-identifier-naming) 2 | #include 3 | 4 | #include "scippp/model.hpp" 5 | #include "scippp/parameters.hpp" 6 | #include "scippp/solving_statistics.hpp" 7 | 8 | using namespace scippp; 9 | using namespace std; 10 | 11 | BOOST_AUTO_TEST_SUITE(Basic) 12 | 13 | BOOST_AUTO_TEST_CASE(ModelCtorWithScip) 14 | { 15 | SCIP* scip; 16 | { // to enforce that the d'tor of Model is called 17 | SCIPcreate(&scip); 18 | Model model("StructuredBinding", scip); 19 | BOOST_TEST(model.scip() == scip); 20 | array coeff { { 1, 1 } }; 21 | const auto& [x1, x2] = model.addVars<2>("x_", coeff); 22 | model.addConstr(x1 + x2 <= 1, "capacity"); 23 | model.setObjsense(Sense::MAXIMIZE); 24 | model.solve(); 25 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 26 | } 27 | SCIPfree(&scip); 28 | } 29 | 30 | BOOST_AUTO_TEST_CASE(StructuredBinding) 31 | { 32 | Model model("StructuredBinding"); 33 | array coeff { { 1, 1 } }; 34 | const auto& [x1, x2] = model.addVars<2>("x_", coeff); 35 | model.addConstr(x1 + x2 <= 1, "capacity"); 36 | model.setObjsense(Sense::MAXIMIZE); 37 | model.solve(); 38 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 39 | // test that deprecated method returns the same result 40 | BOOST_TEST(model.getPrimalbound() == 1); 41 | } 42 | 43 | BOOST_AUTO_TEST_CASE(StructuredBindingWithCoeff) 44 | { 45 | Model model("StructuredBinding"); 46 | const auto& [x1, x2] = model.addVars<2>("x_"); 47 | model.addConstr(x1 + x2 <= 1, "capacity"); 48 | model.setObjsense(Sense::MAXIMIZE); 49 | model.solve(); 50 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 0); 51 | } 52 | 53 | BOOST_AUTO_TEST_CASE(StructuredBindingWithPredefinedConstantCoeff) 54 | { 55 | Model model("StructuredBinding"); 56 | const auto& [x1, x2] = model.addVars<2>("x_", COEFF_ONE); 57 | model.addConstr(x1 + x2 <= 1, "capacity"); 58 | model.setObjsense(Sense::MAXIMIZE); 59 | model.solve(); 60 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 61 | } 62 | 63 | BOOST_AUTO_TEST_CASE(CoefficientsFromExternalSource) 64 | { 65 | struct Coeff { 66 | string strangeCoeffSource; 67 | double operator[](size_t index) const 68 | { 69 | return strangeCoeffSource[index]; 70 | } 71 | }; 72 | Model model("StructuredBinding"); 73 | const auto& [x1, x2] = model.addVars<2>("x_", Coeff { "22" }); 74 | model.addConstr(x1 + x2 <= 1, "capacity"); 75 | model.setObjsense(Sense::MAXIMIZE); 76 | model.solve(); 77 | char expected = '2'; 78 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == expected); 79 | } 80 | 81 | BOOST_AUTO_TEST_CASE(SimpleMaxRhs) 82 | { 83 | Model model("Simple"); 84 | auto x1 = model.addVar("x_1", 1); 85 | auto x2 = model.addVar("x_2", 1); 86 | model.addConstr(x1 + x2 <= 1, "capacityRight"); 87 | model.setObjsense(Sense::MAXIMIZE); 88 | model.solve(); 89 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 90 | } 91 | 92 | BOOST_AUTO_TEST_CASE(SimpleMaxLhs) 93 | { 94 | Model model("Simple"); 95 | auto x1 = model.addVar("x_1", 1); 96 | auto x2 = model.addVar("x_2", 1); 97 | model.addConstr(1 >= x1 + x2, "capacityRight"); 98 | model.setObjsense(Sense::MAXIMIZE); 99 | model.solve(); 100 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 101 | } 102 | 103 | BOOST_AUTO_TEST_CASE(SimpleMinRhs) 104 | { 105 | Model model("Simple"); 106 | auto x1 = model.addVar("x_1", 1); 107 | auto x2 = model.addVar("x_2", 1); 108 | model.addConstr(1 <= x1 + x2, "capacity"); 109 | model.setObjsense(Sense::MINIMIZE); 110 | model.solve(); 111 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 112 | } 113 | 114 | BOOST_AUTO_TEST_CASE(SimpleMinLhs) 115 | { 116 | Model model("Simple"); 117 | auto x1 = model.addVar("x_1", 1); 118 | auto x2 = model.addVar("x_2", 1); 119 | model.addConstr(x1 + x2 >= 1, "capacity"); 120 | model.addConstr(x1 == x2, "equal"); 121 | model.setObjsense(Sense::MINIMIZE); 122 | model.solve(); 123 | BOOST_REQUIRE(model.getNSols() > 0); 124 | BOOST_TEST(model.getStatus() == SCIP_STATUS_OPTIMAL); 125 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 126 | } 127 | 128 | BOOST_AUTO_TEST_CASE(GetLastReturnCodeOkay) 129 | { 130 | Model model("Simple"); 131 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 132 | auto x1 = model.addVar("x_1", 1); 133 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 134 | auto x2 = model.addVar("x_2", 1); 135 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 136 | model.addConstr(x1 + x2 <= 1, "capacityRight"); 137 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 138 | model.setObjsense(Sense::MAXIMIZE); 139 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 140 | model.solve(); 141 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 142 | BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 143 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 144 | } 145 | 146 | BOOST_AUTO_TEST_CASE(SetCallWrapper) 147 | { 148 | string result; 149 | Model model("Simple"); 150 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 151 | BOOST_TEST(result.empty()); 152 | model.setScipCallWrapper([&result](SCIP_Retcode r) { 153 | result += to_string(r); 154 | }); 155 | auto x1 = model.addVar("x_1", 1); 156 | BOOST_TEST(!result.empty()); 157 | } 158 | 159 | BOOST_AUTO_TEST_CASE(EpsilonIsNotZero) 160 | { 161 | Model model("Simple"); 162 | BOOST_TEST(model.epsilon() > 0); 163 | BOOST_TEST(model.isZero(model.epsilon())); 164 | BOOST_TEST(model.round(model.epsilon()) == 0.0); 165 | } 166 | 167 | BOOST_AUTO_TEST_SUITE_END() 168 | -------------------------------------------------------------------------------- /test/test_add_var.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scippp/model.hpp" 4 | #include "scippp/solving_statistics.hpp" 5 | 6 | using namespace scippp; 7 | using namespace std; 8 | 9 | BOOST_AUTO_TEST_SUITE(AddVar) 10 | 11 | // lb nullopt ub value lNuD 12 | // lb nullopt ub nullopt lNuN 13 | // lb value ub value GetSolVal 14 | // lb value ub nullopt lDuN 15 | 16 | const SCIP_Real LB_DEFAULT { 0.0 }; 17 | const SCIP_Real UB_DEFAULT { 1.0 }; 18 | 19 | BOOST_AUTO_TEST_CASE(lDuN, *boost::unit_test::tolerance(1e-3)) 20 | { 21 | Model m1("Simple"); 22 | auto x1 = m1.addVar("x_1", 1, VarType::CONTINUOUS, LB_DEFAULT, nullopt); 23 | auto x2 = m1.addVar("x_2", 1); 24 | m1.addConstr(x1 + x2 <= 1, "line"); 25 | m1.setObjsense(Sense::MINIMIZE); 26 | m1.solve(); 27 | BOOST_REQUIRE(m1.getNSols() > 0); 28 | BOOST_TEST(m1.getSolvingStatistic(statistics::PRIMALBOUND) == 0); 29 | 30 | Model m2("Simple"); 31 | x1 = m2.addVar("x_1", 1, VarType::CONTINUOUS, LB_DEFAULT, nullopt); 32 | x2 = m2.addVar("x_2", 1); 33 | m2.addConstr(x1 + x2 >= 1, "line"); 34 | m2.setObjsense(Sense::MAXIMIZE); 35 | m2.solve(); 36 | BOOST_TEST(m2.getStatus() == SCIP_STATUS_UNBOUNDED); 37 | } 38 | 39 | BOOST_AUTO_TEST_CASE(lNuD, *boost::unit_test::tolerance(1e-3)) 40 | { 41 | Model m1("Simple"); 42 | auto x1 = m1.addVar("x_1", 1, VarType::CONTINUOUS, nullopt, UB_DEFAULT); 43 | auto x2 = m1.addVar("x_2", 1); 44 | m1.addConstr(x1 + x2 <= 1, "line"); 45 | m1.setObjsense(Sense::MINIMIZE); 46 | m1.solve(); 47 | BOOST_TEST(m1.getStatus() == SCIP_STATUS_UNBOUNDED); 48 | 49 | Model m2("Simple"); 50 | x1 = m2.addVar("x_1", 1, VarType::CONTINUOUS, nullopt, UB_DEFAULT); 51 | x2 = m2.addVar("x_2", 1); 52 | m2.addConstr(x1 + x2 <= 1, "line"); 53 | m2.setObjsense(Sense::MAXIMIZE); 54 | m2.solve(); 55 | BOOST_REQUIRE(m2.getNSols() > 0); 56 | BOOST_TEST(m2.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 57 | } 58 | 59 | BOOST_AUTO_TEST_CASE(lNuN, *boost::unit_test::tolerance(1e-3)) 60 | { 61 | Model m1("Simple"); 62 | auto x1 = m1.addVar("x_1", 1, VarType::CONTINUOUS, nullopt, nullopt); 63 | auto x2 = m1.addVar("x_2", 1); 64 | m1.addConstr(x1 + x2 <= 1, "line"); 65 | m1.setObjsense(Sense::MINIMIZE); 66 | m1.solve(); 67 | BOOST_TEST(m1.getStatus() == SCIP_STATUS_UNBOUNDED); 68 | 69 | Model m2("Simple"); 70 | x1 = m2.addVar("x_1", 1, VarType::CONTINUOUS, nullopt, nullopt); 71 | x2 = m2.addVar("x_2", 1); 72 | m2.addConstr(x1 + x2 <= 1, "line"); 73 | m2.setObjsense(Sense::MAXIMIZE); 74 | m2.solve(); 75 | BOOST_REQUIRE(m2.getNSols() > 0); 76 | BOOST_TEST(m2.getSolvingStatistic(statistics::PRIMALBOUND) == 1); 77 | } 78 | 79 | BOOST_AUTO_TEST_SUITE_END() 80 | -------------------------------------------------------------------------------- /test/test_initial_solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "scippp/model.hpp" 6 | 7 | using namespace scippp; 8 | using namespace std; 9 | namespace bdata = boost::unit_test::data; 10 | 11 | BOOST_AUTO_TEST_SUITE(InitSolution) 12 | 13 | BOOST_DATA_TEST_CASE(InitialSolutionStored, bdata::make({ 1, 2, 3 }), indexSetToOne) 14 | { 15 | Model model("Simple"); 16 | auto x1 = model.addVar("x_1", 1, VarType::BINARY); 17 | auto x2 = model.addVar("x_2", 1, VarType::BINARY); 18 | auto x3 = model.addVar("x_3", 1, VarType::BINARY); 19 | model.addConstr(x1 + x2 + x3 <= 1, "upperBound"); 20 | model.setObjsense(Sense::MAXIMIZE); 21 | 22 | InitialSolution is; 23 | is.setValue(x1, indexSetToOne == 1 ? 1 : 0); 24 | is.setValue(x2, indexSetToOne == 2 ? 1 : 0); 25 | is.setValue(x3, indexSetToOne == 3 ? 1 : 0); 26 | BOOST_TEST(model.addSolution(is)); 27 | 28 | BOOST_REQUIRE(model.getNSols() > 0); 29 | auto sol = model.getBestSol(); 30 | BOOST_TEST(x1.getSolValAsInt(sol) == (indexSetToOne == 1 ? 1 : 0)); 31 | BOOST_TEST(x2.getSolValAsInt(sol) == (indexSetToOne == 2 ? 1 : 0)); 32 | BOOST_TEST(x3.getSolValAsInt(sol) == (indexSetToOne == 3 ? 1 : 0)); 33 | } 34 | 35 | BOOST_AUTO_TEST_CASE(Infeasible) 36 | { 37 | Model model("Simple"); 38 | auto x1 = model.addVar("x_1", 1, VarType::BINARY); 39 | model.setObjsense(Sense::MAXIMIZE); 40 | InitialSolution is; 41 | is.setValue(x1, 2); 42 | BOOST_TEST(!model.addSolution(is)); 43 | } 44 | 45 | BOOST_AUTO_TEST_CASE(UpdateSolution) 46 | { 47 | Model model("Simple"); 48 | const auto& [x1, x2] = model.addVars<2>("x_"); 49 | InitialSolution is; 50 | is(x1) = 4; 51 | is(x1) += 38; 52 | is(x2) += 42; 53 | BOOST_TEST(is(x1) == 42); 54 | BOOST_TEST(is(x2) == 42); 55 | } 56 | 57 | BOOST_AUTO_TEST_SUITE_END() 58 | -------------------------------------------------------------------------------- /test/test_io.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "scippp/model.hpp" 8 | 9 | using namespace boost::algorithm; 10 | using namespace scippp; 11 | using namespace std; 12 | 13 | class TempFile { 14 | filesystem::path m_path { filesystem::temp_directory_path() }; 15 | 16 | public: 17 | explicit TempFile(const string& extension) 18 | { 19 | m_path += boost::filesystem::unique_path("/%%%%-%%%%-%%%%-%%%%." + extension).string(); 20 | } 21 | ~TempFile() 22 | { 23 | filesystem::remove(m_path); 24 | } 25 | [[nodiscard]] filesystem::directory_entry path() const 26 | { 27 | return filesystem::directory_entry(m_path); 28 | } 29 | [[nodiscard]] string content() const 30 | { 31 | ifstream t(m_path); 32 | ostringstream sstr; 33 | sstr << t.rdbuf(); 34 | return sstr.str(); 35 | } 36 | }; 37 | 38 | auto createModel() 39 | { 40 | Model model("Simple"); 41 | auto x1 = model.addVar("x_1", 1); 42 | auto x2 = model.addVar("x_2", 1); 43 | model.addConstr(x1 + x2 >= 1, "capacity"); 44 | model.addConstr(x1 == x2, "equal"); 45 | model.setObjsense(Sense::MINIMIZE); 46 | return model; 47 | } 48 | 49 | BOOST_AUTO_TEST_SUITE(IO) 50 | 51 | BOOST_AUTO_TEST_CASE(FileLP) 52 | { 53 | auto model { createModel() }; 54 | 55 | TempFile tf("lp"); 56 | model.writeOrigProblem(tf.path()); 57 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 58 | auto content { tf.content() }; 59 | BOOST_TEST(contains(content, "Obj: +1 x_1 +1 x_2")); 60 | BOOST_TEST(contains(content, "capacity:")); 61 | BOOST_TEST(contains(content, "equal:")); 62 | BOOST_TEST(contains(content, "Minimize")); 63 | } 64 | 65 | BOOST_AUTO_TEST_CASE(FileLPGenericNames) 66 | { 67 | auto model { createModel() }; 68 | 69 | TempFile tf("lp"); 70 | model.writeOrigProblem(tf.path(), true); 71 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 72 | auto content { tf.content() }; 73 | BOOST_TEST(contains(content, "Obj: +1 x0 +1 x1")); 74 | BOOST_TEST(contains(content, "c0:")); 75 | BOOST_TEST(contains(content, "c1:")); 76 | BOOST_TEST(contains(content, "Minimize")); 77 | } 78 | 79 | BOOST_AUTO_TEST_CASE(FileMPS) 80 | { 81 | auto model { createModel() }; 82 | 83 | TempFile tf("mps"); 84 | model.writeOrigProblem(tf.path()); 85 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 86 | auto content { tf.content() }; 87 | BOOST_TEST(contains(content, "G capacity")); 88 | BOOST_TEST(contains(content, "E equal")); 89 | BOOST_TEST(contains(content, "\n MIN")); 90 | } 91 | 92 | BOOST_AUTO_TEST_CASE(StdoutLP) 93 | { 94 | auto model { createModel() }; 95 | model.writeOrigProblem("lp"); 96 | BOOST_TEST(model.getLastReturnCode() == SCIP_OKAY); 97 | } 98 | 99 | BOOST_AUTO_TEST_CASE(StdoutWithInvalidExtension) 100 | { 101 | auto model { createModel() }; 102 | model.writeOrigProblem("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); 103 | BOOST_TEST(model.getLastReturnCode() == SCIP_PLUGINNOTFOUND); 104 | } 105 | 106 | BOOST_AUTO_TEST_SUITE_END() 107 | -------------------------------------------------------------------------------- /test/test_objscip.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scippp/model.hpp" 4 | #include 5 | 6 | using namespace scippp; 7 | using namespace std; 8 | 9 | BOOST_AUTO_TEST_SUITE(ObjSCIP) 10 | 11 | /** 12 | * We check whether SCIP++ works with ObjSCIP 13 | */ 14 | class MyEventHandler : public scip::ObjEventhdlr { 15 | public: 16 | MyEventHandler(SCIP* scip) 17 | : scip::ObjEventhdlr(scip, "NAME", "DESC") 18 | { 19 | } 20 | SCIP_DECL_EVENTINIT(scip_init) 21 | override 22 | { 23 | clog << "MyEventHandler: Here I am" << endl; 24 | return SCIP_OKAY; 25 | } 26 | SCIP_DECL_EVENTEXEC(scip_exec) 27 | override 28 | { 29 | return SCIP_OKAY; 30 | } 31 | }; 32 | 33 | BOOST_AUTO_TEST_CASE(UseEventHandler) 34 | { 35 | Model model("Simple"); 36 | auto x1 = model.addVar("x_1", 1); 37 | auto x2 = model.addVar("x_2", 1); 38 | model.addConstr(x1 + x2 >= 1, "capacity"); 39 | model.addConstr(x1 == x2, "equal"); 40 | SCIPincludeObjEventhdlr(model.scip(), new MyEventHandler(model.scip()), false); 41 | model.setObjsense(Sense::MINIMIZE); 42 | model.solve(); 43 | BOOST_TEST(model.getNSols() > 0); 44 | } 45 | 46 | BOOST_AUTO_TEST_SUITE_END() 47 | -------------------------------------------------------------------------------- /test/test_var.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "scippp/model.hpp" 4 | 5 | using namespace scippp; 6 | using namespace std; 7 | 8 | BOOST_AUTO_TEST_SUITE(Var) 9 | 10 | BOOST_AUTO_TEST_CASE(GetSolVal, *boost::unit_test::tolerance(1e-3)) 11 | { 12 | Model model("Simple"); 13 | auto x1 = model.addVar("x_1", 1); 14 | auto x2 = model.addVar("x_2", 1); 15 | model.addConstr(x1 + x2 >= 1, "capacity"); 16 | model.addConstr(x1 == x2, "equal"); 17 | model.setObjsense(Sense::MINIMIZE); 18 | model.solve(); 19 | BOOST_REQUIRE(model.getNSols() > 0); 20 | auto sol = model.getBestSol(); 21 | BOOST_TEST(x1.getSolVal(sol) == 0.5); 22 | BOOST_TEST(x2.getSolVal(sol) == 0.5); 23 | } 24 | 25 | BOOST_AUTO_TEST_CASE(GetSolValInt) 26 | { 27 | Model model("Simple"); 28 | auto x1 = model.addVar("x_1", 1); 29 | auto x2 = model.addVar("x_2", 1); 30 | model.addConstr(x1 + x2 >= 2, "capacity"); 31 | model.addConstr(x1 == x2, "equal"); 32 | model.setObjsense(Sense::MINIMIZE); 33 | model.solve(); 34 | BOOST_REQUIRE(model.getNSols() > 0); 35 | auto sol = model.getBestSol(); 36 | BOOST_TEST(x1.getSolValAsInt(sol) == 1); 37 | BOOST_TEST(x2.getSolValAsInt(sol) == 1); 38 | } 39 | 40 | BOOST_AUTO_TEST_CASE(GetSolValLongInt) 41 | { 42 | long long big { static_cast(numeric_limits::max()) + 1 }; 43 | Model model("Simple"); 44 | auto x1 = model.addVar("x_1", 1.0, VarType::CONTINUOUS, 0.0, model.infinity()); 45 | model.addConstr(x1 >= static_cast(big), "capacity"); 46 | model.setObjsense(Sense::MINIMIZE); 47 | model.solve(); 48 | BOOST_REQUIRE(model.getNSols() > 0); 49 | auto sol = model.getBestSol(); 50 | BOOST_TEST(x1.getSolValAsLongInt(sol) == big); 51 | } 52 | 53 | BOOST_AUTO_TEST_CASE(IsZero) 54 | { 55 | Model model("Simple"); 56 | auto x1 = model.addVar("x_1", 1); 57 | model.setObjsense(Sense::MINIMIZE); 58 | model.solve(); 59 | BOOST_REQUIRE(model.getNSols() > 0); 60 | auto sol = model.getBestSol(); 61 | BOOST_TEST(x1.isZero(sol)); 62 | BOOST_TEST(model.isZero(x1.getSolVal(sol))); 63 | } 64 | 65 | BOOST_AUTO_TEST_CASE(IsVoid) 66 | { 67 | scippp::Var x; 68 | BOOST_TEST(x.var == nullptr); 69 | BOOST_TEST(x.getVar() == nullptr); 70 | BOOST_TEST(x.isVoid()); 71 | 72 | Model model("Simple"); 73 | auto x1 = model.addVar("x_1", 1); 74 | BOOST_TEST(x1.var != nullptr); 75 | BOOST_TEST(!x1.isVoid()); 76 | } 77 | 78 | BOOST_AUTO_TEST_SUITE_END() 79 | -------------------------------------------------------------------------------- /utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(gen_constexpr_parameters gen_constexpr_parameters.cpp) 2 | target_link_libraries(gen_constexpr_parameters PUBLIC scip::scip) 3 | -------------------------------------------------------------------------------- /utils/extract_solvingstats.py: -------------------------------------------------------------------------------- 1 | # usage: extract_solvingstats.py FULL_PATH_TO_ 2 | 3 | import argparse 4 | from caseconverter import macrocase 5 | import CppHeaderParser 6 | from typing import Optional 7 | 8 | 9 | def create_object_name(c_name: str) -> str: 10 | assert c_name.startswith('SCIPget') 11 | return macrocase(c_name.split('SCIPget')[1]).replace("_L_P_", "_LP_") 12 | 13 | 14 | def extract_description(doxygen: str) -> Optional[str]: 15 | if '@return' not in doxygen: 16 | return None 17 | res = doxygen.split("@return")[1] 18 | if '\n' in res: 19 | res = res.split('\n')[0].strip() 20 | if res.startswith("the"): 21 | res = res[4:] 22 | return res.strip() 23 | 24 | 25 | parser = argparse.ArgumentParser() 26 | parser.add_argument("header", help="Full path to scip_solvingstats.h", type=str) 27 | args = parser.parse_args() 28 | 29 | header = CppHeaderParser.CppHeader(args.header) 30 | for fn in header.functions: 31 | name = fn['name'] 32 | params = fn['parameters'] 33 | if name.startswith("SCIPget") and len(params) == 1: 34 | prm = params[0] 35 | assert prm['raw_type'] == 'SCIP' 36 | doc = fn['doxygen'] 37 | if descr := extract_description(doc): 38 | print(f"//! {descr}") 39 | rtype = fn['returns'].split(' ')[1] 40 | print(f"static const Statistic<{rtype}> {create_object_name(name)} {{ &{name} }};") 41 | -------------------------------------------------------------------------------- /utils/gen_constexpr_parameters.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std; 14 | 15 | class ScipParam { 16 | vector nameSplit; 17 | string description; 18 | SCIP_PARAMTYPE type; 19 | static vector fitToLineLength(string str, size_t line_sz) 20 | { 21 | if (std::all_of(std::begin(str), std::end(str), [](char c) { return std::isspace(c); })) 22 | return {}; // empty string or string with all spaces, return an empty vector 23 | 24 | std::vector result(1); // vector containing one empty string 25 | 26 | std::istringstream stm(str); // use a string stream to split on white-space 27 | std::string word; 28 | while (stm >> word) // for each word in the string 29 | { 30 | // if this word will fit into the current line, append the word to the current line 31 | if ((result.back().size() + word.size()) <= line_sz) 32 | result.back() += word + ' '; 33 | 34 | else { 35 | result.back().pop_back(); // remove the trailing space at the end of the current line 36 | result.push_back(word + ' '); // and place this new word on the next line 37 | } 38 | } 39 | 40 | result.back().pop_back(); // remove the trailing space at the end of the last line 41 | return result; 42 | } 43 | static string getCppIdentifier(const string& s) 44 | { 45 | auto res { boost::algorithm::to_upper_copy(s) }; 46 | boost::replace_all(res, "-", "_"); 47 | return res; 48 | } 49 | string getScipName() const 50 | { 51 | return boost::algorithm::join(nameSplit, "/"); 52 | } 53 | string getVariableName() const 54 | { 55 | return getCppIdentifier(nameSplit.back()); 56 | } 57 | string getType() const 58 | { 59 | switch (type) { 60 | case SCIP_PARAMTYPE_BOOL: 61 | return "bool"; 62 | case SCIP_PARAMTYPE_INT: 63 | return "int"; 64 | case SCIP_PARAMTYPE_LONGINT: 65 | return "long long"; 66 | case SCIP_PARAMTYPE_REAL: 67 | return "double"; 68 | case SCIP_PARAMTYPE_CHAR: 69 | return "char"; 70 | case SCIP_PARAMTYPE_STRING: 71 | return "std::string"; 72 | } 73 | } 74 | 75 | public: 76 | explicit ScipParam(SCIP_PARAM* param) 77 | : description { param->desc } 78 | , type { param->paramtype } 79 | { 80 | boost::split(nameSplit, param->name, boost::is_any_of("/")); 81 | // doxygen treats as xml 82 | boost::algorithm::replace_all(description, "<", "\\<"); 83 | boost::algorithm::replace_all(description, ">", "\\>"); 84 | boost::algorithm::replace_all(description, "#", "\\#"); 85 | } 86 | string getNamespace() const 87 | { 88 | vector parts(nameSplit.begin(), prev(nameSplit.end())); 89 | for_each(parts.begin(), parts.end(), [](string& s) { 90 | s = getCppIdentifier(s); 91 | }); 92 | return boost::algorithm::join(parts, "::"); 93 | } 94 | static string getPrefixFromNamespace(string namesp) 95 | { 96 | boost::algorithm::replace_all(namesp, "::", "/"); 97 | boost::algorithm::to_lower(namesp); 98 | return namesp; 99 | } 100 | string getDoxygen() const 101 | { 102 | string res; 103 | for (const string line : fitToLineLength(description, 120 - 8)) { 104 | res += " //! " + line + "\n"; 105 | } 106 | return res; 107 | } 108 | string getConstexprParam() const 109 | { 110 | return "constexpr Param<" + getType() + "> " + getVariableName() + " { \"" + getScipName() + "\" };"; 111 | } 112 | }; 113 | 114 | string createNamespaceDocu(const string& namesp) 115 | { 116 | return "//! Parameters with prefix " + ScipParam::getPrefixFromNamespace(namesp); 117 | } 118 | 119 | int main() 120 | { 121 | SCIP* scip { nullptr }; 122 | SCIPcreate(&scip); 123 | SCIPincludeDefaultPlugins(scip); 124 | auto* paramset = scip->set->paramset; 125 | map> paramsPerNamespace; 126 | for (size_t i = 0; i < paramset->nparams; ++i) { 127 | ScipParam param { paramset->params[i] }; 128 | paramsPerNamespace[param.getNamespace()].push_back(param); 129 | } 130 | for (const auto& [namesp, elems] : paramsPerNamespace) { 131 | cout << createNamespaceDocu(namesp) << "\n"; 132 | cout << "namespace " << namesp << " {\n"; 133 | for (const auto& param : elems) { 134 | cout << param.getDoxygen(); 135 | cout << " " << param.getConstexprParam() << "\n"; 136 | } 137 | cout << "}" << endl; 138 | } 139 | SCIPfree(&scip); 140 | } 141 | -------------------------------------------------------------------------------- /utils/requirements.txt: -------------------------------------------------------------------------------- 1 | case-converter==1.1.0 2 | CppHeaderParser==2.7.4 3 | --------------------------------------------------------------------------------