Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
344 changes: 344 additions & 0 deletions .github/workflows/optimize.yml

Large diffs are not rendered by default.

155 changes: 154 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ option(GECODE_ENABLE_INT_VARS "Build int variables module" ON)
option(GECODE_ENABLE_SET_VARS "Build set variables module" ON)
option(GECODE_ENABLE_FLOAT_VARS "Build float variables module" ON)
option(GECODE_ENABLE_MINIMODEL "Build minimodel module" ON)
option(GECODE_ENABLE_LP_RELAXATION "Enable certified LP relaxation module (requires HiGHS)" OFF)
option(GECODE_ENABLE_OPTIMIZE "Build additive numerical optimization API" OFF)
option(GECODE_ENABLE_DRIVER "Build driver module" ON)
option(GECODE_ENABLE_FLATZINC "Build FlatZinc module" ON)

Expand Down Expand Up @@ -241,6 +243,15 @@ if(GECODE_SANITIZER_NORMALIZED)
endif()

# Keep dependency closure behavior similar to configure.
if(GECODE_ENABLE_LP_RELAXATION)
gecode_force_option_on(GECODE_ENABLE_MINIMODEL "LP relaxation requires minimodel")
gecode_force_option_on(GECODE_ENABLE_SEARCH "LP relaxation requires search")
gecode_force_option_on(GECODE_ENABLE_INT_VARS "LP relaxation requires int variables")
find_package(highs 1.15 CONFIG REQUIRED)
if(NOT TARGET highs::highs)
message(FATAL_ERROR "LP relaxation requires the highs::highs CMake target")
endif()
endif()
if(GECODE_ENABLE_SET_VARS)
gecode_force_option_on(GECODE_ENABLE_INT_VARS "Set variables require int variables")
endif()
Expand Down Expand Up @@ -1068,6 +1079,7 @@ if(DOXYGEN_FOUND AND GECODE_UV_EXECUTABLE)
-P ${GECODE_DOXYGEN_COMPAT_ALIASES}
DEPENDS gecode-varimp-gen
${CMAKE_CURRENT_BINARY_DIR}/doxygen.hh
${CMAKE_CURRENT_SOURCE_DIR}/doxygen/optimize.hh
${CMAKE_CURRENT_BINARY_DIR}/doxygen.conf.use
${CMAKE_CURRENT_BINARY_DIR}/header.html
${CMAKE_CURRENT_SOURCE_DIR}/misc/doxygen/footer.html
Expand Down Expand Up @@ -1326,9 +1338,27 @@ if(GECODE_ENABLE_FLATZINC)
endif()
endif()

# The LP module is header-only. Keep its external dependency off the native
# minimodel target so applications can select the additional functionality.
if(GECODE_ENABLE_LP_RELAXATION)
add_library(gecodelp INTERFACE)
target_link_libraries(gecodelp INTERFACE gecodeminimodel highs::highs)
add_library(Gecode::gecodelp ALIAS gecodelp)
list(APPEND GECODE_LIBRARY_COMPONENTS lp)
list(APPEND GECODE_INSTALL_TARGETS gecodelp)
list(APPEND GECODE_EXPORT_TARGETS gecodelp)
endif()

if(GECODE_ENABLE_OPTIMIZE)
add_subdirectory(gecode/optimize)
list(APPEND GECODE_LIBRARY_COMPONENTS optimize)
list(APPEND GECODE_INSTALL_TARGETS gecodeoptimize gecodeoptimize_c)
list(APPEND GECODE_EXPORT_TARGETS gecodeoptimize gecodeoptimize_c)
endif()

# Compatibility aggregate target for downstream projects expecting Gecode::gecode.
add_library(gecode INTERFACE)
foreach(component IN ITEMS support kernel search int set float minimodel driver flatzinc gist)
foreach(component IN ITEMS support kernel search int set float minimodel driver flatzinc gist lp optimize)
if(TARGET gecode${component})
target_link_libraries(gecode INTERFACE gecode${component})
endif()
Expand All @@ -1351,6 +1381,64 @@ if(GECODE_ENABLE_FLATZINC)
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}")
endif()
list(APPEND GECODE_INSTALL_TARGETS fzn-gecode)
if(TARGET gecodeoptimize)
# Separate executable; experimental registration is explicitly opt-in below.
add_executable(fzn-gecode-optimize tools/flatzinc/fzn-gecode-optimize.cpp)
target_link_libraries(fzn-gecode-optimize PRIVATE gecodeflatzinc gecodeoptimize)
if(APPLE)
set_target_properties(fzn-gecode-optimize PROPERTIES
INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}")
elseif(UNIX)
set_target_properties(fzn-gecode-optimize PROPERTIES
INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}")
endif()
list(APPEND GECODE_INSTALL_TARGETS fzn-gecode-optimize)
endif()
endif()

option(GECODE_OPTIMIZE_MINIZINC_REGISTRATION "Build/install the explicit experimental MiniZinc solver registration" OFF)
set(GECODE_OPTIMIZE_MINIZINC_EXECUTABLE "" CACHE FILEPATH "Optional pinned MiniZinc compiler for experimental registration tests (no download)")
if(GECODE_OPTIMIZE_MINIZINC_REGISTRATION)
if(NOT TARGET fzn-gecode-optimize OR NOT GECODE_OPTIMIZE_WITH_NATIVE)
message(FATAL_ERROR "Experimental MiniZinc registration requires the optimization FlatZinc driver and native backend")
endif()
if(IS_ABSOLUTE "${CMAKE_INSTALL_BINDIR}" OR IS_ABSOLUTE "${CMAKE_INSTALL_DATADIR}")
message(FATAL_ERROR "Relocatable experimental MiniZinc registration requires relative CMAKE_INSTALL_BINDIR and CMAKE_INSTALL_DATADIR")
endif()
set(_gecode_optimize_msc_template "${PROJECT_SOURCE_DIR}/tools/flatzinc/gecode-optimize.msc.in")
set(_gecode_optimize_msc_encoder "${PROJECT_SOURCE_DIR}/tools/flatzinc/configure-optimize-msc.cmake")
# execute_process below generates the install registration at configure time.
# Keep it in sync when solver flags or JSON encoding change between builds.
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
"${_gecode_optimize_msc_template}" "${_gecode_optimize_msc_encoder}")
set(_gecode_optimize_build_msc "${PROJECT_BINARY_DIR}/minizinc/$<CONFIG>/gecode-optimize.msc")
# Target paths are expanded before the encoder escapes them as JSON. This
# also supports multi-configuration builds and source/build paths with quotes.
add_custom_target(gecode-optimize-minizinc-config ALL
COMMAND "${CMAKE_COMMAND}"
"-DTEMPLATE=${_gecode_optimize_msc_template}" "-DVERSION=${GECODE_VERSION}"
"-DDRIVER=$<TARGET_FILE:fzn-gecode-optimize>"
"-DMZNLIB=${PROJECT_SOURCE_DIR}/tools/flatzinc/mznlib-optimize"
"-DOUTPUT=${_gecode_optimize_build_msc}" -P "${_gecode_optimize_msc_encoder}"
DEPENDS fzn-gecode-optimize "${_gecode_optimize_msc_template}" "${_gecode_optimize_msc_encoder}"
VERBATIM)
set(_gecode_optimize_msc_destination "${CMAKE_INSTALL_DATADIR}/minizinc/solvers")
file(RELATIVE_PATH _gecode_optimize_installed_driver "/${_gecode_optimize_msc_destination}"
"/${CMAKE_INSTALL_BINDIR}/fzn-gecode-optimize${CMAKE_EXECUTABLE_SUFFIX}")
file(RELATIVE_PATH _gecode_optimize_installed_library "/${_gecode_optimize_msc_destination}"
"/${CMAKE_INSTALL_DATADIR}/minizinc/gecode-optimize-experimental")
set(_gecode_optimize_install_msc "${PROJECT_BINARY_DIR}/minizinc/install/gecode-optimize.msc")
execute_process(COMMAND "${CMAKE_COMMAND}"
"-DTEMPLATE=${_gecode_optimize_msc_template}" "-DVERSION=${GECODE_VERSION}"
"-DDRIVER=${_gecode_optimize_installed_driver}" "-DMZNLIB=${_gecode_optimize_installed_library}"
"-DOUTPUT=${_gecode_optimize_install_msc}" -P "${_gecode_optimize_msc_encoder}"
COMMAND_ERROR_IS_FATAL ANY)
if(GECODE_INSTALL)
install(FILES "${_gecode_optimize_install_msc}" DESTINATION "${_gecode_optimize_msc_destination}")
install(DIRECTORY tools/flatzinc/mznlib-optimize/
DESTINATION "${CMAKE_INSTALL_DATADIR}/minizinc/gecode-optimize-experimental"
FILES_MATCHING PATTERN "*.mzn")
endif()
endif()

if(BUILD_TESTING)
Expand All @@ -1372,6 +1460,61 @@ if(BUILD_TESTING)
endif()

if(GECODE_ENABLE_FLATZINC)
if(NOT TARGET Threads::Threads)
find_package(Threads REQUIRED)
endif()
add_executable(gecode-flatzinc-capture-test test/flatzinc-capture/capture.cpp)
target_compile_features(gecode-flatzinc-capture-test PRIVATE cxx_std_17)
target_link_libraries(gecode-flatzinc-capture-test PRIVATE gecodeflatzinc Threads::Threads)
if(MSVC)
target_compile_options(gecode-flatzinc-capture-test PRIVATE /UNDEBUG)
else()
target_compile_options(gecode-flatzinc-capture-test PRIVATE -UNDEBUG)
endif()
add_test(NAME gecode-flatzinc-capture COMMAND gecode-flatzinc-capture-test)
set_tests_properties(gecode-flatzinc-capture PROPERTIES TIMEOUT 120)
if(TARGET gecodeoptimize AND GECODE_OPTIMIZE_BUILD_TESTS)
add_executable(optimize-flatzinc-driver-test test/optimize/flatzinc_driver.cpp)
target_link_libraries(optimize-flatzinc-driver-test PRIVATE gecodeflatzinc gecodeoptimize)
if(GECODE_OPTIMIZE_WITH_NATIVE OR GECODE_OPTIMIZE_WITH_HIGHS)
target_compile_definitions(optimize-flatzinc-driver-test PRIVATE GECODE_FLATZINC_DRIVER_EXPECT_BACKEND=1)
endif()
if(MSVC)
target_compile_options(optimize-flatzinc-driver-test PRIVATE /UNDEBUG)
else()
target_compile_options(optimize-flatzinc-driver-test PRIVATE -UNDEBUG)
endif()
add_test(NAME optimize-flatzinc-driver COMMAND optimize-flatzinc-driver-test)
set_tests_properties(optimize-flatzinc-driver PROPERTIES TIMEOUT 120)
find_package(Python3 3.9 COMPONENTS Interpreter QUIET)
if(Python3_Interpreter_FOUND AND GECODE_OPTIMIZE_WITH_NATIVE)
if(GECODE_OPTIMIZE_WITH_HIGHS)
set(_gecode_cli_highs available)
else()
set(_gecode_cli_highs unavailable)
endif()
add_test(NAME optimize-flatzinc-driver-cli COMMAND "${CMAKE_COMMAND}" -E env
"PYTHONDONTWRITEBYTECODE=1" "${Python3_EXECUTABLE}"
"${PROJECT_SOURCE_DIR}/test/optimize/flatzinc_driver_cli.py"
--binary "$<TARGET_FILE:fzn-gecode-optimize>" --highs "${_gecode_cli_highs}")
set_tests_properties(optimize-flatzinc-driver-cli PROPERTIES TIMEOUT 90)
add_test(NAME optimize-minizinc-configure COMMAND "${CMAKE_COMMAND}" -E env
"PYTHONDONTWRITEBYTECODE=1" "${Python3_EXECUTABLE}" -B
"${PROJECT_SOURCE_DIR}/test/optimize/minizinc_configure.py" --cmake "${CMAKE_COMMAND}")
set_tests_properties(optimize-minizinc-configure PROPERTIES TIMEOUT 60)
if(GECODE_OPTIMIZE_MINIZINC_REGISTRATION AND GECODE_OPTIMIZE_MINIZINC_EXECUTABLE)
if(NOT EXISTS "${GECODE_OPTIMIZE_MINIZINC_EXECUTABLE}")
message(FATAL_ERROR "The explicitly configured MiniZinc test compiler is missing")
endif()
add_test(NAME optimize-minizinc-registration COMMAND "${CMAKE_COMMAND}" -E env
"PYTHONDONTWRITEBYTECODE=1" "${Python3_EXECUTABLE}" -B
"${PROJECT_SOURCE_DIR}/test/optimize/minizinc_registration.py"
--minizinc "${GECODE_OPTIMIZE_MINIZINC_EXECUTABLE}"
--binary "$<TARGET_FILE:fzn-gecode-optimize>" --registration "${_gecode_optimize_build_msc}")
set_tests_properties(optimize-minizinc-registration PROPERTIES TIMEOUT 150)
endif()
endif()
endif()
add_executable(gecode-test-blackbox-exec
${GECODE_TEST_BLACKBOX_EXEC_SOURCE})
target_compile_features(gecode-test-blackbox-exec PRIVATE cxx_std_17)
Expand Down Expand Up @@ -1633,12 +1776,22 @@ if(GECODE_INSTALL)
PATTERN "flatzinc/blackbox.hh" EXCLUDE
PATTERN "flatzinc/blackbox-backend.hh" EXCLUDE
PATTERN "flatzinc/blackbox-process.hh" EXCLUDE
PATTERN "optimize/quadratic_bound.hpp" EXCLUDE
PATTERN "optimize/lp_observations_detail.hpp" EXCLUDE
PATTERN "optimize/lp_basis_detail.hpp" EXCLUDE
PATTERN "optimize/lp_sensitivity_backend.hpp" EXCLUDE
PATTERN "optimize/lp_sensitivity_highs_detail.hpp" EXCLUDE
PATTERN "optimize/native_regular_limits.hpp" EXCLUDE
PATTERN "exampleplugin" EXCLUDE
PATTERN "standalone-example" EXCLUDE
PATTERN "abi*" EXCLUDE)

install(FILES ${PROJECT_BINARY_DIR}/gecode/support/config.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gecode/support/)
if(GECODE_ENABLE_OPTIMIZE)
install(FILES gecode/optimize/c_api.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gecode/optimize/)
endif()
if(GECODE_REGENERATE_VARIMP)
install(FILES ${GECODE_VAR_TYPE_HPP} ${GECODE_VAR_IMP_HPP}
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gecode/kernel/)
Expand Down
1 change: 1 addition & 0 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -1957,6 +1957,7 @@ DOXYGEN_MIN_VERSION = 1.17.0
.PHONY: doc

DOCSRC_NOTGENERATED = \
doxygen/optimize.hh \
misc/doxygen/back.png misc/doxygen/footer.html \
misc/doxygen/gecode-logo-100.png \
misc/doxygen/stylesheet.css \
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ In particular,
Gecode comes with
[extensive tutorial and reference documentation](https://gecode.github.io/documentation.html).

## Optional Optimization API

`Gecode::Optimize` provides sparse optimization models, numerical solving with
HiGHS, and bounded integer search with Gecode. Enable it with
`-DGECODE_ENABLE_OPTIMIZE=ON`. See the
[optimization build and test guide](docs/optimize.md) and the
[reference documentation](doxygen/optimize.hh) for the supported interfaces.

## CMake Build Options

CMake exposes options aligned with the Autoconf build switches.
Expand All @@ -44,6 +52,7 @@ Version metadata shared by autoconf and CMake lives in `gecode-version.m4`.
| `--enable-set-vars` | `GECODE_ENABLE_SET_VARS` | Supported directly | Default `ON` |
| `--enable-float-vars` | `GECODE_ENABLE_FLOAT_VARS` | Supported directly | Default `ON` |
| `--enable-minimodel` | `GECODE_ENABLE_MINIMODEL` | Supported directly | Default `ON` |
| None | `GECODE_ENABLE_OPTIMIZE` | CMake-only | Optional optimization component; default `OFF` |
| `--enable-driver` | `GECODE_ENABLE_DRIVER` | Supported directly | Default `ON` |
| `--enable-flatzinc` | `GECODE_ENABLE_FLATZINC` | Supported directly | Default `ON` |
| `--enable-mpfr` | `GECODE_ENABLE_MPFR` | Supported directly | Default `ON`; uses `find_package(MPFR)` |
Expand Down
34 changes: 32 additions & 2 deletions cmake/GecodeConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ set(_gecode_supported_components
set
float
minimodel
lp
optimize
driver
flatzinc
gist)
Expand Down Expand Up @@ -72,6 +74,10 @@ while(_gecode_dependency_queue)
set(_gecode_component_dependencies int kernel)
elseif(_gecode_dependency_component STREQUAL minimodel)
set(_gecode_component_dependencies int set search float)
elseif(_gecode_dependency_component STREQUAL lp)
set(_gecode_component_dependencies minimodel)
elseif(_gecode_dependency_component STREQUAL optimize AND "@GECODE_OPTIMIZE_WITH_NATIVE@")
set(_gecode_component_dependencies int search)
elseif(_gecode_dependency_component STREQUAL gist)
set(_gecode_component_dependencies search int set float)
elseif(_gecode_dependency_component STREQUAL driver)
Expand All @@ -87,8 +93,22 @@ while(_gecode_dependency_queue)
endwhile()

include(CMakeFindDependencyMacro)
if(support IN_LIST _gecode_component_closure AND
"@GECODE_PACKAGE_NEEDS_THREADS@" STREQUAL "ON")
if(optimize IN_LIST _gecode_component_closure AND "@GECODE_OPTIMIZE_WITH_HIGHS@")
find_dependency(highs 1.15 CONFIG)
elseif(lp IN_LIST _gecode_component_closure)
find_dependency(highs CONFIG)
endif()
if(lp IN_LIST _gecode_component_closure OR
(optimize IN_LIST _gecode_component_closure AND "@GECODE_OPTIMIZE_WITH_HIGHS@"))
if(NOT TARGET highs::highs)
set(Gecode_FOUND FALSE)
set(Gecode_NOT_FOUND_MESSAGE "Requested Gecode optimization component requires highs::highs")
return()
endif()
endif()
if((support IN_LIST _gecode_component_closure AND
"@GECODE_PACKAGE_NEEDS_THREADS@" STREQUAL "ON") OR
optimize IN_LIST _gecode_component_closure)
find_dependency(Threads)
endif()
if(float IN_LIST _gecode_component_closure AND
Expand Down Expand Up @@ -171,6 +191,16 @@ unset(_gecode_qt_version)
set(PACKAGE_PREFIX_DIR "${_gecode_package_prefix_dir}")

include("${CMAKE_CURRENT_LIST_DIR}/GecodeTargets.cmake")
if(TARGET Gecode::gecodeoptimize AND NOT TARGET Gecode::optimize)
add_library(Gecode::optimize INTERFACE IMPORTED)
set_target_properties(Gecode::optimize PROPERTIES
INTERFACE_LINK_LIBRARIES Gecode::gecodeoptimize)
endif()
if(TARGET Gecode::gecodeoptimize_c AND NOT TARGET Gecode::optimize_c)
add_library(Gecode::optimize_c INTERFACE IMPORTED)
set_target_properties(Gecode::optimize_c PROPERTIES
INTERFACE_LINK_LIBRARIES Gecode::gecodeoptimize_c)
endif()

set(Gecode_VERSION "@GECODE_PROJECT_VERSION@")
set_and_check(Gecode_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
Expand Down
1 change: 1 addition & 0 deletions cmake/GecodeSources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ set(GECODE_FLATZINC_SOURCES
gecode/flatzinc/blackbox-process-windows.cpp
gecode/flatzinc/blackbox-propagator.cpp
gecode/flatzinc/branch.cpp
gecode/flatzinc/capture.cpp
gecode/flatzinc/flatzinc.cpp
gecode/flatzinc/lexer.yy.cpp
gecode/flatzinc/parser.tab.cpp
Expand Down
Loading
Loading